diff --git a/.gitignore b/.gitignore index bf47f4a..5c08c36 100644 --- a/.gitignore +++ b/.gitignore @@ -227,3 +227,18 @@ dist/ .astro/ # Slide deck exports (PDF/PPTX) — published to the Hub, not tracked here export/ + +# Generated run artifacts: training checkpoints, capture traces, Harbor trial trees, eval records. +# These reach tens of GB -- 22 GB on disk here, of which 877 MB had actually been committed and had +# to be stripped from this branch's history before its first push. Committing generated data is the +# fastest way to make a repo unusable, and none of it is reproducible-by-review anyway. +# +# `logs/` now holds ONLY generated output. The operational scripts that used to live there +# (start_server.sh, stop_all.sh, hf_token.sh, needle.txt) moved to each environment's `tools/`, +# because tracked source inside an ignored directory is a contradiction waiting to bite -- a fresh +# clone would look complete and silently lack them. +04-data-agent/**/logs/ + +# Gradio scratch. `certificate.pem` is the CA bundle gradio writes on first tunnel use and +# `flagged/` is its UI capture dir -- both generated per machine, neither is source. +.gradio/ diff --git a/04-data-agent/.gitignore b/04-data-agent/.gitignore new file mode 100644 index 0000000..239a08e --- /dev/null +++ b/04-data-agent/.gitignore @@ -0,0 +1,8 @@ +/temp/ +/.env +/.venv*/ +/__pycache__/ +**/__pycache__/ +/.pytest_cache/ +.gradio/ +*.pyc diff --git a/04-data-agent/README.md b/04-data-agent/README.md new file mode 100644 index 0000000..53dcb85 --- /dev/null +++ b/04-data-agent/README.md @@ -0,0 +1,52 @@ +# Data Agent: compare who owns the agent loop + +Train and evaluate **Qwen3.5-2B** on the same fixed data-analysis tasks, using three OpenEnv implementations. + +| Recipe | Agent loop | Trainer | Environment | +| --- | --- | --- | --- | +| `harbor-multi` | OpenCode, Claude Code, Codex and Mini-SWE-Agent via Harbor | AsyncGRPO | [Blackbox Harbor Space](https://huggingface.co/spaces/HuggingEnvs/data-agent-blackbox-harbor-env) | +| `harbor-opencode` | OpenCode via Harbor | AsyncGRPO | Same Harbor Space | +| `native-opencode` | Original standalone OpenCode adapter | AsyncGRPO | [Blackbox OpenCode Space](https://huggingface.co/spaces/HuggingEnvs/data-agent-blackbox-opencode-env) | +| `seta` | Model calls native bash/SETA tools through TRL | Synchronous GRPO | [SETA Whitebox Space](https://huggingface.co/spaces/HuggingEnvs/data-agent-seta-whitebox-env) | + +Start with **[reproduce.md](reproduce.md)** for local/Slurm and HF Jobs instructions. See **[results.md](results.md)** for measured pass@1, checkpoint curves, difficulty breakdowns and the limits of the comparison. + +```bash +cd 04-data-agent +python reproduce.py prepare --recipe harbor-opencode --env-file .env +python reproduce.py --help +``` + +Training uses 1,000 fixed tasks (150 easy, 600 medium, 250 hard), eight rollouts per selected task, LR `3e-6`, checkpoint saves every 50 optimizer steps and independent evaluations every 100. The four-harness recipe assigns one harness to each task per pass. Task count, rollout count and optimizer steps are different quantities. + +Checkpoint evaluation is pass@1 on 250 held-out tasks: 1,000 cells through the four Harbor harnesses for either async trainer; 250 native bash/SETA cells for the sync trainer. Hub evaluation defaults to concurrency **35**. Each Space serves both training and evaluation; reserved sandbox slots protect training. Evaluation uses separate inference GPUs and never swaps the trainer's active weights. + +Exact engine token IDs, processed log probabilities and loss masks are retained. Prompt rewrites can produce multiple training rows; the async recipe consumes complete rollout groups before updating. This prevents partial-group admission but does not establish that different harnesses receive identical gradient weighting. + +## Validated environments + + +| Env | Tools | Backend | `openenv` | +|---|---|---|---| +| **blackbox-opencode** | agent-owned | `e2b / hf / daytona` | ✅ | +| **blackbox-harbor** | agent-owned | `Harbor / Daytona / E2B` | ✅ | +| **whitebox-bash** | bash, read, write, edit, grep, glob, ls, submit_solution | `e2b / daytona` | ✅ | + + +## Folder map + +| Path | Purpose | +| --- | --- | +| `reproduce.py` | Main command: prepare, upload, deploy, smoke, evaluate, train | +| `hf/configs/` | Shared recipe, immutable model/source/task pins | +| `hf/locks/` | Separate hash-locked training and environment dependencies | +| `hf/runtime/` | Shared Space/Job runtime, artifact upload, eval coordinator, TiTO audits | +| `envs/` | Standalone OpenCode and whitebox implementations; Harbor uses OpenEnv | +| `train/` | Trainer recipes, deterministic schedule, atomic rollout batching, save/resume | +| `eval/` | Evaluation client, first-graded ledger and scoring checks | +| `serve/` | Validated vLLM launcher | +| `tools/` | Shared native evaluators, capture audits and logging utilities | +| `results/` | Committed score tables, plots and dated provenance | +| `temp/` | Ignored bundles, local runs and preserved superseded material | + +The baseline cohorts and recipe history differ in infrastructure; the recorded curves are observational, not a controlled causal comparison. Do not reinterpret incomplete evaluations as scores. Credentials, private grading data, raw captures, model weights and Trackio databases stay out of Git. diff --git a/04-data-agent/envs/blackbox-harbor/README.md b/04-data-agent/envs/blackbox-harbor/README.md new file mode 100644 index 0000000..c82c07b --- /dev/null +++ b/04-data-agent/envs/blackbox-harbor/README.md @@ -0,0 +1,126 @@ +# blackbox-harbor + +The same data-analysis tasks as [`blackbox-opencode`](../blackbox-opencode), served the other way: as +a **Harbor task catalog** rather than a flat dataset. There is no environment package here — +OpenEnv's `harbor_env` already serves this shape, so this is a recipe for the CLI plus a script that +proves the rollouts come back trainable. + +## Which one should I use? + +| | [`blackbox-opencode`](../blackbox-opencode) | this (`harbor_env` + the CLI) | +| --- | --- | --- | +| dataset | [`HuggingEnvs/data-agent`](https://huggingface.co/datasets/HuggingEnvs/data-agent) — flat, one row per task | `HuggingEnvs/data-agent-harbor-{train,test,eval}` — one directory + `task.toml` per task | +| task setup | the env stages tables from a bucket | the task's own Dockerfile and healthcheck | +| agent | opencode | any of Harbor's ~30 harnesses, per rollout | +| grading | the env's verifier | the task's own verifier | +| to change a task | edit a dataset row | edit a task directory | + +Neither replaces the other. Use the flat env when the task definition is data and you want to +iterate on it quickly; use Harbor when the task ships its own container and verifier, or when you +want to train one policy against several agent harnesses. + +## Serve it + +```bash +openenv harbor serve \ + --dataset HuggingEnvs/data-agent-harbor-train \ + --llm-url http://127.0.0.1:8455/v1 \ + --model Qwen/Qwen3.5-2B \ + --port 8000 --capture-port 8100 \ + --expose gradio +``` + +`--expose` is how the **sandbox** reaches the capture proxy, which is not the same as how the trainer +reaches the env server. The sandbox is on another machine, so `direct` only works when this host is +already routable from it; `gradio` and `cloudflare` mint a public URL. + +> **Serve the engine with `--return-tokens-as-token-ids --logprobs-mode processed_logprobs`.** +> Without them the engine probes as eval tier and every rollout comes back with reward and a trace +> but nothing trainable — silently. `openenv harbor info` reports the measured tier before you start. + +One engine is not a requirement of the deployment. `--llm-url` sets a *default*; each rollout may name +its own, which is what lets a training run and an evaluation run share one server. The dataset and its +prebuilt sandbox templates are the expensive things to host; the engine is the cheap, changing part. + +## One rollout, no trainer + +```bash +openenv harbor rollout \ + --dataset HuggingEnvs/data-agent-harbor-train \ + --llm-url http://127.0.0.1:8455/v1 --model Qwen/Qwen3.5-2B \ + --harness opencode --sandbox e2b --indices 0,1,2 +``` + +This runs the whole path — engine, capture proxy, sandbox, harness, task verifier — with **no env +server in the way**. When something breaks, that halves the search space immediately: if `rollout` +works and `serve` does not, the fault is in the serving layer and nothing below it. + +## Check the rollouts are trainable + +```bash +uv run python rollout.py --server http://127.0.0.1:8000 \ + --llm-url http://127.0.0.1:8455/v1 --model Qwen/Qwen3.5-2B +``` + +It asserts the three things that are silent when wrong: `rollout_type == "train"`, every turn carries +non-empty `prompt_token_ids`, and turn *k+1*'s prompt equals turn *k*'s prompt plus its completion. +That last one is the contract — it is what makes turns link by exact token prefix instead of by a +re-rendered string. + +## Train on it + +```python +from harbor_env.harness import HarborSessionFactory + +factory = HarborSessionFactory( + server_url="http://127.0.0.1:8000", + split="HuggingEnvs/data-agent-harbor-train", + llm_url=VLLM_URL, # the same engine the trainer syncs weights into + model=MODEL, + harness="opencode", + sandbox="e2b", + agent_step_limit=10, +) +``` + +`agent_step_limit` is worth setting. AsyncGRPO packs every turn of a rollout into one training row and +each turn re-sends the whole conversation, so packed length grows with the **square** of the turn +count — an unbounded rollout can OOM the loss step while every rollout log line looks healthy. + +## The suite declares too little memory, and it OOM-kills rollouts + +**Every task in `HuggingEnvs/data-agent-harbor-{train,test,eval}` declares `cpus = 1` and +`memory_mb = 1024`.** Harbor passes both straight into `AsyncTemplate.build` +(`harbor/environments/e2b.py:192`), and `_resource_value` reads them from `task.toml` with no +environment override (`harbor/environments/base.py:315`) -- so that is the size of the sandbox the +agent actually gets. + +It is not enough. Task 0's bucket is 0.31 GB and pandas wants roughly 3-5x a file's size resident, so +1 GB is marginal at best. Measured on `0000_324_324276_qa_3` with Qwen3.5-2B: + +| | `cpus=1, memory_mb=1024` (as shipped) | `cpus=2, memory_mb=4096` | +| --- | --- | --- | +| outcome | `FAILED (NonZeroAgentExitCodeError)`, **exit 137**, 517 s | `ok`, 158 s | +| capture | 0/1 usable | **1/1 usable** | +| turns / trainable tokens | 17 / 3252, discarded | 11 / 231 | +| graded | no -- the verifier never ran | yes | + +Exit 137 is SIGKILL, i.e. the OOM killer. The expensive part is that an OOM-killed rollout files no +answer, which scores **identically to a model that could not do the task** -- so a training run reads +as a policy that never learns, with nothing in the reward to say otherwise. + +**The fix belongs in the dataset**, not here: bump `[environment] cpus` and `memory_mb` in the task +files. Patching the downloaded cache works for one run and is undone the moment the server refetches. + +## Warm the sandbox templates first + +E2B builds one template per `env_hash`, from the task's **own Dockerfile** — `docker_image` in the +task config is ignored whenever a Dockerfile exists. Concurrent first use races that build and fails +with `404 tag default`, so run a single serial rollout once before any parallel run. + +## Concurrency + +The capture proxy is a single uvicorn process and is the real ceiling: `/health` starved at ~200 +concurrent sessions and the process crashed outright at 320 (3,525 fds, 542 threads, 6.7 GB). E2B +allows 500 sandboxes per account, so capture gives out first. Size `MAX_CONCURRENT_ENVS` well under +that, and remember a training run and an eval run share one deployment. diff --git a/04-data-agent/envs/blackbox-harbor/pyproject.toml b/04-data-agent/envs/blackbox-harbor/pyproject.toml new file mode 100644 index 0000000..fac5811 --- /dev/null +++ b/04-data-agent/envs/blackbox-harbor/pyproject.toml @@ -0,0 +1,25 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# NOT AN ENVIRONMENT PACKAGE, and deliberately so. There is no server and no environment code here: +# OpenEnv's `harbor_env` already serves this task shape, so this variant is a dataset plus a recipe. +# All this file does is pin what `rollout.py` needs to talk to a running one. + +[build-system] +requires = ["setuptools>=45", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "data-agent-blackbox-harbor" +version = "0.1.0" +description = "Data-agent tasks as a Harbor catalog, served through OpenEnv's harbor_env" +requires-python = ">=3.12" +dependencies = [ + # Brings `openenv harbor` (the CLI that serves this), `harbor_env` and the capture stack. + "openenv>=0.3.1", + # The sandbox backends Harbor will drive. Listed individually rather than via `harbor[cloud]`, + # which cannot be installed at all: it pulls `langsmith[sandbox]` (websockets>=15) and + # `tensorlake` (websockets>=13,<14) together, and uv reports the pair as unsatisfiable. + "harbor[e2b,modal,daytona]>=0.20.0", + "huggingface_hub>=1.12", +] diff --git a/04-data-agent/envs/blackbox-harbor/rollout.py b/04-data-agent/envs/blackbox-harbor/rollout.py new file mode 100644 index 0000000..1230d13 --- /dev/null +++ b/04-data-agent/envs/blackbox-harbor/rollout.py @@ -0,0 +1,156 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Run one Harbor rollout and check it is actually trainable. + +WHY THIS SCRIPT EXISTS RATHER THAN A GLANCE AT THE LOG +Every failure it checks for is SILENT. An engine served without `--return-tokens-as-token-ids` +produces rollouts with a reward, a transcript and a plausible turn count that carry nothing to train +on; a consumer that re-renders prompts produces training rows that look fine and describe a +conversation the model never had. Both read as healthy until the loss step, and one of them cost two +production runs a night each. + + uv run python rollout.py --server http://127.0.0.1:8000 \\ + --llm-url http://127.0.0.1:8455/v1 --model Qwen/Qwen3.5-2B +""" + +from __future__ import annotations + +import argparse +import sys + +# `openenv.harbor.client`, not the `harbor_env` env package. That package lives in OpenEnv's +# envs/ tree and is not published to PyPI, so it is unreachable from an installed environment -- +# the same reason this project vendors its sandbox backends rather than importing opencode_env's. +# `harbor_env/__init__.py` only re-exports this class anyway. +from openenv.harbor.client import HarborEnv + + +def main() -> int: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--server", default="http://127.0.0.1:8000", help="a running harbor_env server") + p.add_argument("--llm-url", default="", help="engine for this rollout; omit for the server's") + p.add_argument("--model", default="", help="served model id") + p.add_argument("--split", default="", help="dataset; omit for the server's first") + p.add_argument("--index", type=int, default=0) + p.add_argument("--harness", default="opencode") + p.add_argument("--sandbox", default="e2b") + p.add_argument("--step-limit", type=int, default=10) + args = p.parse_args() + + env = HarborEnv(args.server) + try: + caps = env.capabilities() + llm = caps.get("llm") or {} + print(f"server {args.server}") + print(f"datasets {caps.get('datasets')}") + print(f"engine {llm.get('url') or args.llm_url} capture_level={llm.get('capture_level')}") + if llm.get("capture_level") not in ("tokens", None) and not args.llm_url: + print( + "\nWARNING: the server's default engine is EVAL TIER. Rollouts will carry a reward " + "and a trace but nothing trainable. Serve it with --return-tokens-as-token-ids " + "--logprobs-mode processed_logprobs, or name a train-tier engine with --llm-url.", + file=sys.stderr, + ) + + print(f"\nrunning {args.harness} on {args.sandbox}, task index {args.index} ...") + result = env.run_rollout( + split=args.split, + task_index=args.index, + harness=args.harness, + sandbox=args.sandbox, + llm_url=args.llm_url, + model=args.model, + agent_step_limit=args.step_limit, + ) + finally: + env.close() + + print( + f"\nreward {result.reward} rollout_type {result.rollout_type} " + f"turns {result.n_turns} roots {result.n_roots} capture_level {result.capture_level}" + ) + if result.findings: + print("findings:") + for f in result.findings[:10]: + print(f" {f}") + + return _check(result) + + +# Above this, the capture graph forks instead of realigning; it is its own `fork_threshold_tokens`. +DRIFT_FORK_THRESHOLD = 1024 + + +def _check(result) -> int: + """The three properties that are silent when wrong. Returns a process exit code.""" + failures: list[str] = [] + + # 1. Tier. `reward=None` is an UNGRADED rollout, not a zero, so it is reported separately: a + # trainer drops an ungraded rollout from the group baseline rather than treating it as a loss. + if result.rollout_type != "train": + failures.append( + f"rollout_type is {result.rollout_type!r}: the engine did not return token ids, so this " + "rollout carries nothing to train on" + ) + if result.reward is None: + print("\nnote: reward is None -- UNGRADED, not zero. The task's verifier did not run.") + + turns = [t for t in result.turns if getattr(t, "trainable", False)] + if not turns: + failures.append("no trainable turns came back") + + # 2. Every turn carries the ENGINE's tokenisation. Without it a consumer has to re-render the + # prompt, which matched the engine on 0 of 28 measured turns on Qwen3.5-4B. + missing = [i for i, t in enumerate(turns) if not getattr(t, "prompt_token_ids", None)] + if missing: + failures.append(f"turns {missing[:5]} carried no prompt_token_ids") + + # 3. CHAINING, measured rather than asserted as byte equality. + # + # The tempting check is `turn k+1's prompt == turn k's prompt + completion`, exactly. It fails + # legitimate rollouts: a harness that re-sends a `messages` list gets the engine's tokenisation + # of the RECONSTRUCTED history, and Qwen3.5's template does not round-trip -- it emits + # `\n\n\n\n` for the turn being generated and strips it from history. Measured + # live, that drifted 6-8 tokens per transition and produced 3 graph roots for 3 turns. + # + # The size of the drift is what decides realign-versus-fork, and fork is the real failure: one + # conversation becoming several short rollouts, each still training. Real opencode rollouts over + # 60 steps: drift_tokens_mean 0.26, fork_frac 0.0000, 8.19 turns into 1.00 sample. + drifts = [] + for a, b in zip(turns, turns[1:]): + want = list(a.prompt_token_ids) + list(a.completion_token_ids) + got = list(b.prompt_token_ids)[: len(want)] + common = next((j for j, (x, y) in enumerate(zip(want, got)) if x != y), min(len(want), len(got))) + drifts.append(len(want) - common) + if drifts: + print(f"chaining drift per transition: mean {sum(drifts)/len(drifts):.2f}, max {max(drifts)}") + if max(drifts) > DRIFT_FORK_THRESHOLD: + failures.append( + f"a transition drifted {max(drifts)} tokens (threshold {DRIFT_FORK_THRESHOLD}): the " + "capture graph forks rather than realigns, fragmenting one rollout into several" + ) + + if failures: + print("\nFAIL") + for f in failures: + print(f" - {f}") + return 1 + + print(f"\nOK: {len(turns)} trainable turns, engine tokenisation intact, prefixes chain.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/04-data-agent/envs/blackbox-harbor/tools/hf_token.sh b/04-data-agent/envs/blackbox-harbor/tools/hf_token.sh new file mode 100644 index 0000000..a17dd21 --- /dev/null +++ b/04-data-agent/envs/blackbox-harbor/tools/hf_token.sh @@ -0,0 +1,20 @@ +# Export HF_TOKEN for Harbor, WITHOUT printing it. +# +# Harbor's task containers read HF_TOKEN from the HOST environment -- each task.toml declares it in +# [environment.env] for the pull_bucket.py healthcheck -- and harbor refuses the rollout outright with +# "Environment variable 'HF_TOKEN' not found in host environment" when it is missing. +# +# It lives in three different places here, so try all of them: experiments/.env calls it HF_API_KEY, +# huggingface_hub keeps a cached login of its own, and HF_TOKEN may already be set. +if [ -z "${HF_TOKEN:-}" ]; then + if [ -n "${HF_API_KEY:-}" ]; then + export HF_TOKEN="$HF_API_KEY" + else + HF_TOKEN=$("$(dirname "${BASH_SOURCE[0]}")/../.venv/bin/python" -c \ + 'from huggingface_hub import get_token; print(get_token() or "")' 2>/dev/null) + export HF_TOKEN + fi +fi +# `${VAR:+present}` ALONE. Combining it with `${VAR:-...}` prints the value when it IS set, which is +# exactly the case being tested for -- that leaked a key into a transcript once already. +echo " HF_TOKEN: ${HF_TOKEN:+present}" diff --git a/04-data-agent/envs/blackbox-harbor/tools/start_server.sh b/04-data-agent/envs/blackbox-harbor/tools/start_server.sh new file mode 100755 index 0000000..6724f92 --- /dev/null +++ b/04-data-agent/envs/blackbox-harbor/tools/start_server.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Serve the data-agent Harbor catalog through OpenEnv. Credentials by NAME from experiments/.env. +set -euo pipefail +cd "$(dirname "$0")/.." +set -a; . ../../../../experiments/.env; set +a +. "$(dirname "$0")/hf_token.sh" +# `harbor serve` defaults MAX_CONCURRENT_ENVS to 4 -- a demo setting. A trainer or an eval +# holds one WebSocket SESSION per in-flight rollout, so anything above four is refused with +# "Server at capacity: 4/4 sessions active (CAPACITY_REACHED)" -- which arrives as a failed +# rollout, not as a queue, so an eval burns its whole split in seconds scoring nothing. +# Separate from the EXECUTION ceiling: the capture proxy is a single uvicorn process that +# starved /health at ~200 concurrent and crashed at 320, so stay well under that. +export MAX_CONCURRENT_ENVS="${MAX_CONCURRENT_ENVS:-128}" + +exec .venv/bin/python -m openenv.cli harbor serve \ + --dataset "${DATASET:-HuggingEnvs/data-agent-harbor-train}" \ + --llm-url "${LLM_URL:?set LLM_URL}" \ + --model "${MODEL:-Qwen/Qwen3.5-2B}" \ + --port "${PORT:-8210}" \ + --capture-port "${CAPTURE_PORT:-8311}" \ + --expose "${EXPOSE:-gradio}" diff --git a/04-data-agent/envs/blackbox-opencode/.openenvignore b/04-data-agent/envs/blackbox-opencode/.openenvignore new file mode 100644 index 0000000..d481202 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/.openenvignore @@ -0,0 +1,4 @@ +__pycache__/ +*.pyc +.venv/ +logs/ diff --git a/04-data-agent/envs/blackbox-opencode/README.md b/04-data-agent/envs/blackbox-opencode/README.md new file mode 100644 index 0000000..a92ba57 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/README.md @@ -0,0 +1,162 @@ +--- +title: Data Agent +emoji: 📊 +colorFrom: indigo +colorTo: green +sdk: docker +app_port: 8000 +--- + +# blackbox-opencode + +**Train a policy to actually analyse data.** An agent is given a question and a directory of real +tables, works in a sandbox with its own tools, and files its answer to a file. You get back the +**engine's own token ids** for every model call it made, a per-token loss mask, and the task's reward. + +## Overview + +The agent's loop belongs to the harness, not to the trainer. opencode runs inside the sandbox with +bash, read, edit and grep; it decides how many turns to take and when it is done. This environment +records that loop through a **capture proxy** the agent talks to instead of the engine, so what comes +back is what the model actually saw, token for token. + +That last part is the whole point. A trainer that re-renders each turn's prompt with +`apply_chat_template` is training on a *different* string from the one the engine scored — measured on +Qwen3.5-4B, the re-rendered prompt matched the engine on **0 of 28 turns**, which fragmented one long +conversation into many short ones and collapsed the run at its first weight update. Here the engine +returns `prompt_token_ids` and turn *k+1*'s prompt **is** the canonical tokenisation of everything +before it, so turns link by exact token prefix and nothing is ever tokenised locally. + +## Quick start + +```bash +uv sync +SPLITS=train:medium LLM_URL=http://127.0.0.1:8455/v1 MODEL=Qwen/Qwen3.5-2B ./serve.sh +``` + +`serve.sh` is the same configuration a deployment uses, so a client cannot tell the two apart. +Credentials come from the environment (`E2B_API_KEY`, `HF_TOKEN`) and are never arguments. + +```python +from data_agent_env import DataAgentEnv + +# The folder is `blackbox-opencode`, which is not a legal Python identifier; `uv sync` installs it +# under the package name `data_agent_env` via the package-dir mapping in pyproject.toml. +env = DataAgentEnv("http://127.0.0.1:8200") +print(env.capabilities()) # usable sandboxes, splits, concurrency budget +print(env.splits()) # train / test / eval, and per-difficulty variants + +result = env.run_rollout( + split="train:medium", + index=0, + llm_url="http://127.0.0.1:8000/v1", + model="Qwen/Qwen3.5-2B", +) +print(result.reward, result.answer, len(result.turns)) +print(result.turns[0].prompt_token_ids[:8]) # the engine's tokenisation, not ours +``` + +## Training with TRL + +The environment does the work; the training script stays short. It hosts vLLM and orchestrates — +everything else (dataset, sandbox, agent, grading, tokenisation, loss mask) is on this side. + +```python +from data_agent_env import DataAgentSessionFactory +from datasets import Dataset +from trl.experimental.async_grpo import AsyncGRPOTrainer, HarnessRolloutWorker + +factory = DataAgentSessionFactory( + "http://127.0.0.1:8200", split="train:medium", llm_url=VLLM_URL, model=MODEL +) +dataset = Dataset.from_list(factory.prompt_rows()) +worker = HarnessRolloutWorker(harness_session_factory=factory, harness_adapter=None, ...) +AsyncGRPOTrainer(model=MODEL, args=config, train_dataset=dataset, rollout_worker=worker).train() +``` + +`harness_adapter=None` selects the **loop-owning** path: the trainer blocks on the rollout and reads +the recorded trace, rather than driving turns itself. + +> **Serve the engine with `--return-tokens-as-token-ids --logprobs-mode processed_logprobs`.** +> Without them capture degrades to text level *silently*: every rollout looks completely normal and +> carries nothing to train on. The session mint probes for this and refuses, rather than letting a run +> spend hours discovering it. An eval run may pass `require_tokens=False` — a text-only endpoint is a +> perfectly good eval backend. + +## Splits + +Splits come from [`HuggingEnvs/data-agent`](https://huggingface.co/datasets/HuggingEnvs/data-agent), +a flat row-based dataset. A difficulty is a **named split**, `train:medium`, not a filter argument: +a filter shifts every index after it, and the index is task identity everywhere downstream. + +| split | meaning | +| --- | --- | +| `train`, `test`, `eval` | the whole split | +| `train:easy`, `train:medium`, `train:hard` | one difficulty tier of it | + +## Sandboxes + +`e2b` and `hf`, chosen per rollout (`run_rollout(sandbox=...)`), so switching is one word and needs no +redeploy. Install only the backend you will use; the import is lazy. + +The one thing that must never be hardcoded is the **home directory**: E2B runs the agent as `user` +(`/home/user`), Hugging Face sandboxes run as root (`/root`). Get it wrong and opencode writes its +provider config where it cannot read it back, so the agent starts with **no model configured** and +makes zero model calls — which arrives as a flat-zero reward that looks exactly like a policy that +cannot do the task. `sandbox/sandbox_home()` is the single place that knows. + +## Reward + +`correctness` comes from the task's own grader: exact match, then numeric within `atol`/`rtol`, then +list comparison. On top of it: + +- **Filing the answer is what counts.** An answer only stated in chat gets partial credit (0.3), never + full. A string that merely *narrates* the submission (`echo -n "2.14" > answer.txt`) gets nothing — + 42% of partial credit once went to exactly that. +- **The efficiency bonus is gated on a solve and is never a penalty.** Ungated, "make zero tool calls" + becomes the highest-scoring move available to a policy that cannot solve the task. +- **An ungraded rollout returns `reward=None`, never `0.0`.** `None` means the infrastructure failed + and the trainer drops it from the group baseline; `0.0` means the policy was wrong. Collapsing the + two silently turns a flaky sandbox into a training signal. + +## Concurrency + +Three limits stack, and the tightest is not the obvious one: + +| limit | value | why | +| --- | --- | --- | +| capture proxy | **the real ceiling** | single uvicorn process; `/health` starved at ~200 concurrent, crashed at 320 (3,525 fds, 542 threads, 6.7 GB) | +| `DATA_AGENT_MAX_CONCURRENT` | 64 | rollouts executing; a semaphore, so an over-limit rollout waits rather than failing | +| `MAX_CONCURRENT_ENVS` | 128 | WebSocket sessions; must exceed `num_generations` or rollouts queue at the door | +| E2B account | 500 | sandboxes; capture gives out first | + +Sessions release slowly, not instantly, and killing a client leaks its sessions — leftovers collide +with the next run's claim and surface as a burst of `CAPACITY_REACHED`. A training run and an +evaluation run share one deployment, and the eval must not be able to starve training out. + +## Step limits + +`agent_step_limit` is enforced **in the capture proxy**, which is the only component that sees every +model call. It is not enforced by the agent's own config: measured on opencode 1.18.30 against a fake +engine, `agent.build.steps=3`, `maxSteps=3` and no setting at all each produced **61** model calls. + +At the cap the proxy answers a terminal completion itself — the agent's loop ends cleanly and +`opencode run` exits 0 — and because that happens before capture ingests anything, no turn the model +never generated can enter the training data. + +## Environment variables + +| variable | meaning | +| --- | --- | +| `DATA_AGENT_SPLITS` | comma-separated splits to serve (default `train`) | +| `DATA_AGENT_SANDBOX` | default backend, `e2b` or `hf` | +| `DATA_AGENT_MAX_CONCURRENT` | rollouts executing at once (default 64) | +| `DATA_AGENT_CAPTURE_PORT` | port the capture proxy binds (default 8300) | +| `CAPTURE_PUBLIC_URL` | how the **sandbox** reaches that port, when it is not localhost | +| `OPENENV_LLM_URL` / `OPENENV_MODEL` | default engine; optional, since a rollout may name its own | +| `HF_TOKEN` | reads the dataset and stages each task's tables | +| `E2B_API_KEY` | required for the `e2b` backend | + +`OPENENV_LLM_URL` is optional on purpose. The dataset and its prebuilt sandbox templates are the +expensive things to host; an engine restarts every training run, and a train-tier engine and an +eval-tier one are usually both wanted against the same tasks at once. diff --git a/04-data-agent/envs/blackbox-opencode/__init__.py b/04-data-agent/envs/blackbox-opencode/__init__.py new file mode 100644 index 0000000..2cbaa4c --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/__init__.py @@ -0,0 +1,66 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Data-analysis agent tasks, with token-level capture. + +An agent gets a question and a directory of real tables, works in a sandbox with its own tools, and +files its answer to `/workdir/answer.txt`. The environment grades it and returns every model call the +agent made -- with the ENGINE'S OWN token ids, so a trainer never re-renders a prompt. + +Nothing from `server/` is exported here. A client talks to a running deployment over HTTP; importing +the server would drag a dataset, a sandbox SDK and a web framework into a trainer that needs none of +them. + +Examples: + +```python +from data_agent_env import DataAgentEnv, DataAgentSessionFactory + +# Direct use: run one rollout against a deployment. +env = DataAgentEnv("http://127.0.0.1:8200") +result = env.run_rollout(split="train:medium", index=0, llm_url=VLLM_URL, model=MODEL) +print(result.reward, len(result.turns)) + +# Training use: hand the factory to TRL's HarnessRolloutWorker. +factory = DataAgentSessionFactory( + "http://127.0.0.1:8200", split="train:medium", llm_url=VLLM_URL, model=MODEL, + sampling={"temperature": 0.8, "top_p": 1.0, "top_k": 0}, +) +``` +""" + +from .client import DataAgentEnv +from .config import DataAgentConfig +from .harness import ( + DataAgentSession, + DataAgentSessionFactory, + opencode_agent_turns, + to_trace_entries, +) +from .models import DataAgentRolloutResult, DataAgentState, DataAgentTurn +from .task import DataAgentTask + + +__all__ = [ + "DataAgentConfig", + "DataAgentEnv", + "DataAgentRolloutResult", + "DataAgentSession", + "DataAgentSessionFactory", + "DataAgentState", + "DataAgentTask", + "DataAgentTurn", + "opencode_agent_turns", + "to_trace_entries", +] diff --git a/04-data-agent/envs/blackbox-opencode/client.py b/04-data-agent/envs/blackbox-opencode/client.py new file mode 100644 index 0000000..8eca79b --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/client.py @@ -0,0 +1,236 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Client for a running data-agent environment server. + +TWO TRANSPORTS, BECAUSE THE SERVER GENUINELY HAS TWO SURFACES +`run_rollout` and `capabilities` are MCP tools, so they go through `MCPToolClient` -- the same base +every other environment client uses, and the same split `HarborEnv` makes. Task discovery +(`/splits`, `/task`, `/num_tasks`, `/task_range`) is plain HTTP routes on the env server, not MCP +tools, so those are plain HTTP. Neither half is reimplemented here. + +ONE CLIENT PER SESSION, NOT ONE SHARED +A single client shared across concurrent rollouts raises `ConcurrencyError: cannot call recv while +another coroutine is already running recv` -- the transport has no request-id correlation. With +`num_generations` rollouts in flight that is every rollout of every step, each returning unscorable: a +run that looks alive and trains on nothing. `DataAgentSessionFactory` mints one of these per session. +""" + +from __future__ import annotations + +import inspect +import json +import logging +from typing import Any + +import httpx +from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation +from openenv.core.mcp_client import MCPToolClient +from openenv.core.utils import run_async_safely + +from .models import DataAgentRolloutResult + + +logger = logging.getLogger(__name__) + +# The Task API is registered under the environment name (`/{env_name}/splits`), not at the root. +# `HarborEnv` hits `/splits` unprefixed and that is what this client was first copied from; here +# it returns 404 {"detail":"Not Found"}, which reads as a dead server rather than a wrong path. +ENV_NAME = "data_agent_env" + +# A rollout is one long call: sandbox boot, data staging, the agent's own tool loop, then grading. +# 60-600 s is normal, so the default message timeout would abort healthy rollouts. +ROLLOUT_TIMEOUT_S = 1800.0 + + +class DataAgentEnv(MCPToolClient): + """Talks to a data-agent environment server. + + Args: + base_url (`str`): + Where the server is, e.g. `http://127.0.0.1:8200`. + message_timeout_s (`float`, *optional*, defaults to `1800.0`): + Per-rollout timeout. + + Examples: + + ```python + env = DataAgentEnv("http://127.0.0.1:8200") + print(env.splits()) + task = env.get_task("train:medium", 0) + result = env.run_rollout(split="train:medium", index=0, llm_url=..., model=...) + ``` + """ + + def __init__( + self, + base_url: str, + *, + message_timeout_s: float = ROLLOUT_TIMEOUT_S, + **kwargs: Any, + ) -> None: + super().__init__( + base_url=base_url, message_timeout_s=message_timeout_s, **kwargs + ) + self._timeout = message_timeout_s + self._http = httpx.Client(base_url=base_url.rstrip("/"), timeout=120.0) + + # --- discovery (Task API, plain HTTP routes) ----------------------------------------------- + + def splits(self) -> list[dict[str, Any]]: + return self._http.get(f"/{ENV_NAME}/splits").raise_for_status().json() + + def num_tasks(self, split: str = "") -> int: + return int(self._post(f"/{ENV_NAME}/num_tasks", {"split": split})["num_tasks"]) + + def get_task(self, split: str, index: int) -> dict[str, Any]: + return self._post(f"/{ENV_NAME}/task", {"split": split, "index": index})["task"] + + def get_task_range( + self, split: str, start: int | None = None, stop: int | None = None + ) -> list[dict[str, Any]]: + body: dict[str, Any] = {"split": split} + if start is not None: + body["start"] = start + if stop is not None: + body["stop"] = stop + return self._post(f"/{ENV_NAME}/task_range", body)["tasks"] + + # --- execution (MCP tools) ------------------------------------------------------------------ + + def run_rollout( + self, + *, + split: str, + index: int, + llm_url: str, + model: str, + sandbox: str = "e2b", + agent_step_limit: int = 10, + agent_timeout_s: float = 600.0, + require_tokens: bool = True, + api_key: str = "", + sampling: dict[str, float | int] | None = None, + timeout_s: float | None = None, + ) -> DataAgentRolloutResult: + """Run one rollout to completion and return its token-level result. + + The ENGINE is chosen per call rather than baked into the deployment, so one server can serve a + training run and an evaluation run against different engines at the same time. + + `timeout_s` bounds THIS CALL from the client side and is separate from `agent_timeout_s`, + which bounds the agent inside the sandbox. `LoopOwningSession.wait_for_completion(timeout_s)` + passes it straight through, so dropping it from this signature makes every training rollout + die with `run_rollout() got an unexpected keyword argument 'timeout_s'` -- and because the + trainer then retries, each retry builds another client and another thread pool, until the + process dies with `RuntimeError: can't start new thread`. One missing parameter, two failures, + and the thread exhaustion is the one you see first. + """ + # The MCP call carries its own deadline; the tool arguments do not include it. + raw = self._call( + "run_rollout", + _timeout_s=timeout_s, + split=split, + index=index, + llm_url=llm_url, + model=model, + sandbox=sandbox, + agent_step_limit=agent_step_limit, + agent_timeout_s=agent_timeout_s, + require_tokens=require_tokens, + api_key=api_key, + **({"sampling": sampling} if sampling is not None else {}), + ) + return DataAgentRolloutResult.model_validate(_as_json(raw)) + + def capabilities(self) -> dict[str, Any]: + """Usable sandboxes, splits, concurrency budget, and whether rollouts will be trainable. + + Worth calling before dispatching a training run. An engine that cannot return token ids + produces rollouts that look completely normal and carry nothing to train on. + """ + return _as_json(self._call("capabilities")) + + # --- internals ------------------------------------------------------------------------------ + + def _call(self, name: str, _timeout_s: float | None = None, **kwargs: Any) -> Any: + """Call an MCP tool from synchronous code. + + `MCPToolClient.call_tool` cannot be used here. It is a coroutine that internally does + `await self.step(action)`, but `EnvClient.step` dispatches on execution mode and returns a + concrete `StepResult` in sync mode, so awaiting it raises `TypeError: object StepResult can't + be used in 'await' expression`. Driving `step` directly works in both modes. + """ + result = self.step( + CallToolAction(tool_name=name, arguments=kwargs), + timeout_s=_timeout_s or self._timeout, + ) + if inspect.isawaitable(result): # async mode returns an awaitable instead + result = run_async_safely(result) + + observation = result.observation + if isinstance(observation, CallToolObservation): + if observation.error is not None: + raise RuntimeError( + f"tool {name!r} failed: {observation.error.message} " + f"({observation.error.error_type.value})" + ) + return observation.result + return observation + + def _post(self, path: str, body: dict[str, Any]) -> dict[str, Any]: + return self._http.post(path, json=body).raise_for_status().json() + + def close(self) -> None: + try: + self._http.close() + finally: + super().close() + + +def _as_json(raw: Any) -> Any: + """Unwrap an MCP tool result into the JSON these tools return. + + Two layers, and both bite. The transport wraps the result as + `{"content": [{"type": "text", "text": "..."}]}` (or `structured_content`), and FastMCP wraps a + non-object return in `{"result": ...}`. These tools return a JSON *string*, so after unwrapping + there is still a string to parse. + + Getting this wrong is quiet: a plain `isinstance(raw, dict) -> return raw` hands back the + ENVELOPE, so `capabilities()["sandboxes"]` is simply absent and the caller concludes no sandbox + is usable here rather than that the response was never unwrapped. + """ + payload: Any = raw + if isinstance(payload, dict): + structured = payload.get("structured_content") or payload.get("structuredContent") + if isinstance(structured, dict) and structured: + payload = structured.get("result", structured) + else: + content = payload.get("content") + if isinstance(content, list) and content: + first = content[0] + text = first.get("text") if isinstance(first, dict) else getattr(first, "text", None) + if text is not None: + payload = text + elif isinstance(payload, (tuple, list)) and payload: + first = payload[0] + text = first.get("text") if isinstance(first, dict) else getattr(first, "text", None) + if text is not None: + payload = text + + if isinstance(payload, str): + return json.loads(payload) + if isinstance(payload, (dict, list)): + return payload + raise TypeError(f"cannot read a tool result out of {type(raw).__name__}") diff --git a/04-data-agent/envs/blackbox-opencode/config.py b/04-data-agent/envs/blackbox-opencode/config.py new file mode 100644 index 0000000..330fdc9 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/config.py @@ -0,0 +1,100 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""How the agent is configured inside the sandbox. Several of these are not preferences.""" + +from __future__ import annotations + +from dataclasses import dataclass, field + +from .sandbox import DEFAULT_IMAGE, sandbox_home + + +@dataclass +class DataAgentConfig: + """Per-rollout agent configuration. + + Attributes: + sandbox (`str`, *optional*, defaults to `"e2b"`): + Backend name. `"e2b"`, `"hf"` or `"daytona"`; see `sandbox/__init__.py`. + image (`str`, *optional*): + Container image carrying pandas/numpy/scipy and friends, which the instruction + promises. USED BY THE `hf` AND `daytona` BACKENDS -- E2B has no image parameter; it carries the + equivalent inside its prebuilt template (see `sandbox/__init__.py`). + agent_timeout_s (`float`, *optional*, defaults to `600.0`): + Wall clock for one rollout before it is abandoned and scored on whatever it filed. + agent_step_limit (`int`, *optional*, defaults to `10`): + Hard cap on MODEL CALLS, enforced host-side in the capture proxy. + max_output_tokens (`int`, *optional*, defaults to `4096`): + Output cap per model call. The hosted Task API uses4096for test and16384for train. + setup_timeout_s (`float`, *optional*, defaults to `600.0`): + Wall clock for staging the task's tables, separate from the agent's own budget. + install_timeout_s (`float`, *optional*, defaults to `300.0`): + Wall clock for installing opencode when the image does not ship it (`hf`). + disabled_tools (`list[str]`, *optional*): + Tools removed from the agent. Web access and sub-agents make a rollout unreproducible + and are off by default. + """ + + sandbox: str = "e2b" + image: str = DEFAULT_IMAGE + agent_timeout_s: float = 600.0 + agent_step_limit: int = 10 + max_output_tokens: int = 4096 + # Staging pulls a bucket that can reach gigabytes; the agent's own budget is a different + # clock and must not be spent on it. + setup_timeout_s: float = 600.0 + # nvm + a ~50 MB Node tarball + `npm i -g`. Only the E2B template skips it. + install_timeout_s: float = 300.0 + disabled_tools: list[str] = field( + default_factory=lambda: ["webfetch", "question", "task"] + ) + + @property + def home(self) -> str: + """Sandbox home for the chosen backend. Never hardcode this; see `sandbox/__init__.py`.""" + return sandbox_home(self.sandbox) + + def opencode_settings(self) -> dict: + """Settings written into the agent's config inside the sandbox. + + `permission.external_directory = "allow"` IS NOT OPTIONAL. opencode runs with its cwd at + `{home}/workdir` and treats `/home/user/input/*` and `/workdir/answer.txt` as external, so + without it the agent auto-rejects reading its own data and writing its own answer. That one + block is the entire explanation for a measured train/eval gap on the same model: 12.7 tool + calls and pass@1 0.514 under eval, against 1.3 calls and zero solves under training, with 79 + of 95 rollouts making exactly one model call before giving up. + + `agent.build.steps` is deliberately absent. It caps nothing -- measured on opencode 1.18.30 + against a fake engine, `steps=3`, `maxSteps=3` and no cap all produced 61 model calls. The + only component that sees every call is the capture proxy, which is where `agent_step_limit` + is enforced instead. + + TOOLS ARE DISABLED VIA THE `tools` MAP, NOT A `disabled_tools` LIST. opencode's schema has no + `disabled_tools` key, so an earlier revision that emitted one had every tool ENABLED while the + config read as if three were off. `task` spawns subagents, and a subagent is a separate + conversation: capture flagged `multiple_roots` on 136 of 576 rollouts (24%) naming "subagent" + first, which breaks the prefix chain the trainer needs (`rollout/fork_frac` 0.02-0.06 against + a reference's flat 0, `drift_tokens_max` 32,770). `webfetch` additionally gives a sandbox with + no egress a tool that can only fail, and every failure is error text in a tool result. + """ + return { + "permission": { + "external_directory": "allow", + "bash": "allow", + "edit": "allow", + }, + # {name: False} is the shape opencode actually reads; see the docstring. + "tools": {tool: False for tool in self.disabled_tools}, + } diff --git a/04-data-agent/envs/blackbox-opencode/curriculum.py b/04-data-agent/envs/blackbox-opencode/curriculum.py new file mode 100644 index 0000000..3d21e8f --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/curriculum.py @@ -0,0 +1,100 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Ordering the prompts by difficulty, instead of shuffling them. + +A PERMUTATION, NOT A FILTER (except where it says otherwise). Every prompt appears once, so "one +epoch" keeps its meaning and two runs under different curricula see the same work in a different +order -- which is what makes their reward curves comparable at all. + +Two schedules, named rather than expressed as a mini-language, because a curriculum you cannot read +off the name is a curriculum nobody will check: + + `sprinkle` medium throughout, hard evenly interspersed at the rate the pool implies. + Difficulty stays roughly stationary -- the right default when you want a + clean reward curve rather than a schedule. + + `warmup:` n EASY prompts first, then `sprinkle` over medium and hard. The warmup is + there because a group whose `num_generations` rollouts ALL score zero + contributes exactly zero gradient, and at the start of training that is the + likely outcome on the harder tiers. This one SUBSETS the pool: only n easy + prompts are used and the rest are dropped, so one epoch is + (n easy + all medium + all hard). + +Deterministic given `seed`. +""" + +from __future__ import annotations + +import logging +import random +from typing import Any + + +logger = logging.getLogger(__name__) + +TIERS = ("easy", "medium", "hard") + + +def apply_curriculum(rows: list[Any], tier_of: dict[int, str | None], spec: str, seed: int) -> list[Any]: + """Reorder `rows` according to `spec`. + + Args: + rows (`list`): + Prompt rows, in any order. + tier_of (`dict[int, str]`): + `id(row) -> difficulty tier`. Passed in rather than derived here so this module never has + to know how a row stores its instruction -- and so a lookup that silently returns `None` + for every row, degrading the whole thing to a plain shuffle, is the caller's bug to make + and the caller's to test. + spec (`str`): + `"sprinkle"` or `"warmup:"`. + seed (`int`): + Makes the result reproducible. + + Returns: + `list`: the reordered rows. + """ + med = [r for r in rows if tier_of.get(id(r)) == "medium"] + hard = [r for r in rows if tier_of.get(id(r)) == "hard"] + other = [r for r in rows if tier_of.get(id(r)) not in ("medium", "hard")] + rng = random.Random(seed) + rng.shuffle(med) + rng.shuffle(hard) + rng.shuffle(other) + + if spec.startswith("warmup:"): + n_easy = int(spec.split(":", 1)[1].split(",")[0]) + easy = [r for r in rows if tier_of.get(id(r)) == "easy"] + rng.shuffle(easy) + head = easy[:n_easy] + rest = apply_curriculum( + med + hard + [r for r in other if tier_of.get(id(r)) != "easy"], tier_of, "sprinkle", seed + ) + dropped = len(easy) - len(head) + if dropped: + logger.warning( + "curriculum %s: using %d easy prompts as warmup and DROPPING %d unused easy prompts; " + "one epoch is %d prompts, not %d", + spec, + len(head), + dropped, + len(head) + len(rest), + len(rows), + ) + ordered = head + rest + elif spec == "sprinkle": + ordered = med + other + if hard: + every = max(1, len(ordered) // len(hard)) + for i, h in enumerate(hard): + pos = min(len(ordered), (i + 1) * every + i) + ordered.insert(pos, h) + else: + raise ValueError(f"unknown curriculum {spec!r}; use 'sprinkle' or 'warmup:'") + + # A curriculum that grows or duplicates the pool trains some prompts twice per "epoch" and + # reports nothing. Cheap to assert, expensive to discover from a reward curve. + assert len(ordered) <= len(rows), f"curriculum GREW the pool {len(rows)} -> {len(ordered)}" + assert len(set(map(id, ordered))) == len(ordered), "curriculum duplicated a prompt" + return ordered diff --git a/04-data-agent/envs/blackbox-opencode/grader.py b/04-data-agent/envs/blackbox-opencode/grader.py new file mode 100644 index 0000000..45d8e86 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/grader.py @@ -0,0 +1,220 @@ +"""Deterministic short-answer grader v2 — NO LLM judge. + +Tiers (all offline/deterministic): + 1. Exact (case-insensitive, whitespace-collapsed) + 2. Numeric (abs/rel tolerance from ATOL/RTOL; gated to clean-number golds) + + percent<->fraction bridge (e.g. gold 96.00 == pred 0.9621) + 3. List (comma-separated): split, strip, order-insensitive; numeric-tolerant per element + (fixes 'a, b' vs 'a,b' spacing and reordering) + 4. Math-Verify (symbolic/numeric equivalence) +""" + +from __future__ import annotations + +import os +import re +import sys +from dataclasses import dataclass + +_NUMERIC_RE = re.compile(r"-?\d+(?:[.,]\d+)?(?:[eE][-+]?\d+)?") + + +@dataclass +class GradeResult: + reward: float + method: str + + +def _normalize(s: str) -> str: + return re.sub(r"\s+", " ", (s or "").strip().lower()) + + +def _to_float(s: str): + if not s: + return None + m = _NUMERIC_RE.search(str(s).replace(",", "")) + if not m: + return None + try: + return float(m.group(0)) + except ValueError: + return None + + +def _num_close(g, c, abs_tol, rel_tol) -> bool: + return abs(g - c) <= abs_tol or abs(g - c) / max(abs(g), 1e-9) <= rel_tol + + +def _is_clean_number(s: str) -> bool: + t = (s or "").strip().strip("%$").strip().replace(",", "") + return bool(_NUMERIC_RE.fullmatch(t)) + + +def _elem_match(a, b, abs_tol, rel_tol) -> bool: + if _normalize(a) == _normalize(b): + return True + fa, fb = _to_float(a), _to_float(b) + if fa is not None and fb is not None: + return _num_close(fa, fb, abs_tol, rel_tol) + return False + + +def _list_match(gold: str, cand: str, abs_tol, rel_tol) -> bool: + gl = [x.strip() for x in gold.split(",") if x.strip()] + cl = [x.strip() for x in cand.split(",") if x.strip()] + if len(gl) < 2 or len(gl) != len(cl): + return False + for gs, cs in ((gl, cl), (sorted(gl, key=str.lower), sorted(cl, key=str.lower))): + if all(_elem_match(a, b, abs_tol, rel_tol) for a, b in zip(gs, cs)): + return True + return False + + +def _math_verify_match(gold: str, candidate: str) -> bool: + try: + from math_verify import parse, verify + + return bool(verify(parse(gold), parse(candidate), timeout_seconds=5)) + except Exception: + return False + + +def grade( + gold: str, + candidate: str, + *, + question: str = "", + reward_mode: str = "", + judge: bool = True, + judge_model=None, + rel_tol: float = 1e-3, + abs_tol: float = 1e-3, +) -> GradeResult: + if not gold or candidate is None: + return GradeResult(0.0, "miss") + + # Tier 1: exact + if _normalize(gold) == _normalize(candidate): + return GradeResult(1.0, "exact") + + # Tier 2: numeric (clean-number gold) + percent/fraction bridge + if reward_mode in ("numeric", "flexible") or _is_clean_number(gold): + g, c = _to_float(gold), _to_float(candidate) + if g is not None and c is not None: + if _num_close(g, c, abs_tol, rel_tol): + return GradeResult(1.0, "numeric") + # percent<->fraction: one side is a fraction (<1), the other a percent (>=1) + if (0 < abs(c) < 1 <= abs(g)) or (0 < abs(g) < 1 <= abs(c)): + if _num_close(g, c * 100, abs_tol, rel_tol) or _num_close( + g, c / 100, abs_tol, rel_tol + ): + return GradeResult(1.0, "numeric_scaled") + + # Tier 3: list (comma-separated), order-insensitive, per-element tolerant + if reward_mode in ("list", "list_csv") or ("," in gold and "," in candidate): + if _list_match(gold, candidate, abs_tol, rel_tol): + return GradeResult(1.0, "list") + + # Tier 4: math-verify + if _math_verify_match(gold, candidate): + return GradeResult(1.0, "math_verify") + + return GradeResult(0.0, "miss") + + +def _tool_calls(): + try: + with open("/workdir/.n_tool_calls") as fh: + return int(fh.read().strip()) + except (OSError, ValueError): + pass + raw = os.environ.get("N_TOOL_CALLS") + if raw is not None: + try: + return int(str(raw).strip()) + except ValueError: + return None + return None + + +def _tool_efficiency(n): + if n is None: + return None + budget = float(os.environ.get("TOOL_BUDGET", "15") or "15") + if budget <= 0: + return None + return max(0.0, min(1.0, 1.0 - n / budget)) + + +def _tols(): + def _f(name): + try: + return float(os.environ.get(name, "") or 1e-3) + except ValueError: + return 1e-3 + + return _f("ATOL"), _f("RTOL") + + +def main_cli() -> int: + gold = (os.environ.get("EXPECTED_ANSWER") or "").strip() + question = (os.environ.get("QUESTION") or "").strip() + candidate = sys.stdin.read().strip() + at, rt = _tols() + r = grade( + gold, + candidate, + question=question, + reward_mode=os.environ.get("REWARD_MODE", "") or "", + abs_tol=at, + rel_tol=rt, + ) + print(f"{r.reward:.1f}") + print( + f"[grader] gold={gold!r} pred={candidate[:80]!r} reward={r.reward} method={r.method}", + file=sys.stderr, + ) + return 0 + + +def main_json() -> int: + import json + + gold = (os.environ.get("EXPECTED_ANSWER") or "").strip() + question = (os.environ.get("QUESTION") or "").strip() + candidate = sys.stdin.read().strip() + if not candidate: + print( + json.dumps({"correctness": 0.0, "submission": 0.0, "tool_efficiency": 0.0}) + ) + return 0 + at, rt = _tols() + r = grade( + gold, + candidate, + question=question, + reward_mode=os.environ.get("REWARD_MODE", "") or "", + abs_tol=at, + rel_tol=rt, + ) + n = _tool_calls() + print( + json.dumps( + { + "correctness": float(r.reward), + "submission": 1.0, + "tool_efficiency": _tool_efficiency(n), + } + ) + ) + print( + f"[grader] gold={gold!r} pred={candidate[:80]!r} correctness={r.reward} method={r.method}", + file=sys.stderr, + ) + return 0 + + +if __name__ == "__main__": + if "--json" in sys.argv[1:]: + raise SystemExit(main_json()) + raise SystemExit(main_cli()) diff --git a/04-data-agent/envs/blackbox-opencode/harness.py b/04-data-agent/envs/blackbox-opencode/harness.py new file mode 100644 index 0000000..6c99260 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/harness.py @@ -0,0 +1,388 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The client side: what a trainer imports. + +A trainer gets a `ResourceSessionFactory`. It calls `create(prompt)`, blocks on +`wait_for_completion()`, reads `fetch_proxy_trace()`, and trains. It never learns which sandbox ran, +which harness drove, how the answer was graded, or that an HTTP server was involved -- those are the +environment's business, and keeping them there is what makes the training script short. +""" + +from __future__ import annotations + +import logging +from typing import Any + +from openenv.core.env_server.mcp_types import Tool +from openenv.core.harness import ( + ResourceSession, + ResourceSessionFactory, + ToolResult, + TraceEntry, + VerifyResult, +) + +from .models import DataAgentRolloutResult +from .task import instruction_id +from .tasks import index_of_instruction + + +logger = logging.getLogger(__name__) + + +def to_trace_entries(result: DataAgentRolloutResult) -> list[TraceEntry]: + """`DataAgentRolloutResult` -> `list[TraceEntry]`, carrying the engine's own tokenization. + + Every entry includes `prompt_token_ids` and `loss_mask`, so the consumer never re-renders a + prompt. `request_messages` is what makes this possible at all: without it the token fields say + what was produced but not what produced them. + + Turns that are not trainable are dropped rather than emitted with a zero mask. They carry no + gradient either way, and emitting them would inflate the turn count that the efficiency bonus + and every turn-based diagnostic read. + """ + entries: list[TraceEntry] = [] + for turn in result.turns: + if not turn.trainable or not turn.completion_token_ids: + continue + from openenv.core.harness.capture.validate import validate_training_turn + validate_training_turn(turn.prompt_token_ids, turn.completion_token_ids, + turn.per_token_logps, turn.loss_mask) + entries.append( + { + "request": { + "messages": list(turn.request_messages), + "tools": turn.request_tools, + }, + "response": { + "choices": [ + { + "message": { + "role": "assistant", + "content": turn.text, + "tool_calls": turn.tool_calls or None, + }, + "finish_reason": turn.finish_reason, + } + ] + }, + "prompt_token_ids": list(turn.prompt_token_ids), + "completion_token_ids": list(turn.completion_token_ids), + "per_token_logps": list(turn.per_token_logps), + "loss_mask": list(turn.loss_mask), + "reward": result.reward, + "metadata": {**result.metadata, **turn.capture_metadata}, + } + ) + return entries + + + +def opencode_agent_turns(trace: list[TraceEntry]) -> list[TraceEntry]: + """`agent_turn_fn` for TRL: keep only the REAL agent turns. + + opencode fires extra model calls for its own bookkeeping -- a conversation-title generator and a + context summarizer -- either without tools or under a different system prompt. They are a + different task from the one being trained, and leaving them in the trace is not a cosmetic + problem: measured on a 400-step run against a reference that filtered them, + + * each aux call has its own prompt, so it does not extend the previous turn. The prefix chain + breaks -- `rollout/fork_frac` 0.02-0.06 against the reference's flat 0, and + `rollout/drift_tokens_max` reached 32,770, a whole rollout's worth; + * one rollout therefore stops being one sample (`rollout/samples_per_rollout` 1.04-1.31 vs + exactly 1.0), which doubles `sample/forwarded_tokens_mean` and roughly doubles `grad_norm` + at an unchanged learning rate; + * worst, the aux tokens get TRAINED, carrying the data-analysis task's advantage. The policy is + optimised to write titles and summaries. Reward fell 0.46 -> 0.00 while entropy rose + 0.195 -> 0.708, from a run whose step-0 reward was HIGHER than the reference's; + * and the title call fires LAST, so `entries[-1]` is an aux call and every metric read off it + -- `tools/failure_frequency` above all -- describes the wrong conversation. + + The agent loop reuses ONE tool-enabled system prompt, so anchor on the first tool-enabled turn's + system prompt and keep only the entries that match it. + """ + + def system_of(messages: list[dict[str, Any]]) -> Any: + return next((m.get("content") for m in messages if m.get("role") == "system"), None) + + primary = None + for entry in trace: + request = entry.get("request") or {} + if request.get("messages") and request.get("tools"): + primary = system_of(request["messages"]) + break + return [ + entry + for entry in trace + if (request := entry.get("request") or {}).get("messages") + and request.get("tools") + and system_of(request["messages"]) == primary + ] + + +class DataAgentSession(ResourceSession): + """One rollout. The agent owns its loop; this blocks on it and reads back what it did. + + Satisfies `LoopOwningSession` structurally via `wait_for_completion` and `fetch_proxy_trace`. + The tool methods exist because `ResourceSession` requires them, but nothing calls them in + loop-owning mode -- the agent inside the sandbox has its own tools. + """ + + def __init__( + self, + client: Any, + split: str, + index: int, + instruction: str, + **rollout_kwargs: Any, + ): + self._client = client + self._split = split + self._index = index + self._instruction = instruction + self._rollout_kwargs = rollout_kwargs + self._result: DataAgentRolloutResult | None = None + + # --- ResourceSession --------------------------------------------------------------------- + + def initial_messages(self) -> list[dict[str, Any]]: + return [{"role": "user", "content": self._instruction}] + + def list_tools(self) -> list[Tool]: + return [] # the agent's tools live inside the sandbox, not on this side of the wire + + def call_tool(self, name: str, arguments: dict[str, Any]) -> ToolResult: + raise NotImplementedError("loop-owning session: the agent calls its own tools") + + def verify( + self, transcript: list[dict[str, Any]], final_state: Any | None = None + ) -> VerifyResult: + """The environment's reward, forwarded. Never synthesised here. + + `env_reward=None` means the rollout could not be graded, and the trainer drops it from the + group baseline. Coercing it to 0.0 would silently turn an infrastructure failure into a + training signal that says the policy was wrong. + """ + if self._result is None: + return VerifyResult(env_reward=None, done=True) + return VerifyResult( + env_reward=self._result.reward, + done=True, + metrics={ + "correctness": self._result.correctness, + "n_tool_calls": self._result.n_tool_calls, + "timed_out": self._result.timed_out, + }, + artifacts={"answer": self._result.answer, **self._result.metadata}, + ) + + def close(self) -> None: + closer = getattr(self._client, "close", None) + if closer is not None: + try: + closer() + except Exception: + logger.warning( + "closing data-agent session client failed", exc_info=True + ) + + # --- LoopOwningSession ------------------------------------------------------------------- + + def wait_for_completion(self, timeout_s: float | None = None) -> int: + """Run the rollout to completion on the server and keep the result. + + This is one long call rather than a poll loop: the whole rollout -- sandbox boot, data + staging, the agent's own tool loop, grading -- happens server-side, and there is no + meaningful intermediate state to observe. + """ + self._result = self._client.run_rollout( + split=self._split, + index=self._index, + timeout_s=timeout_s, + **self._rollout_kwargs, + ) + return 0 + + def fetch_proxy_trace(self) -> list[TraceEntry]: + if self._result is None: + return [] + if self._result.rollout_type != "train": + # Better to say so than to hand back rows of zeros. The engine was not serving token ids, + # so nothing here can be trained on. + raise ValueError( + "this rollout is eval-tier: the engine did not return token ids, so it produced no " + "trainable turns. Serve with --return-tokens-as-token-ids --logprobs-mode " + "processed_logprobs." + ) + return to_trace_entries(self._result) + + +class DataAgentSessionFactory(ResourceSessionFactory[DataAgentSession]): + """Hands the trainer one session per rollout. + + Args: + server (`str`): + Base URL of a running data-agent environment server. + split (`str`, *optional*, defaults to `"train"`): + Which split to draw from, e.g. `"train"` or `"train:medium"`. + llm_url (`str`): + The engine the agent should call. Chosen per rollout, so one server serves training and + evaluation against different engines at once. + model (`str`): + Served model id. + sandbox (`str`, *optional*, defaults to `"e2b"`): + Backend name; `"e2b"`, `"hf"` or `"daytona"`. + sampling (`dict`, *optional*): + Explicit trainer policy, e.g. `{"temperature": 0.8, "top_p": 1.0, "top_k": 0}`. + The existing capture registry enforces full-vocabulary sampling and records the + submitted policy on each turn. Pass the same temperature used by the trainer. + curriculum (`str`, *optional*): + Order the prompts by difficulty instead of shuffling: `"sprinkle"` or `"warmup:"`. + Empty keeps the split's own order. See `curriculum.py`. + seed (`int`, *optional*, defaults to `0`): + Makes the curriculum reproducible. + """ + + def __init__( + self, + server: str, + *, + split: str = "train", + llm_url: str, + model: str, + sandbox: str = "e2b", + api_key: str = "", + sampling: dict[str, float | int] | None = None, + agent_step_limit: int = 10, + agent_timeout_s: float = 600.0, + curriculum: str = "", + seed: int = 0, + ): + self._server = server.rstrip("/") + self._split = split + self._curriculum = curriculum + self._seed = seed + self._rollout_kwargs = { + "llm_url": llm_url, + "model": model, + "sandbox": sandbox, + "api_key": api_key, + "sampling": sampling, + "agent_step_limit": agent_step_limit, + "agent_timeout_s": agent_timeout_s, + } + + def _new_client(self): + """A CLIENT PER SESSION. + + One shared MCP client across concurrent rollouts raises `ConcurrencyError: cannot call recv + while another coroutine is already running recv` -- the transport has no request-id + correlation. With `num_generations` rollouts in flight that is every rollout of every step, + each returning unscorable: a run that looks alive and trains on nothing. + """ + from .client import DataAgentEnv + + return DataAgentEnv(base_url=self._server) + + def prompt_rows(self) -> list[dict[str, Any]]: + """Rows for the trainer's dataset: one prompt per task in the configured split. + + The trainer forwards only the prompt, so `create()` recovers the index by hashing the + instruction back. That round trip is why this returns instructions rather than indices. + + ORDER IS PART OF THE RECIPE, not a detail. The trainer walks this list in order, so the + curriculum has to be applied HERE -- once, where the tiers are still known. Requesting + `curriculum="warmup:125"` puts 125 easy prompts first and then sprinkles hard through medium; + without it the run sees a shuffled mix from step 0, and a group whose `num_generations` + rollouts all score zero contributes exactly zero gradient. On the harder tiers, early on, that + is the likely outcome. + """ + client = self._new_client() + try: + tasks = client.get_task_range(self._split) + finally: + client.close() + # DEDUPLICATED BY INSTRUCTION, and this is a correctness fix rather than tidying. + # + # The trainer forwards only the prompt, so `create()` resolves an instruction back to ONE task + # index. When two tasks share an instruction, every rollout for it is graded against whichever + # gold that lookup returns. Measured on `train`: 5000 tasks carry 4940 distinct instructions, + # 48 instructions are shared by 108 tasks, and 15 of those 48 have CONFLICTING gold answers -- + # so without this, a slice of the run is scored against an answer to a different question and + # nothing anywhere says so. + # + # Keeping the first occurrence also reproduces the pool the published +0.2028 run used, which + # was 4940 prompts: its dataset builder deduplicated the same way. + seen: set[str] = set() + rows, kept_tasks = [], [] + for task in tasks: + instruction = task["instruction"] + key = instruction_id(instruction) + if key in seen: + continue + seen.add(key) + rows.append({"prompt": [{"role": "user", "content": instruction}]}) + kept_tasks.append(task) + if len(kept_tasks) != len(tasks): + logger.warning( + "%d of %d tasks in %s share an instruction with an earlier one and were dropped; " + "the prompt cannot distinguish them, so a rollout could only be graded against one " + "of their golds", + len(tasks) - len(kept_tasks), + len(tasks), + self._split, + ) + tasks = kept_tasks + if not self._curriculum: + return rows + + from .curriculum import apply_curriculum + + # Keyed by id(row), so the tier travels with the row rather than being re-derived from the + # instruction later. Deriving it from the raw instruction string is how the original version + # of this silently degraded to a plain shuffle: the task index is keyed by a HASH of the + # instruction, so a raw-string lookup returns None for every row and the curriculum reports + # success while doing nothing. + tier_of = {id(row): task.get("difficulty_tier") for row, task in zip(rows, tasks)} + return apply_curriculum(rows, tier_of, self._curriculum, self._seed) + + def create( + self, task: Any, seed: int | None = None, episode_id: str | None = None + ) -> DataAgentSession: + instruction = _instruction_of(task) + index = index_of_instruction(self._split, instruction) + if index is None: + raise ValueError( + "could not map this prompt back to a task in split " + f"{self._split!r}. The dataset the trainer was built from and the split this factory " + "serves have diverged." + ) + return DataAgentSession( + self._new_client(), self._split, index, instruction, **self._rollout_kwargs + ) + + +def _instruction_of(task: Any) -> str: + """The instruction text, from whatever shape the trainer passed.""" + if isinstance(task, str): + return task + if isinstance(task, list) and task: # a message list + return task[-1].get("content", "") + if isinstance(task, dict): + prompt = task.get("prompt") + if isinstance(prompt, list) and prompt: + return prompt[-1].get("content", "") + return task.get("instruction", "") + raise TypeError(f"cannot read an instruction out of {type(task).__name__}") diff --git a/04-data-agent/envs/blackbox-opencode/models.py b/04-data-agent/envs/blackbox-opencode/models.py new file mode 100644 index 0000000..2982161 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/models.py @@ -0,0 +1,118 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Wire types for the data-agent environment.""" + +from __future__ import annotations + +from typing import Any + +from openenv.core.env_server.types import State +from pydantic import BaseModel, Field + + +class DataAgentTurn(BaseModel): + """One model call, with the tokens the ENGINE produced. + + `prompt_token_ids` is the engine's own tokenization of everything before this turn, not a local + re-render. That distinction is the reason this environment can be trained on at all: a + re-rendered prompt matched the engine on 0 of 28 measured turns on Qwen3.5-4B, and training on + the difference collapsed a run at its first weight update. + + Attributes: + turn (`int`): + Position in the rollout. + prompt_token_ids (`list[int]`): + Engine tokenization of the conversation before this turn. + completion_token_ids (`list[int]`): + Tokens the model sampled. + per_token_logps (`list[float]`): + Generator logprobs, aligned with `completion_token_ids`. + trainable (`bool`): + Whether this turn may be trained on. False when its logprobs were rejected on ingest: + the tokens remain as context but must not carry gradient. + request_messages (`list[dict]`): + The conversation sent upstream, for inspection and for partial-credit grading. + text (`str`): + The assistant's text content. + tool_calls (`list[dict]`): + Tool calls the assistant emitted. + finish_reason (`str`, *optional*): + Why generation stopped. + n_tools (`int`): + Tools offered on this call. + """ + + turn: int = 0 + prompt_token_ids: list[int] = Field(default_factory=list) + completion_token_ids: list[int] = Field(default_factory=list) + per_token_logps: list[float] = Field(default_factory=list) + loss_mask: list[int] = Field(default_factory=list) + capture_metadata: dict[str, Any] = Field(default_factory=dict) + trainable: bool = False + request_messages: list[dict[str, Any]] = Field(default_factory=list) + request_tools: list[dict[str, Any]] | None = None + text: str = "" + tool_calls: list[dict[str, Any]] = Field(default_factory=list) + finish_reason: str | None = None + n_tools: int = 0 + + +class DataAgentRolloutResult(BaseModel): + """Everything one rollout produced: the turns, the grade, and how it was graded. + + Attributes: + rollout_type (`str`): + `"train"` when the engine returned token ids, `"eval"` otherwise. An eval rollout yields + no trainable turns rather than rows of zeros. + reward (`float`, *optional*): + The training reward. `None` means UNGRADED and must not be read as 0.0 -- an ungraded + rollout is dropped from the group baseline, not counted as a failure. + correctness (`float`, *optional*): + The graded score before the efficiency bonus. + answer (`str`, *optional*): + What the agent submitted. + answer_source (`str`): + `"file"`, `"chat"` or `"none"`. + graded_by (`str`): + Which comparison tier matched. + turns (`list[DataAgentTurn]`): + Per-turn token records. + n_tool_calls (`int`): + Tool calls made, used for the efficiency bonus. + timed_out (`bool`): + Whether the agent hit its wall clock. + metadata (`dict`): + Task id, tier, sandbox, session id. + """ + + rollout_type: str = "eval" + reward: float | None = None + correctness: float | None = None + answer: str | None = None + answer_source: str = "none" + graded_by: str = "" + turns: list[DataAgentTurn] = Field(default_factory=list) + n_tool_calls: int = 0 + timed_out: bool = False + metadata: dict[str, Any] = Field(default_factory=dict) + + +class DataAgentState(State): + """Server state: which task is selected, and what the engine can do.""" + + split: str | None = None + index: int | None = None + task_id: str | None = None + rollout_type: str = "eval" diff --git a/04-data-agent/envs/blackbox-opencode/openenv.yaml b/04-data-agent/envs/blackbox-opencode/openenv.yaml new file mode 100644 index 0000000..bb734e2 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/openenv.yaml @@ -0,0 +1,6 @@ +spec_version: 1 +name: data_agent_env +type: space +runtime: fastapi +app: server.app:app +port: 8000 diff --git a/04-data-agent/envs/blackbox-opencode/pyproject.toml b/04-data-agent/envs/blackbox-opencode/pyproject.toml new file mode 100644 index 0000000..c1e30f4 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/pyproject.toml @@ -0,0 +1,55 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[build-system] +requires = ["setuptools>=45", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "openenv-data-agent-env" +version = "0.1.0" +description = "Data-analysis agent tasks: an agent reads real tables in a sandbox and files an answer" +# 3.12, not 3.10: the capture stack and `openenv.core.sandbox` both require it. +requires-python = ">=3.12" +dependencies = [ + # Brings the capture proxy and the harness contract. The sandbox backends are vendored into + # sandbox/ rather than imported, because openenv-opencode-env is not published to PyPI. + "openenv>=0.3.1", + "datasets>=2.19", + "huggingface_hub>=1.12", + "fastapi>=0.115", + "fastmcp>=2.0", + "httpx>=0.27", + "pydantic>=2.0", + "uvicorn[standard]>=0.24", +] + +# Sandbox backends are OPTIONAL and independent: a deployment installs only the one it will use, +# and `sandbox/__init__.py` imports each lazily, so a missing SDK for one never breaks the other. +# The backend code itself is VENDORED into sandbox/ (see its docstring) -- these are just its SDKs. +[project.optional-dependencies] +e2b = ["e2b>=1.0.0"] +# `huggingface_hub.Sandbox` ships in 1.22; there is no `[sandbox]` extra, and asking for one makes +# uv warn and then silently resolve without it. +hf = ["huggingface_hub>=1.22"] +daytona = ["daytona>=0.203.0"] +dev = ["pytest>=9.0.3", "pytest-cov>=4.0.0"] + +[project.scripts] +server = "data_agent_env.server.app:main" + +[tool.setuptools] +include-package-data = true +packages = ["data_agent_env", "data_agent_env.server", "data_agent_env.sandbox"] +package-dir = { "data_agent_env" = ".", "data_agent_env.server" = "server", "data_agent_env.sandbox" = "sandbox" } diff --git a/04-data-agent/envs/blackbox-opencode/reward.py b/04-data-agent/envs/blackbox-opencode/reward.py new file mode 100644 index 0000000..fe61c21 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/reward.py @@ -0,0 +1,117 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The training reward: correctness, plus a small efficiency bonus that is gated on a solve. + +The SHAPE of this function matters far more than its constants, and it was arrived at by walking a +policy into an absorbing state twice. Do not simplify it without reading `data_agent_reward`. +""" + +from __future__ import annotations + +import logging +import os + + +logger = logging.getLogger(__name__) + +# Credit for stating the right value in chat without filing it. Filing is worth 3.3x chat, so the +# gradient points at the contract; chat is worth infinitely more than silence, so there is a gradient +# at all. Measured: this lifts the fraction of rollouts with a non-zero reward from ~0.6% to ~17.5%, +# which is what makes groups non-uniform enough to learn from. +CHAT_ANSWER_CREDIT = float(os.environ.get("CHAT_ANSWER_CREDIT", "0.3")) + +# The dataset's own default (its `grader.py` `_tool_efficiency`). Kept rather than re-chosen so this +# efficiency term means the same thing the benchmark's does. +TOOL_BUDGET = float(os.environ.get("TOOL_BUDGET", "15")) + +# Weight on the efficiency BONUS. Deliberately small, and deliberately a bonus rather than a penalty. +EFFICIENCY_WEIGHT = float(os.environ.get("EFFICIENCY_WEIGHT", "0.1")) + + +def tool_efficiency( + n_tool_calls: int | None, budget: float = TOOL_BUDGET +) -> float | None: + """`1 - n/budget`, clamped to [0, 1]. The dataset's own definition.""" + if n_tool_calls is None or budget <= 0: + return None + return max(0.0, min(1.0, 1.0 - n_tool_calls / budget)) + + +def data_agent_reward( + correctness: float | None, n_tool_calls: int | None +) -> float | None: + """`correctness + EFFICIENCY_WEIGHT * tool_efficiency`, with the bonus gated on a solve. + + THE SHAPE IS CHOSEN AGAINST A FAILURE THAT ACTUALLY HAPPENED, TWICE. + + The first version subtracted a penalty of up to 0.5 for tool calls beyond a budget of 30. On + Qwen3.5-4B that inverted the objective: opencode emits several tool calls per assistant message + (~6.5/iteration measured, against ~0.3-0.8 on dense models), so the budget was crossed in about + five turns, the penalty saturated, task reward went to 0, and the only remaining gradient was + "use fewer tools". The policy took the cheapest route and stopped calling tools altogether -- and + because `train_turn_fn=has_tool_call` reinforces only action turns, a policy that takes no + actions produces no training rows, hence no gradient, and cannot climb back out. An absorbing + state, reached by following the reward exactly as written. + + Reshaping the penalty into a BONUS was not enough. Ungated, the table read + + wrong + no tools 0.0 + 0.10 = 0.10 + wrong + wasteful 0.0 + 0.00 = 0.00 + + and 0.10 is tied for the best outcome among failures while strictly dominating every real + attempt that fails. For a policy whose solve rate is a few percent, correctness is effectively + unreachable, so 0.10-by-inaction IS the achievable maximum. Measured, jobs 72452 (Qwen3.5-2B) and + 72473 (Qwen3-4B-Instruct-2507): all 495 empty groups scored reward_mean=0.1000 with + reward_std=0.0000 -- every generation making zero tool calls -- and both runs froze at steps 7 + and 10 of 100, never recovering across 294 and 201 consecutive empty groups. + + GATED, the table is + + correct + wasteful 1.0 + 0.00 = 1.00 still beats everything incorrect + correct + efficient 1.0 + 0.10 = 1.10 efficiency breaks ties among CORRECT runs only + wrong + anything 0.0 + 0.00 = 0.00 no reward for inaction + + Cost of the gate, stated plainly: a group of eight failures has zero reward variance and yields + no gradient. That does not stall the trainer -- zero variance gives zero advantages, which is + harmless -- but throughput depends on groups containing both outcomes. + + Args: + correctness (`float`, *optional*): + The graded score, or `None` if the rollout could not be graded at all. + n_tool_calls (`int`, *optional*): + Tool calls the agent made, used only for the efficiency bonus. + + Returns: + `float` or `None`: The training reward. `None` is NOT zero -- an ungraded rollout is dropped + from the group baseline rather than averaged in as a failure. + """ + if correctness is None: + return None + correctness = float(correctness) + # LOUD, because this collapse was silent for 28 minutes across 495 groups while every other gate + # stayed green (capture_level=tokens, no OOM, reward_std>0 at the step level). A rollout that made + # zero tool calls yields no trainable turns under `has_tool_call`, so a sustained run of them + # starves the trainer with no error raised anywhere. + if n_tool_calls == 0: + logger.warning( + "rollout made ZERO tool calls -> no trainable turns under has_tool_call. A sustained run " + "of these wedges the trainer (jobs 72452/72473 froze exactly this way)." + ) + eff = tool_efficiency(n_tool_calls) + # THE GATE. Ungated, `1 - n/15` makes zero tool calls the highest-scoring behaviour available to + # a policy that cannot solve the task. + if eff is None or correctness < 1.0: + return correctness + return correctness + EFFICIENCY_WEIGHT * eff diff --git a/04-data-agent/envs/blackbox-opencode/rollout.py b/04-data-agent/envs/blackbox-opencode/rollout.py new file mode 100644 index 0000000..94e45f1 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/rollout.py @@ -0,0 +1,145 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""One rollout against a running data-agent environment, with the checks that matter. + + ./serve.sh & + uv run python rollout.py --llm-url http://127.0.0.1:8455/v1 --model Qwen/Qwen3.5-2B + +WHY THIS CHECKS RATHER THAN JUST PRINTS +Every failure it looks for is SILENT. An engine served without `--return-tokens-as-token-ids` +produces rollouts with a reward, a transcript and a plausible turn count that carry nothing to train +on. A consumer that re-renders prompts produces training rows that look fine and describe a +conversation the model never had -- measured on Qwen3.5-4B, a re-rendered prompt matched the engine +on 0 of 28 turns. Both read as healthy right up until the first weight update. +""" + +from __future__ import annotations + +import argparse +import sys + +from data_agent_env import DataAgentEnv + + +def main() -> int: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--server", default="http://127.0.0.1:8200") + p.add_argument("--llm-url", default="", help="engine for this rollout; omit for the server's") + p.add_argument("--model", default="") + p.add_argument("--split", default="train") + p.add_argument("--index", type=int, default=0) + p.add_argument("--sandbox", default="e2b", choices=["e2b", "hf"]) + p.add_argument("--step-limit", type=int, default=10) + p.add_argument( + "--eval", + action="store_true", + help="allow a text-only engine. Rollouts come back scored but not trainable.", + ) + args = p.parse_args() + + env = DataAgentEnv(args.server) + try: + caps = env.capabilities() + usable = [n for n, ok in (caps.get("sandboxes") or {}).get("usable", {}).items() if ok] + print(f"server {args.server}") + print(f"splits {[s.get('name') for s in caps.get('splits') or []]}") + print(f"sandboxes usable here: {usable or 'NONE'}") + print(f"concurrency {(caps.get('concurrency') or {}).get('max_concurrent_rollouts')}") + if args.sandbox not in usable: + print( + f"\n{args.sandbox!r} is not usable here -- its SDK or its credential is missing. " + "A rollout would fail after paying for a sandbox that could never have started.", + file=sys.stderr, + ) + return 2 + + task = env.get_task(args.split, args.index) + print(f"\ntask {args.split}[{args.index}] tier={task.get('difficulty_tier')}") + print(f" {str(task.get('instruction'))[:160]}...") + + print(f"\nrunning opencode in a {args.sandbox} sandbox ...") + result = env.run_rollout( + split=args.split, + index=args.index, + llm_url=args.llm_url, + model=args.model, + sandbox=args.sandbox, + agent_step_limit=args.step_limit, + require_tokens=not args.eval, + ) + finally: + env.close() + + print( + f"\nreward {result.reward} correctness {result.correctness} " + f"answer {result.answer!r} (from {result.answer_source}, graded_by {result.graded_by})" + ) + print(f"turns {len(result.turns)} tool calls {result.n_tool_calls} type {result.rollout_type}") + return _check(result, want_trainable=not args.eval) + + +def _check(result, *, want_trainable: bool) -> int: + failures: list[str] = [] + + # `reward=None` is UNGRADED, not zero. Reported, never counted as a loss: a trainer drops an + # ungraded rollout from the group baseline, whereas a zero says the policy was wrong. Collapsing + # the two turns a flaky sandbox into a training signal. + if result.reward is None: + print(f"\nUNGRADED: {result.metadata.get('error', 'the verifier did not run')}") + return 1 + + if not want_trainable: + print("\nOK (eval): scored, not trainable -- as asked for with --eval.") + return 0 + + if result.rollout_type != "train": + failures.append( + "rollout_type is 'eval': the engine returned no token ids. Serve it with " + "--return-tokens-as-token-ids --logprobs-mode processed_logprobs." + ) + turns = [t for t in result.turns if t.trainable] + if not turns: + failures.append("no trainable turns came back") + if any(not t.prompt_token_ids for t in turns): + failures.append("a turn carried no prompt_token_ids -- a consumer would have to re-render it") + + # CHAINING. Capture already decided this; do not re-derive it. + # + # The tempting check is `turn k+1's prompt == turn k's prompt + completion`, exactly. That is + # wrong as a pass/fail and it fails legitimate rollouts: a harness that re-sends a `messages` + # list gets the engine's tokenisation of the RECONSTRUCTED history, and Qwen3.5's template does + # not round-trip -- it emits `\n\n\n\n` for the turn being generated and strips + # it from history. Measured live, that drifts 6-8 tokens per transition, which is small, + # legitimate, and indistinguishable by eye from the real failure. + # + # Capture reports the real answer directly, as `per_turn_capture_only`: "every turn is its own + # root ... this harness re-renders its prompt rather than appending, so rows are single-turn. + # Tokens and logprobs are exact; multi-turn credit assignment is not available." That is a + # structural fact about the graph, not an inference from token counts. + # + # It is a WARNING, not a failure. The rollout still trains -- just as N single-turn rows rather + # than one multi-turn row. Real opencode rollouts do chain: 60 steps measured at + # drift_tokens_mean 0.26, fork_frac 0.0000, 8.19 turns collapsing into 1.00 sample. + findings = result.metadata.get("capture_findings") or [] + for f in findings: + print(f"capture {f}") + if any("per_turn_capture_only" in f for f in findings): + print( + "\n NOTE: turns did not chain, so this rollout trains as single-turn rows. Expected for " + "a harness that re-renders its prompt; unexpected for opencode, which appends." + ) + + if failures: + print("\nFAIL") + for f in failures: + print(f" - {f}") + return 1 + + total = sum(len(t.completion_token_ids) for t in turns) + print(f"\nOK: {len(turns)} trainable turns, {total} completion tokens, prefixes chain exactly.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/04-data-agent/envs/blackbox-opencode/sandbox/__init__.py b/04-data-agent/envs/blackbox-opencode/sandbox/__init__.py new file mode 100644 index 0000000..08cebf5 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/sandbox/__init__.py @@ -0,0 +1,148 @@ +"""Swappable sandbox backends. Pick one per rollout; nothing else in this env knows which. + +VENDORED, ON PURPOSE. `base.py`, `e2b.py` and `hf.py` are copies of +`OpenEnv/envs/opencode_env/sandbox/` at the revision pinned in this project's README. They are not +imported from there, for two reasons that both matter: + + * `openenv-opencode-env` is NOT published to PyPI -- only `openenv` is -- so `opencode_env.sandbox` + is not reachable from an installed environment at all; + * an environment in this repo has to stand on its own. Someone should be able to copy this + directory, `uv sync`, and get a working env without also cloning OpenEnv. + +The cost is a copy that can drift, and the honest answer to that is that this is a snapshot, not a +dependency. The three files have no upward imports, so they lift cleanly. + +THE SANDBOX HOME IS THE WHOLE REASON `sandbox_home` EXISTS +E2B runs the agent as `user` with a home of `/home/user`; Hugging Face sandboxes run as root with +`/root`. Get it wrong and opencode writes its provider config where it cannot read it back, so the +agent starts with NO MODEL CONFIGURED and makes zero model calls -- which arrives as a flat-zero +reward that looks exactly like a policy that cannot do the task. This is the single place that knows; +every other module asks rather than assuming. +""" + +from __future__ import annotations + +import logging +import os +from typing import Any + +from .base import BgJob, ExecResult, SandboxBackend, SandboxHandle # noqa: F401 + + +logger = logging.getLogger(__name__) + +BACKENDS = ("e2b", "hf", "daytona") + +# Per-backend home directory. See the module docstring for what a wrong value costs. +_HOMES = {"e2b": "/home/user", "hf": "/root", "daytona": "/root"} + +# THE TWO PROVIDERS TAKE DIFFERENT THINGS, AND THAT IS NOT A DETAIL TO PAPER OVER. +# +# E2B takes a prebuilt TEMPLATE, with the data-science stack and opencode already baked in -- which is +# also what lets the harness's "is opencode already installed" check short-circuit instead of curling +# an installer into every sandbox. It has no `image` parameter at all. +# +# HF takes an IMAGE, and installs opencode at runtime. +# +# Sizing is baked into the E2B template at BUILD time and cannot be set per sandbox: `Sandbox.create` +# has no cpu/memory parameters. That matters here because the defaults are too small -- pandas wants +# roughly 3-5x a CSV's size in RAM and these tables reach 2.7 GB, so a 1 GB sandbox is OOM-killed, and +# an OOM-killed rollout files no answer, which is identical in the reward to a model that could not do +# the task. The template is built at cpu=2, mem=4096; to change it, rebuild the template. +E2B_TEMPLATE = os.environ.get("E2B_TEMPLATE", "data-agent-opencode") +HF_FLAVOR = os.environ.get("HF_SANDBOX_FLAVOR", "cpu-basic") + +# Used by the HF backend only; E2B carries the equivalent inside its template. +DEFAULT_IMAGE = os.environ.get( + "DATA_AGENT_IMAGE", "docker.io/savatar101/env-data-agent-train:base" +) + + +def sandbox_home(backend: str) -> str: + """Home directory the agent runs under, for `backend`.""" + try: + return _HOMES[backend] + except KeyError: + raise ValueError(f"unknown sandbox backend {backend!r}; expected one of {BACKENDS}") from None + + +def build_backend(backend: str, *, image: str = DEFAULT_IMAGE, **kwargs: Any) -> SandboxBackend: + """Construct a sandbox backend by name. + + Args: + backend (`str`): + `"e2b"` or `"hf"`. + image (`str`, *optional*): + Container image. Used by the HF backend ONLY -- E2B carries the equivalent inside + `E2B_TEMPLATE`, and its constructor has no `image` parameter. + + Returns: + A sandbox backend. + """ + if backend == "e2b": + from .e2b import E2BSandboxBackend + + return E2BSandboxBackend( + template=E2B_TEMPLATE, + # The agent pulls its task's tables from a Hugging Face bucket, so it needs the network. + sandbox_kwargs={"allow_internet_access": True}, + **kwargs, + ) + if backend == "hf": + from .hf import HFSandboxBackend + + return HFSandboxBackend(image=image, flavor=HF_FLAVOR, **kwargs) + if backend == "daytona": + from .daytona import DaytonaSandboxBackend + return DaytonaSandboxBackend(image=image, **kwargs) + raise ValueError(f"unknown sandbox backend {backend!r}; expected one of {BACKENDS}") + + +def available() -> dict[str, bool]: + """Which backends this machine can run: SDK importable AND credentials present. + + Reported by `capabilities()` so a caller learns before dispatching rollouts, rather than after + paying for a sandbox that could never have started. + """ + out: dict[str, bool] = {} + try: + import e2b # noqa: F401 + + out["e2b"] = bool(os.environ.get("E2B_API_KEY")) + except ImportError: + out["e2b"] = False + try: + from huggingface_hub import get_token + + out["hf"] = bool(os.environ.get("HF_TOKEN") or get_token()) + except ImportError: + out["hf"] = False + try: + import daytona # noqa: F401 + out["daytona"] = bool(os.environ.get("DAYTONA_API_KEY")) + except ImportError: + out["daytona"] = False + return out + + +def describe() -> str: + """One line for the startup log: which backends are usable, and how each is configured.""" + got = available() + return ( + f"e2b={'ready' if got.get('e2b') else 'unavailable'} (template={E2B_TEMPLATE}), " + f"hf={'ready' if got.get('hf') else 'unavailable'} (flavor={HF_FLAVOR}), " + f"daytona={'ready' if got.get('daytona') else 'unavailable'}" + ) + + +__all__ = [ + "BACKENDS", + "DEFAULT_IMAGE", + "E2B_TEMPLATE", + "HF_FLAVOR", + "SandboxBackend", + "available", + "build_backend", + "describe", + "sandbox_home", +] diff --git a/04-data-agent/envs/blackbox-opencode/sandbox/base.py b/04-data-agent/envs/blackbox-opencode/sandbox/base.py new file mode 100644 index 0000000..7686914 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/sandbox/base.py @@ -0,0 +1,100 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Sandbox backend protocol. + +A ``SandboxBackend`` produces ``SandboxHandle`` instances that the harness uses +to stage files, run the OpenCode install, launch the agent as a background +process, and later tear the sandbox down. + +Backends can be implemented against any provider (E2B, Docker, Modal, Prime) +as long as they satisfy the Protocols defined here. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Protocol, runtime_checkable + + +@dataclass +class ExecResult: + """Result of a synchronous command inside a sandbox.""" + + exit_code: int + stdout: str + stderr: str + + +@runtime_checkable +class BgJob(Protocol): + """Handle to a background process running inside a sandbox.""" + + @property + def pid(self) -> int: ... + + def wait(self, timeout: float | None = None) -> int: + """Block until the process exits, returning its exit code. + + Implementations must raise ``TimeoutError`` if ``timeout`` elapses + before the process exits. + """ + + def kill(self) -> None: + """Terminate the process.""" + + +@runtime_checkable +class SandboxHandle(Protocol): + """Opaque handle to one live sandbox.""" + + @property + def sandbox_id(self) -> str: ... + + def exec( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + timeout: float | None = 60, + ) -> ExecResult: + """Run a shell command synchronously and return its result.""" + + def start_bg( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + ) -> BgJob: + """Launch a background process and return a handle.""" + + def write_text(self, path: str, content: str) -> None: + """Write text to ``path`` inside the sandbox (parent dirs auto-created).""" + + def read_text(self, path: str) -> str: + """Read ``path`` as text from the sandbox.""" + + def exists(self, path: str) -> bool: + """Return whether ``path`` exists in the sandbox.""" + + def kill(self) -> None: + """Terminate the sandbox and release resources.""" + + +@runtime_checkable +class SandboxBackend(Protocol): + """Factory for fresh sandbox instances.""" + + def create( + self, + *, + timeout_s: int = 900, + envs: dict[str, str] | None = None, + metadata: dict[str, str] | None = None, + ) -> SandboxHandle: + """Create and return a new, ready-to-use sandbox.""" diff --git a/04-data-agent/envs/blackbox-opencode/sandbox/daytona.py b/04-data-agent/envs/blackbox-opencode/sandbox/daytona.py new file mode 100644 index 0000000..a5d8ffa --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/sandbox/daytona.py @@ -0,0 +1,103 @@ +"""Daytona implementation of the standalone OpenCode sandbox protocol.""" +from __future__ import annotations + +from functools import lru_cache +import math +import os +import shlex +import time +import uuid + +from .base import ExecResult + + +@lru_cache(maxsize=1) +def client(): + from daytona import Daytona + return Daytona() + + +class DaytonaBgJob: + def __init__(self, handle, directory, pid): + self.handle, self.directory, self._pid = handle, directory, pid + + @property + def pid(self): + return self._pid + + def wait(self, timeout=None): + deadline = time.monotonic() + timeout if timeout is not None else float("inf") + while time.monotonic() < deadline: + if self.handle.exists(self.directory + "/exit"): + return int(self.handle.read_text(self.directory + "/exit").strip()) + time.sleep(min(0.5, max(0, deadline-time.monotonic()))) + raise TimeoutError("Background command exceeded its deadline") + + def kill(self): + self.handle.exec(f"kill -TERM -- -{self.pid} 2>/dev/null || true", timeout=10) + + +class DaytonaSandboxHandle: + def __init__(self, sandbox): + self._sandbox = sandbox + self._deleted = False + + @property + def sandbox_id(self): + return self._sandbox.id + + def exec(self, cmd, *, envs=None, cwd=None, timeout=60): + # The SDK's exec is not a shell. Quote the complete script once and let bash + # interpret it; neither user text nor environment values are interpolated. + result = self._sandbox.process.exec("bash -lc " + shlex.quote(cmd), cwd=cwd, + env=envs, timeout=max(1, math.ceil(timeout)) if timeout is not None else None) + code = result.exit_code + return ExecResult(124 if code is None else int(code), result.result or "", "") + + def write_text(self, path, content): + parent = path.rsplit("/", 1)[0] or "." + result = self.exec("mkdir -p " + shlex.quote(parent)) + if result.exit_code: + raise RuntimeError("Could not create sandbox directory") + self._sandbox.fs.upload_file(content.encode(), path) + + def read_text(self, path): + return self._sandbox.fs.download_file(path).decode() + + def exists(self, path): + return self.exec("test -e " + shlex.quote(path), timeout=15).exit_code == 0 + + def start_bg(self, cmd, *, envs=None, cwd=None): + directory = "/tmp/openenv-process-" + uuid.uuid4().hex + self.write_text(directory + "/run.sh", "#!/bin/bash\n(\n" + cmd + "\n)" + + "\ncode=$?\nprintf '%s' \"$code\" > " + shlex.quote(directory + "/exit") + "\n") + result = self.exec("setsid bash " + shlex.quote(directory + "/run.sh") + + " > " + shlex.quote(directory + "/output") + " 2>&1 < /dev/null & echo $!", + envs=envs, cwd=cwd, timeout=15) + if result.exit_code: + raise RuntimeError("Could not launch background command") + return DaytonaBgJob(self, directory, int(result.stdout.strip().splitlines()[-1])) + + def kill(self): + if not self._deleted: + client().delete(self._sandbox, timeout=60, wait=True) + self._deleted = True + + +class DaytonaSandboxBackend: + def __init__(self, *, image, snapshot=None): + self.image, self.snapshot = image, snapshot or os.environ.get("DATA_AGENT_DAYTONA_SNAPSHOT") + + def create(self, *, timeout_s=900, envs=None, metadata=None): + from daytona import CreateSandboxFromImageParams, CreateSandboxFromSnapshotParams, Resources + options = dict(language="python", os_user="root", env_vars=envs, + labels={**(metadata or {}), "openenv_component": "blackbox-opencode", + "openenv_owner": os.environ.get("RUN_OWNER", "local")}, + public=False, auto_stop_interval=max(30, math.ceil(timeout_s/60)), + auto_delete_interval=0) + if self.snapshot: + params = CreateSandboxFromSnapshotParams(snapshot=self.snapshot, **options) + else: + params = CreateSandboxFromImageParams(image=self.image, + resources=Resources(cpu=1, memory=4, disk=5), **options) + return DaytonaSandboxHandle(client().create(params, timeout=300)) diff --git a/04-data-agent/envs/blackbox-opencode/sandbox/e2b.py b/04-data-agent/envs/blackbox-opencode/sandbox/e2b.py new file mode 100644 index 0000000..b567a9e --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/sandbox/e2b.py @@ -0,0 +1,192 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""E2B implementation of :class:`SandboxBackend`.""" + +from __future__ import annotations + +import os +import threading +from pathlib import PurePosixPath + +from e2b import Sandbox +from e2b.sandbox_sync.commands.command_handle import CommandHandle + +from .base import BgJob, ExecResult, SandboxBackend, SandboxHandle + + +class E2BBgJob: + """Wraps an E2B ``CommandHandle`` to satisfy :class:`BgJob`. + + The E2B SDK's ``CommandHandle.wait()`` blocks indefinitely with no native + timeout. We poll in a worker thread and raise ``TimeoutError`` if the + process does not exit within the caller-supplied budget. + """ + + def __init__(self, handle: CommandHandle) -> None: + self._handle = handle + self._result: "object | None" = None + self._error: BaseException | None = None + self._thread = threading.Thread(target=self._run, daemon=True) + self._thread.start() + + def _run(self) -> None: + try: + self._result = self._handle.wait() + except BaseException as exc: # noqa: BLE001 + self._error = exc + + @property + def pid(self) -> int: + return self._handle.pid + + def wait(self, timeout: float | None = None) -> int: + self._thread.join(timeout) + if self._thread.is_alive(): + raise TimeoutError( + f"Background command did not exit within {timeout}s" + ) + if self._error is not None: + # E2B raises CommandExitException on non-zero; treat as exit code. + code = getattr(self._error, "exit_code", None) + if code is None: + raise self._error + return int(code) + return int(self._result.exit_code) if self._result is not None else 0 + + def kill(self) -> None: + try: + self._handle.kill() + except Exception: + pass + + +class E2BSandboxHandle: + """Wraps a live ``e2b.Sandbox`` to satisfy :class:`SandboxHandle`.""" + + def __init__(self, sandbox: Sandbox) -> None: + self._sbx = sandbox + + @property + def sandbox_id(self) -> str: + return self._sbx.sandbox_id + + @property + def raw(self) -> Sandbox: + """Escape hatch for callers that need the underlying SDK object.""" + return self._sbx + + def exec( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + timeout: float | None = 60, + ) -> ExecResult: + from e2b.sandbox.commands.command_handle import CommandExitException + + try: + result = self._sbx.commands.run( + cmd, + envs=envs, + cwd=cwd, + timeout=timeout, + background=False, + ) + return ExecResult( + exit_code=result.exit_code, + stdout=result.stdout, + stderr=result.stderr, + ) + except CommandExitException as exc: + # Non-zero exit codes are expected in many contexts (e.g. polling + # healthz before the server is up). Surface them as a proper + # ExecResult instead of an exception. + return ExecResult( + exit_code=int(getattr(exc, "exit_code", 1)), + stdout=str(getattr(exc, "stdout", "") or ""), + stderr=str(getattr(exc, "stderr", "") or str(exc)), + ) + + def start_bg( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + timeout: float = 0, + ) -> BgJob: + """Start a background command. + + ``timeout=0`` disables E2B's server-side command deadline (the default + is 60s, which would otherwise kill long-running agent processes). + Sandbox lifetime still bounds the job. + """ + handle = self._sbx.commands.run( + cmd, + envs=envs, + cwd=cwd, + background=True, + timeout=timeout, + ) + return E2BBgJob(handle) + + def write_text(self, path: str, content: str) -> None: + parent = str(PurePosixPath(path).parent) + if parent not in ("", "/"): + self._sbx.files.make_dir(parent) + self._sbx.files.write(path, content) + + def read_text(self, path: str) -> str: + return self._sbx.files.read(path) + + def exists(self, path: str) -> bool: + return self._sbx.files.exists(path) + + def kill(self) -> None: + self._sbx.kill() + + +class E2BSandboxBackend: + """Creates E2B sandboxes for OpenCode rollouts. + + The backend uses the E2B default base template unless ``template`` is + provided. Resource sizing and other E2B-specific options can be forwarded + via ``sandbox_kwargs``. + """ + + def __init__( + self, + *, + api_key: str | None = None, + template: str | None = None, + sandbox_kwargs: dict | None = None, + ) -> None: + self._api_key = api_key or os.environ.get("E2B_API_KEY") + if not self._api_key: + raise RuntimeError( + "E2BSandboxBackend requires an api_key or E2B_API_KEY env var." + ) + self._template = template + self._sandbox_kwargs = sandbox_kwargs or {} + + def create( + self, + *, + timeout_s: int = 900, + envs: dict[str, str] | None = None, + metadata: dict[str, str] | None = None, + ) -> SandboxHandle: + sbx = Sandbox.create( + template=self._template, + timeout=timeout_s, + envs=envs, + metadata=metadata, + api_key=self._api_key, + **self._sandbox_kwargs, + ) + return E2BSandboxHandle(sbx) diff --git a/04-data-agent/envs/blackbox-opencode/sandbox/hf.py b/04-data-agent/envs/blackbox-opencode/sandbox/hf.py new file mode 100644 index 0000000..763a8df --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/sandbox/hf.py @@ -0,0 +1,190 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +"""Hugging Face implementation of :class:`SandboxBackend` (``huggingface_hub.Sandbox``, >=1.22).""" + +from __future__ import annotations + +import time +import os +from pathlib import PurePosixPath + +import httpx +from huggingface_hub import Sandbox + +from .base import BgJob, ExecResult, SandboxHandle + + +_WAIT_POLL_INTERVAL_S = 0.5 +# The sandbox API occasionally drops a single poll; give up only after this many +# consecutive transient failures. +_MAX_TRANSIENT_POLL_ERRORS = 10 + + +class HFBgJob: + """Satisfies :class:`BgJob`. The SDK has no blocking wait(), so poll ``processes()``.""" + + def __init__(self, sandbox: Sandbox, process) -> None: + self._sandbox = sandbox + self._process = process + + @property + def pid(self) -> int: + return self._process.pid + + def wait(self, timeout: float | None = None) -> int: + deadline = None if timeout is None else time.monotonic() + timeout + transient_errors = 0 + while True: + try: + procs = self._sandbox.processes() + transient_errors = 0 + except httpx.TransportError: + # Transient sandbox-API disconnect (RemoteProtocolError, read + # timeout, ...): retry on the next poll tick, still honoring the deadline. + transient_errors += 1 + if transient_errors > _MAX_TRANSIENT_POLL_ERRORS: + raise + procs = None + if procs is not None: + proc = next((p for p in procs if p.pid == self.pid), None) + # Finished processes stay listed (running=False); a vanished pid means + # the sandbox was torn down mid-run, not a clean exit. + if proc is None: + raise RuntimeError(f"process {self.pid} vanished (sandbox torn down?)") + if not proc.running: + return int(proc.exit_code) if proc.exit_code is not None else 0 + if deadline is not None: + remaining = deadline - time.monotonic() + if remaining <= 0: + raise TimeoutError(f"Background command did not exit within {timeout}s") + time.sleep(min(_WAIT_POLL_INTERVAL_S, remaining)) + else: + time.sleep(_WAIT_POLL_INTERVAL_S) + + def kill(self) -> None: + try: + self._process.kill() # idempotent server-side + except Exception: + pass + + +class HFSandboxHandle: + """Wraps a live ``huggingface_hub.Sandbox`` to satisfy :class:`SandboxHandle`.""" + + def __init__(self, sandbox: Sandbox) -> None: + self._sbx = sandbox + + @property + def sandbox_id(self) -> str: + return self._sbx.id + + @property + def raw(self) -> Sandbox: + """Escape hatch for callers that need the underlying SDK object.""" + return self._sbx + + def exec( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + timeout: float | None = 60, + ) -> ExecResult: + # check=False: surface non-zero exits as a result instead of raising + result = self._sbx.run( + ["bash", "-lc", cmd], + env=envs, + cwd=cwd, + timeout=timeout, + check=False, + ) + # A timed-out command is killed with exit_code unset; surface it as a + # non-zero exit instead of a false success. The SDK may return None for + # stdout/stderr, so coerce to str (ExecResult / downstream expect str). + if result.timed_out: + return ExecResult( + exit_code=124, + stdout=result.stdout or "", + stderr=(result.stderr or "") + f"\n[timed out after {timeout}s]", + ) + return ExecResult( + exit_code=int(result.exit_code) if result.exit_code is not None else 0, + stdout=result.stdout or "", + stderr=result.stderr or "", + ) + + def start_bg( + self, + cmd: str, + *, + envs: dict[str, str] | None = None, + cwd: str | None = None, + ) -> BgJob: + process = self._sbx.run( + ["bash", "-lc", cmd], + env=envs, + cwd=cwd, + background=True, + ) + return HFBgJob(self._sbx, process) + + def write_text(self, path: str, content: str) -> None: + parent = str(PurePosixPath(path).parent) + if parent not in ("", "/", "."): # "." == relative path with no parent dir to create + self._sbx.files.mkdir(parent) + self._sbx.files.write(path, content) + + def read_text(self, path: str) -> str: + return self._sbx.files.read_text(path) + + def exists(self, path: str) -> bool: + return self._sbx.files.exists(path) + + def kill(self) -> None: + # kill() tears the sandbox down; close() would only drop the client (it keeps idling) + self._sbx.kill() + + +class HFSandboxBackend: + """Creates Hugging Face sandboxes for OpenCode rollouts. + + ``image`` may be a plain base (e.g. ``python:3.12``), in which case opencode + and the proxy deps are cold-installed per rollout, or a pre-baked image that + already ships them. + """ + + def __init__( + self, + *, + image: str, + flavor: str = "cpu-basic", + forward_hf_token: bool = False, + sandbox_kwargs: dict | None = None, + ) -> None: + self._image = image + self._flavor = flavor + self._forward_hf_token = forward_hf_token + self._sandbox_kwargs = sandbox_kwargs or {} + + def create( + self, + *, + timeout_s: int = 900, + envs: dict[str, str] | None = None, + metadata: dict[str, str] | None = None, + ) -> SandboxHandle: + # metadata: accepted for protocol parity; Sandbox.create has no such param + sbx = Sandbox.create( + image=self._image, + flavor=self._flavor, + idle_timeout=timeout_s, + env=envs, + forward_hf_token=self._forward_hf_token, + **{"namespace": os.environ.get("HF_SANDBOX_NAMESPACE"), **self._sandbox_kwargs}, + ) + return HFSandboxHandle(sbx) diff --git a/04-data-agent/envs/blackbox-opencode/serve.sh b/04-data-agent/envs/blackbox-opencode/serve.sh new file mode 100755 index 0000000..81fce3d --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/serve.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +# Copyright 2026 The HuggingFace Team. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Run the environment locally with the same configuration a deployment uses, so a client cannot tell +# the two apart. +# +# Usage: +# ./serve.sh +# PORT=8200 SPLITS=train:medium SANDBOX=e2b ./serve.sh +# EXPOSE=gradio ./serve.sh # publish the capture port for a remote sandbox +# +# The engine is OPTIONAL. With none named the server still comes up serving its splits, and each +# rollout names the engine it wants. That is the useful way round: the dataset and its prebuilt +# sandbox templates are the expensive things to host, while an engine restarts every training run and +# a train-tier engine and an eval-tier one are usually both wanted at once. + +set -euo pipefail + +# This script sits at the environment root, so its own directory IS the environment. The directory is +# named `blackbox-opencode`, which is not a legal Python identifier, so the package cannot be +# imported by adding this path to PYTHONPATH -- `uv sync` here installs it as `data_agent_env` via the +# package-dir mapping in pyproject.toml, and that is what makes the import work at all. +cd "$(dirname "$(readlink -f "$0" 2>/dev/null || echo "$0")")" +ENV_DIR="$PWD" +PYTHON="${PYTHON:-$ENV_DIR/.venv/bin/python}" + +PORT="${PORT:-8200}" +HOST="${HOST:-0.0.0.0}" + +if [ ! -x "$PYTHON" ]; then + echo "no interpreter at $PYTHON" >&2 + echo "run 'uv sync' in $ENV_DIR, or set PYTHON= to one that has openenv-data-agent-env installed" >&2 + exit 1 +fi + +if ! "$PYTHON" -c "import data_agent_env" 2>/dev/null; then + echo "data_agent_env is not importable by $PYTHON" >&2 + echo "run 'uv sync' in $ENV_DIR" >&2 + exit 1 +fi + +# Which splits this deployment serves. A difficulty is a NAMED SPLIT (`train:medium`), never a filter +# argument: a filter shifts every index after it, and the index is task identity everywhere +# downstream -- in the eval matrix, in the trace store, in every result already recorded. +export DATA_AGENT_SPLITS="${SPLITS:-train}" +export DATA_AGENT_SANDBOX="${SANDBOX:-e2b}" + +# Rollouts EXECUTING at once. The ceiling is the capture proxy, which is a single uvicorn process: it +# starved /health at ~200 concurrent and crashed outright at 320 (3,525 fds, 542 threads, 6.7 GB). +# E2B allows 500 sandboxes per account, so capture gives out first. A semaphore, not a rejection -- an +# over-limit rollout waits, because an eval run must not be able to starve a training run out. +export DATA_AGENT_MAX_CONCURRENT="${MAX_CONCURRENT:-64}" + +# WebSocket SESSIONS, which is a different number: a trainer holds one per in-flight rollout, so this +# has to exceed `num_generations` or rollouts queue at the door. Core defaults to 4, a demo setting. +export MAX_CONCURRENT_ENVS="${MAX_CONCURRENT_ENVS:-128}" + +# The port the capture proxy binds, and -- separately -- how the SANDBOX reaches it. The sandbox runs +# on another machine, so a deployment behind a tunnel must advertise its outside address here. +# Getting this wrong is quiet: opencode starts, cannot reach the engine, makes zero model calls, and +# the rollout returns a flat zero that reads exactly like a policy that cannot do the task. +export DATA_AGENT_CAPTURE_PORT="${CAPTURE_PORT:-8300}" + +# How the sandbox reaches that port. `direct` is right only when this host is already routable from +# the sandbox -- on a cluster node it is not, and the symptom is the quiet one above. `gradio` mints +# a public URL; prefer it over `cloudflare`, which wedged for 32 minutes on this cluster, and a +# forwarder that hangs is worse than one that fails because rollouts queue behind it looking healthy. +export DATA_AGENT_CAPTURE_EXPOSE="${EXPOSE:-direct}" + +# An already-published deployment (a Space, a reverse proxy) sets this and no tunnel is started. +[ -n "${CAPTURE_PUBLIC_URL:-}" ] && export CAPTURE_PUBLIC_URL + +export OPENENV_LLM_URL="${LLM_URL:-}" +export OPENENV_MODEL="${MODEL:-}" +export ENABLE_WEB_INTERFACE=true + +echo "data_agent_env -> http://$HOST:$PORT/web/" +echo " splits $DATA_AGENT_SPLITS" +echo " sandbox $DATA_AGENT_SANDBOX" +echo " concurrency $DATA_AGENT_MAX_CONCURRENT executing / $MAX_CONCURRENT_ENVS sessions" +echo " engine ${OPENENV_LLM_URL:-none (each rollout names its own)}" +echo " capture :$DATA_AGENT_CAPTURE_PORT expose=$DATA_AGENT_CAPTURE_EXPOSE${CAPTURE_PUBLIC_URL:+ public=$CAPTURE_PUBLIC_URL}" +echo + +exec "$PYTHON" -m uvicorn data_agent_env.server.app:app --host "$HOST" --port "$PORT" diff --git a/04-data-agent/envs/blackbox-opencode/server/Dockerfile b/04-data-agent/envs/blackbox-opencode/server/Dockerfile new file mode 100644 index 0000000..8382db7 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/server/Dockerfile @@ -0,0 +1,36 @@ +ARG BASE_IMAGE=ghcr.io/huggingface/openenv-base:latest +FROM ${BASE_IMAGE} AS builder + +# This env requires Python >= 3.12 while openenv-base ships 3.11, so `uv sync` downloads its own +# interpreter and the venv's bin/python becomes a symlink into uv's install dir. Pinning that dir +# (and creating it up front, so the COPY below cannot fail when uv reuses a system interpreter) +# is what lets the runtime stage carry the interpreter the venv actually points at. Without it the +# venv arrives with a dangling bin/python and the container dies with "not found". +ENV UV_PYTHON_INSTALL_DIR=/opt/uv-python +RUN mkdir -p /opt/uv-python + +WORKDIR /app/env +COPY . /app/env +RUN --mount=type=cache,target=/root/.cache/uv \ + if [ -f uv.lock ]; then uv sync --frozen --no-editable; else uv sync --no-editable; fi + +FROM ${BASE_IMAGE} +COPY --from=builder /opt/uv-python /opt/uv-python +COPY --from=builder /app/env/.venv /app/.venv +COPY --from=builder /app/env /app/env + +# Fail at build time rather than at startup if the interpreter did not survive the stage boundary. +RUN /app/.venv/bin/python -c "import sys; print('venv python', sys.version)" + +ENV PATH="/app/.venv/bin:$PATH" +# `openenv push` bundles the working tree's openenv/ into /app/env when pushing from a source +# checkout; PYTHONPATH puts it ahead of the released wheel in site-packages, which has no +# `openenv.core.harness.capture.CaptureServer` until this lands upstream. +ENV PYTHONPATH="/app/env:$PYTHONPATH" +ENV ENABLE_WEB_INTERFACE=true + +HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ + CMD /app/.venv/bin/python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1 + +EXPOSE 8000 +CMD ["sh", "-c", "cd /app/env && exec /app/.venv/bin/python -m uvicorn server.app:app --host 0.0.0.0 --port 8000"] diff --git a/04-data-agent/envs/blackbox-opencode/server/__init__.py b/04-data-agent/envs/blackbox-opencode/server/__init__.py new file mode 100644 index 0000000..a9646c0 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/server/__init__.py @@ -0,0 +1,20 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Server side of `data_agent_env`. Clients never import from here.""" + +from .environment import DataAgentEnvironment + + +__all__ = ["DataAgentEnvironment"] diff --git a/04-data-agent/envs/blackbox-opencode/server/app.py b/04-data-agent/envs/blackbox-opencode/server/app.py new file mode 100644 index 0000000..724cf92 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/server/app.py @@ -0,0 +1,146 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""ASGI entry point for a deployed `data_agent_env`. + +Everything is read from the environment, so one image serves any split and any engine with no +rebuild: + + DATA_AGENT_SPLITS comma-separated splits, e.g. `train:medium,test` (default `train`) + DATA_AGENT_SANDBOX default backend, `e2b`, `hf` or `daytona` + DATA_AGENT_MAX_CONCURRENT rollouts in flight; see the ceiling note below + DATA_AGENT_CAPTURE_PORT port the capture proxy binds (default 8300) + CAPTURE_PUBLIC_URL how the SANDBOX reaches that port, when it is not localhost + OPENENV_LLM_URL default engine; optional, since a rollout may name its own + OPENENV_MODEL default served model id + HF_TOKEN reads the dataset and stages each task's tables + E2B_API_KEY required for the `e2b` backend + +OPENENV_LLM_URL IS OPTIONAL, AND THAT IS THE USEFUL WAY ROUND. With no engine the server still comes +up serving its splits, and each rollout names the engine it wants -- probed once, then cached. The +dataset and its prebuilt sandbox templates are the expensive things to host; an engine restarts every +training run, and a train-tier engine and an eval-tier one are usually both wanted at once. + +THE CAPTURE PROXY IS NOT STARTED HERE. It is started lazily by the first rollout, against that +rollout's engine (`capture.capture_server`). Starting it at import would mean either binding it to a +default engine that no rollout uses, or refusing to boot without one. +""" + +from __future__ import annotations + +import logging +import os + +from openenv.core.env_server.http_server import create_app +from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation + +from ..sandbox import describe +from .environment import DataAgentEnvironment + + +logging.basicConfig(level=os.environ.get("LOG_LEVEL", "INFO")) +logger = logging.getLogger(__name__) + +_SPLITS = [ + s.strip() + for s in os.environ.get("DATA_AGENT_SPLITS", "train").split(",") + if s.strip() +] + + +def _hf_token() -> str | None: + """Resolved ONCE, here, and passed down by value. + + Not re-resolved per rollout: it is used to stage every task's tables, and a per-rollout lookup + would hit the token store thousands of times in a run. Passed as a value, never logged. + """ + token = os.environ.get("HF_TOKEN") + if token: + return token + try: + from huggingface_hub import get_token + + return get_token() + except ImportError: + return None + + +DataAgentEnvironment.configure( + splits=_SPLITS, + llm_url=os.environ.get("OPENENV_LLM_URL", ""), + model=os.environ.get("OPENENV_MODEL", ""), + hf_token=_hf_token(), + sandbox=os.environ.get("DATA_AGENT_SANDBOX", "e2b"), +) + +# Which backends are actually usable is worth one line at startup: a missing E2B_API_KEY otherwise +# surfaces as a rollout that fails after paying for a sandbox that could never have started. +logger.info("data_agent_env: splits=%s sandboxes: %s", _SPLITS, describe()) + +# WARM THE CAPTURE PROXY when a default engine is configured. +# +# It is lazy by default, which is right for a deployment whose engine arrives per rollout. But when +# the engine is known at boot, starting it here moves two slow, failure-prone steps off the first +# rollout: binding the port and minting the public tunnel. Under a trainer that opens `num_generations` +# rollouts at once, all of them would otherwise queue on the one holding the lock -- and a tunnel that +# fails to mint would surface as a rollout timeout rather than as a boot error. +# +# Never fatal. A server that cannot publish its proxy can still serve the Task API, and saying so at +# boot beats refusing to start. +if os.environ.get("OPENENV_LLM_URL"): + try: + from .capture import agent_base_url, capture_server + + _srv = capture_server( + os.environ["OPENENV_LLM_URL"], os.environ.get("OPENENV_MODEL", "") + ) + logger.info("capture proxy ready; agents will be pointed at %s", agent_base_url(_srv)) + except Exception: # noqa: BLE001 + logger.warning( + "could not warm the capture proxy; the first rollout will try again", exc_info=True + ) + +os.environ.setdefault("ENABLE_WEB_INTERFACE", "true") + +# TWO DIFFERENT CONCURRENCY LIMITS, AND THEY ARE NOT THE SAME NUMBER. +# +# `max_concurrent_envs` caps WebSocket SESSIONS -- how many callers may hold this env open. A trainer +# holds one session per in-flight rollout, so it has to be at least `num_generations` or rollouts +# queue at the door. +# +# `DATA_AGENT_MAX_CONCURRENT` (in `rollout.py`) caps rollouts actually EXECUTING, and that is the one +# bounded by measurement: the capture proxy is a single uvicorn process which starved `/health` at +# ~200 concurrent and crashed outright at 320 (3,525 fds, 542 threads, 6.7 GB). E2B allows 500 +# sandboxes per account, so capture gives out first. +# +# Session cap above execution cap on purpose: a rollout that arrives over the execution limit WAITS +# on a semaphore rather than being refused. An evaluation run and a training run share one deployment, +# and the eval must not be able to starve training out or take the proxy down with it. +app = create_app( + DataAgentEnvironment, + CallToolAction, + CallToolObservation, + env_name="data_agent_env", + max_concurrent_envs=int(os.environ.get("MAX_CONCURRENT_ENVS", "128")), +) + + +def main() -> None: + import uvicorn + + uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8000"))) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/envs/blackbox-opencode/server/capture.py b/04-data-agent/envs/blackbox-opencode/server/capture.py new file mode 100644 index 0000000..327ec99 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/server/capture.py @@ -0,0 +1,290 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The capture proxy this environment runs, and the control plane for one rollout's session. + +WHY THE CONTROL PLANE IS IN-PROCESS AND THE DATA PLANE IS NOT +The proxy has to serve HTTP: the agent runs inside a sandbox, on another machine, and reaches the +engine only by URL. That part is unavoidable and stays HTTP. + +Minting a session and reading its turns back is a different matter. Those happen in *this* process, +which owns the `SessionRegistry` the proxy is writing into. An earlier version of this file went out +over HTTP for both, and got two things wrong that the direct path cannot get wrong: + + * it had to re-parse `/trace_entries` JSON that was serialised from objects sitting in local memory, + once per rollout, for every turn of every rollout; + * it never deleted the session, because there was no obvious place to. Sessions release slowly and a + leaked one collides with the next run's claim, which surfaces as a burst of `CAPACITY_REACHED` + rather than as a leak. + +`openenv.core.harness.capture.CaptureServer` was written for exactly this and says so in its own +docstring: "a thread rather than a subprocess because the rollout path needs the live +`SessionRegistry` -- it mints a session, then reads the graph back out of it directly." + +ONE SERVER PER PROCESS, AT MODULE SCOPE +The Task API builds a throwaway environment instance per request and closes it in a `finally` +(`http_server.py:1082-1097`). Anything held on `self` dies with the request, so a per-instance proxy +would bind a port per rollout and then leak it. The server, the engine tier and the port live here, +at module level, guarded by a lock. +""" + +from __future__ import annotations + +import logging +import hashlib +import os +import threading +from typing import Any + +from openenv.core.harness.capture import CaptureServer, to_trace_entries +from openenv.core.harness.capture.export import export_session +from openenv.core.harness.capture.sessions import Upstream + +from ..models import DataAgentTurn + + +logger = logging.getLogger(__name__) + +# The port the agent inside the sandbox will be pointed at. It has to be reachable from there, so a +# deployment behind a tunnel sets CAPTURE_PUBLIC_URL to the outside address of this same port. +CAPTURE_PORT = int(os.environ.get("DATA_AGENT_CAPTURE_PORT", "8300")) + +# How the SANDBOX reaches the proxy: `gradio`, `cloudflare` or `direct`. Not how the trainer reaches +# the env server -- those are different hosts with different reachability. `direct` only works when +# this machine is already routable from the sandbox, which on a cluster node it is not. +CAPTURE_EXPOSE = os.environ.get("DATA_AGENT_CAPTURE_EXPOSE", "direct") + +_LOCK = threading.Lock() +_SERVER: CaptureServer | None = None +_FORWARDER: Any = None +_PUBLIC_URL: str = "" +# Measured tier per (llm_url, model). Deciding it means sending real completions, so it is measured +# once per engine and shared, never per rollout. +_TIERS: dict[tuple[str, str, str], str] = {} + + +def capture_server( + llm_url: str, model: str, *, port: int = CAPTURE_PORT, expose: str = "" +) -> CaptureServer: + """The one proxy this process runs, started on first use, and exposed once. + + Args: + llm_url (`str`): + Default engine, used by any request that does not name its own. + model (`str`): + Default served model id. + port (`int`, *optional*): + Port to bind. `start()` verifies that the server answering on it is *this* one rather + than merely that something answers -- reachability is not identity, and a stale process + holding this port answers every probe while capturing nothing. + expose (`str`, *optional*): + `"gradio"`, `"cloudflare"` or `"direct"`. Defaults to `DATA_AGENT_CAPTURE_EXPOSE`. + + Returns: + `CaptureServer`: running, with a live `SessionRegistry`. + """ + global _SERVER, _FORWARDER, _PUBLIC_URL + with _LOCK: + if _SERVER is None: + server = CaptureServer(llm_url=llm_url, model=model, port=port, + max_output_tokens=16384, admin_key=os.environ.get("DATA_AGENT_CAPTURE_ADMIN_KEY") or __import__("secrets").token_urlsafe(32)) + server.start() + logger.info( + "capture proxy listening on :%d (engine %s, model %s)", + port, + llm_url, + model, + ) + # Exposed ONCE, here, rather than per rollout: a forwarder mints a tunnel process and a + # public hostname, and one per rollout would be both slow and a leak. An explicit + # CAPTURE_PUBLIC_URL wins -- a deployment that is already published (a Space, a reverse + # proxy) must not have a second tunnel stood up in front of it. + _PUBLIC_URL = (os.environ.get("CAPTURE_PUBLIC_URL") or "").rstrip("/") + kind = expose or CAPTURE_EXPOSE + if not _PUBLIC_URL and kind != "direct": + from openenv.core.harness.capture.forwarding import make_forwarder + + # gradio rather than cloudflare by default where a tunnel is wanted at all: + # cloudflared wedged for 32 minutes on this cluster, and a forwarder that hangs is + # worse than one that fails, because the rollouts queue behind it looking healthy. + _FORWARDER = make_forwarder(kind) + _PUBLIC_URL = _FORWARDER.start(port).rstrip("/") + logger.info("capture proxy published at %s (%s)", _PUBLIC_URL, kind) + _SERVER = server + return _SERVER + + +def agent_base_url(server: CaptureServer) -> str: + """The address the agent inside the sandbox should call. + + NOT necessarily where we bound. The agent runs on another machine, so a deployment behind a + tunnel or a reverse proxy has to advertise its outside address. `capture_server()` resolves that + once at startup -- from `CAPTURE_PUBLIC_URL` if set, otherwise from the forwarder it started -- + and this returns it. + + Getting it wrong is quiet: opencode starts, cannot reach the engine, makes zero model calls, and + the rollout comes back with a flat zero that reads exactly like a policy that cannot do the task. + Falling back to loopback is therefore deliberate and only correct for a sandbox on this host. + """ + return _PUBLIC_URL or f"http://127.0.0.1:{server.port}" + + +def engine_tier(llm_url: str, model: str, *, require_tokens: bool, api_key: str | None = None) -> str: + """Measure what this engine can return, once, and remember it. + + An engine served without `--return-tokens-as-token-ids --logprobs-mode processed_logprobs` + degrades to `text` capture SILENTLY. Every rollout then looks completely normal and carries + nothing to train on; two jobs spent hours that way before anyone noticed. So a training caller + passes `require_tokens=True` and this raises at the first rollout instead. + + Args: + require_tokens (`bool`): + Raise if the engine cannot return token ids. True for training, False for evaluation -- + a text-only endpoint is a perfectly good eval backend, and refusing it would rule out + every hosted provider. + + Returns: + `str`: `"tokens"` or `"text"`. + """ + key = (llm_url, model, hashlib.sha256((api_key or "").encode()).hexdigest()) + with _LOCK: + hit = _TIERS.get(key) + if hit is not None and not (require_tokens and hit != "tokens"): + return hit + + from openenv.core.harness.capture.validate_llm import require_llm + + report = require_llm(llm_url, model, require_tokens=require_tokens, api_key=api_key) + level = report.capture_level or "text" + with _LOCK: + _TIERS[key] = level + logger.info("engine %s (%s) probed: capture_level=%s", llm_url, model, level) + return level + + +def mint_session( + server: CaptureServer, + *, + llm_url: str, + model: str, + rollout_id: str, + capture_level: str, + max_model_calls: int = 0, + api_key: str | None = None, + **metadata: Any, +) -> tuple[str, str]: + """Create a capture session on the live registry. + + `upstream` names the engine for THIS rollout rather than for the deployment, which is what lets + one server serve a training run and an evaluation run against different engines at the same time. + + Args: + max_model_calls (`int`, *optional*, defaults to `0`): + Ceiling on model calls for this rollout; `0` is unlimited. Enforced by the proxy, which + is the only component that sees every call -- `agent.build.steps` caps nothing, measured. + + Returns: + `tuple[str, str]`: the session id -- which is also the agent's API key -- and `"train"` or + `"eval"`. + """ + session = server.registry.create( + session_id=None, + upstream=Upstream(llm_url=llm_url, model=model, api_key=api_key), + capture_level=capture_level, + max_model_calls=max_model_calls, + rollout_id=rollout_id, + **metadata, + ) + level = session.capture_level or capture_level + return session.session_id, ("train" if level == "tokens" else "eval") + + +def fetch_turns( + server: CaptureServer, session_id: str +) -> tuple[list[DataAgentTurn], list[str]]: + """Read the session's turns back, with the engine's own tokenization, and what capture saw. + + `to_trace_entries` puts `prompt_token_ids` and `loss_mask` on every entry, so nothing downstream + re-renders a prompt. Before that field existed a consumer had to rebuild each prompt with + `apply_chat_template`, which matched the engine on 0 of 28 measured turns on Qwen3.5-4B and + collapsed a run at its first weight update. + + Findings are RETURNED, not just logged. Capture knows things about a rollout that cannot be + re-derived from the turns alone -- `per_turn_capture_only` in particular, which says every + turn became its own graph root because the harness re-renders its prompt instead of + appending. Tokens and logprobs are still exact when it fires; what is lost is multi-turn + credit assignment. A caller holding only the turn list would have to infer this from token + drift and would infer it wrongly, since that drift is small and legitimate for such a + harness. + + Returns: + `tuple[list[DataAgentTurn], list[str]]`: the turns, and any non-INFO capture findings. + """ + from .rollout import turns_from_capture + + session = server.registry.get(session_id) + if session is None: + logger.warning("capture session %s is gone; no turns to read", session_id) + return [], [] + level = session.capture_level or server.capture_level + document = export_session(session, include_messages=True, capture_level=level) + findings = [f for f in document.get("validation", []) if not f.startswith("[INFO]")] + if findings: + logger.warning( + "capture findings for %s: %s", session_id, "; ".join(findings[:5]) + ) + if level == "tokens": + return turns_from_capture(to_trace_entries(session.graph, document)), findings + # Hosted text-only inference remains evaluable, with no fabricated token arrays. + turns = [DataAgentTurn(turn=n.index, text=(n.response_message or {}).get("content") or "", + tool_calls=(n.response_message or {}).get("tool_calls") or [], + request_messages=n.request_messages, request_tools=n.request_tools, + finish_reason=n.finish_reason) for n in session.graph.nodes()] + return turns, findings + + +def release_session(server: CaptureServer, session_id: str | None) -> None: + """Drop the session as soon as its turns have been read. + + Not optional bookkeeping. A session held past its rollout keeps the graph alive and its key valid, + and leftovers collide with the next run's claim -- which presents as a burst of + `CAPACITY_REACHED` on a server that looks idle, not as a leak. + """ + if not session_id: + return + try: + server.registry.delete(session_id) + except Exception: + logger.warning( + "could not release capture session %s", session_id, exc_info=True + ) + + +def shutdown() -> None: + """Stop the proxy and its forwarder. For tests and a clean exit; a rollout never calls this.""" + global _SERVER, _FORWARDER, _PUBLIC_URL + with _LOCK: + # Forwarder first: it is a child process holding a public hostname, and stopping the server + # underneath it leaves a live tunnel pointing at a closed port -- which from outside is + # indistinguishable from a healthy server, and is how a stale URL outlives its service. + if _FORWARDER is not None: + try: + _FORWARDER.stop() + except Exception: + logger.warning("capture forwarder did not stop cleanly", exc_info=True) + _FORWARDER = None + _PUBLIC_URL = "" + if _SERVER is not None: + _SERVER.stop() + _SERVER = None diff --git a/04-data-agent/envs/blackbox-opencode/server/environment.py b/04-data-agent/envs/blackbox-opencode/server/environment.py new file mode 100644 index 0000000..7f927f3 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/server/environment.py @@ -0,0 +1,287 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The server-side environment: task selection over MCP, and one long `run_rollout`. + +EVERY PIECE OF STATE HERE IS CLASS- OR MODULE-LEVEL, AND THAT IS NOT STYLE +OpenEnv builds a FRESH environment instance per request and closes it in a `finally` +(`http_server.py:1082-1097`). `/metadata` and `/schema` build one too. So anything cached on `self` +is rebuilt per call -- for this env that would mean re-downloading and re-parsing a dataset of +thousands of rows on every `num_tasks()` -- and any credential read in `__init__` is read on the docs +page. Config arrives through `configure()` onto the class; the dataset cache lives in `tasks.py` at +module scope. + +`run_rollout` RATHER THAN `reset`/`step` +No state survives between requests, and the agent owns its own loop: it runs inside a sandbox with +its own tools and its own turn structure, and there is no meaningful intermediate state for a caller +to observe. `reset()` is selection only and boots nothing. +""" + +from __future__ import annotations + +import json +import logging +from typing import Any +from uuid import uuid4 + +from openenv.core.env_server.mcp_environment import MCPEnvironment +from openenv.core.env_server.types import Observation + +from ..config import DataAgentConfig +from ..models import DataAgentState +from ..tasks import DataAgentTaskProvider + + +logger = logging.getLogger(__name__) + +# A rollout is sandbox boot, data staging, the agent's whole tool loop, then grading: 60-600 s is +# normal. MCP tools default to 30 s, which would abort every healthy rollout, so `step` is shadowed +# below to raise the floor. +ROLLOUT_TIMEOUT_S = 1800.0 + + +class DataAgentEnvironment(MCPEnvironment): + """Per-session environment exposing `run_rollout`, `capabilities` and `list_tasks` over MCP.""" + + SUPPORTS_CONCURRENT_SESSIONS = True + + # Server-wide, set once by `app.py`. Class-level for the reason in the module docstring. + _splits: list[str] = [] + _llm_url: str = "" + _model: str = "" + _hf_token: str | None = None + _sandbox: str = "e2b" + + @classmethod + def configure( + cls, + *, + splits: list[str], + llm_url: str = "", + model: str = "", + hf_token: str | None = None, + sandbox: str = "e2b", + ) -> None: + cls._splits = list(splits) + cls._llm_url = llm_url + cls._model = model + # Resolved ONCE at startup, never per rollout: it is used to stage each task's tables, and + # re-resolving it per rollout would hit the token store thousands of times per run. + cls._hf_token = hf_token + cls._sandbox = sandbox + + def __init__(self) -> None: + from fastmcp import FastMCP + + self._provider = DataAgentTaskProvider(self._splits) + self._state = DataAgentState(episode_id=str(uuid4())) + + mcp = FastMCP("data_agent_env") + + @mcp.tool + def run_rollout( + split: str = "", + index: int = 0, + llm_url: str = "", + model: str = "", + sandbox: str = "", + agent_step_limit: int = 10, + agent_timeout_s: float = 600.0, + require_tokens: bool = True, + api_key: str = "", + sampling: dict[str, float | int] | None = None, + ) -> str: + """Run one data-agent rollout and return a JSON `DataAgentRolloutResult`. + + The ENGINE is per call, not per deployment. A dataset and its prebuilt sandbox templates + are the expensive things to host and an engine restarts every training run, so one server + serves a training run and an evaluation run against different engines at once. + + `require_tokens` refuses an engine that cannot return token ids. Leave it on for training + -- such a rollout looks completely normal and carries nothing to train on -- and turn it + off for evaluation, where a text-only endpoint is a perfectly good backend. + """ + return self._run_rollout( + split or (self._splits[0] if self._splits else ""), + index, + llm_url, + model, + sandbox, + agent_step_limit, + agent_timeout_s, + require_tokens, + api_key, + sampling, + ) + + @mcp.tool + def capabilities() -> str: + """Usable sandboxes, splits, concurrency budget, and whether rollouts can be trained on.""" + return json.dumps(self._capabilities()) + + @mcp.tool + def list_tasks(split: str = "", start: int = 0, stop: int = 20) -> str: + """A window of tasks in a split, for browsing without pulling all of them.""" + return json.dumps(self._provider.get_task_range(split, start, stop)) + + super().__init__(mcp) + + # --- Task API (OpenEnv discovers these by duck typing) -------------------------------------- + + def list_splits(self) -> list[dict[str, Any]]: + return self._provider.list_splits() + + def num_tasks(self, split: str) -> int: + return self._provider.num_tasks(split) + + def list_tasks(self, split: str) -> list[dict[str, Any]]: + return self._provider.list_tasks(split) + + def get_task(self, split: str, index: int) -> dict[str, Any]: + return self._provider.get_task(split, index) + + def get_task_range( + self, split: str, start: int | None = None, stop: int | None = None + ) -> list[dict[str, Any]]: + return self._provider.get_task_range(split, start, stop) + + # --- Environment --------------------------------------------------------------------------- + + def reset( + self, + seed: int | None = None, + episode_id: str | None = None, + split: str = "", + index: int = 0, + **_: Any, + ) -> Observation: + """Select a task. Boots nothing -- a sandbox is created per `run_rollout`. + + The selection is ECHOED BACK, and a caller should assert on it. OpenEnv's reset body is + `extra="allow"` (`http_server.py:682-686`), so a misspelled kwarg is DROPPED rather than + rejected: send `task_index=` where this declares `index=` and every rollout silently runs + index 0, with nothing anywhere reporting a problem. + """ + self._state = DataAgentState( + episode_id=episode_id or str(uuid4()), + split=split or (self._splits[0] if self._splits else None), + index=index, + ) + task_name = "" + if self._state.split is not None: + try: + task_name = self._provider.get_task(self._state.split, index).get( + "task_id", "" + ) + except Exception: # noqa: BLE001 -- an out-of-range index is the caller's to see, not a crash + logger.warning( + "reset could not resolve %s[%d]", self._state.split, index + ) + self._state.task_id = task_name or None + return Observation( + done=False, + reward=None, + metadata={ + "status": "ready", + "split": self._state.split, + "index": index, + "task_id": task_name, + "message": "Call run_rollout(split=..., index=..., llm_url=..., model=...)", + }, + ) + + def _step_impl( + self, action: Any, timeout_s: float | None = None, **_: Any + ) -> Observation: + return Observation( + done=False, + reward=None, + metadata={ + "error": f"Unknown action {type(action).__name__}; " + "use CallToolAction(name='run_rollout', ...)" + }, + ) + + def step( + self, action: Any, timeout_s: float | None = None, **kwargs: Any + ) -> Observation: + return super().step(action, timeout_s=timeout_s or ROLLOUT_TIMEOUT_S, **kwargs) + + async def step_async( + self, action: Any, timeout_s: float | None = None, **kwargs: Any + ) -> Observation: + return await super().step_async( + action, timeout_s=timeout_s or ROLLOUT_TIMEOUT_S, **kwargs + ) + + @property + def state(self) -> DataAgentState: + return self._state + + # --- internals ----------------------------------------------------------------------------- + + def _capabilities(self) -> dict[str, Any]: + from .rollout import concurrency_status + from ..sandbox import available, BACKENDS + + return { + "env": "data_agent_env", + "splits": self._provider.list_splits(), + "sandboxes": {"supported": list(BACKENDS), "usable": available()}, + "llm_url": self._llm_url, + "model": self._model, + "concurrency": concurrency_status(), + } + + def _run_rollout( + self, + split: str, + index: int, + llm_url: str, + model: str, + sandbox: str, + agent_step_limit: int, + agent_timeout_s: float, + require_tokens: bool, + api_key: str = "", + sampling: dict[str, float | int] | None = None, + ) -> str: + from ..tasks import task_at + from .rollout import run_rollout + + # `task_at`, NOT `provider.get_task`. The latter returns the PUBLIC projection, which omits + # the gold answer on purpose -- a task spec travels to whoever asks, including the agent's + # own side of the wire. Grading needs the gold, so the rollout path takes the full task. + task = task_at(split, index) + config = DataAgentConfig( + sandbox=sandbox or self._sandbox, + agent_step_limit=agent_step_limit, + agent_timeout_s=agent_timeout_s, + max_output_tokens=4096 if split.partition(":")[0] == "test" else 16384, + ) + result = run_rollout( + task, + llm_url=llm_url or self._llm_url, + model=model or self._model, + hf_token=self._hf_token, + config=config, + require_tokens=require_tokens, + api_key=api_key or None, + sampling=sampling, + ) + self._state.rollout_type = result.rollout_type + self._state.split = split + self._state.index = index + self._state.task_id = task.instruction_id + return result.model_dump_json() diff --git a/04-data-agent/envs/blackbox-opencode/server/rollout.py b/04-data-agent/envs/blackbox-opencode/server/rollout.py new file mode 100644 index 0000000..5e31403 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/server/rollout.py @@ -0,0 +1,466 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Server-side execution of one rollout: mint capture, boot sandbox, run agent, grade. + +CONCURRENCY IS A HARD CEILING HERE, NOT A TUNING KNOB +Three separate limits stack, and the tightest one is not the obvious one: + + * THE CAPTURE PROXY IS A SINGLE UVICORN PROCESS. It is the real ceiling. Measured: `/health` + starved at ~200 concurrent sessions while rollouts still succeeded, and the process crashed + outright at 320 (3,525 file descriptors, 542 threads, 6.7 GB). It survived 75+ minutes at 200. + * E2B allows 500 concurrent sandboxes per account, but the practical limit is lower because the + capture server gives out first. + * Sessions release SLOWLY, not instantly, and killing a client leaks its sessions. Leftovers + collide with the next run's claim and surface as a burst of CAPACITY_REACHED. + +So the gate below is deliberately well under the crash point, and it is a SEMAPHORE rather than a +rejection: a rollout that arrives over the limit waits its turn instead of failing. A training run +and an evaluation run share one deployment, and the eval must not be able to starve training out or +take the proxy down with it. +""" + +from __future__ import annotations + +import logging +import os +import threading +import uuid +from typing import Any + +from ..config import DataAgentConfig +from ..models import DataAgentRolloutResult, DataAgentTurn +from ..reward import data_agent_reward +from ..sandbox import build_backend +from ..task import DataAgentTask +from ..verifier import answer_paths_for, grade_rollout, metadata_for + + +logger = logging.getLogger(__name__) + +# Well under the 320 that killed the capture process and the ~200 where /health starved. Raise it +# only alongside a measurement, and remember that a deployment serves training AND eval at once. +MAX_CONCURRENT_ROLLOUTS = int(os.environ.get("DATA_AGENT_MAX_CONCURRENT", "64")) + +_SLOTS = threading.BoundedSemaphore(MAX_CONCURRENT_ROLLOUTS) + + +def concurrency_status() -> dict[str, Any]: + """What `capabilities()` reports, so a caller can size its own inflight budget.""" + return { + "max_concurrent_rollouts": MAX_CONCURRENT_ROLLOUTS, + "note": ( + "the capture proxy is a single uvicorn process; it starved /health at ~200 concurrent " + "and crashed at 320. Training and eval share this budget." + ), + } + + +def run_rollout( + task: DataAgentTask, + *, + llm_url: str, + model: str, + hf_token: str | None, + config: DataAgentConfig, + require_tokens: bool = True, + api_key: str | None = None, + sampling: dict[str, float | int] | None = None, +) -> DataAgentRolloutResult: + """Run one rollout end to end. Never raises. + + A rollout that fails to launch, or whose agent dies, comes back UNGRADED (`reward=None`) rather + than as a zero. That distinction is load-bearing: the trainer drops an ungraded rollout from the + group baseline, whereas a zero says the policy was wrong. One suite once emitted a null reward, + the type rejected it, and 86 of 250 tasks vanished from scoring while the run printed clean + numbers over a third of the data. + + Args: + task (`DataAgentTask`): + The task to run, carrying gold and the bucket to stage. + llm_url (`str`): + The engine behind the proxy. + model (`str`): + Served model id. + hf_token (`str`, *optional*): + Token for staging the task's tables. Resolved once at startup, not per rollout. + config (`DataAgentConfig`): + Sandbox choice, timeouts, step cap. + require_tokens (`bool`, *optional*, defaults to `True`): + Refuse an engine that cannot return token ids. True for training, where such a rollout is + worthless; False for evaluation, where a text-only endpoint is a fine backend. + + Returns: + `DataAgentRolloutResult`: turns with engine token ids, the grade, and how it was graded. + """ + rollout_id = uuid.uuid4().hex + acquired = _SLOTS.acquire(timeout=config.agent_timeout_s * 2) + if not acquired: + logger.warning( + "rollout %s waited past its budget for a slot; returning ungraded", + rollout_id, + ) + return DataAgentRolloutResult( + metadata={"error": "no capacity", "rollout_id": rollout_id} + ) + server = None + session_id = None + sandbox = None + rollout_type = "eval" + try: + from .capture import ( + agent_base_url, + capture_server, + engine_tier, + fetch_turns, + mint_session, + ) + + server = capture_server(llm_url, model) + capture_url = agent_base_url(server) + session_id, rollout_type = mint_session( + server, + llm_url=llm_url, + model=model, + rollout_id=rollout_id, + capture_level=engine_tier(llm_url, model, require_tokens=require_tokens, api_key=api_key), + api_key=api_key, + sampling=sampling, + max_output_tokens=config.max_output_tokens, + max_model_calls=config.agent_step_limit, + task=task.instruction_id, + sandbox=config.sandbox, + ) + backend = build_backend(config.sandbox, image=config.image) + # `create` takes only timeout/envs/metadata -- there is no setup hook on it. Staging runs as a + # separate `exec` below, which is also what `opencode_env`'s harness does. + sandbox = backend.create( + timeout_s=int(config.agent_timeout_s), + envs=task.env(None), + metadata={"rollout_id": rollout_id, "task": task.instruction_id}, + ) + _wait_ready(sandbox) + _ensure_opencode(sandbox, config) + _stage_inputs(sandbox, task, hf_token, config) + # The agent's API KEY is the capture session id. That is how one proxy serves many concurrent + # rollouts without a port per rollout, and why the sandbox never sees a real credential. + exit_code = _run_agent( + sandbox, capture_url, session_id, model, config, task.instruction + ) + timed_out = exit_code != 0 + + turns, capture_findings = fetch_turns(server, session_id) + n_tool_calls = sum(len(t.tool_calls) for t in turns) + final = turns[-1].text if turns else None + + # ZERO MODEL CALLS IS AN INFRASTRUCTURE FAILURE, NOT A WRONG ANSWER. + # + # If capture saw nothing, the agent never reached the proxy -- opencode missing from the + # image, a base URL the sandbox cannot route to, auth rejected. Grading that produces + # correctness 0.0, which says the POLICY was wrong, and the trainer then counts it in the + # group baseline. An ungraded rollout is dropped from the baseline instead, which is the + # honest treatment: nothing about the model was measured here. + if not turns: + logger.warning( + "rollout %s produced no model calls; returning ungraded. findings: %s", + rollout_id, + "; ".join(capture_findings[:3]) or "none", + ) + return DataAgentRolloutResult( + rollout_type=rollout_type, + turns=[], + timed_out=timed_out, + metadata={ + "error": "the agent made no model calls", + "rollout_id": rollout_id, + "session_id": session_id, + "sandbox": config.sandbox, + "capture_findings": capture_findings, + }, + ) + + grade = grade_rollout( + task, sandbox.read_text, answer_paths_for(config.home), final_message=final + ) + reward = data_agent_reward(grade.correctness, n_tool_calls) + return DataAgentRolloutResult( + rollout_type=rollout_type, + reward=reward, + correctness=grade.correctness, + answer=grade.answer, + answer_source=grade.source, + graded_by=grade.graded_by, + turns=turns, + n_tool_calls=n_tool_calls, + timed_out=timed_out, + metadata={ + **metadata_for(task, grade, n_tool_calls), + "rollout_id": rollout_id, + "session_id": session_id, + "sandbox": config.sandbox, + "implementation": "standalone-opencode", + "opencode_version": OPENCODE_VERSION, + "max_output_tokens": config.max_output_tokens, + "task_id": task.task_id, + # Surfaced rather than swallowed: `per_turn_capture_only` means the turns are exact + # but became one graph root each, so a consumer expecting multi-turn credit + # assignment is not getting it. That is invisible in the turn list itself. + "capture_findings": capture_findings, + }, + ) + except Exception as exc: # noqa: BLE001 -- a flaky sandbox must not take the server down + logger.warning( + "rollout %s failed; returning ungraded", rollout_id, exc_info=True + ) + # Preserve observed tokens for diagnosis even when infrastructure prevents + # grading. A partial capture must never turn an ungraded attempt into zero. + turns, capture_findings = [], [] + if server is not None and session_id is not None: + try: + turns, capture_findings = fetch_turns(server, session_id) + except Exception as capture_exc: + capture_findings = [f"capture export failed: {type(capture_exc).__name__}"] + return DataAgentRolloutResult( + rollout_type=rollout_type, turns=turns, + metadata={"error": f"{type(exc).__name__}: {exc}", "rollout_id": rollout_id, + "task_id": task.task_id, "capture_findings": capture_findings} + ) + finally: + # Order matters: kill the sandbox and drop the capture session before releasing the slot, or + # the next rollout claims a slot while this one is still holding an E2B seat and a live + # session. Leftover sessions collide with the next run's claim and surface as a burst of + # CAPACITY_REACHED on a server that looks idle. + if sandbox is not None: + try: + sandbox.kill() + except Exception: + logger.warning( + "sandbox cleanup failed for %s", rollout_id, exc_info=True + ) + if server is not None and session_id is not None: + from .capture import release_session + + release_session(server, session_id) + _SLOTS.release() + + +def _stage_inputs(sandbox: Any, task: DataAgentTask, hf_token: str | None, config: DataAgentConfig) -> None: + """Pull this task's tables into the sandbox before the agent starts. + + RETRIED ONCE, deliberately. The exec channel is the fragile part of a sandbox, not the registry: + across 13,200 trials, 5 of the 6 hard install failures were + `Request timed out: the stream didn't open within 'request_timeout' (60.0 s)`, with zero npm/nvm + rate-limiting. A single transient exec failure would otherwise cost the whole rollout. + + A staging failure RAISES rather than continuing. An agent that starts with no data files cannot + solve the task, and the resulting empty answer scores identically to a model that could not do + it -- so this must surface as an ungraded rollout, not as a zero. + """ + setup = task.setup_shell(hf_token) + if not setup: + return + last = None + for attempt in (1, 2): + result = sandbox.exec(setup, timeout=config.setup_timeout_s, envs=task.env(hf_token)) + code = _exit_code(result, default=0) + if code == 0: + return + last = getattr(result, "stderr", "") or getattr(result, "stdout", "") + logger.warning("staging attempt %d failed (%s): %s", attempt, code, str(last)[:300]) + raise RuntimeError(f"staging this task's inputs failed: {str(last)[:400]}") + + +# opencode lands here when installed at runtime; the E2B template also puts it on PATH. +OPENCODE_BIN = "$HOME/.opencode/bin" +OPENCODE_VERSION = os.environ.get("DATA_AGENT_OPENCODE_VERSION", "1.18.31") + + +def _exit_code(result: Any, *, default: int = 1) -> int: + """Exit code of an `ExecResult`, treating a MISSING code and a ZERO code as different things. + + `int(getattr(r, "exit_code", 1) or 1)` looks right and is not: `0 or 1` is 1, so every SUCCESSFUL + command reads as a failure. That turned "is opencode installed?" into a permanent no, which made + every rollout reinstall it, and the installer then exits non-zero on "already installed" -- so a + perfectly good sandbox failed with a message saying the thing it needed was already there. + """ + code = getattr(result, "exit_code", None) + return default if code is None else int(code) + + +def _wait_ready(sandbox: Any, *, attempts: int = 15, delay_s: float = 1.0) -> None: + """Probe until `echo ok` succeeds. A backend returns the handle before the guest is usable. + + Without this, the FIRST command run in the sandbox fails for a reason that has nothing to do with + what it was trying to do. Here that surfaced as "opencode is not installed" on an image where it + was installed all along -- the probe simply ran too early. + """ + import time + + last = "" + for _ in range(attempts): + try: + r = sandbox.exec("echo ok", timeout=5) + if _exit_code(r) == 0 and "ok" in (getattr(r, "stdout", "") or ""): + return + last = (getattr(r, "stderr", "") or getattr(r, "stdout", "") or "").strip() + except Exception as exc: # noqa: BLE001 -- a not-yet-listening guest raises rather than returns + last = f"{type(exc).__name__}: {exc}" + time.sleep(delay_s) + raise RuntimeError(f"sandbox never became ready: {last[:300]}") + + +def _opencode_present(sandbox: Any) -> bool: + """Whether `opencode` runs in this sandbox. The PATH export is load-bearing on a fresh install.""" + try: + r = sandbox.exec(f'export PATH="{OPENCODE_BIN}:$PATH"; opencode --version', timeout=20) + return _exit_code(r) == 0 and r.stdout.strip() == OPENCODE_VERSION + except Exception: # noqa: BLE001 + return False + + +def _ensure_opencode(sandbox: Any, config: DataAgentConfig) -> None: + """Install opencode if the image does not already ship it. + + THE TWO BACKENDS DIFFER HERE AND IT IS NOT COSMETIC. The E2B template bakes opencode in, so the + probe short-circuits and a rollout starts immediately. The HF image carries the data-science stack + only, so opencode is installed at runtime -- roughly 30-50 s of the rollout. + + Skipping it on HF produces no useful error: `opencode run` is simply not found, the agent makes + ZERO model calls, and the rollout returns an empty answer. Capture's `no_turns` finding is the + only clue. + + SUCCESS IS DECIDED BY RE-PROBING, NOT BY THE INSTALLER'S EXIT CODE. The upstream installer exits + non-zero when it finds the version already present ("Version 1.18.30 already installed"), so + trusting the code turns a working sandbox into a failed rollout. + """ + if _opencode_present(sandbox): + return + install = ( + f"mkdir -p {config.home}/.config/opencode {config.home}/workdir && " + "set -o pipefail; curl -fsSL https://opencode.ai/install | bash -s -- --version " + + __import__("shlex").quote(OPENCODE_VERSION) + ) + last = None + for attempt in (1, 2, 3): + try: + result = sandbox.exec(install, timeout=config.install_timeout_s) + last = getattr(result, "stderr", "") or getattr(result, "stdout", "") + except Exception as exc: # noqa: BLE001 -- curl | bash is flaky; retry rather than abort + last = f"{type(exc).__name__}: {exc}" + if _opencode_present(sandbox): + logger.info("opencode available after install attempt %d", attempt) + return + logger.warning("opencode still absent after attempt %d: %s", attempt, str(last)[:300]) + raise RuntimeError(f"could not install opencode in the sandbox: {str(last)[:400]}") + + +def _run_agent( + sandbox: Any, + capture_url: str, + session_id: str, + model: str, + config: DataAgentConfig, + instruction: str, +) -> int: + """Configure opencode inside the sandbox and run it to completion. + + Written into `{home}` rather than a fixed path: the home differs by backend, and a config the + agent cannot read means it starts with no model configured and makes zero model calls. + """ + import json + + settings = { + "$schema": "https://opencode.ai/config.json", + "provider": { + "openai_compatible": { + # `npm` names the SDK opencode loads for a custom provider; the reference sets it + # explicitly rather than relying on the provider key resolving by name. + "npm": "@ai-sdk/openai-compatible", + "name": "Intercepted", + "options": { + "baseURL": f"{capture_url}/v1", + "apiKey": session_id, + # 10 minutes, matching the reference. Qwen3.5 is hybrid linear-attention with NO + # prefix caching, so every turn reprocesses the whole conversation and late turns + # are slow. An undeclared client timeout turns that into a FAILED tool result + # rather than a slow one. + "timeout": 600_000, + }, + "models": {model: {}}, + } + }, + "model": f"openai_compatible/{model}", + **config.opencode_settings(), + } + sandbox.write_text( + f"{config.home}/.config/opencode/opencode.json", json.dumps(settings, indent=2) + ) + # THE INSTRUCTION GOES THROUGH A FILE, NEVER ONTO THE COMMAND LINE. + # + # `json.dumps(instruction)` produces a DOUBLE-quoted shell word, and these instructions contain a + # backticked example: + # + # Write only that value to /workdir/answer.txt (e.g. `echo -n "" > /workdir/answer.txt`) + # + # Inside double quotes the shell runs backticks as COMMAND SUBSTITUTION. So the shell itself wrote + # `` into answer.txt before the agent started, AND deleted the example from the text the + # agent got, which arrived as "(e.g. ), then stop.". Measured: 498 of 575 eval rollouts filed the + # literal string ``, dragging pass@1 to 0.032 against 0.104 for the same model under an + # invocation that used a file. It reads as a model that cannot follow instructions. + # + # json.dumps also escapes newlines to a literal two-character \n, so a multi-paragraph instruction + # reached the agent as one line of backslash-n. + # + # A file has neither problem, and it is what the working eval harness did. + sandbox.write_text(f"{config.home}/workdir/task.md", instruction) + # The shared backend protocol already supports detached execution. In particular, + # Daytona's synchronous exec holds one HTTP request for the whole command and can + # lose its result at the command deadline. Poll a background process instead; + # a genuine agent budget expiry still leaves the captured trajectory available. + process = sandbox.start_bg( + f'export PATH="{OPENCODE_BIN}:$PATH"; ' + f"cd {config.home}/workdir && " + f'opencode run --print-logs "$(cat {config.home}/workdir/task.md)"', + ) + try: + return process.wait(timeout=config.agent_timeout_s) + except TimeoutError: + process.kill() + return 124 + + +def turns_from_capture(entries: list[dict[str, Any]]) -> list[DataAgentTurn]: + """Capture trace entries -> `DataAgentTurn`s, keeping the engine's own tokenization.""" + out = [] + for i, e in enumerate(entries): + msg = ((e.get("response") or {}).get("choices") or [{}])[0].get("message") or {} + out.append( + DataAgentTurn( + turn=i, + prompt_token_ids=list(e.get("prompt_token_ids") or []), + completion_token_ids=list(e.get("completion_token_ids") or []), + per_token_logps=list(e.get("per_token_logps") or []), + loss_mask=list(e.get("loss_mask") or []), + capture_metadata=dict(e.get("metadata") or {}), + trainable=bool(any(e.get("loss_mask") or [])), + request_messages=list((e.get("request") or {}).get("messages") or []), + request_tools=(e.get("request") or {}).get("tools"), + text=msg.get("content") or "", + tool_calls=list(msg.get("tool_calls") or []), + finish_reason=((e.get("response") or {}).get("choices") or [{}])[0].get( + "finish_reason" + ), + ) + ) + return out diff --git a/04-data-agent/envs/blackbox-opencode/task.py b/04-data-agent/envs/blackbox-opencode/task.py new file mode 100644 index 0000000..c1a9b7d --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/task.py @@ -0,0 +1,196 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""One data-agent task: what the agent is asked, where its data lives, and how it will be graded.""" + +from __future__ import annotations + +import hashlib +import os +import shlex +from typing import Any + +from pydantic import BaseModel, Field + + +# Absolute paths the instruction text itself promises the agent. They cannot be changed here without +# editing every instruction in the dataset, so they are constants rather than configuration. +INPUT_DIR = "/home/user/input" +ANSWER_PATH = "/workdir/answer.txt" + +# The agent may write its answer relative to a working directory that is not `/`, so the same file +# legitimately appears under the sandbox home. Both are read back; see `verifier.py`. +ANSWER_PATHS = (ANSWER_PATH, "{home}/workdir/answer.txt", "/root/workdir/answer.txt") + + +def instruction_id(instruction: str) -> str: + """Stable id for an instruction string. + + The loop-owning path forwards only the prompt, so this is how a rollout finds its way back to the + task that produced it. Hashing the instruction rather than trusting a row index means the mapping + survives reordering, filtering and curricula. + """ + return hashlib.sha1(instruction.encode()).hexdigest() + + +def _tolerance(value: Any) -> float: + """Default an omitted tolerance while preserving an explicit numeric zero.""" + return 1e-3 if value is None or value == "" else float(value) + + +class DataAgentTask(BaseModel): + """A single task, parsed from one dataset row. + + Attributes: + task_id (`str`): + The dataset's own identifier, carried for reporting. + instruction (`str`): + The full prompt shown to the agent, including the submission protocol. + answer (`str`): + Gold value. Never sent to the sandbox. + question (`str`): + The question alone, without the surrounding protocol text. + reward_mode (`str`): + How `grader.py` should compare: exact, numeric, list. + atol (`float`): + Absolute tolerance for numeric comparison. + rtol (`float`): + Relative tolerance for numeric comparison. + hf_bucket (`str`): + Hugging Face bucket holding this task's tables. + bucket_prefix (`str`): + Prefix within the bucket. + files (`list[str]`): + File names staged into `INPUT_DIR`, used only to name them in the prompt. + difficulty_tier (`str`): + easy, medium or hard. + difficulty_level (`int`, *optional*): + The dataset's finer-grained level, carried through for analysis. + """ + + task_id: str = "" + instruction: str + answer: str + question: str = "" + reward_mode: str = "" + atol: float = 1e-3 + rtol: float = 1e-3 + hf_bucket: str + bucket_prefix: str + files: list[str] = Field(default_factory=list) + difficulty_tier: str | None = None + # An INT in the dataset (1/2/3), not a string -- `difficulty_tier` is the word form + # ('easy'/'medium'/'hard') and these two are easy to mix up. Declaring this `str` made + # every `get_task` 500 with a pydantic error that named the field but not the dataset. + difficulty_level: int | None = None + + @classmethod + def from_row(cls, row: dict[str, Any]) -> "DataAgentTask": + """Build from one `HuggingEnvs/data-agent` row.""" + return cls( + task_id=str(row.get("task_id") or ""), + instruction=row["instruction"], + answer=str(row["answer"]), + question=row.get("question", "") or "", + reward_mode=row.get("reward_mode") or "", + atol=_tolerance(row.get("atol")), + rtol=_tolerance(row.get("rtol")), + hf_bucket=row["hf_bucket"], + bucket_prefix=row["bucket_prefix"], + files=list(row.get("files") or []), + difficulty_tier=row.get("difficulty_tier"), + difficulty_level=row.get("difficulty_level"), + ) + + @property + def instruction_id(self) -> str: + return instruction_id(self.instruction) + + def env(self, token: str | None) -> dict[str, str]: + """Environment for the staging step. + + A missing token is not defaulted to empty: the bucket pull then succeeds while downloading + nothing, `INPUT_DIR` is empty, and every rollout scores zero in a way indistinguishable from + a model that cannot do data analysis. The caller resolves the token once at startup and fails + there instead. + """ + env = { + "HF_BUCKET": self.hf_bucket, + "BUCKET_PREFIX": self.bucket_prefix, + "INPUT_DIR": INPUT_DIR, + } + if token: + env["HF_TOKEN"] = token + return env + + def setup_shell(self, token: str | None) -> str: + """Shell that stages this task's tables into `INPUT_DIR` before the agent starts. + + HF BUCKETS, NOT A DATASET REPO. `hf_bucket` names a bucket (`hf://buckets//`), + so it is read with `list_bucket_tree` / `download_bucket_files`. Reaching for + `snapshot_download(repo_type="dataset")` instead returns a plain 404 that names the repo and + reads exactly like a permissions problem. + + FAILS LOUDLY ON AN EMPTY DIRECTORY, with a distinct exit code per cause. A silent miss hands + the agent a task whose data is absent; it then scores 0 for a reason indistinguishable from a + wrong answer, which is the most expensive kind of failure to debug because the reward looks + entirely plausible. + + Credentials travel by NAME through the environment and are never interpolated into the command + text, so a token cannot reach a log line or a trace. + """ + bucket = self.hf_bucket + prefix = self.bucket_prefix.rstrip("/") + return ( + "set -e; " + f"mkdir -p {shlex.quote(INPUT_DIR)} /workdir; " + "python3 - <<'PULL'\n" + "import sys\n" + "from pathlib import Path\n" + "from huggingface_hub import download_bucket_files, list_bucket_tree\n" + f"dest = Path({INPUT_DIR!r})\n" + "dest.mkdir(parents=True, exist_ok=True)\n" + "if [p for p in dest.iterdir() if p.is_file()]:\n" + " print('[pull] already staged'); sys.exit(0)\n" + # Flattened AT DOWNLOAD TIME rather than moved afterwards: the instruction promises the + # files directly in INPUT_DIR with no subfolders, and a post-hoc `find -exec mv` silently + # collides when two prefixes contain the same basename. + f"targets = [(it.path, str(dest / Path(it.path).name))\n" + f" for it in list_bucket_tree({bucket!r}, prefix={prefix + '/'!r}, recursive=True)\n" + " if getattr(it, 'type', None) == 'file']\n" + "if not targets:\n" + f" print('[pull] FATAL: nothing at hf://buckets/{bucket}/{prefix}'); sys.exit(2)\n" + f"download_bucket_files({bucket!r}, files=targets)\n" + "print('[pull] staged', len(targets), 'file(s)')\n" + "PULL\n" + f'[ -n "$(ls -A {shlex.quote(INPUT_DIR)})" ] || {{ echo "[pull] FATAL: {INPUT_DIR} empty"; exit 3; }}' + ) + + +def resolve_hf_token() -> str | None: + """The Hub token, from the environment or a cached login. + + Checked at server startup rather than per rollout: see `env()` for why an absent token is worse + than an obvious failure. + """ + for name in ("HF_TOKEN", "HUGGINGFACE_HUB_TOKEN", "HF_API_KEY"): + value = os.environ.get(name) + if value: + return value + try: + from huggingface_hub import get_token + + return get_token() + except Exception: + return None diff --git a/04-data-agent/envs/blackbox-opencode/tasks.py b/04-data-agent/envs/blackbox-opencode/tasks.py new file mode 100644 index 0000000..2918802 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/tasks.py @@ -0,0 +1,265 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Task discovery for the data-agent suite, served through OpenEnv's Task API. + +WHY EVERY CACHE HERE IS MODULE LEVEL +`HTTPEnvServer` constructs a FRESH environment for every task request and closes it in a `finally` +(`core/env_server/http_server.py`). Anything cached on `self` is therefore rebuilt on each `/task` +call, and a dataset download would happen per request. The caches below live at module scope, which +is the same discipline `openenv.harbor.tasks` follows for the same reason. + +WHY DIFFICULTY IS A SPLIT AND NOT A FILTER ARGUMENT +A task's INDEX is its identity everywhere downstream: checkpoints, eval subsets and reproduction +manifests all record it. A filter applied at listing time shifts every index after it, so "task 12" +silently means different things to two callers. `train:medium` is therefore its own split with its +own index space, and no method takes a difficulty keyword. +""" + +from __future__ import annotations + +import logging +import threading +from typing import Any + +from .task import DataAgentTask, instruction_id, _tolerance + + +logger = logging.getLogger(__name__) + +DATASET = "HuggingEnvs/data-agent" + +# Unbounded listing would materialise thousands of task specs on a throwaway instance, and the Task +# API request times out before it answers. `num_tasks` still reports the true total, which the +# TaskProvider protocol explicitly allows. +PREVIEW_LIMIT = 200 + +TIERS = ("easy", "medium", "hard") + +_LOCK = threading.Lock() +_ROWS: dict[ + str, list[dict[str, Any]] +] = {} # split name -> usable rows, in dataset order +_TASKS: dict[tuple[str, int], DataAgentTask] = {} # (split, index) -> parsed task + + +def _load_split(split: str) -> list[dict[str, Any]]: + """Rows for one base split, dropping any that cannot produce a gradable rollout. + + A row without a bucket stages no data, and a row without an answer cannot be graded; both would + score zero for reasons that have nothing to do with the policy. They are dropped at discovery so + they never reach a sandbox, and the count is logged rather than silently absorbed. + """ + import os + frozen = os.environ.get("DATA_AGENT_FROZEN_TASKS_DIR") + if frozen: + return _frozen_rows(frozen, split) + from datasets import load_dataset + + raw = list(load_dataset(DATASET, split=split)) + rows, skipped = [], {"no_bucket": 0, "no_answer": 0} + for row in raw: + if not row.get("hf_bucket") or not row.get("bucket_prefix"): + skipped["no_bucket"] += 1 + continue + if not str(row.get("answer") or "").strip(): + skipped["no_answer"] += 1 + continue + rows.append(row) + if any(skipped.values()): + logger.warning( + "data-agent %s: %d of %d rows unusable %s", + split, + sum(skipped.values()), + len(raw), + skipped, + ) + return rows + + +def rows_for(split: str) -> list[dict[str, Any]]: + """Rows for a split name, which is `` or `:`. Cached for the process.""" + with _LOCK: + if split in _ROWS: + return _ROWS[split] + base, _, tier = split.partition(":") + if tier and tier not in TIERS: + raise ValueError(f"unknown difficulty tier {tier!r}; expected one of {TIERS}") + rows = _load_split(base) + if tier: + rows = [r for r in rows if r.get("difficulty_tier") == tier] + with _LOCK: + _ROWS.setdefault(split, rows) + return _ROWS[split] + + +def task_at(split: str, index: int) -> DataAgentTask: + """The parsed task at `index` within `split`. + + Raises: + IndexError: If out of range. `HTTPEnvServer` turns this into a 400 rather than a 500. + """ + key = (split, index) + with _LOCK: + cached = _TASKS.get(key) + if cached is not None: + return cached + rows = rows_for(split) + if index < 0 or index >= len(rows): + raise IndexError( + f"task index {index} out of range for split {split!r} ({len(rows)} tasks)" + ) + task = DataAgentTask.from_row(rows[index]) + with _LOCK: + _TASKS.setdefault(key, task) + return task + + +def index_of_instruction(split: str, instruction: str) -> int | None: + """Recover a task index from its instruction text. + + The loop-owning trainer forwards only the prompt, so this is how a rollout is matched back to its + task. Collisions are counted and warned about rather than resolved last-write-wins: these + instructions are template-generated, so two tasks CAN share wording, and a silent overwrite would + grade a rollout against the wrong gold answer. + """ + wanted = instruction_id(instruction) + hits = [ + i + for i, row in enumerate(rows_for(split)) + if instruction_id(row["instruction"]) == wanted + ] + if not hits: + return None + if len(hits) > 1: + logger.warning( + "instruction maps to %d tasks in %s (indices %s); using the first. Gold answers may differ.", + len(hits), + split, + hits[:5], + ) + return hits[0] + + +def prefetch(splits: list[str]) -> None: + """Warm the caches before the server accepts traffic, so no request pays for a dataset download.""" + for split in splits: + try: + rows_for(split) + except Exception: + logger.warning("prefetch failed for split %s", split, exc_info=True) + + +class DataAgentTaskProvider: + """The five Task API methods, over `HuggingEnvs/data-agent`. + + Holds only the split names. Everything expensive lives in this module's caches, because the + server rebuilds this object per request. + """ + + def __init__(self, splits: list[str]) -> None: + self._splits = list(splits) + + def list_splits(self) -> list[dict[str, Any]]: + """Every split, each with its own index space. + + Returned as dicts rather than bare strings: the server coerces an unrecognised string's + `type` to `"validation"`, so anything that is not literally train/validation/test would be + mislabelled. + """ + out: list[dict[str, Any]] = [] + for configured in self._splits: + # A configured split may ALREADY name a tier (`train:medium`). Appending tiers to it + # produces `train:medium:easy`, which is not a split -- it parses as tier `medium:easy` + # and every one of them comes back as an error entry, burying the real splits in noise. + base = configured.split(":", 1)[0] + tiers = () if ":" in configured else TIERS + for name in (configured, *(f"{base}:{tier}" for tier in tiers)): + entry: dict[str, Any] = { + "name": name, + "type": "train" if base == "train" else "validation", + "dataset": DATASET, + } + try: + entry["num_tasks"] = len(rows_for(name)) + except ( + Exception + ) as exc: # a broken split must not hide the working ones + entry["error"] = str(exc) + out.append(entry) + return out + + def num_tasks(self, split: str) -> int: + return len(rows_for(split)) + + def get_task(self, split: str, index: int) -> dict[str, Any]: + task = task_at(split, index) + return _public(task, split, index) + + def get_task_range( + self, split: str, start: int | None = None, stop: int | None = None + ) -> list[dict[str, Any]]: + total = len(rows_for(split)) + lo = 0 if start is None else max(0, start) + hi = total if stop is None else min(total, stop) + return [self.get_task(split, i) for i in range(lo, hi)] + + def list_tasks(self, split: str) -> list[dict[str, Any]]: + """A bounded preview. `num_tasks` remains the authority on how many there are.""" + return self.get_task_range(split, 0, PREVIEW_LIMIT) + + +def _public(task: DataAgentTask, split: str, index: int) -> dict[str, Any]: + """What a caller may see. The gold answer is deliberately absent: it is grading material, and a + task spec travels to whoever asks, including the agent's own side of the wire.""" + return { + "split": split, + "index": index, + "task_id": task.task_id, + "instruction": task.instruction, + "question": task.question, + "difficulty_tier": task.difficulty_tier, + "difficulty_level": task.difficulty_level, + "reward_mode": task.reward_mode, + "files": task.files, + } + + +def _frozen_rows(root: str, split: str) -> list[dict[str, Any]]: + """Read the shared frozen data without invoking Harbor's execution path.""" + from pathlib import Path + import hashlib + import json + import tomllib + if split not in {"train", "test"}: + raise ValueError("Frozen task split must be train or test") + root = Path(root) + manifest = json.loads((root.parent / f"{split}_manifest.json").read_text()) + rows = [] + for entry in sorted(manifest["tasks"], key=lambda t: t["name"]): + path = root / split / "tasks" / entry["name"] + instruction = (path / "instruction.md").read_text() + expected = entry["file_hashes"][f"tasks/{entry['name']}/instruction.md"] + if hashlib.sha256(instruction.encode()).hexdigest() != expected: + raise ValueError(f"Frozen instruction hash mismatch: {entry['name']}") + if hashlib.sha256((path / "task.toml").read_bytes()).hexdigest() != entry.get("effective_task_toml_sha256", entry["file_hashes"][f"tasks/{entry['name']}/task.toml"]): + raise ValueError(f"Frozen task configuration mismatch: {entry['name']}") + spec = tomllib.loads((path / "task.toml").read_text()) + meta, env, verifier = spec["metadata"], spec["environment"]["env"], spec["verifier"]["env"] + rows.append(dict(task_id=entry["name"], instruction=instruction, answer=meta["gold_answer"], + question=spec["task"]["description"], reward_mode=verifier["REWARD_MODE"], + atol=_tolerance(verifier.get("ATOL")), rtol=_tolerance(verifier.get("RTOL")), + hf_bucket=env["HF_BUCKET"], bucket_prefix=env["BUCKET_PREFIX"], + difficulty_tier=meta["difficulty_tier"], difficulty_level=meta["difficulty_level"])) + return rows diff --git a/04-data-agent/envs/blackbox-opencode/tools/needle.txt b/04-data-agent/envs/blackbox-opencode/tools/needle.txt new file mode 100644 index 0000000..3e8d27f --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/tools/needle.txt @@ -0,0 +1 @@ +data_agent_env.server.app:app diff --git a/04-data-agent/envs/blackbox-opencode/tools/start_server.sh b/04-data-agent/envs/blackbox-opencode/tools/start_server.sh new file mode 100755 index 0000000..7049196 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/tools/start_server.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Start blackbox-opencode against the live engine. Credentials come from experiments/.env by NAME. +set -euo pipefail +cd "$(dirname "$0")/.." +set -a; . ../../../../experiments/.env; set +a +. "$(dirname "$0")/../../blackbox-harbor/tools/hf_token.sh" +export PORT="${PORT:-8200}" +export SPLITS="${SPLITS:-train:medium}" +export SANDBOX="${SANDBOX:-e2b}" +export EXPOSE="${EXPOSE:-gradio}" +export CAPTURE_PORT="${CAPTURE_PORT:-8301}" +export LLM_URL="${LLM_URL:?set LLM_URL}" +export MODEL="${MODEL:-Qwen/Qwen3.5-2B}" +exec ./serve.sh diff --git a/04-data-agent/envs/blackbox-opencode/tools/stop_all.sh b/04-data-agent/envs/blackbox-opencode/tools/stop_all.sh new file mode 100755 index 0000000..6423797 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/tools/stop_all.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Stop the env server and any rollout. +# +# MATCHES ON argv[0] BEING A PYTHON INTERPRETER, not on the command line containing a string. A +# substring match also hits every bash wrapper whose command line happens to quote the same command -- +# including the agent harness's own shells. That has killed this session's shell once and deadlocked a +# restart loop for 11 minutes, and CLAUDE.md warns about exactly it. Ancestors are skipped too, so +# this can never kill whatever launched it. +cd "$(dirname "$0")/.." + +ancestors=" " +p=$$ +while [ "$p" -gt 1 ] 2>/dev/null; do + ancestors="$ancestors$p " + p=$(awk '{print $4}' /proc/$p/stat 2>/dev/null) || break +done + +for pid in $(ls /proc 2>/dev/null | grep -E '^[0-9]+$'); do + case "$ancestors" in *" $pid "*) continue;; esac + [ -r "/proc/$pid/cmdline" ] || continue + argv0=$(tr '\0' '\n' < "/proc/$pid/cmdline" 2>/dev/null | head -1) + case "$argv0" in *python*) ;; *) continue;; esac + rest=$(tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null) + case "$rest" in + *"uvicorn data_agent_env.server.app"*|*"rollout.py --server"*) + echo " stopping $pid: $(echo "$rest" | cut -c1-90)" + kill "$pid" 2>/dev/null ;; + esac +done diff --git a/04-data-agent/envs/blackbox-opencode/uv.lock b/04-data-agent/envs/blackbox-opencode/uv.lock new file mode 100644 index 0000000..8694603 --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/uv.lock @@ -0,0 +1,3714 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'win32'", + "python_full_version < '3.13' and sys_platform == 'win32'", + "python_full_version == '3.13.*' and sys_platform == 'emscripten'", + "python_full_version < '3.13' and sys_platform == 'emscripten'", + "python_full_version == '3.13.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.13' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] + +[[package]] +name = "aiofile" +version = "3.12.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "caio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/14/31/edb06aabd8f8f0b56d659f30800795f40b93cba96be946ce179f6931e3a5/aiofile-3.12.3.tar.gz", hash = "sha256:caa6aa746b5e47e2165f7abd741b6415e49cf4d44fddc0f61844612cc3924d41", size = 21600, upload-time = "2026-08-04T22:59:27.171Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/79/6e45e778c4c3cab39e0937b007b720c15f76c50c6453d153282d0fcc3588/aiofile-3.12.3-py3-none-any.whl", hash = "sha256:5c1bcc9e929c50834608e8cc1a4cc1d7503eb60c15a535b779fd39e2f372c017", size = 22122, upload-time = "2026-08-04T22:59:25.838Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757, upload-time = "2026-07-01T17:11:55.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038, upload-time = "2026-07-01T17:11:54.055Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/d9/22ce5786ac0c1653ae8b6c23bded02c1686d11f0dbb45b31ce128e0df985/aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc", size = 7971213, upload-time = "2026-07-23T01:57:27.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/d4/eb96299230e20acf2efae207cb8d69051f1f68e357e5ea5e479bf6fb097a/aiohttp-3.14.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:39aded8c7f3b935b54aab1d8d73c70ec0ee2d3ec3b943e0e86611bc150ba47f5", size = 754690, upload-time = "2026-07-23T01:53:47.332Z" }, + { url = "https://files.pythonhosted.org/packages/88/11/e7a70a209eb9a067c0d3212b518a0134e3484f5178c7533878b6b514d469/aiohttp-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5bcb6ff3fdab1258a192679ff1a05d44f59626430aa05cd1a9d2447423599228", size = 509484, upload-time = "2026-07-23T01:53:51.159Z" }, + { url = "https://files.pythonhosted.org/packages/30/07/4bbc222cc8dbe31d4c3e8a5baad2286e4d42026ac0c570027b89afce6344/aiohttp-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:617105e2c3018ee38d0c8ce5ee3c84f621a6d8b9f723202aacaff28449ca91ee", size = 511949, upload-time = "2026-07-23T01:53:55.083Z" }, + { url = "https://files.pythonhosted.org/packages/54/b9/42e74c46b7b7c794b995bbc1f573fb48950c38b19d8600c62a6804ee2d67/aiohttp-3.14.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f631fe87a6f30df5fbe6d79640b25e4cffb38c31c7fb6f10871517b84b0f8c1a", size = 1765282, upload-time = "2026-07-23T01:53:59.662Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ed/62bc4d74363ad346d518e0720363a949f63e2e23439a79eb5813d4d29bb3/aiohttp-3.14.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a94dbaae5ae27bd849c93570669bff91e0510f33a80805738e3de72a7be0447b", size = 1741511, upload-time = "2026-07-23T01:54:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9f/181e8a8bc79e47d13c7fc4540bd7a3b729d9505609c61f392a8dd2fbfe55/aiohttp-3.14.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8f2f1c4c032c7cedd7d8da6f54c97b70266c6570c3108d3fdffee7188bb70529", size = 1810680, upload-time = "2026-07-23T01:54:09.882Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9a/dec94d6ad694552fe3424e3f1928d7a606a5d9d9433a04e7ecdd9d38ae7f/aiohttp-3.14.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea05e1f97ceea523942d9b2a7d7c0359d781d683d6b043f5943a602b14da4787", size = 1905646, upload-time = "2026-07-23T01:54:13.475Z" }, + { url = "https://files.pythonhosted.org/packages/52/b7/7cd31f29d6055bd711ae6e669367fba6f5ae9de463910a793e30556a8db7/aiohttp-3.14.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42", size = 1792122, upload-time = "2026-07-23T01:54:15.752Z" }, + { url = "https://files.pythonhosted.org/packages/66/73/10b1ef93afa61f4963c746257b70ced619cf31a4798671de5fdb2608501d/aiohttp-3.14.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0a5ff2dfbb9ce645fa5b8ef3e02c6c0b9cc3f6030ff863d0c51fffc50cb5541b", size = 1591127, upload-time = "2026-07-23T01:54:19.489Z" }, + { url = "https://files.pythonhosted.org/packages/49/ed/3b203fa6de1b338c14acdc06bf6ca9b043b7944f005966958c2ced932cde/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:041badb8f84396357c4d3ad26de6afd7a32b112f43d3c63045c0c8278cfd2043", size = 1725210, upload-time = "2026-07-23T01:54:24.129Z" }, + { url = "https://files.pythonhosted.org/packages/28/b7/1c2aab8c706436dcc28598452488ac9cd7c409da815237c28c27d58993e6/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:530125ee1163c4219af35dc3aa1206e541e7b31b6efc1a3f93b70a136f65d427", size = 1764848, upload-time = "2026-07-23T01:54:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/54/50/94c28f08b131c4bf10984ea2c7a536c9920608bb2d6e7f95642c30cc87b7/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8653fd547c93a61aadc612007790f5555cdd18946fa48cf45e26d8ea4ea473d", size = 1777102, upload-time = "2026-07-23T01:54:31.775Z" }, + { url = "https://files.pythonhosted.org/packages/13/d4/e7d09ba7d345fb2d74440fd2fa033c5e079fac05552927705986f41a364f/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:89176250f686cb9853c0fb7ead90e639e915b84a6f43eedc2a4e7ec21f1037f0", size = 1580205, upload-time = "2026-07-23T01:54:34.518Z" }, + { url = "https://files.pythonhosted.org/packages/a3/84/072a91d68e1e1eb587985b54baab94221277f877e8ef274fc213a0ceae28/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3a26434dafe408229ff3403458ca58de24fb51936504decac49ce6755f77e59d", size = 1797219, upload-time = "2026-07-23T01:54:36.995Z" }, + { url = "https://files.pythonhosted.org/packages/e0/eb/aad34e897e668424d6e995da5dff8a4a09af93363d3392488772957a63aa/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19", size = 1768629, upload-time = "2026-07-23T01:54:40.103Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2b/6bb88ddba0fecd9122aa3ebcad25996cf6c083a4a7040dbb3a4f97972af6/aiohttp-3.14.3-cp312-cp312-win32.whl", hash = "sha256:16100ad3ab8d649fdfbee87602d9d2dcdca9df0b9eda8a1b5fdc0d41f96da559", size = 451481, upload-time = "2026-07-23T01:54:42.547Z" }, + { url = "https://files.pythonhosted.org/packages/76/9b/f2f8f108da17ecef2cc3efc424e8b7ad3782b1a8360f7b8eae8ced84f6ea/aiohttp-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:33a2d7c28d33797a2e99923dffa63f83d908a19b6bf26cfe80fa790aa5e1a75a", size = 476845, upload-time = "2026-07-23T01:54:44.853Z" }, + { url = "https://files.pythonhosted.org/packages/3e/44/28dac80a8941b604f4da10ce21097614ca1bf905ce93dca28d8d7de9c1e7/aiohttp-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:362a3fd481769cac1a824514bcd86fda51c65e8fe6e051099e008fddde6db17c", size = 448050, upload-time = "2026-07-23T01:54:47.087Z" }, + { url = "https://files.pythonhosted.org/packages/57/be/5afd201cc0ab139029aadb75392efe85a293403d9dd3a3226161c21ce00c/aiohttp-3.14.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:2e9878ae68e4a5f1c0abe4dd497dbc3d51946f5837b56759e2a02e78fa90ef86", size = 506269, upload-time = "2026-07-23T01:54:49.075Z" }, + { url = "https://files.pythonhosted.org/packages/22/09/dec8189d62b45ade009f6792a2264b942a90cb88aeaf181239933cd72c3c/aiohttp-3.14.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:f3d2669fe7dec7fc359ecdb5984b29b50d85d5d00f8c1cb61de4f4a24ee42627", size = 515166, upload-time = "2026-07-23T01:54:51.894Z" }, + { url = "https://files.pythonhosted.org/packages/28/24/2854869d29ed8a8b19d74f9ec6629515f7e04d02dd329d9d179201e58e47/aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:cc7cb243a68167172f48c1fd43cee91ec4b1d40cefd190edd43369d1a6bc9c82", size = 486263, upload-time = "2026-07-23T01:54:54.223Z" }, + { url = "https://files.pythonhosted.org/packages/d4/dd/57187c8be2a35aea65eaee3bd2c3dcbbcf0204f5106c89637e3610380cd1/aiohttp-3.14.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:78253b573e6ffab5028924fc98bc281aae05445969982a10864bc360dea2016c", size = 492299, upload-time = "2026-07-23T01:54:56.236Z" }, + { url = "https://files.pythonhosted.org/packages/b9/11/06ae6ed8f0d414edf4068861e233d8fe23ee699bfd4b3ceb8663db948a62/aiohttp-3.14.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7041d52c3a7fa20c9e8c182b534704abb19502c8bdcbde7ab23bfda6f642394f", size = 502235, upload-time = "2026-07-23T01:54:58.377Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a3/559639c34a345d2cf7c52dff6838119f2eaf29eb508227b5b83f573af813/aiohttp-3.14.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac74facc01463f138b0da5580329cfcc82818dea5656e83ddcd11268fc12ff80", size = 750883, upload-time = "2026-07-23T01:55:00.65Z" }, + { url = "https://files.pythonhosted.org/packages/91/cd/41e131f13afd1e7b0172a9d9eda085ef90eb8439f41f0d279db81ed3ae60/aiohttp-3.14.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d6218d92e450824e9b4881f44e8c09f1853b490f9a64130801024a4793b1b3b0", size = 508473, upload-time = "2026-07-23T01:55:02.945Z" }, + { url = "https://files.pythonhosted.org/packages/bc/6b/e7f13410d391c6e55b4c007a8de024355389d7d459e3d64c42b2d33617e5/aiohttp-3.14.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:11fb37ef075669eee52ab1928fbf6e1741fada40409fa309ebde9607a962aebf", size = 509190, upload-time = "2026-07-23T01:55:05.173Z" }, + { url = "https://files.pythonhosted.org/packages/97/21/6464573e53d69672cc1eada3e5c5cb2d2efa82701e8305a0f2047a576967/aiohttp-3.14.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55bdcc472aafe2de4a253045cc128007a64f1e0264fb675791e132ea5edaa3bd", size = 1761478, upload-time = "2026-07-23T01:55:07.383Z" }, + { url = "https://files.pythonhosted.org/packages/1a/81/d217043a4c17fbce360905e3b2bdd20139ebc9a2de836d035d179c4da006/aiohttp-3.14.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c39846c3aad97a8530c89d7a3869a8f8e9e3762c6ac0504481e5c80948f7e807", size = 1735092, upload-time = "2026-07-23T01:55:09.803Z" }, + { url = "https://files.pythonhosted.org/packages/a1/66/e13a02d0eeb1a9a502402a977abb4e4abff9fe4051c26f80558c57a7c975/aiohttp-3.14.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5895ef58c4620afe02fa16044f023dc4dafec08158f9d08874a46a7dbc0341b8", size = 1800546, upload-time = "2026-07-23T01:55:12.012Z" }, + { url = "https://files.pythonhosted.org/packages/26/5e/57d42fca1d18cb5acc1cad945d017fabc5d6ae71d8a08ad66be8dc3ee544/aiohttp-3.14.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa9467a8113aa69d3d7c55a70ef0b7c636010a40993f3df9d9d0d73b3eb7ef24", size = 1895250, upload-time = "2026-07-23T01:55:14.357Z" }, + { url = "https://files.pythonhosted.org/packages/ca/1c/7da8d08e74d56f00070822f9638ff3f1c563f8ad87d1efa996c87bfc8644/aiohttp-3.14.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d2deec16eeedf55f2c7cf75b521ea3856a5177e123844f8fd0f114ce252cb5", size = 1789289, upload-time = "2026-07-23T01:55:16.668Z" }, + { url = "https://files.pythonhosted.org/packages/cd/0f/cf16bcf56896981c1a0319f5d5db9337994b5165730c48a8fa07e9b34be6/aiohttp-3.14.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dd54d0e8717de95939766febac482ac0474d8ac3b048115f9f2b1d23a16e7db4", size = 1586706, upload-time = "2026-07-23T01:55:18.913Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6f/76eac12a7f2480e1e304f842efdb07db33256b0d9165b866b6ef0806c202/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:df82f3787c940c94986b34222d59c9e38843fba85139f36e85255a82ad5355a9", size = 1724652, upload-time = "2026-07-23T01:55:21.296Z" }, + { url = "https://files.pythonhosted.org/packages/39/b6/19c8c592baeeb94b75f966547d40c02ac7590902306ec5863d5c027cf506/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:42a67efc36300d052fb4508a53e8b6901b9284b599ae63945c377569c5fcc1e1", size = 1756239, upload-time = "2026-07-23T01:55:23.705Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c9/4e9383150296f97f873b680c4de8fb2cd88608fb9f48c79edcb111611abc/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7a75aa63cbf9b21cfaf60dc2657e19df2c2867d91707d653fee171ffeedd1371", size = 1769161, upload-time = "2026-07-23T01:55:26.082Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1e/147bdc6cc5de5f3ab011be8bf5d6e786633249f22c20bae06f85e45f5387/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e92eb8acc45eb6a9f4935071a77edf5b85cc6f8dfad5cd99e97653c26593cdde", size = 1578759, upload-time = "2026-07-23T01:55:28.846Z" }, + { url = "https://files.pythonhosted.org/packages/fd/31/78388a9d6040ece2e11df62ea229a822cf5e52d238374b220ae9975b2623/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b014a6ed7cf912e787149fdc529166d3ceabac23f26efeea3158c9aba2354e7e", size = 1792025, upload-time = "2026-07-23T01:55:31.457Z" }, + { url = "https://files.pythonhosted.org/packages/03/51/a3d29fdf2c25d796746af8ad6fe56a45d6256c38b0a8a2ed752e1160b3a2/aiohttp-3.14.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3d4f72af88ac2474bb5bca640030320e3d38a0163a1d7533500e87be458eef71", size = 1768477, upload-time = "2026-07-23T01:55:33.87Z" }, + { url = "https://files.pythonhosted.org/packages/29/a6/442e18b5afeade534d877a2dc3c3e392aff8d49787890b0cf84790410267/aiohttp-3.14.3-cp313-cp313-win32.whl", hash = "sha256:5f08ec777f35ee70720233b8b9811d3bb5d728137f30ac91b7457709c3261ac0", size = 451069, upload-time = "2026-07-23T01:55:36.121Z" }, + { url = "https://files.pythonhosted.org/packages/9d/69/3d876ac02659f271cf7f6769f14a8e3de5b6e888ed8b5a7e998086a4cec8/aiohttp-3.14.3-cp313-cp313-win_amd64.whl", hash = "sha256:dff9461ec275f22135650d5ba4b4931a11f3958df7dfbb8db630000d4dee0883", size = 476518, upload-time = "2026-07-23T01:55:38.303Z" }, + { url = "https://files.pythonhosted.org/packages/b2/0e/50d6e6471cd31edce8b282bdec59375a3a69124d8a989a0b1313355cae52/aiohttp-3.14.3-cp313-cp313-win_arm64.whl", hash = "sha256:ddcac3c6b382e81f1dd0499199d4136b877beb4cb5ef770bbbfba56c4b8f55d2", size = 447676, upload-time = "2026-07-23T01:55:40.451Z" }, + { url = "https://files.pythonhosted.org/packages/c8/20/887fdcf832326571b370ffc347b3e70abe101096f3720126aac161b1d872/aiohttp-3.14.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:49f7325beb0f85ef4aef5f48f490269575f83e6e2acad00a1d80b807eb027062", size = 509067, upload-time = "2026-07-23T01:55:42.618Z" }, + { url = "https://files.pythonhosted.org/packages/ad/a3/92cec936f78cc4bf0fa5554ebe593b73459d94e3c62303e1902a4cccb6f7/aiohttp-3.14.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:e3be98a7c30b8c25d573dafba7171d66dfb05ee6a9070fc46535464ff97700a6", size = 514774, upload-time = "2026-07-23T01:55:44.937Z" }, + { url = "https://files.pythonhosted.org/packages/29/ba/2a0c38df3fc557620b6a5acd98364af050053b6285b4dc7ee74100c63c18/aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:614c61d478b83953e261d02bb2df750f17227cd33ef8002945bf5aebbde21919", size = 488134, upload-time = "2026-07-23T01:55:47.135Z" }, + { url = "https://files.pythonhosted.org/packages/48/d6/d51b7d4bf309af3693940d8ffd2b9ed0b682434ef85959b7c9c137f60cf8/aiohttp-3.14.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:1caa7b0d05f3e3a36f87788c59e970a7ee1cefcfcbb924a9f138c4a6551c9cb7", size = 494201, upload-time = "2026-07-23T01:55:49.451Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5a/8f624384e5f1efabb5229b94157eb966b021e97bdb188c62860c2ae243c2/aiohttp-3.14.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:dfa68deb2a443bdaa3ea5297b0699c1464f08aef3812b486d1348eee61b07dc0", size = 502766, upload-time = "2026-07-23T01:55:51.656Z" }, + { url = "https://files.pythonhosted.org/packages/a6/26/4ff0164370deec18fb19254ee4ab10b7a73304ac0c860b13f5f84663759b/aiohttp-3.14.3-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e72ee89e28d907a18f46959b4eb0bb06701cc7f8cf4366e00029e2ccfaaf5924", size = 756557, upload-time = "2026-07-23T01:55:53.964Z" }, + { url = "https://files.pythonhosted.org/packages/97/a3/7056b86dc0d9ec709ea9777eae3b0161428f943372f8b98c01c11593b682/aiohttp-3.14.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ad4c8b7488d745d2ca4838ebd8ae5ba9b56341d30b1da43640e4ce87f9f49646", size = 510168, upload-time = "2026-07-23T01:55:56.22Z" }, + { url = "https://files.pythonhosted.org/packages/85/ed/0357a015892fd68058bf2d39d3fd1958e459b997a7db30aaa6aaa434ae96/aiohttp-3.14.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:db332af25642007330fca8be5c4d194caf2bea7a7fc84415aff3497af5dfee6b", size = 512957, upload-time = "2026-07-23T01:55:58.437Z" }, + { url = "https://files.pythonhosted.org/packages/47/d1/8aba53f15ccb2238405f5e9d30e2a8ca44f93878c26e7165ade00d374b1c/aiohttp-3.14.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25bd2708db6bdf6a6630dd37bdcdfcb47c4434d22ac69c64665b802910140b30", size = 1750149, upload-time = "2026-07-23T01:56:00.856Z" }, + { url = "https://files.pythonhosted.org/packages/49/bd/40c3fee327529284375c6701cbb0fa4600cc2e8432af1378f897e2ef7d3a/aiohttp-3.14.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:cef89a58e628c4efcac3275c2d68083f82426dcdc89c1492a6f654f9f7ea6ab9", size = 1707685, upload-time = "2026-07-23T01:56:03.371Z" }, + { url = "https://files.pythonhosted.org/packages/2a/a3/ca0cc6724cca8114b05694abd916060758c79894c3aa5b012cdadc1bc28e/aiohttp-3.14.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c23ec8ee9d5ab2f5421f9c7fffce208435607af27fd46d4a44e031954352838f", size = 1803911, upload-time = "2026-07-23T01:56:05.817Z" }, + { url = "https://files.pythonhosted.org/packages/95/b5/85b099c299c3ffd38ad9b3e43694c8a346934e4a30c88c4fd5a841234f77/aiohttp-3.14.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e2667f0bbe7eb6c74eae5e9691441ad186e5845ca3cff63230fc09c4e7514f5d", size = 1876929, upload-time = "2026-07-23T01:56:08.413Z" }, + { url = "https://files.pythonhosted.org/packages/d5/b7/1da684a04175473fa4cddbf9a2f572e79514c3fd27a74597f43057d4f3da/aiohttp-3.14.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18cb43369747b2ae007bd2655fb8e63a099c2ff1d207962943636dac989b3147", size = 1761112, upload-time = "2026-07-23T01:56:10.918Z" }, + { url = "https://files.pythonhosted.org/packages/d1/16/bc4b55e3e5cb175fd69c53c90d60d2f47797cb343da5106e23863dc4dba4/aiohttp-3.14.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d77640cc618c1d99fc4f8589c0f24a730adfa54eb1e57ef7bf0c8dfb78da898c", size = 1583500, upload-time = "2026-07-23T01:56:13.613Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e8/13a9d957a1ee40837f46aa30f0f4c657e673ad86a2e6362a9f9be20d26d9/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:53e5179d8abb5710f8e83ba207c41c8d1261fcffd4616500e15ca2b7a33be10a", size = 1713940, upload-time = "2026-07-23T01:56:15.969Z" }, + { url = "https://files.pythonhosted.org/packages/38/05/d33c680c1bcf1c7e130f9cbfc1fc02fe8bb0c4af2a94a53dd5fb56131e5c/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:cd817772b2fcf2b8c0905795318485f9ec16eae60b29feb7f4c77085311637f0", size = 1724413, upload-time = "2026-07-23T01:56:18.591Z" }, + { url = "https://files.pythonhosted.org/packages/85/1d/af798d306f7a74b6a632dbcabcf62a4c91391b7582d2a8c6d7712e2cc54e/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:4e3ac92d90e92773b2362d506068e9a948192bd553e743c5b2429e28527c8661", size = 1770748, upload-time = "2026-07-23T01:56:21.074Z" }, + { url = "https://files.pythonhosted.org/packages/a8/92/ad720d472556a995049206867765e9410969684f86ee09423ff9969044c1/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3f42e9b78301f11c8f861746175d8b9c1ccef713fcad9eab396e2f6db8ed4a22", size = 1577564, upload-time = "2026-07-23T01:56:23.475Z" }, + { url = "https://files.pythonhosted.org/packages/60/ad/0ed7586cbef7a884e23a752fa2bb987a122e6a5dd50dab109258d0a95193/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9d9edccfe496b476db5f398d97b865e9a6752bcf8aec4eef8390ce20fb64bb41", size = 1782080, upload-time = "2026-07-23T01:56:25.994Z" }, + { url = "https://files.pythonhosted.org/packages/97/ea/dbaed0d73e8a69aad653b045dab451c67c2454bb731a37b45a86593e9422/aiohttp-3.14.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1c5ec8fb1bcc31a8466f74aaf26c345d5c386fa4bd08a3f0eb9c7a4a3fe8b5bf", size = 1745813, upload-time = "2026-07-23T01:56:28.604Z" }, + { url = "https://files.pythonhosted.org/packages/81/1b/6893d4bc57e434fc93a6c9217c637d967a0b651d989f6e3265179375754a/aiohttp-3.14.3-cp314-cp314-win32.whl", hash = "sha256:38901a84da3ce22249f6e860bf8f90d141bcab7da090cc398f8bb58c0e44b7da", size = 455872, upload-time = "2026-07-23T01:56:31.031Z" }, + { url = "https://files.pythonhosted.org/packages/f5/8b/c7baa1ba1eda4db6989baefe5de6d99834921b84ebd7918624febcb9f290/aiohttp-3.14.3-cp314-cp314-win_amd64.whl", hash = "sha256:8b3b60de05f3dcb6f6a00f818bb2ec781cee4de0645f59ccaf99b1d1823b6100", size = 481030, upload-time = "2026-07-23T01:56:33.365Z" }, + { url = "https://files.pythonhosted.org/packages/22/8c/c29d067df825a2df88ca432db848aa2fe8199598359cc06c12b09320cac9/aiohttp-3.14.3-cp314-cp314-win_arm64.whl", hash = "sha256:1576145bdceeb92382d899751e12743a3a5b8e460a841e3e50543859e54864dc", size = 453669, upload-time = "2026-07-23T01:56:35.731Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a4/9c033beb355d39b6147980597ec9645e4729243f686ee4dc73945de72030/aiohttp-3.14.3-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:8800c996b01c2772a783e3e46f3e1abd5823029adca0df54231960de9bfefa5b", size = 791403, upload-time = "2026-07-23T01:56:37.972Z" }, + { url = "https://files.pythonhosted.org/packages/80/ca/87c32a0a7704583cfc49660bd817889bae5b830bf53b5dcb4e92145ac2da/aiohttp-3.14.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ebe8e504f058fe91223351cecd2d9d6946c9d241bb0250d898ffbdf584cc72b0", size = 526413, upload-time = "2026-07-23T01:56:40.523Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d8/8ec0e471248c500acdce2be3f46db8fb62b5eb60efef072529cc85ee1d26/aiohttp-3.14.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:30402d03a7c0ff52bce290b57e564e9079fd9d0cb545c8aba73f86a103162d2e", size = 532135, upload-time = "2026-07-23T01:56:42.876Z" }, + { url = "https://files.pythonhosted.org/packages/fe/45/f8919fd936e8b79fcd9bda7b6d8e62613462a713f4f17987fd7c34399142/aiohttp-3.14.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fc7b5bfec6573f3ae844f457fdde5adeb713f8b8e4a81ad64fc207b49383716", size = 1922742, upload-time = "2026-07-23T01:56:45.528Z" }, + { url = "https://files.pythonhosted.org/packages/f6/ec/9ca76b28a27525b0cc53e20842e0228b022f301ce1f436b7d814b4aaf2df/aiohttp-3.14.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8a5fd34f7f7410d1730d5c2ba873cacb2eed3fede366feb268a70ba22581ed8f", size = 1787371, upload-time = "2026-07-23T01:56:48.045Z" }, + { url = "https://files.pythonhosted.org/packages/b1/04/6acdbf17315f7b55f1937e3387acb89a3cddeb4995689553d064af8e92ab/aiohttp-3.14.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:270d3dace9ca2f10f0da5d8ebe519b7a310fc6112ed916e32df5866df0888553", size = 1912623, upload-time = "2026-07-23T01:56:50.605Z" }, + { url = "https://files.pythonhosted.org/packages/86/e6/438b0c79ca6f45eb9fd9817dd4c01a91919a38c0de5ee9e05e2b4dc0ece7/aiohttp-3.14.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3ae5b3a59436d089b5395d910121a390feed4d00578eb95a0fd1a329fe963100", size = 2005515, upload-time = "2026-07-23T01:56:53.153Z" }, + { url = "https://files.pythonhosted.org/packages/bb/6b/62cbd6577758699525f5c712d1ddef57d9875fbab0ae8d5f5a202fd598f8/aiohttp-3.14.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2498f0fe69ead802f9675beca44a7c21c62fdaa4ec5145ea1c3ad6edbee29f85", size = 1879906, upload-time = "2026-07-23T01:56:55.818Z" }, + { url = "https://files.pythonhosted.org/packages/00/95/18bcbf830a21dc3aae24d8f6b6feaf3db1d2090242d00a7868db2ffb0b67/aiohttp-3.14.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a0dc483c00da8b673abbb367eb6f8d8f4bcec30eb58529ea13cb42e7fd2dfa33", size = 1675849, upload-time = "2026-07-23T01:56:58.861Z" }, + { url = "https://files.pythonhosted.org/packages/a9/19/47f4968659c5e23606c3790c80fc624e691c153d036148449ee84d31b287/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c7d3a97c678d34fc5b59da671ee9cd630096ddc643e7b5a30d54a2a6f3574d3f", size = 1843496, upload-time = "2026-07-23T01:57:01.591Z" }, + { url = "https://files.pythonhosted.org/packages/64/af/38c33c4dd82fddcb4e56c4653b6f1072a8edbc6b7fa15809f14932c41e2d/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8fb78a83c9e5f741ca3a68cfb455c1f5bb83b4e7249a3848b3cd78d0a8563b0", size = 1827746, upload-time = "2026-07-23T01:57:05.131Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9d/0537cda4885ac8f5b7053d164dd06312f4c483a4edcb8ee5b8aaf2a989bf/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:74ab5b6a9fb13e873e5a90946588baecaf488745e1db1a4a5c433f971f035098", size = 1853810, upload-time = "2026-07-23T01:57:08.043Z" }, + { url = "https://files.pythonhosted.org/packages/19/fe/26f9c5e6458385aa86497836b0dea6fb2f027827d63f37c7856cce9286ee/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd52f811e65f6fb634b1047159657c98f52b407f8efec907bcfc09da9a4c0a25", size = 1668895, upload-time = "2026-07-23T01:57:10.837Z" }, + { url = "https://files.pythonhosted.org/packages/ec/4c/618b1db9b9ba079b8875d2cdf78e7c4a3bf72903bd5850fee7dd9544600a/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f0f177d1b195b9e06376cfd7d308d8a1b920909a609d03ac82a8c73bbb16d3b9", size = 1883833, upload-time = "2026-07-23T01:57:13.672Z" }, + { url = "https://files.pythonhosted.org/packages/94/c6/bd959bd1e4771f9fd944e9e436224c48c77b018b73b519b5aad346335bcc/aiohttp-3.14.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:498c6c623134f8e09a3c4e60bcd607a0b4590dd7dbf08dd40851b27cbb520ccb", size = 1844251, upload-time = "2026-07-23T01:57:16.593Z" }, + { url = "https://files.pythonhosted.org/packages/5e/19/08d41839658bdd44a0ed2480f3891705ecb487ce28c0dde62c9040c997e0/aiohttp-3.14.3-cp314-cp314t-win32.whl", hash = "sha256:b304db572b4368edd8dda8a2274f73156fe15558fca4a917cb8a09fc47af5963", size = 474180, upload-time = "2026-07-23T01:57:19.306Z" }, + { url = "https://files.pythonhosted.org/packages/99/5d/3cd6ef0a2b2851f7ab913b5b079334781bd50ff56a323e4454063377a080/aiohttp-3.14.3-cp314-cp314t-win_amd64.whl", hash = "sha256:b20032766aedf6261c7a566585a40867d092ac03a0d81592d5370ef9b054f99b", size = 500528, upload-time = "2026-07-23T01:57:21.762Z" }, + { url = "https://files.pythonhosted.org/packages/a4/37/cfd1ed540a4d318da025590d96b728e63713c09e9377950fc655dadeb856/aiohttp-3.14.3-cp314-cp314t-win_arm64.whl", hash = "sha256:2e1161602f45a54de2ce0905243a95f58cb42dcd378402f3697f5e0b21e9d2e7", size = 469280, upload-time = "2026-07-23T01:57:24.241Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" }, +] + +[[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.15.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.15'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/d2/f4d173e22df740bc37b1db102b386ba719b66e95b0f0d751f556b387e6d2/anyio-4.15.1.tar.gz", hash = "sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94", size = 276966, upload-time = "2026-09-05T10:42:39.44Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "audioop-lts" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/53/946db57842a50b2da2e0c1e34bd37f36f5aadba1a929a3971c5d7841dbca/audioop_lts-0.2.2.tar.gz", hash = "sha256:64d0c62d88e67b98a1a5e71987b7aa7b5bcffc7dcee65b635823dbdd0a8dbbd0", size = 30686, upload-time = "2025-08-05T16:43:17.409Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/d4/94d277ca941de5a507b07f0b592f199c22454eeaec8f008a286b3fbbacd6/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd3d4602dc64914d462924a08c1a9816435a2155d74f325853c1f1ac3b2d9800", size = 46523, upload-time = "2025-08-05T16:42:20.836Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5a/656d1c2da4b555920ce4177167bfeb8623d98765594af59702c8873f60ec/audioop_lts-0.2.2-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:550c114a8df0aafe9a05442a1162dfc8fec37e9af1d625ae6060fed6e756f303", size = 27455, upload-time = "2025-08-05T16:42:22.283Z" }, + { url = "https://files.pythonhosted.org/packages/1b/83/ea581e364ce7b0d41456fb79d6ee0ad482beda61faf0cab20cbd4c63a541/audioop_lts-0.2.2-cp313-abi3-macosx_11_0_arm64.whl", hash = "sha256:9a13dc409f2564de15dd68be65b462ba0dde01b19663720c68c1140c782d1d75", size = 26997, upload-time = "2025-08-05T16:42:23.849Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3b/e8964210b5e216e5041593b7d33e97ee65967f17c282e8510d19c666dab4/audioop_lts-0.2.2-cp313-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51c916108c56aa6e426ce611946f901badac950ee2ddaf302b7ed35d9958970d", size = 85844, upload-time = "2025-08-05T16:42:25.208Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2e/0a1c52faf10d51def20531a59ce4c706cb7952323b11709e10de324d6493/audioop_lts-0.2.2-cp313-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:47eba38322370347b1c47024defbd36374a211e8dd5b0dcbce7b34fdb6f8847b", size = 85056, upload-time = "2025-08-05T16:42:26.559Z" }, + { url = "https://files.pythonhosted.org/packages/75/e8/cd95eef479656cb75ab05dfece8c1f8c395d17a7c651d88f8e6e291a63ab/audioop_lts-0.2.2-cp313-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba7c3a7e5f23e215cb271516197030c32aef2e754252c4c70a50aaff7031a2c8", size = 93892, upload-time = "2025-08-05T16:42:27.902Z" }, + { url = "https://files.pythonhosted.org/packages/5c/1e/a0c42570b74f83efa5cca34905b3eef03f7ab09fe5637015df538a7f3345/audioop_lts-0.2.2-cp313-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:def246fe9e180626731b26e89816e79aae2276f825420a07b4a647abaa84becc", size = 96660, upload-time = "2025-08-05T16:42:28.9Z" }, + { url = "https://files.pythonhosted.org/packages/50/d5/8a0ae607ca07dbb34027bac8db805498ee7bfecc05fd2c148cc1ed7646e7/audioop_lts-0.2.2-cp313-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e160bf9df356d841bb6c180eeeea1834085464626dc1b68fa4e1d59070affdc3", size = 79143, upload-time = "2025-08-05T16:42:29.929Z" }, + { url = "https://files.pythonhosted.org/packages/12/17/0d28c46179e7910bfb0bb62760ccb33edb5de973052cb2230b662c14ca2e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4b4cd51a57b698b2d06cb9993b7ac8dfe89a3b2878e96bc7948e9f19ff51dba6", size = 84313, upload-time = "2025-08-05T16:42:30.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/ba/bd5d3806641564f2024e97ca98ea8f8811d4e01d9b9f9831474bc9e14f9e/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4a53aa7c16a60a6857e6b0b165261436396ef7293f8b5c9c828a3a203147ed4a", size = 93044, upload-time = "2025-08-05T16:42:31.959Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5e/435ce8d5642f1f7679540d1e73c1c42d933331c0976eb397d1717d7f01a3/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:3fc38008969796f0f689f1453722a0f463da1b8a6fbee11987830bfbb664f623", size = 78766, upload-time = "2025-08-05T16:42:33.302Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3b/b909e76b606cbfd53875693ec8c156e93e15a1366a012f0b7e4fb52d3c34/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_s390x.whl", hash = "sha256:15ab25dd3e620790f40e9ead897f91e79c0d3ce65fe193c8ed6c26cffdd24be7", size = 87640, upload-time = "2025-08-05T16:42:34.854Z" }, + { url = "https://files.pythonhosted.org/packages/30/e7/8f1603b4572d79b775f2140d7952f200f5e6c62904585d08a01f0a70393a/audioop_lts-0.2.2-cp313-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:03f061a1915538fd96272bac9551841859dbb2e3bf73ebe4a23ef043766f5449", size = 86052, upload-time = "2025-08-05T16:42:35.839Z" }, + { url = "https://files.pythonhosted.org/packages/b5/96/c37846df657ccdda62ba1ae2b6534fa90e2e1b1742ca8dcf8ebd38c53801/audioop_lts-0.2.2-cp313-abi3-win32.whl", hash = "sha256:3bcddaaf6cc5935a300a8387c99f7a7fbbe212a11568ec6cf6e4bc458c048636", size = 26185, upload-time = "2025-08-05T16:42:37.04Z" }, + { url = "https://files.pythonhosted.org/packages/34/a5/9d78fdb5b844a83da8a71226c7bdae7cc638861085fff7a1d707cb4823fa/audioop_lts-0.2.2-cp313-abi3-win_amd64.whl", hash = "sha256:a2c2a947fae7d1062ef08c4e369e0ba2086049a5e598fda41122535557012e9e", size = 30503, upload-time = "2025-08-05T16:42:38.427Z" }, + { url = "https://files.pythonhosted.org/packages/34/25/20d8fde083123e90c61b51afb547bb0ea7e77bab50d98c0ab243d02a0e43/audioop_lts-0.2.2-cp313-abi3-win_arm64.whl", hash = "sha256:5f93a5db13927a37d2d09637ccca4b2b6b48c19cd9eda7b17a2e9f77edee6a6f", size = 24173, upload-time = "2025-08-05T16:42:39.704Z" }, + { url = "https://files.pythonhosted.org/packages/58/a7/0a764f77b5c4ac58dc13c01a580f5d32ae8c74c92020b961556a43e26d02/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:73f80bf4cd5d2ca7814da30a120de1f9408ee0619cc75da87d0641273d202a09", size = 47096, upload-time = "2025-08-05T16:42:40.684Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ed/ebebedde1a18848b085ad0fa54b66ceb95f1f94a3fc04f1cd1b5ccb0ed42/audioop_lts-0.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:106753a83a25ee4d6f473f2be6b0966fc1c9af7e0017192f5531a3e7463dce58", size = 27748, upload-time = "2025-08-05T16:42:41.992Z" }, + { url = "https://files.pythonhosted.org/packages/cb/6e/11ca8c21af79f15dbb1c7f8017952ee8c810c438ce4e2b25638dfef2b02c/audioop_lts-0.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fbdd522624141e40948ab3e8cdae6e04c748d78710e9f0f8d4dae2750831de19", size = 27329, upload-time = "2025-08-05T16:42:42.987Z" }, + { url = "https://files.pythonhosted.org/packages/84/52/0022f93d56d85eec5da6b9da6a958a1ef09e80c39f2cc0a590c6af81dcbb/audioop_lts-0.2.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:143fad0311e8209ece30a8dbddab3b65ab419cbe8c0dde6e8828da25999be911", size = 92407, upload-time = "2025-08-05T16:42:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/87/1d/48a889855e67be8718adbc7a01f3c01d5743c325453a5e81cf3717664aad/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfbbc74ec68a0fd08cfec1f4b5e8cca3d3cd7de5501b01c4b5d209995033cde9", size = 91811, upload-time = "2025-08-05T16:42:45.325Z" }, + { url = "https://files.pythonhosted.org/packages/98/a6/94b7213190e8077547ffae75e13ed05edc488653c85aa5c41472c297d295/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cfcac6aa6f42397471e4943e0feb2244549db5c5d01efcd02725b96af417f3fe", size = 100470, upload-time = "2025-08-05T16:42:46.468Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e9/78450d7cb921ede0cfc33426d3a8023a3bda755883c95c868ee36db8d48d/audioop_lts-0.2.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:752d76472d9804ac60f0078c79cdae8b956f293177acd2316cd1e15149aee132", size = 103878, upload-time = "2025-08-05T16:42:47.576Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e2/cd5439aad4f3e34ae1ee852025dc6aa8f67a82b97641e390bf7bd9891d3e/audioop_lts-0.2.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:83c381767e2cc10e93e40281a04852facc4cd9334550e0f392f72d1c0a9c5753", size = 84867, upload-time = "2025-08-05T16:42:49.003Z" }, + { url = "https://files.pythonhosted.org/packages/68/4b/9d853e9076c43ebba0d411e8d2aa19061083349ac695a7d082540bad64d0/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c0022283e9556e0f3643b7c3c03f05063ca72b3063291834cca43234f20c60bb", size = 90001, upload-time = "2025-08-05T16:42:50.038Z" }, + { url = "https://files.pythonhosted.org/packages/58/26/4bae7f9d2f116ed5593989d0e521d679b0d583973d203384679323d8fa85/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:a2d4f1513d63c795e82948e1305f31a6d530626e5f9f2605408b300ae6095093", size = 99046, upload-time = "2025-08-05T16:42:51.111Z" }, + { url = "https://files.pythonhosted.org/packages/b2/67/a9f4fb3e250dda9e9046f8866e9fa7d52664f8985e445c6b4ad6dfb55641/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:c9c8e68d8b4a56fda8c025e538e639f8c5953f5073886b596c93ec9b620055e7", size = 84788, upload-time = "2025-08-05T16:42:52.198Z" }, + { url = "https://files.pythonhosted.org/packages/70/f7/3de86562db0121956148bcb0fe5b506615e3bcf6e63c4357a612b910765a/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:96f19de485a2925314f5020e85911fb447ff5fbef56e8c7c6927851b95533a1c", size = 94472, upload-time = "2025-08-05T16:42:53.59Z" }, + { url = "https://files.pythonhosted.org/packages/f1/32/fd772bf9078ae1001207d2df1eef3da05bea611a87dd0e8217989b2848fa/audioop_lts-0.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e541c3ef484852ef36545f66209444c48b28661e864ccadb29daddb6a4b8e5f5", size = 92279, upload-time = "2025-08-05T16:42:54.632Z" }, + { url = "https://files.pythonhosted.org/packages/4f/41/affea7181592ab0ab560044632571a38edaf9130b84928177823fbf3176a/audioop_lts-0.2.2-cp313-cp313t-win32.whl", hash = "sha256:d5e73fa573e273e4f2e5ff96f9043858a5e9311e94ffefd88a3186a910c70917", size = 26568, upload-time = "2025-08-05T16:42:55.627Z" }, + { url = "https://files.pythonhosted.org/packages/28/2b/0372842877016641db8fc54d5c88596b542eec2f8f6c20a36fb6612bf9ee/audioop_lts-0.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9191d68659eda01e448188f60364c7763a7ca6653ed3f87ebb165822153a8547", size = 30942, upload-time = "2025-08-05T16:42:56.674Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/baf2b9cc7e96c179bb4a54f30fcd83e6ecb340031bde68f486403f943768/audioop_lts-0.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c174e322bb5783c099aaf87faeb240c8d210686b04bd61dfd05a8e5a83d88969", size = 24603, upload-time = "2025-08-05T16:42:57.571Z" }, + { url = "https://files.pythonhosted.org/packages/5c/73/413b5a2804091e2c7d5def1d618e4837f1cb82464e230f827226278556b7/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f9ee9b52f5f857fbaf9d605a360884f034c92c1c23021fb90b2e39b8e64bede6", size = 47104, upload-time = "2025-08-05T16:42:58.518Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/daa3308dc6593944410c2c68306a5e217f5c05b70a12e70228e7dd42dc5c/audioop_lts-0.2.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:49ee1a41738a23e98d98b937a0638357a2477bc99e61b0f768a8f654f45d9b7a", size = 27754, upload-time = "2025-08-05T16:43:00.132Z" }, + { url = "https://files.pythonhosted.org/packages/4e/86/c2e0f627168fcf61781a8f72cab06b228fe1da4b9fa4ab39cfb791b5836b/audioop_lts-0.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b00be98ccd0fc123dcfad31d50030d25fcf31488cde9e61692029cd7394733b", size = 27332, upload-time = "2025-08-05T16:43:01.666Z" }, + { url = "https://files.pythonhosted.org/packages/c7/bd/35dce665255434f54e5307de39e31912a6f902d4572da7c37582809de14f/audioop_lts-0.2.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6d2e0f9f7a69403e388894d4ca5ada5c47230716a03f2847cfc7bd1ecb589d6", size = 92396, upload-time = "2025-08-05T16:43:02.991Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d2/deeb9f51def1437b3afa35aeb729d577c04bcd89394cb56f9239a9f50b6f/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9b0b8a03ef474f56d1a842af1a2e01398b8f7654009823c6d9e0ecff4d5cfbf", size = 91811, upload-time = "2025-08-05T16:43:04.096Z" }, + { url = "https://files.pythonhosted.org/packages/76/3b/09f8b35b227cee28cc8231e296a82759ed80c1a08e349811d69773c48426/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2b267b70747d82125f1a021506565bdc5609a2b24bcb4773c16d79d2bb260bbd", size = 100483, upload-time = "2025-08-05T16:43:05.085Z" }, + { url = "https://files.pythonhosted.org/packages/0b/15/05b48a935cf3b130c248bfdbdea71ce6437f5394ee8533e0edd7cfd93d5e/audioop_lts-0.2.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0337d658f9b81f4cd0fdb1f47635070cc084871a3d4646d9de74fdf4e7c3d24a", size = 103885, upload-time = "2025-08-05T16:43:06.197Z" }, + { url = "https://files.pythonhosted.org/packages/83/80/186b7fce6d35b68d3d739f228dc31d60b3412105854edb975aa155a58339/audioop_lts-0.2.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:167d3b62586faef8b6b2275c3218796b12621a60e43f7e9d5845d627b9c9b80e", size = 84899, upload-time = "2025-08-05T16:43:07.291Z" }, + { url = "https://files.pythonhosted.org/packages/49/89/c78cc5ac6cb5828f17514fb12966e299c850bc885e80f8ad94e38d450886/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0d9385e96f9f6da847f4d571ce3cb15b5091140edf3db97276872647ce37efd7", size = 89998, upload-time = "2025-08-05T16:43:08.335Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4b/6401888d0c010e586c2ca50fce4c903d70a6bb55928b16cfbdfd957a13da/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:48159d96962674eccdca9a3df280e864e8ac75e40a577cc97c5c42667ffabfc5", size = 99046, upload-time = "2025-08-05T16:43:09.367Z" }, + { url = "https://files.pythonhosted.org/packages/de/f8/c874ca9bb447dae0e2ef2e231f6c4c2b0c39e31ae684d2420b0f9e97ee68/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8fefe5868cd082db1186f2837d64cfbfa78b548ea0d0543e9b28935ccce81ce9", size = 84843, upload-time = "2025-08-05T16:43:10.749Z" }, + { url = "https://files.pythonhosted.org/packages/3e/c0/0323e66f3daebc13fd46b36b30c3be47e3fc4257eae44f1e77eb828c703f/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:58cf54380c3884fb49fdd37dfb7a772632b6701d28edd3e2904743c5e1773602", size = 94490, upload-time = "2025-08-05T16:43:12.131Z" }, + { url = "https://files.pythonhosted.org/packages/98/6b/acc7734ac02d95ab791c10c3f17ffa3584ccb9ac5c18fd771c638ed6d1f5/audioop_lts-0.2.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:088327f00488cdeed296edd9215ca159f3a5a5034741465789cad403fcf4bec0", size = 92297, upload-time = "2025-08-05T16:43:13.139Z" }, + { url = "https://files.pythonhosted.org/packages/13/c3/c3dc3f564ce6877ecd2a05f8d751b9b27a8c320c2533a98b0c86349778d0/audioop_lts-0.2.2-cp314-cp314t-win32.whl", hash = "sha256:068aa17a38b4e0e7de771c62c60bbca2455924b67a8814f3b0dee92b5820c0b3", size = 27331, upload-time = "2025-08-05T16:43:14.19Z" }, + { url = "https://files.pythonhosted.org/packages/72/bb/b4608537e9ffcb86449091939d52d24a055216a36a8bf66b936af8c3e7ac/audioop_lts-0.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a5bf613e96f49712073de86f20dbdd4014ca18efd4d34ed18c75bd808337851b", size = 31697, upload-time = "2025-08-05T16:43:15.193Z" }, + { url = "https://files.pythonhosted.org/packages/f6/22/91616fe707a5c5510de2cac9b046a30defe7007ba8a0c04f9c08f27df312/audioop_lts-0.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:b492c3b040153e68b9fdaff5913305aaaba5bb433d8a7f73d5cf6a64ed3cc1dd", size = 25206, upload-time = "2025-08-05T16:43:16.444Z" }, +] + +[[package]] +name = "authlib" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "joserfc" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/51/bc1729d3cfdc214b4935f4e886e4dd443c3065fd8e1e66423fe84b490f81/authlib-1.8.0.tar.gz", hash = "sha256:f3ecd5f1da737262fb53bf1a4d95c4ea1ad9dd509316587a255c99ab1838a4f0", size = 177759, upload-time = "2026-08-30T12:12:34.833Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/c6/6f124bcfbbfb20fba22c939b4e43a06dccfc0e1ca20e5634ca573cb1e271/authlib-1.8.0-py2.py3-none-any.whl", hash = "sha256:88aebbd9af6757e14e912d5dc007ae1dc1f3e27e3b2152ce7c552ee2c3b3c121", size = 260804, upload-time = "2026-08-30T12:12:33.162Z" }, +] + +[[package]] +name = "beartype" +version = "0.22.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, +] + +[[package]] +name = "bracex" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/01/5f394b8bcd6e5b92f73130990960423bbb19711f906bd9fe9ea5557c667c/bracex-3.0.1.tar.gz", hash = "sha256:4e38e32392e4a4780fe15d644bfc7c8514057cfc3861e060b11814ce829c25e4", size = 44019, upload-time = "2026-07-20T13:43:00.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/8f/6f7273a7adb8d73fc8d21ede4376a3e475e52f98435c6007f69100dec8ca/bracex-3.0.1-py3-none-any.whl", hash = "sha256:6523ad83aeb5098a4ee597cff0f964442ff74e460bd3fafaffab6a013ff2288c", size = 11940, upload-time = "2026-07-20T13:42:59.268Z" }, +] + +[[package]] +name = "brotli" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/16/c92ca344d646e71a43b8bb353f0a6490d7f6e06210f8554c8f874e454285/brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a", size = 7388632, upload-time = "2025-11-05T18:39:42.86Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/ee/b0a11ab2315c69bb9b45a2aaed022499c9c24a205c3a49c3513b541a7967/brotli-1.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:35d382625778834a7f3061b15423919aa03e4f5da34ac8e02c074e4b75ab4f84", size = 861543, upload-time = "2025-11-05T18:38:24.183Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2f/29c1459513cd35828e25531ebfcbf3e92a5e49f560b1777a9af7203eb46e/brotli-1.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7a61c06b334bd99bc5ae84f1eeb36bfe01400264b3c352f968c6e30a10f9d08b", size = 444288, upload-time = "2025-11-05T18:38:25.139Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/feba03130d5fceadfa3a1bb102cb14650798c848b1df2a808356f939bb16/brotli-1.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:acec55bb7c90f1dfc476126f9711a8e81c9af7fb617409a9ee2953115343f08d", size = 1528071, upload-time = "2025-11-05T18:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/2b/38/f3abb554eee089bd15471057ba85f47e53a44a462cfce265d9bf7088eb09/brotli-1.2.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:260d3692396e1895c5034f204f0db022c056f9e2ac841593a4cf9426e2a3faca", size = 1626913, upload-time = "2025-11-05T18:38:27.284Z" }, + { url = "https://files.pythonhosted.org/packages/03/a7/03aa61fbc3c5cbf99b44d158665f9b0dd3d8059be16c460208d9e385c837/brotli-1.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:072e7624b1fc4d601036ab3f4f27942ef772887e876beff0301d261210bca97f", size = 1419762, upload-time = "2025-11-05T18:38:28.295Z" }, + { url = "https://files.pythonhosted.org/packages/21/1b/0374a89ee27d152a5069c356c96b93afd1b94eae83f1e004b57eb6ce2f10/brotli-1.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adedc4a67e15327dfdd04884873c6d5a01d3e3b6f61406f99b1ed4865a2f6d28", size = 1484494, upload-time = "2025-11-05T18:38:29.29Z" }, + { url = "https://files.pythonhosted.org/packages/cf/57/69d4fe84a67aef4f524dcd075c6eee868d7850e85bf01d778a857d8dbe0a/brotli-1.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7a47ce5c2288702e09dc22a44d0ee6152f2c7eda97b3c8482d826a1f3cfc7da7", size = 1593302, upload-time = "2025-11-05T18:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/d5/3b/39e13ce78a8e9a621c5df3aeb5fd181fcc8caba8c48a194cd629771f6828/brotli-1.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:af43b8711a8264bb4e7d6d9a6d004c3a2019c04c01127a868709ec29962b6036", size = 1487913, upload-time = "2025-11-05T18:38:31.618Z" }, + { url = "https://files.pythonhosted.org/packages/62/28/4d00cb9bd76a6357a66fcd54b4b6d70288385584063f4b07884c1e7286ac/brotli-1.2.0-cp312-cp312-win32.whl", hash = "sha256:e99befa0b48f3cd293dafeacdd0d191804d105d279e0b387a32054c1180f3161", size = 334362, upload-time = "2025-11-05T18:38:32.939Z" }, + { url = "https://files.pythonhosted.org/packages/1c/4e/bc1dcac9498859d5e353c9b153627a3752868a9d5f05ce8dedd81a2354ab/brotli-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:b35c13ce241abdd44cb8ca70683f20c0c079728a36a996297adb5334adfc1c44", size = 369115, upload-time = "2025-11-05T18:38:33.765Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d4/4ad5432ac98c73096159d9ce7ffeb82d151c2ac84adcc6168e476bb54674/brotli-1.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab", size = 861523, upload-time = "2025-11-05T18:38:34.67Z" }, + { url = "https://files.pythonhosted.org/packages/91/9f/9cc5bd03ee68a85dc4bc89114f7067c056a3c14b3d95f171918c088bf88d/brotli-1.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c", size = 444289, upload-time = "2025-11-05T18:38:35.6Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b6/fe84227c56a865d16a6614e2c4722864b380cb14b13f3e6bef441e73a85a/brotli-1.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f", size = 1528076, upload-time = "2025-11-05T18:38:36.639Z" }, + { url = "https://files.pythonhosted.org/packages/55/de/de4ae0aaca06c790371cf6e7ee93a024f6b4bb0568727da8c3de112e726c/brotli-1.2.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6", size = 1626880, upload-time = "2025-11-05T18:38:37.623Z" }, + { url = "https://files.pythonhosted.org/packages/5f/16/a1b22cbea436642e071adcaf8d4b350a2ad02f5e0ad0da879a1be16188a0/brotli-1.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c", size = 1419737, upload-time = "2025-11-05T18:38:38.729Z" }, + { url = "https://files.pythonhosted.org/packages/46/63/c968a97cbb3bdbf7f974ef5a6ab467a2879b82afbc5ffb65b8acbb744f95/brotli-1.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48", size = 1484440, upload-time = "2025-11-05T18:38:39.916Z" }, + { url = "https://files.pythonhosted.org/packages/06/9d/102c67ea5c9fc171f423e8399e585dabea29b5bc79b05572891e70013cdd/brotli-1.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18", size = 1593313, upload-time = "2025-11-05T18:38:41.24Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4a/9526d14fa6b87bc827ba1755a8440e214ff90de03095cacd78a64abe2b7d/brotli-1.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5", size = 1487945, upload-time = "2025-11-05T18:38:42.277Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e8/3fe1ffed70cbef83c5236166acaed7bb9c766509b157854c80e2f766b38c/brotli-1.2.0-cp313-cp313-win32.whl", hash = "sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a", size = 334368, upload-time = "2025-11-05T18:38:43.345Z" }, + { url = "https://files.pythonhosted.org/packages/ff/91/e739587be970a113b37b821eae8097aac5a48e5f0eca438c22e4c7dd8648/brotli-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8", size = 369116, upload-time = "2025-11-05T18:38:44.609Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/298c2ddf786bb7347a1cd71d63a347a79e5712a7c0cba9e3c3458ebd976f/brotli-1.2.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21", size = 863080, upload-time = "2025-11-05T18:38:45.503Z" }, + { url = "https://files.pythonhosted.org/packages/84/0c/aac98e286ba66868b2b3b50338ffbd85a35c7122e9531a73a37a29763d38/brotli-1.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac", size = 445453, upload-time = "2025-11-05T18:38:46.433Z" }, + { url = "https://files.pythonhosted.org/packages/ec/f1/0ca1f3f99ae300372635ab3fe2f7a79fa335fee3d874fa7f9e68575e0e62/brotli-1.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e", size = 1528168, upload-time = "2025-11-05T18:38:47.371Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a6/2ebfc8f766d46df8d3e65b880a2e220732395e6d7dc312c1e1244b0f074a/brotli-1.2.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7", size = 1627098, upload-time = "2025-11-05T18:38:48.385Z" }, + { url = "https://files.pythonhosted.org/packages/f3/2f/0976d5b097ff8a22163b10617f76b2557f15f0f39d6a0fe1f02b1a53e92b/brotli-1.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63", size = 1419861, upload-time = "2025-11-05T18:38:49.372Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/d76df7176a2ce7616ff94c1fb72d307c9a30d2189fe877f3dd99af00ea5a/brotli-1.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b", size = 1484594, upload-time = "2025-11-05T18:38:50.655Z" }, + { url = "https://files.pythonhosted.org/packages/d3/93/14cf0b1216f43df5609f5b272050b0abd219e0b54ea80b47cef9867b45e7/brotli-1.2.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361", size = 1593455, upload-time = "2025-11-05T18:38:51.624Z" }, + { url = "https://files.pythonhosted.org/packages/b3/73/3183c9e41ca755713bdf2cc1d0810df742c09484e2e1ddd693bee53877c1/brotli-1.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888", size = 1488164, upload-time = "2025-11-05T18:38:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/64/6a/0c78d8f3a582859236482fd9fa86a65a60328a00983006bcf6d83b7b2253/brotli-1.2.0-cp314-cp314-win32.whl", hash = "sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d", size = 339280, upload-time = "2025-11-05T18:38:54.02Z" }, + { url = "https://files.pythonhosted.org/packages/f5/10/56978295c14794b2c12007b07f3e41ba26acda9257457d7085b0bb3bb90c/brotli-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3", size = 375639, upload-time = "2025-11-05T18:38:55.67Z" }, +] + +[[package]] +name = "cachetools" +version = "7.1.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/39/9a4689914dd907915cee74733b95888fc1d8a21aad47a24a0a2deec73ac4/cachetools-7.1.8.tar.gz", hash = "sha256:1221d547a0b24b7f26fa891d40d488b5258beab9aebd8ed68c729be3af849c43", size = 40909, upload-time = "2026-08-31T19:02:53.985Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/3d/9487690d0e937854db587205c66bab3c3cf88d9f00ed380b74cb88cc94ee/cachetools-7.1.8-py3-none-any.whl", hash = "sha256:a81e3844acaa7355b6567f97bd67a94a14ec3a9bc2cbbdae45b9592cc036775b", size = 16842, upload-time = "2026-08-31T19:02:52.554Z" }, +] + +[[package]] +name = "caio" +version = "0.12.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d2/c9/ac301b7f86ccf6ad02ee78eb953ce8f75175754cc5e76d398e447af42e3e/caio-0.12.4.tar.gz", hash = "sha256:32d8e9f3e2099c8db29446679252766c9bcd806eb88b4fb60ad274f73df2a5e9", size = 81006, upload-time = "2026-09-07T06:52:39.18Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/35/7ddc8de48cd0142797db186c07e901005b966dc6e8a7852f0273d33e4840/caio-0.12.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:67b725641fea682a2e9b1d3b2a150d21f1a25383e3ec8351bd7677ff857fa982", size = 47927, upload-time = "2026-09-07T06:52:13.58Z" }, + { url = "https://files.pythonhosted.org/packages/8c/b4/37556bde83253cf1ed071a754c8405d02b955febd021c400f0ffa6258d5f/caio-0.12.4-cp312-cp312-manylinux_2_34_aarch64.whl", hash = "sha256:4854a029e359e5ddfb5589ab66157a34af6a0dda0acdaa43f5e640d60182c1ea", size = 161402, upload-time = "2026-09-07T06:52:14.581Z" }, + { url = "https://files.pythonhosted.org/packages/7a/11/519e40a1da4d18515ab5ddb54e75cdd896532339010f2dcee7e798e4edab/caio-0.12.4-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:f063624a98a64bab387430c5eaea61ef3825a98399104a297b8da4741c15139f", size = 159311, upload-time = "2026-09-07T06:52:15.853Z" }, + { url = "https://files.pythonhosted.org/packages/45/2a/0db30de6c2561e41721d94ac627aa65db61a403545e67914814d8c2b5305/caio-0.12.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a926d562f8c06767774a91239770ebaa93c10a24074e21ea741db208df9cc1d", size = 158910, upload-time = "2026-09-07T06:52:17.27Z" }, + { url = "https://files.pythonhosted.org/packages/1e/e0/5eb43a89aecdf9d6f929b0080e49d92ccdf3f88bbc637353f52843b483af/caio-0.12.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:53605636e3b1eaeca368b475f1471ad9774736c6502a4b58b8b6b8d74b531770", size = 158973, upload-time = "2026-09-07T06:52:18.527Z" }, + { url = "https://files.pythonhosted.org/packages/11/ab/12e4896a1e74ff9a65c84894acdfc3b70b8b49e63874a37e0dffcc31f396/caio-0.12.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:134d9d145d75f454de5ece9e87595bad433639b51061b693bafc369f689f8742", size = 47934, upload-time = "2026-09-07T06:52:19.644Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d4/3a96f5537e3fa4e3256f93244fe883a5a4879f2b9e2d59d50151bb417f37/caio-0.12.4-cp313-cp313-manylinux_2_34_aarch64.whl", hash = "sha256:27ec5671ac05650abc7ac1fb12e95ad09417ae495ce9be565927786688edd6c5", size = 161559, upload-time = "2026-09-07T06:52:20.637Z" }, + { url = "https://files.pythonhosted.org/packages/05/64/f33b9aaf7bc9bad988211d8e42b4b85c25a1d5dcc0906798aebe49bd421b/caio-0.12.4-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:1e1d5570fd0ec75b1b2c2877c5545ed9f0738b5d23b000e2b6a8fc830dc8b310", size = 159453, upload-time = "2026-09-07T06:52:22.011Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9e/3fc5e143728d99f8af11453789d755937fbe795a6c5c9c9ea529c91df519/caio-0.12.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e22ce2e69d94e4c80e0c11c871e547b3c2d147f977632894cb2527ddb6e87a8d", size = 159028, upload-time = "2026-09-07T06:52:23.225Z" }, + { url = "https://files.pythonhosted.org/packages/ca/8d/e7a165aa44bb2876bec96f7fb1995346d14e43d46237421382f8af4f37da/caio-0.12.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9dc0fd6f09ef72d18d3f43ca3d1140295222d925c583114ab9b1e8843a109a6e", size = 159088, upload-time = "2026-09-07T06:52:24.362Z" }, + { url = "https://files.pythonhosted.org/packages/6f/31/d31ed073a7f8e02d352b390ff556757ba82afbd747c68238b1bda638ce6f/caio-0.12.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:413565d77dfdf2dd841ac100571ef1cc710f9c56137312f3ec51356350b4f3a5", size = 41919, upload-time = "2026-09-07T06:52:25.5Z" }, + { url = "https://files.pythonhosted.org/packages/6d/07/ab0bc8a481126e6b4f6f56f3bb94e41568fc4604df15420616251b2e28da/caio-0.12.4-cp314-cp314-macosx_26_0_arm64.whl", hash = "sha256:cd1d00ed1867a3b7a4cffb64b1bd8644de4b6f88cb25240cf82a9cc160ace975", size = 41131, upload-time = "2026-09-07T06:52:26.512Z" }, + { url = "https://files.pythonhosted.org/packages/24/3b/49382d9f7b3f65c76bb63a18a657833c4bfa695555b9663139950dec3c11/caio-0.12.4-cp314-cp314-manylinux_2_34_aarch64.whl", hash = "sha256:4189104e5579553031340a677762398c130b81ddb7d7c9b69eebffddaecc4f58", size = 162063, upload-time = "2026-09-07T06:52:27.717Z" }, + { url = "https://files.pythonhosted.org/packages/14/7c/eaf41fb7e0c864c3a5ce4c259eba0f505633efe4f789fffb6d3f1eb7e9e1/caio-0.12.4-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:4f2e0b6d393dc0ea78cfd13a9a4321fc35fd6a2db802e5f64651317e859fc929", size = 159514, upload-time = "2026-09-07T06:52:28.914Z" }, + { url = "https://files.pythonhosted.org/packages/41/7e/4041947d8caa760b74a27c8082844c72974da7cbfb96737990b8c6749e53/caio-0.12.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:710ffe4a6f3d69dbfe98e9f856c24a544e4d639c7bd210f9f5503a26e62502f4", size = 159753, upload-time = "2026-09-07T06:52:30.081Z" }, + { url = "https://files.pythonhosted.org/packages/32/76/b6c524a51eca13b861e8f98c927b933fdf5766f0c7c9017a21d889a84e3d/caio-0.12.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ddf8d946b795ebd7c75b331b6dcbab4808fdd2c9f16ca76012b4757512861133", size = 159167, upload-time = "2026-09-07T06:52:31.288Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f4/c39ed874d420bb2719d52932d763953c28371c536d6bf264de1febeb27d5/caio-0.12.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:17a41de51203fc4d787d8d577bd56f169167aa9822f14fada015504688791aa4", size = 41920, upload-time = "2026-09-07T06:52:32.432Z" }, + { url = "https://files.pythonhosted.org/packages/e7/54/58f4ba2ac9ba69bd30e5c89493d34aa483743f823f79a854f20771fb80ca/caio-0.12.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ee9de68d6ede7ed69e55a1c09073ce146d9f9470088796d55238c485a93fe203", size = 183327, upload-time = "2026-09-07T06:52:33.492Z" }, + { url = "https://files.pythonhosted.org/packages/25/81/6b86722be50975b80a636afebed5af1a6e6588afeff0ae1a0a7f8c262707/caio-0.12.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:1c1a387f8784a86a56a8912ca42e1ad67599f16406f497b5b655b461a1b89426", size = 179368, upload-time = "2026-09-07T06:52:34.66Z" }, + { url = "https://files.pythonhosted.org/packages/a3/a3/19ab810f11d1688bb2009e669b2645410a89b7a35b608239ac4076dc1611/caio-0.12.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ceee64265a55b9aba9c38c1d01a159b99f1e36eb56938022018199d2e7fe1742", size = 181460, upload-time = "2026-09-07T06:52:35.841Z" }, + { url = "https://files.pythonhosted.org/packages/0f/b2/e54e6e445d600e22b107e4005714cad0657bc7dc19d200817ab7a62ac722/caio-0.12.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:456a93868ff007cea65d916d96d192cda8b9992c6eee046cb0159040f9446ab5", size = 179230, upload-time = "2026-09-07T06:52:36.978Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e6/07100a694613344d22958fb6d803fdf914ee9395278fe58ad1b1b9c59e52/caio-0.12.4-py3-none-any.whl", hash = "sha256:7a5e231bbf81eaed269f99afa9ef46457f51f9407952a1795c0795b3a62c23c0", size = 25628, upload-time = "2026-09-07T06:52:38.147Z" }, +] + +[[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/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 = "charset-normalizer" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/3f/143b048436775b0f76ac3eec145c019e8173ccc2885c8f20319b996d5e83/charset_normalizer-3.5.1.tar.gz", hash = "sha256:6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3", size = 171764, upload-time = "2026-08-15T08:20:44.807Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/27/78873dc8b6a56357517b74b6bb9568b80450e7bb4f6ef7e3fa9d22aa0bd7/charset_normalizer-3.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5b6d1386bf0096d26d3a863dc0a487a5b4eb9aa93cf5ba69683d29dde6b9d60f", size = 344456, upload-time = "2026-08-15T08:17:10.072Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4c/be49ada26b1f0232d57aa89bbebf997a5cc2332a5616b6eca26ff680044d/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4582c27e8c889d64811987b5967fbd3ae0c823fe1fd933b543d55ac20bb475fa", size = 238530, upload-time = "2026-08-15T08:17:11.563Z" }, + { url = "https://files.pythonhosted.org/packages/76/84/6f1290fa07ae6978d3960caa3eb1b8019bf9284ab7c2297b00c099ef4250/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d1c7a53a6c2103925cdd6d7229f8c567379f211c869793df679f2e9f738c369", size = 230200, upload-time = "2026-08-15T08:17:12.919Z" }, + { url = "https://files.pythonhosted.org/packages/e7/a0/47b18adeed31c8f16ba9700f32c1b18594cfa09f47eb672a488c273c22bf/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e6621fb2a4988d6e53eedc455e5903e2679f3967b8acb3d639f1b63c14a2e893", size = 262222, upload-time = "2026-08-15T08:17:14.571Z" }, + { url = "https://files.pythonhosted.org/packages/38/fe/341861ac118dae06f3ec0eb487488af52128f2ef2faf0b11003944d22259/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c0c10730342b0c9b35dd1d619beb8214e520bd96a1f870f452680b238aab3e0", size = 258951, upload-time = "2026-08-15T08:17:16.158Z" }, + { url = "https://files.pythonhosted.org/packages/6f/89/bb5108dc6c3651dca963f2b0a3ba19bbcb370c94e1b6d3e0e844a58e6dca/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08", size = 248801, upload-time = "2026-08-15T08:17:17.683Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ba/ef83ae3aca816393decfa3530976f38a79812d707b80b580ac33b83f9877/charset_normalizer-3.5.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9f8405c2c758532c74fed975dbee57be1f31a6e865c031870c79a6ed3212ada", size = 244070, upload-time = "2026-08-15T08:17:19.191Z" }, + { url = "https://files.pythonhosted.org/packages/f6/0b/c5292a2462d69b7378ea89793bbb5b2b6fcf6f7dd6d1667f9619094ad553/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:96fef3e886d6a9874b14f27fc193fbdc69d5d8035783d86aa4e1cea594e695f9", size = 240110, upload-time = "2026-08-15T08:17:20.547Z" }, + { url = "https://files.pythonhosted.org/packages/46/22/111e5be3b740d5c2a5bfcedb3d237b6591e5c2e82ae9d6ffcb121fe0909c/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5d8531a6569d025f68e2321e7638fb7978f23db58e5f69f56913837aae03816e", size = 232836, upload-time = "2026-08-15T08:17:21.895Z" }, + { url = "https://files.pythonhosted.org/packages/f9/d2/d2aad6fe0dbb44b194bf3becb60f5a0ac48446ade999a47fe7bb41eb09a7/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:aae2ee51122d3ae968a3837d97dc24a0aeebb0dea23694422cd172bd30017cd6", size = 262712, upload-time = "2026-08-15T08:17:23.727Z" }, + { url = "https://files.pythonhosted.org/packages/35/5a/337e4663a5eae6de99db940ee8066d4145caafb61327db62deda15313cce/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7235dc28fc6dd9d832ac7c7bce95367dedb85929f17368a0c2bee1e080b9acbf", size = 242977, upload-time = "2026-08-15T08:17:25.157Z" }, + { url = "https://files.pythonhosted.org/packages/ca/85/f82f8a92e31c7519410e2e1afdc630f28ec47490ce2c09a11c1a43cbb459/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4abdc5f9ad448c1ecbfae2974b820535d6bc6e7eef63babbab3d81cf46968c71", size = 260207, upload-time = "2026-08-15T08:17:26.602Z" }, + { url = "https://files.pythonhosted.org/packages/b7/52/643d11ffd60e9ac2fd1fb87e167a19285b9eefeff4a40e63c87cbfbeab36/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba501e667c17d8411f98e67a022d9604ef179aff0e459b7e292c796837c13573", size = 250562, upload-time = "2026-08-15T08:17:27.971Z" }, + { url = "https://files.pythonhosted.org/packages/62/16/46556278c2168d12df9da7fede5dc6fc70e60301b26a82bbeec238c9cfe3/charset_normalizer-3.5.1-cp312-cp312-win32.whl", hash = "sha256:cfa1c0cc3a8f9f53f1243a5a99ac36fd003880199383b37672e86ddda9cb07e2", size = 178507, upload-time = "2026-08-15T08:17:29.277Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7a/4c6c298171e6b3e745633180ff59350fc0ca0db1ffd28df1e369e0579f71/charset_normalizer-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3617ac3cfd8b9888f145ad89dd6e692285834b0201c6074a5eeaad3fd4d668c2", size = 200551, upload-time = "2026-08-15T08:17:30.668Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d7/eb95a042f0dd22e304b0b6472b154f3546a1a039a9ee89ccb2a7f61591fc/charset_normalizer-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:88e85ab89cb822c1e635f51d6d32e488f94e002e70e2f492bdb8b945543f345a", size = 180700, upload-time = "2026-08-15T08:17:32.028Z" }, + { url = "https://files.pythonhosted.org/packages/bc/61/2cb6ad133dbbb449fa2d37ccae973232f4827e799af258d15e589a3d1e9e/charset_normalizer-3.5.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:4f298bdadb8f0b9e5672877f647d1be9373ef5320c9e2f049795e26cad28b6a9", size = 211584, upload-time = "2026-08-15T08:17:33.597Z" }, + { url = "https://files.pythonhosted.org/packages/18/57/a305c968be1ca13f3dd1b32f445877e97addf55d80b65c7cb35fac82b777/charset_normalizer-3.5.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:88ca277405c2d3b71c4e1c2ee0e7966e807bcba86a69d11e19ba199d18ae4491", size = 223359, upload-time = "2026-08-15T08:17:35.022Z" }, + { url = "https://files.pythonhosted.org/packages/09/0a/d3646670292ce8d8f8cc11ac067d44885e697a5591f57a9221128da5e7b3/charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9362dd90aa7dab48c0054a21187791ccf05473f7dba5d92b8033ae62164675e7", size = 194464, upload-time = "2026-08-15T08:17:36.452Z" }, + { url = "https://files.pythonhosted.org/packages/de/93/d51ec556e01042fed6f993ea859311bc7917b466684182fbbceb6ca24762/charset_normalizer-3.5.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:977cdbd483a9cff38179bea4fd754289a6f2195c7abd414aba85410b3e66cc5e", size = 197676, upload-time = "2026-08-15T08:17:37.819Z" }, + { url = "https://files.pythonhosted.org/packages/a4/a0/562247944386f7d4ef94467e84876600cc1e0f1b93239aaa9213d2bc3cbd/charset_normalizer-3.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e90251c0c7bdd54a100a0dce3c07b7e637278c93af29dbf78ebb89a58c4bac7d", size = 340473, upload-time = "2026-08-15T08:17:39.303Z" }, + { url = "https://files.pythonhosted.org/packages/31/e7/1d994be1b93d41e9502b8b0460eaa88a1dd8df335df415db87d6c3e91ab2/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94d78ecec2605a8d0398b0f365d5f12a63248438516f5dac536a5eff7337df4a", size = 240156, upload-time = "2026-08-15T08:17:40.66Z" }, + { url = "https://files.pythonhosted.org/packages/09/53/27923ce5cc6cbccb832037b27dca98882d9c53e9b69e866bbbef4aae7fc8/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d59b75732e9b6f27388e10c14b0259cc5f2e48c78627d185e6a177b58ad3cffe", size = 228246, upload-time = "2026-08-15T08:17:42.003Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/5a97e84d63af1d55c07439cb80e56d99a8efb4295700eb4e18c0d1615d2c/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0d929fc574b4d6fd9e7c0f5c2ede8716a41911923aa7fa5fce38e0818aa4a1ac", size = 263660, upload-time = "2026-08-15T08:17:43.627Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c2/071575791dcc88316c0a9a65ce38897a82e4cfe4a325f0f7fe1b1ac47bcf/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:394fea06235c8543390050ed5f529187074b029fb027213f6c46ac11ab5d950e", size = 260354, upload-time = "2026-08-15T08:17:45.094Z" }, + { url = "https://files.pythonhosted.org/packages/fb/af/63240b0c0248c075c2535a1f1bd992821d8251b9f173abc13329661d09e4/charset_normalizer-3.5.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62b55f6722735a6c472f88361cde6640608773d9443cebdbb51abf436a1fcdd3", size = 250638, upload-time = "2026-08-15T08:17:46.496Z" }, + { url = "https://files.pythonhosted.org/packages/4d/66/70dfad64f15be09c15ccfee81330a7e515895dbe296dd23114e9a231268a/charset_normalizer-3.5.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa48b1b63d639f9483e0633e092f5851e2348c352f1f9bb6c8182f87884ef876", size = 244583, upload-time = "2026-08-15T08:17:47.963Z" }, + { url = "https://files.pythonhosted.org/packages/c0/24/ef36367d38b9ddd4bccbf72888c342e8de1f5ae506fa0b2dcf970e2732a1/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c71fb0d56c920c269cd3e2e3fe7c610e3f1fdb21a6ce60efa6430ff63676cea6", size = 242038, upload-time = "2026-08-15T08:17:49.481Z" }, + { url = "https://files.pythonhosted.org/packages/db/ab/55e683ba0fff2e43adafc10daa3001eac90fdaa419a97227d5a7067eedde/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:485a0d363cafefcd2538a73c7c838daa2035f09b2c9f9b5e3133f80c6aeb84c2", size = 233677, upload-time = "2026-08-15T08:17:50.845Z" }, + { url = "https://files.pythonhosted.org/packages/bd/67/0f40eaf8d1b6e7cf15e82382a2965efaca787fc1c2794b7021d37aaf5036/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0ea61a470e070686aa30892fed79e297d2c8d0ab46b8bcdf027d38c51da591", size = 264491, upload-time = "2026-08-15T08:17:52.61Z" }, + { url = "https://files.pythonhosted.org/packages/5c/64/12b4c2a11ee8df4fcc518c78b0d93e3a92bd3d5253d1617ce74ff0e8c7ef/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:90b7481fb62fbe172c558bc6fd1c4c98d82004a54a7551f20e11ac9bf0b8708c", size = 245196, upload-time = "2026-08-15T08:17:54.023Z" }, + { url = "https://files.pythonhosted.org/packages/37/2e/651d910af6d0fba325eee1cda37ec5443462ed25360e666c144166eb6091/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:35fe081843b35aad20ffeccec3eeffbe637b15d14f3fb22cc1b59cd8ec17e93c", size = 261660, upload-time = "2026-08-15T08:17:55.491Z" }, + { url = "https://files.pythonhosted.org/packages/90/c6/b09e05e6db7f64338e0dc067c79577b1138da86c1e38369096851d96be88/charset_normalizer-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fd0350afdc3aabd5576f60ea109228bd5538139713c7b094c5cd27c73a98bc6f", size = 252618, upload-time = "2026-08-15T08:17:57.025Z" }, + { url = "https://files.pythonhosted.org/packages/76/4e/362d4f9fdcdf5556fb2aa3ce7d4a58ebce03ed1ff03aa1d9aca8d02f13f3/charset_normalizer-3.5.1-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:9d9a0dc7cbe9bec24c3f767c9122c41fe5a1bc43f47cd099d00d393e09769de4", size = 140362, upload-time = "2026-08-15T08:17:58.425Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d4/703be739b26acce318bd29eb3b25b7209e1b1f527f9eae3d1f1f01fdde2b/charset_normalizer-3.5.1-cp313-cp313-win32.whl", hash = "sha256:d63600d620ad0064c3a748b950ac5ea38a80190e5498532efefa4b7b3f1da1f3", size = 177755, upload-time = "2026-08-15T08:18:00.037Z" }, + { url = "https://files.pythonhosted.org/packages/8a/33/56d97ade41c8db611e727168c52ae46c9224c362ec28d4b65d7e9869e8da/charset_normalizer-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:aea996a6aba25260827c9ea511d1addfde2da9eb686ac961838509086188b7e6", size = 199295, upload-time = "2026-08-15T08:18:01.506Z" }, + { url = "https://files.pythonhosted.org/packages/5b/75/5b20dd1e6573a01a08158fe104104fa2c8abf941745596954185726cd46c/charset_normalizer-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:fd0a274c0e5f9a21565cd9d3dd749b61f96b7aa1e20a93aa1ba4029518f2e5c0", size = 179856, upload-time = "2026-08-15T08:18:02.929Z" }, + { url = "https://files.pythonhosted.org/packages/29/cd/2b812ce5e888f1ce69a5350281e58aab07ae64a958ecae8912f30865718e/charset_normalizer-3.5.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:774d157f112367ff4abd29019f38f023c24e00e56edc7829c20e358a5a913ad8", size = 212318, upload-time = "2026-08-15T08:18:04.403Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4a/a6ee107430768a5334e6d63f31f148a04a1a491ef161a1ac9415a73f2fa8/charset_normalizer-3.5.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:26422d45fd13551cf564c58932f7d72b4f58b93b0fcf18c35ba6be12b46bb102", size = 224897, upload-time = "2026-08-15T08:18:05.997Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d9/35ae3f64f29d0179c35c3baefe575904df2913dde519129c7f75995a2b1d/charset_normalizer-3.5.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:09a7bba9f739468c8e78c36a75c33768e53cb1959fc638f510454c14683f00d5", size = 194848, upload-time = "2026-08-15T08:18:07.397Z" }, + { url = "https://files.pythonhosted.org/packages/74/76/f2fc7380f056cc273a53af37f50d08ad54b2c59f61078f31432edcf1c2bd/charset_normalizer-3.5.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4c9548dc78002099910abaebc0a72ac58b7d30931869e0351c09b507dff4ece3", size = 198163, upload-time = "2026-08-15T08:18:08.989Z" }, + { url = "https://files.pythonhosted.org/packages/e9/40/095ce62fa078483cccc1fa2b36e6bc9580b85422a20ee9f925341c50e44f/charset_normalizer-3.5.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c428c6c31eb5f4277d7f8eccaf767fbd548ddd5ce3c8b4f4cbbfab3d96b5904c", size = 341823, upload-time = "2026-08-15T08:18:10.458Z" }, + { url = "https://files.pythonhosted.org/packages/f1/5a/0e58b1c04a1596e0256f407274a92d5fb2ee21324409d1fab1da48a65b5b/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f06b7eae9dbe77fe1d644ca244dad508de8d302870a43f3c559b521270938a0", size = 242458, upload-time = "2026-08-15T08:18:11.989Z" }, + { url = "https://files.pythonhosted.org/packages/22/95/b4618ce912e6db0b1aae89ba788e38e8a7eba0f3025cc66e8c0699f977b2/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b7430cf5728e68f6c462254009a6ef4086e1bea43cf2f57aa9c55fb4f50ff96", size = 226717, upload-time = "2026-08-15T08:18:13.401Z" }, + { url = "https://files.pythonhosted.org/packages/8a/76/c681192bbda3d55356db5dadd64381d5202b37c6b598fcda5282e88b5d3d/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab743e9bc90c1f73552ec33e10e3331315acd2c397b36065b591b0181de533cc", size = 266111, upload-time = "2026-08-15T08:18:14.961Z" }, + { url = "https://files.pythonhosted.org/packages/88/be/55127bfca72c0cff6c022488d140d7c5b04c771e3b72e9bdb4836d54979d/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6f7deae3feb4edfa2efaf7c574fe88cbf055038a6abdb40188e4fff66d5699f", size = 263128, upload-time = "2026-08-15T08:18:16.515Z" }, + { url = "https://files.pythonhosted.org/packages/e0/91/39c3af510b0aa32bbda03374259200f28430febfd1bf5e511fe765282ce5/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15f024313246a4ed976c60f440bb8d257815513a681d212ff74fd46f7d715a90", size = 251240, upload-time = "2026-08-15T08:18:18.127Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a5/cbe418bbc6ecdfc3e05a0116002897c4b403a5e838d697e64c78e9f0190d/charset_normalizer-3.5.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:823f82903d189af463d7df250ef1f7f696f3cee08cc8d91deb565e8d425f6506", size = 245282, upload-time = "2026-08-15T08:18:19.625Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a4/689bb42e8e7cd492f3cb64907c6bc00ad247ec9a3628cd3f8eed126e8ae1/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:01e93745f7f219b703b60ba7afead36cfc4242782be5af484673fc500df12da5", size = 244597, upload-time = "2026-08-15T08:18:21.121Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ce/9962938e179cf9f699d3f1e7b3114b5d7642dee6a893745229f9dd04f274/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:329fc3ccb63ad22d867d84c2adea759a64079a37ba4a343433b02c7a2816871e", size = 231376, upload-time = "2026-08-15T08:18:22.57Z" }, + { url = "https://files.pythonhosted.org/packages/85/54/46000450ada53bd9eac5429a2c8c54cd2d9b39c0c255f229aea9af0948a5/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:bb57753e36e4855b8ca375069482250a6246372331a3e4f3407eaebb007443f5", size = 266715, upload-time = "2026-08-15T08:18:24.235Z" }, + { url = "https://files.pythonhosted.org/packages/3d/bb/618749d70f792b44252a777bf89bfb86823b9bbc1ea13fe8ce759b07f38a/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fce8cbd4997efeb450bd298b54f755dcdff18d496f7a5ddbb4867c6d7c88fdc3", size = 245848, upload-time = "2026-08-15T08:18:25.726Z" }, + { url = "https://files.pythonhosted.org/packages/7e/3f/ffb64458527c7668031d5eb095d978de561958dc9f5b53f8e488a533e603/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6c9cdde8becb25a7fde49924511aa2644d6f8081cc8df8e9452724303348d8e3", size = 264521, upload-time = "2026-08-15T08:18:27.193Z" }, + { url = "https://files.pythonhosted.org/packages/4f/ab/74a55fd803916a35ac461daf002708191aac19b546b80dc8cabfedc63d98/charset_normalizer-3.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9ac4444d8d4fd4c4bd08bf451ed3167aa9e7ec6cdb41b648794f1d1103652e36", size = 253054, upload-time = "2026-08-15T08:18:28.568Z" }, + { url = "https://files.pythonhosted.org/packages/a0/2a/6a9034b7d3c60b17499afb482df5878bf9fa20b50cc3887d5ef017a833db/charset_normalizer-3.5.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:f03ac127268b43ef4fe9e6ab6794a6794b49485a0cc0c1db79876d2f33f75bc7", size = 140580, upload-time = "2026-08-15T08:18:30.214Z" }, + { url = "https://files.pythonhosted.org/packages/f3/46/1d362e1a00d035d66b9869e1281eee115907f7e390a16a07824ab5737360/charset_normalizer-3.5.1-cp314-cp314-win32.whl", hash = "sha256:1f5883d77fd409a261abb5dc8ccbe335720d798b1de4abb3b1d47ccbbc76b53b", size = 180325, upload-time = "2026-08-15T08:18:31.877Z" }, + { url = "https://files.pythonhosted.org/packages/7a/7c/4938c329b6a9d446f6a59aa2092ff7118f274209b5ed0e26893d1d30a63c/charset_normalizer-3.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:c658c50ac0c98cd755a2dd50b7977d3bca7df401dcc47fbdfa87db53ef7d4e8b", size = 204175, upload-time = "2026-08-15T08:18:33.466Z" }, + { url = "https://files.pythonhosted.org/packages/ac/33/eeb384dbd8dec570661354592f4f2e1b2fcc92585624d146a000caf53841/charset_normalizer-3.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:4bea7f8ebe90bbd7f0e4a2de42ca6924ba23e3e76418c408ff82f1d46fabd687", size = 184123, upload-time = "2026-08-15T08:18:34.913Z" }, + { url = "https://files.pythonhosted.org/packages/1c/6c/c73fa9d5a85f6ab05395de61c5f6984e0a9ff40bb5ff888d46dff02526c6/charset_normalizer-3.5.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:fbc597639158fd7c14d55e808718848319540f51b0e6746e3eefa59723a4a348", size = 381682, upload-time = "2026-08-15T08:18:36.349Z" }, + { url = "https://files.pythonhosted.org/packages/30/c7/63565f860921457feba93bae6c86fb7746deb4cffeed2f375cb845318146/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e71c909f353863b2b89c83de2ebed71ea6d0df8a6ef65a128193c5e650766bef", size = 240826, upload-time = "2026-08-15T08:18:37.887Z" }, + { url = "https://files.pythonhosted.org/packages/06/ae/7ae8807410dfa33f8e6f1715740adeaafa8a816cc4cb33508f54b1f7c896/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7ac76cf9afd34929d76eb7fcb63be476a4853d8a96f0dcf2d0db68a0cbdf9885", size = 227861, upload-time = "2026-08-15T08:18:39.315Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a3/887c1642f0da26000b0e0652d91071113c0e72cea33952e225cf589f49a9/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a3a370082ce34d0612f421e15fe011c53bb1feff21a26d06ad4fb244dab5a375", size = 260758, upload-time = "2026-08-15T08:18:40.88Z" }, + { url = "https://files.pythonhosted.org/packages/3e/11/e6f5b9a3d0e55b0ef7505cd3765cdd48f22db89994c947b316f52f801fd8/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:256dd4d85d9e4dc595e2bc983c980e73f62ddeb3165c58b4c3dfe78c5c8548c1", size = 259950, upload-time = "2026-08-15T08:18:42.351Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ee/e4e10a94d51cd1ee638aa7e00b65399e6b2a4e8376ab6d2eac9f95586671/charset_normalizer-3.5.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:58d4aa13a59c969dbfdf9e6a9560e242cbfd9e8a8f50c2747714df1a423adf65", size = 249329, upload-time = "2026-08-15T08:18:43.914Z" }, + { url = "https://files.pythonhosted.org/packages/c4/25/d5f4198819e6059735a84e8d0bfb72dc33976da67b97adcd3fb5a5e07ec6/charset_normalizer-3.5.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0c6dfb5ca6723eeed15aa8e564a014d69fcb8812f94eef11fe3631e0508199f5", size = 243137, upload-time = "2026-08-15T08:18:45.368Z" }, + { url = "https://files.pythonhosted.org/packages/a5/e9/e925ca7569cf9fb9701fd82503fee73eea5268fdb856bdd64947092d3daa/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c010f5581d9c612804cc59fcf7b524b707fbcb72828551237ab545bb5c7034af", size = 242820, upload-time = "2026-08-15T08:18:46.842Z" }, + { url = "https://files.pythonhosted.org/packages/34/17/672c251a888ed2aebcdd2fe830ad0104e25ff83c43f5c4f9c15e9fc6853c/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:52ec005752a56ae79547a05c0139ca2501a0c866390b6115008456b9f0e7cde1", size = 230504, upload-time = "2026-08-15T08:18:48.353Z" }, + { url = "https://files.pythonhosted.org/packages/3f/fc/f6a85abebd42ce4da2f1db0aa56cc6a0df1995e318b3875d14401b8381d1/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2bced4061f000f7187254a02ad3433ae17eaf991747ceea2f478422590a5bba9", size = 263087, upload-time = "2026-08-15T08:18:49.859Z" }, + { url = "https://files.pythonhosted.org/packages/98/66/7c42677e739ba66746b297e2046918d793078094dc239e1e72768cffccc6/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:9eea3ab2597a5e65fe65296e2d6a84570845a6b55532d90333d740d48bbc850a", size = 243269, upload-time = "2026-08-15T08:18:51.601Z" }, + { url = "https://files.pythonhosted.org/packages/de/d8/a50b79237f417af10f8c2a501ce8d1ca87829a22e69117891ca4ba20a69e/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:496846868fea80e479324862fa877f02411f2fd0f83b79ccee2607aa68b2a032", size = 258766, upload-time = "2026-08-15T08:18:53.23Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1d/0fc91aeaeb3c83b748f532399ce67cf84604b48297405d740000f7a9e786/charset_normalizer-3.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:85d5855daafc240cc045c026d7a15fd198a09b0fc8ff6f5ecbb5297b509cb11e", size = 250814, upload-time = "2026-08-15T08:18:54.768Z" }, + { url = "https://files.pythonhosted.org/packages/ae/10/3d8c777cf9024615295aa1b808324ad5b4a77855869c00824bad74ffaf8a/charset_normalizer-3.5.1-cp314-cp314t-win32.whl", hash = "sha256:58d3e12c88e0950bca850ae1f7c256055c097639c2edb9eb123af9807d8b15e4", size = 191074, upload-time = "2026-08-15T08:18:56.305Z" }, + { url = "https://files.pythonhosted.org/packages/4d/81/ae557d3c44d1a1d688696d60563413a0866a91b7ebc50f20df838be3d8c8/charset_normalizer-3.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:acaf604462bf330b0d07e7a07c1d6e4adac79e5fb13e9c5140590542cafacc00", size = 216476, upload-time = "2026-08-15T08:18:57.889Z" }, + { url = "https://files.pythonhosted.org/packages/27/e9/61c01fb8b804692569c036b3fc50495814502dcf13a60649c6055390b02c/charset_normalizer-3.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:fdb8a068947befafba9952162645dc2fecaeb400e64584829ed5e9b2fbe21a7f", size = 194115, upload-time = "2026-08-15T08:18:59.418Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4e/8544831ef59d8f27ce92c80871380fdacc8076a8a56ed62f82e54f991333/charset_normalizer-3.5.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:9085f87b0e38a2b92b8923059b4e8789fe40d9279712d15dcc670048d77079af", size = 342048, upload-time = "2026-08-15T08:19:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a6/e3b46852424246065355644f4fb6dbccc0239a42a2eee27ecfc8957f0bcd/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2679de311c7946dde5d3b6f44941844133ff5c7cb86099c0061ab1e8901c20a8", size = 242997, upload-time = "2026-08-15T08:19:02.492Z" }, + { url = "https://files.pythonhosted.org/packages/03/3b/0cc9a26777334ab2f2e3089b948bbf4e4fe72ea70b897715ef6415043ec8/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:baf3775a2635e5a11fbd5e4e64ee69c7e86875d224a5c72aca4c141064589a90", size = 237014, upload-time = "2026-08-15T08:19:03.943Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c2/027335f0aa337a2a2e121bac1ad88c4f02ba6053ea0926802784f3db11af/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ac8c94b6539074e0f40899301273ac8402b9b3e01c7b7ba269ff30340aaaf20", size = 266174, upload-time = "2026-08-15T08:19:05.598Z" }, + { url = "https://files.pythonhosted.org/packages/86/d3/e367787febe4e74769dec0f406f2c3c8d1b955fce5aee1fd0f94e8367a45/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe532b3c966d1fb794e0698e4589d0444017ae77fc0b31edea13c0e35bcc449", size = 263361, upload-time = "2026-08-15T08:19:07.251Z" }, + { url = "https://files.pythonhosted.org/packages/af/3d/391b193eb9f3e84b02f9314088c386debdc0debee843535aaea2e2c6715d/charset_normalizer-3.5.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5c84bec0ab5ae0c64bfe73a7d2adcb5ce73b467523fc27fd6a28ab2aa6cbe35a", size = 252143, upload-time = "2026-08-15T08:19:08.816Z" }, + { url = "https://files.pythonhosted.org/packages/2e/57/de221f1745a90d418199761967e2776bfe2c275a1194220985e8c1d37833/charset_normalizer-3.5.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:854066be00447fa8de2ccbbe893e2ffc4b123ef16d897af794c1e18bd4a714b0", size = 252086, upload-time = "2026-08-15T08:19:10.255Z" }, + { url = "https://files.pythonhosted.org/packages/c8/e3/d119f86a01f9331e8186175f24873b1d74a7ee9e2e4b4d68f9947dae5afd/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:21b82d8082f6f5e7f456ef0bd16323d08de1266efbfeb476e64b2a91d1471a4e", size = 245231, upload-time = "2026-08-15T08:19:11.807Z" }, + { url = "https://files.pythonhosted.org/packages/26/de/d8e48c135ae480879539cdb179c8d3b50c7879497d75dd899b5763b69cee/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:838648accb3a7fd9803fd45c87bce8509648eb0c11bc34e216141300977244f2", size = 241546, upload-time = "2026-08-15T08:19:13.416Z" }, + { url = "https://files.pythonhosted.org/packages/67/c4/217755fd1abc50d326c252922cd642002758095a81ff45010337b8b3ef65/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:195ce897c6153c0700078142cf8efe3e6454ca4cf4357499e4078dfd83396626", size = 267033, upload-time = "2026-08-15T08:19:14.981Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d7/34d8e404e358d2adcc5a228c2134643af00104c8fb0bf525f3688d756f05/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:978eab16f55b4ab2c2a745be9a0a840bf8f09a7f227d9c76eb30214d078865a5", size = 252045, upload-time = "2026-08-15T08:19:16.618Z" }, + { url = "https://files.pythonhosted.org/packages/5e/fa/40414471acf0aa0692ca77305aa00e434fcd8288f0941c93c30e9a5f8f2f/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:cc0329df4caaceb950d2f580b5ac716a377f7059624a0bafaeaf8a218c6ed774", size = 264866, upload-time = "2026-08-15T08:19:18.101Z" }, + { url = "https://files.pythonhosted.org/packages/32/90/fcc850bae791abd2e0c041847f13e270aa08692a79f3e00de6d2dce1cb50/charset_normalizer-3.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:687c9ca3035544b113bea2055e180af96fb63c0c476e22a9180f51925186e7b7", size = 253932, upload-time = "2026-08-15T08:19:19.734Z" }, + { url = "https://files.pythonhosted.org/packages/af/af/53afe99068b3c10b4cbae592a52ef72a7c92c0188440e83ee3a078fd8f75/charset_normalizer-3.5.1-cp315-cp315-win32.whl", hash = "sha256:706bfd38730a5ac7a365793269a00f4e988178cec121391f4248d84ad8c972e9", size = 180320, upload-time = "2026-08-15T08:19:21.37Z" }, + { url = "https://files.pythonhosted.org/packages/c9/bc/f46a132041b29e4a8779ed712d3df1bf112e94ca8de58b66d7ec2c0cf8b9/charset_normalizer-3.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:92caef967d287a407085d61176fce4012b1dd62daed4eb6d5ceb26d3d2538712", size = 204174, upload-time = "2026-08-15T08:19:23.088Z" }, + { url = "https://files.pythonhosted.org/packages/a1/5d/9ed554480eda8e447b673648628fdc29574d23dbad01fe11837adedd1cae/charset_normalizer-3.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:5fc45d653ea8c9a20479167e11d4a0f8cb2fa3470737ab6f9c827532313187b7", size = 184126, upload-time = "2026-08-15T08:19:24.471Z" }, + { url = "https://files.pythonhosted.org/packages/3b/32/9b8929bf384061ee1fe5d9c27c6f9776d3d824039ad4e14c88ec00c7808e/charset_normalizer-3.5.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:59171c6e45bf07d0d5cab3b0bf81d945035530f6873398b3b531c31184d46663", size = 381441, upload-time = "2026-08-15T08:19:26.038Z" }, + { url = "https://files.pythonhosted.org/packages/96/10/e9aa7923d3ddac652c99a1c5f7be494e737e151566a44abe018daf757f2c/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9dbdd9205662134957cf0c324f639bdc5031c0ca056e2369e238db75187c0f11", size = 241742, upload-time = "2026-08-15T08:19:27.532Z" }, + { url = "https://files.pythonhosted.org/packages/28/53/a2d249ebddf47b889a100c0bdcb61a2f9dbb8bc24ef325cc062e4f476877/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4b018dc5a0eee4676e38fe84a47a427816c590b93b55d9025274ec4d6ffc2dc", size = 235298, upload-time = "2026-08-15T08:19:29.274Z" }, + { url = "https://files.pythonhosted.org/packages/7d/07/469f78af590f7d5cd48e20d8dbfa3d66deeff9ba37768c04d886b5afd45c/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced3fdd71aaa83ce593746c2edb42b7a59cb4c19c8b5c407781c72e493aae55a", size = 262500, upload-time = "2026-08-15T08:19:30.955Z" }, + { url = "https://files.pythonhosted.org/packages/55/66/3bb56a47f7dcba014055b1a1d33c6f08bbe9c1e74dba154cfa25f90ae885/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:19a3dd5aa73cef1c99687c4fc57db016a9c17104ae1185da88ba566a5d3bebe4", size = 258888, upload-time = "2026-08-15T08:19:32.458Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c1/2adc2800903fb013210349313b710a5376856578d9e33e6b9a1d8b36714a/charset_normalizer-3.5.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc5d36d96478aa9c60654bd932525bf32964c62a7281eafdf16d85003a8d6004", size = 250243, upload-time = "2026-08-15T08:19:33.94Z" }, + { url = "https://files.pythonhosted.org/packages/95/b5/a18d0dd1157ab655cc2cb14a545f4a4784bbad70ab3502412e36097502d9/charset_normalizer-3.5.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:04368edf83514385ffc3e1cfd4546e595f4f1272dd23ba437a93a9cc3741d47b", size = 249871, upload-time = "2026-08-15T08:19:35.413Z" }, + { url = "https://files.pythonhosted.org/packages/ad/c3/525f508cd1e58d0450ac55ed40ac75bc3a97482c59def5278456a5fbf03c/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:9b5db6052055d34d41230fb78d7c439c23dc536a9896f6cb039e8dd92cfc1263", size = 243580, upload-time = "2026-08-15T08:19:36.886Z" }, + { url = "https://files.pythonhosted.org/packages/7c/c1/49a91fe7e97c8140094ca5c64161ab623a70d9f636bf834eace14048acb5/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:252d099029bcbea642f2a06c4ed5046bdf8b5a8150b64afa5e027e88b106e5ee", size = 239807, upload-time = "2026-08-15T08:19:38.392Z" }, + { url = "https://files.pythonhosted.org/packages/d3/58/56a48c296601274c4689b864a8e2dfb209b81dfcb39472753ce95eea662b/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:6199d5606e2bbf2b096cf64d03f8b6790c91081d5ac866b8e7bb6422738cc60c", size = 264083, upload-time = "2026-08-15T08:19:39.856Z" }, + { url = "https://files.pythonhosted.org/packages/10/4c/dc48409274a1817ff349711d26c62aa0c597df865d4d69ef79160c859193/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:77efcff2b23071c349402ac1066667a3d011f62398d81408c9b88ad991747c9e", size = 250317, upload-time = "2026-08-15T08:19:41.53Z" }, + { url = "https://files.pythonhosted.org/packages/81/58/d325912115caec62d6bdd77bbab5e0b7da5d234a9f20affdffcbcb530d0b/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:a5cbd90ecf0fc62e64726917ad083b73001f0563657a87ec3c0b504e277dc90d", size = 258173, upload-time = "2026-08-15T08:19:43.07Z" }, + { url = "https://files.pythonhosted.org/packages/34/f7/b13b1ccae2c8ec63980d13be1890eb73f8aeabbfce02a24aabc0908788f5/charset_normalizer-3.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:4d26f14f041e83dd8edfd61f4cd4fa7285d31798b5bf1f28e70c367ba6c41d61", size = 251960, upload-time = "2026-08-15T08:19:44.587Z" }, + { url = "https://files.pythonhosted.org/packages/1e/25/ed3f9919c5aef8cc818be1f972f565f7610d7b2076b8ebb98839516ffc3c/charset_normalizer-3.5.1-cp315-cp315t-win32.whl", hash = "sha256:ac13b004224fb341e1e25a1ed5e19d32f57cdb2a403e01f003b46f051a550f6f", size = 191186, upload-time = "2026-08-15T08:19:46.293Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/43c2b3e9d8267092b913eb8b0603f0f71993c395632886bd37a7223f96cf/charset_normalizer-3.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:35aea775dc2bd5f54cd84a1cd2696cc3207c479cb9cf0bd346f0d343e4300ddb", size = 215947, upload-time = "2026-08-15T08:19:47.853Z" }, + { url = "https://files.pythonhosted.org/packages/a8/76/9aad3e9c8865e5e0efa9a7f6f81c37a67635a985145ecd44528a81e088ee/charset_normalizer-3.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:fb78f6e7fcd8ad785d28cd577168bc1aaee827b25bb8755638f694794ea98f0a", size = 193909, upload-time = "2026-08-15T08:19:49.383Z" }, + { url = "https://files.pythonhosted.org/packages/5b/97/fb4e82231aba271ffd775a1b4993b0defc4e3059f286ae41d9433409fe85/charset_normalizer-3.5.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2", size = 331467, upload-time = "2026-08-15T08:19:50.959Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2f/fe3f187327aac18e2d54e9d2b08e15d27bf9b642d9e51c219f130fc34d1a/charset_normalizer-3.5.1-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99", size = 253057, upload-time = "2026-08-15T08:19:52.654Z" }, + { url = "https://files.pythonhosted.org/packages/d7/c7/9e48cee5c161fe24da823b61bf381921d77cb994a0a4de148e95018c1984/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2", size = 240930, upload-time = "2026-08-15T08:19:54.163Z" }, + { url = "https://files.pythonhosted.org/packages/49/e0/716601f3cc69be7b198951150c75ead1ece33c3c8036ff6ffa46029659a0/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:343fb4f2821043bd87095f7b08a1a181febc8e36ac64212143bbfd0a0e1bc235", size = 230822, upload-time = "2026-08-15T08:19:55.807Z" }, + { url = "https://files.pythonhosted.org/packages/d3/05/71bfc5caa0abcc45aea1f6a4d50ac68e59605ddc7666fe8494f4cd229665/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ae4a097991662cd4fff0ddc74e0fe7874f82e00042fa0ea00855645ed0c79598", size = 260037, upload-time = "2026-08-15T08:19:57.312Z" }, + { url = "https://files.pythonhosted.org/packages/c3/92/de7e32ed05341e7a9c4c877c318418197b7f2d66a3b68d561bf2ac57ca3e/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b599739b93b2cbeded49645ae3c8d1405c29ddfbceac1545c87a3f9580a9e96", size = 255097, upload-time = "2026-08-15T08:19:59.056Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7b/ade0a122600319dfa0b1000ab0f9731c94a817904cf3c5de408c73a4ede7/charset_normalizer-3.5.1-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b39b69b347e5e47a3b5b8cfc005c68c1ba347474e3960236c4944a8ecd174962", size = 250166, upload-time = "2026-08-15T08:20:00.612Z" }, + { url = "https://files.pythonhosted.org/packages/75/9c/019fbb9f4834491a160951349b1a3714439376f66e5f7cf18b4f18f0c7aa/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a2028475ba855475b8b4d3cfeb4994269c967aea8b9892dfba907f4263a863a3", size = 241821, upload-time = "2026-08-15T08:20:02.321Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b8/11d4840bfc99330cc7fbcc2681ee5a044553a6e77655508d8f9b2bff7b34/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:36047af20e17097c3bb9476c2b7655f2f7aa51322c0ba58c07695bedf755a950", size = 232529, upload-time = "2026-08-15T08:20:04.008Z" }, + { url = "https://files.pythonhosted.org/packages/18/96/2b3a21492d9f65171ac75d872f5018260013d00bfa0ff70ec9f179148cbd/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c4fb141a727957c93edfe5c32a26ceb6b5f6461d67146e2d39f51e16170bea8", size = 260348, upload-time = "2026-08-15T08:20:05.877Z" }, + { url = "https://files.pythonhosted.org/packages/d6/aa/a69a2028e8bd052476c245460ab19d7de595de084dd968f2d75cd50c3e25/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:2f293479cce755c75f1697e87c409b7ae4c555c7dfecb6e988ad13abba943031", size = 247234, upload-time = "2026-08-15T08:20:07.487Z" }, + { url = "https://files.pythonhosted.org/packages/35/8a/3d130aeabcaf3d2466af76b7b141c08d9e89c9016ab4b7cdd0f7dc2d1c62/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:3588e376b3ea2eea84976f67273d679f229e24c66dce7b82ae45aef04ff6e072", size = 256917, upload-time = "2026-08-15T08:20:09.142Z" }, + { url = "https://files.pythonhosted.org/packages/80/c2/a7379b840292d0c1ab9fbd17d1f3967aa81794dc95bc74be8999d7fedcf7/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d", size = 254846, upload-time = "2026-08-15T08:20:10.727Z" }, + { url = "https://files.pythonhosted.org/packages/01/65/d43b714731bb2f40d4053dfa00ecfc1c5a301f8e3316c5db3a09af59fe94/charset_normalizer-3.5.1-cp37-abi3-win32.whl", hash = "sha256:dd732602a7009217f658d5863d12d79d373a4de0eebc111094bcdd3bb8e0a6cc", size = 174216, upload-time = "2026-08-15T08:20:12.334Z" }, + { url = "https://files.pythonhosted.org/packages/35/4f/b911ed898b26a09789eba9c9200c999aff6c61b4bafaf4838e56d1a1e1a3/charset_normalizer-3.5.1-cp37-abi3-win_amd64.whl", hash = "sha256:70055ff39b97c99e7ae40ea3e393fb62aa2e44dbd9b29f8d14f42fb0025c3959", size = 199764, upload-time = "2026-08-15T08:20:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a7/920baf467bfd9bf689f3b318340f37aee4572a71f162bd8db51da55ba4fa/charset_normalizer-3.5.1-cp37-abi3-win_arm64.whl", hash = "sha256:87e4f41d375c0b9be2fb5251aee4b8a689169e134535aed81bf085c3b647451e", size = 287318, upload-time = "2026-08-15T08:20:15.551Z" }, + { url = "https://files.pythonhosted.org/packages/cc/61/d01fc49b8dea277640b55a9e15960dbca9fdc8c9fde18e572d39c59f4019/charset_normalizer-3.5.1-py3-none-any.whl", hash = "sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6", size = 68658, upload-time = "2026-08-15T08:20:43.306Z" }, +] + +[[package]] +name = "click" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" }, +] + +[[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.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf-py" }, + { name = "pyqwest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/b5/63e14ab9d4d4cc58818562db4120a35fa7454e3035633da41bdc6b712abd/connectrpc-0.11.1.tar.gz", hash = "sha256:18277f7838847b4271ca38d40c7d2387b5a2ea6a29f240689c19e1ec84aaff66", size = 46222, upload-time = "2026-07-15T06:33:31.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/ac/aa647675812cd075f2cb9acb00d62f4f2cbcbc6736049a62342b7371ce5b/connectrpc-0.11.1-py3-none-any.whl", hash = "sha256:8a52e2e92a485fa9681c1101a79a5ebeb31807e3ea3d5aabd41f484a6398bc7b", size = 64991, upload-time = "2026-07-15T06:33:29.396Z" }, +] + +[[package]] +name = "coverage" +version = "7.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/f5/deb1a27aa20746c0278ac998c4179e272004699b2d33959ce020c5ac1615/coverage-7.16.0.tar.gz", hash = "sha256:077f0964087883176ff6ab9b074694cae29f8c708273b13ca62c183c6ed716cd", size = 945620, upload-time = "2026-08-28T21:54:37.74Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/9c/8d2688694f53dc0b0f0e4783c7eb3c4bb1e79beaf1411879f6dabedf4607/coverage-7.16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d1c77c3579ac42798f8b7eed6d3dd258debacca32c8753fc8a1f6eaf1db644f5", size = 223194, upload-time = "2026-08-28T21:51:27.767Z" }, + { url = "https://files.pythonhosted.org/packages/ca/11/f002163dd688aa3fa49ac6a424b7c2705c7fcf80fba18ec9f586d77827ca/coverage-7.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1f81cb1554c3712e41649ed5dc98656b50b958e4da12f0f5adb681ce3db92831", size = 223553, upload-time = "2026-08-28T21:51:29.46Z" }, + { url = "https://files.pythonhosted.org/packages/81/65/f9d469e97c4554372a710650a109004a2434dfc56f577142e5d6057fa0cc/coverage-7.16.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e701938ec9081d3e400a0c9a9a8ae0f7ca44214741daeac4454b1c6ef6dbd19", size = 255054, upload-time = "2026-08-28T21:51:31.54Z" }, + { url = "https://files.pythonhosted.org/packages/95/29/dd89fd39af1a3b6e9a9c3eddeaf03f6376ba517d43d6cbf8b519177e2a10/coverage-7.16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:719a3feb6220dd32ed932d4c3676d17fb8739e2643b29c0e7c3af400ff80ac44", size = 257790, upload-time = "2026-08-28T21:51:33.374Z" }, + { url = "https://files.pythonhosted.org/packages/0a/64/208d26cedc525d6b5db9c492cf9130784c42d9eb08d22badaa7b806005ad/coverage-7.16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:87771ecf986cff55e87413238cd5e4f54d949c2074bd6fc1657d26a56314ee24", size = 258904, upload-time = "2026-08-28T21:51:35.096Z" }, + { url = "https://files.pythonhosted.org/packages/1f/98/28e2752aa9a8baee5798edade9c95602ca200f4e7eeb503eb64df42e5921/coverage-7.16.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:47d5e1fc0b321c8308a2aacee0497c435b08acaa629b7059798fdf6fc3006352", size = 261165, upload-time = "2026-08-28T21:51:36.744Z" }, + { url = "https://files.pythonhosted.org/packages/eb/77/fa6ae699a0ea2bc12acb38a85d96b786fea0f833c12b5756056350e0e547/coverage-7.16.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01b18b8a6c9cec8d5f45550e2501426ed982cf2c35016b0acd2ba9b5d8b2fb06", size = 255416, upload-time = "2026-08-28T21:51:38.495Z" }, + { url = "https://files.pythonhosted.org/packages/89/c8/5ee46d1de7d34cb00ba08b5c50da1971114dbc09ca9898ccc32975ec74dd/coverage-7.16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:32c56b5b47c50635081445ac404dd08c2d591b9c837c22570aa9e182c3b42cd4", size = 256825, upload-time = "2026-08-28T21:51:40.27Z" }, + { url = "https://files.pythonhosted.org/packages/15/f6/d59e1c0693ad48855fe20169fbf6ee5befefe5887a7fabf5f0bcb464a2dc/coverage-7.16.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6ad3bbad240ab937512156bc944fdee63ac4dd34a7558a3094548fd4c1150c02", size = 254970, upload-time = "2026-08-28T21:51:43.136Z" }, + { url = "https://files.pythonhosted.org/packages/df/7b/b51bbe05b3a7565927fccfb1be42b8b3c1f4ab15e53d91b303e9923969aa/coverage-7.16.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4c1f16d5555a195295d0dc9c902612270e3dfed6a11f3bf7bc470b7b6a79ed3c", size = 259039, upload-time = "2026-08-28T21:51:44.983Z" }, + { url = "https://files.pythonhosted.org/packages/fa/04/d513f816456a8a43c1859abe88a37d01d7d2515b6c3e24ebb3c9b1dd44ec/coverage-7.16.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:f6c9c21a8bf0d19788f3c5f3e020c90317a0a63ef60521b376003801e21250fb", size = 254539, upload-time = "2026-08-28T21:51:46.733Z" }, + { url = "https://files.pythonhosted.org/packages/dc/54/5542190ceb97e0d1333a4ce0c8f95b2ef2efe790f1ad018a4b61766f849e/coverage-7.16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f20145a9eb5bf1fd1dde3c0bc2af2e7c22135ab07ca6284d6ada7cc3904c4e", size = 256410, upload-time = "2026-08-28T21:51:48.363Z" }, + { url = "https://files.pythonhosted.org/packages/ee/28/78643f361ff6bb5b2ade90f8bfc8395fe9ca367a18c101f8991215b4c65b/coverage-7.16.0-cp312-cp312-win32.whl", hash = "sha256:916cf8d25c1ce148f7eceb1d45afc9724841200110adc4e53250391852debd91", size = 225239, upload-time = "2026-08-28T21:51:50.22Z" }, + { url = "https://files.pythonhosted.org/packages/67/61/8e76b36c36b1a033dc933dd2480db96b04ce3be975793ce3fad122e7174d/coverage-7.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:78f8b56261d608be102c62edd3a60b66bcd0b581f3f86fdcabaf8b8d95adc950", size = 225775, upload-time = "2026-08-28T21:51:51.912Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f3/bb4787a4b81c1792ca69b502f5f730dbbb609f73fed552ab074c6b92cb8b/coverage-7.16.0-cp312-cp312-win_arm64.whl", hash = "sha256:577c2ac8c0036f6f8edd3a7783a9e67302b17771d1abf0fd2ed246e3158be51b", size = 225159, upload-time = "2026-08-28T21:51:53.667Z" }, + { url = "https://files.pythonhosted.org/packages/54/c5/e62c87f4799d1e3647d5b2ae16ea1d12205d72fde1ea8529e13fe050f678/coverage-7.16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1545c52ce756b8a97007f439a220297f1cd72a2cbbcdffccdf1c1f70e74f9a42", size = 223215, upload-time = "2026-08-28T21:51:55.628Z" }, + { url = "https://files.pythonhosted.org/packages/89/e9/5e62fda9397175fb206f75368b6e85da06d831c181b6d0f67ca073cd2f89/coverage-7.16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0598aadae641f30a0796b75b45c0b9c5de8619bd5cfb251bb0cc254e86e6dd13", size = 223585, upload-time = "2026-08-28T21:51:57.355Z" }, + { url = "https://files.pythonhosted.org/packages/b9/40/bede08621b1ba67e88c4d3336c22b52cb7911ff1fa4ef055344b6670e58a/coverage-7.16.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4080ad6bad9f14690e6b2104f5e8d137ccc65a4b5427a36662090637d4bd16d5", size = 254575, upload-time = "2026-08-28T21:51:59.233Z" }, + { url = "https://files.pythonhosted.org/packages/12/d8/ab0bdaa45dfd6b8cbf1a3ec548fdf827684b1997f9724375c5b3e89144fb/coverage-7.16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e9883a2f8206ce3af59117dc278e5d043fea06912bca3f199816129e5e2de354", size = 257172, upload-time = "2026-08-28T21:52:01.015Z" }, + { url = "https://files.pythonhosted.org/packages/1d/bb/135de81784bbd7dfedcab2b92b03d71d75b09b0815b42d6dabb052def5a6/coverage-7.16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:984e5430fc6f858385009e92549955157d79335b1f3e13e1031e0f89d1284261", size = 258410, upload-time = "2026-08-28T21:52:02.76Z" }, + { url = "https://files.pythonhosted.org/packages/ad/72/ce44ecc062fb2e43d9447bb76154d091c2139232f20c125297c4b58f4c6a/coverage-7.16.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b1374099dd1ad0d31fbb6c95d00a56a3c5e85fb3343dca14fc12f78323a2b42a", size = 260539, upload-time = "2026-08-28T21:52:04.821Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/9389c36a41e59406ca2bba493807c2294d2e5186a7e9ebcc2e63a0f2a711/coverage-7.16.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34d8686bce035c8465b318a8c2890e69ba14a00801a27f4eb6bdc97c23944d87", size = 254756, upload-time = "2026-08-28T21:52:06.68Z" }, + { url = "https://files.pythonhosted.org/packages/ad/0f/7762447b15e01fb84263608540123c4d9941f06303265ee74d801ccbec0e/coverage-7.16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:857fceba6ff4b507ee0ad98798a33d544a8473df0c542bf04251ee4ed5ee6292", size = 256540, upload-time = "2026-08-28T21:52:08.529Z" }, + { url = "https://files.pythonhosted.org/packages/e6/fa/c60dc75a8346c1dbebebc7279b19971c88f70dd575f0bc10bc0cb16f92d5/coverage-7.16.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bbf08d951abaa1ce89e28c998361d56b952413846b459cd017f116ad4c9adbfa", size = 254508, upload-time = "2026-08-28T21:52:10.323Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f0/4e0834f3a1fccaa8bf625a2a1d73bde0fa32577dc3249853c0dd0e7f2b20/coverage-7.16.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1a03e78f53e4d2ab13adac19958a89322d1829913e5623d642627bf60b35da21", size = 258659, upload-time = "2026-08-28T21:52:12.124Z" }, + { url = "https://files.pythonhosted.org/packages/b4/ec/fe712d3a11fd6e874565a5fa5497c48b8ece561d9611da040b44cdcf8386/coverage-7.16.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:dcd3dafcdd78305d27c59a1006b53a4990acb89e68d8fbe0992f4f83503c827f", size = 254326, upload-time = "2026-08-28T21:52:14.181Z" }, + { url = "https://files.pythonhosted.org/packages/e7/78/093e12072e01034c65ff380f76c74b79dd83e44fa92b689a2154389be734/coverage-7.16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c1bcfe470a796fbea6234accd81d258a31574dc0b7bf569e16be757572c4de17", size = 256102, upload-time = "2026-08-28T21:52:16.003Z" }, + { url = "https://files.pythonhosted.org/packages/9b/c0/265176117ca5d06e3f65575842884cdda96cf213350a31e9d41c80d65854/coverage-7.16.0-cp313-cp313-win32.whl", hash = "sha256:1420370276f1694b663207b8245c3628aafb9624fe3cebf313a13d860e55ee67", size = 225250, upload-time = "2026-08-28T21:52:17.82Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/8a87f2c04fde322430b45d16d8f543693e9894c5b2d2ca238a287c00beca/coverage-7.16.0-cp313-cp313-win_amd64.whl", hash = "sha256:496277c8d7beed695e02c7be53516a0152e4caef8738a0feab6a638546cce449", size = 225790, upload-time = "2026-08-28T21:52:19.641Z" }, + { url = "https://files.pythonhosted.org/packages/23/40/c21feacd9edfe7063195bf9cc84d650e9938fc6a23063e4f027199b160e1/coverage-7.16.0-cp313-cp313-win_arm64.whl", hash = "sha256:181c2906b9b3759955c1c33c51fbb91c754fbd0b82ea49e2c81061f5a052082c", size = 225180, upload-time = "2026-08-28T21:52:21.613Z" }, + { url = "https://files.pythonhosted.org/packages/ea/73/850675f262391b322c4c988b6cdc32cdc6629288f0fb158687b587a393a8/coverage-7.16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:54b7fba6a74d010de34319a0419d5b65af8c00f539ad0b6f39fc6f342ab99697", size = 223258, upload-time = "2026-08-28T21:52:23.558Z" }, + { url = "https://files.pythonhosted.org/packages/61/c1/4f54c6d47c80d1cc58ef8fe6b74e6eb50f9e2c0f6e2de6cf38dbca2937b8/coverage-7.16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fa4ff0b3dd52208d2b30903022d5087f82000507b504753dfeee83e4f32d6883", size = 223587, upload-time = "2026-08-28T21:52:25.627Z" }, + { url = "https://files.pythonhosted.org/packages/3c/be/298f2456230fb44e272a4e53a41b3f3c39f0821c242d7b7daa9787b4d6f7/coverage-7.16.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:35a9676bf86097f790113ebd9fb67681804ef54d40941d2f10ba68c02239e575", size = 254632, upload-time = "2026-08-28T21:52:27.689Z" }, + { url = "https://files.pythonhosted.org/packages/a3/9c/a1bda6439c19c4783d50df896142b67b9e7d432db36675d339a32778669d/coverage-7.16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f98d438add63546745e5e847192e3e9ab897ed6f2ca96f8281e2f5a15958ae62", size = 257139, upload-time = "2026-08-28T21:52:29.741Z" }, + { url = "https://files.pythonhosted.org/packages/f8/cd/cd735c9be757f97237c305f36897a5e5b348bdbc12ebed3b2b80060dd8a9/coverage-7.16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:151855767480be14db595cbc2040f6a4db965cdfeebd354d79b0256742b029e0", size = 258484, upload-time = "2026-08-28T21:52:31.68Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/84b2e1e8aae9db3f549782f28ce25bba5fd6a9c7bfba3782ffe8b4cd2559/coverage-7.16.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:183613f664718b340589d7f005c7e92b4b601cffd20a8a4117cfda3e983b080f", size = 260798, upload-time = "2026-08-28T21:52:33.642Z" }, + { url = "https://files.pythonhosted.org/packages/8a/4f/e04cf52483619a4dc5dd6367b30c9a8ac52243567fdfacec9b11a441565c/coverage-7.16.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:785b114356c99c0dd5b3f57b9696cfd57b7704f4c53847df8dc88c6cc0d9bcb6", size = 254612, upload-time = "2026-08-28T21:52:35.543Z" }, + { url = "https://files.pythonhosted.org/packages/da/33/627c4113f66bfffd43807f54dbf080c4632ecf12e4ef7a3bdd4ec38e46a2/coverage-7.16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:30f5aee6d1d517abcdfd4f9cad027969ff79a1440a22da263f9514e31b5b66e9", size = 256495, upload-time = "2026-08-28T21:52:37.485Z" }, + { url = "https://files.pythonhosted.org/packages/3c/38/aaca432f4e008a88f2bc4d1459aa7016d8d1bbbe801f7e4fa3cf2746557b/coverage-7.16.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:190ffa0f5af966254c249fb3aeaca2cef389785e3e287fd577d39e134d20f8a3", size = 254454, upload-time = "2026-08-28T21:52:39.425Z" }, + { url = "https://files.pythonhosted.org/packages/cc/db/8430aa87ef0a508f4c17c1b8fa7e0cf80231988d9081aa36c194036592d6/coverage-7.16.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0ccc37c00e1a5d30840902c54557e104d04aead872cedf6d2281c8725a467e06", size = 258728, upload-time = "2026-08-28T21:52:41.32Z" }, + { url = "https://files.pythonhosted.org/packages/76/88/cd8aa8c82493ffbd291d3ef5554452fffc634c6c6098a04ac848c79c98f3/coverage-7.16.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:6c60cde430c0e7e3be612973af39b4cff90ec2e2defe7b2b701daea3a0ffff04", size = 254271, upload-time = "2026-08-28T21:52:43.278Z" }, + { url = "https://files.pythonhosted.org/packages/a8/49/fe16c811ea9314a84b48f34e4bf5a3d9013091093b285a74b2272fc863d7/coverage-7.16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c5297028c8df849a61b29129cadfe682f90b5b396f528eb319a57d7678eefdad", size = 255927, upload-time = "2026-08-28T21:52:45.461Z" }, + { url = "https://files.pythonhosted.org/packages/d1/45/d0bd410e78cfbf768acc8099b335e1d5c0d5c26103c796d2bebdee001715/coverage-7.16.0-cp314-cp314-win32.whl", hash = "sha256:136988df5bc5a48795d9c42c75c4bbda5d9a78e750a080c1233010edff93a1af", size = 225424, upload-time = "2026-08-28T21:52:47.658Z" }, + { url = "https://files.pythonhosted.org/packages/17/78/1ce6ce4646822e9308dcdb1942eaf31bfd7da43247b8886338b0d6fe3767/coverage-7.16.0-cp314-cp314-win_amd64.whl", hash = "sha256:ce2ba5e9f1842fe09165825abfb3bc6b527c71a27bc2eb3a10f2284ced64506d", size = 225918, upload-time = "2026-08-28T21:52:49.692Z" }, + { url = "https://files.pythonhosted.org/packages/f9/cd/e1323fe3a7dfcdd709451a43fe708ca1dfd36a7fc07b34eb7bd1dfdfb52d/coverage-7.16.0-cp314-cp314-win_arm64.whl", hash = "sha256:a89d07e48d9baead9a15599923a02f62c6df6c3d85aa84ef34be3c9fd6aeb91f", size = 225344, upload-time = "2026-08-28T21:52:51.665Z" }, + { url = "https://files.pythonhosted.org/packages/39/fb/1c15460d4cf915f09ae3ad3862fef4f901838991c5641b0cec545050d810/coverage-7.16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:6e2854b62601c89a63814ad5def3b90d99c6724cc4cb977f75b725e5fca4b1e3", size = 223986, upload-time = "2026-08-28T21:52:53.572Z" }, + { url = "https://files.pythonhosted.org/packages/9f/73/347d2d0009ac211f79ee2a2364fd2aa19d6b9628dc22ed13a9b9386097ab/coverage-7.16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f093faf23df888518d273be6da65f0ec5a25b5d8b670231e4d87de07361042e7", size = 224254, upload-time = "2026-08-28T21:52:55.59Z" }, + { url = "https://files.pythonhosted.org/packages/5a/2f/51442e6ad9d705369596f08496021647e276d5b57311818fd4312d93509b/coverage-7.16.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b7dbbbf6551eb94618e7bc76ab61cc2740a5b3d13294171bd6adb36e12346c3c", size = 265619, upload-time = "2026-08-28T21:52:57.645Z" }, + { url = "https://files.pythonhosted.org/packages/ea/8e/0f752276f6d13efbd019ab6d90792e20d6272c44cda039dc5c6d27b91e7f/coverage-7.16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:51e7d0e311d2fba3915f971236cbdd4ad821fc7a23988221c0b33c964b0eba22", size = 267734, upload-time = "2026-08-28T21:52:59.611Z" }, + { url = "https://files.pythonhosted.org/packages/fa/02/4df3baef8029881c9d1a380859f2be73f90080d430def567d182e8566a35/coverage-7.16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0bb04ee77e557d7476471969d35fbbfb5fc8a4152e9409aa5811780c36d9b23e", size = 270156, upload-time = "2026-08-28T21:53:01.658Z" }, + { url = "https://files.pythonhosted.org/packages/9f/30/ce10fdb74055ebbfb5c8a025d8845dc19c76e4b2c42bb5c755b56678990c/coverage-7.16.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c72c9b201dc0e8c2c8821d49858fd865010d08181bf877d2320971b6464ebfd5", size = 271279, upload-time = "2026-08-28T21:53:03.698Z" }, + { url = "https://files.pythonhosted.org/packages/71/19/c7e1fc9504d90da848493bad4018dd235c713a80633e48c5f0a41b63d45e/coverage-7.16.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0fca700cae4635656668ba6e2b66a85aac9f2622d7b2bcf82e844c409eaa1313", size = 264677, upload-time = "2026-08-28T21:53:05.741Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f3/4021519dd41583ab396c81955387f927779641f6bac26818b6918a45aafc/coverage-7.16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:584896fb8b650e999e24ef57e9513e482c12f8e15a73ee9d4584e23c99465867", size = 267610, upload-time = "2026-08-28T21:53:07.763Z" }, + { url = "https://files.pythonhosted.org/packages/55/fc/df65aac93938d8f506434c8e96440c1d696f6be0a6a01d3c6bfe5d49403e/coverage-7.16.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:949eae7e0f562b1518355aaef4b03523e49a6d3fea12aa3542d9e36c863f8267", size = 265217, upload-time = "2026-08-28T21:53:09.786Z" }, + { url = "https://files.pythonhosted.org/packages/32/2d/dc9a5e62715165fcb4c715f965f411e324917c9daeddde16536e9d36ce3f/coverage-7.16.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:64f0611ee05364fc85cc3e5bc371804117a76fd337720e6017332fc7c534257a", size = 268948, upload-time = "2026-08-28T21:53:11.866Z" }, + { url = "https://files.pythonhosted.org/packages/8b/4e/fe73a5560f25fca52acda76fc1554f30de081793ae4de97e920f8ab161d7/coverage-7.16.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:050a291b3cfe5e0df5999ef2fa5a7aff6e2db329f069d47eb63f02bde2e7e96b", size = 264061, upload-time = "2026-08-28T21:53:13.996Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f7/bb78cc4b97085ebbd77fa18cbc25abfab462814efa3e2363b4e50885c775/coverage-7.16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a336b1e2990a64f5c356a9b8380fb9c029d56c832b801255250c44d603271bfd", size = 266371, upload-time = "2026-08-28T21:53:16.233Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ec/84b4af5cd4ad498477b3bfb2217e47b048da919451053790efda66f7383c/coverage-7.16.0-cp314-cp314t-win32.whl", hash = "sha256:058631257350b31784ed43ceb808298b6f074edf4ebca4c7ce5082e6bf873a61", size = 225736, upload-time = "2026-08-28T21:53:18.632Z" }, + { url = "https://files.pythonhosted.org/packages/7e/43/50fc0e6c675c3ef14895a74bab2d6120cb5d6f4b562a3d3f5046797758dc/coverage-7.16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:ed35097438dfa980c1ec75bc83edf8acbe7a374d7007e571957a257fbd0e2fb3", size = 226570, upload-time = "2026-08-28T21:53:20.754Z" }, + { url = "https://files.pythonhosted.org/packages/fc/24/9effce7bcd3c6eeb4da3561905837509e582dcdde7a7f07d6ef2c8512f76/coverage-7.16.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0466f4a5c0370461b7d8c7eb259d7d1db0b5756f13d66230b04d22a1d380ee11", size = 225879, upload-time = "2026-08-28T21:53:22.747Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2c/318e4379106bc8047ba235e3732ddc87d1b393ac3db9776f5405ff14f322/coverage-7.16.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:80d7d5d744a041f08637df743ac086204ec5acbcd8432a42b00b49e607358024", size = 223257, upload-time = "2026-08-28T21:53:25.376Z" }, + { url = "https://files.pythonhosted.org/packages/81/4d/a5c54d9144e9db6505749758ba50a28be624148873751728a59cbb72d27a/coverage-7.16.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:c5feffce90c3d602e149de1c477578efc34dee5f069f9764cc15808ce01ee15c", size = 223596, upload-time = "2026-08-28T21:53:27.461Z" }, + { url = "https://files.pythonhosted.org/packages/bc/97/38e93a10899c9315964c0a4e729b3e5867f8f46e977808f9c6fbda52525a/coverage-7.16.0-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:acadbf2f2a18d7f9c7f119ac798c00c540d7c79c93abd71ed648c87891303633", size = 254699, upload-time = "2026-08-28T21:53:29.715Z" }, + { url = "https://files.pythonhosted.org/packages/fa/7a/acddda030b4630f68167f3daa94b41d22071847822a70d8178d43dcf678e/coverage-7.16.0-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4212cec9b42fd9929e70b462732fefd8b13406371871c82f3c14397499d6550b", size = 257614, upload-time = "2026-08-28T21:53:31.948Z" }, + { url = "https://files.pythonhosted.org/packages/15/7e/225b182497c1ce6d3f0d76a3074a4dbc9f272300e92bb100df53b03de0aa/coverage-7.16.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c5a43cc0ef101637ae920a9eed24cf0549ef815621eae68b3ad577ec5a7ad2f", size = 259236, upload-time = "2026-08-28T21:53:34.291Z" }, + { url = "https://files.pythonhosted.org/packages/2e/19/76641ddc50cb2410ebbd0ed7fe1052614d0e5612e802a2817521adb9febb/coverage-7.16.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c76a9b50a344261fe4a9bd20c322b48d3913cc48e8c37f78c21a596008296e68", size = 261433, upload-time = "2026-08-28T21:53:36.401Z" }, + { url = "https://files.pythonhosted.org/packages/12/9e/5f89de8b7c2017f36b68b4e4a25940723a748b21474820bf61e8bce0891c/coverage-7.16.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80cf547379ad6b1878fd03b033b51188beab4b41824c96e7839e014a4cb947be", size = 255182, upload-time = "2026-08-28T21:53:38.496Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c1/ce94b2ec502e79775efb5efa22c741ebb0bd2be10bdd29650825ff57bdcb/coverage-7.16.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:4b1d09cb5d8dc2c7164450f5217e6f0717497de9c588806a0780d352abef904a", size = 257329, upload-time = "2026-08-28T21:53:40.87Z" }, + { url = "https://files.pythonhosted.org/packages/86/8d/3f5374df3a6ca19ee5f98a6bd21dbb05f1e9d399bd9978e9821d260eab5e/coverage-7.16.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:cd1e85abed2d2499c16664137ac802356316f92b4e2bf3c150bdf0c45f5dd9ae", size = 255210, upload-time = "2026-08-28T21:53:43.393Z" }, + { url = "https://files.pythonhosted.org/packages/8e/b8/1bc5751496d0be6fd9dde8ca547d9a8a9f07847856aba3f3ae5ac594cd81/coverage-7.16.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:360967a6fd77794c167529eec2d16ff8e38216110619d23acc3fd466a1648bee", size = 259442, upload-time = "2026-08-28T21:53:45.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/dc/8aca78e47e1e6fcc761cd28a20daf4a84bd847a7369e2701a93ccfc3d1fd/coverage-7.16.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:92cbc2bf4f7f67c79f1d3ca4fe8c50faddf48e852a3d07eaaf02dc014889832f", size = 254618, upload-time = "2026-08-28T21:53:48.292Z" }, + { url = "https://files.pythonhosted.org/packages/73/fd/787842cdf6ce16ac5c1bd8a26549bab3b3f27b02500075bc540dc7853bca/coverage-7.16.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cce4dc8528453128c6fae523b15f3887fbea1d4d7c9eb9639d3d4fdcbe570c73", size = 256541, upload-time = "2026-08-28T21:53:50.805Z" }, + { url = "https://files.pythonhosted.org/packages/ef/79/8df302cbef373dd1f3401044cdb94dfc74517e5af2af27b4d0e721557e0e/coverage-7.16.0-cp315-cp315-win32.whl", hash = "sha256:5205baea687133613dced668a3d0168ea1479349615bfc255849a7944988c889", size = 225429, upload-time = "2026-08-28T21:53:53.177Z" }, + { url = "https://files.pythonhosted.org/packages/85/87/5bad7ac45f76b3728ca211028ee561c2ede3ba44da401129e28bb8737291/coverage-7.16.0-cp315-cp315-win_amd64.whl", hash = "sha256:4fcb5f07a9b7083bfb715115d27ce263ba2b5b89dddeee536b295ba0e3c2c627", size = 225903, upload-time = "2026-08-28T21:53:55.535Z" }, + { url = "https://files.pythonhosted.org/packages/cc/ea/67d84b11caf240f059ec313f616d82212df5004e8bc85802c1edfc50bb3d/coverage-7.16.0-cp315-cp315-win_arm64.whl", hash = "sha256:d568a8adcec0eda42ec23e5e65dfb8c184fc255120f9e99b484f7c869d923fb9", size = 225334, upload-time = "2026-08-28T21:53:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/65/21/a88349cce3ff720729b754916ac47e2e3646a8137552e4fa7cdd5967cc7f/coverage-7.16.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:3e8037e8213adf882e9d7eedd2c5c557933ab0b9632c42d98fe98ec9bcdb4025", size = 223980, upload-time = "2026-08-28T21:54:00.082Z" }, + { url = "https://files.pythonhosted.org/packages/fd/02/4d54abf3e6a4d8b7675921b20e91163b1064a5a9dbefebb71c05065dd136/coverage-7.16.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:289f2ed4d56eebf029b649e7dfc3c1153b111962a75e294cdd8e4a1598a04cc3", size = 224276, upload-time = "2026-08-28T21:54:02.381Z" }, + { url = "https://files.pythonhosted.org/packages/f6/39/10dbc96d95d20b9b041045d293480bd49e536180e93af62dd7662376284d/coverage-7.16.0-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9b83f6ac575530783771c8dcf05284f7c8b5b12f1e7cb226d63445aac4497a3a", size = 265135, upload-time = "2026-08-28T21:54:04.558Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3b/6b326544afd1a8aef3a495bbae109a7ab5baf23e04a2741d8d64e2df2ba2/coverage-7.16.0-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c3ff6580f2dfc5bec34717b85b2e6cf5ec993b721e7bb58a794babd525a8178", size = 268216, upload-time = "2026-08-28T21:54:06.97Z" }, + { url = "https://files.pythonhosted.org/packages/54/34/1dc8265f3ed990690e24d5f31ff79bc9fb9b25d54f9f89bebad5a6a8b7a1/coverage-7.16.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:507596cee23e9968b1934fe86d799b76166541af0a293930918b1b48a5c84bd2", size = 270772, upload-time = "2026-08-28T21:54:09.234Z" }, + { url = "https://files.pythonhosted.org/packages/66/a7/3a8463713a402b44044ec832f4a76e442ce4b3a207804303f4d1dc1a9bb4/coverage-7.16.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edc2be98e6c55ccc5ff7832bb64f023a4b03dba39dfa84b850046cf08a8249b0", size = 271752, upload-time = "2026-08-28T21:54:11.701Z" }, + { url = "https://files.pythonhosted.org/packages/25/3b/dd5e795cfbe1842f69899189089ae289a96d6a68de312960ea668542e33c/coverage-7.16.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9c0690994b84a15a53bdd39e0b2fdb539b22533820623eb86ba75b93760c645b", size = 265589, upload-time = "2026-08-28T21:54:14.12Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b6/fd90636cbd95cb018312f6ca1ca2bbd70fbe8e4ee6f3992fc36a4230364e/coverage-7.16.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:de24c62bf798940a14674a47489a81b79915ec4134f556d5199830e065225dd0", size = 268596, upload-time = "2026-08-28T21:54:16.303Z" }, + { url = "https://files.pythonhosted.org/packages/91/10/ef2d59264f3b3b358cc5885ca375e6cdbda7c195e78304d5aae800a72d9d/coverage-7.16.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:69474d81f198774c9d2937599ca5da04c9e1c5de5032da23c607ce4960ce360e", size = 265072, upload-time = "2026-08-28T21:54:18.597Z" }, + { url = "https://files.pythonhosted.org/packages/3f/5b/400891c364c0170408d172501b340b18611800f4c42d8fbb16f9f5497c24/coverage-7.16.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:72a0795cc6d34acc2b03dfeabdc82b61b72087f2737018b56ac92c1cf5446c54", size = 269768, upload-time = "2026-08-28T21:54:20.985Z" }, + { url = "https://files.pythonhosted.org/packages/98/93/9792c80271df04d287d21ed5d662fd8fa58b1737888d817679b1ce5d2fab/coverage-7.16.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:d9a218d3f9c7d6916684ed5ba94f620661117a730e733cd6ef5e87accc5872eb", size = 265211, upload-time = "2026-08-28T21:54:23.344Z" }, + { url = "https://files.pythonhosted.org/packages/81/67/5b8f827cfa6616e6bd7ba9397acfe7e3c4fd5b9fca4125511d5089f55d5a/coverage-7.16.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:49fa72ead28c8216f8916398a4f3c4669acb30a061822810ee20a727a1be2897", size = 267170, upload-time = "2026-08-28T21:54:25.85Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ee/c135d2d2cb617d744bc3e13c922f2fae66964494176ddef225dc4656bd2c/coverage-7.16.0-cp315-cp315t-win32.whl", hash = "sha256:27461af9f3ed7d2cf2411eb083784f87055ebf42211789ae3a216c48609bc743", size = 225731, upload-time = "2026-08-28T21:54:28.151Z" }, + { url = "https://files.pythonhosted.org/packages/8a/4d/dc3d53eadf155916e183bf5dfacbfc4aa5bfb7f13b7da11c01caa7a05cbc/coverage-7.16.0-cp315-cp315t-win_amd64.whl", hash = "sha256:c5612cc20ca76abc883e50269af47c1494b42958bb63dbb9aa79729a1ab5f7d3", size = 226562, upload-time = "2026-08-28T21:54:30.42Z" }, + { url = "https://files.pythonhosted.org/packages/2f/00/ac9da1a60a4e84c3ad0f7db4723fd327154a8f9add210c0dcd2db3ec5156/coverage-7.16.0-cp315-cp315t-win_arm64.whl", hash = "sha256:2ddaa9e2af4760a329d80008b7a3b4762fbb0dbcb169199360f9a5179c32f2dc", size = 225872, upload-time = "2026-08-28T21:54:32.806Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5a/234e8fadf85c3cc48cb31c247b9e8e0c7f06ece80f5b29f9b8c241f9da4c/coverage-7.16.0-py3-none-any.whl", hash = "sha256:245f7de6d023a5bba375dbec9f2e0869bfa26ac0cc639bbb7b4c814884000b73", size = 214977, upload-time = "2026-08-28T21:54:35.189Z" }, +] + +[[package]] +name = "cryptography" +version = "50.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/ad/5d6702db60b1e40b41ef513b6967ff5848f307d50f8449baf1634f5908f1/cryptography-50.0.1.tar.gz", hash = "sha256:5dd9bda1c12b4162f6ff568eeb5e0ff956c28d14406e875cfe8a63a2d414ff20", size = 880381, upload-time = "2026-08-25T19:45:45.499Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/19/797e2aaac9df6a66f1550f49979dc1b1e39ecd2077501c30efa81e8d5d67/cryptography-50.0.1-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:b8f852c65863251b9e3a1b8c150ce21e59b522dbb6a7d4bc80e680d38388e986", size = 4010153, upload-time = "2026-08-25T19:44:03.155Z" }, + { url = "https://files.pythonhosted.org/packages/90/34/9ce9a62ed9dc82ca9fd6a34445b6904af56e5f38b3eae2ed32e49c36053d/cryptography-50.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:53e279950892dc102c6b4e52af03ae5ea92fac572a1ddab78ca73a997f62b69f", size = 4723133, upload-time = "2026-08-25T19:44:05.461Z" }, + { url = "https://files.pythonhosted.org/packages/57/26/e6d4fc8512a51a5f9ee7bfdbfb853bce1197087df40c9ad993ad370b846f/cryptography-50.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef", size = 4712478, upload-time = "2026-08-25T19:44:07.375Z" }, + { url = "https://files.pythonhosted.org/packages/e6/de/d3cdc2815697aae84126cbd6a030ca7b6b452e28a88b501b836bd3aa7a86/cryptography-50.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e74591e283fe6eb956416c929eb58262a719fe0311fd9054c62c3350ed8760d8", size = 4730726, upload-time = "2026-08-25T19:44:09.294Z" }, + { url = "https://files.pythonhosted.org/packages/55/32/38c0d344b98c06d34b5df8946565a9c0d6dbf32c8e0730a7f05f0a3c6cab/cryptography-50.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5fe002589592ed749ce77fe0695fcbd3500dd61d7d6db5858a7544c612fa8e45", size = 5353524, upload-time = "2026-08-25T19:44:11.96Z" }, + { url = "https://files.pythonhosted.org/packages/e1/1b/82f0f0d8858d4432be1af790477edf62aef90324041aa07c57e57bef1af7/cryptography-50.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:51593d180cf6d179bde5c5d065bed81386b1f381656ae7d042b7ffc87a9895ad", size = 4746720, upload-time = "2026-08-25T19:44:14.051Z" }, + { url = "https://files.pythonhosted.org/packages/29/ba/042ca458b8c64348c768284b5d23e69b92ed53d057ab779fee628564676d/cryptography-50.0.1-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:359e62deae718bce96170e223fdcb6357e4fbd3bb7a3a75f4430763532560e49", size = 4361866, upload-time = "2026-08-25T19:44:16.167Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/e96c1ef71edef71057c7e3c3d982ce8fda554e0c52d0cc19c18845cde3eb/cryptography-50.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e2ca8fd1b6b4b82a1c4cb02841d0837e3c12336c2e24b520ab8ab3b969733d8f", size = 4730028, upload-time = "2026-08-25T19:44:18.085Z" }, + { url = "https://files.pythonhosted.org/packages/e3/38/45abd72ef63f2e7d0754a6cacf97bd8b69512ace7f6130d24c39ece65da2/cryptography-50.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:76de83fbd91ac49c0feaaa983d0748fd7a53176afac5fb3bf7478d244f0eb527", size = 5308405, upload-time = "2026-08-25T19:44:20.197Z" }, + { url = "https://files.pythonhosted.org/packages/85/66/6ccca4722987ddedaa7fc9c3f4708af7431f5535666c174350830888c6b7/cryptography-50.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:51afcfceb15597cf2635068e4ac9a56b2abde622edde17f37d85fd7b5306497a", size = 4746230, upload-time = "2026-08-25T19:44:22.376Z" }, + { url = "https://files.pythonhosted.org/packages/13/0e/b1f92e013228111413f2e6743948b80bc24dfd3c1b87ba98ceea16f5df89/cryptography-50.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:be224a65493ec5b74a158ff22a5522ce4a5ca1e543c647a3a4730d4a09e5f959", size = 4862596, upload-time = "2026-08-25T19:44:24.472Z" }, + { url = "https://files.pythonhosted.org/packages/7e/22/c3654cccc856e9d682817b04ac3ee79731cb09ca6f95996a95c904de2883/cryptography-50.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9ebcdd5519be9b652a46f507817a74591774fc3d6923ac364e4dfa64e36b291b", size = 5014082, upload-time = "2026-08-25T19:44:26.709Z" }, + { url = "https://files.pythonhosted.org/packages/42/8b/cb12b1b60c91b074ca6bf0fdd59aa8f10d8bc5f73af8faece86ef0421b37/cryptography-50.0.1-cp311-abi3-win_amd64.whl", hash = "sha256:aed8db4f6d71c51efb89530e12d9464e7bf2923d46c3205dc794a2a93f8c0648", size = 3842826, upload-time = "2026-08-25T19:44:28.784Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f0/424cb557d99aa86ac55da5e2add02e2882e44047b6264f93ade1b975a993/cryptography-50.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:30a125032e5642a21ff816e021152bd4e7e94f03eff3f4b7fca41cd22bc3110f", size = 3973525, upload-time = "2026-08-25T19:44:30.7Z" }, + { url = "https://files.pythonhosted.org/packages/4d/72/3a2711d967977ab5fc80b782837c7e8d1ac7445e764c20c381a265c57ef3/cryptography-50.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a0b1a59e3a089064a0ec309e9428c8e3ae4e161419d20ac33600767e83fc658a", size = 4708817, upload-time = "2026-08-25T19:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f2/bb1f56e10815b789df0b409a69fa4992ff3d3fef9c72747f4a6b26fed38e/cryptography-50.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8921d58f426793c5f1b47f0b59575780de9a095214958d0eb37d909593db8367", size = 4697300, upload-time = "2026-08-25T19:44:35.144Z" }, + { url = "https://files.pythonhosted.org/packages/08/bd/ed5396be499ffcf8807a585bfe38b71a1fbdd1c342b4f9b6d0ef5162a946/cryptography-50.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a8f40ea47330e71b594a7e246898f93177c259490c63183dbaf9e571d71ed9a5", size = 4716039, upload-time = "2026-08-25T19:44:37.192Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6e/1cf405c5c8e8df7545378048e954792f00b7f2367af8863ce8b8f3e10607/cryptography-50.0.1-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:a255449073358275b64b67d3f595f268bbef70e72b6edb65e0c70c735bf739c9", size = 5332388, upload-time = "2026-08-25T19:44:39.16Z" }, + { url = "https://files.pythonhosted.org/packages/47/92/b4317e8c32c4f47b062f5398bd79106b220a124546f42be83bf32b761e2a/cryptography-50.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:8df2de9102026855887e4587084f6eabd80ed0f345b8ad8a7ac27ab9bf4723e0", size = 4730293, upload-time = "2026-08-25T19:44:41.298Z" }, + { url = "https://files.pythonhosted.org/packages/39/0d/a1e7633e2c744d0f2983320a27e924ef2264c79c56e1a58d5fb0a1cfd413/cryptography-50.0.1-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:ac02b07824d4d1001bd4367599f839c19cb171924c796e52c23508ac14c2c0cc", size = 4346031, upload-time = "2026-08-25T19:44:43.245Z" }, + { url = "https://files.pythonhosted.org/packages/88/dd/b215616f9bab3fc18510c78a4e5c9f362d77838503c363dc747c7d4f5c6f/cryptography-50.0.1-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:cbf74a81765ee67413503ca6e26dcc4f6f5a519822436cc0a1b97aab6c1b8a17", size = 4715344, upload-time = "2026-08-25T19:44:45.291Z" }, + { url = "https://files.pythonhosted.org/packages/b1/1b/ec3ebd31741d0e963612c4fe43caa39341b9b1e031e469820e42e4c83918/cryptography-50.0.1-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:16c5ecd954b3330ebfb6605eca4fd952da8bef376551d5cc264534e3770a9ee6", size = 5287201, upload-time = "2026-08-25T19:44:47.297Z" }, + { url = "https://files.pythonhosted.org/packages/1a/01/0127d11a762b31a9ee0221894f540318761783f3fdc4bc5d057698caebd5/cryptography-50.0.1-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:79bf008d1f9af6071c797ad133e39915dfee7614f18f18f4db9072eb715064a3", size = 4730023, upload-time = "2026-08-25T19:44:49.435Z" }, + { url = "https://files.pythonhosted.org/packages/9e/b9/e7425ebfb599241a0c1d7000f1b466c3062da66c19d9525031315dff7213/cryptography-50.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:330fbb252391c596f1ae42c5754449dc924e6ad012dca8efe0d703f9f2d12ec6", size = 4847362, upload-time = "2026-08-25T19:44:51.94Z" }, + { url = "https://files.pythonhosted.org/packages/2d/fd/60d0ddf4defa12e482c9d5e0f554384d6e8ab25341fd15f060028fd92e6a/cryptography-50.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:42be3bb70596b3abe4ac097b75be223e8b3ab614a0e5de068e3dcc54d71d6149", size = 4999247, upload-time = "2026-08-25T19:44:53.876Z" }, + { url = "https://files.pythonhosted.org/packages/4d/56/bc4f2b209e766c93372cfcd59b781a0b2b59700f62a969580415b699c2b2/cryptography-50.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:f74455bb086a85d5e81246412602aaa97ed095e504cd40dd261ef50be42205bf", size = 3825806, upload-time = "2026-08-25T19:44:56.209Z" }, + { url = "https://files.pythonhosted.org/packages/84/a9/ee16a903f13755e914d1eecc482fe64d1f10761c3960e5d8fa6837377aff/cryptography-50.0.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ca83d00d9e69cd5eb63f2e69c3a5a59e0cecae5ae14c6ae0b35830fe3b37bad0", size = 4035307, upload-time = "2026-08-25T19:44:58.305Z" }, + { url = "https://files.pythonhosted.org/packages/5e/a5/9ec7e81e8526c0d7a387d73386b2daed3f39e10d81a85930bd1b6bfba65c/cryptography-50.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:05ba322c4da95b262a212c345af888ef2c37c88c0509756ea00a0e6d68850f23", size = 4751900, upload-time = "2026-08-25T19:45:00.401Z" }, + { url = "https://files.pythonhosted.org/packages/7e/3c/0e77bd5ffcf078e9dd27d3074aad6c030d9b10d0bf69329d573c927a188c/cryptography-50.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e22dfed744bd4002e909464cb23d2f0b05c6f3113a79ef2e9864a53db737c733", size = 4738357, upload-time = "2026-08-25T19:45:02.786Z" }, + { url = "https://files.pythonhosted.org/packages/27/3a/3c5f80daa4dcd47323c7af8a2fcb90de27a33564d4fcac69846c0972691a/cryptography-50.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4c4188f7c0cf655be5c06342b817ed0f9595b69ffa2b12026e5353eed29dea88", size = 4758474, upload-time = "2026-08-25T19:45:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/6e/2b/214cf0cf93db9628c3c20c896b229f327f6fb1b20e4b3743d8ad3f00af8b/cryptography-50.0.1-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2ebbfb0f1fed745e91796e3e1080a1440423fdae8ece1b995a1d80883a409054", size = 5375862, upload-time = "2026-08-25T19:45:07.163Z" }, + { url = "https://files.pythonhosted.org/packages/d6/51/3f9701867a46b6c1740c9b52fc4d3bed6cbdcfedcc9b6e64305c07f39cff/cryptography-50.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:407fe2b6db00939c05c0e945e9914238f2f0a430974839429dafc82b1ee6bee5", size = 4772942, upload-time = "2026-08-25T19:45:09.396Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5c/13ea642e08e2544d0f5396122055f4820cfacb3203562197b5967125ea97/cryptography-50.0.1-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:2b34d76a652ea2b6faf777c35df230c5637842cd904e04f16230c3f9f03e4361", size = 4383347, upload-time = "2026-08-25T19:45:11.659Z" }, + { url = "https://files.pythonhosted.org/packages/84/d5/7d1fe1cb93f91c428093ff234e128c89ba8ea61a6f26aab406081f9b996e/cryptography-50.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:01f41478cf33fc605a6a089cd56d28b45c6c0b45a1928b61797f2621a04bac71", size = 4758050, upload-time = "2026-08-25T19:45:13.745Z" }, + { url = "https://files.pythonhosted.org/packages/dd/04/557fc5ead96a829e0bc812a3b9dc4a52a2f27e4f7f5950da7ff27653a805/cryptography-50.0.1-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:fc3ed7ebd2a8c96f5b166de0ab9b624996bef3b07bbeb19364dfb78222c22c80", size = 5332955, upload-time = "2026-08-25T19:45:16.193Z" }, + { url = "https://files.pythonhosted.org/packages/8c/eb/5d7124083e8d8cda8f5b348f544b71ad6f707ad63193758ef4d8e569da02/cryptography-50.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9dde0a357190eb3b1da1bb9ab750e9c85cba82ca5977aa0836cbb94e92611239", size = 4772694, upload-time = "2026-08-25T19:45:18.315Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/f1f955e0921dd2b6d22eae7e8d24a4c4b638d10735ffbf6a71f99eb0fcb8/cryptography-50.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd3718b960d0b5dd213cdf03f3bcb7000e69dda0de8b956061947ff6bcff5558", size = 4888413, upload-time = "2026-08-25T19:45:20.4Z" }, + { url = "https://files.pythonhosted.org/packages/1f/ab/89e2b798d2c3925f82e2bb72d5979f3d2f6da2dd22ef4a8cd8b70d920039/cryptography-50.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2a93d05e34d5f67fba6f891fe85d929999baa7195e853923ea6d7576c9e68c5e", size = 5044355, upload-time = "2026-08-25T19:45:22.353Z" }, + { url = "https://files.pythonhosted.org/packages/99/89/87ef49ffe383ef4e147d27b7bf2088fb0b54ea409dd87b5a89442e5828a5/cryptography-50.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:55d16b1ef3ee0958d893a977b19777887e546c9954ea81b200c3301a864013f2", size = 3875429, upload-time = "2026-08-25T19:45:24.418Z" }, +] + +[[package]] +name = "cyclopts" +version = "4.25.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "docstring-parser" }, + { name = "rich" }, + { name = "rich-rst" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/8f/6caec9cf291ba294a0d0669b0388e5676d8887d8b75a8c3ad0a2dc2eeed9/cyclopts-4.25.2.tar.gz", hash = "sha256:0776bc1fa796cd351646c345b7420279e58d6c2c4a8f5d5dd54dea85bdb2de8f", size = 202467, upload-time = "2026-09-08T16:58:04.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/0e/eadf712ca776fd96982c09348dd8f141ae6f96746a4eabc3e3fb54dea738/cyclopts-4.25.2-py3-none-any.whl", hash = "sha256:51b42513eea5e4ba6a08b68acf187b81f1191032cf77b9631086cfa2c88fa7a4", size = 242400, upload-time = "2026-09-08T16:58:02.696Z" }, +] + +[[package]] +name = "datasets" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/5b/836516269d4f618efe621661cfb6f9acc57e6f95265db3efaee48a5ffe04/datasets-5.0.1.tar.gz", hash = "sha256:ce22bb851efd7494f08aad33b940803784434f6e77763d00679a0dc45fcf686a", size = 641498, upload-time = "2026-07-28T11:09:12.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/0b/98fc6eb83333508ca5f44c52b3e287ea8137a0ad582714e2cbc67a02154b/datasets-5.0.1-py3-none-any.whl", hash = "sha256:9fbf73688f8c18f7529b4fe592abd04015f81d1e58001e4bac73ffb2b39d7cc4", size = 559079, upload-time = "2026-07-28T11:09:10.266Z" }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + +[[package]] +name = "dnspython" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, +] + +[[package]] +name = "dockerfile-parse" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/df/929ee0b5d2c8bd8d713c45e71b94ab57c7e11e322130724d54f469b2cd48/dockerfile-parse-2.0.1.tar.gz", hash = "sha256:3184ccdc513221983e503ac00e1aa504a2aa8f84e5de673c46b0b6eee99ec7bc", size = 24556, upload-time = "2023-07-18T13:36:07.897Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/6c/79cd5bc1b880d8c1a9a5550aa8dacd57353fa3bb2457227e1fb47383eb49/dockerfile_parse-2.0.1-py2.py3-none-any.whl", hash = "sha256:bdffd126d2eb26acf1066acb54cb2e336682e1d72b974a40894fac76a4df17f6", size = 14845, upload-time = "2023-07-18T13:36:06.052Z" }, +] + +[[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 = "e2b" +version = "2.49.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "connectrpc" }, + { name = "dockerfile-parse" }, + { name = "h2" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "protobuf-py" }, + { name = "pyqwest" }, + { name = "python-dateutil" }, + { name = "rich" }, + { name = "typing-extensions" }, + { name = "wcmatch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/14/c1/fb9bf7726dafdc5e8be439218d4c8819e251baca3591ef5ed4ed4c1e883f/e2b-2.49.1.tar.gz", hash = "sha256:300810e78cdd7d9a0b96b0c66e26ed53ed643bc3227f8dd7e9725e04f271dff3", size = 228685, upload-time = "2026-09-10T17:37:25.249Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/51/ea23a1d61460199b4c250d0a6beffc2fb750aab1fbf9301a3db01a6b7fe1/e2b-2.49.1-py3-none-any.whl", hash = "sha256:2ea7c10927ab6e75aae54badef41b49ab0ef0db03b747bf09db2a86f7906ffe6", size = 389026, upload-time = "2026-09-10T17:37:23.907Z" }, +] + +[[package]] +name = "email-validator" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "fastapi" +version = "0.141.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/02/91e3416a8fdd715abb903a952a6bec7cdd8d14eed55d415fc8595524c319/fastapi-0.141.1.tar.gz", hash = "sha256:e8822fc40db1e1858054d7a949a888695bc9bdce70139178e33bd2871a453ca1", size = 425799, upload-time = "2026-07-29T17:18:05.568Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/03/10388a42375ee7e4ac9b94eb2c5c569c8b5795e377e701c9ac3ad63de890/fastapi-0.141.1-py3-none-any.whl", hash = "sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3", size = 131954, upload-time = "2026-07-29T17:18:04.364Z" }, +] + +[[package]] +name = "fastmcp" +version = "4.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastmcp-slim", extra = ["client", "server"] }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5d/1a/bee10aff530338f3b8982a3dd1c377c3ba5d0bc724615bf358422c4e9ecf/fastmcp-4.0.3.tar.gz", hash = "sha256:0dd50baf070105ee4436c245f087ac3c047e655c32d9e783de16d0bf15783a98", size = 42311418, upload-time = "2026-09-05T00:31:36.932Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/cb/66600db497b3be21cf141f01296308169859d48ed9b1bc8ba7c9d83279b7/fastmcp-4.0.3-py3-none-any.whl", hash = "sha256:f743f43193e1daf606e6497a9dddba5bbaad7df7957a98fd093716ecf4458b99", size = 8076, upload-time = "2026-09-05T00:31:33.957Z" }, +] + +[[package]] +name = "fastmcp-slim" +version = "4.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mcp-types" }, + { name = "platformdirs" }, + { name = "pydantic", extra = ["email"] }, + { name = "pydantic-settings" }, + { name = "python-dotenv" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b0/a9/b7b796b0d4a5e095dadef233833c82ac1136f5c110dd9947b2e9a4988518/fastmcp_slim-4.0.3.tar.gz", hash = "sha256:3ce04a82b82cc41ccb12bb3bb366cfd65b52bc797b8fee332a75da9d480f9b74", size = 685704, upload-time = "2026-09-05T00:31:12.346Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/4b/6b31820d87f56d5773538860878c8634b618cd1e9044b3bfbd8a78380a0f/fastmcp_slim-4.0.3-py3-none-any.whl", hash = "sha256:3756ed4bd9f82f40adaf51974b7cdd1463ab6b9f479cf69b69b2692969312b87", size = 859717, upload-time = "2026-09-05T00:31:10.93Z" }, +] + +[package.optional-dependencies] +client = [ + { name = "authlib" }, + { name = "exceptiongroup" }, + { name = "httpx2" }, + { name = "mcp" }, + { name = "opentelemetry-api" }, + { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, + { name = "starlette" }, +] +server = [ + { name = "authlib" }, + { name = "cyclopts" }, + { name = "exceptiongroup" }, + { name = "griffelib" }, + { name = "httpx2" }, + { name = "joserfc" }, + { name = "jsonref" }, + { name = "jsonschema-path" }, + { name = "mcp" }, + { name = "openapi-pydantic" }, + { name = "opentelemetry-api" }, + { name = "packaging" }, + { name = "py-key-value-aio", extra = ["filetree", "keyring", "memory"] }, + { name = "pyperclip" }, + { name = "python-multipart" }, + { name = "pyyaml" }, + { name = "starlette" }, + { name = "uncalled-for" }, + { name = "uvicorn" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "filelock" +version = "3.32.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/46/126b1831dca12060d4a8296bf9c4fe5c93c4f22197fa239cb0cc82042bba/filelock-3.32.6.tar.gz", hash = "sha256:a3f55a18af3652a94d8f47d6055df434f254ca1d02ef2524850c6d249ca2512c", size = 225172, upload-time = "2026-09-08T22:57:11.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/06/4f138f618dbea66803291274f228f01daf29f306fe8b96bc30dab765df75/filelock-3.32.6-py3-none-any.whl", hash = "sha256:3f16ecd0117feae0dfc147e8c62eb5daeccd8bd800378c3ddf416de9b4feb6b1", size = 100189, upload-time = "2026-09-08T22:57:10.182Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/a1/ae4e3e5003468d6391d2c77b6fa1cd73bd5d13511d81c642d7b28ac90ed4/fsspec-2026.6.0.tar.gz", hash = "sha256:f5bac145310fe30e16e1471bd6840b2d990d609e872251d7e674241822abf01a", size = 313646, upload-time = "2026-06-16T01:57:28.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/22/4222d7ddf3da30f363edaa98e329c2bce6c65497c9cb2810931c8b2c0fbc/fsspec-2026.6.0-py3-none-any.whl", hash = "sha256:02e0b71817df9b2169dc30a16832045764def1191b43dcff5bb85bdee212d2a1", size = 203949, upload-time = "2026-06-16T01:57:26.358Z" }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[package]] +name = "gradio" +version = "6.27.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "audioop-lts", marker = "python_full_version >= '3.13'" }, + { name = "brotli" }, + { name = "fastapi" }, + { name = "gradio-client" }, + { name = "groovy" }, + { name = "hf-gradio" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "numpy" }, + { name = "orjson" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "pydantic" }, + { name = "pydub" }, + { name = "python-multipart" }, + { name = "pytz" }, + { name = "pyyaml" }, + { name = "safehttpx" }, + { name = "semantic-version" }, + { name = "starlette" }, + { name = "tomlkit" }, + { name = "typer" }, + { name = "typing-extensions" }, + { name = "uvicorn" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/1b/789b86449bf9fee5e4c97e3550c92f97586a9fc885882994c3ff5d2d8a91/gradio-6.27.0.tar.gz", hash = "sha256:b7212b021c2a7a2f6f1e31341783ba7ab2679bbd286e2956f7e6c701348daf92", size = 45025357, upload-time = "2026-09-11T03:02:02.703Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/4c/901183a7040fa02d2943ae75f16613424894bb5a5d8e12d427c8d78467df/gradio-6.27.0-py3-none-any.whl", hash = "sha256:6f4b9057c4a771283caa35a80dfdb3f599b5732108591fe9dcc32689cda17b68", size = 31334963, upload-time = "2026-09-11T03:01:58.869Z" }, +] + +[[package]] +name = "gradio-client" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fsspec" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "packaging" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/ac/4c3afc631a98ec05e06f7b572cc347ac4affc2880f5a1616f53dcf92c16c/gradio_client-2.7.0.tar.gz", hash = "sha256:212c88f7f6a212c973b4429ec450b35efba7805a34aee53f0892535ceee194ae", size = 62140, upload-time = "2026-09-11T03:02:12.131Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/5c/c99c923dc00a0fa74893bbb0c730b5a64c0e5c510f206b43e8628d0d60a4/gradio_client-2.7.0-py3-none-any.whl", hash = "sha256:1eccee1d2b387dc63b9b59124dd07e39cbe0ca7c90cc52ac4525074570b6365e", size = 63024, upload-time = "2026-09-11T03:02:10.776Z" }, +] + +[[package]] +name = "griffelib" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/af/018c10bc9edd42b6ef6db2e96b09542050d5253f9b195e74bc910b2d13ab/griffelib-2.3.0.tar.gz", hash = "sha256:7b0952caf5bca6afa4bb5ee8c6a2d183fe3f21b62efc5f6c7243cb2b26d2d115", size = 234534, upload-time = "2026-09-04T15:08:17.472Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/63/e876e789525063c840ccfa8857febdabd6523bcef9ce7eb979b9305ea895/griffelib-2.3.0-py3-none-any.whl", hash = "sha256:1b8f9cd525681c26b1d6d574faa1371651e8459ca51d209684f50b8096ae06e0", size = 169423, upload-time = "2026-09-04T15:08:12.956Z" }, +] + +[[package]] +name = "groovy" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/36/bbdede67400277bef33d3ec0e6a31750da972c469f75966b4930c753218f/groovy-0.1.2.tar.gz", hash = "sha256:25c1dc09b3f9d7e292458aa762c6beb96ea037071bf5e917fc81fb78d2231083", size = 17325, upload-time = "2025-02-28T20:24:56.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/27/3d6dcadc8a3214d8522c1e7f6a19554e33659be44546d44a2f7572ac7d2a/groovy-0.1.2-py3-none-any.whl", hash = "sha256:7f7975bab18c729a257a8b1ae9dcd70b7cafb1720481beae47719af57c35fa64", size = 14090, upload-time = "2025-02-28T20:24:55.152Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "h2" +version = "4.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/85/7c366e69d84c17bb778fe41419e1fbcce3033d5b7ce29bbffff0a98b859f/h2-4.4.1.tar.gz", hash = "sha256:4e866ffb1a869ae14dd9b5e6beb5c24a13da0495ad72b65925ded182521c1516", size = 2157281, upload-time = "2026-08-03T11:45:09.509Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/22/e85faf23bd72a92d1921e37d674ca56eb298a3c8be31fdecef0ff2b3aaac/h2-4.4.1-py3-none-any.whl", hash = "sha256:0e25f1462b23c9cb82d9eb02e28bc706dac2a68cb457c6a0d74d63c8a2a5d0e6", size = 62636, upload-time = "2026-08-03T11:44:59.164Z" }, +] + +[[package]] +name = "hf-gradio" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gradio-client" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/86/c9694b7cfada5780e75769e60dc161a161f4dd7fc91b61db5e3a3338bef9/hf_gradio-0.4.1.tar.gz", hash = "sha256:a017d942618f0d495a58ee4563047fa04bef614c00e0cb789a9a6d0633cffa7b", size = 6560, upload-time = "2026-04-22T14:01:32.334Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/2d/afff2ee87e75d8eb85c92bb8cf0e15b05c23c2ebd8fd8dec781d8601ed7f/hf_gradio-0.4.1-py3-none-any.whl", hash = "sha256:76b8cb8be6abe62d74c1ad2d35b42f0629db89aa9e1a8d033cecfe7c856eeab3", size = 4482, upload-time = "2026-04-17T19:53:31.827Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/ab/522a2ab67f27971a9d48ca666d4fca85ef7d5282d142e31fd087e27b1bbe/hf_xet-1.6.0.tar.gz", hash = "sha256:2e58454a340b3556dfa4972d5451aff4fba8dd42a236600ba1a1d2b1514f0fef", size = 920527, upload-time = "2026-08-03T22:33:13.243Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/62/3c062f593bd92ef4e77a0ef39541e3d82a0a1d3947c8a777a02a13a27828/hf_xet-1.6.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:70cbb9c896901600128cb9b6f06e132954fbede1db30f31f7c6c63f84cb7c31d", size = 4074584, upload-time = "2026-08-03T22:32:47.364Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1e/c0ad437dd267a8e435bef594acf781bbc3874ff0b6435b4962d03ecf7cc4/hf_xet-1.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:23379c2f9ec8696d952b16414a2bae72cad86a52df869b050698ba60f538c675", size = 3867381, upload-time = "2026-08-03T22:32:49.049Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ee/7c0d7b6ab336167531b1c30af2af003f054af4c749becbd7209ae33a77c3/hf_xet-1.6.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2f7278c05c22fd60cb436cda1269649b3e81db65ecdc8496e5e164aa4143e7b", size = 4453982, upload-time = "2026-08-03T22:32:50.568Z" }, + { url = "https://files.pythonhosted.org/packages/63/06/ad8eab1c9525246650cbaa821caa3cdbaca734ab1a5b8c91bea09cbd8d69/hf_xet-1.6.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:948f15d3a9545cfe5932f6bd8b440f6ae630aee108f14b7bd6c561f7c2dcc522", size = 4249445, upload-time = "2026-08-03T22:32:52.391Z" }, + { url = "https://files.pythonhosted.org/packages/d8/26/1eee8aedb0dafc1ab9717dc9ac602cde33361b232dc06803f1f6ed18b58c/hf_xet-1.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5153e6bb103ad49d6ea9f1b2e230db5a2ea32551ad09a706d2f61d7c7c80d80e", size = 4451099, upload-time = "2026-08-03T22:32:54.114Z" }, + { url = "https://files.pythonhosted.org/packages/67/57/0b88af1f194ab6c9c650547d9cc06bfeaab836ae4dcdb331676bfb8be95a/hf_xet-1.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:35cec30d75c6f9eb9c16a77cef68e85a103b72e24d4b473714ec9ff06428bab9", size = 4664712, upload-time = "2026-08-03T22:32:55.547Z" }, + { url = "https://files.pythonhosted.org/packages/53/a0/26b717a9d1840e8abf48dcec64b5ed8fbe472671d38ad28d30e147132b33/hf_xet-1.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5789835d7c6bc9436962853192082374297fb72d7eff7e7762ec25ceb7e25338", size = 4025906, upload-time = "2026-08-03T22:32:57.391Z" }, + { url = "https://files.pythonhosted.org/packages/49/f6/4a9966633c6fef83af997e2cff68ec1963676d412bdfd096df2a93b8e185/hf_xet-1.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:75765820ce4700db3750c94acc8fe27c5fae4c9ec000a0dbac3ca082acf97765", size = 3849221, upload-time = "2026-08-03T22:32:59.123Z" }, + { url = "https://files.pythonhosted.org/packages/a2/50/7afa2c9c787405864fc47a0d1bbc02c62e9101947ed43c1f43899fc7d91d/hf_xet-1.6.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:633dc0cd71d32da58ab8c03ad38e2fac452c15c2b0a2866ebf6ededfe0a5061d", size = 4071729, upload-time = "2026-08-03T22:33:00.721Z" }, + { url = "https://files.pythonhosted.org/packages/4b/69/55b8dcf636142ae660fec1869fcac14c4da2e8412e14d6eee1523be77e9f/hf_xet-1.6.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:f0906082d9932ae0c0057fa194041c22b4e2cdb46b2592ef3b91f020d62a081a", size = 3876287, upload-time = "2026-08-03T22:33:02.251Z" }, + { url = "https://files.pythonhosted.org/packages/67/4e/a28359bf1c1ecf11eba22123168c138698f7cb576ac678f5a2e16cd5da08/hf_xet-1.6.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d62671bb130879cef0ee4c9ebe47a14af6c66ec53e6d84dc15936e5ffdfac82f", size = 4464663, upload-time = "2026-08-03T22:33:03.802Z" }, + { url = "https://files.pythonhosted.org/packages/9a/69/1f0cbc2fb22ae6082d094f743d1b8945a3f36f6089cb95f42b7ee348cda7/hf_xet-1.6.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0e6e21fa3cdfcdcd76748564bf593870a5e013f47d97cf10aed63aa222cff5b7", size = 4262538, upload-time = "2026-08-03T22:33:05.287Z" }, + { url = "https://files.pythonhosted.org/packages/d1/3a/4f4f2301ade26e404462d3336fa11f7958d914cabbabdd6e03c3c5d5658c/hf_xet-1.6.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4fc74352a17015bd0ee90038bc9efe38db894cde45f268b6712b04fce8cd0acb", size = 4460520, upload-time = "2026-08-03T22:33:06.81Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5f/311725e2a905534dfee2dcb5b08414f249147f1f12252bfc2bd24caa075c/hf_xet-1.6.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4f71cba6129110c3374a33f919001ff130488fc23553698e34cc1c2a1198c", size = 4675937, upload-time = "2026-08-03T22:33:08.616Z" }, + { url = "https://files.pythonhosted.org/packages/98/b7/8c59a66d15205024662f1d66968136f13893f96df1ddc5087e2e281fc95f/hf_xet-1.6.0-cp38-abi3-win_amd64.whl", hash = "sha256:fb4fadde1b2b70bf4c0c14a6dccbe7194b1c28947fefd5bbe3fed9d940676c3b", size = 4033128, upload-time = "2026-08-03T22:33:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/73/63/ca511b6f802f28cf3489b280fe77475bcca8de85e81a6299d7916b5b5555/hf_xet-1.6.0-cp38-abi3-win_arm64.whl", hash = "sha256:3dc3e35441ba395006af5aaacc40ef2e603c51ef46c3530b9156185f00935ea3", size = 3859359, upload-time = "2026-08-03T22:33:11.725Z" }, +] + +[[package]] +name = "hpack" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/5b/fcabf6028144a8723726318b07a32c2f3314acdff6265743cf08a344b18e/hpack-4.2.0.tar.gz", hash = "sha256:0895cfa3b5531fc65fe439c05eb65144f123bf7a394fcaa56aa423548d8e45c0", size = 51300, upload-time = "2026-06-23T18:34:46.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/b4/4a9fcfb2aef6ba44d9073ecd301443aa00b3dac95de5619f2a7de7ec8a91/hpack-4.2.0-py3-none-any.whl", hash = "sha256:858ac0b02280fa582b5080d68db0899c62a80375e0e5413a74970c5e518b6986", size = 34246, upload-time = "2026-06-23T18:34:45.472Z" }, +] + +[[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 = "httpcore2" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11" }, + { name = "truststore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/ad/f4f0e57345f1870f3e8cb624e058d7eca6e5a27d33bcc3311d9b618734cd/httpcore2-2.12.0.tar.gz", hash = "sha256:9293522bba0aa7c4c8e9e3f040c16575bd8868e155a77fa30c7a9085a5eae648", size = 67548, upload-time = "2026-08-18T13:22:08.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/74/d370e55600d9bcfa0d9794b0166126d49291a3d2b20c268fc98c453a4948/httpcore2-2.12.0-py3-none-any.whl", hash = "sha256:7e04258ce01013d7d615e5b910a3b27fac937d7a95038227e79652b4ba3b4ceb", size = 83074, upload-time = "2026-08-18T13:22:05.854Z" }, +] + +[[package]] +name = "httptools" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/e5/d471fcb0e14523fe1c3f4ba58ca52480e7bd70ad7109a3846bc75892f7fb/httptools-0.8.0.tar.gz", hash = "sha256:6b2a32f18d97e16e90827d7a819ffa8dbd8cc245fc4e1fa9d1095b54ef4bd999", size = 271342, upload-time = "2026-05-25T22:17:48.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/88/1d21a36da8f5cb0fa49eafd4b169eba5608d57e75bbcf61845cbc6243216/httptools-0.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:880490234c10f70a9830743097e8958d6e4b9f5a0ffc24515023afeef984054d", size = 208247, upload-time = "2026-05-25T22:17:07.843Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/cc4feea2945cb3051038f090c9b36bd5b8a9d7f5a894a506a8983e33fd1c/httptools-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5931891fb7b441b8a3853cf1b85c82c903defce084dd5f6771ca46e31bf862c5", size = 113064, upload-time = "2026-05-25T22:17:09.136Z" }, + { url = "https://files.pythonhosted.org/packages/e3/a6/febbb8b8db0f58b38e44ad6cb946e6a255ae49b55f2e8543408fb7501ccd/httptools-0.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b15fc622b0f869d19207c4089a501d9bcc63ca5e071ffdd2f03f922df882dcb2", size = 523851, upload-time = "2026-05-25T22:17:10.106Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e4/f90a0df0b83beff265b7e3b65f2a4cefd95792d4be0ac3e16049f2acd3c2/httptools-0.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:425f83884fd6343828d8c565f046cb72b6d19063f6924093e11bcd8e1548cd09", size = 518842, upload-time = "2026-05-25T22:17:11.218Z" }, + { url = "https://files.pythonhosted.org/packages/9e/2d/0c9ac76dd2c893841fbf6498d6acec4f2442e1b7067f6e3e316a80e494e8/httptools-0.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ef7c3c97f4311c7be57e2986629df89d49cb434dbff78eafcd48c2bff986b15a", size = 501238, upload-time = "2026-05-25T22:17:12.728Z" }, + { url = "https://files.pythonhosted.org/packages/ca/42/906adc91ae3a5fa9c59c0a2f21c139725bd7e5b41ae6acd485cd14123ebf/httptools-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a1afd7c9fbff0d9f5d489c4ce2768bd09c84a46ddefc7161e6aa82ae35c85745", size = 509567, upload-time = "2026-05-25T22:17:13.842Z" }, + { url = "https://files.pythonhosted.org/packages/05/0b/4240efeb672751ee5b9b380cb0e3fdc050bc05f68adc7a8aefc4fcd9a69a/httptools-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:cd96f29b4bab1d42fa6e3d008711c75e0f79e94e06827330160e3a304227f150", size = 90918, upload-time = "2026-05-25T22:17:15.155Z" }, + { url = "https://files.pythonhosted.org/packages/5e/e5/8cfcabc5546e8022f168be28bcdaa128a240a0befdd03b59d558b4f18bd6/httptools-0.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:614ceea8ea606848bece2338ac03b3ce5324bcb4be8dc7d377ed708012fa4db8", size = 205148, upload-time = "2026-05-25T22:17:16.333Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0e/0fb14848c19a686c8062ff9067c1a48793e3224b47bc5b201535b6036fce/httptools-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2d689918c15a013c65ef52d9fd495d766893ab831a2c8d89f2ac5940a5df847c", size = 111368, upload-time = "2026-05-25T22:17:17.586Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/46f1cecf06b9bbde8e4b8c88034ac7908989e5ff7a3a388ef38392949c1f/httptools-0.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eb3028cca2fc0a6d720e52ef61d8ebb62fcbfeb1de56874546d858d3f25a26b7", size = 486447, upload-time = "2026-05-25T22:17:18.564Z" }, + { url = "https://files.pythonhosted.org/packages/77/00/258bfc0837221f81d9725c45f9b948a6a6b2994a147a4fb66e85100c668f/httptools-0.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:88bdd940f2b5d487b4d032c6afa5489a7dc4694410d43de3c38c4fb3af0dc45d", size = 482448, upload-time = "2026-05-25T22:17:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/04/ab/d1cef3b5523f4d272a70f42a776c3169a2dddfe3a54de4b2ce4a36341528/httptools-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6a43c9dd399758ccc0531acb0a3c4a6c299ee893ee9400e9c893b7bdcfae0681", size = 464460, upload-time = "2026-05-25T22:17:20.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/5d1d072442277bb2b3434e0e60690b8e8c23840ef7de8b6ea54040a536d3/httptools-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0770728beb05094c809b98e814edff5fef69d26ad7d21185f2f6d5884a0ba683", size = 471312, upload-time = "2026-05-25T22:17:22.085Z" }, + { url = "https://files.pythonhosted.org/packages/0d/66/b96623b27e51a68199ef4efdda0613cced9233fe3062ac74e50749c5ad37/httptools-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:7685df791fad561384bfb139e77fde27a1ffd93134e016f95a0db424ffbf77b1", size = 90117, upload-time = "2026-05-25T22:17:23.074Z" }, + { url = "https://files.pythonhosted.org/packages/1a/12/fa3fbf5f9517b273edea2dc982aa82a8c634091e67c590792b729017bc6f/httptools-0.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:de242a49b5d18e0a8776e654e9f6bf6d89f3875a5c35b425a0e7ce940feb3fd6", size = 206183, upload-time = "2026-05-25T22:17:24.004Z" }, + { url = "https://files.pythonhosted.org/packages/30/fc/5e7c4cb443370f2090a3aba0453a07384d29ff66b7435bb90e77e1037599/httptools-0.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:159e9ab5f701ccd42e555a12f1ad8ff69702910fc1c996cf2bb66e5fcb7a231b", size = 112079, upload-time = "2026-05-25T22:17:25.216Z" }, + { url = "https://files.pythonhosted.org/packages/ba/53/771bd891eb0f236f32145d6a1775777ec85745f3cc983a1f23d1a3b8ddfe/httptools-0.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c4a9f1707e4823d54dfec6c33fa3697d302aed536ed352a7ebb5a061ddb869d0", size = 481596, upload-time = "2026-05-25T22:17:26.186Z" }, + { url = "https://files.pythonhosted.org/packages/62/42/94e15bc68ce3d423243c45d7f1b0c7561f13844f97dc52ae23182fb65628/httptools-0.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d76ad7b951387e3632c8716a9bb03ac5b45c5f16119aa409db0459520887944e", size = 480865, upload-time = "2026-05-25T22:17:27.542Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7c/fe2980fc03723272e30f135b62360b075f513dfe7cc73aef36c7f04012bd/httptools-0.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a3b7387147361c3fd47a0bde763c5c91b5b4cd4dc9989b8ece84ff436c99843b", size = 463189, upload-time = "2026-05-25T22:17:28.546Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/47fc5fff68acd1bfa20b4734059c9a06cadb88119dcd5258b5b0d21d91c8/httptools-0.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f256d6ce930c52ca1cb2a960b7da03548c454e7d28b06059ad41bfe789036ce0", size = 466610, upload-time = "2026-05-25T22:17:29.816Z" }, + { url = "https://files.pythonhosted.org/packages/60/bd/07b13c93ffd9bec9546e0d43f8e19378dd696dbd278511406bc07371ef1f/httptools-0.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:19d1ee275bb59ba2643ba9a3a1e51cc0c788caf2b8df506368e03f56fdd08527", size = 92705, upload-time = "2026-05-25T22:17:31.133Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c4/121648f68ce066d7bd762d6b6d97e620847642d38d54f3d90ff11d947629/httptools-0.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:de1ed58a974e75d56560acc7e7fed01a454994429456f65209789992e41f2568", size = 215023, upload-time = "2026-05-25T22:17:32.401Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b0/312a062ae741ae3e8baa8c8bf20be81b2e67337b259ab4349bebc7b6142e/httptools-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e93c227b595c6926c1acee96891dd9da4be338cfbe82e5cd3bb9d8dd7dc4ac0b", size = 117405, upload-time = "2026-05-25T22:17:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/fc/37/fccd705f795386bb05bf413012fecff2a33e5aa8c2f069096de3e9fd8702/httptools-0.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2a021c3a8e65cc125390d72f59b968afca3bdcaff25bd67965e0a055a14946ca", size = 558497, upload-time = "2026-05-25T22:17:34.732Z" }, + { url = "https://files.pythonhosted.org/packages/bd/39/f172e8003576de35f5ba77ff417cf0e34429d35dc014deef15afa337a72c/httptools-0.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48774d39cbb70e2b1f71f88852a3087ae1d3a1eb80482bb48c13067ab080c14f", size = 571585, upload-time = "2026-05-25T22:17:35.813Z" }, + { url = "https://files.pythonhosted.org/packages/3e/b9/f5564760af99f3dbbf3f9104dc00e5da27e96cf433c6bdcf77617f70bf3f/httptools-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:88eead8ec8680a9f146c655bc88445a325bd7921cfd8194c7337e9467282427d", size = 543297, upload-time = "2026-05-25T22:17:37.08Z" }, + { url = "https://files.pythonhosted.org/packages/99/67/8d9f2c313618e161b82f3873188e7196126da1d6e29688df40eb3997c77a/httptools-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2c032fa028f46871ec7e1fc59fc15e8023eab3e6bbe6ece786a1611719a5d081", size = 539535, upload-time = "2026-05-25T22:17:38.032Z" }, + { url = "https://files.pythonhosted.org/packages/48/63/b906c01e53f50d432c0defe43ce52764a111dc1bdd028bafbeb54dcfd008/httptools-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:384c17174464c8e873398b7af24f0b1f44d992c820328413951a625323155d77", size = 108209, upload-time = "2026-05-25T22:17:39.473Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx2" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "sys_platform != 'emscripten'" }, + { name = "httpcore2", marker = "sys_platform != 'emscripten'" }, + { name = "httpx2-jsfetch", marker = "sys_platform == 'emscripten'" }, + { name = "idna" }, + { name = "truststore", marker = "sys_platform != 'emscripten'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7f/f8/579a8b51e42e38ee32647df9f08aa25643ae788e275cc625b199829c4671/httpx2-2.12.0.tar.gz", hash = "sha256:7631fe9887a8a2275f4a2540e053aa670fcc50742864a9ae7c66e609fdcf12cf", size = 100040, upload-time = "2026-08-18T13:22:09.086Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/95/411ba65569158e862368917aaf56597f3e5fa3b91b0502919638465a08f3/httpx2-2.12.0-py3-none-any.whl", hash = "sha256:cc8b6eecb8661c146b8f89a60e97456ee086e91a784ed31ac450c3a9e613dd36", size = 95427, upload-time = "2026-08-18T13:22:06.834Z" }, +] + +[[package]] +name = "httpx2-jsfetch" +version = "1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/0e5636363151a2a1795e0a77617168b9ca438e1748ec05fc9b5687f93d64/httpx2_jsfetch-1.0.tar.gz", hash = "sha256:70a0e3eabfef7cce5ad9c629f7d01ca05e418f586646f4ddf14782e4c1454c60", size = 6872, upload-time = "2026-08-07T00:13:07.492Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/43/832f631d32e4f1211caa2ba368317739fe71f0b8530e4c9d15dc454bac2a/httpx2_jsfetch-1.0-py3-none-any.whl", hash = "sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32", size = 6382, upload-time = "2026-08-07T00:13:06.567Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "1.31.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/f0/61159db90b5cd275d55516fe27920828e7d3be4053fdbdb27c3f70e5f1ef/huggingface_hub-1.31.0.tar.gz", hash = "sha256:f8e9e710a210613fa5d0f26bba6da05ef4aef9fba5a0f23f508f5ac4d08b6f90", size = 968039, upload-time = "2026-09-10T10:27:22.724Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/7f/3f886a625043b77312b80da2f2bf00b5ecbf5a73061af1aa0259cd258c9d/huggingface_hub-1.31.0-py3-none-any.whl", hash = "sha256:9dbb6a503cbe2494ea666695207e7262d410659e09134059deb83e5480864667", size = 798313, upload-time = "2026-09-10T10:27:20.798Z" }, +] + +[[package]] +name = "hyperframe" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/02/e7/94f8232d4a74cc99514c13a9f995811485a6903d48e5d952771ef6322e30/hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08", size = 26566, upload-time = "2025-01-22T21:41:49.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, +] + +[[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 = "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" } +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 = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "jiter" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/1f/8176d92e001f86505424b41664032ae26a882bc9ca41a32c803f373f9195/jiter-0.17.0.tar.gz", hash = "sha256:03e432f226a453851079fb84cd17c6da9991eab723e28d716f14ae3d906e0c12", size = 229037, upload-time = "2026-09-12T15:14:14.253Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/f8/07bd8c3a23f7a8a6875e6a820bbffe1483a18f18f9398a91b5495123176e/jiter-0.17.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ebf918dfd6a74adc1b9ad71f63c4ab00902fcd3b7fd39f2e24d871db8d713b91", size = 291633, upload-time = "2026-09-12T15:11:49.431Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5e/0de4c6f84ffefa6809ffc2d550b9a314365acf7e7ec9b6c7375d49047900/jiter-0.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61aed66ee042b3b49ef85fdf75714234d055d89d8496ac1c6e47f89e7a30d5e4", size = 321695, upload-time = "2026-09-12T15:11:52.727Z" }, + { url = "https://files.pythonhosted.org/packages/20/ac/befe2e82065bee37a0252081666ed2f48c1ac5f5c6c318c2de8168ba393d/jiter-0.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76eb4a5c20e86f9f848286f167024890f2862258a965d254774deb7fc1545ca1", size = 341967, upload-time = "2026-09-12T15:11:54.231Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cd/9797c1e529746750ae589da7c1a8c24373f00d88e11a989f9e5eb1959079/jiter-0.17.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bcc064f99183a9cbe7f26ed648c352031a74145cd61ed75d34632c73eb46a5a8", size = 326546, upload-time = "2026-09-12T15:11:55.41Z" }, + { url = "https://files.pythonhosted.org/packages/d9/fd/e6914c38d6347bab4ebff2b1f0c0f191db276e7a1d5c376176757da42fe3/jiter-0.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73b64e69c4150748e020356d958af94bec33c70a0a93d665cfa8f6d580fe1a63", size = 340995, upload-time = "2026-09-12T15:11:58.211Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7d/611b3abf6f88945b5474da5cdc6d1a185e805ac9bf446bb7766dcda6ea87/jiter-0.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0bc7f684b65bcda9c20434267577db71bf9905ceddd32b60d1d93278d8c8d3a", size = 352188, upload-time = "2026-09-12T15:11:59.414Z" }, + { url = "https://files.pythonhosted.org/packages/52/f8/b6e513ecbdf3b3cebe587c2279281ecf775b729a58cf4cc7bdf898ded029/jiter-0.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c21265b251d99bbb40080d178a8953e35601d3a1564e05c4de4c0d2ca616797", size = 345025, upload-time = "2026-09-12T15:12:00.697Z" }, + { url = "https://files.pythonhosted.org/packages/28/a8/fe26d06c5a6c5a4cfe703c5154c8a140da1305671eb3681aba9422d4f393/jiter-0.17.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:f3d7f7b34114f7ddc6d72a8e882d49de636b35d9fd12b4d420d3c5729f6c9812", size = 329180, upload-time = "2026-09-12T15:12:01.831Z" }, + { url = "https://files.pythonhosted.org/packages/e1/58/e6d66a26af40a20e62486feb7e222fd50f6e7aaa4f107abd89675dcc835b/jiter-0.17.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5078ab00664307fab2019b522a93aeb191122789f085daf5fd9e362154021d4a", size = 335805, upload-time = "2026-09-12T15:12:03.056Z" }, + { url = "https://files.pythonhosted.org/packages/ef/3e/96520aa2fef5ef831d95483a902140bfab83dcac9eaa74f7df61b5e50a1b/jiter-0.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:470e1b1e4c42f1ead2189166a299691871a2df5056c976e7fb96feafaf5f9d44", size = 484121, upload-time = "2026-09-12T15:12:04.414Z" }, + { url = "https://files.pythonhosted.org/packages/6a/8f/5d9d92fe538bf36ff481a2278c48147e59c1cf8eb2f7be665260665febe5/jiter-0.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6eb6aedeb7352b8f3b6af9cbd67983840165c00428e63f1b420a85885128ea31", size = 521310, upload-time = "2026-09-12T15:12:05.612Z" }, + { url = "https://files.pythonhosted.org/packages/50/06/a09f979b22e652afbc3de66c709b2ba92edcef555f7535ab937c86b4f21a/jiter-0.17.0-cp312-cp312-win32.whl", hash = "sha256:362bb47423886d45a9f705d2d9d4008c6eedd4e41eb1bab4e96fb6daa06b33fd", size = 185029, upload-time = "2026-09-12T15:12:06.994Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d9/98265a005b2473ec2be5a84e2b64c2f65382c673879f1574845cd4bcd77c/jiter-0.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:9bd3caac219df476dd0cc3fe01d2f1581ed588906feac767abd9614c1c12f8b3", size = 227381, upload-time = "2026-09-12T15:12:08.823Z" }, + { url = "https://files.pythonhosted.org/packages/a8/11/2e05bf5a56e57a543ebb8f585074adf09383e99d7b062dac92eab1f4d57f/jiter-0.17.0-cp312-cp312-win_arm64.whl", hash = "sha256:36ee6e69027396664e59995b9a635a947a5304ee9837279584a0bb8145c8f6b8", size = 183610, upload-time = "2026-09-12T15:12:10.374Z" }, + { url = "https://files.pythonhosted.org/packages/40/eb/2c4a8075ed5ea02b56911e9375d4c8d7784572ff4af32e5a99ae0d071044/jiter-0.17.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1b18434638228c0c184281609bf3d9459026a0f1ea48fb76c205e3ef72069caa", size = 290991, upload-time = "2026-09-12T15:12:11.641Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b1/34bfa29599d420423baac6ff7cada6674fe63d5a7a2ccb3900b904678783/jiter-0.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ec89771f4272b989487a6364e519db6bbaba323e8bbf949ac89a45ea9c18b7a3", size = 321425, upload-time = "2026-09-12T15:12:13.855Z" }, + { url = "https://files.pythonhosted.org/packages/11/71/a5ac64a62a04aebd556afadab14a6b730001e16df87266ded943a100a1d9/jiter-0.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e3f052c671d5f425cca5ea5901cf11a831369fba4a55a3862cab93c323b4c3b", size = 343138, upload-time = "2026-09-12T15:12:15.046Z" }, + { url = "https://files.pythonhosted.org/packages/01/dd/f761e320ea473314cb68612bc6a435393464dbd198051399b36848b4ebf3/jiter-0.17.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:785a216bbaf8f15fc974e964ced7322cd3d774bb0e86949edd78c6bffd6ba35b", size = 325805, upload-time = "2026-09-12T15:12:16.506Z" }, + { url = "https://files.pythonhosted.org/packages/19/1a/27d8e40f0fb29bbc7a5adf30907144396a115dbe93d5d8976c054a6dfe96/jiter-0.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d85c558c9f8532bba287a990ac63767c7daf756f0d8c030219f62499b1fa228a", size = 340230, upload-time = "2026-09-12T15:12:17.682Z" }, + { url = "https://files.pythonhosted.org/packages/ac/c0/30bcde78a28155461f965d16b7aca4ffca6d17494d905f7a0bb072e6c64e/jiter-0.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c23849235d2142ce444b2b8c6eceee9f82f4cc0bd5c9081602e4155c6197807", size = 351343, upload-time = "2026-09-12T15:12:19.337Z" }, + { url = "https://files.pythonhosted.org/packages/27/17/91420b156315ae22732f5ee1a7b5725a030aab9dc8fd7dcdacfb4aa588d3/jiter-0.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58df29268a95e910f17db7ec9178eb7f15aa8619aaca3575275c4e6b3f4fe4c5", size = 344990, upload-time = "2026-09-12T15:12:20.705Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a2/ae6d5672644cc11127970277c9aeb0fa6fae376845587f5b0a8e8828167c/jiter-0.17.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:a277f97eba7d66b1ee27eb5dab5b774ff46a10c78d89a1d3dcce04ce1357c8ca", size = 328624, upload-time = "2026-09-12T15:12:23.859Z" }, + { url = "https://files.pythonhosted.org/packages/04/62/45cb1162f6aa586536e4a973fc339d72dc6b08cca030d70a838a307aa778/jiter-0.17.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe15ddf316f1f1f643347d3a474e74ce61880c79a11ec5dca53df20c071bd3e8", size = 334731, upload-time = "2026-09-12T15:12:25.229Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5f/45c1574b644da7deda0b7591c349520dcf83ce45b24d7ca19922dab1fc27/jiter-0.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:02adebb7ce6413c44d40af9ad59d1c1cd79630ccdcb6f7bdd2d461e48c03d8f9", size = 483649, upload-time = "2026-09-12T15:12:27.557Z" }, + { url = "https://files.pythonhosted.org/packages/c1/d3/ebea1ecb5b241c519f192b30215c79a8e47f42f1621acbcd6f8830728416/jiter-0.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:55d0e0e613a3f9ad600cf436e0e2b8057d1b52bcf1d91b2d36ac53451231e6a8", size = 520758, upload-time = "2026-09-12T15:12:28.99Z" }, + { url = "https://files.pythonhosted.org/packages/64/e6/682b641ff0765ea9bdc349dbc7d223de5c8af8ec1abda0db3406992f92fe/jiter-0.17.0-cp313-cp313-win32.whl", hash = "sha256:2c45ad7c973ef33fe5114a953377b35a95240f4542c0724d9f781e47dc24bac7", size = 184334, upload-time = "2026-09-12T15:12:30.813Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d2/9a49aac2b27af4cc5015e368c0cc3588491a532f717a668ffce1f1ac57da/jiter-0.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:a3cebb1fe4a1abb00465f3f8a17e09112603e8b7c59e5c3adbcd9f7815a64acd", size = 226601, upload-time = "2026-09-12T15:12:32.096Z" }, + { url = "https://files.pythonhosted.org/packages/b4/ce/9a43e9f614608eafa78de22aedcff54cd21324467b5d442d5c9b00244145/jiter-0.17.0-cp313-cp313-win_arm64.whl", hash = "sha256:96b8b0c6dc5d78682f54a450785e075aa929cde768304cad363cd4efba5a82ac", size = 183103, upload-time = "2026-09-12T15:12:34.396Z" }, + { url = "https://files.pythonhosted.org/packages/01/9e/23065f8e2c7a4c372c1b6f6622e4cfab4dc786cb5150052b1527e6a6a840/jiter-0.17.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:00d783a779c5664e16dbad5e3a3c3a75e128b07dd5f4765159658d9210a50ca5", size = 292210, upload-time = "2026-09-12T15:12:35.613Z" }, + { url = "https://files.pythonhosted.org/packages/ea/81/67b58647560bc82a4490d722caa8561d7a86a9f45d4fa620b7e5fe282c7a/jiter-0.17.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0619d806e260ecf0c2a64521942c94af5d547c9ec99b55ae4f51b538b5576a76", size = 321512, upload-time = "2026-09-12T15:12:36.907Z" }, + { url = "https://files.pythonhosted.org/packages/c7/07/6658359a25f55927f7f8bf0e16465dee2ccd0b2a1a5208acc0df8972e074/jiter-0.17.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc0288ce39190ee33fe6e4ec73161eed34e7e2da509b525546ca061778d62b64", size = 343897, upload-time = "2026-09-12T15:12:38.189Z" }, + { url = "https://files.pythonhosted.org/packages/46/04/5d50a9f0319cbdc37fd53c27f8c313d46afc34f1b048219ae6d8ea068da4/jiter-0.17.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a52a430d04225ffde633e6840bf2381d34c019ff98526b5929755b9052fb199", size = 326519, upload-time = "2026-09-12T15:12:39.532Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c7/d02517832b29eb8275fdd0f4ce0f17b80f58cc4c3ebecd4d9ace990d633d/jiter-0.17.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37f33d327900bf2879613b3363fd48df97b4232d0c41f54bcf2e790c2fc40a71", size = 341369, upload-time = "2026-09-12T15:12:41.486Z" }, + { url = "https://files.pythonhosted.org/packages/3b/07/499b5f5603501cdd93a73a6a176dfad9c96555a3ae58ca9f8e3acba63dc9/jiter-0.17.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cf564d43c4388149ca58ee571d0f5ccf875e20d1fd4662fd94cc0d1ea3b10ef", size = 352160, upload-time = "2026-09-12T15:12:42.721Z" }, + { url = "https://files.pythonhosted.org/packages/f5/75/b04013c7743269d4533ef4e746fc0ed678a143968dd7448658e3f51daad2/jiter-0.17.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:523c499235fb65add25d4bb01b1c4709ce695efdc7deb6c0a7bc515b5c44e0fb", size = 345018, upload-time = "2026-09-12T15:12:44.192Z" }, + { url = "https://files.pythonhosted.org/packages/1d/96/cbb6fd1e42a77c8412ec4643db95059b30cdfc635e387cc9193e098ce268/jiter-0.17.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:455e4ab35cb2a4a91a8404e08fd3c621bae433922e59bf1c494fe20a426b013b", size = 329244, upload-time = "2026-09-12T15:12:45.491Z" }, + { url = "https://files.pythonhosted.org/packages/15/67/d3be402f398566a379bf40ae65be5c3505b14d9e95e0802a597ddde7ddee/jiter-0.17.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6871973bfbd4408f7f1c632b30bbb5bbd9671c1bc8650af6823e24b7be13709b", size = 335693, upload-time = "2026-09-12T15:12:46.935Z" }, + { url = "https://files.pythonhosted.org/packages/7f/8d/98e2c4130b93d64f1d67c89060b928d04102549bf05e64451c9e6024f9ca/jiter-0.17.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:77f6aac0137309b31448c1bdcda4c6c77077664a6d018ece8d94019c68a5a5b9", size = 484329, upload-time = "2026-09-12T15:12:48.361Z" }, + { url = "https://files.pythonhosted.org/packages/78/5e/8da91e49f0fbca37c3489fb4cf3ad6676d4965f00ae5468bca3a2513737a/jiter-0.17.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:93946d89fa04d5ba64dd323a8dd8d901676cb8a3c81d99ae4f6c051a9b4c3f2f", size = 521358, upload-time = "2026-09-12T15:12:49.856Z" }, + { url = "https://files.pythonhosted.org/packages/be/21/5388684a5a38af3557cd9c2424b9827c71809cff24373c75ef9d0d3dfba9/jiter-0.17.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:70f19a2ca8429f91e82eeffb2f51cb87bc2d6e953b009b91a92d29c3a16ccb03", size = 110459, upload-time = "2026-09-12T15:12:51.747Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ad/58b3a93525d2ffca7f54d9dee441381990082bd1172fbeb8d6a3f72a4dc3/jiter-0.17.0-cp314-cp314-win32.whl", hash = "sha256:71dbd74314c5df52a1bccf7b8bca46d14e943af7a2012e73b23f49977ef194c8", size = 185043, upload-time = "2026-09-12T15:12:54.477Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4a/1aa520eb6c359b262c14ff995ca7283837208ddfb1202082ce9d73cf214d/jiter-0.17.0-cp314-cp314-win_amd64.whl", hash = "sha256:ac3c6ee3264d6f5c44c617f90bc7e8b9e1587e7d6708c9d8f811cb65582ee312", size = 227163, upload-time = "2026-09-12T15:12:55.931Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e4/5997f648794bd9b499491d0ff480b096cc9a9c65bdba29f57568e6aa1705/jiter-0.17.0-cp314-cp314-win_arm64.whl", hash = "sha256:6219adaf59711ba7063a52496e8ec6d3fa3e209d7827d83eee3b2abc780a1744", size = 183505, upload-time = "2026-09-12T15:12:58.196Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4a/84a5ec271d09f7590b6073af5ee4abb44eab4ccace453b7e2c5ce45234ca/jiter-0.17.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:59bddbe6f9ffecc68d641e1e2d619ce64cf8a9e9eeb74e5c518f74fc87abf1b0", size = 321527, upload-time = "2026-09-12T15:12:59.394Z" }, + { url = "https://files.pythonhosted.org/packages/39/71/9e1fd0045f5920b4c36be35c3f0f0dfd123668684f8ad352619d7aa44183/jiter-0.17.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cb41cd1432f1dc19a231cf70b54d42b2c9f05085155859263fce06fa4d41388", size = 340865, upload-time = "2026-09-12T15:13:00.756Z" }, + { url = "https://files.pythonhosted.org/packages/b7/2b/14627fd2bc377f3dd09491bcace6b90e34b4d7fea2f1f3295031ff91f528/jiter-0.17.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd7790aa79c8b518e512ebcdfce9f11d8ef5f30efd43720c8a19a548b39fa489", size = 325412, upload-time = "2026-09-12T15:13:02.152Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f3/8d5808f7bf0f456bde79e6393587183a0cee5f83d179fe1f7f1eff2ba067/jiter-0.17.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dbbfe4e3c21c8166980cddc5bee1a315df082454f007947dfb6fb73800768165", size = 340473, upload-time = "2026-09-12T15:13:03.485Z" }, + { url = "https://files.pythonhosted.org/packages/4f/da/1d8c7c6c4ae6b2423b94a81b6b907d37b28f87664e077427b531bf1b5313/jiter-0.17.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c286860abfe8b100cac1c02e225e5776eb9216edd71ba17cdb237da4af32bc9", size = 350757, upload-time = "2026-09-12T15:13:04.828Z" }, + { url = "https://files.pythonhosted.org/packages/eb/96/c1813dcca15c5a370145a448aaea7d1f83f6f0228a5f1130e79340ee385f/jiter-0.17.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f753eb70b1474a29e635e7542ff7312e6d6b951e0b25e8a2e8c34eeb1ddcd478", size = 345203, upload-time = "2026-09-12T15:13:06.131Z" }, + { url = "https://files.pythonhosted.org/packages/d7/f7/fc61cbcf2992d169ede13648fc3fd8e2d3171a3669dde43cd4db556549ac/jiter-0.17.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:eae86b1f027031e39db2e0e9c4842221edb7b8cd474d23f87a79b3bd4b651768", size = 328322, upload-time = "2026-09-12T15:13:07.392Z" }, + { url = "https://files.pythonhosted.org/packages/8f/88/46418a3abbdffb7dc41b314200360f24f75faaeb35573e81c92de322cce9/jiter-0.17.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5bf350452a43173e69e1fc74847c57a60e3d7515807287f29849baa2a85d8718", size = 336570, upload-time = "2026-09-12T15:13:08.666Z" }, + { url = "https://files.pythonhosted.org/packages/f0/28/b8a55b949be6306df8888e365a8df05441de8a7b11289f6957004302e41e/jiter-0.17.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:da139721f4b7cafdbff580a4f511ea24cb91f4909330c6b926a1ca53836c0a59", size = 482879, upload-time = "2026-09-12T15:13:10.037Z" }, + { url = "https://files.pythonhosted.org/packages/75/3b/21d0afa53ba0680962c39f3eb95ed2946f8793369ed44b0c82b490723081/jiter-0.17.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:8079849db9a1371bfd90bad088458a8fb836261879df2233cc9632464ecf64e1", size = 520406, upload-time = "2026-09-12T15:13:11.456Z" }, + { url = "https://files.pythonhosted.org/packages/ef/03/bcbaf8b6b9ea23c2c074411f8ecfbb02d820abac5d0cb8f4e280209174a2/jiter-0.17.0-cp314-cp314t-win32.whl", hash = "sha256:8f770b0c77e5fac482e1ba03ca1a7e18286bfb213d749932a00a7e4cd5de5e06", size = 184434, upload-time = "2026-09-12T15:13:13.037Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b5/5d6ce2c93ef6fe1241b37a9005547f9b6d58db1f07f39fe95807d4b98f51/jiter-0.17.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c4289293e5278d9314b00f15c37f2120fa51d3d68565292e715524c750e775a9", size = 227392, upload-time = "2026-09-12T15:13:14.933Z" }, + { url = "https://files.pythonhosted.org/packages/f5/4b/1e52baf90187606e33a7b8cfa8f96f5829acd7f01870077eb01059ab76d0/jiter-0.17.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4dfbfe5a6e1e80a7082af559f66386405025ec278833e0c649f69cbc6e1004cc", size = 182776, upload-time = "2026-09-12T15:13:16.239Z" }, + { url = "https://files.pythonhosted.org/packages/05/fc/efe3ac75564ab10f53517958f5ccdc231fc7334af66c76776cb554a88967/jiter-0.17.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:84963d3f395ef5e9a32ce47155e08a7962fa292c159a10cb98b931cef1416925", size = 292143, upload-time = "2026-09-12T15:13:17.502Z" }, + { url = "https://files.pythonhosted.org/packages/d1/4c/46982118d91f9ffe9714319d21ec4f98d9b7e0cfd9062826c524a54de24e/jiter-0.17.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:ffa0380ad091de7d3fc33e17a97ff479851ee18a0a2a3ee56ff3215cdc886656", size = 321341, upload-time = "2026-09-12T15:13:19.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/12/9b1ac6ecc6307049913db54839ddba1c11c1ef72c5a8bbb5514bc3b50d1b/jiter-0.17.0-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:755079792868ce5d4938e83b91a0939b34fb858a1ca65a104f2d771bea57faa1", size = 344383, upload-time = "2026-09-12T15:13:20.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/b6/527cc72af836d824e9d4d666e64f0a1ca7eafd662a8da9657b78592172ba/jiter-0.17.0-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3bf4dc2b84a464117fb097d15a25c58d100d2692888e3b0d92df5b48ed16b7c0", size = 326841, upload-time = "2026-09-12T15:13:21.83Z" }, + { url = "https://files.pythonhosted.org/packages/d1/41/567f98617e88005b249503b933803f633ec6ba2d427cf4cc35e5c832125c/jiter-0.17.0-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02a360707033d8cef53f7f3480817a1489177a259ec6ec01e98c37e0b922ddca", size = 341354, upload-time = "2026-09-12T15:13:23.323Z" }, + { url = "https://files.pythonhosted.org/packages/40/da/b29cda895b785f7d426e224638a885b6145a08ce853b381f34afe3e88c5d/jiter-0.17.0-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:300ce01ab0215e3dea4d00090143c909aedc65c0f809b3c07983e1d038f291b9", size = 351985, upload-time = "2026-09-12T15:13:26.526Z" }, + { url = "https://files.pythonhosted.org/packages/f7/5c/8a73829e7389e72ea298a450f2b3cb58e71a3e464b45f6d8753740f1c4f5/jiter-0.17.0-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746243a080b4ca790b8499af3d7cf9825d5f5987933950cd818e767ee353d826", size = 346052, upload-time = "2026-09-12T15:13:27.887Z" }, + { url = "https://files.pythonhosted.org/packages/1d/2f/98d6001026932c095ba440925570123043bed29f5ff56158dfe729a9e81b/jiter-0.17.0-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:b550585523339b71cb852b811aae49d08d7601ad8ffe9f5dc1562f4c3d22fd87", size = 329159, upload-time = "2026-09-12T15:13:31.569Z" }, + { url = "https://files.pythonhosted.org/packages/94/2e/708dc1d2678f092c31c12754e860cd8353e6a85ecbdb1010157edca0da9e/jiter-0.17.0-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0239520085cac678e77a606fd7e3f1c60c371d719790c5e3807388d3da4354c2", size = 336001, upload-time = "2026-09-12T15:13:32.846Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/75a5ae38862f4eaf0fe2f8a9fbf6484c4890df04c06dcdffc45e36bca61a/jiter-0.17.0-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:eb2295da7c3769f6719b227a237aa6a5cfa6550e478bc838001b592c57e16575", size = 484281, upload-time = "2026-09-12T15:13:35.333Z" }, + { url = "https://files.pythonhosted.org/packages/a0/32/6636fae811c27c7f93e1b11fb5800de6a5c9e4269a27cf718e0b31218ad1/jiter-0.17.0-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:e088612ff90ebc9247e1a43074b72835804261c47e6a6c01cb3ddcb55360d688", size = 521300, upload-time = "2026-09-12T15:13:37.101Z" }, + { url = "https://files.pythonhosted.org/packages/61/aa/12df7e0b0b1a2602e3d5a5a7104d7d9700f254b400f134a9b50955c4d231/jiter-0.17.0-cp315-cp315-win32.whl", hash = "sha256:0b52d52035b3907c5b1f6277857b29c1cbfc965e24e0f27330dbed83edb591ec", size = 185138, upload-time = "2026-09-12T15:13:38.901Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ec/3dd2e495032cddde05723c1f4c743b67a23e55d2af244692a7f58f0cdae3/jiter-0.17.0-cp315-cp315-win_amd64.whl", hash = "sha256:10f5558eed511b830488003449d942bd75829ad6257dc58cb9a03e596a7777b1", size = 226950, upload-time = "2026-09-12T15:13:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c7/ef85704e0a57e9cadb2babc05f6d7c5df4a1c75da1a6ee31e1986b0099a5/jiter-0.17.0-cp315-cp315-win_arm64.whl", hash = "sha256:fa13acf1046f95df808c64b1310705e143fab87aee73ae00cc42d640867fd2c1", size = 183618, upload-time = "2026-09-12T15:13:41.432Z" }, + { url = "https://files.pythonhosted.org/packages/0e/9a/a4b348349de68762b58d6713973d363ad80a1c741d0bf8def7975f0ecb26/jiter-0.17.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:af2f7501580f274b63c4b2283bc425f5df7edf06ae5b171e5f87d912ff359a20", size = 321155, upload-time = "2026-09-12T15:13:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/c1/70/aebd6d0b5f0677de3a3d0bdc4a05fac949b97c4ede454c8809f180ac7b17/jiter-0.17.0-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10c5349312e5cb02b7a21e123a57665afa895953f05bf252a9dd4c13a572b7ab", size = 340985, upload-time = "2026-09-12T15:13:44.115Z" }, + { url = "https://files.pythonhosted.org/packages/a7/82/4c3b49796b5eb62f3f5046f957683f4ba0135fe1a60957c11180512460df/jiter-0.17.0-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:86f3f9343a288eb85a81ef20a752b2f84564296636db54a9fff0b5c8deaf1df2", size = 325670, upload-time = "2026-09-12T15:13:45.901Z" }, + { url = "https://files.pythonhosted.org/packages/bc/43/f6341ecb4872202a4ef150486fcee0e1ace4aa3da39b71b82061452cdd3a/jiter-0.17.0-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4607ec7d93355fbc25b8dc5189153cf21d66063b9f9cd04dd2774e6e783f9b6a", size = 340339, upload-time = "2026-09-12T15:13:47.442Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c4/bc2c86e08fa065e03cb2fbc53b367c3640a7d257ef9d877b29118ea636b7/jiter-0.17.0-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:10cd64a5720ad7f809ac5466ff1705813f1b6b510f195a73acafba0ac0e1f675", size = 350705, upload-time = "2026-09-12T15:13:48.848Z" }, + { url = "https://files.pythonhosted.org/packages/9d/67/91f12aa111cca6e3a197c3e36bf60a034bf9f122f6d41112a639e44217d8/jiter-0.17.0-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efe9f61bb30174d2f5c8396445c360c96c44e78164d0815dfe627ccf57849574", size = 345011, upload-time = "2026-09-12T15:13:50.215Z" }, + { url = "https://files.pythonhosted.org/packages/f5/cb/9f5556e8f6ec89755fb5a709d8eb8270c9a324e31079eda0dfbeca451b6e/jiter-0.17.0-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:370d8fe5bf201dc6925e8a84c81ac7291f74d9fd1778234fc79d517064a5c76b", size = 328268, upload-time = "2026-09-12T15:13:51.809Z" }, + { url = "https://files.pythonhosted.org/packages/22/98/153f20680fb75781a490fb849940e2b00f95035c7aa054df592f36ed33fc/jiter-0.17.0-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6b303d88e6a0bda789ec4b7801c7bad68e27230ba1fe4baffc756d1fbd32dc9d", size = 337024, upload-time = "2026-09-12T15:13:53.095Z" }, + { url = "https://files.pythonhosted.org/packages/af/59/b16c9be3a5035df4466cc72e888188c027562de90a723d290ab6814cb9d4/jiter-0.17.0-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:30793a24a31e968969757c9e08d830cbb15a2cd3c4959b4498b38f4b1c2258eb", size = 482766, upload-time = "2026-09-12T15:13:55.713Z" }, + { url = "https://files.pythonhosted.org/packages/d0/55/667dea313094024bef082175d6bfe8976f90d1c00c926af9df1d8e0eab48/jiter-0.17.0-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:686c93d86f2b426c803024b805bd161a6cd10e9627c23e901640eab646c0ad8a", size = 520367, upload-time = "2026-09-12T15:13:57.674Z" }, + { url = "https://files.pythonhosted.org/packages/21/e3/4b1a43501fb9ed17b01d137e380cb0e8fdcb39a254ce31aa2ab95bc861ac/jiter-0.17.0-cp315-cp315t-win32.whl", hash = "sha256:86d703d9faa1ffc8ae4e9de0fa007712ed2171b5c0d93811a8e2e105ac729b0d", size = 184603, upload-time = "2026-09-12T15:13:59.27Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f2/b8ee0372b6ebdf1bde5cc44495d5291d17f961065f5b48f8616cc67cac2e/jiter-0.17.0-cp315-cp315t-win_amd64.whl", hash = "sha256:42b0260445251b1bc520a63baa94a32d88e0f931fba234f1764db7feb7c72174", size = 227936, upload-time = "2026-09-12T15:14:00.472Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b4/923a1215daba959aed8355973315cb3f81f53e0d01c5b211870a27b41f45/jiter-0.17.0-cp315-cp315t-win_arm64.whl", hash = "sha256:d47687806f9c54c84ea38733507081337922beca90ce819c7d852dd485bc0f23", size = 182977, upload-time = "2026-09-12T15:14:01.799Z" }, + { url = "https://files.pythonhosted.org/packages/17/31/4bb27f54333d3b9ef1e5bd3312dc0b4bbe59c68bb0885fdb40583a6b1567/jiter-0.17.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:454c4997d73cc466c71fd565d91e603b0274e48ea0c6b0b7a7aee6967e4ceb7c", size = 288415, upload-time = "2026-09-12T15:14:08.455Z" }, + { url = "https://files.pythonhosted.org/packages/28/30/879570ecf82574eaea77c5eb10309f4b630dece5f2a556e9814a90ba3f2d/jiter-0.17.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:40d2c240f8f80b5b0f201b29f0ae129c81448c60c772227a41747b5e0026f6a2", size = 279113, upload-time = "2026-09-12T15:14:10.117Z" }, + { url = "https://files.pythonhosted.org/packages/77/7a/1f0b8a35fbd079a4f1752c31a15dc99cf277f863747c459be0af39e900e5/jiter-0.17.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e05f5adbf68c4bd11e1610f394034d984152988e84be6f8314235ce6f2139e5", size = 303708, upload-time = "2026-09-12T15:14:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/e1/8b/d76219ebdbcf3d4209d9d21a0810db4c8d0a6f88e3ee87d30bdea4e90d30/jiter-0.17.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c0bf24c72fd0491405dce5d40194f2070e9021ce648c1a1d46234b93d848ff", size = 307147, upload-time = "2026-09-12T15:14:12.897Z" }, +] + +[[package]] +name = "joserfc" +version = "1.7.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/94/80fea1514b7c6d7d37804d3fe9ca81455f633347fc98731bd71ffe1faa17/joserfc-1.7.5.tar.gz", hash = "sha256:d5ff536e658e17664f8c1b1ab60dc4aa62aa973fcef1edd33cc44bda45d6f5ea", size = 234990, upload-time = "2026-08-29T13:05:42.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/c5/82addfd375e5ee6520644e0553e4aadde92d668c4fc99cc716d337fe7bb3/joserfc-1.7.5-py3-none-any.whl", hash = "sha256:add2c2c84e8373b084d526a8b53daba5d7a513a118cd2dcd9fc9f979d0922159", size = 71269, upload-time = "2026-08-29T13:05:40.718Z" }, +] + +[[package]] +name = "jsonref" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/0d/c1f3277e90ccdb50d33ed5ba1ec5b3f0a242ed8c1b1a85d3afeb68464dca/jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552", size = 8814, upload-time = "2023-01-16T16:10:04.455Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/ec/e1db9922bceb168197a558a2b8c03a7963f1afe93517ddd3cf99f202f996/jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9", size = 9425, upload-time = "2023-01-16T16:10:02.255Z" }, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + +[[package]] +name = "jsonschema-path" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "pathable" }, + { name = "pyyaml" }, + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/39/79/cd02a4df6d9270efdc7d3feefe6edd730b0820c39eeaa107a2faee8322d5/jsonschema_path-0.5.0.tar.gz", hash = "sha256:493b156ba895c97602655b620a8456caa2ce08c1aa389f5a7addec065e6e855c", size = 19597, upload-time = "2026-05-19T20:45:00.971Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/2c/9e69d73c4297508be9e3b64a970ea3971b3eb8db64ffc5802d40bd25981f/jsonschema_path-0.5.0-py3-none-any.whl", hash = "sha256:2790a070bc7abb08ea3dbe4d340ece4efadf639223001f020c7503229ba068e2", size = 24077, upload-time = "2026-05-19T20:44:59.225Z" }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + +[[package]] +name = "keyring" +version = "25.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "secretstorage", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "mcp" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx2" }, + { name = "jsonschema" }, + { name = "mcp-types" }, + { name = "opentelemetry-api" }, + { name = "pydantic" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/31/ac54fb0fdd5b37de704486e288bba4fbbb463f24cfcfedbede407b854513/mcp-2.2.0.tar.gz", hash = "sha256:2dc37ecb1974becdcebdbf7561e7c15a07dbbf20ba21ba16c3593b3038b3afbd", size = 4084129, upload-time = "2026-09-07T16:06:23.439Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/ff/8e7eade68b8a28f7da0ed1085544341b51f9c935dbf6b95c76b7edfea6a0/mcp-2.2.0-py3-none-any.whl", hash = "sha256:bde982589473a060ae145e3406e9a5333fe538c97229ba841f5a7f92be004f81", size = 365656, upload-time = "2026-09-07T16:06:19.711Z" }, +] + +[[package]] +name = "mcp-types" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/91/762d7755d971aff8a28d75f7961656148edf27875c8026e6385aaab08ae7/mcp_types-2.2.0.tar.gz", hash = "sha256:d3ed53703ddd10d9c6399f29d322bb66f3f67ab41348ac8556ba23e07fedefad", size = 65892, upload-time = "2026-09-07T16:06:25.187Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/6ffba5d8cd5dd9b8a19478875c50e04945314ba5074e84d749283f27f62d/mcp_types-2.2.0-py3-none-any.whl", hash = "sha256:ea476b73ee86709ab5abc9452385ed36cc05907e582355622e294595c9a04f13", size = 69106, upload-time = "2026-09-07T16:06:21.461Z" }, +] + +[[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 = "multidict" +version = "6.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/95/989c1b5ca17b72128661530cd6e351a0a83cda9a4d6c036e9ed976c18931/multidict-6.8.0.tar.gz", hash = "sha256:5cd4637ce76312ba1e05eb9c5193fec231f64fee0944e135fa1e951242355b37", size = 122412, upload-time = "2026-09-09T13:57:57.967Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/83/a4621577679149ea001806f5963f3fc687c391c1bd5217157be2278863f5/multidict-6.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:22a310ad37672a261e55a8b5e28d0ae08cfb68abb1f46418ccd19835c3b8e836", size = 84146, upload-time = "2026-09-09T13:53:49.163Z" }, + { url = "https://files.pythonhosted.org/packages/09/00/236b063f3e606055a3a9ba8faa5d40e6c688b059a58056b055f213476f46/multidict-6.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bb8c7da8c861391f7ae48e3593762be2dabe405109e01aec520fbe1a6d15d14b", size = 51049, upload-time = "2026-09-09T13:53:50.46Z" }, + { url = "https://files.pythonhosted.org/packages/91/9d/954b139bfa969855f2d4cb5ae7b7d44dd7106f754305b6e21a9068213aa7/multidict-6.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10456943903744ae1249728161c96bd9d2f7eb5ee17fcc2ffda2dc32e1bb36c7", size = 49362, upload-time = "2026-09-09T13:53:51.878Z" }, + { url = "https://files.pythonhosted.org/packages/d7/8a/8774f5b3f6d5266ecd1117876e04b405f0f1ce19aa750b35a826efe6cfe4/multidict-6.8.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:658f5a1895b804423d97b22d06fc0d0b171c7c01dcc3aa9c8faf0c0e26a249a5", size = 278619, upload-time = "2026-09-09T13:53:53.44Z" }, + { url = "https://files.pythonhosted.org/packages/db/47/736080fec911ed9f2dd57ccab5a8145e4f17c4987de0bfc27bee20e4d170/multidict-6.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90c10b22860dbd09982d0b8993b66231a861bea2993d4a817ff35273f6ea285a", size = 283771, upload-time = "2026-09-09T13:53:55.048Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d5/b7f41f59b0583f092602308a5e7c16ec5efd00d60214b22511e89a38dd19/multidict-6.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:088b04a66b3c1fce6fe4d771ec184a0426262d0b86709c908477b4ac7965df40", size = 262108, upload-time = "2026-09-09T13:53:56.631Z" }, + { url = "https://files.pythonhosted.org/packages/54/b2/a52dc06c6e2598672308e3d392fd85b837b23c25dda459bedaea84985080/multidict-6.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9caef53b20a105c0d66518a34be2f71b2783de8d091767575ef86f6ea422236d", size = 289899, upload-time = "2026-09-09T13:53:58.415Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/00cda7983f37d119b86f1f89d5b4cf771ecb6d0fedeb9a0971758d6d6d4a/multidict-6.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a5e1583c14775580da05641240ce0d93f36ce3ddef3d5083a827468b0bcfe874", size = 293025, upload-time = "2026-09-09T13:53:59.973Z" }, + { url = "https://files.pythonhosted.org/packages/c6/c7/4544cc02e45bbfac4d8788b05379bb360021fd8c53fa74b0f624126ac188/multidict-6.8.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:003a3bddb32915c3f67096ea41d24e53edf710edb65a1f5d0c70ab40b0e4d20b", size = 287410, upload-time = "2026-09-09T13:54:01.652Z" }, + { url = "https://files.pythonhosted.org/packages/43/1a/7abed90b8eba381842235bfa6f4d730204fd7deb374fc87e3ec9b2c2b4ac/multidict-6.8.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:35977263d9bf506dbc65349f63b3b8c91606d4abc110990945e3b94bc671319c", size = 255878, upload-time = "2026-09-09T13:54:03.366Z" }, + { url = "https://files.pythonhosted.org/packages/25/3e/73fae10e15fc4d711975337caff7e494c87de5d0189afe3518b21b945326/multidict-6.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9dc7b4ff6ef184504b49ef9a4113d49a646653b2ce89f5f48c1f57cdf6ba081", size = 277831, upload-time = "2026-09-09T13:54:04.963Z" }, + { url = "https://files.pythonhosted.org/packages/c5/cf/01cfc81492933331147004861bdff201d8adeba8485ecd8f490e755fe7e8/multidict-6.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:887f9a975996032c686719eb7b3e1e7942fab5079c2b778bbd9afe9a9d78244f", size = 275096, upload-time = "2026-09-09T13:54:06.661Z" }, + { url = "https://files.pythonhosted.org/packages/de/59/e9a3773b17297fa1e38fd4b3c6f5f2f458380796be62eca7d0d77c250618/multidict-6.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f3071e6515cc63714d014da8f738ae9fa3997c476203f3cd46de380c2376ed7b", size = 279803, upload-time = "2026-09-09T13:54:08.389Z" }, + { url = "https://files.pythonhosted.org/packages/64/9d/2d712a2605b3971908e3b4f5eb6f98c353d9991e106f684d0e08ae581814/multidict-6.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5f3a2af441670d80ce5fdf13b6c1b421fc1fc7fc5182d58ac7486738bb2b742", size = 284595, upload-time = "2026-09-09T13:54:10.17Z" }, + { url = "https://files.pythonhosted.org/packages/58/6c/21aded8586e552b29892268c576e5745d1a894c5451c9866ca3c06b7ec50/multidict-6.8.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:31e8901637e20ccb3cf8f8848b5d0f7a00462bf5b34f7cf3dcbb2753b18e8b39", size = 252641, upload-time = "2026-09-09T13:54:11.811Z" }, + { url = "https://files.pythonhosted.org/packages/2a/70/56a415ae0a45e5eae2ec817d46aeb72a1ae777863621c85f1f39d329275b/multidict-6.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:13967dca8b2f33230a1427b52438326bb1c9101a1df22a3309ed3fcbbb3c96f0", size = 283369, upload-time = "2026-09-09T13:54:13.59Z" }, + { url = "https://files.pythonhosted.org/packages/08/7e/7b7cd611fd94bf2f6bd16244c50495867ba394d5baaf8e6e487d39494ab3/multidict-6.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad474c11d851b6fc97cb625e4822bc0cbd567fc07dc2602e28faec5a36b42bbb", size = 281653, upload-time = "2026-09-09T13:54:15.174Z" }, + { url = "https://files.pythonhosted.org/packages/33/4a/b19a5892ef2ef6c68ae278b4f1504b82e01037baedd92c55d37e55ecad00/multidict-6.8.0-cp312-cp312-win32.whl", hash = "sha256:7bb0dad75068fee80fcb60f88569722c199d8656a16706702dc6e3b786819c90", size = 47936, upload-time = "2026-09-09T13:54:16.638Z" }, + { url = "https://files.pythonhosted.org/packages/29/00/1952f9f282aa71e7c3db3a6b47afb689d0ddf283dbded7e6326a91d421c9/multidict-6.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:7d26dc8f070c0ec5579e987fa615ffd6883086106eefdff9e10d160fc5630630", size = 51723, upload-time = "2026-09-09T13:54:18.05Z" }, + { url = "https://files.pythonhosted.org/packages/49/b5/c9d57dbafe25b8f3460ce2961c968539a81ff7a70160c44dcfd4255cbcd1/multidict-6.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:e6ec7d37841609a691b96a10b4fde386c7cd93ebbb939f59c9f23325ee788395", size = 48492, upload-time = "2026-09-09T13:54:19.42Z" }, + { url = "https://files.pythonhosted.org/packages/84/1f/d7112c2dd7db02677097be72fb65542f51a5aa73cb472b87ec211ba9e0dd/multidict-6.8.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:ec0a4d066356054d569a66e0a94691a2058b680be5e710298f61db11a3c4609f", size = 54197, upload-time = "2026-09-09T13:54:20.814Z" }, + { url = "https://files.pythonhosted.org/packages/ae/24/876015abbcb4a179d946579eb77b778eb5a948fc8381bc7928ba895bc051/multidict-6.8.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:714597cb5d5e15a8a449d2ae23c45b486a9e8fa33c462c7a33d7f35b65d92943", size = 47787, upload-time = "2026-09-09T13:54:22.51Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/ceb7d25f8a567599db2eb19b08cac58d67ff553cff42dcadbea9aba56a20/multidict-6.8.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:e0db3a4d1e264e225037a6023888972c25206a96e016021a5bea41c9a939f2a9", size = 48815, upload-time = "2026-09-09T13:54:23.986Z" }, + { url = "https://files.pythonhosted.org/packages/18/e3/e1c6e9c3818c34b782f23ce5fdba3eaa34ec6750dc53078dfac80fa59be7/multidict-6.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:27747162712e85c84598d364425dbf1714ff335bdb6ba3171c4e5081196e8916", size = 83484, upload-time = "2026-09-09T13:54:25.674Z" }, + { url = "https://files.pythonhosted.org/packages/4a/a0/c23f78a4badee9a5b3e760495c661c62a92c340a1dfd00f829cd16e256bb/multidict-6.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:847d6082ae694dc95e548acb201bc100e1cfa96513bc71fdcb86f709dad6c435", size = 50763, upload-time = "2026-09-09T13:54:27.135Z" }, + { url = "https://files.pythonhosted.org/packages/c4/fe/db552d402a3f6b650f5d3ae11b82b93833836aebb51bcda22d8691121129/multidict-6.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:88a6df88567680504ae28bfa7a1f2f64243d91e79a40b2c92ef42efc531e23da", size = 49029, upload-time = "2026-09-09T13:54:28.483Z" }, + { url = "https://files.pythonhosted.org/packages/01/b4/546853fba19dcef77cdf91fc173faf0b02284a49106cf250511166b4ec5c/multidict-6.8.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:560b211fc3bd4a1e1c6de44f6d38113bf5b410dfc89a4c0d2a3c0edbf1a0dfb8", size = 278863, upload-time = "2026-09-09T13:54:30.145Z" }, + { url = "https://files.pythonhosted.org/packages/ee/3f/4b52dac7db547936eb762123ac1d99df23f92fdb358bae600e322f611247/multidict-6.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202436df907c15adbb94360296c425ea53cf8968a5d2cff9b5b9790ae1972b33", size = 283915, upload-time = "2026-09-09T13:54:31.937Z" }, + { url = "https://files.pythonhosted.org/packages/fd/6e/c0dfbf170e49a91bcb9ce850d51cb98357f3033c5227529200ca7625853e/multidict-6.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c46a08bf070d6849fed483e9d9833f9d06aecb8382ed985be0b38508b3ae958e", size = 260704, upload-time = "2026-09-09T13:54:33.529Z" }, + { url = "https://files.pythonhosted.org/packages/91/02/56973a060ab8dfc2e80bb6797682f6577aff7123cdb1de1a568670ae3499/multidict-6.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2cd560498ae8e1bcc955643c1d78eb8e338226d07a983c656ea8c4443d3eec0f", size = 290243, upload-time = "2026-09-09T13:54:35.408Z" }, + { url = "https://files.pythonhosted.org/packages/d5/67/69112989f131bdea4a87b74e82cb0a2daf37880cd92b0e6f0420020adceb/multidict-6.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:758233648ac47b07c575224c4eadd73c8929c3b4c31e2afcfea935fde1cda735", size = 291131, upload-time = "2026-09-09T13:54:37.205Z" }, + { url = "https://files.pythonhosted.org/packages/c2/75/9435f68b0cfc442d4917de85c26f2b2e1292630883414a25576083fa2469/multidict-6.8.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:122adc7c46ac1e31ecfc7f81b2530533dccafdba70f5d741649f87e336c63384", size = 287551, upload-time = "2026-09-09T13:54:38.835Z" }, + { url = "https://files.pythonhosted.org/packages/13/08/2ee4838081d6587849611aa7ec722c4cb2469e912fd0eaee980e7bac064c/multidict-6.8.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8125e60f3c70e323ac07dd8b3635f7b3bbc5c3a9ac04ae5988f668ff7ae28a18", size = 254591, upload-time = "2026-09-09T13:54:40.806Z" }, + { url = "https://files.pythonhosted.org/packages/94/f1/05673b51191f77f4198b8e4b35f16ea71c0300c72ca8aa027a66a61b6edc/multidict-6.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:83ff054b04915be5c15680da6c6012474a2cc2bf534129a0e8c6a99f17ba7238", size = 278204, upload-time = "2026-09-09T13:54:42.672Z" }, + { url = "https://files.pythonhosted.org/packages/45/4f/b6cf74322b3fbd3e011a1e903730191922291a7779f6d404114c2189b806/multidict-6.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:930c6058047410e3edff445f5a6e4457f2e089042dede00e2d18ce06f3ceae2e", size = 275600, upload-time = "2026-09-09T13:54:44.348Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ab/958bbb04377159ff03c7314cd9d8a48dd6fc4f78c840589c22ab155ee9c7/multidict-6.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:13e26f59f0eecfc5f67c663ad550ffdaf62c0f657547cde387f6c86af1c9449e", size = 279793, upload-time = "2026-09-09T13:54:46.086Z" }, + { url = "https://files.pythonhosted.org/packages/a0/3a/706605ab0dfc4179748ee7949829e63c6f14ae28667aceeefaf2c701807f/multidict-6.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fd789a294d8e098528be29b2669b83005ce569339f8cef167fc0274c3115c34c", size = 284751, upload-time = "2026-09-09T13:54:47.793Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a5/567e36c013ad023546de633079c6b22101dd43226b193cba00e6399703be/multidict-6.8.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3126f2a96704505aa4e92a72d6e8a5d7f29d40a987ced8bf69e29d71dfc71fbc", size = 250812, upload-time = "2026-09-09T13:54:49.509Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5f/6b0b64aa0cd346b07831dabaa6ccda0e73014c5df044b68baa763f0f0552/multidict-6.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:23c9ee89967b6a9b4048acb3b93b660ed714ce9c8bf3bbe652959bc120dc02dc", size = 281606, upload-time = "2026-09-09T13:54:51.288Z" }, + { url = "https://files.pythonhosted.org/packages/31/8c/b846b6796f26d496efb07fedef2b69f6de533da32a56f12d236722a96157/multidict-6.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a62e302fc8cd6aa8972207e7e951d1fdee7c1dda18568305041d19f0e2c00f5", size = 281733, upload-time = "2026-09-09T13:54:53.05Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f3/bf14a39d4af5697fd9404baaf70a0aeeb82d258b95de5cb16b1a7f98ae6f/multidict-6.8.0-cp313-cp313-win32.whl", hash = "sha256:093167d22a8c95af30f597b8a5686f20a14512989942d4be804d119899caca20", size = 47738, upload-time = "2026-09-09T13:54:54.676Z" }, + { url = "https://files.pythonhosted.org/packages/19/0a/598511a5741a3cb374971b3b02eda8a09896118ba528a54795f7e7e8bfb4/multidict-6.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:f25b61a708bd276e8cbb6afcbbf1b8e793a3be70ba0a842d0b8692020f83b706", size = 51609, upload-time = "2026-09-09T13:54:56.38Z" }, + { url = "https://files.pythonhosted.org/packages/fd/b7/6f5c1bd4ffe42d4a6db0f2f65491d4088e9c25c990358fb31a614621d664/multidict-6.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb36381e1f9f9d06eba2f10bdd438e5d20c07d5b55e1a3eee30b9f44cbf52316", size = 48280, upload-time = "2026-09-09T13:54:58.03Z" }, + { url = "https://files.pythonhosted.org/packages/ab/85/153341590e233a967c1d6791a83402d01693dec0f4c1f695606ef16c7ed2/multidict-6.8.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:f8b09b25e0f4dc2ea9e2adbb1cc3ba11a94d6fa3dd978ae659c8743052e1afbc", size = 53758, upload-time = "2026-09-09T13:54:59.563Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ff/44f72d516ece0398683ef52061797d83a74b16b8c1e4587408e97959d783/multidict-6.8.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1f57c414be82490bc0e0305fdb834186229b2d9b6a35fa0afd1eb1a772d125ab", size = 47495, upload-time = "2026-09-09T13:55:01.382Z" }, + { url = "https://files.pythonhosted.org/packages/50/5f/6e118f761b024dd35d26c2fe7ba41572bb0e8ac5f8cfccbbcbc2ff76da4e/multidict-6.8.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:00be37bde741bf60871082cd347a093218c44886e99231b7516671c70f2c280d", size = 48540, upload-time = "2026-09-09T13:55:02.989Z" }, + { url = "https://files.pythonhosted.org/packages/e8/4b/3eed744491b32f0e318e7db89dc06858732362f706e8d045fa9ab51a343a/multidict-6.8.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e37b744849fb631bb52e3dadde35ffeee365a6c41cf71257b5b7acc9cd83fd38", size = 83130, upload-time = "2026-09-09T13:55:04.554Z" }, + { url = "https://files.pythonhosted.org/packages/6d/de/95c2c0ddcccb9a41ffbaa5df8ea059a8ff81916b7617a8847ecd89ed8061/multidict-6.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c2b2a96cf1dd99fe7867be4c013314225f4d5786e6685906e29932d42aca6f11", size = 50574, upload-time = "2026-09-09T13:55:06.387Z" }, + { url = "https://files.pythonhosted.org/packages/f5/b7/f4f4989594f99bc121ad9277090c4e49819b08ab1a96e132b628a9e10b7d/multidict-6.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:bea7df027015856ba5d0a88e3b4777ff8cb5c66b58fc108050fe79d4dd9d4d2d", size = 48786, upload-time = "2026-09-09T13:55:08.131Z" }, + { url = "https://files.pythonhosted.org/packages/b2/86/f1d86a0222f31fb3df8eef3d6c9abf7e8d65d49edd8d0d7e7afaf23d23cc/multidict-6.8.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d3da668e903c934ed0b587ecacfed6901f6ae6384a6e975887592b61845e78bc", size = 276670, upload-time = "2026-09-09T13:55:09.803Z" }, + { url = "https://files.pythonhosted.org/packages/03/50/6945c50f86a978b2bcace9ca344165ff80883be47d984489bbba8fa0ab20/multidict-6.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64eaeda36ee8d88f9e8616a587a8c66a663283cf6e0dcf013c1ddd8c758e4aef", size = 279339, upload-time = "2026-09-09T13:55:11.685Z" }, + { url = "https://files.pythonhosted.org/packages/ee/2c/e649889ba23fd1f4442a85427b99d9e6261226b2ac31914aa7f5b241d947/multidict-6.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ac746cb365bac1c462da9e3e6ab8904a8efe2217a56b0b2e3d9480f41d2b2602", size = 252549, upload-time = "2026-09-09T13:55:13.527Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f8/1023b66e011b1395fb160dabb0f0608ef67e569f0bdb2c1d5ac9b2f2adc6/multidict-6.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:18f0e06360c3e451a3ab800355773c8d125a758238d780c800b0ee5e90ee903c", size = 286203, upload-time = "2026-09-09T13:55:15.19Z" }, + { url = "https://files.pythonhosted.org/packages/7e/6c/48aea545cbda6d0444848ec23d988c13b86538a00a1b7d3868cc2382ff94/multidict-6.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:69708fecaa88bcb2341397b49fc95057a835b02a3670c551b37f95dd79e64e3a", size = 285039, upload-time = "2026-09-09T13:55:16.928Z" }, + { url = "https://files.pythonhosted.org/packages/68/2a/066123b17291671bf67d2a5c65ee81a48de53913bd1b1578791519eacdb0/multidict-6.8.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9606f583e7acaf61e7b3f56074e14037b9af7cb194590edfc0114b3ae5931ff7", size = 281075, upload-time = "2026-09-09T13:55:19.155Z" }, + { url = "https://files.pythonhosted.org/packages/47/20/4f0b2c485da2e8a659cc677717a3745872918c9c85064491a1ef75d7a3bf/multidict-6.8.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1f66fe6a021173d0d47968491791966b9f3e6d61115f2491744aa0c07a6e67af", size = 250431, upload-time = "2026-09-09T13:55:21.07Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c7/b9a288901577aa0b82c33c64d52246c88076d260ad7b6c16b021ca0f8e99/multidict-6.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:be007d1aee2cbd530347dcafedb400891a3b5f1bd7135f95cf5d5b330b5219ee", size = 273891, upload-time = "2026-09-09T13:55:22.887Z" }, + { url = "https://files.pythonhosted.org/packages/da/51/0ba50cab2cfd067988de2abb73f23076ac727fe18d03f1368a59def64727/multidict-6.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8457aff3c12a89a8e1c4674de5c777857fbc429f40fe117a3d29538547cbc364", size = 265262, upload-time = "2026-09-09T13:55:24.77Z" }, + { url = "https://files.pythonhosted.org/packages/0f/d6/e5be1117dbca6eb9ce231142b7e20599418bb3500147db51bf844ce8afcb/multidict-6.8.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:95c27b4f3f04320fc44e338573f40c5c956b504a7fcf081a157fd0b02579311c", size = 278033, upload-time = "2026-09-09T13:55:26.67Z" }, + { url = "https://files.pythonhosted.org/packages/d2/28/cad0afaec3caa56ea2c1ceed43c164d62ad3e83e950daf0d0c87bcf9dca7/multidict-6.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:d244cf6b52b5ba1c34c3832f4652a668ebb36d95949b96eed9a1c54d916a90dd", size = 281717, upload-time = "2026-09-09T13:55:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d2/025702df0b69b856db70a4d66f77622f51c3d99771ec9a07f3ca80f7e098/multidict-6.8.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5bbbb696c8024475b1877d14ce20d5f1cc05b8f6d786cea0fe3aa7fedc02e891", size = 247124, upload-time = "2026-09-09T13:55:30.497Z" }, + { url = "https://files.pythonhosted.org/packages/b4/96/9dddca563f06a921956389c0bc9b894355b98b0bdf62299e2560c50afb6d/multidict-6.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:cbd86f9787c5e2f5fd27d8b21458222f107347c6731c4e93dde68f554b466a2d", size = 275954, upload-time = "2026-09-09T13:55:32.57Z" }, + { url = "https://files.pythonhosted.org/packages/ec/91/8b2f1f2a774a955665f268340a2b59db7020c5f12baac02ae9ef1b1660cf/multidict-6.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2f8a4b0b4d639d525928c7f30de527bfdf9ead6e44a5e8cb9c50aced5e4590cb", size = 275508, upload-time = "2026-09-09T13:55:34.368Z" }, + { url = "https://files.pythonhosted.org/packages/b6/1a/e2cabdfc0880a61a99d2b8bc361035036fb5a2c6af31ea3fa054ba1065c5/multidict-6.8.0-cp314-cp314-win32.whl", hash = "sha256:8890c89d662560e51c55ac1304d6f919b23942abe9ae1127cb1de9aa6132fa52", size = 46938, upload-time = "2026-09-09T13:55:36.057Z" }, + { url = "https://files.pythonhosted.org/packages/b9/7c/11234bcba62c22a58f2ba168499cfe3531f49de3edd5090d04a8c6cdc936/multidict-6.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:45cc39ba50fb0754a4359b90f8229ae08598fe2266abe3521b4e5a9ba916534a", size = 50291, upload-time = "2026-09-09T13:55:37.698Z" }, + { url = "https://files.pythonhosted.org/packages/ab/61/793668439df924752a8137d6db0de97ed1add494779b01e4764dfc60571b/multidict-6.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d0264f8d5cb0a803f650a6a8572dfa0cd1e099a2234c588dc8fb220b415b865f", size = 47622, upload-time = "2026-09-09T13:55:39.335Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b8/3c091b929e6b5b2f6e0eba2232178e76d4503c8b96b92dfc281ff1d823be/multidict-6.8.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:1969971900b0871530f9b62280dcc2d75688e74d2a69262bc01faf2b96c78f04", size = 88789, upload-time = "2026-09-09T13:55:41.086Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d7/3df83fab22dd64615db71e3b3cc1346b581d1459719637ce52144f9f6558/multidict-6.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:8180b635290a75af8478f1b3e9810135381ae24833293fe77b85c1c21ff842ab", size = 53399, upload-time = "2026-09-09T13:55:42.685Z" }, + { url = "https://files.pythonhosted.org/packages/2d/78/41bd04c04b0aed16540c4856c9e012afc1c254298da154398308df05e26a/multidict-6.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:4261863fc8b5ab1b815ede94e592e94c6af5b04616014929057e61859e7382a9", size = 51597, upload-time = "2026-09-09T13:55:44.569Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6b/7bc4cdddf624e1e7e0231734b1331729ea46df10d7c8fd3fce79756e7d0e/multidict-6.8.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0b143d53590e89f43153d81d505a8448d4d57354354385aef8a51d67ffefa27e", size = 264391, upload-time = "2026-09-09T13:55:46.548Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b6/d2a946e5938771e92c39354563e535ef6bc6dfe399dd4307c6df8dfea183/multidict-6.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da1c112c5784ccd9d32cd90be6739fee32644e874eff6ae8f0497cba3e352e58", size = 264680, upload-time = "2026-09-09T13:55:49.915Z" }, + { url = "https://files.pythonhosted.org/packages/33/6b/3f9e981c42e7eb9329918523f0f9362ceb0ac3ee0ee1165c28f674249d75/multidict-6.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f7eefd0233a7c33ca980a5cfef26f1e9b5e2137839e752a99963696729f12d91", size = 235420, upload-time = "2026-09-09T13:55:51.92Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e1/a3a33a039fb6d381800ae5d1d587b697b8c27fcdfe48819420f08703acba/multidict-6.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:348bb85e2038b40c007383616d73f734869063772372519549ebd7da1723d1a4", size = 270309, upload-time = "2026-09-09T13:55:54.023Z" }, + { url = "https://files.pythonhosted.org/packages/95/5d/8b06724a957f2e480f159b9550988a67810fbe9555a09c5f6a2a4b829607/multidict-6.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:095f62ea4e7a3be2f6c567ab695ce10e950f2adb905c1bec82281593e0b2d2ad", size = 275169, upload-time = "2026-09-09T13:55:55.948Z" }, + { url = "https://files.pythonhosted.org/packages/ab/32/8f3dfe2ffa5d0df2a95f71e63c2f11fe3b5e1771f26ef73bb1af84de83f8/multidict-6.8.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be569fff1d85cd29391c431c5641c8772acb75bbdc61e60a8e82fceb9023d385", size = 264900, upload-time = "2026-09-09T13:55:57.803Z" }, + { url = "https://files.pythonhosted.org/packages/6b/73/d5829fc00a055d6ab445e0876346ee9cdee670766cd4190dc0a496188c0f/multidict-6.8.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3533a03e4e789baf6a286e7b0b1b6da3f3d7c3eab569686ee29ee1d8b52e2cb4", size = 242486, upload-time = "2026-09-09T13:56:00.002Z" }, + { url = "https://files.pythonhosted.org/packages/b7/58/e8d7874038e31e0533182d1c3c5331a856b9c849a71bb26a21850e8c91e1/multidict-6.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1bdb9b8fba5a9aef673ec90db3f55b1ce743f2fbdea4d37dc04d14ccdfc153ff", size = 259916, upload-time = "2026-09-09T13:56:01.802Z" }, + { url = "https://files.pythonhosted.org/packages/4d/f8/1b56a7401acda20efc016440f4fad3bef66c4aee54ca080ec143881ebb0d/multidict-6.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8d7b66c9e09c0bb0add2b5895e646b62a0849e71155066f215523de6b95cbe6", size = 251209, upload-time = "2026-09-09T13:56:03.767Z" }, + { url = "https://files.pythonhosted.org/packages/bd/5b/68d67a9e302b0645a747ba910c30eb41f2834fcdc1d85f53eae2dfceee0a/multidict-6.8.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:563d6500ca80dac7bba6f48a78e0ffd87e21a7d4d24642c6503a2ddccd70c110", size = 264505, upload-time = "2026-09-09T13:56:05.795Z" }, + { url = "https://files.pythonhosted.org/packages/18/13/4dc304ba2c5f5307b474ab2ce1ed1f6b02b0b4e233c182e3981ed436c2e3/multidict-6.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:346ac52e56bcda320c0dcdfdd081947ed7cada33afea4e2284bef7b0733bff9b", size = 264916, upload-time = "2026-09-09T13:56:09.079Z" }, + { url = "https://files.pythonhosted.org/packages/dc/0f/7b1f729d18369915009185201be5d0b8df0e525340fe6a600d2f8441d6cf/multidict-6.8.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4ee953a5ebaeed38dc21cc032ed17a9d9782802e00042200497ab4b01b0bf7c0", size = 236839, upload-time = "2026-09-09T13:56:11.273Z" }, + { url = "https://files.pythonhosted.org/packages/22/d1/eba1b88b18b7019d9136303fe77909257c40fabde5aaf138a4d900b6ce3c/multidict-6.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:2f79cc3e8039a8cf5c77e0811b0807953fd52d0863b9b76970b20d696dc64a78", size = 265307, upload-time = "2026-09-09T13:56:13.379Z" }, + { url = "https://files.pythonhosted.org/packages/aa/a6/6c1e4106faa27118ac612f4d664eaf909de252634785286262a627108e58/multidict-6.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:43a4b56555bbcf8af161e7c7682bd93eec10f068c95844511864c018c8e5e13b", size = 259041, upload-time = "2026-09-09T13:56:15.666Z" }, + { url = "https://files.pythonhosted.org/packages/c0/bc/ecfb8b6faa8e158a71b03bdf7f947f30e0bc5d899cc357573a76ab7bb1e5/multidict-6.8.0-cp314-cp314t-win32.whl", hash = "sha256:48ea524a25a1cd5972cf293bc95713918cba0bcd6fa9b992d906c857c546abe2", size = 50628, upload-time = "2026-09-09T13:56:17.837Z" }, + { url = "https://files.pythonhosted.org/packages/30/7f/e27fb699b70ad24dbd02ddee604658acb36f907c03c045baffe4ea774501/multidict-6.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d0be2b832435001bc623ca7f1499ca1a853d4f082fb61221a80ce71132f50b26", size = 55592, upload-time = "2026-09-09T13:56:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/eb/7a/76de70b2f6733696803f1ee56abe44a3757a52777383032c7373d3fea0f4/multidict-6.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:62b8e291a4f7edbf7cde7a43d831d893ba443a1b627498b53581943b0e348feb", size = 50300, upload-time = "2026-09-09T13:56:21.516Z" }, + { url = "https://files.pythonhosted.org/packages/ce/32/4de7320ae032dc768090d11f708d2d386df3db04cb6b8b0db0230cfc66c3/multidict-6.8.0-cp315-cp315-android_24_x86_64.whl", hash = "sha256:e192018b732f7b168e6604cbdf40fa8e05c996693b9eb445a0d8a73f4b77c5d3", size = 53761, upload-time = "2026-09-09T13:56:23.192Z" }, + { url = "https://files.pythonhosted.org/packages/5c/45/ecb641309dc2cdc6040f18e22c68eb5e94398f9404c4365d810f4292e053/multidict-6.8.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b25426f9f6ed402835617c8f23609a47045f91ecff365eb6734817e039a8ed25", size = 47505, upload-time = "2026-09-09T13:56:24.902Z" }, + { url = "https://files.pythonhosted.org/packages/eb/68/87d6161b9fef11943e0b894203da3fff561933ca3c9b2952b6e7100e9c9f/multidict-6.8.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:fa6c2880709c84457de104385b704fc28860f27e442ad13966fc4af8e714fe9c", size = 48549, upload-time = "2026-09-09T13:56:26.574Z" }, + { url = "https://files.pythonhosted.org/packages/97/f7/d852d2276407640cdbd29fe11cac6e93f70f59542cba174ef9d146738946/multidict-6.8.0-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:eabb03dc3e4ed6333ecd1cc9826ec80e7a98b5506deeb832d7260c8e44166d23", size = 83157, upload-time = "2026-09-09T13:56:28.227Z" }, + { url = "https://files.pythonhosted.org/packages/14/e3/16fe7ffa6090591d83cf6bc2486e77ce891705fb6d0191823140928311b5/multidict-6.8.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:59e539c4eb4d3a53b0e630a6ba2b2f2824732b5e73f90e30a280f12fde157b15", size = 50578, upload-time = "2026-09-09T13:56:30Z" }, + { url = "https://files.pythonhosted.org/packages/d0/0c/e38e41c1087a599f86ff58a01f358abf7c4db3c26a3e90eebb3e02193ef1/multidict-6.8.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:835d5a90b11d1f5f8200ff3cc8316bded76eebebc92436398947a27657e645e7", size = 48815, upload-time = "2026-09-09T13:56:32.056Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f0/eb691f42af8e7775992f57904ec75dc356fc7cdc896e5f30879decdd26f2/multidict-6.8.0-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d2d236b8a44ae91536a12ebcb996bdb31cf27425f36b4d05c87f2ba2716050ba", size = 274804, upload-time = "2026-09-09T13:56:36.741Z" }, + { url = "https://files.pythonhosted.org/packages/87/05/28472ccfeb43c00a043c0385ca4294da21a5957859fb7860e2ebdb3e3011/multidict-6.8.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb9a60b7faa5d37c426fa91cf4d6738182a1f2755b9fab7c9c64cd466c4ce51e", size = 279693, upload-time = "2026-09-09T13:56:38.531Z" }, + { url = "https://files.pythonhosted.org/packages/f3/a1/2b4fe73e5fecff807b47650a155c391a103136428cb21d6ba8e39c5912b5/multidict-6.8.0-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0ef606c15cac6c90279acf34120784b6f36662cbf382defd3955cd8f1115336b", size = 254969, upload-time = "2026-09-09T13:56:40.407Z" }, + { url = "https://files.pythonhosted.org/packages/44/e0/c97d1822783dfe52e02fd150fa3f02eb22410211a9e2615f71541803ed4b/multidict-6.8.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:68186a2d4051c8ffd17be33553bea2ec9bbc8ef860fe2980a221d96126296f31", size = 286392, upload-time = "2026-09-09T13:56:42.234Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d9/772f1339e1d051236bcc137b0eac2b4aaaa0bbb56aaf924e9aaba901d9c1/multidict-6.8.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2cc66abb85e2108c9ff8a1c0d20fa260bf690bbb33caef4ff3ecb2c2cbdfff5d", size = 285348, upload-time = "2026-09-09T13:56:44.255Z" }, + { url = "https://files.pythonhosted.org/packages/06/ae/cd045747e4680362e02955a82c468e95b5e4d319e3a79574b3fb677de568/multidict-6.8.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:69b3e519a132bb943b0daae15fc8c2168706b17f826481d32a32a5e784b129e3", size = 282721, upload-time = "2026-09-09T13:56:46.088Z" }, + { url = "https://files.pythonhosted.org/packages/35/14/0802d9a3aae4ef21eaa39adbd729a380fa095932105e1424e417b53e783f/multidict-6.8.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e41226ecf607f062fe34a2f4cf64ad3a89e3a0180dc800b463b6b14c06dd10dc", size = 253168, upload-time = "2026-09-09T13:56:48.07Z" }, + { url = "https://files.pythonhosted.org/packages/b1/64/3f92298bab8fbe1332e708863fb55b66e755be6f416b3459720d48b33af9/multidict-6.8.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:610c7637bc36b90f39e6c66f710f93d57018f83d53e1e187caaa218c6892b95f", size = 274209, upload-time = "2026-09-09T13:56:50.023Z" }, + { url = "https://files.pythonhosted.org/packages/08/c2/2001ac0eac1a8b7390a5902d7115f66d4f256268057a502200b6ab12dad7/multidict-6.8.0-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:65c85c79f5a2c04fbbc18f006c014674dc5fdf270cb978d8862c82c6f694e60c", size = 268044, upload-time = "2026-09-09T13:56:52.033Z" }, + { url = "https://files.pythonhosted.org/packages/0d/90/78a9e26c85f89abd562a67f7fcbaef9007fd5c37bb9efac19f1cf604e7c2/multidict-6.8.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:628ff11e6720f90acd0c305dfa3339f04a783a20de8cda6ac333ba46447261e8", size = 274806, upload-time = "2026-09-09T13:56:53.975Z" }, + { url = "https://files.pythonhosted.org/packages/3d/71/713bd445421b21531234c1f3630b768192cb9d80c8b1c5b05c5b505ff4c0/multidict-6.8.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:0935971bffd0b479fc90c4811ca787703e93fcb6afea939a375dfc80285ab368", size = 281890, upload-time = "2026-09-09T13:56:55.848Z" }, + { url = "https://files.pythonhosted.org/packages/de/a5/1387c538663e2dc8c27bbc7cd6955cb66de0f55c780cf7cd0fc06a1a16ca/multidict-6.8.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:9442b14eec262a1f74369bbd07e75bc5155105164649a4b9fbc1ebc7b8fb0b14", size = 249749, upload-time = "2026-09-09T13:56:58.01Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/104296c9d70896b9759ce0812aa4899fab76d8b16bb32dcc5a78ab547c89/multidict-6.8.0-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:397599503b718f0137f26d3f6532d6955069cd2e5917c47ef581495bc2529ff8", size = 276138, upload-time = "2026-09-09T13:57:03.591Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0a/f2a0c2658e9d7ff5964ec2820a02054558636fafd663230ddc8310b8ed39/multidict-6.8.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:9e37024b41d7a7e7e9cce14b248d54707c21c2a2ea30a47b71bdcefcafec00f2", size = 277077, upload-time = "2026-09-09T13:57:06.024Z" }, + { url = "https://files.pythonhosted.org/packages/98/50/bc46566caffba5c1c4a510519156371edf7c4ecd35c9ef917d0c1803487d/multidict-6.8.0-cp315-cp315-win32.whl", hash = "sha256:071da134651b04a8507dfb331ac0988f376337c2aea59486bf20989fb5b5a64e", size = 46930, upload-time = "2026-09-09T13:57:08.009Z" }, + { url = "https://files.pythonhosted.org/packages/6f/1a/cafb31049ecc1a6ce52bcc69fa436cca239adc057b1718a0c49044848663/multidict-6.8.0-cp315-cp315-win_amd64.whl", hash = "sha256:3bafff8598f0528017ddc74194e5451d5c22d046c98935f8f86247b0f286e4f8", size = 50294, upload-time = "2026-09-09T13:57:09.986Z" }, + { url = "https://files.pythonhosted.org/packages/6b/51/00e037da14cd1d894b123e0bbe62de5c561679a6ab23ab1c009f2965dcda/multidict-6.8.0-cp315-cp315-win_arm64.whl", hash = "sha256:e886ef8c9879105fe4fc99417447b3a5f35d1131412ce839470bd2089fe2043f", size = 47626, upload-time = "2026-09-09T13:57:11.738Z" }, + { url = "https://files.pythonhosted.org/packages/52/f7/aeb947982197e8b4f5c4da3961ee473ea5a050b94a6ff3b88baf64621401/multidict-6.8.0-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:883284137e25318ed9735b742ae46341a864888fae28e8b6314c4f84da080f08", size = 88801, upload-time = "2026-09-09T13:57:13.957Z" }, + { url = "https://files.pythonhosted.org/packages/35/d8/593948c016c3f850e3cd56a4e0144151eb409d2b8690f0c0ce7f7d33dbea/multidict-6.8.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:ca52b9ec80851366197577154c862c4c4c7036ca76ae94cef5cb59c5cfeab944", size = 53376, upload-time = "2026-09-09T13:57:15.94Z" }, + { url = "https://files.pythonhosted.org/packages/58/b9/097a05bca533027c0477b6a90bf927dbbb4b23cc9090bbb37a2e972af8d5/multidict-6.8.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:91fa75d0a693832106d98f66c849f034f21c828d14437f1fb97d3784aab89e84", size = 51629, upload-time = "2026-09-09T13:57:17.685Z" }, + { url = "https://files.pythonhosted.org/packages/fe/07/938ed21967f12380d0b8861645fb65a942f3669e31d5163ed94d23103b61/multidict-6.8.0-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:307c1acd812fe897e7fbe10c6758822e8c04be4e7c60a9f54901cdf8b5ab8bc3", size = 261967, upload-time = "2026-09-09T13:57:19.752Z" }, + { url = "https://files.pythonhosted.org/packages/89/e8/e66bf843fd29c01712dde9edeb9f4ad0ffab06ab4ada4b721ad7bc73b3d5/multidict-6.8.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf684986a2490628f059a99dd107b566a2d34cf947f8eb8387e0500a1f90c5", size = 265923, upload-time = "2026-09-09T13:57:21.784Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f8/e9be849b225af28a8eee2c6bfea23594a777c753fe97e2ff7e2180c8935a/multidict-6.8.0-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:77745725125d01fd613b6db043362aa7c6bfbfdb23d45dbfc3d92bf58160af62", size = 239380, upload-time = "2026-09-09T13:57:24.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/67/4dbad08f5081978c591afae9e836ec9ddae90e9e76be6d6ce10757483dc4/multidict-6.8.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8daafaa0b2eb43f76898ced78b1e0fb91b38c4fa50da516c18067f2a2d578c20", size = 271591, upload-time = "2026-09-09T13:57:26.611Z" }, + { url = "https://files.pythonhosted.org/packages/92/3f/e9c97222d7e104e54e556f118ec7d091ab41a0c10c630f2b97e5b43f5404/multidict-6.8.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c68e0c0649d17c2d0339e3674e86a4aeba4a7e6b21c1e394cf947a95433b31d0", size = 276091, upload-time = "2026-09-09T13:57:28.997Z" }, + { url = "https://files.pythonhosted.org/packages/26/ca/728e7ce05ac9c0303554e7162e74d91fe49e65bad7dfbb377f783dd32c0a/multidict-6.8.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d8a5ac357ac283490a8d1899b0383355fd1f8634b14ba0d59e4c0dd97db85556", size = 266493, upload-time = "2026-09-09T13:57:31.256Z" }, + { url = "https://files.pythonhosted.org/packages/8f/74/7c658d2769863af16fb7d7c6be50b29659892a06a632858863eee3a31842/multidict-6.8.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:46029e6e27a3ec0dc55b53f58df82d10f04c5e111f78248279b530bedad2c30a", size = 245302, upload-time = "2026-09-09T13:57:33.464Z" }, + { url = "https://files.pythonhosted.org/packages/2d/2c/d4350a20a0e8c66a447d694e8713438262665203fe826c3f4e385f052b72/multidict-6.8.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:8d1046b5427dcafe6e8a0e07527dd74f1ee694006160162f53f3a17f15aad3b4", size = 261016, upload-time = "2026-09-09T13:57:35.651Z" }, + { url = "https://files.pythonhosted.org/packages/1f/78/83df999c8beb72a012cfac42f2b833c4a48f8e836fd4407747b355a2430e/multidict-6.8.0-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:f1f4a220db6ed7c8fd16b6d644ffd1f082651693204daf3275e049fadc849e39", size = 255021, upload-time = "2026-09-09T13:57:37.758Z" }, + { url = "https://files.pythonhosted.org/packages/fb/13/f2c0a2dac6d91f74aa124f3e9f07ec497ceae5ed2df2753d249601cd7262/multidict-6.8.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:029897732a9c798737457e382bf84e8c64237eff224a90aea2639f4413c45e4e", size = 263066, upload-time = "2026-09-09T13:57:39.897Z" }, + { url = "https://files.pythonhosted.org/packages/59/1d/730008d4639ace731bbb1399e1ac13cbdf506f7d6fb861d75044ffb3994d/multidict-6.8.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:29be9fd289e9ab8f480996ea2f686e1654b80242033843cb11691688329423f1", size = 266510, upload-time = "2026-09-09T13:57:42.39Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/bec67a5d206dc5748e50c93f6f71deec14305c3657cfe250c3887caf7839/multidict-6.8.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:29631224698de1e42abc8fa7658d830e0aed0029785144b5832b695da5adef2f", size = 239423, upload-time = "2026-09-09T13:57:44.304Z" }, + { url = "https://files.pythonhosted.org/packages/9e/db/5f153fe51fbac7d80f3bb8bd6fab8db8b6cd061e7a11371676dfed3712bc/multidict-6.8.0-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:962f18c59a000f30b084ea2e6b8001521bb315efd4e5f10acf9fb36f366b7882", size = 266902, upload-time = "2026-09-09T13:57:46.297Z" }, + { url = "https://files.pythonhosted.org/packages/89/0f/9efca48a351551de4dc0c183f523109dbe87c645a4732d5f1c70b4880dca/multidict-6.8.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:c60e50bc5b07faac92fd3a20fa21cc8cf3e3f7204d2867b206c73293ebc19101", size = 260887, upload-time = "2026-09-09T13:57:48.268Z" }, + { url = "https://files.pythonhosted.org/packages/df/d8/bb879a62e0809448e53f6237e71670066ecf3bbc5896a7a6705b6628d86a/multidict-6.8.0-cp315-cp315t-win32.whl", hash = "sha256:fc5460940f50dff00731b4132366840ba9685286ea88ea104b661899084f3fea", size = 50533, upload-time = "2026-09-09T13:57:50.31Z" }, + { url = "https://files.pythonhosted.org/packages/fe/62/3e5308d8871636e4b9620e4b3acfcf2b5caf79b19d317690ec13f7fc8b57/multidict-6.8.0-cp315-cp315t-win_amd64.whl", hash = "sha256:b367c342327717d644db4c0ddb37ceb655c84822215ea0773a3a36911b74b71d", size = 55572, upload-time = "2026-09-09T13:57:52.301Z" }, + { url = "https://files.pythonhosted.org/packages/b9/cc/d3c10e10ee3bb7a7b4abbb3157306b2ce7e0018c9c2d16b32b468739d2b7/multidict-6.8.0-cp315-cp315t-win_arm64.whl", hash = "sha256:0c1c4debad7337627b86837abdf0237ca3cb3d7e17de7eab0177c263878546d4", size = 50322, upload-time = "2026-09-09T13:57:54.099Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ee/be4e1a4b7a2b27f4fb6936510d4bebcb41b0562c946930ad26916e069cf9/multidict-6.8.0-py3-none-any.whl", hash = "sha256:75daa15ca16d6285eb2e104b2f05ee6f8d9836c68da3ce5c85f615a0450eed0e", size = 16297, upload-time = "2026-09-09T13:57:56.106Z" }, +] + +[[package]] +name = "multiprocess" +version = "0.70.19" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/f2/e783ac7f2aeeed14e9e12801f22529cc7e6b7ab80928d6dcce4e9f00922d/multiprocess-0.70.19.tar.gz", hash = "sha256:952021e0e6c55a4a9fe4cd787895b86e239a40e76802a789d6305398d3975897", size = 2079989, upload-time = "2026-01-19T06:47:39.744Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/45/8004d1e6b9185c1a444d6b55ac5682acf9d98035e54386d967366035a03a/multiprocess-0.70.19-py310-none-any.whl", hash = "sha256:97404393419dcb2a8385910864eedf47a3cadf82c66345b44f036420eb0b5d87", size = 134948, upload-time = "2026-01-19T06:47:32.325Z" }, + { url = "https://files.pythonhosted.org/packages/86/c2/dec9722dc3474c164a0b6bcd9a7ed7da542c98af8cabce05374abab35edd/multiprocess-0.70.19-py311-none-any.whl", hash = "sha256:928851ae7973aea4ce0eaf330bbdafb2e01398a91518d5c8818802845564f45c", size = 144457, upload-time = "2026-01-19T06:47:33.711Z" }, + { url = "https://files.pythonhosted.org/packages/71/70/38998b950a97ea279e6bd657575d22d1a2047256caf707d9a10fbce4f065/multiprocess-0.70.19-py312-none-any.whl", hash = "sha256:3a56c0e85dd5025161bac5ce138dcac1e49174c7d8e74596537e729fd5c53c28", size = 150281, upload-time = "2026-01-19T06:47:35.037Z" }, + { url = "https://files.pythonhosted.org/packages/7f/74/d2c27e03cb84251dfe7249b8e82923643c6d48fa4883b9476b025e7dc7eb/multiprocess-0.70.19-py313-none-any.whl", hash = "sha256:8d5eb4ec5017ba2fab4e34a747c6d2c2b6fecfe9e7236e77988db91580ada952", size = 156414, upload-time = "2026-01-19T06:47:35.915Z" }, + { url = "https://files.pythonhosted.org/packages/a0/61/af9115673a5870fd885247e2f1b68c4f1197737da315b520a91c757a861a/multiprocess-0.70.19-py314-none-any.whl", hash = "sha256:e8cc7fbdff15c0613f0a1f1f8744bef961b0a164c0ca29bdff53e9d2d93c5e5f", size = 160318, upload-time = "2026-01-19T06:47:37.497Z" }, + { url = "https://files.pythonhosted.org/packages/7e/82/69e539c4c2027f1e1697e09aaa2449243085a0edf81ae2c6341e84d769b6/multiprocess-0.70.19-py39-none-any.whl", hash = "sha256:0d4b4397ed669d371c81dcd1ef33fd384a44d6c3de1bd0ca7ac06d837720d3c5", size = 133477, upload-time = "2026-01-19T06:47:38.619Z" }, +] + +[[package]] +name = "numpy" +version = "2.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/01/11703282db468b85f6f7b8c7f22d058de5970d5c7e60a3a8aaa313c3de36/numpy-2.5.3.tar.gz", hash = "sha256:df2d5874ff183595a4ba404edd04f6bd9b5505c1d7708573f6a6c17489a67563", size = 20791231, upload-time = "2026-09-06T16:27:47.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/50/8fdbb16af64895706a45f06a4068e29db732ec180f3c1375f14123359138/numpy-2.5.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb189f09db39283b26bfd061ec16189e14f71c6755207f72a0f7540867afe5b9", size = 16994982, upload-time = "2026-09-06T16:24:29.244Z" }, + { url = "https://files.pythonhosted.org/packages/60/39/789131c1188c078dcb3a1692e72e1e050c68b88ffe72c9ccaac9bcd7a9cd/numpy-2.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f59a878c33d6b88122d80d239bb3b845d58708750b0cb06a09aebb9b18ec696c", size = 12009327, upload-time = "2026-09-06T16:24:32.491Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/a312e95696e5f601914dd8b6dd844692ba61670807417e24b68e337b5c70/numpy-2.5.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a72f874bc9e10e4b8f80426fb49716d5141f64442a0c8418065093ec8017fbb0", size = 5445405, upload-time = "2026-09-06T16:24:35.071Z" }, + { url = "https://files.pythonhosted.org/packages/30/d0/5623a1707ed4fe16e3909fe3cf5ee3da004ae677ad23d83bbf3adf1a6faf/numpy-2.5.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fc36dc566135b5eceec4cf89758fcb719266a019ef07dae1754ae7c9f617ef3e", size = 6783213, upload-time = "2026-09-06T16:24:37.253Z" }, + { url = "https://files.pythonhosted.org/packages/f1/32/84146fc020ad3c25f805f70ab60da46fe3c540a21369754a7e4369754b6f/numpy-2.5.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76c2c1e6bfa5c84adc6434dfbf013aa92096a7985221762c8f11fedfd20fff58", size = 15687872, upload-time = "2026-09-06T16:24:39.751Z" }, + { url = "https://files.pythonhosted.org/packages/65/af/aa78d1a88805456e212b65461354cd943197fb9acecc4c90fd12295123a3/numpy-2.5.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7e18c623bb5c95acb3b3328861272816ba199fb531921c5d6d0b675f1fde9e3", size = 16717410, upload-time = "2026-09-06T16:24:42.745Z" }, + { url = "https://files.pythonhosted.org/packages/3b/24/faa79d865e69a97ba17473b23a1b74094b2259c03e820c70297293b9ea49/numpy-2.5.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4f8929ee6c96bfbd7b4ed2032e0c03af86fe1826740ab61ddabf9072d06e57ff", size = 17040975, upload-time = "2026-09-06T16:24:45.961Z" }, + { url = "https://files.pythonhosted.org/packages/62/4a/8877e629445a7176297dffcaf9c485faa96a95d81728a62521ad55bd4c0f/numpy-2.5.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b5d93cf48f687479941d12b69c873ad2cc76bbd487f0091c2200636497f34034", size = 18476479, upload-time = "2026-09-06T16:24:49.35Z" }, + { url = "https://files.pythonhosted.org/packages/c8/db/35e1c2d38b04cbd5b731f9d71495e055e813197669d22b612f11748d2ff9/numpy-2.5.3-cp312-cp312-win32.whl", hash = "sha256:bf63afbe037eb5d2fe87fbcc7778e61da53ebaf21d938a4515aa73b62532a5d4", size = 6133378, upload-time = "2026-09-06T16:24:51.915Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/accf6d4f0c80c5d9ba9735d6b1550e444180599f34dec69ca01360f717ad/numpy-2.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0a59a421a32580a009e8a1751345bf829631b990dc1794b80514ab722b435def", size = 12567828, upload-time = "2026-09-06T16:24:54.255Z" }, + { url = "https://files.pythonhosted.org/packages/22/43/1764aff32e4652526ae2f71fa8b3efd8d25c8a3d6926914454e47138ed1e/numpy-2.5.3-cp312-cp312-win_arm64.whl", hash = "sha256:ccb32e0525d29e8b0572eb84c9a57af0e7a4e615726927506f55063c62414034", size = 10485432, upload-time = "2026-09-06T16:24:57.278Z" }, + { url = "https://files.pythonhosted.org/packages/79/e5/8fb89cd46d14e35699d13bf943a5f5f441ecee8667120a1f6105ab89e349/numpy-2.5.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:66a78fe4556c60aceda5916f9eacd638b18e9e681016ec302dcb4682d6d4d034", size = 16991061, upload-time = "2026-09-06T16:25:00.411Z" }, + { url = "https://files.pythonhosted.org/packages/2f/06/9dc9e48b5e5e941c8b10350c5ff2d721da42a20517d911d15544246775ff/numpy-2.5.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92f30e89b8ee0ecf363033576c422b2f58fed6a80bed0aa48dff6d14c654663e", size = 12003676, upload-time = "2026-09-06T16:25:03.475Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2a/98282aa5b8f58b1157d440bb6282eed47e3632a5de53a714fbab17e659fe/numpy-2.5.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f9a2353b37a1a9e78fd82b27ad7e2a32a2d036604d18f02b05e3136c62ca3b09", size = 5439695, upload-time = "2026-09-06T16:25:05.978Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f9/b6533d777be9d6ffd29dc1be0867e563e6e8cc9a220ff1b716adc317f060/numpy-2.5.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:ccbc4665079665c3cf3bab4db9f6b095370cd6437d66be549b6c2a1fd19e1958", size = 6779395, upload-time = "2026-09-06T16:25:08.599Z" }, + { url = "https://files.pythonhosted.org/packages/73/85/735720d04ec197c5dcfacdfc9922667c7f1f5f496a279b7ba4d7c74c4cc7/numpy-2.5.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c76d5dde9f445058f83d0c02af00557a4db91de9a9a57c0df87d1535001d654b", size = 15681750, upload-time = "2026-09-06T16:25:11.173Z" }, + { url = "https://files.pythonhosted.org/packages/3a/1b/3b16a9bc514a440a7a0883684111dcb1ef1aee960af2ca95da8fc775f124/numpy-2.5.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5fa86b80fd24bcd1aff83ad23be44ea323de3f787be8f8b15d4a65621e25321", size = 16708577, upload-time = "2026-09-06T16:25:14.171Z" }, + { url = "https://files.pythonhosted.org/packages/69/c4/386f397831b07328b639c96c5b62719346cf4baf07c68d927239752b1534/numpy-2.5.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd4cb9ad3c7889b9b3fe0a9a9fb5d2ed26f9879bff2608d9f01aed147a20d231", size = 17042047, upload-time = "2026-09-06T16:25:17.582Z" }, + { url = "https://files.pythonhosted.org/packages/5f/3e/a700ecbf36e85ae8328fd3b0e12eeddc22ed6358a64cb2bd913e0d195d65/numpy-2.5.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1302b90c0e52281681b2975adfe8a860cb7b12216a27b4b0b4207c44bf7bccf0", size = 18465724, upload-time = "2026-09-06T16:25:20.949Z" }, + { url = "https://files.pythonhosted.org/packages/41/ee/38e785e88a4045f6ad1d1f2808dcdfafdca48c760260c0587bf171e29fc9/numpy-2.5.3-cp313-cp313-win32.whl", hash = "sha256:1c80eabb4035ecf4ca9cd49cde8a9fdd69a729e63e6474887d1523ade7aa277f", size = 6129003, upload-time = "2026-09-06T16:25:23.664Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ec/100f2b1794ede74a9b3d7ec6b9736927f56713414c1dfe19ab6c383494bf/numpy-2.5.3-cp313-cp313-win_amd64.whl", hash = "sha256:71cad2b2a7451ab79d8f5e71b453485b6775963d5cf794179144a7463fe6e8ec", size = 12560965, upload-time = "2026-09-06T16:25:26.602Z" }, + { url = "https://files.pythonhosted.org/packages/80/b1/7dc825ca94c12acebbce4c37caa5e198695eb31424bc579679f32b1bb49d/numpy-2.5.3-cp313-cp313-win_arm64.whl", hash = "sha256:8e4dd766076855b5ff7ea52fa5f07ce26286726e0f8bff446b7739d02e6ea204", size = 10482343, upload-time = "2026-09-06T16:25:29.772Z" }, + { url = "https://files.pythonhosted.org/packages/70/78/cf416f15dc29375a229d9dfebf8db6e313f291580b39fa1a568b6052bb07/numpy-2.5.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:350ba9783ce969cf9f7ce6e6a9a58e1a6e2a19ca025b7ee448c4db727706212a", size = 16998686, upload-time = "2026-09-06T16:25:33.171Z" }, + { url = "https://files.pythonhosted.org/packages/9e/59/abcc2d8def4fd60eec7d87f92d27c13448ffd9ab14339bcc63a0d7a2fdea/numpy-2.5.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:012e66aca395d795496446e52aeeb5866312a5d4d3f27da270e5a0b43f70dc5c", size = 12013862, upload-time = "2026-09-06T16:25:36.748Z" }, + { url = "https://files.pythonhosted.org/packages/94/75/4640d2d6e4b64a049e48425a82728a41ef4adb61332d2cba68055774878b/numpy-2.5.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:adc1ada2662f8a5f960b8a10d9986897e7499ef07e06d4cfe7197f8cce923c07", size = 5449793, upload-time = "2026-09-06T16:25:39.476Z" }, + { url = "https://files.pythonhosted.org/packages/96/cd/625b57ae33d4ca560f32cc0b47b4a5922146d9beb998ddf773900d440a73/numpy-2.5.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:54a115e5a73b8fc44f0cebef486365a1894b5c9760685d4558b72b7c3eb846e0", size = 6785176, upload-time = "2026-09-06T16:25:42.069Z" }, + { url = "https://files.pythonhosted.org/packages/9c/72/12918652e7912ef9751e8694c88820fcd1908e0618cb23f5f3caa6004b7b/numpy-2.5.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be5a8381859b6da607c84f4f7d6847725f1cf1853ef8a2c9e115b7d58bef47dc", size = 15703377, upload-time = "2026-09-06T16:25:45.135Z" }, + { url = "https://files.pythonhosted.org/packages/45/8f/9beacf79ca7c650688ad0baa80931adb988fe6e6e5d5903c23cc3dbd70eb/numpy-2.5.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0521d0f4aebb6e06189451025fa17a913287b13c03d5fe05c017333b654ea5b", size = 16711928, upload-time = "2026-09-06T16:25:48.461Z" }, + { url = "https://files.pythonhosted.org/packages/09/8d/41d0a56e1ac4c87495c897a211b1368691b7237aadabec8b3b8f3a74d48f/numpy-2.5.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9deb49575e5b0b94ed72c8a64ec4d033381adc27e9060ae842971f697ba96104", size = 17059507, upload-time = "2026-09-06T16:25:51.873Z" }, + { url = "https://files.pythonhosted.org/packages/08/1e/0dfbc5cc251d54e2af790f254d24ec38637fa97ec7d5d11de7ffed787098/numpy-2.5.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b00eefbcf0f292945c4b4dec2ae845389ef5bcdcd596e6e4328051db5b5ba694", size = 18471002, upload-time = "2026-09-06T16:25:55.233Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2c/dfa40f6991f8185c8c30ffd023dfcbb11888e823cfab9557b920f3bb7bed/numpy-2.5.3-cp314-cp314-win32.whl", hash = "sha256:c2381f82999704f818e2c987a865050e285ec3621262c66d40f5a96c8f899f8e", size = 6180485, upload-time = "2026-09-06T16:25:58.157Z" }, + { url = "https://files.pythonhosted.org/packages/a4/73/d2c08231e4fde7e415501fd02c715d96e98599b2d8384445933944152984/numpy-2.5.3-cp314-cp314-win_amd64.whl", hash = "sha256:2c25dfa72943e4336ddb6b0ee4277b47a0c85bede0807530ec68103bf58e2c10", size = 12698179, upload-time = "2026-09-06T16:26:00.789Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e9/dcdcc9b95cf5f49815055573aee1b11cfbf5299f38a180e437ded050810f/numpy-2.5.3-cp314-cp314-win_arm64.whl", hash = "sha256:15aa985ac73a8db02db7663381aa109510449d3819d37206caed27b33a65a8a6", size = 10769383, upload-time = "2026-09-06T16:26:04.011Z" }, + { url = "https://files.pythonhosted.org/packages/49/c4/af8bc08a7ef4e1529a7c0cf24969accce316b783999802089a581ec99272/numpy-2.5.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ac7bb1c52d445bd4f8f7f97fefe6abc3a084dc4d63df50d79b17fa2b78e89297", size = 12132668, upload-time = "2026-09-06T16:26:07.138Z" }, + { url = "https://files.pythonhosted.org/packages/c5/ae/0f15eb56d4ec5e13c1f7ff04ff407f997d1acbadb45d3e1f2e2645a8f43c/numpy-2.5.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e6ab667ba76450084eb64013762c438ea76d9d29cc676dcd6c2e9892ba37f841", size = 5568580, upload-time = "2026-09-06T16:26:09.828Z" }, + { url = "https://files.pythonhosted.org/packages/23/fb/c72a8f25d4b6e96c354e7ab45ace3b27dc11e5d6a13b6c7d0cd6b08bf112/numpy-2.5.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:f7fabeb6cea87d65f3b926de33d03fb016cfdc29314c90974383b5582ae72891", size = 6882634, upload-time = "2026-09-06T16:26:12.524Z" }, + { url = "https://files.pythonhosted.org/packages/07/a9/968c90ed2ab15060c338e8137f1215b5a60756ae07328e0a60d1c6734df4/numpy-2.5.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1fb6f8fb9ff0b3a69f52c66ce397b0246583e9f28616231b0e32ca49259a5fa6", size = 15748923, upload-time = "2026-09-06T16:26:15.092Z" }, + { url = "https://files.pythonhosted.org/packages/59/08/9df04103947b95e3b6b1f2ed1a70521f325647a31b82da6a2aae3a485508/numpy-2.5.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93e1f5447e2b1e479d7bd74701e84746b86450cff1fc368b132d195e2b8f8211", size = 16746748, upload-time = "2026-09-06T16:26:18.43Z" }, + { url = "https://files.pythonhosted.org/packages/41/a0/14c8d5fe5b53a334aabb653deb391c0fef49558f491880ea300ed6785224/numpy-2.5.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c00abe94c1a69d75d827dcf1c025b25c8a45d230b3bcd77a9020883a1b047653", size = 17111561, upload-time = "2026-09-06T16:26:22.113Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a6/d7e96e42f01522e154c32489640f16dfc4f6181d165d05fc3bec8c2c4999/numpy-2.5.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:536f963710a4e63934d80ac0dc4f478804a83e9a84b6828018f25d09953ada33", size = 18513945, upload-time = "2026-09-06T16:26:25.401Z" }, + { url = "https://files.pythonhosted.org/packages/25/39/3453afb7119d0449ef11c886874120ff180e2c337760e0e2d88f70f1a945/numpy-2.5.3-cp314-cp314t-win32.whl", hash = "sha256:4c8a6d2ebce6305fd82fbefca827775437147052a976ee7c94b36a0c1b52ac6c", size = 6335421, upload-time = "2026-09-06T16:26:28.175Z" }, + { url = "https://files.pythonhosted.org/packages/99/01/22815d2b19a1a746b1d45205cffebb3fe511a18acb75fba6c88491fc9894/numpy-2.5.3-cp314-cp314t-win_amd64.whl", hash = "sha256:9a37475425b431b4d060f23b4f52cd2f3aef6bc7c654bd760adf0040eec9d435", size = 12896420, upload-time = "2026-09-06T16:26:31.265Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ee/a7cbba67eeaff038dc29ca8b98a88396c8b0cc9c89d4924f4a27a5c9150b/numpy-2.5.3-cp314-cp314t-win_arm64.whl", hash = "sha256:2d8240cb4c16fd831074aa2b2cf9fc54664d826341d61c372245b96a74a49a9a", size = 10857177, upload-time = "2026-09-06T16:26:34.167Z" }, + { url = "https://files.pythonhosted.org/packages/45/56/78194492883ff5eec90423fe56a3a44b154da047d88a6307f629713c584f/numpy-2.5.3-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a6391fafaba97500887132cd582abc6e19452b1ac775a47caa7b24490e152058", size = 16996531, upload-time = "2026-09-06T16:26:37.287Z" }, + { url = "https://files.pythonhosted.org/packages/11/39/dd55c0af90bbab564b09ae3b0aa60ec5c02b900fa4f1ba23440525c8b32d/numpy-2.5.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:09d5a423c71ad5feb5625844ad58050e35df43871004b52ac9c0ad44a56775be", size = 12012569, upload-time = "2026-09-06T16:26:40.707Z" }, + { url = "https://files.pythonhosted.org/packages/b6/51/04f67d32e4862b281b1cb84ceeaed3421189a84fb6fb51a391cd6d5009f7/numpy-2.5.3-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:f9579f383d1bf9df80081e72760e84960a7fd4f88cf0c9e535a8597c9bb646f5", size = 5448498, upload-time = "2026-09-06T16:26:43.435Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c9/25b4dc0dd1344ec26c7319e84fd4e9809d2b5628f4e12decd618036e5178/numpy-2.5.3-cp315-cp315-macosx_14_0_x86_64.whl", hash = "sha256:86bff898a431c0fb71f7610b75726e75a54d47b37edc9d537f48de63bb3c0b90", size = 6783026, upload-time = "2026-09-06T16:26:46.374Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c7/29285be1e5232a6e7ee3268a33c85843f5a8ee93350c6465cddd66ebbf76/numpy-2.5.3-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f3ed25271581281f2fccb1adcedfcde4c07362eec69189b50baf6f90e3ae159", size = 15697322, upload-time = "2026-09-06T16:26:49.415Z" }, + { url = "https://files.pythonhosted.org/packages/55/49/bbad5335fb4996a16881f853ff3e0ba582f01720e55c89b1c06b8fc42a90/numpy-2.5.3-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffdc76bfcae6b255dff75202c5e7feaf95b40246bc0a17944facc1fecf9f79ab", size = 16708995, upload-time = "2026-09-06T16:26:53.127Z" }, + { url = "https://files.pythonhosted.org/packages/ef/e9/1df35483760b04a65ea44669f89dc64f30e5aca098b48ceb8b1310b0e0fe/numpy-2.5.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:116f96cadd935c6122e9228d676fe7ede19e741f5c8bb1c3cddbe0c51ccebea2", size = 17052508, upload-time = "2026-09-06T16:26:56.464Z" }, + { url = "https://files.pythonhosted.org/packages/b8/99/66e54da8265cc8be8a7382bf96edce17aaa2837d6f484432025932a3caa5/numpy-2.5.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:09ffa5d903faeaa5c4dd05009cf81c8bab9f2cb37c548b8d39b65b4cfa7c97f7", size = 18468224, upload-time = "2026-09-06T16:26:59.966Z" }, + { url = "https://files.pythonhosted.org/packages/01/bc/b5e90a91c115168d793dfd2ad9c69c438c2fe7a13a437e770bc5b078e732/numpy-2.5.3-cp315-cp315-win32.whl", hash = "sha256:e01c918ac3d48e18a927cf7b14a26a3e29ff2bdf2eacb976da0aecd6a43ed034", size = 6179919, upload-time = "2026-09-06T16:27:03.166Z" }, + { url = "https://files.pythonhosted.org/packages/37/ea/780748fd3985109075514ef8fc64cd25f943e40dde13a6d59141eb268fc8/numpy-2.5.3-cp315-cp315-win_amd64.whl", hash = "sha256:e931e4f499e0dc7ef29d269a8e5b35dd722e5d14be07df6240166ea7c6532fae", size = 12697656, upload-time = "2026-09-06T16:27:06.153Z" }, + { url = "https://files.pythonhosted.org/packages/b3/16/407be69a2a87c8cab64d95975a8977a426a29e138f07e276ec258f0fe4e5/numpy-2.5.3-cp315-cp315-win_arm64.whl", hash = "sha256:26e15e4aecd8617dfbaecb37d223e365d7b39411fba20454be2670a96aa74cb5", size = 10767601, upload-time = "2026-09-06T16:27:09.297Z" }, + { url = "https://files.pythonhosted.org/packages/44/bf/a97ffb01e41d50a32a9177aef942a4d0e389a3daf451d04e5f38ef6afb87/numpy-2.5.3-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:6cef4bb1706dfec49243c05d921eefb4e190d41e2528b30d8035ea1f36b4c24a", size = 17090092, upload-time = "2026-09-06T16:27:12.907Z" }, + { url = "https://files.pythonhosted.org/packages/d1/24/136c02f2c2af9a067a84d0c3aa10c99012c0476fa5066732fa4a4202557d/numpy-2.5.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:d1c89973648c85069c5046ad460f7b8a00218b29a2e42359ac8cc63e9ab94832", size = 12129429, upload-time = "2026-09-06T16:27:16.089Z" }, + { url = "https://files.pythonhosted.org/packages/fe/6c/b47582d6597789bf946d5efbeb6b9e56fd8bcbd5efc6fbf51dbe1ea31eb3/numpy-2.5.3-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:214045a5bf00113a146ab9ee9730c44501af6723cdf1f6830932f7b5ef2e7af0", size = 5565452, upload-time = "2026-09-06T16:27:19.868Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/ef3cc6da73774202d4deae16bb321fd8298a4e0561e3539f8c4be237d916/numpy-2.5.3-cp315-cp315t-macosx_14_0_x86_64.whl", hash = "sha256:8617bbfae4486cf99c9f899966699428d19da931d06ca94ad3da986c76e15997", size = 6876736, upload-time = "2026-09-06T16:27:22.232Z" }, + { url = "https://files.pythonhosted.org/packages/9e/24/e3813329498596cb842703dcacac1741612ed9fb9c4e6a3e0c7e2ebbc597/numpy-2.5.3-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:595d020938c84e320bcf40ad71089e108eac0d377cd018e14a8c094f39e98d85", size = 15745777, upload-time = "2026-09-06T16:27:25.181Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9e/4e7a07fd0776dc2210cdacf2010be8665194d094defc10c419d7dea794cc/numpy-2.5.3-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f24021b9f22bc6301c37b196974a92c1c18dccedb6fef3dd252e95f2d6adbe4", size = 16746949, upload-time = "2026-09-06T16:27:28.576Z" }, + { url = "https://files.pythonhosted.org/packages/91/db/01674c0e20335057813a00c2ebd546ed25bff9ed7914f9bced00f8c55d94/numpy-2.5.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:71b39d9f935b6ec0f8753e3e2afb51e3efba6f2e05b68b32a40754d24bcd4a3c", size = 17108994, upload-time = "2026-09-06T16:27:31.946Z" }, + { url = "https://files.pythonhosted.org/packages/45/7a/584c5e71f8d378e57cac0b033891ed65c683ef90573ba4854e8c28203db0/numpy-2.5.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:6b05c171afb3aa07adbd20abc00aea86fe375beb0fdb9ef780ec5b7f63bab1c0", size = 18512266, upload-time = "2026-09-06T16:27:35.196Z" }, + { url = "https://files.pythonhosted.org/packages/a1/d2/4e1014173aa3c55e6a756e0e567290743a6ab33a288460374d7ef6bcd239/numpy-2.5.3-cp315-cp315t-win32.whl", hash = "sha256:f54660b0eb6b0b9f36e7fe1cdfdff472028dd0d14acd9b9b65098efbad059469", size = 6330292, upload-time = "2026-09-06T16:27:38.149Z" }, + { url = "https://files.pythonhosted.org/packages/6c/b0/ff5658a58199b7bcaad87bf260eef6713d9d42cca4e028f935b4fc5fbac6/numpy-2.5.3-cp315-cp315t-win_amd64.whl", hash = "sha256:1aad64d99730d013cfc6debafed22783b4fc5a7f4b8bc744d2d8cf7dcc880551", size = 12884918, upload-time = "2026-09-06T16:27:40.965Z" }, + { url = "https://files.pythonhosted.org/packages/fb/0b/b12a2df5d1b774bd9007a6fdff9381145b6223d37f11afc9c37ab0efd9a1/numpy-2.5.3-cp315-cp315t-win_arm64.whl", hash = "sha256:befa1ae5bd6030b3f512b43ff3fa5290bbed6b84411a44244b14adf835f5b89d", size = 10850807, upload-time = "2026-09-06T16:27:43.868Z" }, +] + +[[package]] +name = "openai" +version = "3.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx2" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/4874c4f7db30121885e6a763e2670b2fe1d76c79ed4732f8acbb51c8d96e/openai-3.13.0.tar.gz", hash = "sha256:a8f87a9b3b9c08eb446d68bd0a80e8ec907c4c35fdea63f4265c7b34b2de3a60", size = 1624217, upload-time = "2026-09-10T19:38:27.597Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/7f/ee9ebb7c5ab7abf016969faec7c748a91c1fd893078bfe1e5ba5d82e96c2/openai-3.13.0-py3-none-any.whl", hash = "sha256:e35b1f6fe99245e86e37504d9fad1ad2a363807307c424232c1d849bd0666c8e", size = 2009683, upload-time = "2026-09-10T19:38:25.855Z" }, +] + +[[package]] +name = "openapi-pydantic" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" }, +] + +[[package]] +name = "openenv" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastapi" }, + { name = "fastmcp" }, + { name = "gradio" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "openai" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "rich" }, + { name = "tomli" }, + { name = "tomli-w" }, + { name = "typer" }, + { name = "typing-extensions" }, + { name = "uvicorn" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/38/5552b1b208c8b8971a1895a74f3b9e52f824059d3aa5d08992088931d969/openenv-0.4.1.tar.gz", hash = "sha256:08c7c13455854beee5c8f311f57d95180772e3e9ac9c85278d681864488016a3", size = 201919, upload-time = "2026-07-03T11:37:24.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/18/787ddb930e29705479fdd0a58b2ca7fa243c9441f8f1ece33629f8d82b8b/openenv-0.4.1-py3-none-any.whl", hash = "sha256:24c46788c80fa322b34b8e1f8b3adfe42fb8ceec7df99a8b96b44eea0a295921", size = 236036, upload-time = "2026-07-03T11:37:22.607Z" }, +] + +[[package]] +name = "openenv-data-agent-env" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "datasets" }, + { name = "fastapi" }, + { name = "fastmcp" }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "openenv" }, + { name = "pydantic" }, + { name = "uvicorn", extra = ["standard"] }, +] + +[package.optional-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-cov" }, +] +e2b = [ + { name = "e2b" }, +] +hf = [ + { name = "huggingface-hub" }, +] + +[package.metadata] +requires-dist = [ + { name = "datasets", specifier = ">=2.19" }, + { name = "e2b", marker = "extra == 'e2b'", specifier = ">=1.0.0" }, + { name = "fastapi", specifier = ">=0.115" }, + { name = "fastmcp", specifier = ">=2.0" }, + { name = "httpx", specifier = ">=0.27" }, + { name = "huggingface-hub", specifier = ">=1.12" }, + { name = "huggingface-hub", marker = "extra == 'hf'", specifier = ">=1.22" }, + { name = "openenv", specifier = ">=0.3.1" }, + { name = "pydantic", specifier = ">=2.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=9.0.3" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.0.0" }, + { name = "uvicorn", extras = ["standard"], specifier = ">=0.24" }, +] +provides-extras = ["e2b", "hf", "dev"] + +[[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 = "orjson" +version = "3.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/f3/742fb1f62b825f2c010697eaf4e828004bc2a81e7e806666989c132c7c42/orjson-3.12.0.tar.gz", hash = "sha256:d14203fb1aae2ad9b3d52f8a0e82aeb10197ef1c9bc61da7f358bd70b00123d5", size = 4142915, upload-time = "2026-08-14T16:13:30.607Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/4a/295da39c651c2faac8bd351a2a346f0fdedd9d50b847ee9dfc27d2207ef6/orjson-3.12.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:aa3e43a6846e91d7bde3d5a9c66090fcd8744f569a9b6cffc5e1ca38f6a461c0", size = 223427, upload-time = "2026-08-14T16:12:28.525Z" }, + { url = "https://files.pythonhosted.org/packages/29/98/758cf90fbeaaafb7f8141bfac75a432099959f3a2f5db93a412e876415d8/orjson-3.12.0-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:11edb4660a6680abee9788a3a9072208a2c96538cc1322bd79542065229d8e54", size = 123725, upload-time = "2026-08-14T16:12:30.013Z" }, + { url = "https://files.pythonhosted.org/packages/32/b5/5b934d251f8651f7e41df180ad0c57a6e1cabe15c7bd331638413a50ebc9/orjson-3.12.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:2d3a9da945a4d96ae758fdaaca56742e6b73b6fd554c5d8876f252a6dad70b83", size = 113375, upload-time = "2026-08-14T16:12:31.209Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d2/37efb5b12a176ce3ced29f4144f20da57d02757f78ce549637dc1b4e1fc8/orjson-3.12.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:92ffc09e07233a6ab6d4e067f7841edcbcc134cb4812155cf171ea5255a421d7", size = 129983, upload-time = "2026-08-14T16:12:32.721Z" }, + { url = "https://files.pythonhosted.org/packages/50/22/0644b87c73f13e0092df8f35a1fe280d991e5e90072087411e0dd7e44e0c/orjson-3.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf44e374aadde77b1f6109f1030be51433eb61984379852766b6f4e187db7b1e", size = 130629, upload-time = "2026-08-14T16:12:34.084Z" }, + { url = "https://files.pythonhosted.org/packages/8c/57/80b986ebfecd9c6a177ddf1c2319717f0cd8feffb2b78946595a18a2fc88/orjson-3.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1192a7021b6d071aaf909864f6e924d6a2675ca360485b972b8401749311750b", size = 131245, upload-time = "2026-08-14T16:12:35.713Z" }, + { url = "https://files.pythonhosted.org/packages/80/3d/75c5ac5a69161f44492a68fbdde66f4cc4ce48cd5e1fb05918e46f0c8848/orjson-3.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:53c0c474a9d9aff9aebfc0c88de1f28f843d940e6e3a80729abdf6a20274356f", size = 135397, upload-time = "2026-08-14T16:12:37.128Z" }, + { url = "https://files.pythonhosted.org/packages/71/93/4d71f2df314a97ff0d27a4559bf5888fc8406e3c6dec90e92291e3511215/orjson-3.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:532ff8cd4bd59a327a953a7dcde922c7fc25b85e29721bb8633265430d3a3873", size = 127693, upload-time = "2026-08-14T16:12:38.627Z" }, + { url = "https://files.pythonhosted.org/packages/bc/1d/0dbc6be5adfd1730491072fb60beb6bcdf5d7b2596ee41b7fc2e298bfc09/orjson-3.12.0-cp312-cp312-win32.whl", hash = "sha256:a6cf4b18e7de173f209f2084ffbd736dd72389a396326ee80a7022168be232e5", size = 128000, upload-time = "2026-08-14T16:12:39.954Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c9/97b1ce0112ebf5e949c775ed5b1755e562233179f3584579673cc24d6378/orjson-3.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:010811c1b69773450a01cef97727a67b223242f350b77d4ca000e59a9ef2155a", size = 122106, upload-time = "2026-08-14T16:12:41.324Z" }, + { url = "https://files.pythonhosted.org/packages/a8/6a/facd8b312e4a0d3a7fa978c7e15821f74a336adf1d65529faec33b48e18b/orjson-3.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:ad29eece0c601737f2a60edc2752a84e7a0785df3efb62e3012834700a5afe0d", size = 126869, upload-time = "2026-08-14T16:12:42.651Z" }, + { url = "https://files.pythonhosted.org/packages/54/cb/d7b78218a987eb8a8ce4eeae0286b1bb679333eb631ea0eeaf6371680bfc/orjson-3.12.0-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9a36ec60f1796f9a3f13e3b98390295e17a1c7c10155b448d264098bf9ee5900", size = 223397, upload-time = "2026-08-14T16:12:44.003Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4a/bc87c45e7ec639d35ebefd62618e01939531ac8e171426606a01bda05914/orjson-3.12.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ad0422b92d5195443a39f80c3bcf731cc2e00f153bd32063a47b73b057bd0f03", size = 123662, upload-time = "2026-08-14T16:12:45.433Z" }, + { url = "https://files.pythonhosted.org/packages/94/ee/c9a4ff3f2dbedbbe9e635d0fa72c8866adede09b6335ef9644f53752f0d8/orjson-3.12.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:5a0fdbc216388f653d3752ff310e710f59253bd4ed6a2bfb3f4f06b84714bbd8", size = 113374, upload-time = "2026-08-14T16:12:46.755Z" }, + { url = "https://files.pythonhosted.org/packages/75/09/3f330a026a796c8b4c97a6f429652a5e912e7065039bf96ed25e42aa7b25/orjson-3.12.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2eb5c56e534127b2b8fa38d2363c8b1b8190367ee0d1d16c041517d880843b94", size = 130029, upload-time = "2026-08-14T16:12:48.06Z" }, + { url = "https://files.pythonhosted.org/packages/7d/40/094cc53126a3d22f76cdf83b6ea67338bed01d774037621a785aa8e6e5ea/orjson-3.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:784106539f4b9d4b930e0b4eb8d45168507dae001945e71b4675a367f1e5e806", size = 130528, upload-time = "2026-08-14T16:12:49.362Z" }, + { url = "https://files.pythonhosted.org/packages/bc/74/89bb236deb9565f99434b13052bb40ddfcce4adf3afbfa3132ee7e421468/orjson-3.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c680706fc8396d95e7c4c1f9482563f552137aef91b57237a3ad5aaf64629df", size = 131075, upload-time = "2026-08-14T16:12:50.692Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ac/1176360d762c01b5bd34acd56fc098e936c491363d8b6b397ad4aa475547/orjson-3.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:83445adc40cba26d6d621185a45128ce455b766af368cad2ab64b970603a7978", size = 135321, upload-time = "2026-08-14T16:12:52.114Z" }, + { url = "https://files.pythonhosted.org/packages/7a/02/bbd881c8b9276d50b998de38b4e97de8ace1aac940b0ee545aedbf65ed00/orjson-3.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:644d005bc82f917337a95ce270c9f6f92f9834c2bed7b1477572f8db00784222", size = 127472, upload-time = "2026-08-14T16:12:53.517Z" }, + { url = "https://files.pythonhosted.org/packages/8e/02/a0934d7503e6dcbedd6afac3e7f3f8597fd09389949ad94d0f7540e9dbca/orjson-3.12.0-cp313-cp313-win32.whl", hash = "sha256:d8e78d3d93705e3d27cc17cdb209e44d7a8ea203010cac6ce9c7ffc1ae1996f1", size = 128000, upload-time = "2026-08-14T16:12:55.14Z" }, + { url = "https://files.pythonhosted.org/packages/52/87/69f98f8d40faff103a965a5fbb83f08241b01beaf92badb5413fbc9358cc/orjson-3.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b85931be5b6763c31283805c9bdaae1ca03ad9f6f12a15f1cbf6745b907932c2", size = 121841, upload-time = "2026-08-14T16:12:56.507Z" }, + { url = "https://files.pythonhosted.org/packages/e6/07/b83046a4e3cadcc0987d0f160696107c4af706a619b56e4ad01940cadadf/orjson-3.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:6a31348d7dfa64cd9c78bd1f510ff44c48fe64d71094e6b90e364dba3b55949e", size = 126765, upload-time = "2026-08-14T16:12:57.806Z" }, + { url = "https://files.pythonhosted.org/packages/12/9d/3931253e6f3148abf2cbe14830367042a4806b362ea520df2303db188fb9/orjson-3.12.0-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9e6fee342a48760e854d743e7a81534d8e2925a6f46e09f750cf56b50fd1de5d", size = 223391, upload-time = "2026-08-14T16:12:59.184Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0e/b4a4f1e305367245877b967a0bad70fcf001d77c54ac4339a120b66fdae4/orjson-3.12.0-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:8c3bb86dd10f39b3fbf434b7d5dc7cac77d6fc8ac572ae30a10731ede2c4b647", size = 123659, upload-time = "2026-08-14T16:13:00.548Z" }, + { url = "https://files.pythonhosted.org/packages/96/f3/6782c6fa85e2702bc66be183c3b421486167dcf266ee4dc1403fe3824870/orjson-3.12.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:2bb3ce43203936072dd8b4917b01d3aecfc02329bfb42510cb7cfb24708adc9c", size = 113337, upload-time = "2026-08-14T16:13:02.009Z" }, + { url = "https://files.pythonhosted.org/packages/bf/79/b32ab64bacda9d0fa4942ef483bd03cabf0eaf2be819ca9fb7ff610c559d/orjson-3.12.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:6a2a79c89984dc719817d388c8709e0efc2a2795a934eaa746b4882eb6045adc", size = 130112, upload-time = "2026-08-14T16:13:03.404Z" }, + { url = "https://files.pythonhosted.org/packages/ee/49/6e6142999ca01509219be5e5a9c338a3e5ea011f63e91ff473fbbf3734ed/orjson-3.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f06dd838d1e07d9b1de0932ec0485ec92c4d5f5d1ad4817a656268c3e88be1e1", size = 130520, upload-time = "2026-08-14T16:13:04.798Z" }, + { url = "https://files.pythonhosted.org/packages/49/d0/3745af0a4cc9867784f29722929cec4d10bd1c877cd754b01ba6d96eb21a/orjson-3.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6b11be792c3d2c6a4be2af4ebf97a68d0bf5f580aca6e86a418a354f6cc846a", size = 131053, upload-time = "2026-08-14T16:13:06.14Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f4/6fe5a22fa478fffb190e65c338c84df5c311ef597b363150a17cc57063c0/orjson-3.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:477ecaf6b9f88f873341b91fcc736119ca81b5e002a9f7f308ff5b4f2ce2a70e", size = 135321, upload-time = "2026-08-14T16:13:07.544Z" }, + { url = "https://files.pythonhosted.org/packages/ff/41/b1b0ec30289646a81a76e2dbaae2686b96fcccb7cb0323dc1dd78cbc7875/orjson-3.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f3c0683136acdc29afdf88a5bc2f7d3d0e34087788d1d63c0144b805a87a196f", size = 127485, upload-time = "2026-08-14T16:13:08.88Z" }, + { url = "https://files.pythonhosted.org/packages/bf/2b/277404bdcc21c93b112b963655b76443ebfe828f8a3ff1de7d90f8850eb3/orjson-3.12.0-cp314-cp314-win32.whl", hash = "sha256:d39f3f5c3927e2dc0913fe5bbc1a2f6b1b9d1bba1de6358340d0ad0d0c00ca92", size = 128048, upload-time = "2026-08-14T16:13:10.305Z" }, + { url = "https://files.pythonhosted.org/packages/41/2b/395b36fa2b4ce7af70b651d715e88f80d884b2c2b14a6b53e84d554fb5f0/orjson-3.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:0b1ac5bf6609b2716c7954011c5fef6254922df029f45d032ee4ebf5d363cbed", size = 121858, upload-time = "2026-08-14T16:13:11.634Z" }, + { url = "https://files.pythonhosted.org/packages/ea/a3/833e895ff452859eebe75093d26691fe9108f1a7a6a08435d7a5780ea652/orjson-3.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:50fae885cb073eac7556353ff3df93312b0d5137b0a5056b2bb63f97ed9a93c7", size = 126749, upload-time = "2026-08-14T16:13:13.117Z" }, + { url = "https://files.pythonhosted.org/packages/58/64/99c8947ece10c17176af9aae85c4948f1d109da77440ec14d87239efaf73/orjson-3.12.0-cp315-cp315-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:01efac2074fffb4cb1ea3fab7861e9d0f2a26913854a972f5ac760525dbdaf6e", size = 223398, upload-time = "2026-08-14T16:13:14.694Z" }, + { url = "https://files.pythonhosted.org/packages/3e/30/cf983fe09f2731420fda097a9f7ef4343f47fa216c228961ad8f6da44f3d/orjson-3.12.0-cp315-cp315-macosx_15_0_arm64.whl", hash = "sha256:ed4ca42bd55955aa34deedcfdfd0e0c31abf51143aae158ae2bc3520b626e517", size = 123655, upload-time = "2026-08-14T16:13:16.221Z" }, + { url = "https://files.pythonhosted.org/packages/11/50/9cb8ae73fa4749dbbc20f617004213b5ff01c20aaeec34c3f31124f2c1d8/orjson-3.12.0-cp315-cp315-manylinux_2_39_aarch64.whl", hash = "sha256:40f92192227505acca4e2533ce565f8e6b9535f7d0d09b0968452f18b7376b38", size = 130515, upload-time = "2026-08-14T16:13:17.601Z" }, + { url = "https://files.pythonhosted.org/packages/9f/0a/adb6ce1a5b5fbf9cb1790f9961bb668a0dd5429aadaf6cee044724681795/orjson-3.12.0-cp315-cp315-manylinux_2_39_armv7l.whl", hash = "sha256:33efefcf5d88eaf400b47e2eba02f91f319bb9951be61ca500b7d536d3f2079d", size = 113327, upload-time = "2026-08-14T16:13:18.927Z" }, + { url = "https://files.pythonhosted.org/packages/51/5c/d17f61581d8dbdde7048f87a330fa24915edec38db4d72b381fec14fbb56/orjson-3.12.0-cp315-cp315-manylinux_2_39_i686.whl", hash = "sha256:8e386b0bc0ddd7cd2056f884b5a0af33592bd01ac66a7ca4b42a65a7e7774a13", size = 130105, upload-time = "2026-08-14T16:13:20.317Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b7/938befcf33bee4704a92ecec6a2731224c539d939bf9429fd39396d28931/orjson-3.12.0-cp315-cp315-manylinux_2_39_x86_64.whl", hash = "sha256:58c58e1de0006ffb580368d6793c36c7b0b021db066479cf281bf5061e732328", size = 131049, upload-time = "2026-08-14T16:13:21.719Z" }, + { url = "https://files.pythonhosted.org/packages/b0/15/cfa2021d64d5aa8bb5c9f604ef375e00ec8b657651b5dd650b1b7ad13df1/orjson-3.12.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:08231552159be266a7269555bd9f7c016aee7d9ad6dab06eb58796c5ccb7101c", size = 135320, upload-time = "2026-08-14T16:13:23.415Z" }, + { url = "https://files.pythonhosted.org/packages/1a/50/3e75dfe357c1e8f9e287c7a5740260ef15bd23a5299eae8d0835dcad5375/orjson-3.12.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:a15f9a891bce5f5cc5d210e3ad8614d4d1b489a56448c099d6d2a7168b2d954a", size = 127488, upload-time = "2026-08-14T16:13:24.791Z" }, + { url = "https://files.pythonhosted.org/packages/11/a6/79aed402eb3ab284dc5b4791a7ad62c5875127de01b8e3f04bd92d551298/orjson-3.12.0-cp315-cp315-win32.whl", hash = "sha256:03091c8a64db4be38746597ceea68f33c238e27acd9bfe99fb59420224ae7a55", size = 128048, upload-time = "2026-08-14T16:13:26.217Z" }, + { url = "https://files.pythonhosted.org/packages/64/f7/2723e264aab7248c1ed6ecaad8e5d0cb866c0cffde75442102ffa7491aba/orjson-3.12.0-cp315-cp315-win_amd64.whl", hash = "sha256:2b7bcefb9f40fa242fa6b06377232c048e655747790829609168c01162f60578", size = 121860, upload-time = "2026-08-14T16:13:27.577Z" }, + { url = "https://files.pythonhosted.org/packages/82/56/630c9113ec8996778f1f0304b364b091b9a9db5fef5fdc17cca622f5ea24/orjson-3.12.0-cp315-cp315-win_arm64.whl", hash = "sha256:859fc4196855890150bb08e649b30d2c93b249b3e3edd0d3bb2231abf8aa8adc", size = 126754, upload-time = "2026-08-14T16:13:28.962Z" }, +] + +[[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 = "pandas" +version = "3.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/4f/5f3422a2afec5ffc46308b79e53291365a93748b498ac2e58bead0197916/pandas-3.0.5.tar.gz", hash = "sha256:dca3734d6ab7c906e6730f0788b0a1dbb9f2467731f9711f77995c8e9d62d712", size = 4658219, upload-time = "2026-07-22T22:19:28.819Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/54/1dc810ea558d1320b597aa140a514f2fdf1d2ea09c38cf556f13ea712ec9/pandas-3.0.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa290c16964d4963fbfbc358928239cf3bd755b20e988ce944877def2f44471d", size = 10411717, upload-time = "2026-07-22T22:18:08.307Z" }, + { url = "https://files.pythonhosted.org/packages/68/56/fbe81c09195924d8b7b8d4461a20458fe80a6a5ed6b24f0314da684277e1/pandas-3.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2e26bb46934b8a2ca0c3de1d3d606fc5f6746584791b2db264d58cf370e08dc", size = 9957095, upload-time = "2026-07-22T22:18:10.6Z" }, + { url = "https://files.pythonhosted.org/packages/e0/51/fac252f4a913ed5eabf3c11b880a9e8d5a6c10f0b2129d0462212d238b4d/pandas-3.0.5-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73fa87b08a7ef706f8aafda39ddaccf2a99047bea62d8c88a0361bcafb2237bc", size = 10485458, upload-time = "2026-07-22T22:18:12.834Z" }, + { url = "https://files.pythonhosted.org/packages/12/98/e976540c1addf70442be7842a18cf70884a964abbf69442504f4d2939989/pandas-3.0.5-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d373ce03ffd84010ed9839fa73672a9c8256990532e158440c0085db7d914b34", size = 10998091, upload-time = "2026-07-22T22:18:15.209Z" }, + { url = "https://files.pythonhosted.org/packages/a4/8c/1f29b5be8d3fc47dd7567eb167fabba2085879b31e0287ce7cba6d3d2ff4/pandas-3.0.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2a29c53d85ea98c5e792c59ef82ee9fbe6ca902c0d0adb6b23f45ef894cd7bf6", size = 11499501, upload-time = "2026-07-22T22:18:17.689Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e2/bd9c98ad2df7b38bde002adde4cdf353519da51881634323b126c55997f9/pandas-3.0.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a5ad3b02ed6bc7d7ae9b70804b2c6aa31827489d150f8e623ce82491b82085d7", size = 12060559, upload-time = "2026-07-22T22:18:20.147Z" }, + { url = "https://files.pythonhosted.org/packages/f3/9a/ffbd852d58bd74a617fe2f8ee6a58a96982271ce41cf981eab22190b4a4b/pandas-3.0.5-cp312-cp312-pyemscripten_2024_0_wasm32.whl", hash = "sha256:b2acb4650527eec6822c3dadb2b771277b65e7dae7a267d4bccf65fd1bb3fbce", size = 7197652, upload-time = "2026-07-22T22:18:22.502Z" }, + { url = "https://files.pythonhosted.org/packages/70/b5/d2d3e9ae73362ba4229651b0ee1455cf78073a1ce585f6ff693782ce263e/pandas-3.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:80a611068e8a3ac23f7398c6c14eb46dc974e5cc9997f653e2dcfd1da74edd41", size = 9831691, upload-time = "2026-07-22T22:18:24.534Z" }, + { url = "https://files.pythonhosted.org/packages/52/51/dea1e89d6a6796b9c43f85a09b484ee03edb8a4c4842e73e200a8c11301c/pandas-3.0.5-cp312-cp312-win_arm64.whl", hash = "sha256:25ff585b972a18ef1fe9ffa3ac6544d9950508aa76832e5147640b6022821e49", size = 9105796, upload-time = "2026-07-22T22:18:27.064Z" }, + { url = "https://files.pythonhosted.org/packages/bf/09/7b95c4a0025227d6f118c4039b423412ac6a982db02864166185d812fbc7/pandas-3.0.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c1c05a767fe8e5b4fe9e1c29806829c582052eaedb9120a3da83ba3f69e24a5b", size = 10385742, upload-time = "2026-07-22T22:18:29.346Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0c/dc78fd8c4da477b4b5e8ad37295af352190d21ef63a9ee1bc071753074cc/pandas-3.0.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b86765f268b56f7e665b93bce9d5df69dee7f99e595cf8fb839483ab315942a3", size = 9932067, upload-time = "2026-07-22T22:18:31.833Z" }, + { url = "https://files.pythonhosted.org/packages/3e/71/3592c055cf44df9808550f9368ceda80ff2b224d355ef73fe251dcda1802/pandas-3.0.5-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c597ecf5616b5c420372c1d4d4c00dbbfba7398bea857dcc984347e1ea48417b", size = 10466756, upload-time = "2026-07-22T22:18:34.195Z" }, + { url = "https://files.pythonhosted.org/packages/e3/70/4363150359f95b4cb4bcbb34ca23572bb5495749a621a8f3d5a1ddfd293c/pandas-3.0.5-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b11c36e218331d0387cbe3a0a5f75162357a1d92d57b2b08a336ff94b19b2be", size = 10938525, upload-time = "2026-07-22T22:18:36.81Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d0/317e7a0c67c0e69fa905a0161409397a7dc2d46ff611f6ca4803352c042b/pandas-3.0.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf52e1f61d229496da17dc7ab54acdee627357e7008fd4fecba3d0ba2937fa58", size = 11489303, upload-time = "2026-07-22T22:18:39.287Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8d/36dade89b49e4f9d5cbdbe863772581f98c0c6d78fc39ad4c557f6f2e17e/pandas-3.0.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:db172144bb56422bd157812f3b021eacc255451470b31e2c633c349490a1cfee", size = 11989004, upload-time = "2026-07-22T22:18:42.208Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ba/18c4ec8a746e177da05a9e7a7963781d8ea195780724f854601b6ebd6b78/pandas-3.0.5-cp313-cp313-win_amd64.whl", hash = "sha256:0d298e951f23016ce4699951d044ae6418dbc91bf68cefca0f77666fcbb4e5c6", size = 9826896, upload-time = "2026-07-22T22:18:44.539Z" }, + { url = "https://files.pythonhosted.org/packages/de/ec/28a57266b753799a87b8bc79e7887ac6fd981b8c6d2978a0b7e7b6bd708c/pandas-3.0.5-cp313-cp313-win_arm64.whl", hash = "sha256:66266d3442a5e8b3c90274c2b8b230bee42dd1c286bc822cc2f9f2c7e12b883e", size = 9094790, upload-time = "2026-07-22T22:18:47.468Z" }, + { url = "https://files.pythonhosted.org/packages/51/2f/cf6aae281264f4463f0875bcbb15fd2bb6d291cc535187dad1732475e4a9/pandas-3.0.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2f264fc46911cc8131a7322a16199bbf8e353d27c10bb211f5bd0c814324dc36", size = 10390034, upload-time = "2026-07-22T22:18:49.818Z" }, + { url = "https://files.pythonhosted.org/packages/06/ec/5189518c7a7659c4bdcc6b1eb32c46c6f3c86b0661ffd84143d1112c7732/pandas-3.0.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:53730687fcd161883b24e10411c06d6a4c0f2275d2faf3bb2bc25deb4ba8007c", size = 9980065, upload-time = "2026-07-22T22:18:52.249Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f1/598503ce8d7e3c35601e0747ba288c7864baae66380725bc12f13f884dfe/pandas-3.0.5-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:960d3ebcf249f75206899fcd2c6de53f736b7265759ced0d3e559df0b8b709b0", size = 10545532, upload-time = "2026-07-22T22:18:54.813Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/ceae2adf7034e07e9910299fe412e1819c4f0dd520700a888bcb03625448/pandas-3.0.5-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e94c2c5ca43bd3ca32bf64d32308887b65e5f9bfd8023ea52755107a999f93b", size = 10963120, upload-time = "2026-07-22T22:18:57.42Z" }, + { url = "https://files.pythonhosted.org/packages/66/25/86e0f4451874eb79e688deeebe3c451fec4557f8952005818d800ee8ac7e/pandas-3.0.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e819dd5f62966b481a8cb649d3299ebd886a1ea91ed5a99bf7ce77c98d18ab94", size = 11563178, upload-time = "2026-07-22T22:18:59.729Z" }, + { url = "https://files.pythonhosted.org/packages/f3/45/8643daa3b4147e433adfcccefdd0380d3aad79d86b15d8999730fe1944d5/pandas-3.0.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3c5ed2e7c06e91d340dfd091d7934f9bc82e4a36b95f647f090b9d1c9ac649da", size = 12028708, upload-time = "2026-07-22T22:19:02.164Z" }, + { url = "https://files.pythonhosted.org/packages/96/58/ad979ae617615576e8aafd569c9d4b62f1191d896e38f51d66ba06f3b89a/pandas-3.0.5-cp314-cp314-win_amd64.whl", hash = "sha256:cd8f7c6dc98527058ee6264219343f5392240a6f1bfa654fc5d79023020d0c92", size = 9951806, upload-time = "2026-07-22T22:19:04.596Z" }, + { url = "https://files.pythonhosted.org/packages/69/32/7ac03886b304049a9d2625ee88f59af760d8a93bd30ed9239bce7b9869a8/pandas-3.0.5-cp314-cp314-win_arm64.whl", hash = "sha256:5183427f5a8156d480f30333777bc978be93650a49a7c01db26adffe95b31e85", size = 9238297, upload-time = "2026-07-22T22:19:06.836Z" }, + { url = "https://files.pythonhosted.org/packages/be/ed/1d1f2ee5547d5167face2376d11c8b2a4c7bfff5a416ee7a9046891fab1e/pandas-3.0.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:303da736987d481074ca720ada325f8bd80c64ebc2d45ed79b29df3aaa4a26ca", size = 10849690, upload-time = "2026-07-22T22:19:09.391Z" }, + { url = "https://files.pythonhosted.org/packages/57/55/17e17152e98fbb0c4b1e562bc65387a2f20a80db0f4a86bf8d3a0e4248d4/pandas-3.0.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3b2801bbb049d0136f6c213eae02b5fca969384fc2064dd728d8620552aa49da", size = 10509945, upload-time = "2026-07-22T22:19:11.773Z" }, + { url = "https://files.pythonhosted.org/packages/88/90/817d44dbf83facf9556f33576d9af0a241981e7bb5c00606c0bcb5df8dda/pandas-3.0.5-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cce3a9d11d2b1f82c69a27ec1f4948a170e2c403c4bbfa8cca62e3fdebe2ef3a", size = 10392197, upload-time = "2026-07-22T22:19:14.024Z" }, + { url = "https://files.pythonhosted.org/packages/f1/da/889f00c0a6f5aa1545add70abbf01502dff87ab577adb855bd631c54d2f2/pandas-3.0.5-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef01af4d8dc6cd2c8d6c7736f149574ef93fe043811eeb5e445f2647154b5040", size = 10862726, upload-time = "2026-07-22T22:19:16.351Z" }, + { url = "https://files.pythonhosted.org/packages/bc/98/f1e934fb3c98fce859c6147c6785816c7b5b9ab7821115c5d8c4de9842b9/pandas-3.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e2759e890db96dfcffdbd9b86c3c2cb6afaf58def482820317e06163ec1066cd", size = 11414864, upload-time = "2026-07-22T22:19:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/fe/be/d448af7d657d82e1888dd8551f79c6d6fb161080b5b9752d84d910ec2319/pandas-3.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b58b1b39d46a5862e3fb18f50d1a201398619d16a0f9f73f57eea5583cf0e63c", size = 11925105, upload-time = "2026-07-22T22:19:21.515Z" }, + { url = "https://files.pythonhosted.org/packages/29/c1/ccb4238212c8c4f496c584f3044d94e0c030ed8e1d68999db46c91c2242f/pandas-3.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:1c10461f6eeb35d8f05b6184c65c8b9991663b66c46b1d559b682cb34ae7c6ea", size = 10387612, upload-time = "2026-07-22T22:19:24.257Z" }, + { url = "https://files.pythonhosted.org/packages/d2/cf/6a51b2c38980e04c279fd2fa908a1b0982064e860444acfca4ec2e2c8359/pandas-3.0.5-cp314-cp314t-win_arm64.whl", hash = "sha256:3c5015fd1730fbf883647e88068176c839c102cea883ba1769a6f4593bfc1f8c", size = 9509776, upload-time = "2026-07-22T22:19:26.694Z" }, +] + +[[package]] +name = "pathable" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/f3/5a20387de9bcd0607871bfc2198ee0e15836da7baa4592ccd7f24c27c986/pathable-0.6.0.tar.gz", hash = "sha256:6404b8b82aef5ff0fd478934137128b99b12212ba35afdde5525ca4f8388ea58", size = 18970, upload-time = "2026-05-19T18:15:11.911Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/e8/6d75ffd9784bce2e93d1ae4415649427e39a53bb172d4672b2b59c6f0a7b/pathable-0.6.0-py3-none-any.whl", hash = "sha256:82c4ca6c98c502ad12e0d4e9779b6210afee93c38990988c8c5d1b49bdcdf566", size = 18983, upload-time = "2026-05-19T18:15:10.728Z" }, +] + +[[package]] +name = "pillow" +version = "12.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/bf/fb3ebff8ddcb76aac5a01389251bbbb9519922a9b520d8247c1ca864a25d/pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965", size = 5345969, upload-time = "2026-07-01T11:54:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/d8/66/9a386a92561f402389a4fc70c18838bf6d35eb5eb5c6850b4b2dc64f5048/pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7", size = 4780323, upload-time = "2026-07-01T11:54:09.351Z" }, + { url = "https://files.pythonhosted.org/packages/25/27/ac8f99618ffd3dde21db0f4d4b1d2ab00c0880595bfd17df103f7f39fd0c/pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9", size = 6266838, upload-time = "2026-07-01T11:54:11.71Z" }, + { url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/51/8b08617af3ad95e33ce6d7dd2c99ed6c8298f7fb131636303956be022e25/pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c", size = 6344383, upload-time = "2026-07-01T11:54:15.756Z" }, + { url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" }, + { url = "https://files.pythonhosted.org/packages/20/20/25e0f4dc178a6bc0696793720055519a0de89e7661dae886992decbd2f81/pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f", size = 6472684, upload-time = "2026-07-01T11:54:19.839Z" }, + { url = "https://files.pythonhosted.org/packages/45/89/da2f7971a317f83d807fdd4065c0af40208e59e692cc43d315a71a0e96d1/pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09", size = 7227137, upload-time = "2026-07-01T11:54:22.025Z" }, + { url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" }, + { url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" }, + { url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" }, + { url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" }, + { url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" }, + { url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" }, + { url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" }, + { url = "https://files.pythonhosted.org/packages/dc/01/001f65b68192f0228cc1dbbc8d2530ab5d58b61037ba0587f946fea607cd/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330", size = 4161736, upload-time = "2026-07-01T11:54:51.156Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d2/0219746d0fd16fc8a84498e79452375be3797d3ce4044596ce565164b84f/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217", size = 4255435, upload-time = "2026-07-01T11:54:53.414Z" }, + { url = "https://files.pythonhosted.org/packages/c8/02/8d0bc62ef0302318c46ff2a512822d2610e81c7aa46c9b3abe6cbaca5ad0/pillow-12.3.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930", size = 3696262, upload-time = "2026-07-01T11:54:55.739Z" }, + { url = "https://files.pythonhosted.org/packages/85/e2/73c77d218410b14f5f2d565e8a998d5317b7b9c75368d29985139f7a46f0/pillow-12.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8", size = 5350344, upload-time = "2026-07-01T11:54:57.657Z" }, + { url = "https://files.pythonhosted.org/packages/c7/da/32c752228ae345f489e3a42499d817b6c3996da7e8a3bc7a04fc806b243b/pillow-12.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0", size = 4780131, upload-time = "2026-07-01T11:54:59.713Z" }, + { url = "https://files.pythonhosted.org/packages/b1/9d/8b2c807dbef61a5197c047afe99823787eb66f63daf9fb2432f91d6f0462/pillow-12.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321", size = 6263757, upload-time = "2026-07-01T11:55:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/5c/44/c85361f65dbe00eea8576ee467c768d25129989efb76e94f205e9ca9bb46/pillow-12.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b", size = 6936962, upload-time = "2026-07-01T11:55:03.93Z" }, + { url = "https://files.pythonhosted.org/packages/18/7e/e483414b35800b86b6f08dbbc7803fb5cd52c4d6f897f47d53ea2c7e6f65/pillow-12.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198", size = 6339171, upload-time = "2026-07-01T11:55:05.989Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f4/68c491844841ede6bed70189546b3ee9731cf9f2cbad396faff5e1ccba45/pillow-12.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130", size = 7048116, upload-time = "2026-07-01T11:55:08.131Z" }, + { url = "https://files.pythonhosted.org/packages/a3/34/77f3f793fed8efc7d243f21b33c5a3f0d1c97ee70346d3db855587e155ff/pillow-12.3.0-cp314-cp314-win32.whl", hash = "sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a", size = 6467209, upload-time = "2026-07-01T11:55:10.408Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e0/492879f69d94f91f60fc8cd05ba03650e9520afebb2fb7aa12777d7c7f38/pillow-12.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d", size = 7237707, upload-time = "2026-07-01T11:55:12.745Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ac/6b11f2875f1c2ac040d84e1bbf9cf22a88038f901ca1037898b280b38365/pillow-12.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838", size = 2565995, upload-time = "2026-07-01T11:55:14.736Z" }, + { url = "https://files.pythonhosted.org/packages/52/69/c2208e56af9bfc1913afb24020297a691eb1d4ef688474c8a04913f65e04/pillow-12.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e", size = 5352503, upload-time = "2026-07-01T11:55:17.076Z" }, + { url = "https://files.pythonhosted.org/packages/07/70/e5686d753e898a45d778ff1718dba8516ead6ab6b95d85fc8c4b70650cf2/pillow-12.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17", size = 4782956, upload-time = "2026-07-01T11:55:19.448Z" }, + { url = "https://files.pythonhosted.org/packages/d5/37/25c6692f06927ee973ff18c8d9ee98ad0b4d84ee67a09610c2dd1447958e/pillow-12.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385", size = 6322855, upload-time = "2026-07-01T11:55:21.613Z" }, + { url = "https://files.pythonhosted.org/packages/cc/91/420637fcb8f1bc11029e403b4538e6694744428d8246118e45719f944556/pillow-12.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c", size = 6989642, upload-time = "2026-07-01T11:55:24.006Z" }, + { url = "https://files.pythonhosted.org/packages/10/08/b94d7811281ccf0d143a1cf768d1c49e1e54af63e7b708ab2ee3eb87face/pillow-12.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d", size = 6391281, upload-time = "2026-07-01T11:55:26.252Z" }, + { url = "https://files.pythonhosted.org/packages/d2/87/24233f785f55474dc02ce3e739c5528a77e3a862e9333d1dd7a25cc31f70/pillow-12.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931", size = 7096716, upload-time = "2026-07-01T11:55:28.318Z" }, + { url = "https://files.pythonhosted.org/packages/23/26/fcb2f6e37175b04f53570b59937867e2b80ee1685e744023153028fc14f9/pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7", size = 6474125, upload-time = "2026-07-01T11:55:30.956Z" }, + { url = "https://files.pythonhosted.org/packages/90/de/3634abee5f1c9e13c56787b7d5517b0ba8d6de51700b95578cf338349c9f/pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c", size = 7242939, upload-time = "2026-07-01T11:55:34.044Z" }, + { url = "https://files.pythonhosted.org/packages/ce/2a/fd13f8eb24de5714a6eb444a3d67e2842c6c576e159a43793adf23051351/pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45", size = 2567506, upload-time = "2026-07-01T11:55:35.988Z" }, + { url = "https://files.pythonhosted.org/packages/5d/dc/8fdce34ec725a33c81c6ba122b904d6b9024e50ea9ac7bede62fab54506c/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139", size = 4162063, upload-time = "2026-07-01T11:55:37.941Z" }, + { url = "https://files.pythonhosted.org/packages/76/66/2044b9a63d3b84ff048228dfcb7cd9bf0df983e8470971bf7d4c57b693de/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402", size = 4255549, upload-time = "2026-07-01T11:55:40.022Z" }, + { url = "https://files.pythonhosted.org/packages/52/7e/1f67e6f4ece6b582ee4b539decbcc9f848dc245a93ed8cd7338bafef72f1/pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c", size = 3696331, upload-time = "2026-07-01T11:55:41.98Z" }, + { url = "https://files.pythonhosted.org/packages/12/40/d306fc2c8e4d45d7f175c77edca7063be7b86fe7fe6e68f4353bf71d808c/pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f", size = 5350370, upload-time = "2026-07-01T11:55:44.028Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/668fb1437e8ce420f62d6106eb66e44a5971602a4d794615bdf79315d82d/pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701", size = 4780147, upload-time = "2026-07-01T11:55:46.073Z" }, + { url = "https://files.pythonhosted.org/packages/0c/08/93fa2e70e30a2d81547e481b6ee2bb9522117221fb1e0ce4b5df70967677/pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace", size = 6273659, upload-time = "2026-07-01T11:55:48.264Z" }, + { url = "https://files.pythonhosted.org/packages/f8/6d/043e96ff814fc31a33077e4cba86082167db520c93632afdf2042febbb0c/pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4", size = 6947439, upload-time = "2026-07-01T11:55:50.503Z" }, + { url = "https://files.pythonhosted.org/packages/af/92/ba71d2ee2ac0edf3fa33bd9d5ee9ee080da70b1766f3ca3934f9938ddac9/pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39", size = 6353577, upload-time = "2026-07-01T11:55:52.697Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ce/e63064e2122923ff687c8ad792d0d736a7b3920a56a46982e81a7fdd25d6/pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71", size = 7060394, upload-time = "2026-07-01T11:55:55.149Z" }, + { url = "https://files.pythonhosted.org/packages/54/76/a09cc3ccc8d773a7283d34c38bec1708f9e3cc932093cbc4c5e71ac4060b/pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827", size = 6467375, upload-time = "2026-07-01T11:55:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/1846c49ba3b1d5550392a4bbd06d6fb4578e1cd91a803198b5c90f5f7d53/pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5", size = 7237048, upload-time = "2026-07-01T11:55:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bb/89f35dcc79610423f9f195504d7def7f0d1416a711541b42867e25fe3412/pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658", size = 2566006, upload-time = "2026-07-01T11:56:02.143Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/707027ba09942dfa2c28759b5c222d769290a41c6d20ea60ec250801941f/pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf", size = 5352509, upload-time = "2026-07-01T11:56:04.2Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6d/00352fa25332c2569cd387851f568cc5a4b75a9adbfb37ac4fbce4c02eec/pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64", size = 4783167, upload-time = "2026-07-01T11:56:06.631Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/9e049dfa21af7c22427275720e2490267ba8138120add5c4c574deb69782/pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e", size = 6329237, upload-time = "2026-07-01T11:56:08.868Z" }, + { url = "https://files.pythonhosted.org/packages/36/16/cf6eeaae8d0fce8dd390a33437cf68c5d5bd73834a2bc6e2f14efda0ab45/pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777", size = 6997047, upload-time = "2026-07-01T11:56:11.379Z" }, + { url = "https://files.pythonhosted.org/packages/1e/69/dbf769bdd55f48bf5733cac28edc6364ffaa072ec9ba336266e4fe66be55/pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1", size = 6400440, upload-time = "2026-07-01T11:56:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e1/ffc9cfc2eea0d178da8018e18e959301ad9d6bc9f3edb7181e748a474b97/pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9", size = 7105895, upload-time = "2026-07-01T11:56:16.575Z" }, + { url = "https://files.pythonhosted.org/packages/18/f0/a5595c1e8c3ae44b9828cb2f0fa8155e5095ef04d6327b8f61cf44a3df85/pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8", size = 6474384, upload-time = "2026-07-01T11:56:18.855Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/62bcd9f844984c5938d3b05264a61d797a29d3e0812341a8204af70bbdee/pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418", size = 7243537, upload-time = "2026-07-01T11:56:21.214Z" }, + { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.11.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/18/f3bb8ef0d3b930692343da8aa4d3cbcd6749477c053959395ac81965a6e9/platformdirs-4.11.8.tar.gz", hash = "sha256:f23abafea7dd4276d1f29104b83598d7dcc567cafd07c9c951e66665645437fc", size = 37182, upload-time = "2026-09-08T22:20:42.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/e1/5b7b8bbb55084d1425bcb9bc823ff519e1b2be05f6ebb0089e2eacc38413/platformdirs-4.11.8-py3-none-any.whl", hash = "sha256:52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081", size = 24027, upload-time = "2026-09-08T22:20:41.537Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887, upload-time = "2026-05-08T21:00:11.277Z" }, + { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654, upload-time = "2026-05-08T21:00:12.604Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190, upload-time = "2026-05-08T21:00:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995, upload-time = "2026-05-08T21:00:15.526Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422, upload-time = "2026-05-08T21:00:16.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342, upload-time = "2026-05-08T21:00:18.362Z" }, + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, + { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588, upload-time = "2026-05-08T21:00:21.155Z" }, + { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029, upload-time = "2026-05-08T21:00:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774, upload-time = "2026-05-08T21:00:24.001Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532, upload-time = "2026-05-08T21:00:25.545Z" }, + { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592, upload-time = "2026-05-08T21:00:27.186Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788, upload-time = "2026-05-08T21:00:28.8Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018, upload-time = "2026-05-08T21:00:31.622Z" }, + { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322, upload-time = "2026-05-08T21:00:32.918Z" }, + { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172, upload-time = "2026-05-08T21:00:35.124Z" }, + { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457, upload-time = "2026-05-08T21:00:36.355Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835, upload-time = "2026-05-08T21:00:38.072Z" }, + { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545, upload-time = "2026-05-08T21:00:39.319Z" }, + { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886, upload-time = "2026-05-08T21:00:40.621Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261, upload-time = "2026-05-08T21:00:41.775Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184, upload-time = "2026-05-08T21:00:43.018Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534, upload-time = "2026-05-08T21:00:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500, upload-time = "2026-05-08T21:00:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994, upload-time = "2026-05-08T21:00:47.093Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884, upload-time = "2026-05-08T21:00:48.376Z" }, + { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464, upload-time = "2026-05-08T21:00:49.954Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588, upload-time = "2026-05-08T21:00:51.281Z" }, + { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667, upload-time = "2026-05-08T21:00:52.602Z" }, + { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463, upload-time = "2026-05-08T21:00:54.303Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621, upload-time = "2026-05-08T21:00:55.808Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649, upload-time = "2026-05-08T21:00:57.061Z" }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636, upload-time = "2026-05-08T21:00:58.646Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872, upload-time = "2026-05-08T21:00:59.889Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257, upload-time = "2026-05-08T21:01:01.195Z" }, + { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696, upload-time = "2026-05-08T21:01:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378, upload-time = "2026-05-08T21:01:04.475Z" }, + { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283, upload-time = "2026-05-08T21:01:05.959Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616, upload-time = "2026-05-08T21:01:07.228Z" }, + { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773, upload-time = "2026-05-08T21:01:08.514Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664, upload-time = "2026-05-08T21:01:09.874Z" }, + { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643, upload-time = "2026-05-08T21:01:11.132Z" }, + { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595, upload-time = "2026-05-08T21:01:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711, upload-time = "2026-05-08T21:01:13.676Z" }, + { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247, upload-time = "2026-05-08T21:01:14.936Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102, upload-time = "2026-05-08T21:01:16.281Z" }, + { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964, upload-time = "2026-05-08T21:01:17.57Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546, upload-time = "2026-05-08T21:01:18.946Z" }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330, upload-time = "2026-05-08T21:01:20.162Z" }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521, upload-time = "2026-05-08T21:01:21.399Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662, upload-time = "2026-05-08T21:01:22.683Z" }, + { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928, upload-time = "2026-05-08T21:01:23.986Z" }, + { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650, upload-time = "2026-05-08T21:01:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912, upload-time = "2026-05-08T21:01:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300, upload-time = "2026-05-08T21:01:27.937Z" }, + { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208, upload-time = "2026-05-08T21:01:29.484Z" }, + { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633, upload-time = "2026-05-08T21:01:31.068Z" }, + { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724, upload-time = "2026-05-08T21:01:32.374Z" }, + { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069, upload-time = "2026-05-08T21:01:33.67Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099, upload-time = "2026-05-08T21:01:34.915Z" }, + { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391, upload-time = "2026-05-08T21:01:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626, upload-time = "2026-05-08T21:01:37.545Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781, upload-time = "2026-05-08T21:01:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570, upload-time = "2026-05-08T21:01:40.415Z" }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436, upload-time = "2026-05-08T21:01:41.654Z" }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373, upload-time = "2026-05-08T21:01:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554, upload-time = "2026-05-08T21:01:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395, upload-time = "2026-05-08T21:01:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653, upload-time = "2026-05-08T21:01:47.307Z" }, + { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914, upload-time = "2026-05-08T21:01:48.573Z" }, + { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567, upload-time = "2026-05-08T21:01:49.903Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542, upload-time = "2026-05-08T21:01:51.204Z" }, + { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845, upload-time = "2026-05-08T21:01:52.539Z" }, + { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985, upload-time = "2026-05-08T21:01:53.847Z" }, + { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999, upload-time = "2026-05-08T21:01:55.179Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779, upload-time = "2026-05-08T21:01:57.489Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796, upload-time = "2026-05-08T21:01:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023, upload-time = "2026-05-08T21:02:00.228Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448, upload-time = "2026-05-08T21:02:01.888Z" }, + { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329, upload-time = "2026-05-08T21:02:03.484Z" }, + { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172, upload-time = "2026-05-08T21:02:04.745Z" }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813, upload-time = "2026-05-08T21:02:06.025Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764, upload-time = "2026-05-08T21:02:07.353Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140, upload-time = "2026-05-08T21:02:09.065Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +] + +[[package]] +name = "protobuf-py" +version = "0.1.1" +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/72/ed/02fd902d9c51b7ff53dfc9a745eb11490722edfd30073af889e171f07b8e/protobuf_py-0.1.1.tar.gz", hash = "sha256:6bd08ac4d8f1661965bbe2685429d79043704cdd1ee720a7a89617331742240b", size = 133525, upload-time = "2026-06-24T19:02:15.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/00/1b3775aca1c70e3007e06ef5996f6bb9b3a32341eb0cce3ffb6effad8dec/protobuf_py-0.1.1-py3-none-any.whl", hash = "sha256:efc4f50f275ed6dae10a1f30bb81ad1a75368557b3ff22a532b7a472050368f1", size = 181656, upload-time = "2026-06-24T19:01:29.556Z" }, +] + +[[package]] +name = "protobuf-py-ext" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/05/6dc9ccff1e8159eb9a144e6d3c4acfd2211cd4fcd20c34fe9155d17d6a7f/protobuf_py_ext-0.1.1.tar.gz", hash = "sha256:e85bfdfdb3ed50634db8ccc7429dd9286520109489c735463971a418707b4fef", size = 31912, upload-time = "2026-06-24T19:02:16.321Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/70/2b5d62a60a2e0d88ecde1ae98db3132bcd2672fb39c8d581b82b34ac0bd8/protobuf_py_ext-0.1.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:310039e03cb15181781a0b78017419f6d4ee302e988c3c70b87f1facdf05532d", size = 306008, upload-time = "2026-06-24T19:01:31.399Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d6/73c06bb4cac2e04c0adc154965fe8b6520224bd737fd7e73bba405056321/protobuf_py_ext-0.1.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ec8348d1ba045f79b2fedd14e40cca36f2a41b52f2c4fdf55a60c58add2353", size = 314769, upload-time = "2026-06-24T19:01:32.89Z" }, + { url = "https://files.pythonhosted.org/packages/18/c5/e4e6bc6096b66d1c82639a1b501147f16ee65d32567304b987d002e2c666/protobuf_py_ext-0.1.1-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:182798e4861aba72d05855bd06febe4926aa7265e6f444a1b8af5252beee4f7e", size = 328122, upload-time = "2026-06-24T19:01:34.394Z" }, + { url = "https://files.pythonhosted.org/packages/b0/4b/1d792a40d0f0a0f914f1dfa8bb5e9573ca0ecd5fe5cecf80d77193212abd/protobuf_py_ext-0.1.1-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9eb25c3a329c0551cc86b209a5e5d8ecb8d834b9924a3aa019377853a703b6d3", size = 492338, upload-time = "2026-06-24T19:01:36.103Z" }, + { url = "https://files.pythonhosted.org/packages/52/51/5ad329223c905e5c530cae38ae24cde3584d8ab7e09457f2a01afce80e60/protobuf_py_ext-0.1.1-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:aaecbde82bef10c7c40578cbb61b7a19896bf7fa450972050a3bb302acb7d5d6", size = 541578, upload-time = "2026-06-24T19:01:37.481Z" }, + { url = "https://files.pythonhosted.org/packages/ed/c7/01bd8a8bfe2df4b07aafac12ccfb7b7ebe2303f65296a9e02fcafa28ff3e/protobuf_py_ext-0.1.1-cp310-abi3-win_amd64.whl", hash = "sha256:6b0c615c48e95acc53cf33e9310eeaff8b30d2d7555bf93e7bca8fb4f40e9a5c", size = 251319, upload-time = "2026-06-24T19:01:38.946Z" }, + { url = "https://files.pythonhosted.org/packages/24/dc/914065538b5db54b6a920b5af38c1ef142252ea1eea711820435fa259fac/protobuf_py_ext-0.1.1-cp310-abi3-win_arm64.whl", hash = "sha256:72956cd0af5dee24b41c6f5ba5e42622d17e6d555002b5efc1634e27a1446de2", size = 241635, upload-time = "2026-06-24T19:01:40.301Z" }, + { url = "https://files.pythonhosted.org/packages/13/a5/0b5d73cab815fd50615cb87a02e04e8332f9e27380c890aba1459cf7e919/protobuf_py_ext-0.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f0cd58620f415a3534d195358338f4999a774e12510f91c592b38d13d37388", size = 304903, upload-time = "2026-06-24T19:01:41.796Z" }, + { url = "https://files.pythonhosted.org/packages/37/a9/14c120b9ac36a0e11bb05e482fa6ae322de583a8e1068e4859035c289947/protobuf_py_ext-0.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21572764f625d829604fc4d83635f533f35775f11c6da142345b3f3c8d64bd09", size = 316148, upload-time = "2026-06-24T19:01:43.247Z" }, + { url = "https://files.pythonhosted.org/packages/35/54/7c00a1a9783c3ba74f6b2f5d2f049f09037bbd489c465c09a34f32fb611b/protobuf_py_ext-0.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7f14f00c2678bfbe7ad46f057b9f9938c1677bcf39e405e163e272c6f9814b8", size = 326458, upload-time = "2026-06-24T19:01:44.655Z" }, + { url = "https://files.pythonhosted.org/packages/0d/0e/81fa50c0d6c4d664e5be6d0a3c4f2c7edfef87ab12d0bac764abca1f2955/protobuf_py_ext-0.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1610865622e2e27568277ca63d8d2d23dcc55eaa767398865c21539ddf4ce24d", size = 493596, upload-time = "2026-06-24T19:01:46.344Z" }, + { url = "https://files.pythonhosted.org/packages/56/19/3183cf6e4de62846c20549654a1d573dae51ca06aaf7fed06accdfab74f6/protobuf_py_ext-0.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8995efba9476e1ea18ef9873306871dba697308994bc2766558fec3387acc3de", size = 539656, upload-time = "2026-06-24T19:01:48.21Z" }, + { url = "https://files.pythonhosted.org/packages/a4/6c/09841f3dbe7c3d4b3f2779f8f2832ac23fb96ac8ab71674e91af732cf9ff/protobuf_py_ext-0.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ba61d49dace8f874361583030a7c48139b42eb37c9ffbb1e7e8a227a51576f44", size = 305017, upload-time = "2026-06-24T19:01:49.84Z" }, + { url = "https://files.pythonhosted.org/packages/52/43/e450b5e202f6715274acc9acd9f9769908cbdeab861a53c798fcbd467d35/protobuf_py_ext-0.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a9c2f026096ca4c595c89a297067ef371e241d3ff8e1f6d7c779aa8419cdca7", size = 316215, upload-time = "2026-06-24T19:01:51.249Z" }, + { url = "https://files.pythonhosted.org/packages/97/5c/23e79b35f1b5755b9bdc0c0c9b8cd8abababaef1a1639608d8a96bb61a9d/protobuf_py_ext-0.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf78707a040b9294e5e1ec4a1875f0046acfe52e92150cea27de4e9fc9db39bb", size = 326419, upload-time = "2026-06-24T19:01:52.93Z" }, + { url = "https://files.pythonhosted.org/packages/e8/de/5493de0ec12920a3b1dd72608ce0c1e8cdb7e0eb9e87accaecc5216df29e/protobuf_py_ext-0.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ae4373845cbb85bdade3ef5368cc2f4b5f80bf173383afc1ab063f95644e5599", size = 493734, upload-time = "2026-06-24T19:01:54.301Z" }, + { url = "https://files.pythonhosted.org/packages/98/89/31da55b414e6332aad11d858e9a86bd36fbb324f52fa3fc6d8f1c57840a9/protobuf_py_ext-0.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2a4cc478eef7a2acc1daaebcde479a3ac2396d47e6bdc7e776ca4c4147ba8b4c", size = 539703, upload-time = "2026-06-24T19:01:55.707Z" }, + { url = "https://files.pythonhosted.org/packages/1c/71/39f231838ef06476d46ac40dd814894277a732a3688c0a0c994850b3b62f/protobuf_py_ext-0.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:359dccdc1c3eafed2a913c570bb082b8df848a1d27d548ce8385f246a7d68be2", size = 302145, upload-time = "2026-06-24T19:01:57.116Z" }, + { url = "https://files.pythonhosted.org/packages/87/f1/01c81ff5f420600366a0e6a613ce2af20834534d2b3d7523f4f3b4ddb54f/protobuf_py_ext-0.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91c38b4a10a306366443273ee03ca554537a0965bf05b7ada8e7dbdee04cc93e", size = 314541, upload-time = "2026-06-24T19:01:58.745Z" }, + { url = "https://files.pythonhosted.org/packages/86/d8/1cbf5a0298ceddc0cdbb28bf1ecaf8bd3cff54ed4ced6a1392d5226172fc/protobuf_py_ext-0.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79eee3bcbb289d6ea114eb8fe3a1469c5b59bf53e207238469f567d9c53ba56f", size = 325092, upload-time = "2026-06-24T19:02:00.382Z" }, + { url = "https://files.pythonhosted.org/packages/af/23/8b1694616044cbfec2d18d4c6fffb03e05089c8bdb5970c6bafd60cc7fd5/protobuf_py_ext-0.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:10992141a8282a71ac3e3530d1a489efb27618d84000b9a47918cf70e5816d9b", size = 491684, upload-time = "2026-06-24T19:02:01.862Z" }, + { url = "https://files.pythonhosted.org/packages/49/d8/99997a7a6cf6989c944d9183c5deb6a045c27460add0316c7b34763891b3/protobuf_py_ext-0.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4411ffe0e06a774b83c5c71c546ce097640a25f596c45f95f53d3e3148e3f22d", size = 538048, upload-time = "2026-06-24T19:02:03.362Z" }, + { url = "https://files.pythonhosted.org/packages/15/3c/9e99ecbb68e1d5b46016d821eb1cbba9a91e6b50e71e75faf0c1e6189f0d/protobuf_py_ext-0.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55a17d80ea419501ff221a6627523f38a431bb33e6aa3de81ae3a7f271c49c75", size = 296337, upload-time = "2026-06-24T19:02:04.787Z" }, + { url = "https://files.pythonhosted.org/packages/af/f2/305338a28225fb54b28d6c3f5948109b9088b329a2b6f77ca610c2bdcd34/protobuf_py_ext-0.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbb518b5638403ceaa08ac4fc7dac626f45ed9b856b3517de3906cf3de4d632", size = 308961, upload-time = "2026-06-24T19:02:06.185Z" }, + { url = "https://files.pythonhosted.org/packages/60/f9/416103c93677ff2ea407704ea64fa6de9e700dc030c48360a182d91ce373/protobuf_py_ext-0.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a4adc65ab6a5e4885c67fc808bcacb83d755d05b566d312a0e10c2a873f2ad4", size = 321895, upload-time = "2026-06-24T19:02:07.714Z" }, + { url = "https://files.pythonhosted.org/packages/5a/83/eb3e81bb2f83834b7deff4cee2d56eeb1adcbc847492a56b7f910ab257db/protobuf_py_ext-0.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e97f45c49676efacfb8e95bfcb1a002bc337e618a6780be1e55df2ba5ebd2f1e", size = 486091, upload-time = "2026-06-24T19:02:09.243Z" }, + { url = "https://files.pythonhosted.org/packages/99/96/bd88fca38556b3105e4dd41d6a176e31dcc583fe979e830aeedd2f8c20ee/protobuf_py_ext-0.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:53a6b3590f6aa7f97b8ed60f62fef9b096babfeae82f7283fd3a4c405827d4f8", size = 534582, upload-time = "2026-06-24T19:02:11.227Z" }, +] + +[[package]] +name = "py-key-value-aio" +version = "0.4.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beartype" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/e2/d689d922894a7ecde73b6daeaf9b13dab5aae06fe6aaaf7514722644d382/py_key_value_aio-0.4.5.tar.gz", hash = "sha256:c6563a2c6abe5da5e20f4f9e875c2a9b425a2244a54fadbf46cf140a9eea45d7", size = 107547, upload-time = "2026-05-27T16:37:08.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f6/95/b8ba862968712caa12a19666175334fa979e1f198b896a430adb3bacfe87/py_key_value_aio-0.4.5-py3-none-any.whl", hash = "sha256:ab862adbcb8c72547d1c57821f22cbbb71ab86509039c96f36e914e0336c8dd7", size = 170005, upload-time = "2026-05-27T16:37:06.629Z" }, +] + +[package.optional-dependencies] +filetree = [ + { name = "aiofile" }, + { name = "anyio" }, +] +keyring = [ + { name = "keyring" }, +] +memory = [ + { name = "cachetools" }, +] + +[[package]] +name = "pyarrow" +version = "25.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/e3/27f57f80141379d60defe6703eb50a707325706f07fedfd1312c7a751995/pyarrow-25.0.1.tar.gz", hash = "sha256:9150a83248bfed9813ea3c3af74c3856c1984d444aa28e58bf7733b9750ddf6a", size = 1201653, upload-time = "2026-08-10T12:40:53.904Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/e2/9ab15b88cbfac28e16419ce5439ec29234c5172cb8259301b4ba639bdec0/pyarrow-25.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:df961f2e7ae9cf496459259d798652c70625f6c080650d6952f8c04053c58ee9", size = 35861559, upload-time = "2026-08-10T12:38:02.567Z" }, + { url = "https://files.pythonhosted.org/packages/58/79/a0036dbe1eabe1f73127427342f1d99982584c4a2cde2651d6c93499c6f6/pyarrow-25.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:cc4aa407fde9fc660be3939e49ea31f50f3e9fec17c0ec63159f7711edd3efc9", size = 37628383, upload-time = "2026-08-10T12:38:09.083Z" }, + { url = "https://files.pythonhosted.org/packages/13/49/d93a57d375f4bf0cf82913dd6bb54acafde83dd993be2282c81ac5616cad/pyarrow-25.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:4340f0ba6c1d2e13f21658de1d7c662ca2545018568d0030a1e9afca159d87e3", size = 46820190, upload-time = "2026-08-10T12:38:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/60/c9/711ca85d79f1ec98f29a5eae2b051e25b4ecec5de3e3c0e2d5c5dcb15664/pyarrow-25.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:5389cdf79447ed1515c9e31620e6e1e2302249564d603f2ad727d4f6d313e4c3", size = 50102437, upload-time = "2026-08-10T12:38:22.487Z" }, + { url = "https://files.pythonhosted.org/packages/80/53/8fb8359ff17cfb6263a1cf3ebf7caec9fe197de118719e84fcb1d0618026/pyarrow-25.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d51592cb7561e87877c506113e7adbf1342ab579e6c21f0ef44b8ba41cb74c80", size = 49942424, upload-time = "2026-08-10T12:38:28.755Z" }, + { url = "https://files.pythonhosted.org/packages/e8/83/4e5ae02a9341571b18a6fca380ac7a58ce6ddae7ab3c060208c0a1e79f02/pyarrow-25.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6109c94d8b9f3b17a041daca16cacb2f651ad8f1ef70a4232c2c0f37a23da2a8", size = 53144206, upload-time = "2026-08-10T12:38:34.862Z" }, + { url = "https://files.pythonhosted.org/packages/65/ee/197cbf47e49f83e6ebeb946a5259a48a638dea27ac774db42fe78022179d/pyarrow-25.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:8858d7bfc22e3f51529aeaa4077225029724623e4595dc9eff8c793935c34140", size = 27953934, upload-time = "2026-08-10T12:38:39.808Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8d/8f271a7a034c834910ec925d56fa4b29733b1380f5289419f5aaa3b02777/pyarrow-25.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:c7c534ec03c358a76ea3e505e74c1b6aef290af90c444dfd092dbfe23e755b85", size = 35855328, upload-time = "2026-08-10T12:38:45.489Z" }, + { url = "https://files.pythonhosted.org/packages/d2/cd/5bac242f4e841b9971d5eb94fdfe2577e2b70be983e27401e72055786037/pyarrow-25.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:dda9470024204d7bbf2042b47c6e8a0e47a3eeb8e34405882dfaea6577e0c153", size = 37622415, upload-time = "2026-08-10T12:38:51.107Z" }, + { url = "https://files.pythonhosted.org/packages/63/1f/96d03b4e1506524f7087adb0fd6b2f69f0c9c7aaff1ec36d8030082e15a5/pyarrow-25.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:44a9120ce5bd81936b8ab9a88076e3fd47c2c6838e0e43630fed83626aca81d9", size = 46813813, upload-time = "2026-08-10T12:38:57.773Z" }, + { url = "https://files.pythonhosted.org/packages/98/d6/33a411115b61dbfc16ad6ad73e71730f6fea654ee3667673bc53ab0e2fe7/pyarrow-25.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0befcf816e45a1af33ac775a9970b749e4868a230c7372f0ae5e932bee27039f", size = 50104452, upload-time = "2026-08-10T12:39:04.579Z" }, + { url = "https://files.pythonhosted.org/packages/33/ae/b1b97c9ca87f9f9ddbb5230c798df94eccce61bd79b9b45458c69a478588/pyarrow-25.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3f89685964f46e4216103c75483aac0c0692a5f72212d7ca835adba5ede56ce3", size = 49951343, upload-time = "2026-08-10T12:39:11.8Z" }, + { url = "https://files.pythonhosted.org/packages/98/9e/a112df5cfd5a68cb1d9fc31cfe38c28d5aec9f10865ce37ecef2e4450873/pyarrow-25.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6943e2fe7954d29d84de45d29d34c8dc36ce96570e67d89aa9976e650a4a9138", size = 53144784, upload-time = "2026-08-10T12:39:20.503Z" }, + { url = "https://files.pythonhosted.org/packages/31/24/97e8bd98f1e3b07e2ba08bcdff690674fbe16d69a7d2712cc3884665e615/pyarrow-25.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:31e49a7888fcdf3a835da33ae777f6bb9a866334e5a789282fc26dcf426f7f15", size = 27870159, upload-time = "2026-08-10T12:39:26.161Z" }, + { url = "https://files.pythonhosted.org/packages/36/4c/b525824ad3094076919273cd97db61fb3d78252dee76fa3b8dc8f76774aa/pyarrow-25.0.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:bf0b672390cdcb640d7288f96b826d71ff4e9abb254a86c89890baf51a29cee6", size = 35885255, upload-time = "2026-08-10T12:39:32.366Z" }, + { url = "https://files.pythonhosted.org/packages/08/62/448bb0e940de41aec31d1a956e63ad9c54afdf122a103cc3ab20c2a3ce33/pyarrow-25.0.1-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:38a9a4b4b9613380e200641891495a56c3d5a98a092db4a870af9975e220471d", size = 37644461, upload-time = "2026-08-10T12:39:38.142Z" }, + { url = "https://files.pythonhosted.org/packages/6e/9a/13587e38bd4806fd218f50fd13b8903fab60588a699ff0c406372e5b4043/pyarrow-25.0.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:0b726ad7e7b669be982b0c71c07fe4b037d654354130da79a7902a669e93a66b", size = 46877146, upload-time = "2026-08-10T12:39:43.722Z" }, + { url = "https://files.pythonhosted.org/packages/8d/61/1c5d1229fa21da4cff5365e41e57177aaac57c563c727f35419b8513d1c1/pyarrow-25.0.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:9171748cdf796972d85a4b60157c279913e242992e350c90c7450182a9838b2a", size = 50131616, upload-time = "2026-08-10T12:39:49.304Z" }, + { url = "https://files.pythonhosted.org/packages/43/20/291e1d65cc0b09aa19f03cf25cf51a2f5fa94b5db315178f2d254ed5cad4/pyarrow-25.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b7a296aac7a71fa0886c08e155ddb6c636a50013f801f6178daafa0f9e726188", size = 50008879, upload-time = "2026-08-10T12:39:56.891Z" }, + { url = "https://files.pythonhosted.org/packages/8b/7c/1b7c9ec28e76576337e4f97b31141c9a181b89b6d1d6221e9d8205621a58/pyarrow-25.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0fe7c8b6c03969b49c8c66182e4a18e3819ab92d07cfab5d8370c531b9369ef0", size = 53170864, upload-time = "2026-08-10T12:40:04.918Z" }, + { url = "https://files.pythonhosted.org/packages/b7/75/f3d789dc06011a765d14d86bda799cf72ac1d715b6a6edecaa0d73d95062/pyarrow-25.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:f729cfdbd36fd99d543b67a914d2de044c84ebe45be8b34902b299b608c15c8f", size = 28620729, upload-time = "2026-08-10T12:40:51.41Z" }, + { url = "https://files.pythonhosted.org/packages/fc/05/647a8ee6f7c2662feb6921315617bc04dcd6034763fb61b1199720bf6162/pyarrow-25.0.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:59a2de54c0cbd954da861eee4d1d330f8e909c45b53455baef696380f2c55033", size = 36130288, upload-time = "2026-08-10T12:40:11.014Z" }, + { url = "https://files.pythonhosted.org/packages/93/f8/c9ee997554d7bea94520667dd1933f109ac1da3ee3556d2b49381e023484/pyarrow-25.0.1-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:35935cd5de130aa5cf4dea052a63e6bf2e17006c35c3a468194242b9b2bf5956", size = 37762187, upload-time = "2026-08-10T12:40:16.592Z" }, + { url = "https://files.pythonhosted.org/packages/a2/08/a28c01c7fe9e96e8233ce2d13df1d402f4f999f848f51d2daacd6bb4c036/pyarrow-25.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:f3831aaa25c67a99f99dc8b05873cb9d64560390372e2aa197ce9dd4a3f06a44", size = 46888003, upload-time = "2026-08-10T12:40:23.242Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b9/58612e977d28dc58c878448866838369ee8da2f1e7cc8ed2c84b952aafee/pyarrow-25.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6a1fdfc6659b6b19022f2e50627fb5cf7156a66c46bf4299379955cbe742382a", size = 50079036, upload-time = "2026-08-10T12:40:29.169Z" }, + { url = "https://files.pythonhosted.org/packages/72/13/66e1402dcc860e1dc2760b1e0292c9a569b62b3bccab69def1b3e907d006/pyarrow-25.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:169d3429d5be7c752125890620f75a60776d38b0035eddae939651640822332e", size = 50040226, upload-time = "2026-08-10T12:40:35.186Z" }, + { url = "https://files.pythonhosted.org/packages/78/10/3f1a5497a7ef732ab0f03ecca3e66d89d9c0f57fdc61b4794c456b781f01/pyarrow-25.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:119297a6dc197e45d9c6d4415f7814a67ffa36c180d26f68c154c58067ae782d", size = 53149035, upload-time = "2026-08-10T12:40:41.454Z" }, + { url = "https://files.pythonhosted.org/packages/93/c0/37d4a7e8e2f7a6076283673d5298018ca26478b934c6ee369e10505ab32c/pyarrow-25.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4288f27577352d608ca08553b0865e4a9b3aa14820c5d95b53337218d609835b", size = 28753071, upload-time = "2026-08-10T12:40:46.623Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.13.5" +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/53/ef/fc4f868f4e2cee79f863883abffceff107875f569b848507319842d2a681/pydantic-2.13.5.tar.gz", hash = "sha256:51a9c5f7b2f8e636f04c6cada605d9b6a3bf1348fdf945a3d8869b19bba0ee08", size = 845750, upload-time = "2026-08-28T14:04:00.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/47/c95ffc2009878c7aac0c5e08528022dcb885933252a88b5f170058014464/pydantic-2.13.5-py3-none-any.whl", hash = "sha256:346a034f080da3755d8e9cb5e00e8b07de1d39e4f6e2c87d8ab7cafa0b269a73", size = 472589, upload-time = "2026-08-28T14:03:59.136Z" }, +] + +[package.optional-dependencies] +email = [ + { name = "email-validator" }, +] + +[[package]] +name = "pydantic-core" +version = "2.46.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/f9/8a06bea35ef8daf588f707784c973a7046e0034c8d8cfb08828eeffb8b75/pydantic_core-2.46.5.tar.gz", hash = "sha256:10416c15b8839ecc4ef4d0885da76da6fd0f67333a0eb8aff6d93c4b8f2910fc", size = 472262, upload-time = "2026-08-28T10:01:31.677Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3f/76358795aa7a8c6d4f36e2cb828ad1c90ee118e1393a9281664f5aade9d4/pydantic_core-2.46.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b9fe6fb92520e3fd61f2e49000b6911b188824f089b75973ea06d6267f0b476d", size = 2076516, upload-time = "2026-08-28T09:58:21.576Z" }, + { url = "https://files.pythonhosted.org/packages/db/50/26b091836076ce4cb2fac264186936acc069e0595772cfd02a563bc4761a/pydantic_core-2.46.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a39ac25a9a2fa4072efdb429833c4a4c8009a51ff9eea3eeae131713cd27991e", size = 1922874, upload-time = "2026-08-28T09:58:23.766Z" }, + { url = "https://files.pythonhosted.org/packages/09/f0/2a8ce3849e299d44e2d2c196b6082643a3235565a735cb51db7a6261f614/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4fdc8b93a41521988916eeaa271173fcca7fa0803d62f87675aac8dcec1c8e29", size = 1951772, upload-time = "2026-08-28T09:58:25.435Z" }, + { url = "https://files.pythonhosted.org/packages/87/46/ac0dc8bdd9e6048183a14eb127764e7ad9240021c17513074a4711b0e31e/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b98134087d9de723658d17a42c7d0da8d6e2ef08015dee7dc93889047315f5e4", size = 2031832, upload-time = "2026-08-28T09:58:27.102Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c2/339de5bef7be36301a2231eaa52e62163742c2281f11b5f4892bc79785cd/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e652ab17569c94bff5475520f907b7148b8c24036a8ebbe5cf7cf7493d28579a", size = 2208645, upload-time = "2026-08-28T09:58:28.948Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a0/9ff22b797724262da14427abaed4dd1d864a139693fc5e7809114376a716/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d925f3d9afd05a8c0fb3a1031463a8d59ebe5e2afad297e29c78be19e13b4e62", size = 2265935, upload-time = "2026-08-28T09:58:30.625Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a4/eb9409ec0736e50aa70a412f16c204ed149516846912f7e6724d4c73ee53/pydantic_core-2.46.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fc5be0abd4a407e200d844b404e33639a554e7bd0d448e7b9ae181be4789ac2", size = 2066284, upload-time = "2026-08-28T09:58:32.289Z" }, + { url = "https://files.pythonhosted.org/packages/c0/02/7f6156ffc926857f1c37c07d9a388682865a81830ab6a1b637082c25e399/pydantic_core-2.46.5-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:816ff0a6550ffc06c098ccd2e0698600f9aa7da192a79eaa6f9af504a35db869", size = 2105889, upload-time = "2026-08-28T09:58:33.986Z" }, + { url = "https://files.pythonhosted.org/packages/92/b1/e781d357ebe09fc929f995700f1b3503e8897f1cece183ecb1300d4d67e9/pydantic_core-2.46.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c7ea57fc63aa7da93a1bd2d644e6577befae10c52c4e36377635eea1056a74f5", size = 2158006, upload-time = "2026-08-28T09:58:35.647Z" }, + { url = "https://files.pythonhosted.org/packages/70/0a/644597d84ab400e50609c192120b85c9681c22d3a20461b9060a79be0a7a/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:efd62a42486f1bda5d24cb4f63d15a3c7768375fe83d36f9417b4ad7a2fb20b3", size = 2158408, upload-time = "2026-08-28T09:58:37.38Z" }, + { url = "https://files.pythonhosted.org/packages/1e/ee/ca3b7b3a4b3769ffe9ce9432a7c9be755de9593a46d3b0d54d0409323e44/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2bc9419666990c06d7397831f2126a1ecc3594aaa3ff7de5bf2d066802f4e07b", size = 2309609, upload-time = "2026-08-28T09:58:39.22Z" }, + { url = "https://files.pythonhosted.org/packages/ce/52/39fa1f451486019524ca685020390e7ca351832fd874530ba30c8628e6dc/pydantic_core-2.46.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:18a09e1e1011b462f2e32774f25859ef1223d5c2b0546a633cf56654710721e0", size = 2342618, upload-time = "2026-08-28T09:58:40.89Z" }, + { url = "https://files.pythonhosted.org/packages/81/5e/468fc630568c61dcef3cd47ad32ffbeed9af643f49208d1ea86ab4f890c4/pydantic_core-2.46.5-cp312-cp312-win32.whl", hash = "sha256:5cb482e9e84c851f4e623fe4acc1ced89168cf1fe18f7089db4548c8f5bbb65b", size = 1939475, upload-time = "2026-08-28T09:58:42.591Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c9/4c19f41b84cf6b622a72fbeed7665b25d47a187d68d47d0d430c07f23268/pydantic_core-2.46.5-cp312-cp312-win_amd64.whl", hash = "sha256:5e81740c09e310f5aa5cbd3e434a01c154d4bef93241c7877b39f211d2b78ba8", size = 2043140, upload-time = "2026-08-28T09:58:44.272Z" }, + { url = "https://files.pythonhosted.org/packages/af/dd/0c1a050299147c746e5256db16d645ab5efd4f78c59937d581a0524e74a2/pydantic_core-2.46.5-cp312-cp312-win_arm64.whl", hash = "sha256:f7b0ec93a2893de856652154d73b7ba622f26fa97726487dcac373de5f4c6084", size = 1997729, upload-time = "2026-08-28T09:58:46.13Z" }, + { url = "https://files.pythonhosted.org/packages/f5/37/5abe39a8372a61d3dc3c1338fc504281c01b32fdb3169cd7187153b56d3e/pydantic_core-2.46.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:b7ca9034437b6022f941f4857459562ee00a560b97e7cce8a0ec5a74fc6766e0", size = 2075885, upload-time = "2026-08-28T09:58:47.856Z" }, + { url = "https://files.pythonhosted.org/packages/21/43/6323b1f8b217780454c61304bcd2b38ae4762f50754414124603ccc90bb2/pydantic_core-2.46.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f332f0e72a5a0400141f830744e141bf9f97917878dbe968669e8a7fefea78ff", size = 1922768, upload-time = "2026-08-28T09:58:49.58Z" }, + { url = "https://files.pythonhosted.org/packages/0f/a3/c05ca796e1197618a774b01e596aeedfefc2f7d8c01ae3054e910b120e8a/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:193375f3548919d3f0b60936ca113ada3e38f264f91b9b8e0508efaad57be931", size = 1951241, upload-time = "2026-08-28T09:58:51.511Z" }, + { url = "https://files.pythonhosted.org/packages/68/32/33bc39ac705c52cffc908e8389f9754fdb208aea5c69cceddf4eb3ce99af/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:79bdfa52f843137045b2d081cc05c120ba6665d29b7559c2c47690906f39279f", size = 2031975, upload-time = "2026-08-28T09:58:53.166Z" }, + { url = "https://files.pythonhosted.org/packages/b0/70/2333e885c0f6a67bc105c5916965dac9b57f2718ee20d81d1a06a4ebdc13/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:24922243639cbdac66c75fcb6fd6495a9cb52b213d62f9a0d16f0310b1ff8038", size = 2208542, upload-time = "2026-08-28T09:58:55.017Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ea/296debfb4264207bbda5936133892e027c0a58875ad53ebd512fba8ec3a2/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c76fe65e607be28c7fd4d56fc3c42b1583aa058ce3408b7ad0fd540171d31f9f", size = 2264692, upload-time = "2026-08-28T09:58:56.767Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f2/9e4de77a6271e07a76d2d58b11c091a979c191ed2939bf80067568b369d2/pydantic_core-2.46.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f7b393a8b3da82f5c1fc0751e6d01ac6c55b93c18226a60bdfba4a724efafd1", size = 2066633, upload-time = "2026-08-28T09:58:58.531Z" }, + { url = "https://files.pythonhosted.org/packages/8d/db/f9e9d0c97445987b2084823d5c240de88087338f04fc2cfaa2df186b8049/pydantic_core-2.46.5-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:7ac031912d54f3d83ef3b3eb98dfabc1608802e2202263d25957eeed40b94761", size = 2105235, upload-time = "2026-08-28T09:59:00.421Z" }, + { url = "https://files.pythonhosted.org/packages/07/c5/79169b047b3b2c3e99e04bc76372af9637e0bf6db638274fa927df96369e/pydantic_core-2.46.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:837b396ca3d7b74091ca623f6cbd8351bd42d670a79c2683e79fb089f06a2de5", size = 2157367, upload-time = "2026-08-28T09:59:02.442Z" }, + { url = "https://files.pythonhosted.org/packages/26/b5/ba6057afb7c291bd449f51b867f95aef2072941c4ce4e5c31d6ffd132d3b/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:5ee239d575f80b08eca11f6e20f90c4c695de7825c67eefe6091fbf20dda648e", size = 2158420, upload-time = "2026-08-28T09:59:04.2Z" }, + { url = "https://files.pythonhosted.org/packages/6e/28/2057abecaafdc22912afa819603a51f0a62d40643b7c4871c51721fea9be/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:e80675d75ae2cd14372cb65cad5400d9347a3d3f6c13000183f22dfd027283ed", size = 2309588, upload-time = "2026-08-28T09:59:06.048Z" }, + { url = "https://files.pythonhosted.org/packages/71/9d/881156dc404e27479c4246128d73538464cab4a239bec61995e227644c30/pydantic_core-2.46.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9c4b71f10dd532fb7a5cbc8f58707779e64f03a258c2bf8bfbaecfcd9970b519", size = 2341866, upload-time = "2026-08-28T09:59:08.539Z" }, + { url = "https://files.pythonhosted.org/packages/5a/38/d66f443a259f84d13babdceae568e572b0ed26da17ca5d0a649ebb110a67/pydantic_core-2.46.5-cp313-cp313-win32.whl", hash = "sha256:97bf8de4d541598c94a59344eeb988a94c08ff76b5723c41f6567ec18c7892ea", size = 1938580, upload-time = "2026-08-28T09:59:10.402Z" }, + { url = "https://files.pythonhosted.org/packages/2c/1e/1d5371213f4cc9a7ed70c0bfcc7911de22311ee99a662a56077d7292d2ac/pydantic_core-2.46.5-cp313-cp313-win_amd64.whl", hash = "sha256:15f4a94963c95accac15b7b657bb177d3ad82bb90b0d0526d9a9b85079925db5", size = 2041980, upload-time = "2026-08-28T09:59:12.396Z" }, + { url = "https://files.pythonhosted.org/packages/5a/48/4222d90b1c67568bace4dec6dca6271449c66de3595d72b6d098f5fde597/pydantic_core-2.46.5-cp313-cp313-win_arm64.whl", hash = "sha256:d22a945598fb91236b4dd793a6e42e4f3dd7740bb5aace5ebd7d4c08d13bb575", size = 1997213, upload-time = "2026-08-28T09:59:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/8e/8a/14596f2a8367da50cf7cbac48169ee5d9c8e11d486a3b527082384630c72/pydantic_core-2.46.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c1c43ad4339643d70ebb8124e1305a7dab423001eff58bb41a0f731adbc98355", size = 2074081, upload-time = "2026-08-28T09:59:16.141Z" }, + { url = "https://files.pythonhosted.org/packages/ae/d5/d8a4eb6d6c7f66b91dd37c576d76e9e60fba900caf5372c17bcf949febc2/pydantic_core-2.46.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1a353f84de772f423b5ffb11d7ae352fbbef0f446f3c0b0af0f8236d7233606e", size = 1920497, upload-time = "2026-08-28T09:59:18.065Z" }, + { url = "https://files.pythonhosted.org/packages/8e/26/092079428f86e927e030b2c0ced87df69dbb1c875cdeaa67bf42ea2be746/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5086029a57366b8cf81b130a43908738095c270c21a8d7f0e8bdfdb89718e2f3", size = 1952130, upload-time = "2026-08-28T09:59:20.476Z" }, + { url = "https://files.pythonhosted.org/packages/08/c3/8ec0e290a9ebaebd64047bf5fda94be835c6b1551b02437e4b76778fbcd7/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46c25dda9d092a06c08db76ffe0a197107904d0dfac653f7d5306bbcd6d6119c", size = 2026371, upload-time = "2026-08-28T09:59:22.227Z" }, + { url = "https://files.pythonhosted.org/packages/01/72/4fd20ad520fb8da0157f95b27a7eb05a72790ef08138e7701ac972c342ea/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37ea7b83c935e5b0d68c9449b82651accf78a10828b2c02b2f2d9e9496446c21", size = 2202822, upload-time = "2026-08-28T09:59:24.277Z" }, + { url = "https://files.pythonhosted.org/packages/31/b0/d16e0771206b29314f0d52198b720be21e8a99ab2bf11e3bc0d7c9cebdff/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64e88d5585bea9ce95861079de72006c7fa6d3df4e3a3b65ba31eb979c15c9f", size = 2262756, upload-time = "2026-08-28T09:59:26.608Z" }, + { url = "https://files.pythonhosted.org/packages/2c/9b/59634b7ac631c63b2a37760eb6943af3e29573d6b59a4abc5e7f019d4cee/pydantic_core-2.46.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d510bac3ee52247af28ed4bb18a1e799f040ac60fd2bf5ccd4c92f1fbe786f", size = 2068352, upload-time = "2026-08-28T09:59:29.044Z" }, + { url = "https://files.pythonhosted.org/packages/08/7c/570abb1ad2155348dc754ea91be22e5aaa18eb6d69a6068f7c6f2679a6ed/pydantic_core-2.46.5-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:a2a5e1d0ff29adddc9f6d6821a66302e4493f8ca898b715b6b1182c2c201ea0a", size = 2104777, upload-time = "2026-08-28T09:59:30.95Z" }, + { url = "https://files.pythonhosted.org/packages/8e/25/5bf74adc65a1ac5b7be3f6cb0bcb5433615c1598a801c19d830d84c98ded/pydantic_core-2.46.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03b9666e41e35d8909852ba191a0607520f81b74eaf12ccf8737005dbb313821", size = 2156312, upload-time = "2026-08-28T09:59:32.604Z" }, + { url = "https://files.pythonhosted.org/packages/90/6a/2ef38830675e050121040618135564ed56b860b45433b02d9b4ebece46f3/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:a91c17edf6eea2402cb5457b4c89e99bc5ed1004aa34c4adf1d4258c1a5c22c2", size = 2150067, upload-time = "2026-08-28T09:59:34.453Z" }, + { url = "https://files.pythonhosted.org/packages/90/ef/a7dbb03a14a64c2a4621f989c615ed9a892535a6cad938fc27079f919d80/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b49924c73a235e969511bf2aabdff3beebf9820931f646c80274d5d780010c47", size = 2304516, upload-time = "2026-08-28T09:59:36.194Z" }, + { url = "https://files.pythonhosted.org/packages/68/f8/6bb4c4b80e8a6fde1904c64a51c62a1d04fcdfa3ea521a66b2ddefa1d885/pydantic_core-2.46.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:2cbd9a5eff05e51c447c34dfa4632145b26b09120cf04bd0c871e44c1a5e1c9a", size = 2335223, upload-time = "2026-08-28T09:59:37.931Z" }, + { url = "https://files.pythonhosted.org/packages/2a/80/f46b8c681195190b2c1f1c7c0a81abce60663e987613e09ef64d433dd96b/pydantic_core-2.46.5-cp314-cp314-win32.whl", hash = "sha256:2d5d76654becf5efd62c9e51c3756c67b49498b0c9a40884934c40807adbd074", size = 1934827, upload-time = "2026-08-28T09:59:39.836Z" }, + { url = "https://files.pythonhosted.org/packages/f7/3c/60674207246bc0a4009d2391b7c7251c7159f279c8d2ab8aae8ef46f3dee/pydantic_core-2.46.5-cp314-cp314-win_amd64.whl", hash = "sha256:fa10ef4112775900e7a0661068635eb67b2ab824fbde764de6e0e21982a93db0", size = 2042648, upload-time = "2026-08-28T09:59:41.792Z" }, + { url = "https://files.pythonhosted.org/packages/69/0c/117c562c7c1babdf44576b72a5e496906506c93690387ecfbca7c729ae2e/pydantic_core-2.46.5-cp314-cp314-win_arm64.whl", hash = "sha256:045ab3b6d308439e32b81cc173bba5b9018bc6ed896afd0c65b3b009b1699af5", size = 1989652, upload-time = "2026-08-28T09:59:43.702Z" }, + { url = "https://files.pythonhosted.org/packages/e8/66/9336ae58f9eb68c41d121894e52c4c89eccb07eb8f602a04ee9c3f37736a/pydantic_core-2.46.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8816f3d218beb4b787de5c9759c259b8fa61f9dec42dc7811f320a33771778b7", size = 2065829, upload-time = "2026-08-28T09:59:45.364Z" }, + { url = "https://files.pythonhosted.org/packages/c5/02/bc19b47a96c2d3109760711acf22369e56bd7e405ca52f7ade164d2ead57/pydantic_core-2.46.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bce57638e08ac148e5778cce7feb968307a727d66f8e2274a543d0cf0c9ad6a3", size = 1905716, upload-time = "2026-08-28T09:59:47.18Z" }, + { url = "https://files.pythonhosted.org/packages/52/a4/70b47c0509923dd98ccfed04fb3e32ea3849c82a0ff2205bb41009b43c00/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:976e1128455aa595ea04c79ccfedff1aaeab96ee013fcc916bed120c4f0ad94f", size = 1934216, upload-time = "2026-08-28T09:59:49.241Z" }, + { url = "https://files.pythonhosted.org/packages/52/ab/aa03b65f7bb198585edf806b906c3223ecf1795543e39e23aec4cce27ad2/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b891faeedeafba41b2983e5001a81b6a915b69544c7e7570d1989ce1c36ac7", size = 2010635, upload-time = "2026-08-28T09:59:51.692Z" }, + { url = "https://files.pythonhosted.org/packages/3c/8b/0da06343f30b84ec549aafd309c6456223d5dc8bd36af504c573faad561d/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f194189415698233dd1114a093a9b56e61e2c57e11b469be3b0506f46f0771c", size = 2209369, upload-time = "2026-08-28T09:59:53.582Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5b/844c4defaa34a3df66eb9257087d121d70c201298b96abdf9f492fc2f1bf/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82a36973cf8a2ef5406f4fe2edbf8ed0c99629535d959e0b100c76a32535a111", size = 2253238, upload-time = "2026-08-28T09:59:55.484Z" }, + { url = "https://files.pythonhosted.org/packages/f4/64/a4e536cb16d7f61a7fd3120b46c577fc7fa7325992f69c4f52bc786d77d8/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdbb78909f52b981d3b2d56b97328d71eb0b974c36bd77c920123a7ebb192829", size = 2065740, upload-time = "2026-08-28T09:59:58.038Z" }, + { url = "https://files.pythonhosted.org/packages/5f/75/aaa38c6bc2d085f6605b34eabdc6a8a4e0b2e61fc9c8e6e52b28e97b3125/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:52e24eacdb536cade636aa90fb851835222becff8484b7001fdc78cb0290f2aa", size = 2087425, upload-time = "2026-08-28T09:59:59.898Z" }, + { url = "https://files.pythonhosted.org/packages/55/ae/fcab4cfc39aba3689e1d20c8b5250ad280957022c09af2ed9cd585602a5e/pydantic_core-2.46.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:37ae34309d7bd8c0d61ab839668058f2a7962ea1fc51d105d2db228fe0618034", size = 2139306, upload-time = "2026-08-28T10:00:03.057Z" }, + { url = "https://files.pythonhosted.org/packages/2d/f4/f1d03a4bc9d9acbc62f4d742b8a319af52f71885079868b2ff8e48a651ee/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:0cdbada856a1c69a7624a64d3d9aefe79300bd6ef827b43a4f265010b9b55184", size = 2144589, upload-time = "2026-08-28T10:00:05.645Z" }, + { url = "https://files.pythonhosted.org/packages/83/f3/7a53bb1356de514a4cd295f25b6ac39237895620c0462d2592b76c16e114/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:545f26c504b27c3758439a5e6d9349931f0a04f855668d5fe323c89e82300a38", size = 2288882, upload-time = "2026-08-28T10:00:07.931Z" }, + { url = "https://files.pythonhosted.org/packages/cd/94/5a81583660c175c59d49ffb09f4b3a44debeaf86a19fca664ae1cdd9ee32/pydantic_core-2.46.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:ff218293c9c806138dca139765e3b067621be52bcd93cdc14c7711be7ddc90a9", size = 2335210, upload-time = "2026-08-28T10:00:10.177Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9f/5d685c2693b972d1a59c998586e8823712b66603aeff47ee60a4bdaafd37/pydantic_core-2.46.5-cp314-cp314t-win32.whl", hash = "sha256:97cf3eb53a8cccacf9d46686a0926186c9bfb5574f2ed66d3639d5fe117cd3a9", size = 1921180, upload-time = "2026-08-28T10:00:12.35Z" }, + { url = "https://files.pythonhosted.org/packages/70/12/5c94ee16d65a37a15f9e869f5e6256df111154491173801a4c5e800ab548/pydantic_core-2.46.5-cp314-cp314t-win_amd64.whl", hash = "sha256:d2f9fc07a8042a8f95925b35c4f04f469707c981fc33245b6ca187cf5d2dd290", size = 2020515, upload-time = "2026-08-28T10:00:14.774Z" }, + { url = "https://files.pythonhosted.org/packages/63/19/67830dda664e6bdf9285ee2e40f355d0d7d6b92aa0c42e8d217bb8d33d36/pydantic_core-2.46.5-cp314-cp314t-win_arm64.whl", hash = "sha256:acf8a67ba51f4ca9ddbd0e6b3000a65ac51ab734661778b3e7ba64d99a710f2f", size = 1989276, upload-time = "2026-08-28T10:00:16.984Z" }, + { url = "https://files.pythonhosted.org/packages/df/dd/053c2e4303f791f3b8f8a14ab0b22008e8eb21d868c0c90b4f9be705b76a/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:013d6f3483d81e02e7c328831808f336c8596ee33b4bd4026b9ffb1e960b8942", size = 2062540, upload-time = "2026-08-28T10:01:00.318Z" }, + { url = "https://files.pythonhosted.org/packages/d7/dd/a18df751a5e37dd51bfad7f68e766999125bebe68c9e1d10a493ad01bd63/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:e9c134bb666dd54b778b9fc0d2b50cbb7f979b9e3716f26a88c9ab3b6fc1dd0f", size = 1902040, upload-time = "2026-08-28T10:01:02.529Z" }, + { url = "https://files.pythonhosted.org/packages/b7/13/01d40f9d07ce8a779fd6e0bd8ad4fba91309500dd67b869e2e219d261a6d/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:347ec774390c87326a2e4929d58d3f7e8763a104d5d35f4cd595a4c952366433", size = 1967479, upload-time = "2026-08-28T10:01:05.004Z" }, + { url = "https://files.pythonhosted.org/packages/fa/04/c81d4841331c2178b6fb09ae225425e110ed72d990c9fe556c4ec03d1013/pydantic_core-2.46.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e24d8f05fa2d28513d94e877e9c75ad66175376209b3977f916e240e623193c", size = 2111034, upload-time = "2026-08-28T10:01:07.345Z" }, +] + +[[package]] +name = "pydantic-settings" +version = "2.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/ca/31c57507b13119d7d3cfa1576dad2911a4861e3be07b579395f4e9d393f9/pydantic_settings-2.15.0.tar.gz", hash = "sha256:694b793e84f766ba76a90ebdefc01d0a9a045dab0382bee70393da93712ad117", size = 261253, upload-time = "2026-08-07T09:24:57.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/a4/2bffa9f8e804325a09867f0e9d30795c80ea9f8d62560bd1b6ad6220eb2f/pydantic_settings-2.15.0-py3-none-any.whl", hash = "sha256:0ba092c291c94baceb5eff768aa0d56400a457585bc0175925a5a5510303da42", size = 69413, upload-time = "2026-08-07T09:24:55.839Z" }, +] + +[[package]] +name = "pydub" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/9a/e6bca0eed82db26562c73b5076539a4a08d3cffd19c3cc5913a3e61145fd/pydub-0.25.1.tar.gz", hash = "sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f", size = 38326, upload-time = "2021-03-10T02:09:54.659Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/53/d78dc063216e62fc55f6b2eebb447f6a4b0a59f55c8406376f76bf959b08/pydub-0.25.1-py2.py3-none-any.whl", hash = "sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6", size = 32327, upload-time = "2021-03-10T02:09:53.503Z" }, +] + +[[package]] +name = "pygments" +version = "2.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 = "pyjwt" +version = "2.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/c3/8a3b59c25070cc61dc517fbdfa5dc0904670c96f605cc69759dc09166b99/pyjwt-2.14.0.tar.gz", hash = "sha256:77283c83fb56ecf566a886c757a714bc83668e38156de2cce8263302f42e0b86", size = 113177, upload-time = "2026-09-11T13:11:54.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/97/672cb32ce0dfea44b740cb7b4f97038463b9cf7c0ead1aacf595572851d6/pyjwt-2.14.0-py3-none-any.whl", hash = "sha256:ad0cef71c756a56e74863c2919cf0985f72decbcfcb550ee2f422e7c62b5eedc", size = 32896, upload-time = "2026-09-11T13:11:53.409Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "pyperclip" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, +] + +[[package]] +name = "pyqwest" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "opentelemetry-api" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/ee/0ff9facfa9e7a4f6df2a770d4eaf1ad0f74165da7e8c28e888461f07604c/pyqwest-0.10.0.tar.gz", hash = "sha256:6c1a693be17d57d2c2eca4085e32c2809c53090c16719a907c90ebcf1f40dc01", size = 482248, upload-time = "2026-08-21T06:09:20.656Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ee/b1a28f57c689606cfd065d8a553841150f7daaa91d20e58dcc2c5ea191f8/pyqwest-0.10.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:aa492d5777dd145a60795ed95d9d4707a3cd1091fdcdfc93a82ac7fdc43ebacd", size = 5261059, upload-time = "2026-08-21T06:08:04.999Z" }, + { url = "https://files.pythonhosted.org/packages/dc/13/9c5046cfd6ef705bde0b620ba8a794335bcabc0839342a2a647f2427b27e/pyqwest-0.10.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:59f3f16628e518c674102e7b5fcff2101bba6abb4f6737ec5fade9b9278e6a53", size = 5134207, upload-time = "2026-08-21T06:08:06.955Z" }, + { url = "https://files.pythonhosted.org/packages/93/7d/50021dd88d82d6966ab1c27593ceaee9d1ed62fbe597c40e8dc187cfa5fd/pyqwest-0.10.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6e7db305a8318b1f3218053e87501f8f245ca8bd63e948e0282d04bf0883470", size = 5640730, upload-time = "2026-08-21T06:08:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/ff/3f/5bf6c32e9e701837a8c47ce6e3ad38978cfec8eb7bc6596181e5f9e1eaeb/pyqwest-0.10.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5c757cfac5f53c8671dcb4850d5fc4c4339ea3e90636331c9318f8e3ddabc06", size = 5561462, upload-time = "2026-08-21T06:08:10.836Z" }, + { url = "https://files.pythonhosted.org/packages/5f/61/ca9ba5721461b7ce5cfaac373ab3a1723ddcc434af7430f8bd628da6e623/pyqwest-0.10.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:234b3f71e3f314d997c203d8cf829b7117edd041153f9c277d0060ab90134148", size = 5801847, upload-time = "2026-08-21T06:08:12.502Z" }, + { url = "https://files.pythonhosted.org/packages/d2/44/95593919b996a417093f598d887822b9b899e8d025588c9bfaf8c60dd812/pyqwest-0.10.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5637256a0dac0ef57e0eaa02b032014965e4a4c995e1deca1b1b97e6d1765f78", size = 5978692, upload-time = "2026-08-21T06:08:14.383Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b9/b2821ce5188457168ebb25d5ff65b1ca1bf27bc6b4a33df4bcc2357e625c/pyqwest-0.10.0-cp310-abi3-win_amd64.whl", hash = "sha256:7ea761937acf3a00d1a7e70e982949d18946e5471d1419266ab3a78bbfa19759", size = 4876627, upload-time = "2026-08-21T06:08:16.084Z" }, + { url = "https://files.pythonhosted.org/packages/86/b4/16ccef1c203fa258ce46a86aefc1a79c13b5f0b8d49627347d90eef25efd/pyqwest-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a21f1f15252a8303623b4f17b9c6de595ace11b3ade07f2adb6d07121e8191aa", size = 5274815, upload-time = "2026-08-21T06:08:17.777Z" }, + { url = "https://files.pythonhosted.org/packages/5f/61/6a87f84f571441ea43279587d4bfcad4543505918ae2b83a1ebdcfa98be5/pyqwest-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eb472c6e5d6833ebfec79db310e426eb17b01ac64c0e2c251bd9192c0d2ee0c5", size = 5123656, upload-time = "2026-08-21T06:08:19.501Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f2/ab69e581cf9b798b0e169f7b27fd3f8b6f9f1631bd4d3b6e22e5abaf8d8b/pyqwest-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83578e24cccd5e0dc04d60a0af7bfb43325b5f22d03ff74ff79ed0ecf553b50d", size = 5641253, upload-time = "2026-08-21T06:08:21.627Z" }, + { url = "https://files.pythonhosted.org/packages/9f/dd/f1a62eebf8321ace506bd94551a01431f6a45b882225455eae3ea6e8c6d1/pyqwest-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bb511c434f79c641efb5573e5795e56dc972252f4b96e52a9636d4ece5231a4", size = 5567341, upload-time = "2026-08-21T06:08:23.346Z" }, + { url = "https://files.pythonhosted.org/packages/4c/48/51d767691973e046887f5e6d96e32142fe296823b163ccba732233a6ef72/pyqwest-0.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1aaccd8a9db9430b2aedb5bad8ead80742cbc056b85c229516c70dc80539f906", size = 5803874, upload-time = "2026-08-21T06:08:25.096Z" }, + { url = "https://files.pythonhosted.org/packages/58/0a/d2834ccc6e59ad110718895cc65ff2a68aa6e010f0ba8fbe42a57ea33c21/pyqwest-0.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:73d9eb438ab4a957a1ce0619d3af8c1c1126bfb9181033b123d792fcf4224531", size = 5981518, upload-time = "2026-08-21T06:08:27.594Z" }, + { url = "https://files.pythonhosted.org/packages/75/f3/274b4c268e9a55fbdb1b3637ac50b5bf42cd3a85d1cfbdc15c602a7b0d9c/pyqwest-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:317a74d633abe3bc5bccabf479e069c515dab9e6a755274b0ccb1d8a5bbfede3", size = 4870638, upload-time = "2026-08-21T06:08:29.421Z" }, + { url = "https://files.pythonhosted.org/packages/8d/3d/8b1092f25159bf61a9470ebd35438c669b91ef553a7ee205bdec8006107b/pyqwest-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3978e794b9cfd8eaa500fb5d7aee63bc6172c605efa0abc1f62d85485bc049e1", size = 5273599, upload-time = "2026-08-21T06:08:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/75/10/54a9786123942b124c2afb9562b74e158afec7be40ef0caa0d37f615d379/pyqwest-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:715991fd4f04862cd7a9d7452daabcdbd74dff4dff55eb20c22d60382dc2a4ed", size = 5122920, upload-time = "2026-08-21T06:08:33.025Z" }, + { url = "https://files.pythonhosted.org/packages/a2/5b/6a6bd76f91e068b9a619f62aef9fe5ef201f859ddbb6b0a11ad3875ecdda/pyqwest-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c04798bed79c1dfa0e5b0e30fb137124311083490d44d6dfbe068d3dd254349e", size = 5639577, upload-time = "2026-08-21T06:08:34.896Z" }, + { url = "https://files.pythonhosted.org/packages/68/28/12277a24a8dd74b0a7f124c624d9ed58eccb41e2087ff1087a14d348c778/pyqwest-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35b472877e73dd63fed089c2bc8fa198407f005c8c19e0a93f025ebefde01a81", size = 5565835, upload-time = "2026-08-21T06:08:36.567Z" }, + { url = "https://files.pythonhosted.org/packages/9e/b6/b7da4a3f1fe43600154ae75e91ba7969024d886d60077dd3a1ba8e66d170/pyqwest-0.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:564ec360b7848b35e009038ffbca00466305a9708ab21829477f64aa8cad4c64", size = 5803078, upload-time = "2026-08-21T06:08:38.362Z" }, + { url = "https://files.pythonhosted.org/packages/0c/6f/0c9ba210f49f232289afaa8f06369c5f135ac786e1ca0cb22243b7f1fe2c/pyqwest-0.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5c80e88a5967c1cadb3237c450f91a84a3683f8838c8dca96f09fee3612e762", size = 5980431, upload-time = "2026-08-21T06:08:39.967Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f5/306eeed41a3cd3100247e6e442f4345277b70f1d24efe1641181b14839cd/pyqwest-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:dc3d80b402fb59dbe015e25993ac8147456fb231a4c949f92a89f31315ad50f9", size = 4870198, upload-time = "2026-08-21T06:08:41.687Z" }, + { url = "https://files.pythonhosted.org/packages/64/18/0086a408e7cbf39dab18fa5b7e42c969a98382da5a4e6debe40f05acc6a1/pyqwest-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:23a28beb55fa6d975949bffae4adfb69378f3229bb5cbd71231e95bf66f5b26c", size = 5274542, upload-time = "2026-08-21T06:08:43.323Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7b/e1b9aaaf7596e4faaa53cefc2efaca4e3cde721e308e6385e366361cfdcc/pyqwest-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e4415ae40b8eedb1713dab14d7f9fecc3f79d26f3206c561087b88b99d5ce24b", size = 5127922, upload-time = "2026-08-21T06:08:45.116Z" }, + { url = "https://files.pythonhosted.org/packages/75/cd/e6d68bb1de5dd26100fcfc878cbd67c402a928774edf1e8ae304c5a84f5b/pyqwest-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14b875d2273212d7fa8e4b755d8d736ffd226b1c707a9c0017dfdc8393a96eca", size = 5645856, upload-time = "2026-08-21T06:08:47.055Z" }, + { url = "https://files.pythonhosted.org/packages/f7/48/8c9f9f0467c41f6a563146d57a52f8f6d60c0cb09d0fd3ef88ad5a1c442f/pyqwest-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5071491e416ea54e3b95bf9ffbed0bd065b093cb96e10a75c3d8f2cbe3c9823", size = 5569831, upload-time = "2026-08-21T06:08:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/16/ac/c85ab70c6c72078d49a82da76b820e46aaf95a3f6fe271dac955ac195d21/pyqwest-0.10.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b68b5e68d513a4c63a072f8f40e38015160cf90bfbf7e8ef7c3935ca87e9e022", size = 5806735, upload-time = "2026-08-21T06:08:50.652Z" }, + { url = "https://files.pythonhosted.org/packages/66/db/ad7375b22fb2d0807431dcc9bc2aaf840e374c298cd15071024d5f6dd6d1/pyqwest-0.10.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c48910d27820b9c46fcd001b0fe514a3cf47d4784f59512dcdb8c91c395f82e4", size = 5984786, upload-time = "2026-08-21T06:08:52.377Z" }, + { url = "https://files.pythonhosted.org/packages/17/88/c449a772afe129683fd7acc657cbc7c69bec085dc75b6e8710a50fbb44e7/pyqwest-0.10.0-cp314-cp314-win_amd64.whl", hash = "sha256:d03ba2cd17948b623a6210981d342eb122546d8a8e910ec77511aff4b1acdd00", size = 4872288, upload-time = "2026-08-21T06:08:54.101Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f8/439ffc0ee12cd7d9b57ac07ccea78ad3ba66b0d6817d429dd661d73308c4/pyqwest-0.10.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:07a0eb595f4096232c2d22549b6e4612c1ecada7934e46462c2c37ce14a89cfb", size = 5256823, upload-time = "2026-08-21T06:08:55.681Z" }, + { url = "https://files.pythonhosted.org/packages/df/2b/72ecd27d104d2b4284710194cce796d607674966c3ea66436768e812a66a/pyqwest-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:26401baf7dafc71c8d12d2e8389519d141e6f7c14094d0dd4cf9ec1d3b5555bd", size = 5112624, upload-time = "2026-08-21T06:08:57.366Z" }, + { url = "https://files.pythonhosted.org/packages/a4/24/0fb89c3f7d5a0410fcb7560588b4c741ef24b19195c307d4263f04e75c2b/pyqwest-0.10.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5e3c436e041d8873ce5bb0fdcf9f9e86f5604e8f0ef9e03149efebd8cb474f6", size = 5631844, upload-time = "2026-08-21T06:08:59.165Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4f/921d14754a186f0143ad62b50108dd808328e347e98e9dafab3897eeb405/pyqwest-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09364115761579eabfc79d1e954cdb3ded508dac1903fac7285d4c6f058c683f", size = 5555869, upload-time = "2026-08-21T06:09:00.783Z" }, + { url = "https://files.pythonhosted.org/packages/b8/70/504780417319a626fe9549a7e6f9020a3d448eddf8a09617238a3426e90c/pyqwest-0.10.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:559674a98a8b1217e1830ecd41c9905bf2b60983c6b8017063dfac199f00727c", size = 5793738, upload-time = "2026-08-21T06:09:03.324Z" }, + { url = "https://files.pythonhosted.org/packages/45/f7/8d0a5b8a3289f4300dc9005ebde75d316f2371a2671617f942036337731a/pyqwest-0.10.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f399a696392fff3db3eef0a18ef65b8a3b8396d129193487d966b8eb11006376", size = 5972889, upload-time = "2026-08-21T06:09:05.2Z" }, + { url = "https://files.pythonhosted.org/packages/89/c4/f4c781e475c451cb5f4762a2f814a1750ef375b8db4db09bb2dcea03c4e5/pyqwest-0.10.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0f9163d6dd991bf1bf27308ba38ba021af660b15fffa47ebca98e41cf6f00309", size = 4858036, upload-time = "2026-08-21T06:09:06.926Z" }, +] + +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/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-cov" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-dotenv" +version = "1.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/53/ed9d74092561d4b01a2ef1349d52cdbc135e526c245f366b089cfca6de49/python_dotenv-1.2.3.tar.gz", hash = "sha256:a20a594dabeaa385725aa239d5244871c143ecb356add8a20fcf23773a6c3a35", size = 58945, upload-time = "2026-08-16T16:54:54.067Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/17/c5c6b53ddc18f297992099b3d9ec16c855c0ccc83263a21fe4d1c625ec6c/python_dotenv-1.2.3-py3-none-any.whl", hash = "sha256:904552145e8bfed22162c09dab1c2b9b54fefa7b23ba780f4f26ca0316b0f0d9", size = 22780, upload-time = "2026-08-16T16:54:52.473Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.32" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/42/55c32bb9b12693c092ad250a0e82edb5b31ddeda6eb772de5f308b3804ad/python_multipart-0.0.32.tar.gz", hash = "sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e", size = 46881, upload-time = "2026-06-04T16:18:58.647Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042, upload-time = "2026-06-04T16:18:57.319Z" }, +] + +[[package]] +name = "pytz" +version = "2026.3.post1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/48/fb042503b6ca6cd271261dc559fd6432f7d8c713153e9ec5c591af4dfc1c/pytz-2026.3.post1.tar.gz", hash = "sha256:2211d3fcf9a797d3405cac96ac7f61d80e6a644f72a3309607282fe8a2010c5d", size = 319745, upload-time = "2026-07-25T15:12:07.385Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/7b/39c34ca613b0b198cb866466651b26b045e2009864c5183c979a3b83f383/pytz-2026.3.post1-py2.py3-none-any.whl", hash = "sha256:dd95840dd199baea12d9cc096a1d452caa6596a1c1e4b5f3dbd1541855d5e815", size = 508283, upload-time = "2026-07-25T15:12:05.782Z" }, +] + +[[package]] +name = "pywin32" +version = "312" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/ff/32aa7d2ed0ab12b323aaa64f9b75e6ad4f8fd09f9ccfc28c79414d46838d/pywin32-312-cp312-cp312-win32.whl", hash = "sha256:dab4f65ac9c4e48400a2a0530c46c3c579cd5905ecd11b80692373915269208b", size = 6371877, upload-time = "2026-06-04T07:49:28.836Z" }, + { url = "https://files.pythonhosted.org/packages/03/d9/77040d3b43df3f3be32ea289433d660d2727f5ba327bc73be835127d9d60/pywin32-312-cp312-cp312-win_amd64.whl", hash = "sha256:b457f6d628a47e8a7346ce22acb7e1a46a4a78b52e1d17e1af56871bd19a93bc", size = 6914841, upload-time = "2026-06-04T07:49:31.85Z" }, + { url = "https://files.pythonhosted.org/packages/e3/cc/7b1ec671775756020a0ee7f4feeaf3c568f0ab86bd3900088cf986937a92/pywin32-312-cp312-cp312-win_arm64.whl", hash = "sha256:6017c58e12f6809fbb0555b75df144c2922a9ffd18e4b9b5afa863b6c1a9d950", size = 6727901, upload-time = "2026-06-04T07:49:34.244Z" }, + { url = "https://files.pythonhosted.org/packages/2d/41/12fbfd7f36ed2146d8bc9de96c2741296bf0d490b98508496cff322e274c/pywin32-312-cp313-cp313-win32.whl", hash = "sha256:7a27df850933d16a8eabfbaeb73d52b273e2da667f80d70b01a89d1f6828d02c", size = 6370184, upload-time = "2026-06-04T07:49:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/ba/db/36a78e3403099d31d9746d13fdcde5accc43c1155f375a34d15983a479a7/pywin32-312-cp313-cp313-win_amd64.whl", hash = "sha256:c53e878d15a1c44788082bfe712a905433473aa38f86375b7cf8b45e3acbaaf9", size = 6914298, upload-time = "2026-06-04T07:49:38.876Z" }, + { url = "https://files.pythonhosted.org/packages/84/37/c1697194092b76de9ed47ca124323f02c57ffc8a45c06f88a3d5acaf01eb/pywin32-312-cp313-cp313-win_arm64.whl", hash = "sha256:59aba5d5940842075343a5ddc6b11f1cdf0d1567fe745290359dfbcc7c2eb831", size = 6727640, upload-time = "2026-06-04T07:49:41.083Z" }, + { url = "https://files.pythonhosted.org/packages/fc/2b/1f3cded5822fd49c02f40544cbb5f58c7cfd6b1694869fd476cb6170ee97/pywin32-312-cp314-cp314-win32.whl", hash = "sha256:a77a90fbb6881238d2ca9c6fd797b25817f3768fe78d214a90137ff055a75f5b", size = 6468928, upload-time = "2026-06-04T07:49:43.188Z" }, + { url = "https://files.pythonhosted.org/packages/21/82/3bf86d2e2808902013132e1ce905a7da0da53790f3836c64bf44d55e24f3/pywin32-312-cp314-cp314-win_amd64.whl", hash = "sha256:a4dd3a848290ef724347b19f301045831d8e802fa4464f491b98b1e0a081432e", size = 7024157, upload-time = "2026-06-04T07:49:45.34Z" }, + { url = "https://files.pythonhosted.org/packages/a4/0e/73f6d6800b4f27655abd9e9f6aaeaefcddb2b946e4674efa2bab184a7f7b/pywin32-312-cp314-cp314-win_arm64.whl", hash = "sha256:9fce94568364e0155e6dfb781ac5d95903be8baf28670632beab1b523f300daa", size = 6839598, upload-time = "2026-06-04T07:49:47.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/61/caa39686032d2ebdd04ff0ab5cbe163126c0066d98e00c9018646e42393b/pywin32-312-cp315-cp315-win32.whl", hash = "sha256:5c1fbe4a937a73ae9297384a3da38518cbc694c68ad8a809b2e19acd350f03ed", size = 6471159, upload-time = "2026-06-04T07:49:50.035Z" }, + { url = "https://files.pythonhosted.org/packages/0f/cd/7e1de64a4a6f69c04214169657ccab0d93a670ea50e35eb8f489d7378249/pywin32-312-cp315-cp315-win_amd64.whl", hash = "sha256:c2f03a0f73f804a13c2735b99392b0cd426bb4f2c4d0178e5ac966a0f21618d5", size = 7025293, upload-time = "2026-06-04T07:49:54.857Z" }, + { url = "https://files.pythonhosted.org/packages/23/ed/4532e9388e65fa16b46776ef47ad631a64eda1631884488af707666350ed/pywin32-312-cp315-cp315-win_arm64.whl", hash = "sha256:a8597d28f267b39074aef51fa593530082b39cbe5a074226096857b1fed2dfb9", size = 6840337, upload-time = "2026-06-04T07:49:57.531Z" }, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "rich-rst" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/d6/d0b9fafc73b65767200da027acab1db1bdb1048f4fea5ebf659df01c700e/rich_rst-2.1.0.tar.gz", hash = "sha256:f4d117b49697f338769759fa5cacf5197da4888b347b9fda2e50aef5cd8d93bd", size = 302732, upload-time = "2026-07-05T02:59:44.308Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/68/1fc93dd759605b5d00fc98b50200739e41ed32bd22d6ba35ca6c3932371b/rich_rst-2.1.0-py3-none-any.whl", hash = "sha256:7ecd1343ee12c879d0e7ae74c3eb6d263b023d2929c6d114212eb1fd91057255", size = 272987, upload-time = "2026-07-05T02:59:42.792Z" }, +] + +[[package]] +name = "rpds-py" +version = "2026.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051, upload-time = "2026-06-30T07:17:53.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/be/2e8974163072e7bab7df1a5acd54c4498e75e35d6d18b864d3a9d5dadc92/rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0", size = 343691, upload-time = "2026-06-30T07:15:14.96Z" }, + { url = "https://files.pythonhosted.org/packages/a4/73/319dfa745dd668efe89309141ded489126461fcecd2b8f3a3cda185129b6/rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf", size = 338542, upload-time = "2026-06-30T07:15:16.267Z" }, + { url = "https://files.pythonhosted.org/packages/21/63/4239893be1c4d09b709b1a8f6be4188f0870084ff547f46606b8a75f1b03/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24", size = 368180, upload-time = "2026-06-30T07:15:17.62Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ca/9c5de382225234ceb37b1844ebdb140db12b2a278bb9efe2fcd19f6c82ce/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e", size = 375067, upload-time = "2026-06-30T07:15:18.952Z" }, + { url = "https://files.pythonhosted.org/packages/87/dc/863f69d1bf04ade34b7fe0d59b9fdf6f0135fe2d7cbca74f1d665589559d/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975", size = 490509, upload-time = "2026-06-30T07:15:20.434Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ef/eac16a12048b45ec7c7fa94f2be3438a5f26bf9cc8580b18a1cfd609b7f6/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680", size = 382754, upload-time = "2026-06-30T07:15:21.831Z" }, + { url = "https://files.pythonhosted.org/packages/04/8f/d2f3f532616be4d06c316ef119683e832bd3d41e112bf3a88f4151c95b17/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6", size = 366189, upload-time = "2026-06-30T07:15:23.371Z" }, + { url = "https://files.pythonhosted.org/packages/e3/29/41a7b0e98a4b44cd676ab7598419623373eb43b20be68c084935c1a8cf88/rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a", size = 377750, upload-time = "2026-06-30T07:15:24.659Z" }, + { url = "https://files.pythonhosted.org/packages/2e/05/ecda0bec46f9a1565090bcdc941d023f6a25aff85fda28f89f8d19878152/rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4", size = 395576, upload-time = "2026-06-30T07:15:25.987Z" }, + { url = "https://files.pythonhosted.org/packages/68/a8/6ed52f03ee6cb854ce78785cc9a9a672eb880e83fd7224d471f667d151f1/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa", size = 543807, upload-time = "2026-06-30T07:15:27.356Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d6/156c0d3eea27ba09b92562ba2364ba124c0a061b199e17eac637cd25a5e2/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc", size = 611187, upload-time = "2026-06-30T07:15:28.931Z" }, + { url = "https://files.pythonhosted.org/packages/f1/31/774212ed989c62f7f310220089f9b0a3fb8f40f5443d1727abd5d9f52bc9/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822", size = 573030, upload-time = "2026-06-30T07:15:30.553Z" }, + { url = "https://files.pythonhosted.org/packages/c9/50/22f73127a41f1ce4f87fe39aadfb9a126345801c274aa93ae88456249327/rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed", size = 202185, upload-time = "2026-06-30T07:15:32.027Z" }, + { url = "https://files.pythonhosted.org/packages/04/3a/f0ee4d4dde9d3b69dedf1b5f74e7a40017046d55052d173e418c6a94f960/rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f", size = 220394, upload-time = "2026-06-30T07:15:33.359Z" }, + { url = "https://files.pythonhosted.org/packages/f3/83/3382fe37f809b59f02aac04dbc4e765b480b46ee0227ed516e3bdc4d3dfc/rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96", size = 215753, upload-time = "2026-06-30T07:15:34.778Z" }, + { url = "https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012, upload-time = "2026-06-30T07:15:36.005Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203, upload-time = "2026-06-30T07:15:37.462Z" }, + { url = "https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984, upload-time = "2026-06-30T07:15:39.008Z" }, + { url = "https://files.pythonhosted.org/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815, upload-time = "2026-06-30T07:15:40.253Z" }, + { url = "https://files.pythonhosted.org/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545, upload-time = "2026-06-30T07:15:41.729Z" }, + { url = "https://files.pythonhosted.org/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828, upload-time = "2026-06-30T07:15:43.327Z" }, + { url = "https://files.pythonhosted.org/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678, upload-time = "2026-06-30T07:15:44.992Z" }, + { url = "https://files.pythonhosted.org/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811, upload-time = "2026-06-30T07:15:46.523Z" }, + { url = "https://files.pythonhosted.org/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382, upload-time = "2026-06-30T07:15:47.955Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832, upload-time = "2026-06-30T07:15:49.33Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011, upload-time = "2026-06-30T07:15:50.847Z" }, + { url = "https://files.pythonhosted.org/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431, upload-time = "2026-06-30T07:15:52.394Z" }, + { url = "https://files.pythonhosted.org/packages/85/ed/adab103321c0a6565d5ae1c2998349bc3ee175b82ccc5ae8fc04cc413075/rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0", size = 201710, upload-time = "2026-06-30T07:15:53.894Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ed/a03b09668e74e5dabbf2e211f6468e1820c0552f7b0500082da31841bf7b/rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80", size = 219454, upload-time = "2026-06-30T07:15:55.25Z" }, + { url = "https://files.pythonhosted.org/packages/27/17/b8642c12930b71bc2b25831f6708ccf0f75abcd11883932ec9ce54ba3a78/rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb", size = 215063, upload-time = "2026-06-30T07:15:56.573Z" }, + { url = "https://files.pythonhosted.org/packages/b6/36/7fbe9dcdaf857fb3f63c2a2284b62492d95f5e8334e947e5fb6e7f68c9be/rpds_py-2026.6.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e", size = 344510, upload-time = "2026-06-30T07:15:57.921Z" }, + { url = "https://files.pythonhosted.org/packages/ba/54/f785cc3d3f60839ca57a5af4927a9f347b07b2799c373fc20f7949f87c7e/rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd", size = 339495, upload-time = "2026-06-30T07:15:59.238Z" }, + { url = "https://files.pythonhosted.org/packages/63/ef/d4cdaf309e6b095b43597103cf8c0b951d6cca2acce68c474f75ec12e0c7/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d", size = 369454, upload-time = "2026-06-30T07:16:01.021Z" }, + { url = "https://files.pythonhosted.org/packages/96/4a/9559a68b7ee15db09d7981212e8c2e219d2a1d6d4faa0391d813c3496a36/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda", size = 374583, upload-time = "2026-06-30T07:16:02.287Z" }, + { url = "https://files.pythonhosted.org/packages/ef/75/8964aa7d2c6e8ac43eba8eb6e6b0fdda1f46d39f2fc3e6aa9f2cb17f485d/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8", size = 492919, upload-time = "2026-06-30T07:16:03.723Z" }, + { url = "https://files.pythonhosted.org/packages/8f/97/6908094ac804115e65aedfd90f1b5fee4eebebd3f6c4cfc5419939267565/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53", size = 383725, upload-time = "2026-06-30T07:16:05.305Z" }, + { url = "https://files.pythonhosted.org/packages/d1/9c/0d1fdc2e7aba23e290d603bc494e97bd205bae262ce33c6b32a69768ed5e/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504", size = 367255, upload-time = "2026-06-30T07:16:07.086Z" }, + { url = "https://files.pythonhosted.org/packages/c4/fe/f0209ca4a9ed074bc8acb44dfd0e81c3122e94c9689f5645b7973a866719/rpds_py-2026.6.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc", size = 379060, upload-time = "2026-06-30T07:16:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/c6/8d/f1cc54c616b9d8897de8738aac148d20afca93f68187475fe194d09a71b9/rpds_py-2026.6.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77", size = 395960, upload-time = "2026-06-30T07:16:09.989Z" }, + { url = "https://files.pythonhosted.org/packages/fb/04/aafff00f73aeca2945f734f1d483c64ab8f472d0864ab02377fd8e89c3b2/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698", size = 545356, upload-time = "2026-06-30T07:16:11.816Z" }, + { url = "https://files.pythonhosted.org/packages/fd/cc/e229663b9e4ddac5a4acbe9085dd80a71af2a5d356b8b39d6bff233f24b0/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd", size = 612319, upload-time = "2026-06-30T07:16:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7a/8a0e6d3e6cd066af108b71b43122c3fe158dd9eb86acac626593a2582eb1/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d", size = 573508, upload-time = "2026-06-30T07:16:15.23Z" }, + { url = "https://files.pythonhosted.org/packages/87/03/2a69ab618a789cf6cf85c86bb844c62d090e700ab1a2aa676b3741b6c516/rpds_py-2026.6.3-cp314-cp314-win32.whl", hash = "sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8", size = 202504, upload-time = "2026-06-30T07:16:16.893Z" }, + { url = "https://files.pythonhosted.org/packages/85/62/a3892ba945f4e24c78f352e5de3c7620d8479f73f211406a97263d13c7d2/rpds_py-2026.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5", size = 220380, upload-time = "2026-06-30T07:16:18.108Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e7/c2bd44dc831931815ad11ebb5f430b5a0a4d3caa9de837107876c30c3432/rpds_py-2026.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703", size = 215976, upload-time = "2026-06-30T07:16:19.654Z" }, + { url = "https://files.pythonhosted.org/packages/79/9c/fff7b74bce9a091ec9a012a03f9ff5f69364eaf9451060dfc4486da2ffdd/rpds_py-2026.6.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90", size = 346840, upload-time = "2026-06-30T07:16:21.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/77bcb1168b33704908295533d27f10eb811e9e3e193e8993dc99572211d3/rpds_py-2026.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4", size = 340282, upload-time = "2026-06-30T07:16:22.875Z" }, + { url = "https://files.pythonhosted.org/packages/87/3c/7a9081c7c9e645b39efe19e4ffbeccd80add246327cd9b888aecffd72317/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9", size = 370403, upload-time = "2026-06-30T07:16:24.415Z" }, + { url = "https://files.pythonhosted.org/packages/f7/69/af47021eb7dad6ff3396cb001c08f0f3c4d06c20253f75be6421a59fe6b7/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f", size = 376055, upload-time = "2026-06-30T07:16:26.111Z" }, + { url = "https://files.pythonhosted.org/packages/81/fc/a3bcf517084396a6dd258c592567a3c011ba4557f2fde23dceaf26e74f2e/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41", size = 494419, upload-time = "2026-06-30T07:16:27.596Z" }, + { url = "https://files.pythonhosted.org/packages/c9/eb/13d529d1788135425c7bf207f8463458ca5d92e43f3f701365b83e9dffc1/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945", size = 384848, upload-time = "2026-06-30T07:16:29.183Z" }, + { url = "https://files.pythonhosted.org/packages/8e/f4/b7ac49f30013aba8f7b9566b1dd07e81de95e708c1374b7bacc5b9bc5c9c/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f", size = 371369, upload-time = "2026-06-30T07:16:30.912Z" }, + { url = "https://files.pythonhosted.org/packages/31/86/6260bafa622f788b07ddec0e52d810305c8b9b0b8c27f58a2ab04bf62b4f/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1", size = 379673, upload-time = "2026-06-30T07:16:32.486Z" }, + { url = "https://files.pythonhosted.org/packages/19/c3/03f1ee79a047b48daeca157c89a18509cde22b6b951d642b9b0af1be660a/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e", size = 397500, upload-time = "2026-06-30T07:16:34.471Z" }, + { url = "https://files.pythonhosted.org/packages/f0/95/8ed0cd8c377dca12aea498f119fe639fc474d1461545c39d2b5872eb1c0f/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538", size = 545978, upload-time = "2026-06-30T07:16:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f2/0eb57f0eaa83f8fc152a7e03de968ab77e1f00732bebc892b190c6eebde7/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db", size = 613350, upload-time = "2026-06-30T07:16:38.213Z" }, + { url = "https://files.pythonhosted.org/packages/5b/de/e0674bdbc3ef7634989b3f854c3f34bc1f587d36e5bfdc5c378d57034619/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2", size = 576486, upload-time = "2026-06-30T07:16:39.797Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f6/21101359743cd136ada781e8210a85769578422ba460672eea0e29739200/rpds_py-2026.6.3-cp314-cp314t-win32.whl", hash = "sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e", size = 201068, upload-time = "2026-06-30T07:16:41.316Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b2/9574d4d44f7760c2aa32d92a0a4f41698e33f5b204a0bf5c9758f52c79d5/rpds_py-2026.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2", size = 220600, upload-time = "2026-06-30T07:16:43.091Z" }, + { url = "https://files.pythonhosted.org/packages/08/ae/f23a2697e6ee6340a578b0f136be6483657bef0c6f9497b752bb5c0964bb/rpds_py-2026.6.3-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13", size = 344726, upload-time = "2026-06-30T07:16:44.5Z" }, + { url = "https://files.pythonhosted.org/packages/c3/63/e7b3a1a5358dd32c930a1062d8e15b67fd6e8922e81df9e91706d66ee5c8/rpds_py-2026.6.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05", size = 339587, upload-time = "2026-06-30T07:16:46.255Z" }, + { url = "https://files.pythonhosted.org/packages/ec/64/10a85681916ca55fffb91b0a211f84e34297c109243484dd6394660a8a7c/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba", size = 369585, upload-time = "2026-06-30T07:16:48.101Z" }, + { url = "https://files.pythonhosted.org/packages/76/c2/baf95c7c38823e12ba34407c5f5767a89e5cf2233895e56f608167ae9493/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617", size = 375479, upload-time = "2026-06-30T07:16:49.93Z" }, + { url = "https://files.pythonhosted.org/packages/6a/94/0aad06c72d65101e11d33528d438cda99a39ce0da99466e156158f2541d3/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9", size = 492418, upload-time = "2026-06-30T07:16:51.641Z" }, + { url = "https://files.pythonhosted.org/packages/b5/17/de3f5a479a1f056535d7489819639d8cd591ea6281d700390b43b1abd745/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb", size = 384123, upload-time = "2026-06-30T07:16:53.622Z" }, + { url = "https://files.pythonhosted.org/packages/46/7d/bf09bd1b145bb2671c03e1e6d1ab8651858d90d8c7dfeadd85a37a934fd8/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885", size = 367351, upload-time = "2026-06-30T07:16:55.241Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ea/1bb734f314b8be319149ddee80b18bd41372bdcfbdf88d28131c0cd37719/rpds_py-2026.6.3-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a", size = 378827, upload-time = "2026-06-30T07:16:56.841Z" }, + { url = "https://files.pythonhosted.org/packages/4b/93/d9611e5b25e26df9a3649813ed66193ace9347a7c7fc4ab7cf70e94851c0/rpds_py-2026.6.3-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868", size = 395966, upload-time = "2026-06-30T07:16:58.557Z" }, + { url = "https://files.pythonhosted.org/packages/c3/cb/99d77e16e5534ae1d90629bbe419ba6ee170833a6a85e3aa1cc41726fbbc/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187", size = 545680, upload-time = "2026-06-30T07:17:00.164Z" }, + { url = "https://files.pythonhosted.org/packages/59/15/11a29755f790cef7a2f755e8e14f4f0c33f39489e1893a632a2eee59672b/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107", size = 611853, upload-time = "2026-06-30T07:17:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/0c27547e21644da938fb530f7e1a8148dd24d02db07e7a5f2567a17ce710/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba", size = 573715, upload-time = "2026-06-30T07:17:03.693Z" }, + { url = "https://files.pythonhosted.org/packages/29/71/4d8fcf700931815594bce892255bbd973b94efaf0fc1932b0590df18d886/rpds_py-2026.6.3-cp315-cp315-win32.whl", hash = "sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369", size = 202864, upload-time = "2026-06-30T07:17:05.746Z" }, + { url = "https://files.pythonhosted.org/packages/eb/62/b577562de0edbb55b2be85ce5fd09c33e386b9b13eee09833af4240fd5c4/rpds_py-2026.6.3-cp315-cp315-win_amd64.whl", hash = "sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146", size = 220430, upload-time = "2026-06-30T07:17:07.471Z" }, + { url = "https://files.pythonhosted.org/packages/c8/95/d6d0b2509825141eef60669a5739eec88dbc6a48053d6c92993a5704defe/rpds_py-2026.6.3-cp315-cp315-win_arm64.whl", hash = "sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e", size = 215877, upload-time = "2026-06-30T07:17:09.008Z" }, + { url = "https://files.pythonhosted.org/packages/b7/bf/f3ea278f0afd615c1d0f19cb69043a41526e2bb600c2b536eb192218eb27/rpds_py-2026.6.3-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b", size = 346933, upload-time = "2026-06-30T07:17:10.762Z" }, + { url = "https://files.pythonhosted.org/packages/9d/29/9907bdf1c5346763cf10b7f6852aad86652168c259def904cbe0082c5864/rpds_py-2026.6.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690", size = 340274, upload-time = "2026-06-30T07:17:12.266Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2c/8e03767b5778ef25cebf74a7a91a2c3806f8eced4c92cb7406bbe060756d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342", size = 370763, upload-time = "2026-06-30T07:17:14.107Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e1/df2a7e1ba2efd796af26194250b8d42c821b46592311595162af9ef0528d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6", size = 376467, upload-time = "2026-06-30T07:17:15.76Z" }, + { url = "https://files.pythonhosted.org/packages/6b/de/8a0814d1946af29cb068fb259aa8622f856df1d0bab58429448726b537f5/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140", size = 496689, upload-time = "2026-06-30T07:17:17.308Z" }, + { url = "https://files.pythonhosted.org/packages/df/f3/f19e0c852ba13694f5a79f3b719331051573cb5693feacf8a88ffffc3a71/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442", size = 385340, upload-time = "2026-06-30T07:17:18.928Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ae/7ec3a9d2d4351f99e37bcb06b6b6f954512646bfdbf9742e1de727865daf/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12", size = 372179, upload-time = "2026-06-30T07:17:20.539Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ac/9cee911dff2aaa9a5a8354f6610bf2e6a616de9197c5fff4f54f82585f1e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5", size = 379993, upload-time = "2026-06-30T07:17:22.212Z" }, + { url = "https://files.pythonhosted.org/packages/83/6b/7c2a07ba88d1e9a936612f7a5d067467ed03d971d5a06f7d309dff044a7e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf", size = 398909, upload-time = "2026-06-30T07:17:23.66Z" }, + { url = "https://files.pythonhosted.org/packages/97/0b/776ffcb66783637b0031f6d58d6fb55913c8b5abf00aeecd46bf933fb477/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00", size = 546584, upload-time = "2026-06-30T07:17:25.264Z" }, + { url = "https://files.pythonhosted.org/packages/55/33/ba3bc04d7092bd553c9b2b195624992d2cc4f3de1f380b7b93cbee67bd79/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef", size = 614357, upload-time = "2026-06-30T07:17:26.888Z" }, + { url = "https://files.pythonhosted.org/packages/8b/71/14edf065f04630b1a8472f7653cad03f6c478bcf95ea0e6aed55451e33ea/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a", size = 576533, upload-time = "2026-06-30T07:17:28.546Z" }, + { url = "https://files.pythonhosted.org/packages/ba/76/65002b08596c389105720a8c0d22298b8dc25a4baf89b2ce431343c8b1de/rpds_py-2026.6.3-cp315-cp315t-win32.whl", hash = "sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577", size = 201204, upload-time = "2026-06-30T07:17:30.193Z" }, + { url = "https://files.pythonhosted.org/packages/8c/97/d855d6b3c322d1f27e26f5241c42016b56cf01377ea8ed348285f54652f0/rpds_py-2026.6.3-cp315-cp315t-win_amd64.whl", hash = "sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324", size = 220719, upload-time = "2026-06-30T07:17:31.788Z" }, +] + +[[package]] +name = "safehttpx" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/d1/4282284d9cf1ee873607a46442da977fc3c985059315ab23610be31d5885/safehttpx-0.1.7.tar.gz", hash = "sha256:db201c0978c41eddb8bb480f3eee59dd67304fdd91646035e9d9a720049a9d23", size = 10385, upload-time = "2025-10-24T18:30:09.783Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/a3/0f0b7d78e2f1eb9e8e1afbff1d2bff8d60144aee17aca51c065b516743dd/safehttpx-0.1.7-py3-none-any.whl", hash = "sha256:c4f4a162db6993464d7ca3d7cc4af0ffc6515a606dfd220b9f82c6945d869cde", size = 8959, upload-time = "2025-10-24T18:30:08.733Z" }, +] + +[[package]] +name = "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 = "semantic-version" +version = "2.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/31/f2289ce78b9b473d582568c234e104d2a342fd658cc288a7553d83bb8595/semantic_version-2.10.0.tar.gz", hash = "sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c", size = 52289, upload-time = "2022-05-26T13:35:23.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/23/8146aad7d88f4fcb3a6218f41a60f6c2d4e3a72de72da1825dc7c8f7877c/semantic_version-2.10.0-py2.py3-none-any.whl", hash = "sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177", size = 15552, upload-time = "2022-05-26T13:35:21.206Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + +[[package]] +name = "sse-starlette" +version = "3.4.11" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/54/6767bb789b2f2fed6e0f953df949cd39dc263a384c1b65a95232598621d6/sse_starlette-3.4.11.tar.gz", hash = "sha256:1bae716c02f3e6f294be41ff333220692dae7c3cbab077c900f159676719dade", size = 34972, upload-time = "2026-09-05T12:11:04.607Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/6a/2ba3ed4a69babf3afdddf7d8314a48d87562c0a442206bbc2a1b50d5efc0/sse_starlette-3.4.11-py3-none-any.whl", hash = "sha256:c7b2244bdff016fe7f64e10075e89a3e6bbf899649cc89b0fe884b5545042453", size = 17122, upload-time = "2026-09-05T12:11:03.195Z" }, +] + +[[package]] +name = "starlette" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b5/b4/205b0d5241d934e8add0c38aa924c4f9fb7330834ff11e5444db964ec3f9/starlette-1.6.0.tar.gz", hash = "sha256:d4e3ac5e546444960c710297a3c9fc3f7ebae1b7e963f3d36173b49da535be9b", size = 2716969, upload-time = "2026-08-08T18:27:57.512Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/cb/6a6a47d5b464bd08695d254f3da6e7986cc70c9fa5d778eda57538edfe56/starlette-1.6.0-py3-none-any.whl", hash = "sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c", size = 75969, upload-time = "2026-08-08T18:27:56.196Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "tomli-w" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, +] + +[[package]] +name = "tqdm" +version = "4.70.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/ea/b2a5bd54b28a324dae8211928b2d730b6547500342c7e6c6dea08bd0a485/tqdm-4.70.1.tar.gz", hash = "sha256:cefd0eca11b2a37a3aee776544d4f4ae913f02688135b5556b8788dfa474afc4", size = 171846, upload-time = "2026-09-11T07:25:16.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl", hash = "sha256:c293e525e6fef9c20e8728fd4612df02a0aa31bb5fe91ecd93e123b1b7bffa73", size = 80199, upload-time = "2026-09-11T07:25:14.599Z" }, +] + +[[package]] +name = "truststore" +version = "0.10.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" }, +] + +[[package]] +name = "typer" +version = "0.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/f7/57713ba479fd405eb76de31404b2c744c289e336b2d999511ebf51e496f7/typer-0.27.2.tar.gz", hash = "sha256:269b7eb9d3c202ca84b4bc9618cb04ebb43d3d4d1e567e4c768607232c05f945", size = 204045, upload-time = "2026-08-28T10:26:55.046Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/bf/205d0004930ede8f542fb58f601526fccf4ae7626075ca1e6c4de5d3d652/typer-0.27.2-py3-none-any.whl", hash = "sha256:b3a5fc4342d5fc8fda8fc3010b1cf117e9249aab7fae800c2eff62fd3842d97d", size = 123130, upload-time = "2026-08-28T10:26:53.752Z" }, +] + +[[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 = "tzdata" +version = "2026.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/31/3d74fa778a63b98b7374323befcc0be5ab3bd94afd4096a0124e7379152c/tzdata-2026.4.tar.gz", hash = "sha256:f1b8bd365d8d210c55353f4d7f8d6d8561c0ba50d704b700d195a9424bba0d79", size = 199350, upload-time = "2026-09-12T12:56:03.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/bc/8737e8d54cf51106118039b83f485a4783112fab49ea9d044b234978a46e/tzdata-2026.4-py2.py3-none-any.whl", hash = "sha256:c2169a8b0a7a5e9674da5a135ccdfb2b3e671b333ed9fed17b41f73c34476e81", size = 347494, upload-time = "2026-09-12T12:56:01.67Z" }, +] + +[[package]] +name = "uncalled-for" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5a/92ce0b3ea5481915f55da994c2c2c5f7a3c09949afde196ee89f8ab961aa/uncalled_for-0.4.0.tar.gz", hash = "sha256:335b95bd2422332ec210d518f314a16e4c640921c39fc8bf2ad095bd3538f4af", size = 56979, upload-time = "2026-08-10T14:51:46.247Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/40/97cec87c077eb3291fc7905e6633e08b7ca593c57d30238444bcb6bb3d53/uncalled_for-0.4.0-py3-none-any.whl", hash = "sha256:16c4bb3337532e4bd5569adc192285976f3ad5305402256d34c67a12b5c968bd", size = 15502, upload-time = "2026-08-10T14:51:45.068Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.52.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/0f/3f86e61397dd33bf2ccf28188c40db6a740658aeebbbf6e7dbc101a1f487/uvicorn-0.52.4.tar.gz", hash = "sha256:73acfee47a0b133c5de13d219492d62d8a31e935f4fe6e41a232451a15379f86", size = 100627, upload-time = "2026-08-19T06:27:41.821Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/79/4a20b54ab0491485ccd8c077db2d39187c7f12b3e15485d38a7be37c81b4/uvicorn-0.52.4-py3-none-any.whl", hash = "sha256:f86e41a149d7d05a9969337e3946a9c171c06a5d42680896daaba624aeac8da1", size = 79871, upload-time = "2026-08-19T06:27:40.36Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.22.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" }, + { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" }, + { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" }, + { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" }, + { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" }, + { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" }, + { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" }, + { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" }, + { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" }, + { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, + { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" }, + { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" }, + { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" }, + { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" }, + { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" }, + { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" }, + { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" }, + { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252, upload-time = "2026-05-18T04:32:04.251Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/2f/e42c992d2afda3108ea1c02acecc991b9f31d05c14adc2a7cee9ee211fc4/watchfiles-1.2.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:bc13eb17538be00c874699dc0abe4ee2bc8d50bb1166a6b9e175ef3fd7eb8f26", size = 400115, upload-time = "2026-05-18T04:32:02.06Z" }, + { url = "https://files.pythonhosted.org/packages/5f/8f/6af2ea19065c91d8b0ea3516fdfc8c0d349f407e8e9fbf4e5a17360de8ad/watchfiles-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d95ddc1eb6914154253d239089900813f6a767e174b8e6a50e7fdacb7e4236c", size = 393659, upload-time = "2026-05-18T04:30:50.951Z" }, + { url = "https://files.pythonhosted.org/packages/13/01/b32a967c56fb3e3e5be3db52c3d3b87fa4513aa367d8ed1ad96d42952e5f/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f70d8b291ef6e88d19b1f297a6905ddb978888d9272b0d05e6f53309856bcfc", size = 453207, upload-time = "2026-05-18T04:31:04.231Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/97557a812180338cb1abd32e1cffcc4588f59b5f23e0cb006b2ba95ba64a/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56d8641cf834c2836922899105bd3ce3d0dfc69291d52edf0b4d0436829b34c0", size = 459273, upload-time = "2026-05-18T04:31:50.377Z" }, + { url = "https://files.pythonhosted.org/packages/e8/a8/b4b08dcb7653b8087c6586f7ce649505900e866bbcfe40dc9587af02e686/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2581a94056e55d7d0a31a823ea92bf73749c489ca2285bfdc0fbe6b2bb49d50c", size = 489927, upload-time = "2026-05-18T04:31:42.485Z" }, + { url = "https://files.pythonhosted.org/packages/50/94/3dceea03545d2e5ddfd839f0ddd5e1cecbf1697b5a428d5ba11cef6af95d/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41bc1199f7523b3f82843c88cbb979180c949caef0342cf90968f178e5d49b01", size = 570476, upload-time = "2026-05-18T04:31:03.071Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f2/d39a5450c3532092b91f81d274360e613c2371bc874a89c7a1a3c5e8d138/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7571e4464cb6e434958f867f7f730b8ab0b75e3f8e5eac0499168486ab3c33a8", size = 465650, upload-time = "2026-05-18T04:30:12.701Z" }, + { url = "https://files.pythonhosted.org/packages/22/24/ed72f68cbc1333ca9b9f2200aa048bb6658ae41709bc1caad4310f4bdffd/watchfiles-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53a384f76b631c3ae5334ce6a52f0baa3a911eb94a4eac7f160079868b716d5", size = 456398, upload-time = "2026-05-18T04:30:13.784Z" }, + { url = "https://files.pythonhosted.org/packages/0d/64/982ef4a4e5bab5b6e5b6becc8cd5e732f6130a78b855f0abec6439a9a135/watchfiles-1.2.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:d20029a60a71a052a24c4db7673bc4de39ab89adbaccbfb5d67987c5d73f424d", size = 465140, upload-time = "2026-05-18T04:31:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/a0/0c/95282abf4ed680b6096010bcfc30c5fa7a041fc5aa5a2ad17a2cc6c75bba/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2cb93af48550faf1cea04c303107c8b75833de7013e57ce27d3b8d21d8d0f58c", size = 630259, upload-time = "2026-05-18T04:31:25.676Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/607c1de1530c4bdcf2cf1d1ecc2505ddba5d96bd43ba9f2b0e79876f850f/watchfiles-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2995c176de7692b86a2e4c58d9ec718f753150a979cb4a754e2b4ffa38e70906", size = 659859, upload-time = "2026-05-18T04:30:24.333Z" }, + { url = "https://files.pythonhosted.org/packages/fa/08/d9e2e0f9e8e6791d33aefc694ad7eefa7f901f63caff84a81ded38692f9c/watchfiles-1.2.0-cp312-cp312-win32.whl", hash = "sha256:7a2cffd17d27d2ecbb310c2b1d8174f222a5495b1a721894afa88ec11e25b898", size = 275480, upload-time = "2026-05-18T04:30:31.307Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e6/9d42569c0102645cc8cea5d8c7d8a1e9d4ada2cb7f05f75e554b8aa2202a/watchfiles-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:f155b3a1b2a5fc89cdc70d47ee5d54e3b75e88efa34982028a35daef9ba00379", size = 288718, upload-time = "2026-05-18T04:32:10.745Z" }, + { url = "https://files.pythonhosted.org/packages/0a/26/88e0dc6ee3898169d7fa22bb6a69cabf2502d2ee25cb8c876d1262d204f8/watchfiles-1.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:8fa585ede612ee9f9e91b18bebf9ba11b9ae29a4e3a0d0cf6fca3e382133f0d5", size = 281026, upload-time = "2026-05-18T04:30:22.23Z" }, + { url = "https://files.pythonhosted.org/packages/d1/4d/70a7feced9f87e2ff26dba42667290f41694fc64646c67261fbb8cab5d5c/watchfiles-1.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98", size = 399730, upload-time = "2026-05-18T04:31:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/31/3a/0da302f2307aee316922806ebd5726c542cbd787c938271cf14a074c7daf/watchfiles-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44", size = 392842, upload-time = "2026-05-18T04:30:27.051Z" }, + { url = "https://files.pythonhosted.org/packages/db/ef/d5bdb705c224dbc256aa0c1ec47bf4e61ec52558f2afb44a71a1fe4d7015/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658", size = 452989, upload-time = "2026-05-18T04:31:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/71/29/5495f2c1661949ef7a35e4d71111d129cfe7606414a26887a919d0a55406/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb", size = 458978, upload-time = "2026-05-18T04:30:52.606Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/7f9c07c433811c2fffd93e13fdfb7135de9aab5f2ae41be08960fa0047dc/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f", size = 490248, upload-time = "2026-05-18T04:31:36.003Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/d93632febc52fbc21be90231bb7c17fd5387f46c9076fd40a5f9c2ae6910/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0", size = 571847, upload-time = "2026-05-18T04:31:10.862Z" }, + { url = "https://files.pythonhosted.org/packages/55/b4/383173e73aabb07ad1d9c7aa859d95437ac46a6d6a1e11005facda0c9d19/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5", size = 465974, upload-time = "2026-05-18T04:30:17.006Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6c/89b1a230a78f57c52dd8893adb1f92f94411721b6ec12596c56d98c74356/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71", size = 454782, upload-time = "2026-05-18T04:30:35.656Z" }, + { url = "https://files.pythonhosted.org/packages/24/62/1732118367cfff0a9fce3bf62ff4bfded09ef5df21d9d446b858b3f70a96/watchfiles-1.2.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3", size = 465182, upload-time = "2026-05-18T04:30:20.846Z" }, + { url = "https://files.pythonhosted.org/packages/28/96/716f7e5f51339bf22963f3345f9f27d7f3b30e2eadc597e257c881dd3c53/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0", size = 629841, upload-time = "2026-05-18T04:31:05.397Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/c40783950fd771ccf66ab3ec2722d188a9af1c7f96c6e811f36e40c6e03f/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427", size = 658028, upload-time = "2026-05-18T04:31:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/71/72/4508db1856d1d87fcbb3b63f4839bab1b5682cb0e8d224d122263c09654a/watchfiles-1.2.0-cp313-cp313-win32.whl", hash = "sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799", size = 275183, upload-time = "2026-05-18T04:30:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/f9/36/14b76ca57652e5cc5fd1c11f32a261292c08a0d19a00351013c2549cbfb2/watchfiles-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9", size = 288059, upload-time = "2026-05-18T04:32:07.937Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8d/0a85e395398d8d20fadfe5c5d32c726eee17a519e78fb356f2cf7531bffe/watchfiles-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077", size = 280186, upload-time = "2026-05-18T04:31:54.484Z" }, + { url = "https://files.pythonhosted.org/packages/37/68/36db056f1fdcc5f07302f56e631774d6835bcd6fa3ace402304621d5f9e5/watchfiles-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08", size = 399031, upload-time = "2026-05-18T04:30:44.576Z" }, + { url = "https://files.pythonhosted.org/packages/c1/64/01a9d6f66a82a5c101ce939274106cc72759d62427e153f01edd2b9f87c2/watchfiles-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9", size = 391205, upload-time = "2026-05-18T04:30:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/84/2c/0a44fe058cb4bb7b8ede6b6670698bbb7c0400740e378d00022189b7b31d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4", size = 451892, upload-time = "2026-05-18T04:32:14.005Z" }, + { url = "https://files.pythonhosted.org/packages/67/a1/351e0d56cd35e6488b5c8b4fb11a809a5bc923e8fe8fed9faf8920be0c89/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55", size = 458867, upload-time = "2026-05-18T04:31:22.279Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7d/9d09605187f1b838998624049fcf8bf47b73c1a3b76901fcac1782f62277/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925", size = 490217, upload-time = "2026-05-18T04:31:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/60/5d/a17a16eccb182f04188cd308ec24b1a71a9b5c4e7098269cf35d9fa56d02/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4", size = 571458, upload-time = "2026-05-18T04:32:11.875Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3d/4dd457062083ab1938e5dfd45032eb425cee2ac817287ca8ff4356183e5d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2", size = 464707, upload-time = "2026-05-18T04:30:43.492Z" }, + { url = "https://files.pythonhosted.org/packages/c6/71/ea8c57b128f5383de74d0c7d2d9c57ad7c9a65a930c451bd25d524b295b7/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9", size = 454663, upload-time = "2026-05-18T04:30:16.061Z" }, + { url = "https://files.pythonhosted.org/packages/53/fd/2e812bf938406d7db351f0703ddd3fc6c061cf30d96153a77bc79a943a44/watchfiles-1.2.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa", size = 463537, upload-time = "2026-05-18T04:31:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/86/56/d17a7f1dd1bc3035f1072694a551301272f1739c2d8e319c927cb9e29b38/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44", size = 629194, upload-time = "2026-05-18T04:31:14.141Z" }, + { url = "https://files.pythonhosted.org/packages/be/06/f1ff66bf5cae50aa4062779a0ecd0bbaf15e466195719074078947d9a17d/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72", size = 656194, upload-time = "2026-05-18T04:31:47.14Z" }, + { url = "https://files.pythonhosted.org/packages/e7/54/a9c7ea9a82a4ac65e7004c0a03920b5cdd2f9c3b678757d9cd425aa51d53/watchfiles-1.2.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b8c8358484d5fa12ef34f05b7f4168eaf1932f408725ff6d023c33ec17bd79d4", size = 400205, upload-time = "2026-05-18T04:32:05.153Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5d/c9ab3534374a4a67450696905d6ef16a04405448b8dc52bd752ae50423d4/watchfiles-1.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f04b092229ad2c50126dd3c922c8822e51e605993764a33058d4a791ab42281", size = 392508, upload-time = "2026-05-18T04:30:54.849Z" }, + { url = "https://files.pythonhosted.org/packages/26/ca/1ad30103535cf0cecd7b993e8d50edc5351b1820e38f2d22e3df58962feb/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a7ce236284f002a156f70add88efe5c70879cccbb658be0822c54b1306fc09d", size = 452448, upload-time = "2026-05-18T04:30:53.727Z" }, + { url = "https://files.pythonhosted.org/packages/37/a1/ceee2cdf2afbd715fa07758d39c9859513eae411b23196f7fd039e5feedd/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b9909cc2b48468b575eefa944919e1fe8a36c5849d5c7c168f80a8c1db69398e", size = 459605, upload-time = "2026-05-18T04:30:23.312Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f6/421e30fd1cb3907a84ed92ab3f1983e37ba2dca015e9a894a048418417a2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a37faaed405c67e28e6be45a1fa4f206ef5a2860f27c237db9fa30704c38242", size = 490757, upload-time = "2026-05-18T04:30:47.358Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/55ed1b97ed08be7bba6f9a541cac15f2a858e1d74d2b07b6da70a82aab00/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9649193aa27bd9ff2e80ff29bfaa93085496c7a3a377592823cc58b77ee88add", size = 568672, upload-time = "2026-05-18T04:30:38.915Z" }, + { url = "https://files.pythonhosted.org/packages/d1/cf/d8ae8a80dd7bafab395ea7681c10237311bbf34d37704a8c744e7cf31fc7/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e4ff8e37f99cf1da89e255e07c9c4b37c214038c4283707bdec308cb1b0ea1f", size = 464197, upload-time = "2026-05-18T04:30:09.914Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8a/3076c496ca8dafe0e8cd03fcebdfc47be4b1174b4e5b24ff6e396e6b3af2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:054dc20fd2e3132b4c3883b4a00d72fd6e1f56fdaf89fccd12e8057d74cd74d7", size = 453181, upload-time = "2026-05-18T04:30:14.829Z" }, + { url = "https://files.pythonhosted.org/packages/e5/10/9745e17c98e7b8a86454df0a3c7b5686bd650383f1e9f26e4ebcbd6cc0c0/watchfiles-1.2.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e140ed30ebde76796b686e67c182cff10ea2fbab186fafd1560f74bb5a473a6e", size = 465109, upload-time = "2026-05-18T04:30:28.123Z" }, + { url = "https://files.pythonhosted.org/packages/8f/95/8ef4a95481d3e0cb52d62a06fa6e972e81424be2d9698b91a2fecca9904c/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:bb7e52ecf68ba46d22df23467b87cffeb2146908aa523ebfe803019618cfda06", size = 630653, upload-time = "2026-05-18T04:31:49.304Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e4/3b3bf36b0f829b50c6ebcb8d031583863c59f923d6a6af3d485e470d0fac/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:23282a321c8baf9b3a3c4afff673f9fe65eb7fdc2338d765ccad9d3d1916a5ba", size = 657838, upload-time = "2026-05-18T04:31:06.497Z" }, + { url = "https://files.pythonhosted.org/packages/21/b1/6cbbb50c1f3002ab568777d44aa21206dfb8807a840990c4037523b51812/watchfiles-1.2.0-cp314-cp314-win32.whl", hash = "sha256:c0db965c5f79aa49fe672d297cf1febc5ad149b658594944f49a54a2b96270a7", size = 275108, upload-time = "2026-05-18T04:30:06.891Z" }, + { url = "https://files.pythonhosted.org/packages/92/45/190ce6db8dcb4536682cf75d3889ff1a27182a58cb519d343cb6d9ea63d8/watchfiles-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:71283b39fd17e5408eb123bd37aeecfd9d54c81fc184421943208aadb879d103", size = 288441, upload-time = "2026-05-18T04:32:12.901Z" }, + { url = "https://files.pythonhosted.org/packages/74/0d/3eae1c2313ab08378431d907c3f8095ecca00f3eda33111cf4f0f2591799/watchfiles-1.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:c5c19526f4e54a00f2666a6c0e9e40d582c09e865055ea7378bf0009aab857b3", size = 280684, upload-time = "2026-05-18T04:31:26.902Z" }, + { url = "https://files.pythonhosted.org/packages/b1/75/fb64e6c25d6b5ca636d03df34ffb1c6e9873303e76d27967e045f8df088f/watchfiles-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d73a585accffa5ae39c17264c36ec3166d2fad7000c780f5ef83b2722afb9dd2", size = 398857, upload-time = "2026-05-18T04:32:17.108Z" }, + { url = "https://files.pythonhosted.org/packages/73/4e/9f7adf01754cbf81843722ccfec169d8f26c69778281a302855cecd2ee08/watchfiles-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae99b14c5f21e026e0e9d96f40e07d8570ebee6cafd9d8fc318354606daa7a28", size = 392413, upload-time = "2026-05-18T04:31:07.911Z" }, + { url = "https://files.pythonhosted.org/packages/47/c8/bec626bcc2d69f44b9acb24ce7d60ed7b16b73628eea747fcbd169d8edda/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4429f3b105524a10b72c3a819b091c495d2811d419c1e1e8df773a5a5974f831", size = 452409, upload-time = "2026-05-18T04:31:20.142Z" }, + { url = "https://files.pythonhosted.org/packages/00/b7/b6362068e81e7c556d155a34c35d40ac3ef42d747b06d7f6e5bf58e359c2/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43d818978d06062d9b22c4fab2ebe44cf5213d42dc8e62bda8c2760cfa2eeb33", size = 458827, upload-time = "2026-05-18T04:32:06.219Z" }, + { url = "https://files.pythonhosted.org/packages/67/f8/9a813fa42afb1e0b4625e75f0479826644d3ee8dc287e093799bc01f390c/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f732dc58b2dbe69e464ccf8fff7a03b0dd0be439da4c0720d3558527d3d6b4", size = 490104, upload-time = "2026-05-18T04:31:56.034Z" }, + { url = "https://files.pythonhosted.org/packages/2f/bf/27dfb6094ca4c9aad21298b5525b6c53cb36121ee454331d05161e58d130/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f200104103feb097de4cab8fe4f5dd18a2026934c7dea98c55a2f5fd6d5a33b", size = 571360, upload-time = "2026-05-18T04:31:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/fb/39/44a096d67270ea93df91d33877dbe91fbda3aa4f8ec2edf799d93eda8736/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ac26eefbf4af1741247d6fb68b11c49a25b2f7413fbd318a83a12aaa9cf666", size = 464644, upload-time = "2026-05-18T04:30:57.33Z" }, + { url = "https://files.pythonhosted.org/packages/0e/80/c7472203bad6268e3ef1ad260739704847898938ad7ea8b63a5131f46b50/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c4997d4e4a55f0d02b6cde327322daf3a0400e5df6c6b15948994bf72497925", size = 454771, upload-time = "2026-05-18T04:30:48.736Z" }, + { url = "https://files.pythonhosted.org/packages/51/cf/3b10b268b4b7f0fc26e9debb5eef1998b515887840f444cd3ec80c688755/watchfiles-1.2.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4c887eba18b7945ac73067a8b4a66f21cd46c2539b2bc68588f7be6c7eb6d26b", size = 463494, upload-time = "2026-05-18T04:31:33.826Z" }, + { url = "https://files.pythonhosted.org/packages/3d/3e/a4302545cd589262a0dc7d140e86f7688eba3f9c72776c27f7e23b8864c4/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3416ff151bb6b5a8d8d11664974fbef4d9305b9b2957839ab5a270468fd8df30", size = 629383, upload-time = "2026-05-18T04:31:15.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/99/d5649df0a9a410d45b7c882304d0b790903ac9b6e8f2cfd12114e0c6b9f2/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:0e831a271c035d89789cffc386b6aa1375f39f1cd25eb7ca0997e4970d152fc5", size = 656093, upload-time = "2026-05-18T04:31:58.707Z" }, + { url = "https://files.pythonhosted.org/packages/92/b9/362702539275019a54dd2e94511b31a9b89c5f9e6a21966de7eb692549fc/watchfiles-1.2.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:37a6721cdf3f65dbb13aa9503510ccb4451603ac837e44d265d7992a597e1374", size = 400109, upload-time = "2026-05-18T04:31:16.879Z" }, + { url = "https://files.pythonhosted.org/packages/8f/75/71d5ba62db781e5587bded1d944c675374bc4aa37ff33d5018d98e8b6538/watchfiles-1.2.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2b37d10b5a63bd4d87e18472d80fa525bd670586fae62e5dd580452764879b65", size = 392167, upload-time = "2026-05-18T04:31:28.058Z" }, + { url = "https://files.pythonhosted.org/packages/3c/01/c66dd95d0423fe30d31820e2d1d5bda773764131bbb6ac0cb1cf303ac328/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a105bc2283f67e8fbec74253ec2d94925de92ed72c0393f1206bf326b7b7b69", size = 452372, upload-time = "2026-05-18T04:31:00.836Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/2fe99557e72f85627c6a8eed50d889e8d101623e060a22ad75b875cb932d/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5327989a465505f05cfe06f04fa9d0c2fd5432bb243e10e6f012b1bdca3c8579", size = 459596, upload-time = "2026-05-18T04:31:34.96Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/d4acfa0023367428ed48351b3b9b267893037b6cadae55620c61c24bcfd4/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecb47f183a8025b2aa18b546725c3657e542112ae9c0613a2af79b4fa8d04ad7", size = 490869, upload-time = "2026-05-18T04:31:59.923Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5f/3164cbdce06c9fb95c4f7b9e2f9760b5e2797af43a9ecc317ef42a23a278/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8520a4ab0e37f770afc34459c4f8f7019e153f9124dc101c15538365875d1ab2", size = 571641, upload-time = "2026-05-18T04:32:00.948Z" }, + { url = "https://files.pythonhosted.org/packages/41/e6/85d3731c55e65cd7690f3f803d24c139588aaf863e4bf2148fe7a7fa1a19/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71cd71740ed2c15211ebb237ced4e39a1cdf6f80566e5fe95428da1626f4fde6", size = 464444, upload-time = "2026-05-18T04:30:34.298Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7d/562641012b8b09872742c3b8adf9629ec479fd78f8d68ae4a0c13da8add6/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f88af53d6ddaf72179ef613ddc905e6f4785f712b49b80b3bef9f3525e6194b4", size = 453593, upload-time = "2026-05-18T04:31:23.464Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/cb8ef3d6f929d14158fdaaad9925985b7310abc9384dcd4d82dd0016fb59/watchfiles-1.2.0-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:cee9d5efd929efdac5f7e58f72b3376f676b64050a91c5b99a7094c5b2317488", size = 465096, upload-time = "2026-05-18T04:31:30.384Z" }, + { url = "https://files.pythonhosted.org/packages/25/91/80908e835e100527a9267147b08c0eee1fa6ab0ffec15edc04d1d44885f7/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:b718bf356bbc15e559bd8ef41782b573b8ae0e3f177ab244b440568d7ea02cfb", size = 630638, upload-time = "2026-05-18T04:30:49.89Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/95ab2f256bb4af3cb2eb23b9317bda984ee6e0f11733a5c004a6c95b06e3/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:922c0e019fe68b3ae392965a766b02a71ba1168c932cebc3733cd52c5fe5b377", size = 657684, upload-time = "2026-05-18T04:31:32.027Z" }, +] + +[[package]] +name = "wcmatch" +version = "11.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "bracex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/43/30e407989e313677dbb9d5f045f966549a7254834571e342eaa4b55cc67b/wcmatch-11.0.1.tar.gz", hash = "sha256:1ea2b4fa678b8ca268253798d5963935df39132d47c3e241c0a0732224005e7d", size = 144662, upload-time = "2026-08-14T15:20:40.477Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/77/7a02b0f05b3ffcdbef9719ce3ee0b508d6a29b58e95299f1580055671db3/wcmatch-11.0.1-py3-none-any.whl", hash = "sha256:fd149ecddb9f0a88ea780017d6dde17c994e494e7f7303d4e3c9d6251f978f4b", size = 43449, upload-time = "2026-08-14T15:20:39.379Z" }, +] + +[[package]] +name = "websockets" +version = "17.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/72/fba934cb3dff7a85d811820efffcd141ddd52b5a2a01637f64551373ff4d/websockets-17.1.tar.gz", hash = "sha256:acfea4c20bf54384883ea33b1240fc1db4f52e190823a4e2b334bc3e8bfca96a", size = 187520, upload-time = "2026-08-26T17:25:33.063Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/0d/098f23c4c858e5de9459ffc554fa07d5493fbcfca7f040b5800cf1cecc35/websockets-17.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:76dd004f59115087c7b700474cb18f01325e37250032e19396c08ae41448e4b3", size = 217015, upload-time = "2026-08-26T14:55:45.194Z" }, + { url = "https://files.pythonhosted.org/packages/13/86/bc1317b1a4d8c4688e2a7e564b5e004dab44c2534d7ca05de6ae9a863fca/websockets-17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:581fa678ef46f4277cc8491312468e582f8ad609dbab907ba6096a08c6a0ff98", size = 214692, upload-time = "2026-08-26T14:55:46.366Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e7/df821761772beaa48c211ee0e234930b35c1473778470773823f56d3911b/websockets-17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87f0d5e77548b0c40c8464cdb6108792e7e53f487c6400028a4ec28a8afbe5ab", size = 214959, upload-time = "2026-08-26T14:55:47.885Z" }, + { url = "https://files.pythonhosted.org/packages/3e/92/c3fb72f11764812fc648bf3838d224972427b348e8b3989d9e0a9df87da3/websockets-17.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:882af300d2c6a092b93767d5de03c7bb56dfb06314140c8e872d3f48e09f7b74", size = 224278, upload-time = "2026-08-26T14:55:49.241Z" }, + { url = "https://files.pythonhosted.org/packages/fb/05/9f82d090c8d2d861604147ef6dfb938a90b039f9358d5193f1df62558593/websockets-17.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0c863507ada5805517ca6dff1c524dcd42942efe6304dacf06700878398d21a6", size = 224557, upload-time = "2026-08-26T14:55:50.348Z" }, + { url = "https://files.pythonhosted.org/packages/8a/50/5cbf677b865290fe36819ff00615826e7edc1df38786f770123ff39a933d/websockets-17.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d41ef69d5416fbc1d98cf96c37be6192d10fd101c3e0f8b3ddc36e09432b3c08", size = 225791, upload-time = "2026-08-26T14:55:51.75Z" }, + { url = "https://files.pythonhosted.org/packages/c1/1c/eb8a032285243381b09a221ae384c972d5000453ad136add4d1595cec798/websockets-17.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5aefe78e6a3077fe22b5e64b04666a85a3eb8b934d40e8595a693adcbceb6f11", size = 228574, upload-time = "2026-08-26T14:55:52.922Z" }, + { url = "https://files.pythonhosted.org/packages/69/85/413736251cb3ac04ce84cbd90e893d9a36a9698d4820b323aff3aa187e50/websockets-17.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f64e001bb7fa89b9f32cfa600bf8e9ac8ca26759d9b92ae01453ee303d9cd7b4", size = 226428, upload-time = "2026-08-26T14:55:54.263Z" }, + { url = "https://files.pythonhosted.org/packages/d2/2b/a08bcc7fa1ca81a10f84ba32b6e6edd73a913f4b0c2640eed1fd626efacd/websockets-17.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:677014a073bcb1fbaa7e21144786864f16c08f856d66834f611eceb9006cbab8", size = 225184, upload-time = "2026-08-26T14:55:55.943Z" }, + { url = "https://files.pythonhosted.org/packages/e5/8a/3bd2d0cf6b148c8c866d5d9fdcde30c04bfd81fdfac86813e69377eb4448/websockets-17.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0de501b7f2db11e83739ac20e2d33d46da4604b829f506c24be80e7def069391", size = 222430, upload-time = "2026-08-26T14:55:57.103Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c9/8e891ae342668735eabbbc669895e15195e4b45f24a4beeb58af76f414c7/websockets-17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f62114a54117e4948a1e414e89521f7fe1e3c2f83f2a571a06a4fc6718b0900a", size = 225227, upload-time = "2026-08-26T14:55:58.375Z" }, + { url = "https://files.pythonhosted.org/packages/e1/6f/c816f332dca11425e9bda7c07f7573eb5c5f8a735849d02b0d81e8ee20fa/websockets-17.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:eec113a5b41d124ef42ff56b0d74a6da3fd986400038eab9e58ee42a4024e837", size = 223831, upload-time = "2026-08-26T14:55:59.664Z" }, + { url = "https://files.pythonhosted.org/packages/53/67/5e91d5308ce24fc1ec74f56536c12f4888bad45ff5ea50f3180f8c518c57/websockets-17.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5f051f8030a51815dc00e24bd2e5f1435af095c1cc111d747ac6e2a3620d7641", size = 224600, upload-time = "2026-08-26T14:56:00.873Z" }, + { url = "https://files.pythonhosted.org/packages/bb/96/faa298ecf2570d35b0eb37caddf4992178d907e108ed74bfffb6bc092c29/websockets-17.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:655a8e28010f09fd6fa317e857afab3af7647f33e41dee88fa421e92086d1090", size = 225707, upload-time = "2026-08-26T14:56:02.001Z" }, + { url = "https://files.pythonhosted.org/packages/0b/12/5710d2482ca5061c1eec5eb46f6313837c760d4115b1795c85b6c08be4e3/websockets-17.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dc2b79afc074d2f3e64b26539350f697fe1b85ea1c49ea24eb588f247b053ce1", size = 223263, upload-time = "2026-08-26T14:56:03.092Z" }, + { url = "https://files.pythonhosted.org/packages/27/47/0c30f4eebfd1d93fae779d268f678d48847fb98516f5200849574eee8820/websockets-17.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e4bd7eacb87d8cf3ed70d6392c770a0d92441f05d7d2a3efafb5bc171d5e3067", size = 224244, upload-time = "2026-08-26T14:56:04.321Z" }, + { url = "https://files.pythonhosted.org/packages/41/33/46c256195a1255079ae23d1b1267b2e1843dc5f46a67f973cdf2a3523dff/websockets-17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ccbf3f4a9890d50b3a08ee04029fde30a03bfdeffaa19977628bf17251764e60", size = 224520, upload-time = "2026-08-26T14:56:05.521Z" }, + { url = "https://files.pythonhosted.org/packages/06/9a/aef0792731df4352e5f417369b532b3325fe434765ca90c193f594ae1e67/websockets-17.1-cp312-cp312-win32.whl", hash = "sha256:7e724f843fa6a0614aece65a7c73e51d0f4412ca41dccac13c3caf98e69536bb", size = 217485, upload-time = "2026-08-26T14:56:06.715Z" }, + { url = "https://files.pythonhosted.org/packages/50/23/493ecfdaf32898e5ea24dc900e33e5e317f9662d5d9ab2d44b2e111b4e1c/websockets-17.1-cp312-cp312-win_amd64.whl", hash = "sha256:617243e19a0992095956f406ee9cd3bc4ba92862d83cb1d83bb59ce574412bec", size = 217786, upload-time = "2026-08-26T14:56:08.055Z" }, + { url = "https://files.pythonhosted.org/packages/97/3d/91954e2f7876f74ce1213e9b92c65a63b559cc4b942a931ebeb351cd9932/websockets-17.1-cp312-cp312-win_arm64.whl", hash = "sha256:9f4a08ff7cb68c27b18e09223cc6304e01d0f82d5a240d251266dfd2e6e44729", size = 217711, upload-time = "2026-08-26T14:56:09.267Z" }, + { url = "https://files.pythonhosted.org/packages/1d/31/5f6450a7879f4f063ef08897cc385ea3ce3f1fe17f08b11e3fd959abdf27/websockets-17.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2a0162a6372110a5601cb5c9fd826635cedf69f3e110c545dd19774e040b970e", size = 217006, upload-time = "2026-08-26T14:56:10.509Z" }, + { url = "https://files.pythonhosted.org/packages/d0/2a/c1b006fc861695d2aa4e35327b842015ce1d98cf8f99241829b3d6460bfc/websockets-17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:829dba1bc049779de9b332088c1a6a9858e96bd67e50b6b644a95e02b67836bc", size = 214690, upload-time = "2026-08-26T14:56:11.681Z" }, + { url = "https://files.pythonhosted.org/packages/46/69/66e5b7d01445e0eeb1d4ab419c30315f2c90cf7a8a8cd4ecc47f894dba54/websockets-17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd8f47dbf2e8adb15c847215f83436de3fdb120b51fdae0fbbdf69fd97a3ad80", size = 214947, upload-time = "2026-08-26T14:56:12.923Z" }, + { url = "https://files.pythonhosted.org/packages/07/ce/033cafe2d2538562efa876b9149a2c7a0f7787870a4b1bb6e28adc9ceb6b/websockets-17.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9f4c0377a83e163a303514fdfab501dbe379bdc13e5b9312a91d112658b29dce", size = 224329, upload-time = "2026-08-26T14:56:14.212Z" }, + { url = "https://files.pythonhosted.org/packages/34/c7/e1c2e8a67f6cc0aa43abe0046fb3b7a020980649e6a843751dc7ce9eb170/websockets-17.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c3241d684a76eaaef8b2dc789afde4343cd3aad55ea81e4e8ab3605b529bae51", size = 224611, upload-time = "2026-08-26T14:56:15.702Z" }, + { url = "https://files.pythonhosted.org/packages/be/de/07c6d48eb3d2069709410c851e7de10ab83d752c4bd09862899627c2729b/websockets-17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e5f5c7a893507d0e83a80b88aefd6522f7e882cd53f9722c6f23f5a020c9557c", size = 225848, upload-time = "2026-08-26T14:56:16.962Z" }, + { url = "https://files.pythonhosted.org/packages/f3/dd/3c68572d20509648cc2fb6f50ccf3deeb4b87270f2c8966e99476e278ea3/websockets-17.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:00bf34b64501e3477e81fc281532ff3cbf4da26633c10b63979d5085d46602d3", size = 227290, upload-time = "2026-08-26T14:56:18.204Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4a/8f6651c8a22093539c9215af0c5bbf217b87b382c99d2112039b92d593c2/websockets-17.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ce0305b702b20d1e1d60a9aaace6bc89970e1753565543f310d549eab22c2435", size = 226476, upload-time = "2026-08-26T14:56:19.459Z" }, + { url = "https://files.pythonhosted.org/packages/f5/be/f6fc33cea86b1127fd1297b18c107e81580ab55a73a39f9a934441ef321f/websockets-17.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29176d8b429cfa0fa443c473878d37a5c06cfd0cb36b71ba4314accc71e05906", size = 225233, upload-time = "2026-08-26T14:56:20.939Z" }, + { url = "https://files.pythonhosted.org/packages/cb/83/65edaf05f7c9b1dea82f4d252fdc37706a84571646f06119a27b0a16fe19/websockets-17.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3709a1ab30b4b922027d22f68d2b61a0656a91680ac894a537624e6be7dd7f7c", size = 222488, upload-time = "2026-08-26T14:56:22.208Z" }, + { url = "https://files.pythonhosted.org/packages/07/42/d1169c2f7f1f0032b0d4b0c00f0711a070cd7c735de37bfeb876bc0f9606/websockets-17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:43bd0c1ceb924d67f5c1a5254d8361dd9d94246e6331a726064dfa2917880780", size = 225295, upload-time = "2026-08-26T14:56:23.445Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f4/64e2a386c3899b917c2933225c9b47887874229d159797f3bf1a11c20d51/websockets-17.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:1fce0f43e0d41422e0b2cad6561e1970df22f212f4c7e884967df7cf591b031c", size = 223891, upload-time = "2026-08-26T14:56:24.647Z" }, + { url = "https://files.pythonhosted.org/packages/26/b3/dfb5c482f7e310a3432fdbb045ddfe6d34114680e89a233d4ff900a32961/websockets-17.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4031152769179ab8dcdeafc7b0e58052a49117560a28671700b47b2c7b717aad", size = 224661, upload-time = "2026-08-26T14:56:26.027Z" }, + { url = "https://files.pythonhosted.org/packages/a4/cf/94865130a336029f46412adc127c4fbe380f46172b90ce251369e35c4302/websockets-17.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a06f3b5085176763182449559e20391d7ce616a8972a9f7a33deda87ea6d4f3c", size = 225766, upload-time = "2026-08-26T14:56:27.455Z" }, + { url = "https://files.pythonhosted.org/packages/96/34/eb8c658f86dfe562ed49a887a27424bfe9e618c26ea6f865b093d075d3a6/websockets-17.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:77b37cceca17291897c3c73bd30a7c7c7909593554b5da574ec852af83c1742a", size = 223323, upload-time = "2026-08-26T14:56:28.807Z" }, + { url = "https://files.pythonhosted.org/packages/1b/7e/2629609652ece5ca0c7ac235927dd4511b08131e3a5d53439b798fddf002/websockets-17.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d8e83333385cac6030a5167fd18bf96cc6c58b914c308e683f05b0cf94bc8dd0", size = 224276, upload-time = "2026-08-26T14:56:29.991Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/8525737fe840b38e5f40956c198fb586a4fac1e07144d41a5b949b989cf8/websockets-17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:073c5c3f7e127041fa9d34a9e29ceefee8c3cafbd267ed2927318f425144380d", size = 224558, upload-time = "2026-08-26T14:56:31.184Z" }, + { url = "https://files.pythonhosted.org/packages/74/ab/3a958c6cbcf74b118f601c20a80ac8bd5e8dfec0bcf7345116feaeefb121/websockets-17.1-cp313-cp313-win32.whl", hash = "sha256:2afb58c7ba48b329d56769f8dfd89f394efe587b65ef806bae810a484d6d3608", size = 217475, upload-time = "2026-08-26T14:56:32.431Z" }, + { url = "https://files.pythonhosted.org/packages/22/36/fb521f0f2994c25509651f169efe5582dddd8713d57a0757ba87859372ef/websockets-17.1-cp313-cp313-win_amd64.whl", hash = "sha256:0340bbef6bfbe16da888b3983d666a4db4954ac3253c38f13bc7aba0c7db5a2f", size = 217784, upload-time = "2026-08-26T14:56:33.608Z" }, + { url = "https://files.pythonhosted.org/packages/68/92/9b8419584681a12a7534b746dfb2737c466efe2455483e2fbf8b941a04ec/websockets-17.1-cp313-cp313-win_arm64.whl", hash = "sha256:7a72efa3bf4fa3a6669a54420a472ad056da3973d827f10e3a536da463f926c2", size = 217715, upload-time = "2026-08-26T14:56:34.865Z" }, + { url = "https://files.pythonhosted.org/packages/90/0d/500cf5daea09d4669dff3a7d67159094a0bd6c4ef130381404f6edd3eb5f/websockets-17.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0c9982938980e086da59f70d05f9418cd143401a601a0faac10fa48f7bb1cd3e", size = 217048, upload-time = "2026-08-26T14:56:36.03Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/5b12c6168aa269cffbfd24d177cd492b130120403a418c7e89462e27b4ac/websockets-17.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:57b39dc8541cf7ed3f639da82bf7451060483967f9e733da1f8173e4095f0642", size = 214737, upload-time = "2026-08-26T14:56:37.43Z" }, + { url = "https://files.pythonhosted.org/packages/0c/36/e453e5106e4e2416f008ac222837c2f1637a063b08008afcd1088889b631/websockets-17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:96abdecbaae746851b87c3a36cb4a661df93ca3d92f114270f79228bf1d00de6", size = 214955, upload-time = "2026-08-26T14:56:38.71Z" }, + { url = "https://files.pythonhosted.org/packages/dd/30/0204bb86176db02cdfc678ce65ed808a66fab87d250ce61a8790800a60b0/websockets-17.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d9fc873e239c5abeb150bc24dbd1a7af23a9254526383ce0a077f5e20adbeb19", size = 224331, upload-time = "2026-08-26T14:56:39.924Z" }, + { url = "https://files.pythonhosted.org/packages/46/c8/d8372256e00c4e3cab1115c45075d1eeedb642a3f2b42bd70c4deae03f06/websockets-17.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f42912fa9eb4cb7c7ec9fde9b3332ba339eb8a8811981043d4029599f3d950b", size = 224685, upload-time = "2026-08-26T14:56:41.169Z" }, + { url = "https://files.pythonhosted.org/packages/12/7d/650355b8f67f908ff99603351d4458d1a0b787d627950a47c38db7e25308/websockets-17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f98bf378d7a5be047a044a1a27c987a8f355e10e3b5754617dbe756248cbc5ce", size = 225927, upload-time = "2026-08-26T14:56:42.359Z" }, + { url = "https://files.pythonhosted.org/packages/34/6c/a9ffa5b903579eed76017870f055d75ecc73988d9d0c9b65a92ba0bf2a27/websockets-17.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d334d11398086bb5559606cb42d51c013ea7c061c7db701521392373d3c087f5", size = 227300, upload-time = "2026-08-26T14:56:43.538Z" }, + { url = "https://files.pythonhosted.org/packages/9b/5d/4551c2269066af7481ee44605a0813770961615b5b5da3e87a8f5cb859ea/websockets-17.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c27336b1a0ac56569493e858497870347854372395f50483725f8cdacc5a45c", size = 226533, upload-time = "2026-08-26T14:56:44.669Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/237a99233e5c445759a613831b3a92e91905afc064dc3bd0ad33c35fd1e2/websockets-17.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:67258b00302a5aaf0b267771c7014b13429abd7ea17eebc4c55bd935ff101555", size = 225280, upload-time = "2026-08-26T14:56:45.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/b5/e9407a91613d1d1cd932414143a1012096b26674a782fc55a0bd23217ee4/websockets-17.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:455ffeea0879d313205df1e745e5883e1feb7f31ecd26be882f5f0babd3db04f", size = 222540, upload-time = "2026-08-26T14:56:47.053Z" }, + { url = "https://files.pythonhosted.org/packages/db/d2/db76628db0577b783205d9779f64d8e373416b04c62d1546be4b75dc8540/websockets-17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f7233eaf441a345a5943a929fd4b5ea3278f11aed35a9ed0f3106b8cb3ca846a", size = 225354, upload-time = "2026-08-26T14:56:48.32Z" }, + { url = "https://files.pythonhosted.org/packages/a9/4c/2174181c067b89a74ae18e2650c2ac29959f4b796afe876ab3f4d30d642c/websockets-17.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:c65da239a5ad553619804c1f9d65c1a0b3005381c6158ee14da2c7444cbd0c78", size = 223867, upload-time = "2026-08-26T14:56:49.579Z" }, + { url = "https://files.pythonhosted.org/packages/df/75/274decb9a8253561b5be3261e02a6676fc8ecdf31e95b722e53d5bfb8fd2/websockets-17.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fa1ffa08c81a4f809cdab6129f8e55bee4650b9d6d3461019dda73aacd146b6", size = 224652, upload-time = "2026-08-26T14:56:50.885Z" }, + { url = "https://files.pythonhosted.org/packages/9f/e6/49824f1fb4db7656d2f7492b1d8be16147b759d909490e32f4776843ee64/websockets-17.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:406b8107943a43ef4649b1e0cb0cdc052bbf08fe1c8905a623c4af9586e5cebb", size = 225822, upload-time = "2026-08-26T14:56:52.356Z" }, + { url = "https://files.pythonhosted.org/packages/b8/6a/5dc43838c0b02a95f42c47a0de33c5ddd7767a9feeb4d0d8777ac1cfefe4/websockets-17.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:4e8ffcb486c8490a34a4cef5e4409d8da5a1cb1681e5bf7d786ce5e84aa8540d", size = 223379, upload-time = "2026-08-26T14:56:53.699Z" }, + { url = "https://files.pythonhosted.org/packages/c2/62/585637cf06d6b321232f79c55dc14d65518d12cf87c94c44f5864068810e/websockets-17.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:fb88076df585b69c5761c387c0081aa87d7b9eb1b205a6535ca4777e25650d81", size = 224330, upload-time = "2026-08-26T14:56:55.184Z" }, + { url = "https://files.pythonhosted.org/packages/de/68/c3b234a6a1366b6ab5bbfaa4434a1b946e1dc4e8ddd6824bfd93a8835b7f/websockets-17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5d4724255fb8398acd9e583b97eb2279cec20e0bd0f9a94bf75f6056ef9f13da", size = 224622, upload-time = "2026-08-26T14:56:56.393Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d4/84cf3d1376f5d8207f55f43c1c818babd6b89447f5dcd01f18a6d5526796/websockets-17.1-cp314-cp314-win32.whl", hash = "sha256:be3f0129c5654517b2abf07dcb75bb1d9479759a4ccfb569e8293579e9fc029a", size = 217036, upload-time = "2026-08-26T14:56:57.652Z" }, + { url = "https://files.pythonhosted.org/packages/d0/0f/9e7ac63c5d7cb642952200814f584318e65146df008b7d375d5d9c6b2c97/websockets-17.1-cp314-cp314-win_amd64.whl", hash = "sha256:2a4dc6ef83f4559e0d05f313a375cb38f63c986096a9da99fe94fdd779d313e5", size = 217382, upload-time = "2026-08-26T14:56:59.065Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/1ae6b91f7f3ac05f5c9f14a72dc2181c115ff370bcd8a7f10f02c174adfd/websockets-17.1-cp314-cp314-win_arm64.whl", hash = "sha256:46c0331c9eaaf73a559f3a9e388466be0df96eb83d40f06f1ca6ab6613b35c82", size = 217268, upload-time = "2026-08-26T14:57:00.654Z" }, + { url = "https://files.pythonhosted.org/packages/b3/f0/f65644d0e0b2b90918a8c41503841cc4072a58f2bf76c09bc36e751fc0dd/websockets-17.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d411ea5ca18ac1b12c0c94be88b60c18ca641ac43bcdfdf1c9f79d46cdbe1603", size = 217379, upload-time = "2026-08-26T14:57:02.181Z" }, + { url = "https://files.pythonhosted.org/packages/ff/35/4c46d1f620ac1a30f92b6eae78ee40a772a93f568647ca7ccdc5ea283cf8/websockets-17.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:07fa3e7c30e2c577928d359b56bf872a3e0cbcc15553eaa0907c1ee86344b56f", size = 214911, upload-time = "2026-08-26T14:57:03.478Z" }, + { url = "https://files.pythonhosted.org/packages/04/6e/4587e8406d7c1188e97b9cf466c081e93399380d447f885bfce81626cd37/websockets-17.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6de9acef07e3a78e9567fcd26c29011a4da8f050b13004bbf880a0fd82a6eea5", size = 215115, upload-time = "2026-08-26T14:57:04.692Z" }, + { url = "https://files.pythonhosted.org/packages/ec/06/1381c8fff525041025909eb80ace32489194a00ba22a0a8d428030afcc84/websockets-17.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ea0ed9373b880115911d9d39634bccc95b8ce590c9c42e8589f5cacc3ef3cee2", size = 224696, upload-time = "2026-08-26T14:57:05.899Z" }, + { url = "https://files.pythonhosted.org/packages/36/9d/9034e867dc85340be058619751742b895f722326e83100d110063461ca07/websockets-17.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50903d335bfda026c2fa11dd9aed09d8cbee0c451e3a85122a9acb041b7dc69b", size = 224975, upload-time = "2026-08-26T14:57:07.262Z" }, + { url = "https://files.pythonhosted.org/packages/40/eb/ed03aa3cae748ebf6397e5d44028f433f746bad09dc568ff754fda3a3c9b/websockets-17.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a74531ce81af587f906ab42f194032388fcff8fc7938402e5917c9147a39441", size = 226151, upload-time = "2026-08-26T14:57:08.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c9/cc1964a096d16f3b73cb1ee5f14f277f5a3bcac07c6e8f9a1dcded99f4c8/websockets-17.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8fbf28e639544503b7d1c96452a5e5e043e4108d89b1f3fa02910603622d19db", size = 228292, upload-time = "2026-08-26T14:57:09.846Z" }, + { url = "https://files.pythonhosted.org/packages/1a/26/46da6dd0363c2db2e4876fd59a40fd40c1943a82d7018d0a33afbce47d52/websockets-17.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f612dc57f00c07cf4aa2673f7cbceabd654ad2457b7e639f061b794d6e11f9fd", size = 226722, upload-time = "2026-08-26T14:57:11.118Z" }, + { url = "https://files.pythonhosted.org/packages/78/98/ecd8f5e1c5d0e54c08ebc5c66852271112166db68107cb0e17ca1bf25009/websockets-17.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1c7ac77401227212dc6e849182feee50d57cf456ec6329ffd6979c94bb136c5c", size = 225451, upload-time = "2026-08-26T14:57:12.601Z" }, + { url = "https://files.pythonhosted.org/packages/65/4d/da8d2760db53e17aae763738b6ba834b1fcf16813d3632f3edb6951e1ec8/websockets-17.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32a2a68d989d6e5b74a9d5095415c51189ebae29fceb7cf2b64a1c0318a81256", size = 223003, upload-time = "2026-08-26T14:57:13.875Z" }, + { url = "https://files.pythonhosted.org/packages/a4/40/ea401c141a79c5b1d0021a0dab9d0df2051c108f1620fbb39a6e7c714c3b/websockets-17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:aec00f018d34c67500ff0438dc314b40277be4a1b983cbacbf53ccf7db63e257", size = 225704, upload-time = "2026-08-26T14:57:15.091Z" }, + { url = "https://files.pythonhosted.org/packages/e1/8e/07ab3f44215d89840d5385fdcaaab1fed8caeffa67c6899e15062957c12c/websockets-17.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0014eaff8ad5b3b43feda2279f9d34bf2eaae040720b9fbbb55944b10f40b14d", size = 224192, upload-time = "2026-08-26T14:57:16.3Z" }, + { url = "https://files.pythonhosted.org/packages/58/93/ccf1af0a23e5748d4e22292a377d78d15cf294d7e707bbb11a8990ae6bd5/websockets-17.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:db9d7ee47f3ba531e278be539af39e2c7c7d28fb94897b6cd1120d63b0ef5922", size = 225082, upload-time = "2026-08-26T14:57:17.531Z" }, + { url = "https://files.pythonhosted.org/packages/e2/db/e32200f99ce282e728d2929f2c429db353cf3282db7d0eba99eb32c9fec1/websockets-17.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:ff3e2ba7a9f0a110b0555452e9b5a03a34e11662544e01beea15f144b48ba7b7", size = 226101, upload-time = "2026-08-26T14:57:18.802Z" }, + { url = "https://files.pythonhosted.org/packages/28/3d/e7a6e9777b29433620167c98f3caaff0d6b08b1239a273ef7f7fd1393349/websockets-17.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:6da17fc94bd270f5987b10bee113461ac36a36a98b0481ddcc98056e5a90001a", size = 223794, upload-time = "2026-08-26T14:57:20.313Z" }, + { url = "https://files.pythonhosted.org/packages/48/05/ac569090726dedd6656f3ee28b0c02dfb1ba76e898dceaccc2987a237cef/websockets-17.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:e8dc3fa6d6b7ead3f9de57895f41b116a28787548e066365d9d90f7356bcaad2", size = 224567, upload-time = "2026-08-26T14:57:21.634Z" }, + { url = "https://files.pythonhosted.org/packages/14/50/4ef62941111db6b31193f4fabbb65f845a5177579040cb8fe0d774d25034/websockets-17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b65d5fe48219dc2d5e158de9e6514e75600f379cc7e37108d35f31764c155566", size = 224993, upload-time = "2026-08-26T14:57:22.86Z" }, + { url = "https://files.pythonhosted.org/packages/28/42/2b95ada4ea19bf3a2072b68669ce4f4afb212690b727d31640576287fd68/websockets-17.1-cp314-cp314t-win32.whl", hash = "sha256:2cce251f3e2469b99b6802b55435bcdd07123b41870f54c87b336183af9d7e68", size = 217168, upload-time = "2026-08-26T14:57:24.466Z" }, + { url = "https://files.pythonhosted.org/packages/32/0a/67d5ee08dd8060a37d612fd40a625b5376ad19ae48fe1c8ad428c278b817/websockets-17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8f6c38cdcaf98a911d7acc25577f2f9e710f3a2fc2bde1563556784320196b51", size = 217508, upload-time = "2026-08-26T14:57:25.983Z" }, + { url = "https://files.pythonhosted.org/packages/76/a3/822005d0c674451d2411027b878cdc128a2b7ea5a30d337d9e279da22eba/websockets-17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:d1e2f5fa2b6d01f0d85b4f223fea7ed1d504be282a02a81bd2be4817ef7a2f03", size = 217425, upload-time = "2026-08-26T14:57:27.324Z" }, + { url = "https://files.pythonhosted.org/packages/de/d5/99a6c6a1eb5d5ae9f45f59a3c97f4e3b21f310eb404a547fb3e7d2fc054c/websockets-17.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:88381602e379165b66244b2ebc29f9b23ea0851fbe63ae157f91ca324f072d6f", size = 216970, upload-time = "2026-08-26T14:57:28.575Z" }, + { url = "https://files.pythonhosted.org/packages/a6/0e/1e7f6e833728193958d3ed3d67b5d57c3c7cfa948abf94d4bc553257c954/websockets-17.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:88bc5138e53903a85c354e59df7ba73ce306f7b09724cef74dba121e60a88ce2", size = 214699, upload-time = "2026-08-26T14:57:29.862Z" }, + { url = "https://files.pythonhosted.org/packages/07/00/95d39549f86e34425a0412bcbe61708dd1fc46af654e2134a6c4389102ad/websockets-17.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:3546ef55b3a074494106508bc6505c73825970d2d9505f7bf53882b3e88b0d1e", size = 214927, upload-time = "2026-08-26T14:57:31.148Z" }, + { url = "https://files.pythonhosted.org/packages/4c/ff/b442415fc4f7f9943b0fc8e8eebaa13923ca73361e167c439ba634eecbd9/websockets-17.1-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9ae55d24241fc055f22aea3ac924559069848bd0ad4ea065fdd72d2194685fe8", size = 224373, upload-time = "2026-08-26T14:57:32.833Z" }, + { url = "https://files.pythonhosted.org/packages/a8/dd/b83537aae4cf61615b9d8b2dbb235c0030ba85457a6d934798273814600f/websockets-17.1-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d7b349265fad6244013eecd99df8d83c12bf3013943e431f4fadd5bffc37db42", size = 224801, upload-time = "2026-08-26T14:57:34.041Z" }, + { url = "https://files.pythonhosted.org/packages/76/83/5ab0abed58454909e8dbab45086ac68ee4556d7a8ada26735addc909b903/websockets-17.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc5789e5ea182b77a38881383ada5347202a6c66f4857d054e075290e80b604b", size = 225967, upload-time = "2026-08-26T14:57:35.292Z" }, + { url = "https://files.pythonhosted.org/packages/4b/26/e2412f2b998a8c1dfc00c0709ff6ee0c634dd0b0b4f92bdfe9667876b71c/websockets-17.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ce13c7d233239e739600a57d4a73c1192ad8259e655a4d55aa1a454242bc809d", size = 227664, upload-time = "2026-08-26T14:57:36.493Z" }, + { url = "https://files.pythonhosted.org/packages/ec/25/0dd4495df3c0e02f6db705312ba85ab9b2dd42257dc23eb0da10066e4844/websockets-17.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1036189bd34b0bc1b10a4679321e2c7968af317efe6e8e4c1c5141c4254fb5bb", size = 226447, upload-time = "2026-08-26T14:57:37.781Z" }, + { url = "https://files.pythonhosted.org/packages/be/67/6df3f63ffc48f08126ed0cd2fd2a41092967c3e364f8ec100deae90b6d77/websockets-17.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e78fd4b7b2c5086a38671c9c882c1e643385eccea360b5b1fda4a105e590087e", size = 225343, upload-time = "2026-08-26T14:57:39.133Z" }, + { url = "https://files.pythonhosted.org/packages/b1/8d/a8479bbb09ff054907d141123d8f52fb6ae5ac39c6dbe39e6a02a8408309/websockets-17.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:46e7a10bf04318c7b0c0273791925ae5e1cbe4a11e34aa934d2ef27862058a80", size = 222748, upload-time = "2026-08-26T14:57:40.478Z" }, + { url = "https://files.pythonhosted.org/packages/40/fb/4c3d2a3269cde3f3087916de9c3d9fc5d7196b46846d8c3a9ae59ad0a884/websockets-17.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:33e45c7ea38428e740a7f233555d71df0b875cef7fc080acebc9654475e35335", size = 225453, upload-time = "2026-08-26T14:57:41.859Z" }, + { url = "https://files.pythonhosted.org/packages/7f/1c/6467b401d19408f34e1c7389c222c2c7e1dfdf08c551190269b5eabc726c/websockets-17.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:6e63c01803be425ff062b7f7fc201a74def1d49fc94a2410dd17375df75936e9", size = 224112, upload-time = "2026-08-26T14:57:43.136Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5f/744e032ac80e11039a7447657ebabb46e9b5c2dbcec83be571335212932f/websockets-17.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:722ec21717eec6477bce582147a28acdfe034e604239466a6a95daedb863e774", size = 224646, upload-time = "2026-08-26T14:57:44.871Z" }, + { url = "https://files.pythonhosted.org/packages/9f/47/bcb9128d9afc4d0934d9192e2a24897ca2f7a63df2654904915349c6c46d/websockets-17.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:e74e41f0ad12ff1e8983e349daef79d37cc8280c743ce9d134d6c74c18dab5d6", size = 225797, upload-time = "2026-08-26T14:57:46.338Z" }, + { url = "https://files.pythonhosted.org/packages/c7/e0/b058047b7cf565e1105b10ef6b6b24a6ebe3575678c7dc75a645334705a7/websockets-17.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:12fe8984a32dbfd084e0603f1a8d740c0180cb85b3174585c54a80d2455a8394", size = 223605, upload-time = "2026-08-26T14:57:48.175Z" }, + { url = "https://files.pythonhosted.org/packages/b9/69/fc1555bff884de363f1bf9eebf2836dbeb29fa7e4f957debb7bbcf43abba/websockets-17.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:01dcb47deebc40b38fd4a493b9b9f4d0a704b7bec6f35e4d34085b329abce71a", size = 224508, upload-time = "2026-08-26T14:57:49.407Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f9/648d4e68621688b19093b06f7b497d520952e68cdea1c1b54371fe9491de/websockets-17.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f4c45ee2512d3757b5e6c67c5a34e435143f2ecb7df3324f9fd888688c45c0f4", size = 224767, upload-time = "2026-08-26T14:57:50.799Z" }, + { url = "https://files.pythonhosted.org/packages/58/93/f8342b55864f71df13eb8e9ef7dce691b87a87f04f75bb8a1385b3336e7c/websockets-17.1-cp315-cp315-win32.whl", hash = "sha256:0f4f50dfe2cc810fc4e2de979b35e83bf8bb4bccdc6fe472d93762ea7b1d5927", size = 217003, upload-time = "2026-08-26T14:57:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f0/7b5fdb774c245e0b6217009e2a24d2105c1a64923949f33be41aa7959302/websockets-17.1-cp315-cp315-win_amd64.whl", hash = "sha256:4af784f3e436f65b355c117c6497320f2b5cf6a559295cb1c4c7338e335d45cc", size = 217300, upload-time = "2026-08-26T14:57:53.492Z" }, + { url = "https://files.pythonhosted.org/packages/76/33/1fe6ed1b5087516115ca451b2c240314b010647071f8fc3bd78a21e4dddb/websockets-17.1-cp315-cp315-win_arm64.whl", hash = "sha256:d58159af7835fde09c462394293c0d7aaf8fb4557d8f8e5699f5e722ccae013d", size = 217214, upload-time = "2026-08-26T14:57:54.88Z" }, + { url = "https://files.pythonhosted.org/packages/94/ca/ed02e75996a266d76c5fcb5dd9b930db4cf2b388ca5fa3d2a72086f81568/websockets-17.1-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:1a5cf4e7bbe3ca499e6a289206cb4fcb7444b09919e129bd517f57d5fa192c13", size = 217282, upload-time = "2026-08-26T14:57:56.108Z" }, + { url = "https://files.pythonhosted.org/packages/bd/7d/d536f5bc89ea5b52fd1c1727c59fabafee6bc41f5ce92c3bd2f83047908c/websockets-17.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:416b4bc8789a1865a3ff643ec4ee073a5f52402d0dbeafd27b1798d5dd6b6a51", size = 214863, upload-time = "2026-08-26T14:57:57.355Z" }, + { url = "https://files.pythonhosted.org/packages/37/37/944cf17bad668e9be1247e6314f88a48b9faf7c250e383410db8b38af0b9/websockets-17.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:259f45358c76d3b18489e3e80636cdbe807e05ecf1b10fdf1a779106d23d0c8e", size = 215073, upload-time = "2026-08-26T14:57:58.719Z" }, + { url = "https://files.pythonhosted.org/packages/74/bf/3267966cc1bbc2b8fa62fd329651b0af502df1f5d1c0eed027ff339d6aa8/websockets-17.1-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d9d01e8ede41fea4f5a847dad9d628355f74905f437a5b6856d67aa66d193800", size = 225229, upload-time = "2026-08-26T14:58:00.235Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d8/85ea722f483510abb39fc71aafb4465d17cf9051a275ab036874ff3c300c/websockets-17.1-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a7b35181a14cbfcae163b4de545d22abfd07d06c2c41ca69cfcd99251d6888ab", size = 225500, upload-time = "2026-08-26T14:58:01.994Z" }, + { url = "https://files.pythonhosted.org/packages/50/ce/64c7d00005bd0d15ecb5c5fcb7fb2597b6b92ddd16c4fa6bbc3d2835ad63/websockets-17.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a8e768a048c2220697477ce2e67e4345dc9f693d0ee6af53945b5e30227c6a7", size = 226829, upload-time = "2026-08-26T14:58:03.327Z" }, + { url = "https://files.pythonhosted.org/packages/b4/dc/096c67940fb957e667ca3c542818150434eb0388c6fdc90b3a502f3c3e96/websockets-17.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:880069d21cc33a558dcf180924a546d1ecf8ada5be3e4e70acee87019d706a24", size = 228457, upload-time = "2026-08-26T14:58:04.78Z" }, + { url = "https://files.pythonhosted.org/packages/51/fe/f2331b6b7ccc67589891da354fa46a5cb79e95f83b9fd0e734d77f1f2140/websockets-17.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cec1bb8f22abccc8d20f8ca63df9be41600c26c190f4b97ee86c675fd4a863a6", size = 227265, upload-time = "2026-08-26T14:58:06.102Z" }, + { url = "https://files.pythonhosted.org/packages/47/a5/fb1642302f8ec77ca922203074f155a9831a5128ad75e725059a476d1227/websockets-17.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f3a1d577e081667dda7f8e5b4796e6e32f9713c93e2a3d930669519840a3c623", size = 226143, upload-time = "2026-08-26T14:58:07.464Z" }, + { url = "https://files.pythonhosted.org/packages/d7/41/7133fcfb63f5562750b269d6a845c689dde6a2c6407286da395beea19ddd/websockets-17.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dc053f9e95a76213c5eb7ed95779f7daf0d2bf0e4e03073629ebfa43a033f151", size = 223501, upload-time = "2026-08-26T14:58:08.766Z" }, + { url = "https://files.pythonhosted.org/packages/64/b1/82b36bfabc79ff2d383a1fc043cee6a13f794ef4f6bf1b4810ad6988cf6f/websockets-17.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:bb0efe019480a1c93e168ce96479273aaebd672fc8c350d5eed1e507ababb1b8", size = 226330, upload-time = "2026-08-26T14:58:09.987Z" }, + { url = "https://files.pythonhosted.org/packages/41/7d/5b511b9bf6e9ad331e6ff902fcbcc71c3794d10ef3b5efe80ccb8f0a7861/websockets-17.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:615746b12b26a3fd4077bc6fbeb277a1c192a45dd57b531d07ad9ed5c52a9a7a", size = 224980, upload-time = "2026-08-26T14:58:11.303Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/aed08f25301f8eef23be903ff9319fcf35630ca2bdec9d226f7d804dd5b3/websockets-17.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:1a20136d61f9ca3a31493732762661fafc2c20e8861930214e21afc6a8a692a2", size = 225478, upload-time = "2026-08-26T14:58:12.543Z" }, + { url = "https://files.pythonhosted.org/packages/3e/47/0d63d4168536b4682c9d19b7399443b1176f25dbb68878374fa716670230/websockets-17.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:2786cbd273ab69c22612db8a41229ddf2c158060b17b5928884bf388d07887f3", size = 226588, upload-time = "2026-08-26T14:58:14.457Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/844bd0b6386fc81ed6a55f4b6dd26f01c6987eda205afa10175ea12b2164/websockets-17.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:b1c323fc3be1dc3f87f6c59458cb7d9e13dcbbf971d6c3f3e2bbaf58d3bfcdfe", size = 224336, upload-time = "2026-08-26T14:58:15.778Z" }, + { url = "https://files.pythonhosted.org/packages/96/18/03709c84bc88ec4dcea68d4be4ccd07d611073dec111203a5bf45af8809d/websockets-17.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:12c8e2b25df59755954a04dfa09c990b96691025aaf7eafd19ed6da24b09c18d", size = 225197, upload-time = "2026-08-26T14:58:17.141Z" }, + { url = "https://files.pythonhosted.org/packages/27/cf/0d1c694b6466c89e875b85b32b51312c472cf6708eee91914866f5087dde/websockets-17.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f58f58b4b29bbea2a3635e2c56eff4a3adab011fe383802a9e542e31b97085fc", size = 225493, upload-time = "2026-08-26T14:58:18.521Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f5/99857c3dd9676749f33e3668665a34ad6099505fb8d75eb084f49f7807a9/websockets-17.1-cp315-cp315t-win32.whl", hash = "sha256:f78a3ffb1994304db2c0c4588e4d1a518079b557054fa3bb985a6f5e50ff49a3", size = 217130, upload-time = "2026-08-26T14:58:20.037Z" }, + { url = "https://files.pythonhosted.org/packages/2c/84/77599922ab441bfe61508f97dab2c71f8e114d31793993ea54011db16199/websockets-17.1-cp315-cp315t-win_amd64.whl", hash = "sha256:ad68c28a27246fed109a4409393d677b7e1388345cbbd2f5aee5c182d8506110", size = 217448, upload-time = "2026-08-26T14:58:21.382Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3c/8b9a225b523f06a9389be81f1b0ab07c49bec6014742e6aa359c1f920f1f/websockets-17.1-cp315-cp315t-win_arm64.whl", hash = "sha256:e552e0037230ac16e5f568de7012041344d1b18c9feed30ec2891b8eba55af81", size = 217372, upload-time = "2026-08-26T14:58:22.807Z" }, + { url = "https://files.pythonhosted.org/packages/41/63/23572870e01836a98346075b9e17a8bc24a6ddd9800a3204ceee58677f3c/websockets-17.1-py3-none-any.whl", hash = "sha256:f221081107b8c48184d99f7019604486376e7ef826037e70aad6b02540732c23", size = 211134, upload-time = "2026-08-26T17:25:31.397Z" }, +] + +[[package]] +name = "xxhash" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/a5/1386f35da1475fcaeef42581deae73417c6d2a6a0b2d2e8914de18844dcd/xxhash-4.0.1.tar.gz", hash = "sha256:d55bf4ef10eb09b8b6866790e083d26d087d84caa3cc0946ba87c3ca7ecaf7b7", size = 101513, upload-time = "2026-08-17T08:24:08.557Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/6c/dc7cffeadd06336cd934947187cd38abb263103bbc552ca0f55fe4ff595a/xxhash-4.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1ee523f51718e41753f04f7102bb4dc55a18d2ea5cbaceef8ec7ca08571bd428", size = 38444, upload-time = "2026-08-17T08:21:54.332Z" }, + { url = "https://files.pythonhosted.org/packages/75/c9/cf736f6db8c3273af18925061572db0d4357818a9ce425f4b5fb0021918e/xxhash-4.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:515a822c73abbf6a0b7c70976d9662be342835c9d78b8dc7c023411f39c35dbc", size = 36195, upload-time = "2026-08-17T08:35:13.004Z" }, + { url = "https://files.pythonhosted.org/packages/da/a2/ca1929354b6851529d0148f7f335b5e2b0281f83bab3e19f0896dc579796/xxhash-4.0.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f5d031f35962e5483a613214e61f09fe24ab523062c3646d592dc16c4a217451", size = 253113, upload-time = "2026-08-17T08:20:52.152Z" }, + { url = "https://files.pythonhosted.org/packages/de/bb/542005206af59518bc8d78a210f1e0172217bc53beb32f64a5b632e72b6b/xxhash-4.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da0264844a09b538c894e5eff25313d941deb4dedec2131b98418a71a3c9944e", size = 276525, upload-time = "2026-08-17T08:21:01.886Z" }, + { url = "https://files.pythonhosted.org/packages/1b/df/607cff25dcb0f1d35c3b04493f6ad8471edb03fd4eacbdcc5ceddef1f3e9/xxhash-4.0.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1642907941ee4b75aacc3db688af52ea02ca2305ab22af7ee686ed726b332684", size = 297703, upload-time = "2026-08-17T08:21:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/15/ba/9d2275eea0b9d9c6b02921be23f7588356c60df95c763b25f0e045894d43/xxhash-4.0.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4af350bc3f329970c0e3a59af84a8a30998bf8a9167eb50cd48e59baaa1d7bec", size = 280252, upload-time = "2026-08-17T08:20:47.299Z" }, + { url = "https://files.pythonhosted.org/packages/1d/aa/2299d9f6369e550aef2abb64945e39daa34412725aa46a20d99b74d76f67/xxhash-4.0.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8ba782ca3bf1e81492611152b9a0d5264971339e95e34d69de0ac2c926be496d", size = 511041, upload-time = "2026-08-17T08:20:36.771Z" }, + { url = "https://files.pythonhosted.org/packages/83/97/31bd8b8279e6935a0719f6910ced15e9d5a2cd554b253f6027ce1b5a1c2c/xxhash-4.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:237b8f63a2a0fcfb1ffc06e21dad23add44e6d354b2b014364a1d41e419a4dee", size = 261812, upload-time = "2026-08-17T08:22:00.469Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c1/d180a2da23c105d8e0b02d54f9f5841013fc81c233010ec781e31f1aee4c/xxhash-4.0.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:81507a68ba84c55241fb61cce1469f473a5da4205fc8ef6f698e5948eea8dd88", size = 339878, upload-time = "2026-08-17T08:35:17.626Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3d/f584cd3172fe934f0f5a0a3917d0d7ce781f74d794fd43bb72be71c3ef6f/xxhash-4.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1ea31d61bcd2cd2f3ec4ca80a64187bbd7948f490b63cf0dcbc6e717b4c1e9", size = 272871, upload-time = "2026-08-17T08:20:56.067Z" }, + { url = "https://files.pythonhosted.org/packages/34/50/2c7956b2b551682e00b9aebce9ceb0a991a131d65f9850c09f5f9760be2e/xxhash-4.0.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:06713a5aaf1d0905c5579416c020c02e42b3ceb931e86c7d3b7fb85403dee3f3", size = 301440, upload-time = "2026-08-17T08:21:35.911Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a2/0739f6482184a8026f4b022718f5f815d352059312e80696825433f0a8e7/xxhash-4.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e8cda075b10bb3917b002c74a04f9e02b7d13b5bf732571404d51c52b11c7329", size = 260157, upload-time = "2026-08-17T08:22:01.416Z" }, + { url = "https://files.pythonhosted.org/packages/a1/25/b31a7bcf1d7d116842812e54f9b944843b4236ea4fa85634e8259f342212/xxhash-4.0.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c10b9206753b64aa791b35b201485477525b26fdec5bf86e8364c388a03e2592", size = 278233, upload-time = "2026-08-17T08:21:15.674Z" }, + { url = "https://files.pythonhosted.org/packages/db/e8/5293bae090fc6119dbc5fcf5c4cc0e1536394b52d73b7904d033836c73db/xxhash-4.0.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:f3e1a44af01b6692de0ec6caba5f0bf93ceb36896e02b7fc00952c6ea7ef39e1", size = 330270, upload-time = "2026-08-17T08:20:51.128Z" }, + { url = "https://files.pythonhosted.org/packages/72/9e/e2ab12d40921f3f34c9317637d65e011aeababf8288356ea8d527de2c1d0/xxhash-4.0.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c6fc415b5568bd9accc7187f1729a99707330c0a67a8b9f93c1149ed573ed75d", size = 478555, upload-time = "2026-08-17T08:22:04.183Z" }, + { url = "https://files.pythonhosted.org/packages/6d/32/c6148d39a49efa95f39b4cf0d41ef35a487f3b30f6fb1fc8fe8d8eab577e/xxhash-4.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96d8de55029d42251945531f6aa7590c32b48163c66a43bf29d8657d7446a377", size = 258174, upload-time = "2026-08-17T08:35:21.18Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fb/0b04b68d6c5bc71c7a2c344f1287327b67e607f28fbcfd937697caca64b6/xxhash-4.0.1-cp312-cp312-pyemscripten_2024_0_wasm32.whl", hash = "sha256:0163b5d259de23ae9e07b7eabf435ce4704f6f205589a2b154e6af4be985ce1b", size = 20767, upload-time = "2026-08-17T08:21:00.806Z" }, + { url = "https://files.pythonhosted.org/packages/a6/be/476092aba34d1fcd313e1613a3bb3bc692f253d167b54bc90049043b5034/xxhash-4.0.1-cp312-cp312-win32.whl", hash = "sha256:1216f7ba5683f17a89eb7dcb4bc50a0b743dfe1902278d7b3d0786f538118433", size = 34669, upload-time = "2026-08-17T08:21:49.486Z" }, + { url = "https://files.pythonhosted.org/packages/aa/02/f9413d94fae43cec6d1a74c4f12156c6f4a7f5fd50e1d34defebdee3dec9/xxhash-4.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5c2d525a3afabcd8e3549d85fc7e111fde6bc302d06a1893fe73adb79823415e", size = 37073, upload-time = "2026-08-17T08:22:04.886Z" }, + { url = "https://files.pythonhosted.org/packages/c1/83/6fe93c1b95acf962bc61a246df09dc2dcce895ccfc1080c9f48d0b652b92/xxhash-4.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:86b2b12bec60c678ed8f5cca0258ad93a8928ebddb6ca7732f0875afe1451d1a", size = 33299, upload-time = "2026-08-17T08:35:12.708Z" }, + { url = "https://files.pythonhosted.org/packages/f3/dd/c707286b527722f776e1fb81dd202c45623355ba1a2972337a2a26075b2b/xxhash-4.0.1-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:8c9fe122444e129881afd1d4d1c7ac0d3ce2d91b68c2b40173b6025ff1c31f9a", size = 43639, upload-time = "2026-08-17T08:20:54.945Z" }, + { url = "https://files.pythonhosted.org/packages/1b/3b/bb71639a0f95635f61936a6f2653599c4261b645ddddd8d00f9dfe3613e2/xxhash-4.0.1-cp313-cp313-android_24_x86_64.whl", hash = "sha256:1f3346c5c287ac3c7f38b20380f55e8768230e7252af59fabcf3b87ab21e4256", size = 40657, upload-time = "2026-08-17T08:22:12.616Z" }, + { url = "https://files.pythonhosted.org/packages/3c/91/76f3f5385faa9886a36f21fcc603f40b4c0c40ce622382f133160c48b4d9/xxhash-4.0.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:4e5141543c7f7fe3087500bbb4ac2845cb528a980aa91f8f1e661e2292ff4a5d", size = 34708, upload-time = "2026-08-17T08:35:24.614Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4a/f48f0e3e1b1ab072979fff2a5be899234e28090883e8b519d0b10215d708/xxhash-4.0.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f09ee747e2a5f876cc5ad56947734811828335e13b403dd8ea1e06d77a9dd48d", size = 35650, upload-time = "2026-08-17T08:21:09.337Z" }, + { url = "https://files.pythonhosted.org/packages/c4/53/b73d7472b196101ad1f57ed0674af3af803ac3e9ec2feadd650a7b262562/xxhash-4.0.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:acf52474b2494ef66dc7e0fb6d5e2b50c18313039ad4d275fbf9f9907c804bc5", size = 37958, upload-time = "2026-08-17T08:22:10.616Z" }, + { url = "https://files.pythonhosted.org/packages/d0/f2/024946ad8fa532074af4e4380179da54b7ec9facc8bd0b279ec0fac4e63a/xxhash-4.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1b3cccf75eeb5b01639b2feadb042a8e07889293b7ca72fa2985e7dcb64763cf", size = 38032, upload-time = "2026-08-17T08:22:09.535Z" }, + { url = "https://files.pythonhosted.org/packages/da/e0/934af8d99bb5885711006bec30a691f728edd513d2c40f053f887d8e7577/xxhash-4.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cd878d32f5c6cbce9783f8d6897561fb772211edba9dde49d85672b88ed45276", size = 35895, upload-time = "2026-08-17T08:35:16.53Z" }, + { url = "https://files.pythonhosted.org/packages/20/5f/a8011f6a1558f7ca66d9077bb4f192b1871afcea62fbd5733605d2015755/xxhash-4.0.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:41e579025a6e13a99e6d71e39c9cfc621a0dcdbbf19106325e145fa858f2d794", size = 259464, upload-time = "2026-08-17T08:21:06.72Z" }, + { url = "https://files.pythonhosted.org/packages/ff/89/9665a44397547e7a3d58c0942425a976d58dcfd4b538f33220a312bf6912/xxhash-4.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74379a577a9f3b6afbdedf1b90e5c7764467051977f18a326d7d607336d743bd", size = 283949, upload-time = "2026-08-17T08:22:17.003Z" }, + { url = "https://files.pythonhosted.org/packages/34/2d/78774141266457468f29f3f5803092df4db87d8148ba74e4debd041649db/xxhash-4.0.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:acb31ecdd1a97fab5cd39a84ee9f515e727d319f796fec48703b8339b9998360", size = 303898, upload-time = "2026-08-17T08:35:27.951Z" }, + { url = "https://files.pythonhosted.org/packages/59/48/d78d22de576b42528bff87c14207de50de4f0b888221a50ff7c9d675d670/xxhash-4.0.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5b7875ac1a2edcb691f27642b8b94b904baa6bcecb7d79c72df2228ba8cb5c51", size = 287241, upload-time = "2026-08-17T08:21:13.042Z" }, + { url = "https://files.pythonhosted.org/packages/4c/de/7a1755a59c59fd46176f293bbdd99e399a6537ba9537fc723aa4d1bf6e27/xxhash-4.0.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4751f1d7eecae6b2d2a773630f1a7248f125c9a92a456694d03c15bceffc9d68", size = 519856, upload-time = "2026-08-17T08:22:15.35Z" }, + { url = "https://files.pythonhosted.org/packages/6f/fb/76580c08e916507859b0f335393cb5fdc59452c4402edbc6bcca6e47e7df/xxhash-4.0.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9a51b061d54cda8b83e62c44458bfbf0dabbef9b975dd9649952ba5076b9f349", size = 268572, upload-time = "2026-08-17T08:22:14.533Z" }, + { url = "https://files.pythonhosted.org/packages/d0/2b/1abde3e07b8f2077a38b4fbfaf764115008bfe0ff03bc7756a52c9fd0607/xxhash-4.0.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:74a164e8b63f1e9cf35c9a7809d082b033d1a00e7375d5d814415436e7867e57", size = 344967, upload-time = "2026-08-17T08:35:23.569Z" }, + { url = "https://files.pythonhosted.org/packages/5c/15/80b6ddf0732eef48a8b5fe717398274794392bd6dbe82af38d189d214772/xxhash-4.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f5e5c6df4b703afcbe9352d238a51efd97c3b91fdc3a2052e40fdacb1e7505f", size = 279956, upload-time = "2026-08-17T08:21:24.97Z" }, + { url = "https://files.pythonhosted.org/packages/77/e0/11cbc43c205bf81fad50d69c7319cd1b1ccc01a66cd4fb8766357126c43d/xxhash-4.0.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d54b8ae068af532c8cdf56abb9e09a60fbe7b10792444c9c27987bb6d3b450fa", size = 307583, upload-time = "2026-08-17T08:22:22.541Z" }, + { url = "https://files.pythonhosted.org/packages/1c/11/cf0bc07feb2791045b6ac075d4bf64f1a5beedef2f46ae70d7104d63a19f/xxhash-4.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1749f0688020209fe0d357ce1e1cd9ec9c6161ed0405ea949d24581c4c43fa91", size = 265848, upload-time = "2026-08-17T08:35:31.298Z" }, + { url = "https://files.pythonhosted.org/packages/d4/c4/7ada4bea2a2795073dfc42d96842930efbe7a0c1857ef4b522e4e90e5d83/xxhash-4.0.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:94ac8a6b8c47951173f0b67bf862bcb971bf24e493b9fbbdb0e010cbbc7d9f54", size = 284409, upload-time = "2026-08-17T08:21:23.156Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f4/d8ce83dd6b99ccfbdadaf2db968ae40334d2e5f73a0297e593b9ddb3df39/xxhash-4.0.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a33de7633c948ab2dc144af370a66e7e7af29b425dcd0f7e4f59689fb9391b53", size = 335921, upload-time = "2026-08-17T08:22:21.802Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9f/f47d8724bd8bc45b395b06b7cacea2dae0d00031af1b707184a091161df6/xxhash-4.0.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:247ece770647c0aef080561fa996f9774b4dadce2d0c42eeb98229db7dcf820d", size = 487023, upload-time = "2026-08-17T08:22:19.729Z" }, + { url = "https://files.pythonhosted.org/packages/57/54/2d87098f3371cc1e42dd04d2285ad56bca4c56667bc501bff02d2b9fd6b5/xxhash-4.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a4553d36cc0b7fce1f35ba8a94dfd775aa3ed12f5eab2dc3b46ac75a0706b0bb", size = 264333, upload-time = "2026-08-17T08:35:27.001Z" }, + { url = "https://files.pythonhosted.org/packages/27/b8/93795ca5898ec7d7d0455283ad261c0fc76b4f0c0a69e86233bd7badb0bd/xxhash-4.0.1-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:87aa309a93bd5ec13f14309a305ff4e9bf74c5363fc46c264c0a22edfd5b0670", size = 20581, upload-time = "2026-08-17T08:21:39.207Z" }, + { url = "https://files.pythonhosted.org/packages/b6/96/926f7335a0a1647952c00421e8da877f658094f61336306c7cadc335c94d/xxhash-4.0.1-cp313-cp313-win32.whl", hash = "sha256:cba763d84b06bda2c38d5185dee76f1b9dfdc0789e96e476d9e10005526d0788", size = 34449, upload-time = "2026-08-17T08:22:29.362Z" }, + { url = "https://files.pythonhosted.org/packages/ea/61/8a5aeb811de093bab3434e77eff0e9461624a1a56a6a93d315d080aab2aa/xxhash-4.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:97b94fb29abf21f5f0bde15f7dbdd3a4aa2dc59f37026adc7b4bee8563b84375", size = 36520, upload-time = "2026-08-17T08:35:34.852Z" }, + { url = "https://files.pythonhosted.org/packages/04/14/97f3c74000ca36955e9cb86f6d270dcd5848b5c65afa623453f5cf2d83d6/xxhash-4.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:08ed8da18cd4fd0a6a5d6a444852d8fbd0e565388a74a4937085451b5f1a312a", size = 33428, upload-time = "2026-08-17T08:21:31.713Z" }, + { url = "https://files.pythonhosted.org/packages/81/0e/ea406a02b561d3275232ccfdb3e29df80f7a65414940e3a15721c7bea40f/xxhash-4.0.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:af05a3f650220a6c59fa0ad2410249f2d2470a05225807c378fb67458693f8df", size = 43747, upload-time = "2026-08-17T08:22:31.37Z" }, + { url = "https://files.pythonhosted.org/packages/f9/f0/b0c94d61ccf6b5d1f8847b58ef8f923125ac4919ed5bd0eb082750ca7cbd/xxhash-4.0.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:a6e3653df1a70b8ac4191216324242e4be2bca18c9a7c10934e1bd56dc7ca15e", size = 40749, upload-time = "2026-08-17T08:22:29.431Z" }, + { url = "https://files.pythonhosted.org/packages/2f/c5/8085881a538983be0fd1c865d5df236242fea496044e2c8ca32b9f2ba39c/xxhash-4.0.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:4528cf80ebbbf57d40edfb31521ae265daa6dd636d615b1cf0ac86209579e59d", size = 34734, upload-time = "2026-08-17T08:35:33.68Z" }, + { url = "https://files.pythonhosted.org/packages/d3/94/8803d13c968fc75ca434eea991d29ac5fd8a36b4afc9a6a9803c53933db4/xxhash-4.0.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:90cb2a1c9cc503a054a19612b48ff6e8e47805f618bdb3224a07568aad03a37e", size = 35671, upload-time = "2026-08-17T08:21:48.322Z" }, + { url = "https://files.pythonhosted.org/packages/85/d5/ad91d7f0fd294190d37c08236fe661f5c4e3f83dcd1a121877a2e64681ce/xxhash-4.0.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a949b072ea59c6eca0811ccd9e95133cc50d2afda8d464b5b077c78f78efa269", size = 38094, upload-time = "2026-08-17T08:22:39.763Z" }, + { url = "https://files.pythonhosted.org/packages/89/f4/2b7ebdc1869caca5f02c4cba8379b631050d3c3d4adb9187e4dc1a6b8d3c/xxhash-4.0.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:79a3203aadf39637869dfea1185227d8452844d78b837e54fb1117b4d34ba5c3", size = 38244, upload-time = "2026-08-17T08:35:38.081Z" }, + { url = "https://files.pythonhosted.org/packages/90/9d/f66cf6935f528e575f1ae4d6560d376e7587569747186f4fae8777cadc1b/xxhash-4.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d9f3848ffaf010bdbabdbf4c25641fa258b6227ff27bc74a4d06edef521a4873", size = 35904, upload-time = "2026-08-17T08:21:37.358Z" }, + { url = "https://files.pythonhosted.org/packages/07/29/34569d7b482f0dc060074faafd163c588f915cbc3e3e218f1ffd8a3ad340/xxhash-4.0.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9283d9dd6b44acad35118e2976fc763a065509e4118debdb61916ec322ed17b9", size = 259595, upload-time = "2026-08-17T08:22:38.153Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d2/a2370acfcd48732cf5c2b87f06cfbf7fa51c0ce0dd736bde42939eb9ebf7/xxhash-4.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c7c642a0f79c3e3cf2965475507574d3d1a50ec71060039d60cb87358667cb2", size = 284279, upload-time = "2026-08-17T08:22:36.396Z" }, + { url = "https://files.pythonhosted.org/packages/08/15/17d33c24e6c4a1c0b9ddc5584f0c25d51d48b34bacde1416a2235a19db4b/xxhash-4.0.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:96dedccfb09a73a25751053a183159b88f4ee75f388df8166040c152ac0531c6", size = 303973, upload-time = "2026-08-17T08:35:39.22Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e0/4ec0d69ad5738729098a61e631b7ed2df22a922b0e03014b597c72bd863d/xxhash-4.0.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81664268dba92e037b740ecf37fa02f1cab4a391f93f28e35792b3341c60648f", size = 287535, upload-time = "2026-08-17T08:21:52.158Z" }, + { url = "https://files.pythonhosted.org/packages/0f/8b/4f9b17e7a9eb71c65548ecddd9c18b84e3c18ca41c4d436ad2a3000d3f7b/xxhash-4.0.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:839f58c5bd9989875be0fd28446dbf32cace2c2cd8bf2f6762acdc38a95cd1aa", size = 519257, upload-time = "2026-08-17T08:22:43.272Z" }, + { url = "https://files.pythonhosted.org/packages/68/35/3276b3e743b8ddbed9c3f71c76d9dd6a75d72aa4e678b1447b635cfd92e0/xxhash-4.0.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffa44b4c7c5d0ffa31356b4428659516c0e47647825c74079a296b3857b6d99d", size = 268190, upload-time = "2026-08-17T08:35:44.985Z" }, + { url = "https://files.pythonhosted.org/packages/08/d4/f1555de3c96721320930dbb7988c8482d82b85970076aba1a8d40e83ad43/xxhash-4.0.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e681a6fc7e4f715252b9b5acfb30536ec7dd1f75033a32dc617e6fa95af1a3fd", size = 345553, upload-time = "2026-08-17T08:21:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/ac/98/c28908f27007087b61139d290f908dd827ffd40b88af0c43f9e1a1a7ffd5/xxhash-4.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c6301d92545c591ad31c3e050aa40a5f8a4c16413f1f9e6f9322c6f0f9d2b736", size = 280499, upload-time = "2026-08-17T08:22:52.236Z" }, + { url = "https://files.pythonhosted.org/packages/a9/76/3ef57622c65816348f8196273485baab4752aae064959901e85cd867e067/xxhash-4.0.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:6efb8f21cc136c79b3e5bb747c8682d37916fb202cdbbc32182de5c4e47f821f", size = 307211, upload-time = "2026-08-17T08:22:40.815Z" }, + { url = "https://files.pythonhosted.org/packages/8a/4c/5804504bbc808968e57d6a50286dd8f8cc06e0ddd6e4ab4b1dc89ae42f35/xxhash-4.0.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:760de77279e9cf9c81d012ce0705cba13afccee9b09c480f17d778c8c5cefae8", size = 265865, upload-time = "2026-08-17T08:35:42.727Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ee/8572fdfd70e7aaaf150af899871c2cc0bb88c3295ca82172a31e04ca5168/xxhash-4.0.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a16a3fa6936e36bb1414d16a6bd012c9033e5161b68b426805b61d895392437d", size = 284545, upload-time = "2026-08-17T08:21:56.965Z" }, + { url = "https://files.pythonhosted.org/packages/c1/f8/6eadcca0904660c848b466524e82a233d16c9d2d5258433aaf3546142d86/xxhash-4.0.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:9c3c4b9aa9a27196b921197f7daf9e6c1412739df06a99cfa6e923879362eff6", size = 336022, upload-time = "2026-08-17T08:22:46.346Z" }, + { url = "https://files.pythonhosted.org/packages/27/df/4aa107b81602d6d6d09ab5a607c530d2d3a6b28e2e9a59b01875bd877c54/xxhash-4.0.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:863f3d3b44110f7243e86cf994aa5c5d88f2348b6e84ab4402fadadfbf9f7da7", size = 486671, upload-time = "2026-08-17T08:35:49.016Z" }, + { url = "https://files.pythonhosted.org/packages/45/b7/b2bf9b5301e9cd5f2e335fea8da0f5cf209a6594cb1fe77754774ad4a6fd/xxhash-4.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63aa52659bc32bb9bd7cb5caf523b4d14429a477762cfac886132d687c1f80fc", size = 263744, upload-time = "2026-08-17T08:21:56.165Z" }, + { url = "https://files.pythonhosted.org/packages/0b/96/35b1c02177ae26234892c2310fb4822ba62411acccbf425ab8f9fd99354a/xxhash-4.0.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:67e57b834e07ed973cee7b6da1548ff28a56458d77696fd2a5f397f340694848", size = 20563, upload-time = "2026-08-17T08:35:11.924Z" }, + { url = "https://files.pythonhosted.org/packages/51/c2/a06300b165fbd6b0cb4a9742987f2e997a9f447ce3bf7c6ac97b862ce62a/xxhash-4.0.1-cp314-cp314-win32.whl", hash = "sha256:b6c1f9c59bbe593f88a0aad30be4150f15bd57bd64efb95feeabcb8e563f1ecd", size = 35151, upload-time = "2026-08-17T08:22:44.283Z" }, + { url = "https://files.pythonhosted.org/packages/06/96/c5b37296b78f80fc97124c0fee0c7bbd1bdb6f3b18bcd8748bb113b2d8fc/xxhash-4.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:da544672efd9ad76077928a3e6c5d894e52ce82d3bf14002db4a1bf17d1a36a2", size = 37156, upload-time = "2026-08-17T08:35:46.551Z" }, + { url = "https://files.pythonhosted.org/packages/ce/5e/248f9cd169c2fb62236bedfba246d213bce728f74901e99047e3f3c55875/xxhash-4.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:d0d24a4f3fb63852cd09af46ae4b7a4d00cc8b8615a046dca543786e728d1056", size = 34379, upload-time = "2026-08-17T08:21:59.446Z" }, + { url = "https://files.pythonhosted.org/packages/58/c8/db1d37c0da0324d0298f6abd931ca1d4736e049d9f2081230a8421da74d2/xxhash-4.0.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:349775ac30372b344d2338b2a168c0a1312a644194da25b8bec476d55761a128", size = 38656, upload-time = "2026-08-17T08:22:49.119Z" }, + { url = "https://files.pythonhosted.org/packages/c5/8e/e18998ec465fb977bc74272e5bf3c2e886c13b014cbef916cd607802c709/xxhash-4.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:43e5f9169e73d0f0db33b5f6b8554bcce69ac278c966daf83d5eb4eb2f13829f", size = 36306, upload-time = "2026-08-17T08:35:52.853Z" }, + { url = "https://files.pythonhosted.org/packages/ef/1a/b83f86f8a987a3cbcb7e005a6824ff64aecae35abc1395a0d44ee16c3319/xxhash-4.0.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4a252fb862b0ae2590587e625f47a0e03da05cf0205e8830b67b6596c06038b1", size = 273729, upload-time = "2026-08-17T08:21:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/02/4e/2db15aa8508e0cd5b632927a53b98234f24039ea65377e6cf996c06d2d4f/xxhash-4.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2df3ca8757dc381e75e90a4d7995a6324f58a923c7145220a7b2c0231f66fddc", size = 301083, upload-time = "2026-08-17T08:35:14.113Z" }, + { url = "https://files.pythonhosted.org/packages/26/94/ed759787ffe802bd8e31cfcdad3755cbeca2dcdafd2f790cd6f25d195199/xxhash-4.0.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bfed61996d618eb90d6eaae0178002e3466a28b06bfc557a7a3a7266378d8c5a", size = 312745, upload-time = "2026-08-17T08:22:52.232Z" }, + { url = "https://files.pythonhosted.org/packages/45/7a/f64b4a4cc8b51e950709207f55f7f56ae9c5af6631dd31d7fb443312418c/xxhash-4.0.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9761ff4a0ffa583fe850731ad24fe82c88cccb7a2294727db0955f3279a4cb3f", size = 301419, upload-time = "2026-08-17T08:35:50.143Z" }, + { url = "https://files.pythonhosted.org/packages/a0/71/bac313b8de073569b8db3152044a7cfcce87a3fa9698c18fe9f914dee6b1/xxhash-4.0.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edccc2ec58435a580f96a48a3ccae8cd0a480824119165dd90108718ad81ae6e", size = 534485, upload-time = "2026-08-17T08:22:11.515Z" }, + { url = "https://files.pythonhosted.org/packages/b9/0c/16b5e419f24e59507ee05626d2bb0deafdb03f9f27783bc0785a9849602e/xxhash-4.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4741d42d59e4e5fa1a86c17ab9c27dc8ea459c700d91b6742fdb9138d9a516cb", size = 279605, upload-time = "2026-08-17T08:22:52.934Z" }, + { url = "https://files.pythonhosted.org/packages/5f/55/5787dd6e2d8d5b61256a5039f6b18c2193c7c1de4a2fd2413288d0d9c604/xxhash-4.0.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:440c401e146ce64bdb3beb8ff0c84677b6f21307c28a34779071cecee5d4d70c", size = 358924, upload-time = "2026-08-17T08:35:58.164Z" }, + { url = "https://files.pythonhosted.org/packages/f3/68/89be41991f3b0a2e91f940bdf3128852c3ed571cf560d98ad0f67024afe4/xxhash-4.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5b7979f71d06ae45a769de0699900a246d8cb632db1e8bfdc79ec019063a503c", size = 295305, upload-time = "2026-08-17T08:22:13.683Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5a/52ff0a0cc361aad393ff9a46ffe3aabbcf9c03d6c8f2612da7d553048276/xxhash-4.0.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:62198213fc3e0c56e567894b318ba45834e007d065f84ba6dc9165d21546fc56", size = 320228, upload-time = "2026-08-17T08:35:18.946Z" }, + { url = "https://files.pythonhosted.org/packages/0f/b5/91c60ff22c7f6cd5f6d7a5bad5a2cdcb4c33987dfa50bf13f0d856279b2e/xxhash-4.0.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b3bece52127ac20044311ee73567f9f0893b5de64f9028aecc90cc740cfd525a", size = 279414, upload-time = "2026-08-17T08:23:03.212Z" }, + { url = "https://files.pythonhosted.org/packages/b9/94/9685954804d47d0390871a64bec606a0d536406382d71a784df3a5883fb4/xxhash-4.0.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a865d2d470220e659220fdb59d5b6c4422802d8d6098e1324bc4d12444798914", size = 297594, upload-time = "2026-08-17T08:35:57.881Z" }, + { url = "https://files.pythonhosted.org/packages/89/62/b67ac9412907b7a07a2a0c08c3440b9e4480231a7b3de0767e87011e4564/xxhash-4.0.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8580aab306888224074c7edeec734de0c3c5ccde65b2da4e6c9a5e28f7c0a1bd", size = 348526, upload-time = "2026-08-17T08:22:18.571Z" }, + { url = "https://files.pythonhosted.org/packages/37/ed/6723cc49a9f567d52d01fd7c1741b0f2e3a13e71d15f7ac49d753a20c115/xxhash-4.0.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:2d52dc7c33c1b83082b707f6b7814dc76d2faaa2ea62bd9c5fab4b36f83c087f", size = 499307, upload-time = "2026-08-17T08:22:56.52Z" }, + { url = "https://files.pythonhosted.org/packages/fd/2e/7b10e101ab988d93b791023be7191d7661271d6ab31ac082276b9091042a/xxhash-4.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6a9f98af872355e0c02439e48583958eee00e60b928bb20476460d9d40cb7b4e", size = 274989, upload-time = "2026-08-17T08:36:01.834Z" }, + { url = "https://files.pythonhosted.org/packages/9b/8d/7eabcc8d29cce40621443cff24c07d7306ef574b8956c47ac59f21098005/xxhash-4.0.1-cp314-cp314t-win32.whl", hash = "sha256:a14578102a6081465aec9cf73c76c3cd3f79f0709bdb3b8ae7ab0b54c9d8b089", size = 35482, upload-time = "2026-08-17T08:22:32.336Z" }, + { url = "https://files.pythonhosted.org/packages/ca/89/2a4268e1971f63038b79fb75e3b9c8de942cd77acabbb0c5625352a31940/xxhash-4.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c57963970d359a72262f7fe6be88f945e2334d4bc41462b7f08c37b0abf35ca6", size = 37490, upload-time = "2026-08-17T08:35:22.475Z" }, + { url = "https://files.pythonhosted.org/packages/90/7b/950ecab1fe4cf421d0a6211ddd9a0ac82e39e55c45a111ceb90953dc6c9a/xxhash-4.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:b659fad79c99b0238c7ad7e9d7dbf4eebfea9097c2dba65fa0a4d18a25b29a2f", size = 34596, upload-time = "2026-08-17T08:23:10.001Z" }, + { url = "https://files.pythonhosted.org/packages/c4/03/7dc3b85fac10751613bfedb0e120734e0e8710054abad3f931e9d3843a14/xxhash-4.0.1-cp315-cp315-android_24_arm64_v8a.whl", hash = "sha256:5adf927dca8c47fde7e683fe69efdd81bc865c4db1fb6bb00b391e2b6185207b", size = 43749, upload-time = "2026-08-17T08:36:00.47Z" }, + { url = "https://files.pythonhosted.org/packages/a5/55/bfac071c5b1c6d6a3d48ab1ab96a15e958a1d7061f4afc97804292d87264/xxhash-4.0.1-cp315-cp315-android_24_x86_64.whl", hash = "sha256:c30dd1af66a820820398b26e0d74e7a9aa43cae705924f23ed828cd8e5c26c3d", size = 40758, upload-time = "2026-08-17T08:22:30.209Z" }, + { url = "https://files.pythonhosted.org/packages/79/87/49a260e685d1a74c56a69432a8ee0527ddcbd684a3c51f87edc3b75639c5/xxhash-4.0.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1bc591533fc975614f7e13594daee76af96b8e1fbcf8de76c8773858fa9e7cea", size = 34788, upload-time = "2026-08-17T08:23:09.014Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ef/50d72ed2170dae872e1c0fe333d0908e0a2afbffe74c5c9037d5406a4b89/xxhash-4.0.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:567cbc630302a46a8ecfd943b309ccf5372bb3718f1f3762d452df30f033bcf0", size = 35746, upload-time = "2026-08-17T08:36:05.557Z" }, + { url = "https://files.pythonhosted.org/packages/66/f0/969deaa2bab3bfd5ad5b023442124d2255b9961eef6f797ec74eb8683bdf/xxhash-4.0.1-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e998cb3685b92101ec5de0fb4d9485cf01e50bc418211955c55d98064664cf4c", size = 38098, upload-time = "2026-08-17T08:22:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/86/aa/45ed7d7b8d7b66202a47bf8ff3b77cea28d2ea54dfcdd202b4cfe043e3dc/xxhash-4.0.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:c3074db513c81f764053e3da079312ecf85a50d8350c71f4cc0105d9662a9e6c", size = 38251, upload-time = "2026-08-17T08:35:25.774Z" }, + { url = "https://files.pythonhosted.org/packages/f1/9d/45e7520a7856e13800a5dc8cd038d34c6372429465b163af0c5722f16918/xxhash-4.0.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:3088dadbffa33c29e0518578430a7dff2e901a212e487aefa5faaa0dc06dad34", size = 35986, upload-time = "2026-08-17T08:23:25.854Z" }, + { url = "https://files.pythonhosted.org/packages/9e/0e/5ad466e5fea18c9f9bdc5828c0506f62190061b4a1b0e688aa54969d0a9e/xxhash-4.0.1-cp315-cp315-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1b50223d92df94d54e1a31469335a2c74b16692e6c1cb726f1e6949514458706", size = 264609, upload-time = "2026-08-17T08:36:04.229Z" }, + { url = "https://files.pythonhosted.org/packages/aa/cf/8f269f85217e3dbd45e31e25e46cc26f3aff0e159ef05d228b4b982c778c/xxhash-4.0.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:427b62d62d4f967fbb10b82a3813e4875c2a6e7e7634739f17265b650c7f65a6", size = 285910, upload-time = "2026-08-17T08:22:38.589Z" }, + { url = "https://files.pythonhosted.org/packages/ca/30/2fc1a16ee0f9501d074b798ebfae52e24fa602c7117f5c4b81de71eada72/xxhash-4.0.1-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c6370189e8e66b7e608f533b939a9de092ddca6cce084ca0d3d414d2ed5b5d59", size = 306566, upload-time = "2026-08-17T08:23:16.895Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a7/08375cf2b997e1903663fe7525c5973b1987a4f8ad2b8d47463e9143f2ee/xxhash-4.0.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ec1a470c6db94ac4589c203921e89ac1bc13e796a8b1784d8135e1893559cd3b", size = 287978, upload-time = "2026-08-17T08:36:09.296Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e5/90a7b404c11add9e53a497d06236152852490c3b2f21e468d97a58f26afe/xxhash-4.0.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37f667dee0f867c42894b34e2a6fe26bf195c0ea4683d9d2b713db023f242c3a", size = 520098, upload-time = "2026-08-17T08:22:41.565Z" }, + { url = "https://files.pythonhosted.org/packages/11/02/7fba10b1b17eb46308f09cc0a4ed513d74dff16b1e22a1c439f011c77129/xxhash-4.0.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f18732adcc271741bd651c3e56fa519d8a237d2cccda01fe3afb226bf87f783b", size = 268273, upload-time = "2026-08-17T08:35:29.043Z" }, + { url = "https://files.pythonhosted.org/packages/54/49/c21b228877357a3be43eeeaa22182ad1685796f415390ada475922c084e4/xxhash-4.0.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b42a5a26607e4b2409fea174773a66f2dff9dfdbf2c1a851bb7b804e2c97535", size = 350164, upload-time = "2026-08-17T08:23:29.494Z" }, + { url = "https://files.pythonhosted.org/packages/00/3c/c15bb4aa33d94b78a5553b52e7fa1070565f0199925aeadec3871de20ce9/xxhash-4.0.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:99166cc98637e8bf550cda2aab07f4f1d5f899c45fbd721801aeabcc9d404824", size = 281975, upload-time = "2026-08-17T08:36:08.139Z" }, + { url = "https://files.pythonhosted.org/packages/18/7a/b1d0388315fe7752b7725b68a912667526a1dd48ed492fcc031ac03f4b52/xxhash-4.0.1-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:6cf633df84d80a1668fcf61e330791dae46825e395549e7d34f376411e75088a", size = 307872, upload-time = "2026-08-17T08:22:42.206Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a1/037cb2dd8cf725c9565dfe3712b2915c0e0276a9154913dbfcbcecbeb672/xxhash-4.0.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:e259bb7e1e2d8de6b35f430f5c7220b1c0ebf3962d1ba7ec7545980d5931edb8", size = 268241, upload-time = "2026-08-17T08:23:23.997Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a9/67c44422d0ee082169b238ce24bd2796b82d7c21ed953471365df8c508d8/xxhash-4.0.1-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:704381264b36a18b9c62ecbabe2e71d0fc58c77c129c15355c989b10bf05b6b0", size = 284970, upload-time = "2026-08-17T08:36:13.476Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d0/254a5f51c4014cacc77a26f321372338b924f54e89efb730164ee336d850/xxhash-4.0.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:e90b4bcf1d9eb1010fdaee7c9209fb667e74c0684f3ba17f9032bd7319da90c9", size = 338074, upload-time = "2026-08-17T08:22:51.166Z" }, + { url = "https://files.pythonhosted.org/packages/64/03/f21c4830118d72ef3a958ce8bf2152f49e0d4cf200907616c9be6caf372a/xxhash-4.0.1-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:a65785e653573fcd1e33062760ab4c3c3440e8e910765018e4b6ed4ad07b54a0", size = 487626, upload-time = "2026-08-17T08:35:32.768Z" }, + { url = "https://files.pythonhosted.org/packages/45/1f/268a689d741d7da649317eb4ce41760140beb4179aaf43a7216fdbe8100c/xxhash-4.0.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e3996ff9b6f99180357024336bf5749a8ad6476a9a2523e535c5212b995b12a2", size = 263852, upload-time = "2026-08-17T08:23:41.871Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f5/adaf8101cd7f143191a0b390600294d83924b32cb13770fde8803dce27a2/xxhash-4.0.1-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:99054b838b74d8d3995ea0d410976ae967c46207ae22d6ddfc535e809197dab9", size = 20569, upload-time = "2026-08-17T08:36:11.952Z" }, + { url = "https://files.pythonhosted.org/packages/ee/2c/56a5eb8c993420fc07114c08f447a2b66ee996510b4764cb368b9b44c9f0/xxhash-4.0.1-cp315-cp315-win32.whl", hash = "sha256:6c45258a37fc22721395c09927cb982d3e7a83607cab15be7e2416501bd3a330", size = 35145, upload-time = "2026-08-17T08:22:50.038Z" }, + { url = "https://files.pythonhosted.org/packages/67/c7/65f210db43e62157d0fef3b4d4d7b394821e7733c8bb4ece49f91410a725/xxhash-4.0.1-cp315-cp315-win_amd64.whl", hash = "sha256:0ab851b45c70d4992be7cdeeee16f97a0b677408c758c4b1efb1cfe8030bfd37", size = 37161, upload-time = "2026-08-17T08:23:32.438Z" }, + { url = "https://files.pythonhosted.org/packages/33/ae/1a641d1d60ba219756d9ebe907ff0ecf4445adcf4fa96f6e3da57b91d439/xxhash-4.0.1-cp315-cp315-win_arm64.whl", hash = "sha256:a5b21b42a01a343096a1c018d35e9b7aec9c7065dda53ae8da071e37478b2cea", size = 34378, upload-time = "2026-08-17T08:36:15.912Z" }, + { url = "https://files.pythonhosted.org/packages/48/7f/7698b320b251806d1249e513922a626f19027e104c829a611272250350eb/xxhash-4.0.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:44ab12e8cd17d4f001769f00ad465208b4bcb897ed29e65f058f74466b57a98f", size = 38610, upload-time = "2026-08-17T08:22:55.203Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3d/436497e775b647b3b3e9a4ffe8c76c59fa4aa7a9fab6447cb59acf1b50ea/xxhash-4.0.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:45e88111ebe331de478ef8d4293efbe88f3cf8b863386c9a2357136b838e1af0", size = 36378, upload-time = "2026-08-17T08:35:36.18Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d8/17a4f8182b9257898aa2a77c2a45f70233eb8e50681a280e8e09d2ee76e9/xxhash-4.0.1-cp315-cp315t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bf430c587f447a554c53768ad76b9846fe7c5632180ef6f69c4fce8b0552fbd0", size = 273559, upload-time = "2026-08-17T08:23:51.075Z" }, + { url = "https://files.pythonhosted.org/packages/83/28/121bd5a5c5adb88e0da772c7bef61964cf9da92956a7a237c7d24c4351b8/xxhash-4.0.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adbd48b30e3f82c89fb2b3e6a87cdd28d113b190a5ed0ee2dee286323ee9a621", size = 299133, upload-time = "2026-08-17T08:36:14.731Z" }, + { url = "https://files.pythonhosted.org/packages/11/8f/57c7b6e04642ed738a0d08a31bed7fc63fdacb661d665f98739cc9751b62/xxhash-4.0.1-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e71b34978e77868cbf2d18c5206a4603f9c644dd7181bec5643bd40141d3b8c5", size = 314527, upload-time = "2026-08-17T08:22:54.224Z" }, + { url = "https://files.pythonhosted.org/packages/8e/18/42793917dbab0ea1ff71458aea4875e17a7263f2797b798af048dc81e867/xxhash-4.0.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:488ca5c5e28ef56ec4bbb12f835b3f1cbecc5f3510062e70117bc6594851932a", size = 299199, upload-time = "2026-08-17T08:23:36.864Z" }, + { url = "https://files.pythonhosted.org/packages/37/60/51dc92443923d8e908d5614f1145d8d696450f9d6c8f1abe243c6f2a0222/xxhash-4.0.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:421b94f3ba7067958d02e38960d987756347aa150df06df11aa68ae1af78c619", size = 531967, upload-time = "2026-08-17T08:36:18.66Z" }, + { url = "https://files.pythonhosted.org/packages/88/c5/d0de77de09661fac71742c4155b1cd65e274f7cc277819d702b6c8ff2db5/xxhash-4.0.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f33cf0baa91eccd2cb7b62bf00f10c2264ef578b71dd33a12962e71a36eb4d32", size = 278764, upload-time = "2026-08-17T08:22:58.15Z" }, + { url = "https://files.pythonhosted.org/packages/08/9a/589929c655aba1bfb2c41ee03e50eec1547c39c3042a66bda9c173a9614b/xxhash-4.0.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:23a4376b4a3183cb50d4d2a3179f887a7773cc695eb2c908e551bec3221b8c60", size = 359876, upload-time = "2026-08-17T08:35:40.35Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a8/c1d8c94d54d91db2215565f4b4151c1593af3e6d27ac4c00fd1e8d714a02/xxhash-4.0.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:38c3d22129a6958846a3098d68bc8e661704461c0be4793ae28836e4690c8478", size = 293548, upload-time = "2026-08-17T08:23:54.951Z" }, + { url = "https://files.pythonhosted.org/packages/8b/67/85d8abca94508a4dd10561d9dea3e6e68843c6986dd6d9c1b3729c8622e4/xxhash-4.0.1-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:87cbdec1a7dd930079671a60b249f3ca4e773e6fbd0676e21e36fdc9dd0f3b00", size = 318780, upload-time = "2026-08-17T08:36:17.623Z" }, + { url = "https://files.pythonhosted.org/packages/1b/16/2b920ed456b9cdcfc99ddc20c3afe42f9f807ee5850773c12fd891f3c08d/xxhash-4.0.1-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:6cbf4e21ef0890804b5bb9ad25c48f9c127758d7f6c66bef374efcacc63c738a", size = 277582, upload-time = "2026-08-17T08:22:57.156Z" }, + { url = "https://files.pythonhosted.org/packages/fa/cc/5811b5997aebb8452047f5800d32fc50eaa29d0ba08d4e426f84450b9c2f/xxhash-4.0.1-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:c101180495cb4ba3617b279a944345c53a5e73b0c150053d1fa8d8af32de9579", size = 295116, upload-time = "2026-08-17T08:23:40.868Z" }, + { url = "https://files.pythonhosted.org/packages/2d/dc/c2f3f9c2f4d6aadb79f17a9f1c9a7ee82638cc873680da044cf29537d2ee/xxhash-4.0.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:c0e6ccc2b19ec8a726b2e26062ac71ea63e15500d6bf85910e42481844fdffc1", size = 347065, upload-time = "2026-08-17T08:36:21.618Z" }, + { url = "https://files.pythonhosted.org/packages/a2/4c/750cc642c92252e10772ec09e1a1d995581ba4c3ceb24f6e2d57c7ce47ca/xxhash-4.0.1-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:8bcba9456242ebf180a04d9443812fd85ffe6bd12bda464dd116fcece8886ff3", size = 497208, upload-time = "2026-08-17T08:23:17.88Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2d/58693cb13d6395f39b6b9bb40c5e0db53a5df7c9fce805aa7e792f64a1a5/xxhash-4.0.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:83b8c2013edb5dc1f9e7268b6496130705bc48d79c86bb8817b3d210b81a5513", size = 274338, upload-time = "2026-08-17T08:35:44.062Z" }, + { url = "https://files.pythonhosted.org/packages/4a/08/9aa9787586d9b3e92d63343ce7dc24f0f445fd9e74ff5d6e85dd82233df5/xxhash-4.0.1-cp315-cp315t-win32.whl", hash = "sha256:aa6ccc7f31018484d652cf52db020003433f3c9fa83189c028bd807d2adde503", size = 35471, upload-time = "2026-08-17T08:24:05.795Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ab/4615789c333bee331ac417885c50105715eeb8244bfc68d2bc37dcfd63ca/xxhash-4.0.1-cp315-cp315t-win_amd64.whl", hash = "sha256:daade8936c4deaaf7b01561324ce438ba4f885d717e9adc62b4d67212ad7d7bd", size = 37488, upload-time = "2026-08-17T08:36:19.929Z" }, + { url = "https://files.pythonhosted.org/packages/fb/81/49f718beb0c55d0411bc4bd90b50a3fbe5863a0e97a2f4d11682ba13d298/xxhash-4.0.1-cp315-cp315t-win_arm64.whl", hash = "sha256:f00330ac7e24769e2032203f2b01794d670916b0c1799fd261340f1af9499875", size = 34590, upload-time = "2026-08-17T08:23:19.597Z" }, + { url = "https://files.pythonhosted.org/packages/86/79/9127ff42a887a348dc4ce3211cf1a962836887adee6f57078132bfba78b4/xxhash-4.0.1-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:ff48915bf1871a1f19f74c11834c6329443d306cedc0c05fe7fe617810422a80", size = 31836, upload-time = "2026-08-17T08:36:28.261Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e6/f238693bfdd642adb59c99683964d46d9947fe721ff44d3bd850ae675407/xxhash-4.0.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a76345f5aceb4ec404918edf9c7f2b5507db864dc0d7455982009ac0890b57b", size = 34453, upload-time = "2026-08-17T08:23:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/40/4b/796ace33cdfb75c91ba6d11615c3bd436355b9f3103e05865bbee9abce57/xxhash-4.0.1-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:31d86f9e81f3e84e00131ac7c54caf5119ae4ddd82c09c31cff597c813ce1ee2", size = 38488, upload-time = "2026-08-17T08:23:59.901Z" }, + { url = "https://files.pythonhosted.org/packages/ad/23/2d549e5d5d7759eaf9ac2d2d2ab81ff60f1bb2b52cdaae8e5ec5c6524354/xxhash-4.0.1-graalpy312-graalpy250_312_native-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deca2a30d983d240b8375ec2ee0a4288e72042827fc61df2f7671f8467e4cb2f", size = 38206, upload-time = "2026-08-17T08:36:32.193Z" }, + { url = "https://files.pythonhosted.org/packages/79/98/1ee576b27f78e6107ee4ea8ac03e8a52888dff256e57d560f8282c195563/xxhash-4.0.1-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:7c343ee174d417a44d0c3355602c0cbbfa52a04d1bbbf1723378c7d2c8f60626", size = 37127, upload-time = "2026-08-17T08:23:42.705Z" }, +] + +[[package]] +name = "yarl" +version = "1.24.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/31/33/ebe9e3d1f86c7a0b51094c0a146392045ca1631d2664889539dec8088a33/yarl-1.24.5.tar.gz", hash = "sha256:e81b83143bee16329c23db3c1b2d82b29892fcbcb849186d2f6e98a5abe9a57f", size = 228679, upload-time = "2026-07-20T02:07:45.435Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/84/71d051c850b5af41d168c679d9eb67eb7c55283ac4ee131673edf134bc4e/yarl-1.24.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d693396e5aea78db03decd60aec9ece16c9b40ba00a587f089615ff4e718a81d", size = 136035, upload-time = "2026-07-20T02:05:25.489Z" }, + { url = "https://files.pythonhosted.org/packages/03/4d/8ad27f9a1b7e69313cca5d695b925b48efe51208d3490e0844bae97cabc0/yarl-1.24.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3363fcc96e665878946ad7a106b9a13eac0541766a690ef287c0232ac768b6ec", size = 97642, upload-time = "2026-07-20T02:05:27.429Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b4/05b4131c407006cd1e410e9c6539f16a0945724677e5364447313c15ea3e/yarl-1.24.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9d399bdcfb4a0f659b9b3788bbc89babe63d9a6a65aacdf4d4e7065ff2e6316c", size = 97323, upload-time = "2026-07-20T02:05:29.441Z" }, + { url = "https://files.pythonhosted.org/packages/20/16/e618c875c73e0e39611f20a581b3d5e8d59b8857bf001bee3263044c6deb/yarl-1.24.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90333fd89b43c0d08ac85f3f1447593fc2c66de18c3d6378d7125ea118dc7a54", size = 107741, upload-time = "2026-07-20T02:05:31.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c4defeaf3ed33fcb346aacf9c6e971a8d4e2bde04a0310e79abb208e7965/yarl-1.24.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:665b0a2c463cc9423dd647e0bfd9f4ccc9b50f768c55304d5e9f80b177c1de12", size = 103570, upload-time = "2026-07-20T02:05:33.303Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e7/0e0e0de5865ebd5914537ef486f36c727a59865c3ac0cf5ff1b32aececbf/yarl-1.24.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e006d3a974c4ee19512e5f058abedb6eef36a5e553c14812bdeba1758d812e6d", size = 115815, upload-time = "2026-07-20T02:05:35.292Z" }, + { url = "https://files.pythonhosted.org/packages/2b/27/ca56b700cb170aba25a3893b75355b213935657dc5714d2383354a270e62/yarl-1.24.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e7d42c531243450ef0d4d9c172e7ed6ef052640f195629065041b5add4e058d1", size = 116025, upload-time = "2026-07-20T02:05:37.503Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d0/d56c859b8222116f5d68459199f48359e0bf121b6f65a69bf329b3602ba0/yarl-1.24.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f08c7513ecef5aad65687bfdf6bc601ae9fccd04a42904501f8f7141abad9eb9", size = 109835, upload-time = "2026-07-20T02:05:39.506Z" }, + { url = "https://files.pythonhosted.org/packages/70/a2/3a35557e4d1a79425040eba202ccaf08bdc8717680fc77e2498a1ad2e0a5/yarl-1.24.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c95b17fe34ed802f17e205112e6e10db92275c34fee290aa9bdc55a9c724027", size = 108884, upload-time = "2026-07-20T02:05:41.584Z" }, + { url = "https://files.pythonhosted.org/packages/e4/35/ef4c26356b7913c68983bac2d72a4212b3347af551cb8d250b99b5ed7b7f/yarl-1.24.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:56b149b22de33b23b0c6077ab9518c6dcb538ad462e1830e68d06591ccf6e38b", size = 107308, upload-time = "2026-07-20T02:05:43.697Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/ff0dc66c2ccf3e0153ab97ff61eabab4400e6a5264af427ab30cd69f1857/yarl-1.24.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a8fe66b8f300da93798025a785a5b90b42f3810dc2b72283ff84a41aaaebc293", size = 103646, upload-time = "2026-07-20T02:05:45.895Z" }, + { url = "https://files.pythonhosted.org/packages/74/f0/33b9271c7f881766359d58266fa0811d2e5210ed860e28da7dc6d7786344/yarl-1.24.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:377fe3732edbaf78ee74efdf2c9f49f6e99f20e7f9d2649fda3eb4badd77d76e", size = 115305, upload-time = "2026-07-20T02:05:47.832Z" }, + { url = "https://files.pythonhosted.org/packages/ef/65/fd79fb1868c4a80db8661091de525bf430f63c3bea1b20e8b6a84fc7d359/yarl-1.24.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e8ffa78582120024f476a611d7befc123cee59e47e8309d470cf667d806e613b", size = 108404, upload-time = "2026-07-20T02:05:49.604Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ba/dbabe6b262f17a816c70cfc09558dbf03ece3ec76684d02f911a3d3a189c/yarl-1.24.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:daba5e594f06114e37db186efd2dd916609071e59daca901a0a2e71f02b142ce", size = 115940, upload-time = "2026-07-20T02:05:51.741Z" }, + { url = "https://files.pythonhosted.org/packages/a5/43/fab2d1dad9d340a268cdde63756a123d069723efff6a372d123fa74a9517/yarl-1.24.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:65be18ec59496c13908f02a2472751d9ef840b4f3fb5726f129306bf6a2a7bba", size = 110006, upload-time = "2026-07-20T02:05:53.554Z" }, + { url = "https://files.pythonhosted.org/packages/c4/27/41eb51bbd1b8d89546b83897cfb0164f1e109304fd408dbb151b639eec0f/yarl-1.24.5-cp312-cp312-win_amd64.whl", hash = "sha256:a929d878fec099030c292803b31e5d5540a7b6a31e6a3cc76cb4685fc2a2f51b", size = 97618, upload-time = "2026-07-20T02:05:55.57Z" }, + { url = "https://files.pythonhosted.org/packages/3c/25/b2553764b3d65db711d8f45416351ec4f420847558eb669edcbcaadf5780/yarl-1.24.5-cp312-cp312-win_arm64.whl", hash = "sha256:7ce27823052e2013b597e0c738b13e7e36b8ccb9400df8959417b052ab0fd92c", size = 93018, upload-time = "2026-07-20T02:05:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/e1/63/64ef361967cc983573149dc1515d531db5da8a4c92d22bb833d59e01b313/yarl-1.24.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:79af890482fc94648e8cde4c68620378f7fef60932710fa17a66abc039244da2", size = 135075, upload-time = "2026-07-20T02:05:59.671Z" }, + { url = "https://files.pythonhosted.org/packages/bb/89/55920fd853ce43e608adbc3962456f0d649d6bb15250dc2988321da0fe1c/yarl-1.24.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:46c2f213e23a04b93a392942d782eb9e413e6ef6bf7c8c53884e599a5c174dcb", size = 97225, upload-time = "2026-07-20T02:06:01.769Z" }, + { url = "https://files.pythonhosted.org/packages/15/f0/7688d3f2cfff7590df2af38ec46d969f4281a4dddb08a9ad2eafbcdddf98/yarl-1.24.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92ab3e11448f2ff7bf53c5a26eff0edc086898ec8b21fb154b85839ce1d88075", size = 96751, upload-time = "2026-07-20T02:06:03.676Z" }, + { url = "https://files.pythonhosted.org/packages/05/1a/a851a0f94aaaf379dd4f901bfc80f634280bec51eb260b47363e2a4cd62e/yarl-1.24.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ebb0ec7f17803063d5aeb982f3b1bd2b2f4e4fae6751226cbd6ba1fcfe9e63ff", size = 107960, upload-time = "2026-07-20T02:06:05.699Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a8/faea066c12f9c77ca0de90641f1655f9dd7b412477bf28c76d692f3aecff/yarl-1.24.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:82632daed195dcc8ea664e8556dc9bdbd671960fb3776bd92806ce05792c2448", size = 103500, upload-time = "2026-07-20T02:06:07.556Z" }, + { url = "https://files.pythonhosted.org/packages/fb/9c/1e67084c2a6e2f2db0e3be798328cb3be42c0119b621d25461479a224d21/yarl-1.24.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:53e549287ef628fecba270045c9701b0c564563a9b0577d24a4ec75b8ab8040f", size = 115780, upload-time = "2026-07-20T02:06:09.599Z" }, + { url = "https://files.pythonhosted.org/packages/58/86/1f94664e147474337e3359f52012cf3d02f825f694317b178bfba1078c62/yarl-1.24.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fcd3b77e2f17bbe4ca56ec7bcb07992647d19d0b9c05d84886dcd6f9eb810afd", size = 115308, upload-time = "2026-07-20T02:06:11.352Z" }, + { url = "https://files.pythonhosted.org/packages/0a/43/8e55ae7538ba5f28ccb3c845c6dd4549cf7016d5992e5326512519107cdd/yarl-1.24.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d46b86567dd4e248c6c159fcbcdcce01e0a5c8a7cd2334a0fff759d0fa075b16", size = 110574, upload-time = "2026-07-20T02:06:13.129Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ba/a889ec8765cedcf2ac44dcb02d6a21e4861399b243b263c5f2dde27ee740/yarl-1.24.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7f72c74aa99359e27a2ee8d6613fefa28b5f76a983c083074dfc2aaa4ab46213", size = 109914, upload-time = "2026-07-20T02:06:15.243Z" }, + { url = "https://files.pythonhosted.org/packages/9c/c3/e45f821af67b791c2dbbe4a9f4137a1d33f8d386654a05a0c3f47bdfa25d/yarl-1.24.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3f45789ce415a7ec0820dc4f82925f9b5f7732070be1dec1f5f23ec381435a24", size = 107712, upload-time = "2026-07-20T02:06:17.443Z" }, + { url = "https://files.pythonhosted.org/packages/02/00/2ab0f42c9857fcb490bfaa6647b14540b53d241ab209f23220b958cc5832/yarl-1.24.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6e73e7fe93f17a7b191f52ec9da9dd8c06a8fe735a1ecbd13b97d1c723bff385", size = 104251, upload-time = "2026-07-20T02:06:19.259Z" }, + { url = "https://files.pythonhosted.org/packages/7a/70/709d9a286e98af2c7fd8e4e6cada658b5c0e30d87dd7e2a63c2fb5767217/yarl-1.24.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4a36f9becdd4c5c52a20c3e9484128b070b1dcfc8944c006f3a528295a359a9c", size = 115319, upload-time = "2026-07-20T02:06:21.207Z" }, + { url = "https://files.pythonhosted.org/packages/5c/6c/3eaa515142991fe84cfc483ff986492211f1978f90161ccefdbec919d09b/yarl-1.24.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:7bcbe0fcf850eae67b6b01749815a4f7161c560a844c769ad7b48fcd99f791c4", size = 109163, upload-time = "2026-07-20T02:06:23.006Z" }, + { url = "https://files.pythonhosted.org/packages/bb/64/711dafce66c323a3144d470547a71c5384c57623308ac8bb5e4b903ac148/yarl-1.24.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:24e861e9630e0daddcb9191fb187f60f034e17a4426f8101279f0c475cd74144", size = 115435, upload-time = "2026-07-20T02:06:24.923Z" }, + { url = "https://files.pythonhosted.org/packages/cf/f3/9b9d0e6d84bea851eb1ba99e4bdc755b86fd813e49ec86dfe42f26befdef/yarl-1.24.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9335a099ad87287c37fe5d1a982ff392fa5efe5d14b40a730b1ec1d6a41382b4", size = 110691, upload-time = "2026-07-20T02:06:26.973Z" }, + { url = "https://files.pythonhosted.org/packages/86/e4/62a06b7e87c4246ac76b7c2da136f972eb4a3a1fc94abb07e7022d6fdb0a/yarl-1.24.5-cp313-cp313-win_amd64.whl", hash = "sha256:2dbe06fc16bc91502bca713704022182e5729861ae00277c3a23354b40929740", size = 97454, upload-time = "2026-07-20T02:06:29.163Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c9/5fc8025b318ab10db413b61056bd0d95c557a70e8df4210c7511f866329c/yarl-1.24.5-cp313-cp313-win_arm64.whl", hash = "sha256:6b8536851f9f65e7f00c7a1d49ba7f2be0ffe2c11555367fc9f50d9f842410a1", size = 92813, upload-time = "2026-07-20T02:06:31.113Z" }, + { url = "https://files.pythonhosted.org/packages/a9/08/5f3085fef9564217074db9dd8573de1795bc82cde61a7ad10b6a7234a569/yarl-1.24.5-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:2729fcfc4f6a596fb0c50f32090400aa9367774ac296a00387e65098c0befa76", size = 135680, upload-time = "2026-07-20T02:06:33.273Z" }, + { url = "https://files.pythonhosted.org/packages/98/35/ba9436e579bd48a8801f2021d842d9ab4994c26e4c7dd3a4c1f1bcb57a9e/yarl-1.24.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ff330d3c30db4eb6b01d79e29d2d0b407a7ecad39cfd9ec993ece57396a2ec0d", size = 97395, upload-time = "2026-07-20T02:06:35.259Z" }, + { url = "https://files.pythonhosted.org/packages/18/a9/a07f76f3c44e02b25cc743af5ef93eef27f7013eadca770451b6a6ccb5db/yarl-1.24.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e42d75862735da90e7fc5a7b23db0c976f737113a54b3c9777a9b665e9cbff75", size = 97223, upload-time = "2026-07-20T02:06:37.216Z" }, + { url = "https://files.pythonhosted.org/packages/77/f7/a9a1d6fa7dd9e388f95b30f6ad3ec4e285f6c8f61f44ce16070c3fcfe414/yarl-1.24.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a3732e66413163e72508da9eff9ce9d2846fde51fae45d3605393d3e6cd303e9", size = 108777, upload-time = "2026-07-20T02:06:39.292Z" }, + { url = "https://files.pythonhosted.org/packages/2f/44/e0b86c302471fabd6f02808ecf2ac52b8412b624787849d4bf2cdb466f6f/yarl-1.24.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5b8ee53be440a0cffc991a27be3057e0530122548dbe7c0892df08822fce5ede", size = 103119, upload-time = "2026-07-20T02:06:41.456Z" }, + { url = "https://files.pythonhosted.org/packages/d1/16/9c16d180bf8faaf223225eb50e1245870ff1ae0e302a27153988e65c51fd/yarl-1.24.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:af3aefa655adb5869491fa907e652290386800ae99cc50095cba71e2c6aefdca", size = 116471, upload-time = "2026-07-20T02:06:43.696Z" }, + { url = "https://files.pythonhosted.org/packages/d2/8d/b219b9df28a02ce95cfbdd41d2f7caa5669d0ff979c1c9975697145e33c5/yarl-1.24.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2120b96872df4a117cde97d270bac96aea7cc52205d305cf4611df694a487027", size = 115974, upload-time = "2026-07-20T02:06:45.874Z" }, + { url = "https://files.pythonhosted.org/packages/9b/e8/f20557aca240d88e69850ad1ee91756821d094bb1310565c04d25c6682a2/yarl-1.24.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:66410eb6345d467151934b49bfa70fb32f5b35a6140baa40ad97d6436abea2e9", size = 110830, upload-time = "2026-07-20T02:06:47.852Z" }, + { url = "https://files.pythonhosted.org/packages/db/18/199b85109a53eeca64ee19c9cca228287e8e4ab0cc1a09b28f530e65cce0/yarl-1.24.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4af7b7e1be0a69bee8210735fe6dcfc38879adfac6d62e789d53ba432d1ffa41", size = 110054, upload-time = "2026-07-20T02:06:49.84Z" }, + { url = "https://files.pythonhosted.org/packages/aa/2f/ed28147f8cd7f48c49367c90713b30a555284b6105a6a56f3a05568da795/yarl-1.24.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa139875ff98ab97da323cfadfaff08900d1ad42f1b5087b0b812a55c5a06373", size = 108312, upload-time = "2026-07-20T02:06:51.835Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c5/55e16ae0a5c227cea8df1c6871ba57d614a34243146c05729caf2a1bd9c5/yarl-1.24.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:0055afc45e864b92729ac7600e2d102c17bef060647e74bca75fa84d66b9ff36", size = 103662, upload-time = "2026-07-20T02:06:54.061Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ea/dbd7c2caec459c9a426f18b02688ecbfb58620d0f6a3422d24769fbaf8ab/yarl-1.24.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f0e466ed7511fe9d459a819edbc6c2585c0b6eabde9fa8a8947552468a7a6ef0", size = 116090, upload-time = "2026-07-20T02:06:56.015Z" }, + { url = "https://files.pythonhosted.org/packages/06/84/39ce4ce3059e07fece5fbdbee8c4053406af9aca911ce9fa5f8548aab6af/yarl-1.24.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f141474e85b7e54998ec5180530a7cda99ab29e282fa50e0756d89981a9b43c5", size = 109523, upload-time = "2026-07-20T02:06:57.926Z" }, + { url = "https://files.pythonhosted.org/packages/a9/8b/71ff44137b405c64a7788075669c24010019f57a7464b78c3a6cbee539d9/yarl-1.24.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:e2935f8c39e3b03e83519292d78f075189978f3f4adc15a78144c7c8e2a1cba5", size = 116084, upload-time = "2026-07-20T02:06:59.868Z" }, + { url = "https://files.pythonhosted.org/packages/62/c0/423078fdd4042e1862c11f0ffd977a0ffa393783c12bee94685923bc189e/yarl-1.24.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9d1216a7f6f77836617dba35687c5b78a4170afc3c3f18fc788f785ba26565c4", size = 111006, upload-time = "2026-07-20T02:07:01.907Z" }, + { url = "https://files.pythonhosted.org/packages/cf/52/6daa2ee9d95e5c98b8128f8df91eb692eb423ab274b8cf08db52152fad26/yarl-1.24.5-cp314-cp314-win_amd64.whl", hash = "sha256:5ba4f78df2bcc19f764a4b26a8a4f5049c110090ad5825993aacb052bf8003ad", size = 99215, upload-time = "2026-07-20T02:07:03.852Z" }, + { url = "https://files.pythonhosted.org/packages/ec/0e/464a847d7359e0da75dd9fc5c1d1aa35d0159ea31e5f8e66a3c1c29ff3d0/yarl-1.24.5-cp314-cp314-win_arm64.whl", hash = "sha256:9e4e16c73d717c5cf27626c524d0a2e261ad20e46932b2670f64ad5dde23e26f", size = 94566, upload-time = "2026-07-20T02:07:06.074Z" }, + { url = "https://files.pythonhosted.org/packages/e2/55/e03acc4446772660bc335e86e41ef31e4d0d838fd641531a11a5ee33b493/yarl-1.24.5-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e1ae548a9d901adca07899a4147a7c826bbcc06239d3ce9a59f57886a28a4c88", size = 142533, upload-time = "2026-07-20T02:07:08.284Z" }, + { url = "https://files.pythonhosted.org/packages/ae/71/4acd3a1fc7cf14345cdb302665ecd2097f62c365b4f14ca17d4f37775cf9/yarl-1.24.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ff405d91509d88e8d44129cd87b18d70acd1f0c1aeabd7bc3c46792b1fe2acba", size = 100776, upload-time = "2026-07-20T02:07:10.197Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0b/cfb76b7fe99686db264bff829779a539d923e7564ffd7ef18da6c54c3774/yarl-1.24.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:47e98aab9d8d82ff682e7b0b5dded33bf138a32b817fcf7fa3b27b2d7c412928", size = 100913, upload-time = "2026-07-20T02:07:12.357Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3f/7116e782992abbd4fb6948488aec72078895e929a23078290739e8396fce/yarl-1.24.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f0a658a6d3fafee5c6f63c58f3e785c8c43c93fbc02bf9f2b6663f8185e0971f", size = 106507, upload-time = "2026-07-20T02:07:14.173Z" }, + { url = "https://files.pythonhosted.org/packages/33/90/d4d2d73ee78229cc889872eb8e085d8f5c6f51abdb178409fd9b23cf74fd/yarl-1.24.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4377407001ca3c057773f44d8ddd6358fa5f691407c1ba92210bd3cf8d9e4c95", size = 99219, upload-time = "2026-07-20T02:07:16.019Z" }, + { url = "https://files.pythonhosted.org/packages/3e/fa/a6df1a9bccd644eec00abee0dff4277416222cec435330fd1f2858523ec1/yarl-1.24.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7c0494a31a1ac5461a226e7947a9c9b78c44e1dc7185164fa7e9651557a5d9bc", size = 111804, upload-time = "2026-07-20T02:07:18.141Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9e/7b2a1f4bcc20e9447156dd2b1c4d01f70d9df0759025ee7d09a84ffae134/yarl-1.24.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a7cff474ab7cd149765bb784cf6d78b32e18e20473fb7bda860bce98ab58e9da", size = 110943, upload-time = "2026-07-20T02:07:20.06Z" }, + { url = "https://files.pythonhosted.org/packages/08/ff/22c92affb0f9b623ca753d27d968b5625b868f12c6378d049d55ae247643/yarl-1.24.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cbb833ccacdb5519eff9b8b71ee618cc2801c878e77e288775d77c3a2ced858a", size = 108251, upload-time = "2026-07-20T02:07:22.217Z" }, + { url = "https://files.pythonhosted.org/packages/45/44/5769b96298c1e195fb412997b6090af2a84105cf59c17613558a2d011d1f/yarl-1.24.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:82f75e05912e84b7a0fe57075d9c59de3cb352b928330f2eb69b2e1f54c3e1f0", size = 106025, upload-time = "2026-07-20T02:07:24.083Z" }, + { url = "https://files.pythonhosted.org/packages/4c/40/009e8e791fd9762c0e1567e69248acb4f49064597e1680874c16dd8bb798/yarl-1.24.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:16a2f5010280020e90f5330257e6944bc33e73593b136cc5a241e6c1dc292498", size = 106573, upload-time = "2026-07-20T02:07:26.248Z" }, + { url = "https://files.pythonhosted.org/packages/20/c6/b7480578f8a0a80946f36ad6df547ecec704f9ba69d2de60f8aa6f1c1cbf/yarl-1.24.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ffcd54362564dc1a30fb74d8b8a6e5a6b11ebd5e27266adc3b7427a21a6c9104", size = 100751, upload-time = "2026-07-20T02:07:28.098Z" }, + { url = "https://files.pythonhosted.org/packages/d4/27/4476f3360b91a48c5cf125e91f59a3bd35299d84a431a258d57f5977bb11/yarl-1.24.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0465ec8cedc2349b97a6b595ace64084a50c6e839eca40aa0626f38b8350e331", size = 111643, upload-time = "2026-07-20T02:07:30.88Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4b/5cdd3e5ee944e8af31e52f6cd3d3af5fd7b937e036ccbbba2c9ffebede95/yarl-1.24.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4db9aecb141cb7a5447171b57aa1ed3a8fee06af40b992ffc31206c0b0121550", size = 106312, upload-time = "2026-07-20T02:07:33.06Z" }, + { url = "https://files.pythonhosted.org/packages/18/86/f406b0c2a6f99575de2da671ef47aa06f89a5be83a27a46971c3b86cecdb/yarl-1.24.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f540c013589084679a6c7fac07096b10159737918174f5dfc5e11bf5bca4dfe6", size = 110379, upload-time = "2026-07-20T02:07:35.155Z" }, + { url = "https://files.pythonhosted.org/packages/f0/6c/9f3adfbd3b30b4fa0f7ccb3a83eba2c1152d3fff554d535e640ba0f7ba2b/yarl-1.24.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a61834fb15d81322d872eaafd333838ae7c9cea84067f232656f75965933d047", size = 108497, upload-time = "2026-07-20T02:07:37.35Z" }, + { url = "https://files.pythonhosted.org/packages/dd/37/91eb2e5ca883a529c1b390348a74cd9fc0512171727f547ce70bfe02be5c/yarl-1.24.5-cp314-cp314t-win_amd64.whl", hash = "sha256:5c88e5815a49d289e599f3513aa7fde0bc2092ff188f99c940f007f90f53d104", size = 102450, upload-time = "2026-07-20T02:07:39.578Z" }, + { url = "https://files.pythonhosted.org/packages/bf/f4/ed5c402ac8fde4403ed3366c2716bfddc8a6677ebd59f3d62772cc7fe468/yarl-1.24.5-cp314-cp314t-win_arm64.whl", hash = "sha256:cf139c02f5f23ef6532040a30ff662c00a318c952334f211046b8e60b7f17688", size = 97222, upload-time = "2026-07-20T02:07:41.55Z" }, + { url = "https://files.pythonhosted.org/packages/61/02/962c1cbfc401a30c1d034dc67ff395f64b52302c6d62de556c1fca99acc0/yarl-1.24.5-py3-none-any.whl", hash = "sha256:a33700d13d9b7d84fd10947b09ff69fb9a792e519c8cb9764a3ca70baa6c23a7", size = 58612, upload-time = "2026-07-20T02:07:43.461Z" }, +] diff --git a/04-data-agent/envs/blackbox-opencode/verifier.py b/04-data-agent/envs/blackbox-opencode/verifier.py new file mode 100644 index 0000000..9a4428d --- /dev/null +++ b/04-data-agent/envs/blackbox-opencode/verifier.py @@ -0,0 +1,190 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Grade one data-agent rollout: read the answer the agent filed, compare it to gold. + +Grading runs on the HOST, not by executing the suite's own `tests/test.sh` inside the sandbox. Three +reasons: that script pip-installs at grade time and can call out to an LLM judge, which is +nondeterministic, billable and a network dependency inside every rollout; the host already holds the +authoritative tool-call count from the capture document, which the in-container grader cannot see; +and one more in-sandbox exec is one more thing that fails in a way indistinguishable from a wrong +answer. +""" + +from __future__ import annotations + +import logging +import re +from dataclasses import dataclass +from typing import Any, Callable + +from .grader import grade +from .reward import CHAT_ANSWER_CREDIT +from .task import ANSWER_PATH, DataAgentTask + + +logger = logging.getLogger(__name__) + + +def looks_like_a_command(text: str) -> bool: + """True if `text` is the agent NARRATING a command rather than stating a value. + + Load-bearing, because without it partial credit rewards exactly the wrong behaviour. Measured on + job 72695: 8 of 19 awards (42%) went to strings like + + echo -n "2.1410474569861977" > /workdir/answer.txt + + which the grader scored correct on the embedded number. That pays the model for SAYING it will + file the answer while never running the command -- a worse incentive than the inaction it + replaced, and one a policy learns quickly because narrating is cheaper than executing. + + Rejects redirection and piping, a leading shell verb, and any mention of the answer path. A bare + value never contains these: `2.14`, `936`, `C`, `Wii Sports` and `212,357` all pass. + """ + t = text.strip() + if ">" in t or "|" in t: + return True + if "answer.txt" in t or ANSWER_PATH in t: + return True + return bool(re.match(r"(?i)^(echo|cat|printf|tee|python3?|bash|sh|open|with)\b", t)) + + +@dataclass +class Grade: + """The outcome of grading one rollout. + + Attributes: + correctness (`float`, *optional*): + 1.0 filed and right, `CHAT_ANSWER_CREDIT` right in chat only, 0.0 wrong, `None` ungradable. + answer (`str`, *optional*): + What the agent actually submitted, for reporting. + source (`str`): + `"file"`, `"chat"`, or `"none"` -- which path produced the answer. + graded_by (`str`): + Which comparison tier matched, straight from the grader. + """ + + correctness: float | None + answer: str | None + source: str + graded_by: str = "" + + +def read_filed_answer( + read_text: Callable[[str], str | None], paths: tuple[str, ...] +) -> str | None: + """Read the answer file, trying each candidate path in order. + + There are TWO workdirs in the sandbox image -- `/workdir` (what the instruction names) and + `{home}/workdir` (what the harness cds into) -- so an agent that writes `echo -n 42 > answer.txt` + RELATIVE to its cwd lands in the second. Confirmed live: after a relative write, + `/home/user/workdir/answer.txt` is present and `/workdir/answer.txt` is absent, and neither path + errors, so the rollout scores 0 as though the agent never answered. + + EMPTY COUNTS AS ABSENT. A sandbox handle may RETURN `""` for a missing file rather than raising. + An earlier version broke out of the loop on `""`, fell through to the no-answer branch, and + returned 0.0 without ever consulting the transcript -- so partial credit silently never fired. + That was caught twice by a live test expecting 0.3 and getting 0.0. + """ + for path in paths: + try: + got = read_text(path) + except Exception: # a missing path is normal; try the next one + continue + if got is not None and got.strip(): + return got.strip() + return None + + +def grade_rollout( + task: DataAgentTask, + read_text: Callable[[str], str | None], + answer_paths: tuple[str, ...], + final_message: str | None = None, +) -> Grade: + """Grade a finished rollout. + + Args: + task (`DataAgentTask`): + The task, carrying gold and the comparison mode. + read_text (`Callable`): + Reads a path out of the sandbox, returning `None` or raising when absent. + answer_paths (`tuple[str, ...]`): + Candidate answer paths, already resolved for this backend's home directory. + final_message (`str`, *optional*): + The agent's last assistant message, used only for partial credit. + + Returns: + `Grade`: correctness, the submitted answer, and which path produced it. + """ + filed = read_filed_answer(read_text, answer_paths) + if filed: + result = grade( + task.answer, + filed, + question=task.question, + reward_mode=task.reward_mode, + abs_tol=task.atol, + rel_tol=task.rtol, + ) + return Grade(float(result.reward), filed, "file", result.method) + + # No filed answer. Before scoring 0, check whether the agent COMPUTED the right value and merely + # failed to file it: those are different failures and only one of them is about data analysis. + if final_message and not looks_like_a_command(final_message): + result = grade( + task.answer, + final_message, + question=task.question, + reward_mode=task.reward_mode, + abs_tol=task.atol, + rel_tol=task.rtol, + ) + if result.reward >= 1.0: + logger.info( + "no answer file, but the CHAT answer is correct (%r) -> partial %.2f", + final_message[:40], + CHAT_ANSWER_CREDIT, + ) + return Grade( + CHAT_ANSWER_CREDIT, final_message.strip(), "chat", result.method + ) + + logger.info("no %s and no correct chat answer -> correctness 0.0", ANSWER_PATH) + return Grade(0.0, None, "none") + + +def answer_paths_for(home: str) -> tuple[str, ...]: + """Candidate answer paths for a backend whose sandbox home is `home`. + + The home differs by backend -- E2B runs as `user`, HF sandboxes as root -- which is why this is + computed from the backend rather than hardcoded anywhere. + """ + from .task import ANSWER_PATHS + + return tuple(dict.fromkeys(p.format(home=home.rstrip("/")) for p in ANSWER_PATHS)) + + +def metadata_for( + task: DataAgentTask, grade_result: Grade, n_tool_calls: int | None +) -> dict[str, Any]: + """Auditable record of how this rollout was scored, carried on the result.""" + return { + "task_id": task.task_id, + "difficulty_tier": task.difficulty_tier, + "reward_mode": task.reward_mode, + "answer_source": grade_result.source, + "graded_by": grade_result.graded_by, + "tool_calls": n_tool_calls, + } diff --git a/04-data-agent/envs/whitebox-bash/.env.example b/04-data-agent/envs/whitebox-bash/.env.example new file mode 100644 index 0000000..d2e02cb --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/.env.example @@ -0,0 +1,12 @@ +# Sandbox provider. Required by the SERVER; the trainer client never needs it. +E2B_API_KEY= + +# Where the trainer client finds the hosted environment. +WHITE_BOX_BASH_URL=http://127.0.0.1:8000 + +# Optional: swap the built-in task suite for a Hub dataset. +# WHITE_BOX_BASH_DATASET=HuggingEnvs/white-box-bash + +# Bounds. MAX_SESSIONS is the real concurrency ceiling -- see server/environment.py. +WHITE_BOX_BASH_MAX_SESSIONS=200 +WHITE_BOX_BASH_SANDBOX_TIMEOUT_S=1800 diff --git a/04-data-agent/envs/whitebox-bash/README.md b/04-data-agent/envs/whitebox-bash/README.md new file mode 100644 index 0000000..4f47cd9 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/README.md @@ -0,0 +1,134 @@ +# whitebox-bash + +A **white-box** agent environment: bash plus the SETA tool surface over one sandbox, driven by +TRL's **synchronous** `GRPOTrainer`. + +## Which one should I use? + +The two sibling environments here are black box — an agent owns its own loop inside a sandbox, the +trainer never drives a turn, and everything trainable has to be recovered by *observing* model calls +through a capture proxy. This one inverts that: + +| | blackbox-opencode / blackbox-harbor | **whitebox-bash** | +| --- | --- | --- | +| Who owns the loop | the agent (opencode / a Harbor harness) | **TRL** | +| Trainer | `AsyncGRPOTrainer` | **`GRPOTrainer`** (sync) | +| Tools | inside the sandbox, not on the wire | **MCP calls, individually observable** | +| Token ids | recovered by a capture proxy | TRL already has them — it generated them | +| Loss mask | reconstructed per turn | TRL masks tool results itself | +| Needs a tunnel | yes (sandbox must reach capture) | **no** | + +The black-box pair is what you want when the thing being trained *is* a real agent you did not write. +This one is what you want when you want to see and shape every step. + +## Install + +The trainer needs only the client half: + +```bash +pip install -e . # client: the class TRL introspects +pip install -e '.[server]' # server: sandbox stack, only where you host it +``` + +## Train + +```python +from trl import GRPOTrainer +from whitebox_bash import white_box_bash_env + +trainer = GRPOTrainer( + model="Qwen/Qwen3.5-2B", + args=config, + train_dataset=dataset, + environment_factory=white_box_bash_env( + "https://your-space.hf.space", + toolsets="bash,seta", # the default; "bash" alone for a minimal terminal agent + step_limit=20, + ), +) +``` + +TRL calls the factory once per rollout, introspects the instance, and puts its public methods in the +model's tool schema. `reset()` returns the task text; `get_reward()` scores the episode. + +## Serve + +```bash +E2B_API_KEY=... uv run uvicorn server.app:app --host 0.0.0.0 --port 8000 +``` + +## The toolsets + +| set | tools | notes | +| --- | --- | --- | +| `bash` | `bash` | always included | +| `seta` | `read`, `write`, `edit`, `grep`, `glob`, `ls` | SETA's surface, same names | +| — | `submit_solution` | always present; ends the episode | + +Default is `("bash", "seta")` — full SETA parity. + +Both run on **one sandbox** and share its filesystem. That is the point: a file written by `write` +must be visible to `bash` in the very next call. Splitting the toolsets across servers would mean +replicating state between sandboxes, and every divergence would surface as an agent that wrote a file +and then could not find it — which reads as a model failure and is not one. + +**There is deliberately no second execution model.** An earlier revision also offered a persistent +Jupyter kernel; it was dropped because two tools could then do the same job under different state +semantics (kernel names persist, shell state does not), which is easy for a small model to conflate +and is one more unvalidated variable in an environment that has not trained yet. + +`submit_solution` is always present rather than living in `seta`, so a `bash`-only agent still has a +way to finish; it takes SETA's name so a task written against SETA reads unchanged here. + +## Why toolset selection varies the class, not a flag + +TRL turns **every public method** of the instance into a tool: + +```python +for member_name, member in inspect.getmembers(instance, predicate=inspect.ismethod): + if member_name == "reset": has_reset = True + elif member_name == "get_reward": has_reward = True + elif not member_name.startswith("_"): methods.append(member) +``` + +So a runtime flag could not shrink the surface — the model would still be offered every tool and +would call ones the server does not serve. `white_box_bash_env()` therefore composes a class from +mixins, one per toolset. Two consequences worth remembering when editing: + +* **every helper must be `_`-prefixed**, or it silently becomes a tool; +* **type hints and docstrings are the tool schema**, not documentation. Write them for the model. + +`tests/test_surface_matches.py` asserts all three descriptions of the surface agree — the registry in +`tools.py`, the client's methods, and the server's registered tools. If they drift the model calls a +tool nobody implements, gets an error, and the run reads as a policy that cannot use tools. Nothing +else would report it. + +## Tasks + +The Task API is implemented structurally: `list_splits`, `num_tasks`, `list_tasks`, `get_task`, +`get_task_range`. Difficulty is part of the **split name** (`train:medium`), never a filter — a +filter would shift every index after it, and the index is the task's identity everywhere downstream. + +A small built-in suite ships so the environment is testable with no network. Point +`WHITE_BOX_BASH_DATASET` at a Hub dataset to replace it. + +## Reward + +`grade` runs server-side, where the sandbox and the gold answer live. Three rules carried over from +the black-box work, each learned expensively: + +* an **ungraded** rollout is `None`, never `0.0` — a dead sandbox and a wrong answer are different + events, and collapsing them teaches the model the dead sandbox was its fault; +* the **efficiency bonus is gated on a solve and is never a penalty** — ungated, "make no tool calls" + becomes the best move for a policy that cannot solve the task; +* a submission that is really a **shell command** earns nothing — 42% of partial credit once went to + strings like `echo -n "2.14" > answer.txt`. + +## Not yet done + +* **SETA's task suite.** Its *tool surface* and terminator name are implemented here, so a task + written against SETA reads the same. Its 1,376-task suite is a different matter: it grades with + weighted pytest inside its own Ubuntu 24.04 image and speaks ORS. That lands later as a split (if + its image runs in our sandbox) or as a sibling env — a data and protocol decision, not a tools one. +* **A trained run.** The surfaces are verified and the grader is unit-tested; no GRPO run has used + this yet. diff --git a/04-data-agent/envs/whitebox-bash/__init__.py b/04-data-agent/envs/whitebox-bash/__init__.py new file mode 100644 index 0000000..870b80b --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/__init__.py @@ -0,0 +1,34 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""White-box bash/Jupyter/file agent environment. + +Install this package on the TRAINER side and hand `white_box_bash_env(...)` to TRL's +`GRPOTrainer(environment_factory=...)`. The trainer owns the loop; the sandbox lives behind a hosted +OpenEnv server. +""" + +from .client import exposed_tool_names, white_box_bash_env +from .tools import DEFAULT_TOOLSETS, TOOLSETS, resolve, specs_for, tool_names + + +__all__ = [ + "DEFAULT_TOOLSETS", + "TOOLSETS", + "exposed_tool_names", + "resolve", + "specs_for", + "tool_names", + "white_box_bash_env", +] diff --git a/04-data-agent/envs/whitebox-bash/client.py b/04-data-agent/envs/whitebox-bash/client.py new file mode 100644 index 0000000..f005beb --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/client.py @@ -0,0 +1,456 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The trainer-side class. Its methods ARE the model's tools. + +HOW TRL SEES THIS +`GRPOTrainer(environment_factory=...)` calls the factory once per rollout and then introspects the +instance (`trl/trainer/grpo_trainer.py`): + + for member_name, member in inspect.getmembers(instance, predicate=inspect.ismethod): + if member_name == "reset": has_reset = True + elif member_name == "get_reward": has_reward = True + elif not member_name.startswith("_"): methods.append(member) + +So `reset` and `get_reward` are special, everything else public becomes a tool, and anything private +is invisible. Three consequences shape this file: + + * TOOL SELECTION CANNOT BE A RUNTIME FLAG. If every tool were a method on one class, TRL would put + all of them in the schema no matter which toolsets were requested, and the model would call a + tool the server does not serve. The surface therefore has to vary by TYPE, which is why the tools + live on mixins and `white_box_bash_env()` composes a class from the selection. + * EVERY HELPER MUST BE `_`-PREFIXED, or it silently becomes a tool the model can call. + * Type hints and docstrings are not documentation here, they are the tool schema the model reads. + Write them for the model. + +WHY THIS IS WHITE BOX +The trainer owns the loop: it generates, parses the tool call, invokes one of these methods, appends +the result, and generates again. TRL masks the tool-result tokens itself, so the policy is never +trained on text it did not write. That is the thing the black-box path had to reconstruct from a +capture proxy, and it is why this environment needs no proxy, no tunnel and no token-id contract. +""" + +from __future__ import annotations + +import asyncio +import concurrent.futures +import inspect +import logging +import os +import threading +from typing import Any + +from .models import EpisodeStart, Grade, ToolResult +from .tools import SUBMIT, TOOLSETS, resolve + + +logger = logging.getLogger(__name__) + +DEFAULT_SERVER = os.environ.get("WHITE_BOX_BASH_URL", "http://127.0.0.1:8000") +ENV_NAME = "white_box_bash" + + +class _SyncMCP: + """Blocking wrapper over OpenEnv's async MCP client. + + TRL calls tools synchronously, from worker threads, and may itself be inside a running event + loop. `asyncio.run` would raise in that case, so the loop lives on its own daemon thread and every + call is handed to it with `run_coroutine_threadsafe`. One loop per instance: instances are pooled + per rollout and must not share a session. + + TRANSPORT IS HTTP `/mcp`, NOT THE WEBSOCKET -- see `_ensure`. The rest of this note explains why + the WebSocket knobs are still set: they apply if that transport is ever re-enabled. + + KEEPALIVE PINGS ARE DISABLED, DELIBERATELY + The transport is a WebSocket whose default keepalive is a 20 s ping with a 20 s timeout. That + event loop is a Python thread inside the TRAINING process, so it cannot answer a ping while the + main thread holds the GIL through CUDA-graph capture, compilation or a long generation -- all of + which routinely exceed 20 s. The server then closes the connection cleanly and the next call dies + with `ConnectionClosedOK: received 1000 (OK)`, which is what killed the first smoke at step 0. + + Liveness is not lost by turning pings off: every call carries its own timeout, so a genuinely + dead server surfaces there instead. What is lost is *early* detection of a dead peer while idle, + which is worth trading away -- the alternative is a healthy server being declared dead because the + trainer was busy, and this project has already lost a night to exactly that inversion. + + Calls also retry ONCE on a closed connection, because a connection can still be dropped for + reasons unrelated to pings and a fresh session is cheap. + """ + + def __init__(self, base_url: str, timeout_s: float = 600.0) -> None: + self._base_url = base_url.rstrip("/") + self._timeout_s = timeout_s + self._client: Any = None + self._loop: asyncio.AbstractEventLoop | None = None + self._thread: threading.Thread | None = None + + def _ensure(self) -> None: + if self._loop is None: + loop = asyncio.new_event_loop() + thread = threading.Thread(target=self._run_loop, args=(loop,), daemon=True, + name="white-box-bash-mcp") + thread.start() + self._loop, self._thread = loop, thread + if self._client is None: + from openenv.core.mcp_client import MCPToolClient + + client = MCPToolClient( + base_url=self._base_url, + websocket_ping_interval_s=None, # see the class docstring + websocket_ping_timeout_s=None, + ) + # HTTP `/mcp`, NOT the WebSocket. Measured: the server accepts a second concurrent + # WebSocket and then immediately closes it (`ConnectionClosedOK: received 1000 (OK)`), + # so the first client keeps working and every later one dies on its first call. TRL + # builds one environment instance per batch slot, so with `num_generations=4` that is + # three dead clients out of four -- it killed the smoke at step 0, twice. + # + # HTTP suits this environment better anyway: the episode id already travels in the + # payload as `session_id`, so nothing needs a per-connection session, and there is no + # long-lived socket for a GIL-blocked event loop to fail to keep alive. + client.use_production_mode = True + self._client = client + + @staticmethod + def _run_loop(loop: asyncio.AbstractEventLoop) -> None: + asyncio.set_event_loop(loop) + loop.run_forever() + + def call(self, name: str, **kwargs: Any) -> Any: + for attempt in (0, 1): + self._ensure() + try: + fut: concurrent.futures.Future = asyncio.run_coroutine_threadsafe( + self._client.call_tool(name, **kwargs), self._loop # type: ignore[arg-type] + ) + return fut.result(timeout=self._timeout_s) + except Exception as exc: + closed = "ConnectionClosed" in type(exc).__name__ or "closed" in str(exc).lower() + if attempt == 0 and closed: + logger.warning("MCP connection closed on %s; reconnecting once", name) + self._client = None # force a fresh session on the retry + continue + raise + + def close(self) -> None: + if self._loop is not None: + self._loop.call_soon_threadsafe(self._loop.stop) + self._loop = None + self._client = None + + +class _Base: + """Lifecycle and transport. Deliberately holds no tool methods.""" + + def __init__( + self, + base_url: str = DEFAULT_SERVER, + *, + split: str = "train", + index: int | None = None, + toolsets: str | list[str] | None = None, + step_limit: int = 20, + timeout_s: float = 600.0, + ) -> None: + self._mcp = _SyncMCP(base_url, timeout_s=timeout_s) + self._split = split + self._index = index + self._toolsets = resolve(toolsets) + self._step_limit = step_limit + self._session: str | None = None + self._steps = 0 + self._submitted: str | None = None + self._reward: float | None = None + + # --- TRL lifecycle ------------------------------------------------------------------------- + def reset(self, **kwargs: Any) -> str | None: + """Start an episode and return the task text. + + TRL appends the returned string to the last user message, so this is where the task statement + reaches the model. Returning `None` would leave the model with the system prompt alone and no + task -- which reads as a policy that will not engage, so an empty task is an error here, not a + silent no-op. + """ + split = kwargs.pop("split", self._split) + index = kwargs.pop("index", self._index) + self._steps = 0 + self._submitted = None + self._reward = None + payload = self._mcp.call( + "start_episode", split=split, index=index, toolsets=list(self._toolsets) + ) + start = EpisodeStart.from_payload(_as_dict(payload)) + self._session = start.session_id + prompt = start.prompt + if not prompt: + raise RuntimeError( + f"environment returned no task text for split={split!r} index={index!r}; " + "training on this would be training on an empty task" + ) + return prompt + + def get_reward(self) -> float: + """Score the episode from what the agent DID, not only from what it said. + + Called once per completed rollout. Grading happens server-side, where the sandbox and the + gold answer live; the client only carries the number back. + """ + if self._reward is not None: + return self._reward + verdict = Grade.from_payload(_as_dict(self._mcp.call("grade", session_id=self._session))) + # TRL's reward column is a float, so an UNGRADED episode has to become a number here. 0.0 is + # the honest choice at this boundary -- but note it is a LOSS of information the server had, + # and `verdict.note` says which episodes it happened to. + if verdict.ungraded: + logger.warning("episode ungraded (%s); reporting 0.0 to the trainer", verdict.note) + self._reward = 0.0 if verdict.reward is None else verdict.reward + return self._reward + + # --- internals (must stay `_`-prefixed or they become tools) ------------------------------- + def _invoke(self, tool: str, _counts: bool = True, **kwargs: Any) -> str: + """Call one tool. + + `_counts=False` exempts a tool from the step budget. Only the terminator uses it, and it must: + the budget message tells the agent to submit, so if submitting were itself blocked the advice + would be impossible to follow and every capped episode would score 0.0 -- indistinguishable + from a policy that cannot solve the task. Caught by a live rollout, not by any unit test. + """ + if self._session is None: + return "[error] no episode; the trainer must call reset() first" + if _counts and self._steps >= self._step_limit: + # A budget the MODEL can see. Returning an error string rather than raising keeps the + # rollout alive and lets the policy learn to submit before it runs out, which is the + # behaviour we actually want to reinforce. + return (f"[error] step budget of {self._step_limit} exhausted; " + f"call submit_solution with your best answer") + if _counts: + self._steps += 1 + try: + payload = self._mcp.call(tool, session_id=self._session, **kwargs) + except Exception as exc: # a dead sandbox must not kill the whole batch + logger.warning("tool %s failed: %s", tool, exc) + return f"[error] {type(exc).__name__}: {exc}" + return ToolResult.from_payload(_as_dict(payload)).output + + +def _as_dict(payload: Any) -> dict[str, Any]: + """Unwrap an MCP result into a plain dict. + + MCP nests the payload twice -- `structured_content.result`, or a JSON string in `content[0].text` + -- and which one arrives depends on the server's return annotation. Handling only one layer is a + real bug: the call succeeds, the dict comes back empty, and the rollout reads as a tool that + returned nothing. + """ + import json + + if payload is None: + return {} + if isinstance(payload, dict): + for key in ("structured_content", "structuredContent"): + inner = payload.get(key) + if isinstance(inner, dict): + return inner.get("result", inner) if "result" in inner else inner + content = payload.get("content") + if isinstance(content, list) and content: + text = content[0].get("text") if isinstance(content[0], dict) else None + if text: + try: + return json.loads(text) + except (ValueError, TypeError): + return {"output": text} + return payload + if isinstance(payload, str): + try: + return json.loads(payload) + except (ValueError, TypeError): + return {"output": payload} + return {"output": str(payload)} + + +# --- the tool mixins --------------------------------------------------------------------------- +# One class per toolset. `white_box_bash_env` composes only the selected ones, so the model's schema +# and the server's registered tools are the same set by construction. + +class _BashTools: + def bash(self, command: str) -> str: + """Run a shell command in the working directory and return its output. + + Each call is a fresh shell, so `cd` does not persist between calls; use absolute paths or + chain with `&&`. + + Args: + command: The shell command to run. + """ + return self._invoke("bash", command=command) + + +class _SetaTools: + def read(self, path: str) -> str: + """Read a file and return its contents. + + Args: + path: Path to the file, absolute or relative to the working directory. + """ + return self._invoke("read", path=path) + + def write(self, path: str, content: str) -> str: + """Write content to a file, creating or overwriting it. + + Args: + path: Path to write to. + content: The full contents to write. + """ + return self._invoke("write", path=path, content=content) + + def edit(self, path: str, old: str, new: str) -> str: + """Replace the first exact occurrence of `old` with `new` in a file. + + Fails if `old` does not appear, so read the file first to get the text exactly right. + + Args: + path: Path to the file to edit. + old: The exact text to replace. + new: The replacement text. + """ + return self._invoke("edit", path=path, old=old, new=new) + + def grep(self, pattern: str, path: str) -> str: + """Search files for a regular expression and return matching lines with their paths. + + Args: + pattern: A regular expression. + path: File or directory to search. + """ + return self._invoke("grep", pattern=pattern, path=path) + + def glob(self, pattern: str) -> str: + """List paths matching a glob pattern, one per line. + + Args: + pattern: A glob such as `**/*.py`. + """ + return self._invoke("glob", pattern=pattern) + + def ls(self, path: str) -> str: + """List the entries of a directory. + + Args: + path: Directory to list. + """ + return self._invoke("ls", path=path) + + +class _SubmitTool: + # `answer` accepts a number as well as a string, and that is not laziness. + # The type hint IS the schema the model sees, and pydantic validates the model's tool call + # against it. Many tasks end in "submit just the number", so the model emits a bare `3` -- and an + # `answer: str` annotation rejects it with + # Input should be a valid string [input_value=3, input_type=int] + # Every numeric answer then fails to submit and the episode scores 0.0, which is indistinguishable + # from a model that could not finish. Observed on the very first smoke step. Coerced to `str` + # immediately so the grader still sees one type. + def submit_solution(self, answer: str | int | float) -> str: + """Submit your final answer and end the episode. + + Call this once you are confident. The episode is graded on this answer together with what you + did to reach it. + + Args: + answer: The final answer, as a string or a number. + """ + answer = str(answer) + self._submitted = answer + # Exempt from the budget on purpose -- see `_invoke`. + return self._invoke("submit_solution", _counts=False, answer=answer) + + +_MIXINS: dict[str, type] = { + "bash": _BashTools, + "seta": _SetaTools, +} + + +def white_box_bash_env( + base_url: str = DEFAULT_SERVER, + *, + split: str = "train", + index: int | None = None, + toolsets: str | list[str] | None = None, + step_limit: int = 20, + timeout_s: float = 600.0, +): + """Build the `environment_factory` callable TRL expects. + + Returns a zero-argument callable producing instances whose public methods are exactly the tools + for `toolsets` -- so the model's schema cannot drift from what the server serves. + + Args: + base_url (`str`, *optional*): + The hosted environment server, e.g. an HF Space URL. + toolsets (`str` or `list[str]`, *optional*): + `"bash"`, `"seta"`, a comma-separated string, or `"all"`. `"bash"` is always + included. Defaults to `("bash", "seta")` -- full SETA parity. + step_limit (`int`, *optional*, defaults to `20`): + Tool calls per episode. Enforced client-side and reported to the model as an error string + rather than an exception, so the policy can learn to submit before exhausting it. + + Returns: + `Callable[[], object]`: pass directly as `environment_factory`. + + Examples: + + ```python + from trl import GRPOTrainer + from white_box_bash import white_box_bash_env + + trainer = GRPOTrainer( + model="Qwen/Qwen3.5-2B", + args=config, + train_dataset=dataset, + environment_factory=white_box_bash_env("https://my-space.hf.space"), + ) + ``` + """ + names = resolve(toolsets) + bases = tuple(_MIXINS[n] for n in names) + (_SubmitTool, _Base) + cls = type("WhiteBoxBashEnv", bases, {"__doc__": _Base.__doc__}) + + def factory() -> Any: + return cls( + base_url, + split=split, + index=index, + toolsets=names, + step_limit=step_limit, + timeout_s=timeout_s, + ) + + return factory + + +def exposed_tool_names(toolsets: str | list[str] | None = None) -> tuple[str, ...]: + """The tool names TRL will actually expose, derived the same way TRL derives them. + + Used by the consistency check in `tests/` and useful in a smoke script: if this does not match the + server's registered tools, the run is already broken and nothing downstream will say so. + """ + factory = white_box_bash_env(toolsets=toolsets) + instance = factory() + return tuple( + sorted( + name + for name, member in inspect.getmembers(instance, predicate=inspect.ismethod) + if name not in {"reset", "get_reward"} and not name.startswith("_") + ) + ) diff --git a/04-data-agent/envs/whitebox-bash/dataagent.py b/04-data-agent/envs/whitebox-bash/dataagent.py new file mode 100644 index 0000000..713b885 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/dataagent.py @@ -0,0 +1,197 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The real data-agent tasks, borrowed from the black-box environment next door. + +WHY REUSE RATHER THAN REIMPLEMENT +`blackbox-opencode` already loads `HuggingEnvs/data-agent`, stages each task's tables out of an HF +bucket, and grades answers with the right numeric tolerance and list handling. Re-deriving any of +that here would produce a second source of truth for the same decisions, and the two would drift -- +which is the failure this repo's conventions exist to prevent. + +Reusing it buys something better than convenience: PARITY. The white-box and black-box environments +then run the SAME tasks, staged the SAME way, graded by the SAME code, and the only remaining +difference is who owns the agent loop. That makes white-box vs black-box a controlled comparison +instead of two numbers that cannot be put beside each other. + +What this module does NOT reuse is the efficiency shaping in `../grader.py`: the black-box reward was +tuned for an agent whose tool calls are only inferrable from text, whereas here every call is +counted exactly. Correctness comes from the shared grader; the shaping stays local and explicit. +""" + +from __future__ import annotations + +import logging +import os +from typing import Any + +from .tasks import Task + + +logger = logging.getLogger(__name__) + +# The instruction the black-box agent gets tells it to write its answer to a file, because that is +# how a loop-owning agent submits. Here the agent submits through a TOOL, so that sentence would be +# actively misleading -- it would spend turns writing a file nobody reads. Rewritten at load time. +_FILE_SUBMIT_MARKERS = ("answer.txt", "/workdir/answer") + + +def _reword_submission(instruction: str) -> str: + """Replace file-submission wording with tool-submission wording. + + Left verbatim when no marker is present, so a task that never mentioned a file is untouched. + """ + if not any(m in instruction for m in _FILE_SUBMIT_MARKERS): + return instruction + lines = [ln for ln in instruction.splitlines() + if not any(m in ln for m in _FILE_SUBMIT_MARKERS)] + return "\n".join(lines).rstrip() + ( + "\n\nWhen you have the answer, call submit_solution with it. Submit only the value itself, " + "not the command that would produce it." + ) + + +# How many times to attempt the per-episode bucket staging, and how long to back off. +# Every episode lists and downloads from `hf://buckets/AdithyaSK/jupyter-agent-kaggle-all`, so at +# num_generations=8 that is 8 concurrent tree listings per step and ~800 over a 100-step run against +# ONE bucket. A single transient 504 from the bucket API killed a whole run at step 6/100: +# HfHubHTTPError: Server error '504 Gateway Timeout' +# for url 'https://huggingface.co/api/buckets/AdithyaSK/jupyter-agent-kaggle-all/tree...' +# A transient upstream error should cost ONE episode, not the run. Safe to retry because the staging +# script short-circuits when the input directory already has files, so a partial success is not +# repeated -- it is resumed. +STAGING_ATTEMPTS = int(os.environ.get("WHITE_BOX_BASH_STAGING_ATTEMPTS", "4")) +STAGING_BACKOFF_S = int(os.environ.get("WHITE_BOX_BASH_STAGING_BACKOFF_S", "5")) + + +def _with_retry(setup: str, attempts: int = STAGING_ATTEMPTS, + backoff_s: int = STAGING_BACKOFF_S) -> str: + """Wrap the staging shell in a bounded retry with linear backoff. + + The original runs in a SUBSHELL so its `set -e` cannot abort the retry loop, and the loop fails + loudly at the end rather than letting the agent start against an empty input directory -- which + would score 0 for a reason indistinguishable from a wrong answer. + """ + if not setup.strip(): + return setup + return ( + "__staged=0\n" + f"for __attempt in $(seq 1 {attempts}); do\n" + f" if ( {setup}\n ); then __staged=1; break; fi\n" + f" echo \"[stage] attempt $__attempt failed; retrying\" >&2\n" + f" sleep $(( __attempt * {backoff_s} ))\n" + "done\n" + f"[ \"$__staged\" = 1 ] || {{ echo '[stage] FATAL: staging failed after {attempts} attempts' >&2; exit 1; }}\n" + ) + + +def available() -> bool: + """Whether the sibling environment is importable. Reported, never silently worked around.""" + try: + import data_agent_env # noqa: F401 + except Exception: + return False + return True + + +def load(split: str, limit: int = 0) -> tuple[Task, ...]: + """Data-agent tasks for `split`, as white-box `Task`s. + + Args: + split (`str`): + A data-agent split name: `` or `:`, e.g. `train:medium`. Tier is part + of the NAME, never a filter, because the index is the task's identity downstream. + limit (`int`, *optional*): + Keep only the first `limit` tasks. `0` keeps all. + + Returns: + `tuple[Task, ...]`: with `setup` staging the task's tables and `answer` the withheld gold. + """ + from data_agent_env import tasks as da_tasks + + token = os.environ.get("HF_TOKEN") or os.environ.get("HF_API_KEY") or None + if not token: + # Staging pulls from an HF bucket. Without a token the pull fails inside the sandbox and the + # agent is handed an empty input directory -- it then scores 0 for a reason indistinguishable + # from a wrong answer. Say so here rather than let every rollout fail plausibly. + logger.warning( + "HF_TOKEN is not set; data-agent bucket staging will fail and every rollout will score " + "zero in a way that looks like a bad policy" + ) + + rows = da_tasks.rows_for(split) + if limit: + rows = rows[:limit] + out: list[Task] = [] + for i, row in enumerate(rows): + t = da_tasks.task_at(split, i) + out.append( + Task( + instruction=_reword_submission(t.instruction), + answer=str(t.answer), + difficulty=str(getattr(t, "difficulty_tier", "") or "medium"), + setup=_with_retry(t.setup_shell(token)), + check="", + metadata={ + "source": "data-agent", + "split": split, + "index": i, + # Carried so the shared grader can apply the right comparison. A numeric answer + # compared as a string reads as a model that cannot count. + "reward_mode": getattr(t, "reward_mode", "") or "", + "atol": getattr(t, "atol", None), + "rtol": getattr(t, "rtol", None), + "env": t.env(token), + }, + ) + ) + return tuple(out) + + +def grade_answer(task: Task, submitted: str) -> bool: + """Correctness via the BLACK-BOX environment's grader, so both envs agree on what is right. + + Falls back to this package's own comparison only if the sibling is unavailable, and says so -- + a silent fallback would mean the two environments disagreed about correctness without anyone + noticing. + """ + meta = task.metadata or {} + try: + from data_agent_env import grader as da_grader + + # `grade` returns a GradeResult(score, method), not a bool, and its tolerances DEFAULT + # rather than accept None -- passing None straight through would blow up inside the numeric + # comparison. `judge=False` keeps grading deterministic and free of an external model; the + # four tiers below it (exact, numeric with percent/fraction bridging, order-insensitive list, + # math-verify) are what actually decide these answers. + tol = {} + if meta.get("atol") is not None: + tol["abs_tol"] = float(meta["atol"]) + if meta.get("rtol") is not None: + tol["rel_tol"] = float(meta["rtol"]) + result = da_grader.grade( + gold=task.answer, + candidate=submitted, + question=task.instruction, + reward_mode=str(meta.get("reward_mode") or ""), + judge=False, + **tol, + ) + # The field is `reward`, not `score` (GradeResult(reward=..., method=...)). + return float(result.reward) >= 1.0 + except Exception as exc: + logger.warning("data-agent grader unavailable (%s); falling back to local comparison", exc) + from .grader import answers_match + + return answers_match(submitted, task.answer) diff --git a/04-data-agent/envs/whitebox-bash/grader.py b/04-data-agent/envs/whitebox-bash/grader.py new file mode 100644 index 0000000..a424087 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/grader.py @@ -0,0 +1,170 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Scoring, server-side. + +Three rules carried over from the black-box data-agent env, each of which was learned the expensive +way: + + * AN UNGRADED ROLLOUT IS `None`, NEVER 0.0. A sandbox that died and a policy that answered wrongly + are different events, and collapsing them teaches the model that the dead sandbox was its fault. + `None` drops the rollout from its group baseline instead. + * THE EFFICIENCY BONUS IS GATED ON A SOLVE AND IS NEVER A PENALTY. Ungated, "make no tool calls at + all" becomes the highest-scoring move for a policy that cannot solve the task. + * A SUBMITTED ANSWER THAT IS REALLY A COMMAND EARNS NOTHING. On the data-agent env 42% of partial + credit once went to strings like `echo -n "2.14" > answer.txt` -- paying the model for narrating + the submission rather than making it. +""" + +from __future__ import annotations + +import re +from dataclasses import dataclass + + +# A solve is worth this; everything else is a small addition on top, so the ordering of outcomes can +# never be inverted by the shaping terms. +CORRECT_CREDIT = 1.0 +# Paid only when the task ships a `check` and it passes. Independent evidence that the side effect +# actually happened, which a string comparison cannot see. +CHECK_CREDIT = 0.25 +# Gated on a solve. Small on purpose: it breaks ties between two correct agents, it does not decide +# between a correct and an incorrect one. +EFFICIENCY_WEIGHT = 0.10 +# Tool calls treated as "free" before the efficiency term starts decaying. +TOOL_BUDGET = 10 + +_COMMAND_SHAPED = re.compile( + r"(^|\s)(echo|printf|cat|python3?|bash|sh|tee|awk|sed)\b|[>|]{1,2}\s*\S+|\$\(|`", +) + + +def looks_like_a_command(answer: str) -> bool: + """True when the 'answer' is really the shell line that would produce it. + + Checked before any credit: `echo -n "42" > out.txt` contains 42 and would otherwise score as a + correct answer of 42. + """ + if not answer: + return False + return bool(_COMMAND_SHAPED.search(answer.strip())) + + +def normalise(text: str) -> str: + """Lowercase, collapse whitespace, drop surrounding quotes and trailing punctuation.""" + t = (text or "").strip().strip("'\"").strip() + t = re.sub(r"\s+", " ", t) + return t.rstrip(".").lower() + + +def answers_match(submitted: str, gold: str) -> bool: + """Exact after normalisation, then numeric with a tolerance. + + Numeric comparison is separate because `4`, `4.0` and `4 ` are the same answer and a string + comparison says they are not -- which reads as a model that cannot count. + """ + s, g = normalise(submitted), normalise(gold) + if not s: + return False + if s == g: + return True + try: + sv, gv = float(s.replace(",", "")), float(g.replace(",", "")) + except (TypeError, ValueError): + return False + return abs(sv - gv) <= max(1e-6, abs(gv) * 1e-6) + + +@dataclass +class Verdict: + """The outcome of one episode. + + `reward` of `None` means UNGRADED -- see the module docstring. + """ + + reward: float | None + correct: bool = False + check_passed: bool | None = None + submitted: str = "" + n_tool_calls: int = 0 + note: str = "" + + def as_dict(self) -> dict: + return { + "reward": self.reward, + "correct": self.correct, + "check_passed": self.check_passed, + "submitted": self.submitted, + "n_tool_calls": self.n_tool_calls, + "note": self.note, + } + + +def grade( + *, + submitted: str | None, + gold: str, + n_tool_calls: int, + check_passed: bool | None = None, + sandbox_alive: bool = True, + correct_override: bool | None = None, +) -> Verdict: + """Score one episode. + + Args: + submitted (`str`, *optional*): + What the agent passed to `submit`. `None` means it never submitted. + gold (`str`): + The task's gold answer. + n_tool_calls (`int`): + How many tools the agent actually invoked. Observable here because this is a white-box + environment; the black-box path had to infer it from free text. + check_passed (`bool`, *optional*): + Result of the task's `check` script, or `None` when the task ships none. + sandbox_alive (`bool`, *optional*, defaults to `True`): + False when the sandbox died mid-episode, which makes the rollout UNGRADED. + correct_override (`bool`, *optional*): + Correctness decided elsewhere, used for data-agent tasks so this environment and the + black-box one agree on what is right. The shaping below (efficiency, the command-shaped + guard) still applies; only the correct/incorrect decision is taken from here. + + Returns: + `Verdict`: with `reward=None` when the episode could not be judged. + """ + if not sandbox_alive: + return Verdict(reward=None, n_tool_calls=n_tool_calls, + note="sandbox died; ungraded, not wrong") + if submitted is None: + # Never submitting IS a failure of the task, not an infrastructure fault: the agent had the + # tool and the budget and did not use it. Scored 0.0, not None. + return Verdict(reward=0.0, n_tool_calls=n_tool_calls, note="no answer submitted") + if looks_like_a_command(submitted): + return Verdict(reward=0.0, submitted=submitted, n_tool_calls=n_tool_calls, + note="submission is a command, not an answer") + + correct = answers_match(submitted, gold) if correct_override is None else correct_override + reward = CORRECT_CREDIT if correct else 0.0 + if check_passed: + reward += CHECK_CREDIT + if correct: + # Decays from 1 to 0 across TOOL_BUDGET extra calls; never negative, never paid on a failure. + over = max(0, n_tool_calls - TOOL_BUDGET) + reward += EFFICIENCY_WEIGHT * max(0.0, 1.0 - over / float(TOOL_BUDGET)) + return Verdict( + reward=round(reward, 6), + correct=correct, + check_passed=check_passed, + submitted=submitted, + n_tool_calls=n_tool_calls, + ) diff --git a/04-data-agent/envs/whitebox-bash/models.py b/04-data-agent/envs/whitebox-bash/models.py new file mode 100644 index 0000000..0905aa3 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/models.py @@ -0,0 +1,100 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Typed payloads crossing the wire. + +These describe what `start_episode`, a tool call and `grade` return. The MCP tools return plain +dicts -- FastMCP builds the schema from the annotations, and a Pydantic return type there changes +the envelope the client has to unwrap -- so these are the documented shape of those dicts and the +type the client parses them into, rather than the tools' literal return annotations. +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any + + +@dataclass +class EpisodeStart: + """What `start_episode` hands back. + + `prompt` is the task text. It is returned rather than looked up client-side on purpose: the gold + answer never leaves the server, so the client cannot construct the task itself, and a client that + could would be one bug away from training on the answer. + """ + + session_id: str + prompt: str + task_id: str = "" + workdir: str = "" + + @classmethod + def from_payload(cls, d: dict[str, Any]) -> "EpisodeStart": + return cls( + session_id=str(d.get("session_id", "")), + prompt=str(d.get("prompt", "")), + task_id=str(d.get("task_id", "")), + workdir=str(d.get("workdir", "")), + ) + + +@dataclass +class ToolResult: + """What one tool call returns. + + `output` is the exact string the model sees next turn, already clipped server-side. `ok` is the + tool's own verdict and is NOT derivable from the text: a `grep` that matches nothing succeeds + with empty output, and a `read` of a missing file fails with a message that reads like content. + """ + + output: str = "" + ok: bool = True + + @classmethod + def from_payload(cls, d: dict[str, Any]) -> "ToolResult": + return cls(output=str(d.get("output", "")), ok=bool(d.get("ok", True))) + + +@dataclass +class Grade: + """What `grade` returns. + + `reward` of `None` means UNGRADED -- the episode could not be judged (a dead sandbox), which is + a different event from a wrong answer and must not be collapsed into `0.0`. + """ + + reward: float | None = None + correct: bool = False + check_passed: bool | None = None + submitted: str = "" + n_tool_calls: int = 0 + note: str = "" + metadata: dict[str, Any] = field(default_factory=dict) + + @property + def ungraded(self) -> bool: + return self.reward is None + + @classmethod + def from_payload(cls, d: dict[str, Any]) -> "Grade": + reward = d.get("reward") + return cls( + reward=None if reward is None else float(reward), + correct=bool(d.get("correct", False)), + check_passed=d.get("check_passed"), + submitted=str(d.get("submitted", "")), + n_tool_calls=int(d.get("n_tool_calls", 0) or 0), + note=str(d.get("note", "")), + ) diff --git a/04-data-agent/envs/whitebox-bash/openenv.yaml b/04-data-agent/envs/whitebox-bash/openenv.yaml new file mode 100644 index 0000000..96cf5f8 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/openenv.yaml @@ -0,0 +1,6 @@ +spec_version: 1 +name: white_box_bash +type: space +runtime: fastapi +app: server.app:app +port: 8000 diff --git a/04-data-agent/envs/whitebox-bash/pyproject.toml b/04-data-agent/envs/whitebox-bash/pyproject.toml new file mode 100644 index 0000000..0b3b83c --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/pyproject.toml @@ -0,0 +1,32 @@ +[build-system] +requires = ["setuptools>=68"] +build-backend = "setuptools.build_meta" + +[project] +name = "openenv-whitebox-bash-env" +version = "0.1.0" +description = "White-box bash / Jupyter / file agent environment for TRL's synchronous GRPOTrainer." +requires-python = ">=3.10" +# The TRAINER only needs the client half: a class whose methods are the tools. Keep this list to +# what `client.py` imports, so `pip install` on a training node does not drag in the sandbox stack. +dependencies = [ + "openenv-core>=0.1", +] + +[project.optional-dependencies] +# Everything the hosted server needs and the trainer does not. +server = [ + "fastmcp>=2.0", + "fastapi>=0.110", + "uvicorn>=0.27", + "e2b>=1.0", + "datasets>=2.19", +] +dev = ["pytest>=8"] + +[tool.setuptools] +packages = ["whitebox_bash", "whitebox_bash.server"] + +[tool.setuptools.package-dir] +whitebox_bash = "." +"whitebox_bash.server" = "server" diff --git a/04-data-agent/envs/whitebox-bash/server/Dockerfile b/04-data-agent/envs/whitebox-bash/server/Dockerfile new file mode 100644 index 0000000..6a98289 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/server/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.12-slim + +WORKDIR /app + +# Server extras only. The trainer-side install is deliberately lighter -- see pyproject.toml. +COPY pyproject.toml ./ +RUN pip install --no-cache-dir \ + "openenv-core>=0.1" "fastmcp>=2.0" "fastapi>=0.110" "uvicorn>=0.27" \ + "e2b>=1.0" "datasets>=2.19" + +COPY . /app/whitebox_bash/ +ENV PYTHONPATH=/app + +# E2B_API_KEY must be provided at run time. Without it the server starts and then fails every +# episode at sandbox creation, which is worse than refusing to start -- so app.py checks for it. +EXPOSE 8000 +CMD ["uvicorn", "whitebox_bash.server.app:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/04-data-agent/envs/whitebox-bash/server/__init__.py b/04-data-agent/envs/whitebox-bash/server/__init__.py new file mode 100644 index 0000000..cb5761a --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/server/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Server side. Imported only where the environment is hosted, never by the trainer client.""" diff --git a/04-data-agent/envs/whitebox-bash/server/app.py b/04-data-agent/envs/whitebox-bash/server/app.py new file mode 100644 index 0000000..44c62f7 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/server/app.py @@ -0,0 +1,64 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""FastAPI app. + + uv run uvicorn server.app:app --host 0.0.0.0 --port 8000 + +`E2B_API_KEY` must be present: without it every `start_episode` fails at sandbox creation, which +surfaces as an environment that accepts connections and then refuses every episode. +""" + +import os + +from openenv.core.env_server.http_server import create_app +from openenv.core.env_server.mcp_types import CallToolAction, CallToolObservation + +try: + from .environment import WhiteBoxBashEnvironment +except ImportError: # running as `server.app` rather than as a package + from server.environment import WhiteBoxBashEnvironment + + +# MAX_CONCURRENT_ENVS IS NOT OPTIONAL FOR TRAINING. +# +# `create_app` defaults to ONE concurrent MCP session. TRL builds one environment instance per batch +# slot, so at `num_generations=4` three of four clients are refused. Over the WebSocket transport that +# refusal is SILENT -- the server accepts the connection and immediately closes it, and the client +# dies on its first call with `ConnectionClosedOK: received 1000 (OK)`, which names no cause. It +# killed the first two smoke runs at step 0. Over HTTP `/mcp` the same condition reports itself +# properly as `Server at capacity: 1/1 sessions`, which is how it was finally diagnosed. +# +# Safe to raise here because this environment does not set `REQUIRES_SINGLE_THREAD_EXECUTOR`: its +# per-episode state lives in a module-level registry guarded by a lock, not on the instance. +# +# Keep this >= the trainer's concurrent rollouts. The real ceiling is the sandbox provider, which the +# environment bounds separately with WHITE_BOX_BASH_MAX_SESSIONS. +MAX_CONCURRENT_ENVS = int(os.environ.get("WHITE_BOX_BASH_MAX_CONCURRENT_ENVS", "64")) + +app = create_app( + WhiteBoxBashEnvironment, + CallToolAction, + CallToolObservation, + env_name="white_box_bash", + max_concurrent_envs=MAX_CONCURRENT_ENVS, +) + + +if __name__ == "__main__": + import uvicorn + + if not os.environ.get("E2B_API_KEY"): + raise SystemExit("E2B_API_KEY is not set; every episode would fail at sandbox creation") + uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8000"))) diff --git a/04-data-agent/envs/whitebox-bash/server/environment.py b/04-data-agent/envs/whitebox-bash/server/environment.py new file mode 100644 index 0000000..f14341b --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/server/environment.py @@ -0,0 +1,370 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The server. Registers every tool with FastMCP and owns the sandboxes. + +SESSIONS LIVE AT MODULE SCOPE, NOT ON `self` +OpenEnv's HTTP server builds a throwaway environment instance per request and closes it in a +`finally` (`http_server.py`). Anything held on `self` therefore dies with the request that created +it -- a sandbox stored there would be leaked on every single call, and the next tool call would find +no session. The registry below is module-level and guarded by a lock for exactly that reason. + +TOOLS TAKE A `session_id` FOR THE SAME REASON +There is no per-connection server state to hang an episode off, so the episode id travels in the +call. `start_episode` mints it; every other tool presents it. + +WHY THIS IS THE WHITE-BOX HALF +Nothing here runs an agent. The trainer generates a tool call, TRL invokes the matching client +method, that arrives here as one MCP call, this module runs it in the sandbox and returns the +result. Every action is a separate, observable, individually-scoreable event -- which is what lets +`grade` reward what the agent DID rather than only what it finally said. +""" + +from __future__ import annotations + +import fnmatch +import logging +import os +import re +import shlex +import threading +import uuid +from dataclasses import dataclass, field +from typing import Any + +from fastmcp import FastMCP +from openenv.core.env_server.mcp_environment import MCPEnvironment +from openenv.core.env_server.types import Action, Observation + +from .. import grader, tasks +from ..tools import resolve +from .sandbox import WORKDIR, ExecResult, Sandbox + +# The reproducible Harbor catalog uses the same native grader on Daytona. +# Other task sources keep the original E2B backend. +if os.environ.get("WHITE_BOX_BASH_TASK_SOURCE") == "harbor-frozen": + from daytona_whitebox_backend import DaytonaSandbox as Sandbox + WORKDIR = "/workdir" + + +logger = logging.getLogger(__name__) + +# How long a sandbox may live. An episode that outlives this is not salvageable, and leaving it +# running costs money in the sandbox provider rather than in this process, so it is bounded here. +SANDBOX_TIMEOUT_S = int(os.environ.get("WHITE_BOX_BASH_SANDBOX_TIMEOUT_S", "1800")) +MAX_SESSIONS = int(os.environ.get("WHITE_BOX_BASH_MAX_SESSIONS", "200")) + + +@dataclass +class Session: + """One episode: its sandbox, its task, and what the agent has done so far.""" + + session_id: str + split: str + index: int + task: Any + sandbox: Sandbox + toolsets: tuple[str, ...] + submitted: str | None = None + alive: bool = True + calls: list[dict[str, Any]] = field(default_factory=list) + + +_LOCK = threading.Lock() +_SESSIONS: dict[str, Session] = {} + + +def _get(session_id: str | None) -> Session: + with _LOCK: + s = _SESSIONS.get(str(session_id)) + if s is None: + raise KeyError( + f"no such session {session_id!r}; it may have been released after grading. " + "Call start_episode first." + ) + return s + + +def _release(session_id: str) -> None: + """Drop a session and kill its sandbox. + + Not optional bookkeeping: a sandbox held past its episode keeps costing money and counts against + the provider's concurrency cap, and leftovers collide with the next run's claim -- which presents + as a burst of capacity errors on a server that looks idle. + """ + with _LOCK: + s = _SESSIONS.pop(session_id, None) + if s is not None: + s.sandbox.kill() + + +def _run(session_id: str, tool: str, fn, **args: Any) -> dict[str, Any]: + """Invoke one tool, record it, and render the result for the model. + + A failing tool returns its error AS TEXT rather than raising: the agent should see `No such file` + and adapt, which is a thing to learn. Raising would abort the rollout and score a recoverable + mistake as an infrastructure failure. + """ + s = _get(session_id) + try: + result: ExecResult = fn(s) + except Exception as exc: + logger.warning("tool %s raised for session %s", tool, session_id, exc_info=True) + result = ExecResult(error=f"{type(exc).__name__}: {exc}", exit_code=1) + # A sandbox that has gone away makes the whole episode ungradeable, and `grade` must be told + # so it returns None rather than 0.0. + if "sandbox" in str(exc).lower() or "timeout" in str(exc).lower(): + s.alive = False + s.calls.append({"tool": tool, "args": args, "ok": result.ok}) + return {"output": result.render(), "ok": result.ok} + + +class WhiteBoxBashEnvironment(MCPEnvironment): + """Bash and the SETA file tools over one sandbox, plus the Task API.""" + + # The server refuses `max_concurrent_envs > 1` unless an environment asserts this, and the + # assertion is true here for a specific reason: NO per-episode state is held on the instance. + # Sessions live in the module-level `_SESSIONS` registry behind `_LOCK`, each owning its own + # sandbox, and the instance itself is stateless -- which it has to be anyway, because the HTTP + # server builds a throwaway instance per request. + # + # This matters for training: TRL creates one environment client per batch slot, so at + # `num_generations=4` a cap of 1 refuses three of four. Do not set this on an environment that + # keeps episode state on `self`. + SUPPORTS_CONCURRENT_SESSIONS = True + + def __init__(self) -> None: + mcp = FastMCP("white_box_bash") + + # --- episode lifecycle ----------------------------------------------------------------- + @mcp.tool + def start_episode(split: str = "train", index: int | None = None, + toolsets: list[str] | None = None) -> dict: + """Create a sandbox, stage the task's inputs, and return the task text.""" + with _LOCK: + if len(_SESSIONS) >= MAX_SESSIONS: + raise RuntimeError( + f"{MAX_SESSIONS} sessions already live; refusing to start another. " + "Lower the trainer's concurrency or raise WHITE_BOX_BASH_MAX_SESSIONS." + ) + idx = 0 if index is None else int(index) + task = tasks.task_at(split, idx) + # The task's own environment goes into the SANDBOX, not into the setup command text: + # data-agent tasks stage their tables from an HF bucket and need HF_TOKEN, HF_BUCKET and + # BUCKET_PREFIX there. Credentials travel by name through the process environment and are + # never interpolated into a command string, so a token cannot reach a log line or a trace. + sandbox_kwargs = {"task": task} if tasks.TASK_SOURCE == "harbor-frozen" else {} + sb = Sandbox.start(timeout_s=SANDBOX_TIMEOUT_S, + envs=(task.metadata or {}).get("env") or {}, **sandbox_kwargs) + if task.setup: + # Staging failure is fatal to the episode: an agent asked about `data.csv` that was + # never written will look like a model that cannot read a file. + r = sb.bash(task.setup) + if not r.ok: + sb.kill() + raise RuntimeError(f"task setup failed: {r.render()}") + sid = uuid.uuid4().hex + with _LOCK: + _SESSIONS[sid] = Session( + session_id=sid, split=split, index=idx, task=task, sandbox=sb, + toolsets=resolve(toolsets), + ) + return {"session_id": sid, "prompt": task.instruction, + "task_id": task.task_id, "workdir": WORKDIR} + + @mcp.tool + def close_episode(session_id: str) -> dict: + """Release an abandoned episode without inventing a grade.""" + _release(session_id) + return {"closed": True} + + @mcp.tool + def submit_solution(session_id: str, answer: str) -> dict: + """Record the agent's final answer. Does not grade; `grade` does.""" + s = _get(session_id) + s.submitted = answer + s.calls.append({"tool": "submit_solution", "args": {"answer": answer}, "ok": True}) + return {"output": "submitted", "ok": True} + + @mcp.tool + def grade(session_id: str) -> dict: + """Score the episode and release its sandbox.""" + s = _get(session_id) + if (s.task.metadata or {}).get('source') == 'harbor-frozen': + try: + return s.sandbox.grade(s.submitted) + finally: + _release(session_id) + check_passed: bool | None = None + if s.alive and s.task.check: + check_passed = s.sandbox.bash(s.task.check).ok + # Correctness comes from the BLACK-BOX environment's grader for data-agent tasks, so + # both environments agree on what counts as right; the shaping below stays local. + override = None + if (s.task.metadata or {}).get("source") == "data-agent" and s.submitted is not None: + from .. import dataagent + + override = dataagent.grade_answer(s.task, s.submitted) + verdict = grader.grade( + submitted=s.submitted, + gold=s.task.answer, + correct_override=override, + # Submitting is bookkeeping, not work: counting it would charge the agent for + # finishing, which is the one thing we want it to do. + n_tool_calls=sum(1 for c in s.calls if c["tool"] != "submit_solution"), + check_passed=check_passed, + sandbox_alive=s.alive, + ) + _release(session_id) + return verdict.as_dict() + + # --- bash ------------------------------------------------------------------------------ + @mcp.tool + def bash(session_id: str, command: str) -> dict: + """Run a shell command in the working directory.""" + return _run(session_id, "bash", lambda s: s.sandbox.bash(command), command=command) + + # --- seta ------------------------------------------------------------------------------ + @mcp.tool + def read(session_id: str, path: str) -> dict: + """Read a file.""" + return _run(session_id, "read", lambda s: s.sandbox.read_file(path), path=path) + + @mcp.tool + def write(session_id: str, path: str, content: str) -> dict: + """Write a file.""" + return _run(session_id, "write", lambda s: s.sandbox.write_file(path, content), + path=path) + + @mcp.tool + def edit(session_id: str, path: str, old: str, new: str) -> dict: + """Replace the first exact occurrence of `old` with `new`.""" + + def do(s: Session) -> ExecResult: + cur = s.sandbox.read_file(path) + if not cur.ok: + return cur + if old not in cur.stdout: + # Loudly, with a hint. A silent no-op here is the worst outcome: the agent + # believes it edited the file and every later step reasons from a false premise. + return ExecResult( + error=f"`old` not found in {path}; read the file and match the text exactly", + exit_code=1, + ) + return s.sandbox.write_file(path, cur.stdout.replace(old, new, 1)) + + return _run(session_id, "edit", do, path=path) + + @mcp.tool + def grep(session_id: str, pattern: str, path: str) -> dict: + """Search for a regular expression.""" + + def do(s: Session) -> ExecResult: + try: + re.compile(pattern) + except re.error as exc: + return ExecResult(error=f"bad regular expression: {exc}", exit_code=1) + # -r so a directory works, -n for line numbers, -I to skip binaries. `|| true` keeps + # "no matches" (grep's exit 1) from reading as a tool failure -- it is a valid result. + return s.sandbox.bash( + f"grep -rnI -E {shlex.quote(pattern)} {shlex.quote(path)} || true" + ) + + return _run(session_id, "grep", do, pattern=pattern, path=path) + + @mcp.tool + def glob(session_id: str, pattern: str) -> dict: + """List paths matching a glob.""" + + def do(s: Session) -> ExecResult: + listing = s.sandbox.bash("find . -type f") + if not listing.ok: + return listing + names = [p[2:] if p.startswith("./") else p + for p in listing.stdout.splitlines() if p.strip()] + hits = [n for n in names if fnmatch.fnmatch(n, pattern)] + return ExecResult(stdout="\n".join(hits) if hits else "(no matches)") + + return _run(session_id, "glob", do, pattern=pattern) + + @mcp.tool + def ls(session_id: str, path: str = ".") -> dict: + """List a directory.""" + return _run(session_id, "ls", lambda s: s.sandbox.bash(f"ls -la {shlex.quote(path)}"), + path=path) + + super().__init__(mcp) + + + # --- OpenEnv Environment ABC ---------------------------------------------------------------- + # The MCP tools above are how the trainer actually drives this environment. These three exist + # because `Environment` is abstract, and because a human poking the server with the standard + # reset/step API should get something coherent rather than an AttributeError. + + def reset(self, split: str = "train", index: int | None = None, + seed: int | None = None, episode_id: str | None = None, + **kwargs: Any) -> Observation: + """Start an episode through the Gym-style API and hand back its session id. + + The session id is in `metadata` rather than on `self` deliberately: the HTTP server discards + this instance at the end of the request, so anything kept here would be gone by the next + call. Selection is by `split`/`index` per the Task API -- a misspelled kwarg is silently + dropped by the server's permissive model, so the chosen split and index are ECHOED BACK for + the caller to assert on. + """ + result = self.get_callables()["start_episode"]( + split=split, index=index, toolsets=list(kwargs.get("toolsets") or [])or None + ) + return Observation( + done=False, + reward=None, + metadata={"session_id": result["session_id"], "prompt": result["prompt"], + "task_id": result["task_id"], "split": split, + "index": 0 if index is None else int(index)}, + ) + + def _step_impl(self, action: Action, timeout_s: float | None = None, + **kwargs: Any) -> Observation: + """Fallback for non-MCP actions -- point the caller at the tools.""" + return Observation( + done=False, + reward=None, + metadata={ + "error": f"Unknown action type: {type(action).__name__}. " + "Use ListToolsAction or CallToolAction; every capability here is an MCP tool." + }, + ) + + @property + def state(self) -> dict: + """Live session count. Per-episode state belongs to the session, not to this instance.""" + with _LOCK: + return {"live_sessions": len(_SESSIONS), "max_sessions": MAX_SESSIONS} + + # --- Task API, declared structurally (no base class to inherit) ----------------------------- + def list_splits(self) -> list[dict]: + return tasks.list_splits() + + def num_tasks(self, split: str) -> int: + return tasks.num_tasks(split) + + def list_tasks(self, split: str) -> list[dict]: + return tasks.list_tasks(split) + + def get_task(self, split: str, index: int) -> dict: + return tasks.get_task(split, index) + + def get_task_range(self, split: str, start: int | None = None, + stop: int | None = None) -> list[dict]: + return tasks.get_task_range(split, start, stop) diff --git a/04-data-agent/envs/whitebox-bash/server/sandbox.py b/04-data-agent/envs/whitebox-bash/server/sandbox.py new file mode 100644 index 0000000..25fc379 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/server/sandbox.py @@ -0,0 +1,190 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""One sandbox per episode, shared by every toolset. + +WHY ONE SANDBOX AND NOT THREE +`bash` and the SETA file tools are two views of the SAME machine, and the agent expects them to +behave that way: a file written by `write` has to be visible to `bash` in the very next call. E2B +gives both off a single sandbox -- `commands.run` is a shell and `files.*` is the filesystem it sees +-- so keeping them together is not a convenience, it is the semantics. + +Splitting them across sandboxes (or servers) would mean replicating state between them, and every +divergence would surface as an agent that wrote a file and then could not find it. That reads as a +model failure and is not one. + +THE SHELL IS NOT PERSISTENT +Each `commands.run` is a fresh process, so `cd` does not carry between calls; the tool docstring says +so, because an agent that assumes otherwise will `cd` and then be baffled. A durable working +directory is carried explicitly in `cwd`. +""" + +from __future__ import annotations + +import logging +import os +import shlex +from dataclasses import dataclass, field +from typing import Any + + +logger = logging.getLogger(__name__) + +# Everything the agent does happens under here. Kept off `/` so a stray `rm -rf` in the workdir +# cannot take the interpreter with it, and so `glob`/`grep` have a bounded root to walk. +WORKDIR = "/home/user/work" + +# Hard ceiling on any single tool result fed back to the model. +# +# 2,000 characters, NOT 8,000, and the difference decided whether the agent could take a second turn +# at all. Tool-result tokens live in TRL's `completion_ids` (masked out of the loss, and out of the +# `completions/mean_length` metric, but still occupying the budget), so they are bounded by +# `max_completion_length`. A `read` of a real data-agent CSV clipped at 8,000 chars is roughly 3,000 +# tokens and exhausted a 1,024-token completion budget on the FIRST tool result -- leaving no room to +# generate turn two. Measured: `tools/call_frequency` pinned at 1.0 and reward 0 across every step, +# which reads exactly like a model that will not engage. +# +# Dumping raw CSV was never useful anyway: the agent should compute with pandas rather than read a +# 9.8 MB file into its context, and a tighter clip pushes it that way. +MAX_OUTPUT_CHARS = int(os.environ.get("WHITE_BOX_BASH_MAX_OUTPUT_CHARS", "1200")) + + +def _clip(text: str, limit: int = MAX_OUTPUT_CHARS) -> str: + """Trim to `limit`, saying so, and keep BOTH ends. + + The head carries the command that ran and the tail carries the error; truncating either way round + loses the half that diagnoses the failure. + """ + if text is None: + return "" + text = str(text) + if len(text) <= limit: + return text + head, tail = text[: limit // 2], text[-limit // 2 :] + dropped = len(text) - limit + return f"{head}\n\n... [{dropped} characters omitted] ...\n\n{tail}" + + +@dataclass +class ExecResult: + """What a tool call actually did. `ok` is derived, never guessed.""" + + stdout: str = "" + stderr: str = "" + exit_code: int = 0 + error: str = "" + + @property + def ok(self) -> bool: + return self.exit_code == 0 and not self.error + + def render(self) -> str: + """The string the model sees. Empty success is reported as such rather than as nothing.""" + parts = [] + if self.stdout.strip(): + parts.append(_clip(self.stdout.rstrip())) + if self.stderr.strip(): + parts.append(f"[stderr]\n{_clip(self.stderr.rstrip())}") + if self.error: + parts.append(f"[error] {_clip(self.error, 1000)}") + if not self.ok and self.exit_code: + parts.append(f"[exit code {self.exit_code}]") + return "\n".join(parts) if parts else "(no output)" + + +@dataclass +class Sandbox: + """A live E2B sandbox plus the per-episode bookkeeping the grader needs. + + `calls` is the audit trail. A white-box environment's whole advantage over a black-box one is that + every tool call is observable here rather than inferred from free text afterwards, so the reward + can be a function of what the agent DID, not only of what it finally said. + """ + + handle: Any + cwd: str = WORKDIR + calls: list[dict[str, Any]] = field(default_factory=list) + submitted: str | None = None + + # --- lifecycle ----------------------------------------------------------------------------- + # The SAME prebuilt E2B template the black-box environment uses. Not optional for data-agent + # tasks: the default E2B base has no `huggingface_hub`, so the bucket staging cannot run, and + # `/workdir` is not writable by `user`, so the setup dies on `mkdir: Permission denied` before + # the agent starts. Both were measured. Sizing (cpu=2, mem=4096) is baked in at BUILD time and + # cannot be set per sandbox, which is why this is a template name and not a set of kwargs. + TEMPLATE = os.environ.get("E2B_TEMPLATE", "data-agent-opencode") + + @classmethod + def start(cls, *, timeout_s: int = 900, envs: dict[str, str] | None = None) -> "Sandbox": + # Plain `e2b`, not `e2b_code_interpreter`: with no Jupyter toolset there is no kernel to + # drive, so the code-interpreter variant would be a heavier dependency for nothing. + from e2b import Sandbox as E2BSandbox + + handle = E2BSandbox.create(template=cls.TEMPLATE, timeout=timeout_s, envs=envs or {}) + sb = cls(handle=handle) + # `-p` so a re-reset against a warm template is not an error. + sb.handle.commands.run(f"mkdir -p {shlex.quote(WORKDIR)}") + logger.info("sandbox %s up (template %s), workdir %s", + getattr(handle, "sandbox_id", "?"), cls.TEMPLATE, WORKDIR) + return sb + + def kill(self) -> None: + try: + self.handle.kill() + except Exception: + logger.warning("sandbox did not stop cleanly", exc_info=True) + + @property + def sandbox_id(self) -> str: + return str(getattr(self.handle, "sandbox_id", "")) + + # --- the three backends -------------------------------------------------------------------- + def bash(self, command: str, timeout_s: int = 120) -> ExecResult: + """A fresh shell, rooted at `self.cwd`. Not persistent -- see the module docstring.""" + try: + r = self.handle.commands.run( + f"cd {shlex.quote(self.cwd)} && {command}", timeout=timeout_s + ) + except Exception as exc: + return ExecResult(error=f"{type(exc).__name__}: {exc}", exit_code=1) + # `exit_code` of 0 is FALSY -- `getattr(r, "exit_code", 1) or 1` would read every success as a + # failure. This bug cost a full eval on the data-agent env; check for None explicitly. + code = getattr(r, "exit_code", None) + return ExecResult( + stdout=getattr(r, "stdout", "") or "", + stderr=getattr(r, "stderr", "") or "", + exit_code=0 if code is None else int(code), + ) + + def read_file(self, path: str) -> ExecResult: + try: + return ExecResult(stdout=self.handle.files.read(self._abs(path))) + except Exception as exc: + return ExecResult(error=f"{type(exc).__name__}: {exc}", exit_code=1) + + def write_file(self, path: str, content: str) -> ExecResult: + try: + self.handle.files.write(self._abs(path), content) + return ExecResult(stdout=f"wrote {len(content)} bytes to {path}") + except Exception as exc: + return ExecResult(error=f"{type(exc).__name__}: {exc}", exit_code=1) + + def _abs(self, path: str) -> str: + return path if path.startswith("/") else f"{self.cwd.rstrip('/')}/{path}" + + # --- audit --------------------------------------------------------------------------------- + def record(self, tool: str, args: dict[str, Any], result: ExecResult) -> None: + self.calls.append( + {"tool": tool, "args": args, "ok": result.ok, "exit_code": result.exit_code} + ) diff --git a/04-data-agent/envs/whitebox-bash/suite.py b/04-data-agent/envs/whitebox-bash/suite.py new file mode 100644 index 0000000..a876ef4 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/suite.py @@ -0,0 +1,242 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A generated terminal-task suite, big enough to train on. + +WHY GENERATED, AND WHAT THAT COSTS +The five hand-written tasks in `tasks.py` are enough to prove the loop runs and nothing more: four +GRPO steps over five tasks cannot show learning. This module produces a few hundred parameterised +tasks so a real run has something to move on. + +Be clear about what these are: SYNTHETIC and TEMPLATED. A policy can improve here by learning the +shape of ten templates rather than by getting better at terminals, so a gain on this suite is +evidence that the training loop works, NOT evidence of general capability. SETA's 1,376 human-written +tasks are the real target; this exists so the plumbing can be exercised before that lands. + +EVERY ANSWER IS COMPUTED IN PYTHON, NEVER BY RUNNING THE SETUP +The generator decides the data and derives the gold answer from the same values it writes into the +sandbox. Deriving it by executing the setup would make the grader agree with a buggy setup, and the +task would be unsolvable while scoring as if the model were wrong. + +TRAIN AND TEST ARE DISJOINT BY CONSTRUCTION +Both draw from the same templates but from DIFFERENT random streams (the split name seeds the RNG), +so no test task appears in training. Sharing templates is intended -- it measures whether the policy +generalises across parameters -- but sharing an instance would be leakage. +""" + +from __future__ import annotations + +import random +from typing import Callable + +from .tasks import Task + + +NAMES = ["ada", "alan", "grace", "edsger", "barbara", "linus", "ken", "brian", + "donald", "john", "guido", "bjarne", "rob", "james", "anders"] +WORDS = ["ERROR", "WARN", "FATAL", "TIMEOUT", "RETRY"] +EXTS = ["log", "txt", "csv", "json", "cfg"] + + +def _csv(rng: random.Random, n: int) -> tuple[list[tuple[str, int]], str]: + """A small CSV plus the shell that writes it. Rows carry DISTINCT scores. + + Distinct on purpose: a tie makes "which name has the highest score" ambiguous, and an ambiguous + task punishes a correct answer. The generator must not create questions with two right answers. + """ + names = rng.sample(NAMES, n) + scores = rng.sample(range(10, 100), n) + rows = list(zip(names, scores)) + body = "name,score\\n" + "\\n".join(f"{a},{b}" for a, b in rows) + "\\n" + return rows, f"printf '{body}' > data.csv" + + +# Each template returns (instruction, answer, setup, check). `check` is "" where correctness is +# entirely captured by the answer. +def _t_count_rows(rng): + rows, setup = _csv(rng, rng.randint(3, 8)) + return ("There is a file `data.csv` in your working directory. How many rows does it have, " + "excluding the header? Submit just the number.", str(len(rows)), setup, "") + + +def _t_max_name(rng): + rows, setup = _csv(rng, rng.randint(3, 8)) + return ("A file `data.csv` has columns `name` and `score`. Which name has the highest score? " + "Submit just the name.", max(rows, key=lambda r: r[1])[0], setup, "") + + +def _t_min_name(rng): + rows, setup = _csv(rng, rng.randint(3, 8)) + return ("A file `data.csv` has columns `name` and `score`. Which name has the lowest score? " + "Submit just the name.", min(rows, key=lambda r: r[1])[0], setup, "") + + +def _t_sum_col(rng): + rows, setup = _csv(rng, rng.randint(3, 6)) + return ("A file `data.csv` has columns `name` and `score`. What is the sum of all scores? " + "Submit just the number.", str(sum(s for _, s in rows)), setup, "") + + +def _t_grep_count(rng): + word = rng.choice(WORDS) + nfiles = rng.randint(2, 4) + per = [rng.randint(0, 3) for _ in range(nfiles)] + if sum(per) == 0: + per[0] = 1 + cmds = ["mkdir -p logs"] + for i, k in enumerate(per): + lines = "\\n".join([f"{word} line {j}" for j in range(k)] + ["ok"]) + cmds.append(f"printf '{lines}\\n' > logs/f{i}.log") + return (f"Several `.log` files are under `logs/`. How many lines across all of them contain the " + f"word {word}? Submit just the number.", str(sum(per)), " && ".join(cmds), "") + + +def _t_count_files(rng): + ext = rng.choice(EXTS) + n = rng.randint(2, 6) + other = rng.randint(1, 4) + other_ext = rng.choice([e for e in EXTS if e != ext]) + cmds = ["mkdir -p tree/a tree/b"] + for i in range(n): + cmds.append(f"touch tree/{'a' if i % 2 else 'b'}/f{i}.{ext}") + for i in range(other): + cmds.append(f"touch tree/a/o{i}.{other_ext}") + return (f"How many files with the extension `.{ext}` are there anywhere under `tree/`? " + f"Submit just the number.", str(n), " && ".join(cmds), "") + + +def _t_largest_file(rng): + names = rng.sample(NAMES, 4) + sizes = rng.sample(range(20, 400), 4) + cmds = ["mkdir -p blobs"] + for nm, sz in zip(names, sizes): + cmds.append(f"head -c {sz} /dev/zero | tr '\\\\0' 'x' > blobs/{nm}.dat") + biggest = names[sizes.index(max(sizes))] + return ("Which file under `blobs/` is the largest? Submit just its filename, without the " + "directory.", f"{biggest}.dat", " && ".join(cmds), "") + + +def _t_hidden_value(rng): + key = rng.choice(["port", "retries", "timeout", "workers"]) + val = rng.randint(2, 9999) + depth = rng.randint(1, 3) + path = "/".join(f"cfg{i}" for i in range(depth)) + "/app.conf" + return (f"Somewhere under the current directory there is a config file containing a line like " + f"`{key} = `. Find it and submit just the value.", str(val), + f"mkdir -p {path.rsplit('/', 1)[0]} && printf 'name = svc\\n{key} = {val}\\nmode = fast\\n' > {path}", + "") + + +def _t_fib(rng): + n = rng.randint(10, 60) + a, b = 0, 1 + for _ in range(n): + a, b = b, a + b + return (f"Write a file `solution.py` defining a function `fib(n)` returning the n-th Fibonacci " + f"number, with fib(0)=0 and fib(1)=1. Then submit the value of fib({n}).", str(a), "", + f"python3 -c \"import solution; assert solution.fib({n})=={a}\"") + + +def _t_fix_syntax(rng): + token = rng.choice(["OK", "DONE", "READY", "FINE", "PASS", "GOOD", "SET", "LIVE"]) + fn = rng.choice(["main", "run", "start", "go", "entry"]) + return (f"The file `broken.py` has a syntax error. Fix it in place so that `python3 broken.py` " + f"prints {token}, then submit the word {token}.", token, + f"printf 'def {fn}()\\n print(\"{token}\")\\n\\n{fn}()\\n' > broken.py", + f"python3 broken.py | grep -q {token}") + + +def _t_unique_count(rng): + n_unique = rng.randint(2, 5) + cats = rng.sample(["red", "blue", "green", "amber", "violet"], n_unique) + rows = [rng.choice(cats) for _ in range(rng.randint(6, 12))] + # Guarantee every category appears, or the stated answer is wrong. + rows[:n_unique] = cats + body = "colour\\n" + "\\n".join(rows) + "\\n" + return ("A file `items.csv` has a single column `colour`. How many DISTINCT colours appear? " + "Submit just the number.", str(len(set(rows))), f"printf '{body}' > items.csv", "") + + +TEMPLATES: tuple[Callable[[random.Random], tuple[str, str, str, str]], ...] = ( + _t_count_rows, _t_max_name, _t_min_name, _t_sum_col, _t_grep_count, + _t_count_files, _t_largest_file, _t_hidden_value, _t_fib, _t_fix_syntax, + _t_unique_count, +) + +# Difficulty is a property of the TEMPLATE, not of a draw, so it is stable across seeds. +_DIFFICULTY = { + _t_count_rows: "easy", _t_max_name: "easy", _t_min_name: "easy", + _t_sum_col: "easy", _t_unique_count: "medium", _t_grep_count: "medium", + _t_count_files: "medium", _t_largest_file: "medium", _t_hidden_value: "medium", + _t_fib: "hard", _t_fix_syntax: "hard", +} + + +def _signature(instruction: str, answer: str, setup: str, check: str) -> tuple[str, str, str, str]: + """What makes two tasks THE SAME task. + + Not the instruction alone: several templates ask a constant question ("how many rows does + data.csv have") and vary only the data, which is deliberate -- it forces the agent to look + instead of guessing. Identity is the whole instance. + """ + return (instruction, answer, setup, check) + + +def generate(split: str, n: int, exclude: frozenset = frozenset()) -> tuple[Task, ...]: + """Build `n` DISTINCT tasks for `split`, none of them in `exclude`. + + Rejection-sampled rather than merely seeded differently. Seeding alone makes collisions unlikely, + not impossible, and the parameter ranges here are small: measured on a first attempt, 4 of 30 + test tasks were byte-identical to training tasks and 7 train tasks were internal duplicates. Test + items the policy trained on inflate an eval and nothing downstream would report it. + + The split NAME seeds the RNG, so a given split is identical on every call and in every process -- + which it must be, because the index is the task's identity everywhere downstream. + + Args: + exclude (`frozenset`, *optional*): + Signatures (see `_signature`) that must not appear. Pass the training set when building + an eval split. + + Raises: + `RuntimeError`: If `n` distinct tasks could not be drawn. Better to fail loudly than to + return a short split that silently changes what "index 87" means. + """ + rng = random.Random(f"whitebox-bash/{split}") + seen: set = set(exclude) + out: list[Task] = [] + attempts = 0 + max_attempts = 200 * max(n, 1) + while len(out) < n and attempts < max_attempts: + template = TEMPLATES[len(out) % len(TEMPLATES)] + instruction, answer, setup, check = template(rng) + attempts += 1 + sig = _signature(instruction, answer, setup, check) + if sig in seen: + continue + seen.add(sig) + out.append(Task(instruction=instruction, answer=answer, + difficulty=_DIFFICULTY[template], setup=setup, check=check, + metadata={"template": template.__name__, "n": len(out)})) + if len(out) < n: + raise RuntimeError( + f"could only draw {len(out)} distinct tasks of {n} for split {split!r} after " + f"{attempts} attempts; widen the template parameter ranges" + ) + return tuple(out) + + +def signatures(tasks_: tuple[Task, ...]) -> frozenset: + """Signatures of a task tuple, for passing to `generate(exclude=...)`.""" + return frozenset(_signature(t.instruction, t.answer, t.setup, t.check) for t in tasks_) diff --git a/04-data-agent/envs/whitebox-bash/tasks.py b/04-data-agent/envs/whitebox-bash/tasks.py new file mode 100644 index 0000000..744d822 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/tasks.py @@ -0,0 +1,276 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tasks, and the Task API over them. + +CACHED AT MODULE LEVEL, NOT ON THE ENVIRONMENT +OpenEnv's HTTP server builds a throwaway environment instance per request and closes it in a +`finally`, so anything held on `self` dies with the request. A task cache on the instance would be +rebuilt for every `list_tasks` call; the cache therefore lives here, at module scope. + +SPLITS ARE NAMED, NOT FILTERED +`get_task("train", 12)` must mean the same task on every call and in every process, because the index +IS the task's identity everywhere downstream -- in the dataset row the trainer holds, in the eval's +common item set, in a bug report. A difficulty *filter* applied on top of one list would shift every +index after it, which is why difficulty is baked into the split name instead. +""" + +from __future__ import annotations + +import hashlib +import os +from dataclasses import asdict, dataclass, field +from typing import Any + + +# The built-in suite. Deliberately small, self-contained and checkable without network or a dataset +# download, so the environment can be smoke-tested the moment it is installed. `setup` runs before +# the agent sees the task, which is what lets a task ship its own input files. +@dataclass(frozen=True) +class Task: + """One task. + + Attributes: + instruction (`str`): + What the agent is asked to do. This is the text `reset()` returns to the trainer. + answer (`str`): + Gold answer, compared against `submit`. Never sent to the client. + difficulty (`str`): + `"easy"`, `"medium"` or `"hard"`. Part of the split name, not a filter. + setup (`str`, *optional*): + Shell run in the sandbox before the agent starts, to stage inputs. + check (`str`, *optional*): + Shell whose exit code is an extra correctness signal, for tasks whose result is a + side effect on the filesystem rather than a string. + """ + + instruction: str + answer: str + difficulty: str = "easy" + setup: str = "" + check: str = "" + metadata: dict[str, Any] = field(default_factory=dict) + + @property + def task_id(self) -> str: + """Stable identity from the instruction, so an index change is detectable.""" + return hashlib.sha1(self.instruction.encode()).hexdigest()[:12] + + def public(self) -> dict[str, Any]: + """The projection a client may see. The gold answer is withheld BY CONSTRUCTION. + + Returning the answer to the client would make every rollout trivially solvable and the reward + meaningless, and nothing downstream would report it -- the scores would simply be perfect. + """ + d = asdict(self) + d.pop("answer", None) + d.pop("check", None) + d.pop("setup", None) + d.pop("metadata", None) + d["task_id"] = self.task_id + return d + + +_BUILTIN: tuple[Task, ...] = ( + Task( + instruction=( + "There is a file `data.csv` in your working directory. How many rows does it have, " + "excluding the header? Submit just the number." + ), + answer="4", + difficulty="easy", + setup=( + "printf 'name,score\\nada,90\\nalan,85\\ngrace,95\\nedsger,88\\n' > data.csv" + ), + ), + Task( + instruction=( + "A file `data.csv` has columns `name` and `score`. Which name has the highest score? " + "Submit just the name." + ), + answer="grace", + difficulty="easy", + setup=( + "printf 'name,score\\nada,90\\nalan,85\\ngrace,95\\nedsger,88\\n' > data.csv" + ), + ), + Task( + instruction=( + "Several `.log` files are scattered under `logs/`. Count how many lines across all of " + "them contain the word ERROR. Submit just the number." + ), + answer="3", + difficulty="medium", + setup=( + "mkdir -p logs/a logs/b && " + "printf 'ok\\nERROR disk\\nok\\n' > logs/a/one.log && " + "printf 'ERROR net\\nfine\\n' > logs/b/two.log && " + "printf 'ERROR cpu\\n' > logs/b/three.log" + ), + ), + Task( + instruction=( + "Write a file `solution.py` that defines a function `fib(n)` returning the n-th " + "Fibonacci number with fib(0)=0 and fib(1)=1. Then submit the value of fib(20)." + ), + answer="6765", + difficulty="medium", + check="python3 -c \"import solution; assert solution.fib(20)==6765\"", + ), + Task( + instruction=( + "The file `broken.py` has a syntax error. Fix it in place so that `python3 broken.py` " + "prints OK, then submit the word OK." + ), + answer="OK", + difficulty="hard", + setup="printf 'def main()\\n print(\"OK\")\\n\\nmain()\\n' > broken.py", + check="python3 broken.py | grep -q OK", + ), +) + +# How many generated tasks each split holds. `train` and `test` are disjoint by construction -- the +# split name seeds the generator; see `suite.generate`. +N_TRAIN = int(os.environ.get("WHITE_BOX_BASH_N_TRAIN", "100")) +N_TEST = int(os.environ.get("WHITE_BOX_BASH_N_TEST", "30")) + + +def _generated(split: str, n: int) -> tuple[Task, ...]: + from .suite import generate + + return generate(split, n) + + +# split name -> tasks. Difficulty is IN the name; see the module docstring. +# +# `demo` is the five hand-written tasks: enough to prove the loop runs, never enough to train on. +# `train`/`test` are generated and are what a real run uses. +def _splits() -> dict[str, tuple[Task, ...]]: + from .suite import generate, signatures + + train = generate("train", N_TRAIN) + # Test EXCLUDES every training instance. Seeding the two differently is not enough -- the + # template parameter ranges are small enough to collide, and a test item the policy trained on + # inflates the eval with nothing downstream to report it. + test = generate("test", N_TEST, exclude=signatures(train)) + return { + "demo": _BUILTIN, + "train": train, + "train:easy": tuple(t for t in train if t.difficulty == "easy"), + "train:medium": tuple(t for t in train if t.difficulty == "medium"), + "train:hard": tuple(t for t in train if t.difficulty == "hard"), + "test": test, + } + + +_SPLITS: dict[str, tuple[Task, ...]] = _splits() + +_CACHE: dict[str, tuple[Task, ...]] = {} + + +# Where tasks come from. `data-agent` is the default and is the point of this environment living +# beside the two black-box ones: SAME tasks, SAME staging, SAME grader, so the only difference left +# between white box and black box is who owns the agent loop. `synthetic` is the generated suite, +# useful only for exercising the plumbing without network or HF credentials. +TASK_SOURCE = os.environ.get("WHITE_BOX_BASH_TASK_SOURCE", "data-agent") + + +def _load(split: str) -> tuple[Task, ...]: + """Resolve a split, preferring a Hub dataset when one is configured. + + `WHITE_BOX_BASH_DATASET` swaps the built-in suite for a Hub dataset without touching this file. + The built-ins stay as the fallback precisely so a fresh install is testable with no network. + """ + if split in _CACHE: + return _CACHE[split] + if TASK_SOURCE == 'harbor-frozen': + from daytona_whitebox_backend import load_frozen_tasks + tasks = load_frozen_tasks(split) + _CACHE[split] = tasks + return tasks + if TASK_SOURCE == "data-agent" and split not in ("demo",): + from .dataagent import load as _load_data_agent + + tasks = _load_data_agent(split, limit=int(os.environ.get("WHITE_BOX_BASH_LIMIT", "0"))) + _CACHE[split] = tasks + return tasks + repo = os.environ.get("WHITE_BOX_BASH_DATASET", "").strip() + if repo: + from datasets import load_dataset + + base, _, tier = split.partition(":") + rows = load_dataset(repo, split=base) + tasks = tuple( + Task( + instruction=r["instruction"], + answer=str(r.get("answer", "")), + difficulty=str(r.get("difficulty", "easy")), + setup=str(r.get("setup", "")), + check=str(r.get("check", "")), + metadata={k: r[k] for k in r.keys() if k not in + {"instruction", "answer", "difficulty", "setup", "check"}}, + ) + for r in rows + ) + if tier: + tasks = tuple(t for t in tasks if t.difficulty == tier) + else: + if split not in _SPLITS: + raise KeyError(f"unknown split {split!r}; known: {sorted(_SPLITS)}") + tasks = _SPLITS[split] + _CACHE[split] = tasks + return tasks + + +# --- the TaskProvider surface, declared structurally on the environment ------------------------ +# Data-agent split names. Tier is part of the NAME, never a filter -- a filter shifts every index +# after it and the index is the task's identity everywhere downstream. +_DATA_AGENT_SPLITS = ("train", "train:easy", "train:medium", "train:hard", "test", "eval") + + +def list_splits() -> list[dict[str, Any]]: + names = ('train', 'test') if TASK_SOURCE == 'harbor-frozen' else (_DATA_AGENT_SPLITS if TASK_SOURCE == 'data-agent' else tuple(sorted(_SPLITS))) + return [{"name": n, "type": "train" if n.startswith("train") else "test"} for n in names] + + +def num_tasks(split: str) -> int: + return len(_load(split)) + + +def list_tasks(split: str, limit: int = 200) -> list[dict[str, Any]]: + """A BOUNDED preview. `num_tasks` still reports the true total. + + Unbounded, this parses every task on every call, and the Task API calls it on a throwaway + instance -- so an agent browsing splits would rebuild the whole suite each time. + """ + return [t.public() for t in _load(split)[:limit]] + + +def get_task(split: str, index: int) -> dict[str, Any]: + tasks = _load(split) + if not 0 <= index < len(tasks): + raise IndexError(f"index {index} out of range for split {split!r} ({len(tasks)} tasks)") + return tasks[index].public() + + +def task_at(split: str, index: int) -> Task: + """The FULL task, gold answer included. Server-side only -- never routed to a client.""" + tasks = _load(split) + if not 0 <= index < len(tasks): + raise IndexError(f"index {index} out of range for split {split!r} ({len(tasks)} tasks)") + return tasks[index] + + +def get_task_range(split: str, start: int | None = None, stop: int | None = None) -> list[dict[str, Any]]: + return [t.public() for t in _load(split)[slice(start, stop)]] diff --git a/04-data-agent/envs/whitebox-bash/tests/__init__.py b/04-data-agent/envs/whitebox-bash/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/04-data-agent/envs/whitebox-bash/tests/test_surface_matches.py b/04-data-agent/envs/whitebox-bash/tests/test_surface_matches.py new file mode 100644 index 0000000..5ef53d9 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/tests/test_surface_matches.py @@ -0,0 +1,59 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The check that stops the whole thing silently breaking. + +Three descriptions of one tool surface have to agree: `tools.py` (the registry), the client's public +methods (what TRL puts in the model's schema), and the server's registered FastMCP tools (what can +actually be invoked). If they drift, the model is offered a tool nobody implements -- it calls it, +gets an error, and the run reads as a policy that cannot use tools. Nothing else would report it. +""" + +import pytest + +# Absolute, not relative: the environment directory is hyphenated and therefore not a +# Python package. It is importable as `whitebox_bash` (see train/_pypath), which is also +# the name it installs under, so tests exercise the same import path users get. +from whitebox_bash import exposed_tool_names, tool_names + + +# Server-only plumbing: minted before the agent exists and called after it stops, so they are +# deliberately NOT in the model's schema. +SERVER_ONLY = {"start_episode", "grade"} + + +@pytest.mark.parametrize("selection", [None, "all", "bash", "bash,seta", "seta"]) +def test_client_surface_matches_registry(selection): + assert exposed_tool_names(selection) == tuple(sorted(tool_names(selection))) + + +@pytest.mark.parametrize("selection", [None, "all", "bash", "bash,seta"]) +def test_server_implements_every_client_tool(selection): + from whitebox_bash.server.environment import WhiteBoxBashEnvironment + + env = WhiteBoxBashEnvironment() + served = set(env.get_callables()) - SERVER_ONLY + exposed = set(exposed_tool_names(selection)) + missing = exposed - served + assert not missing, f"client exposes tools the server does not implement: {sorted(missing)}" + + +def test_every_served_tool_is_reachable_from_some_selection(): + """The inverse: a server tool no selection exposes is dead code, and dead code rots.""" + from whitebox_bash.server.environment import WhiteBoxBashEnvironment + + env = WhiteBoxBashEnvironment() + served = set(env.get_callables()) - SERVER_ONLY + reachable = set(exposed_tool_names("all")) + assert not (served - reachable), f"unreachable server tools: {sorted(served - reachable)}" diff --git a/04-data-agent/envs/whitebox-bash/tools.py b/04-data-agent/envs/whitebox-bash/tools.py new file mode 100644 index 0000000..40f62a3 --- /dev/null +++ b/04-data-agent/envs/whitebox-bash/tools.py @@ -0,0 +1,143 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""The tool surface, declared ONCE. + +Two consumers need to agree on exactly this list: the server, which registers each tool with FastMCP +so remote callers can invoke it, and the client, whose Python methods are what TRL's +`environment_factory` introspects into the model's tool schema. A surface duplicated across those two +files would drift, and the drift is silent -- the model is offered a tool the server does not +implement, calls it, gets an error, and the run reads as a policy that cannot use tools. + +So this module is the single source of truth, and `client.py` asserts against it at import time. + +WHY TWO TOOLSETS +`bash` alone is a minimal terminal agent: one tool, one way to do anything. `seta` adds the surface +SETA evaluates against -- `read`/`write`/`edit`/`grep`/`glob`/`ls` -- and an agent that can `grep` a +repository behaves very differently from one reduced to `bash` heredocs. + +There is deliberately NO second execution model. An earlier revision also offered a persistent +Jupyter kernel, which meant two tools could do the same job under different state semantics (kernel +names persist, shell state does not) -- an easy thing for a small model to conflate, and one more +unvalidated variable in an environment that has not trained yet. + +Both toolsets run on ONE sandbox and share its filesystem, which is why they mix freely -- see +`server/sandbox.py` for why that is the semantics and not just a convenience. +""" + +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass(frozen=True) +class ToolSpec: + """One tool, as both sides must see it. + + Attributes: + name (`str`): + The tool name the model calls. Must match the client method name exactly. + params (`tuple[str, ...]`): + Parameter names, in order. Used only to check the client against this registry; the + authoritative signature is the client method, because that is what TRL introspects. + summary (`str`): + One line, shown in the tool schema. Written for the MODEL, not for us. + """ + + name: str + params: tuple[str, ...] + summary: str + + +# --- the toolsets ------------------------------------------------------------------------------ +# `bash` is always on: every toolset combination includes it, because an agent with file tools but no +# shell cannot run anything it writes, and that is never what the caller meant. +BASH: tuple[ToolSpec, ...] = ( + ToolSpec("bash", ("command",), "Run a shell command in the working directory."), +) + +# The SETA surface. Deliberately the same names SETA uses, so a task written against SETA reads the +# same here -- that is what makes its suite portable later without rewriting every task prompt. +SETA: tuple[ToolSpec, ...] = ( + ToolSpec("read", ("path",), "Read a file and return its contents."), + ToolSpec("write", ("path", "content"), "Write content to a file, creating or overwriting it."), + ToolSpec("edit", ("path", "old", "new"), "Replace the first exact occurrence of `old` with `new`."), + ToolSpec("grep", ("pattern", "path"), "Search files for a regular expression."), + ToolSpec("glob", ("pattern",), "List paths matching a glob pattern."), + ToolSpec("ls", ("path",), "List a directory."), +) + +# Always present, whatever the toolset, and named as SETA names it. +# +# Always present, because the episode needs a way to end deliberately -- without it the only +# terminator is the step cap, and a capped episode is indistinguishable from a stuck one. It is NOT +# folded into `seta` for that reason: a `bash`-only agent would otherwise have no way to finish. +# +# Named `submit_solution` rather than `submit` for SETA parity, so a task written against SETA reads +# unchanged here. The alternative -- `submit` for bash-only and `submit_solution` with `seta` -- would +# make the terminator's NAME depend on the selection, which is worse than either name alone. +SUBMIT: tuple[ToolSpec, ...] = ( + ToolSpec("submit_solution", ("answer",), "Submit the final answer and end the episode."), +) + +TOOLSETS: dict[str, tuple[ToolSpec, ...]] = { + "bash": BASH, + "seta": SETA, +} + +# What a caller gets by asking for nothing: full SETA parity. +DEFAULT_TOOLSETS: tuple[str, ...] = ("bash", "seta") + + +def resolve(toolsets: str | list[str] | None) -> tuple[str, ...]: + """Normalise a toolset selection, failing loudly on an unknown name. + + A typo'd toolset must not silently degrade the agent to a smaller surface: that shows up as a + policy that "stopped using grep", which is a very expensive thing to debug from metrics alone. + + Args: + toolsets (`str` or `list[str]`, *optional*): + Comma-separated string or list. `None` selects `DEFAULT_TOOLSETS`. `"all"` selects + everything. + + Returns: + `tuple[str, ...]`: Selected toolset names, always including `"bash"`. + """ + if toolsets is None: + names = list(DEFAULT_TOOLSETS) + elif isinstance(toolsets, str): + names = ["all"] if toolsets.strip() == "all" else [t.strip() for t in toolsets.split(",") if t.strip()] + else: + names = [str(t).strip() for t in toolsets if str(t).strip()] + if names == ["all"]: + names = list(TOOLSETS) + unknown = [n for n in names if n not in TOOLSETS] + if unknown: + raise ValueError(f"unknown toolset(s) {unknown}; known: {sorted(TOOLSETS)}") + if "bash" not in names: + names.insert(0, "bash") + return tuple(dict.fromkeys(names)) + + +def specs_for(toolsets: str | list[str] | None) -> tuple[ToolSpec, ...]: + """Every `ToolSpec` a selection exposes, `submit` included.""" + out: list[ToolSpec] = [] + for name in resolve(toolsets): + out.extend(TOOLSETS[name]) + out.extend(SUBMIT) + return tuple(out) + + +def tool_names(toolsets: str | list[str] | None) -> tuple[str, ...]: + return tuple(s.name for s in specs_for(toolsets)) diff --git a/04-data-agent/eval/README.md b/04-data-agent/eval/README.md new file mode 100644 index 0000000..c994035 --- /dev/null +++ b/04-data-agent/eval/README.md @@ -0,0 +1,9 @@ +# Evaluation + +Start with [../reproduce.md](../reproduce.md). Accepted checkpoint scores and full harness/difficulty tables are in [../results.md](../results.md). + +`eval_concurrent.py` is the asynchronous Harbor client for an already-hosted OpenEnv service and model URL; `hf/runtime/job.py` stages and serves the pinned model before invoking it. `checkpoint_evals.py` retains the historical local controller. `hf/runtime/coordinator.py` handles independent HF checkpoint evaluation Jobs. + +Every comparison is pass@1: keep the first graded attempt, including zero; retry only ungraded infrastructure failures. Publish complete cohorts after task, harness-version, checkpoint and TiTO checks. Hub concurrency defaults to 35, local to 50. + +The standalone native OpenCode evaluator is `hf/runtime/eval_opencode.py`; the native SETA evaluator is `tools/eval_whitebox_native.py`. They have different agent protocols and their standalone baselines must be labelled separately from Harbor's four-harness suite. diff --git a/04-data-agent/eval/baseline_checks.py b/04-data-agent/eval/baseline_checks.py new file mode 100644 index 0000000..fc89ced --- /dev/null +++ b/04-data-agent/eval/baseline_checks.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python +"""Baseline launch checks, using Harbor's existing task, sandbox and TiTO code.""" +from __future__ import annotations + +import argparse +import asyncio +import hashlib +import json +import os +from pathlib import Path +import re +import shutil +import time + + +def save(path, value): + path.write_text(json.dumps(value, indent=2, default=str) + "\n") + + +def verify_dataset(run): + manifest = json.loads((run / "manifest.json").read_text()) + split = Path(manifest["split"]) + names = sorted(p.name for p in (split / "tasks").iterdir() if (p / "task.toml").is_file()) + assert names == [t["name"] for t in manifest["tasks"]] and len(names) == 250 + for task in manifest["tasks"]: + for rel, expected in task["file_hashes"].items(): + if rel.endswith("/task.toml"): + expected = task["effective_task_toml_sha256"] + assert hashlib.sha256((split / rel).read_bytes()).hexdigest() == expected, rel + assert sorted(map(int, re.split(r"[,\s]+", (run / "indices.txt").read_text().strip()))) == list(range(250)) + return manifest + + +def ready(url, pid, timeout=1800): + import httpx + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + os.kill(pid, 0) + try: + response = httpx.get(url, timeout=3) + response.raise_for_status() + return response + except httpx.HTTPError: + time.sleep(2) + raise TimeoutError(f"startup timeout: {url}") + + +def preflight(run, logs): + import httpx + from openenv.harbor.client import HarborEnv + from openenv.harbor.tasks import read_instruction + + manifest = verify_dataset(run) + engine = os.environ["VLLM_URL"] + server = os.environ["SERVER_URL"] + with httpx.Client(timeout=120) as client: + models = client.get(engine + "/models").raise_for_status().json() + assert manifest["model"] in [m["id"] for m in models["data"]], models + response = client.post(engine + "/chat/completions", json={ + "model": manifest["model"], "messages": [{"role": "user", "content": "Reply PONG."}], + "max_tokens": 16, "temperature": 0.8, "top_p": 1.0, "top_k": -1, + "logprobs": True, "top_logprobs": 1, "return_token_ids": True, + }).raise_for_status().json() + choice = response["choices"][0] + assert response.get("prompt_token_ids") and choice.get("token_ids"), response + assert len(choice["token_ids"]) == len(choice["logprobs"]["content"]) + capture = f"http://127.0.0.1:{os.environ['CAPTURE_PORT']}" + local = client.get(capture + "/health").raise_for_status().json() + match = re.search(r"capture\s+:\d+\s+->\s+(https://\S+)", (logs / "openenv.log").read_text()) + assert match, "public capture URL missing" + public = match[1] + remote = client.get(public + "/health").raise_for_status().json() + assert local.get("instance") and local["instance"] == remote.get("instance") + env = HarborEnv(base_url=server) + try: + assert env.num_tasks(manifest["split"]) == 250 + refs = env.get_task_range(manifest["split"], 0, 250) + assert len(refs) == 250 + for ref, task in zip(refs, manifest["tasks"], strict=True): + task_dir = Path(manifest["split"]) / "tasks" / task["name"] + assert ref.task_id == str(task_dir) and ref.instruction == read_instruction(task_dir) + finally: + env.close() + vllm_log = Path(os.environ["VLLM_LOG"]).read_text() + assert "data_parallel_size=2" in vllm_log or "'data_parallel_size': 2" in vllm_log + assert "EngineCore_DP0" in vllm_log and "EngineCore_DP1" in vllm_log + save(logs / "preflight.json", {"task_count": 250, "file_hashes_verified": True, + "vllm_url": engine, "server_url": server, "capture_url": public, + "capture_instance": local["instance"], "dp_ranks": [0, 1], "tp": 1, + "models": models, "token_probe": response}) + print("Preflight passed: fixed 250 tasks, two DP ranks, token IDs/logprobs, capture tunnel", flush=True) + + +async def warm(run, logs): + from harbor.environments.e2b import E2BEnvironment + from harbor.models.task.task import Task + from harbor.models.trial.paths import TrialPaths + + manifest = verify_dataset(run) + task = Task(Path(manifest["split"]) / "tasks" / manifest["tasks"][0]["name"]) + directory = logs / "trials" / f"warm-template-{logs.name}" + directory.mkdir(parents=True, exist_ok=True) + env = E2BEnvironment(environment_dir=task.paths.environment_dir, + environment_name=os.environ["HARBOR_SHARED_ENV_NAME"], session_id=directory.name + "__env", + trial_paths=TrialPaths(trial_dir=directory), task_env_config=task.config.environment) + start = time.monotonic() + try: + await env.start(force_build=False) + save(logs / "template.json", {"alias": env._template_name, "elapsed_s": time.monotonic() - start}) + print("Shared sandbox template ready:", env._template_name, flush=True) + finally: + await env.stop(delete=True) + + +def audit_captures(run, logs, phase): + from openenv.harbor.models import HarborRolloutResult + from smoke_multiharness_tito import audit + + manifest = verify_dataset(run) + selected = {} + for path in sorted((logs / "traces").glob("*.jsonl")): + for line in path.read_text().splitlines(): + row = json.loads(line) + if row.get("reward") is not None and row.get("n_turns", 0) > 0: + selected.setdefault((row["harness"], row["index"], row["rep"]), row) + reports = [] + for key, row in selected.items(): + try: + result = HarborRolloutResult.model_validate_json(Path(row["capture_file"]).read_text()) + report, _ = audit(result, token_budget=131072) + reports.append({"harness": key[0], "index": key[1], "reward": row["reward"], **report}) + except Exception as exc: + reports.append({"harness": key[0], "index": key[1], "tito_pass": False, + "error": f"{type(exc).__name__}: {exc}"}) + counts = {h: sum(r["harness"] == h for r in reports) for h in manifest["harnesses"]} + passed = all(r["tito_pass"] for r in reports) + save(logs / f"{phase}_tito.json", {"counts": counts, "tito_pass": passed, "reports": reports}) + if phase == "resume": + assert all(n >= 2 for n in counts.values()), counts + else: + expected = 2 if phase == "smoke" else 250 + assert all(n == expected for n in counts.values()), counts + assert passed, "TiTO audit failed; inspect per-capture reports" + print(f"{phase}: {len(reports)} graded evaluations, all exact-token TiTO audits pass", flush=True) + + +def import_results(run, logs): + manifest = verify_dataset(run) + source = Path(os.environ["BASELINE_RESUME_FROM"]) + config = json.loads((source / "traces/eval_config.json").read_text()) + assert config["split"] == manifest["split"] + assert config["temperature"] == manifest["temperature"] and config["repeat"] == 1 + assert config["agent_step_limit"] == manifest["agent_step_limit"] + assert config["agent_timeout"] == manifest["agent_timeout_s"] + assert [arm["harness"] for arm in config["arms"]] == manifest["harnesses"] + assert all(arm["model"] == manifest["model"] for arm in config["arms"]) + assert sorted(config["indices"]) == list(range(250)) + original = json.loads(json.dumps(config)) + config["server"] = os.environ["SERVER_URL"] + for arm in config["arms"]: + arm["base_url"] = os.environ["VLLM_URL"] + shutil.copytree(source / "traces", logs / "traces") + save(logs / "traces/eval_config.json", config) + save(logs / "resume_transport_migration.json", {"source": str(source), "previous": original, + "current": config, "identity_check": "preflight.json and fixed manifest/model revision"}) + + +def cleanup(logs): + from e2b import Sandbox, SandboxQuery + own = {p.name + "__env" for p in (logs / "trials").glob("*") if p.is_dir()} + query = SandboxQuery(metadata={"environment_name": os.environ["HARBOR_SHARED_ENV_NAME"]}) + def found(): + pager = Sandbox.list(query=query) + out = [] + while pager.has_next: + out.extend(s for s in pager.next_items() if s.metadata.get("session_id") in own) + return out + removed = [] + for sandbox in found(): + Sandbox.kill(sandbox.sandbox_id) + removed.append(sandbox.sandbox_id) + remaining = [s.sandbox_id for s in found()] + save(logs / "cleanup.json", {"removed": removed, "remaining_owned_sandboxes": remaining}) + assert not remaining, remaining + print(f"Cleanup: {len(removed)} removed; zero owned sandboxes remain", flush=True) + + +def metrics(logs): + import httpx + with httpx.Client(timeout=5) as client, (logs / "metrics.jsonl").open("a", buffering=1) as file: + while True: + start = time.monotonic() + try: + response = client.get(os.environ["VLLM_URL"].removesuffix("/v1") + "/metrics") + response.raise_for_status() + value = {"metrics": response.text} + except httpx.HTTPError as exc: + value = {"error": type(exc).__name__} + file.write(json.dumps({"time": time.time(), "scrape_s": time.monotonic() - start, **value}) + "\n") + time.sleep(30) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("mode", choices=["dataset", "ready", "preflight", "warm", "smoke", "resume", "import", "final", "cleanup", "metrics"]) + parser.add_argument("--run", type=Path, required=True) + parser.add_argument("--logs", type=Path, required=True) + parser.add_argument("--url") + parser.add_argument("--pid", type=int) + args = parser.parse_args() + if args.mode == "ready": + ready(args.url, args.pid) + elif args.mode == "dataset": + verify_dataset(args.run) + elif args.mode == "preflight": + preflight(args.run, args.logs) + elif args.mode == "warm": + asyncio.run(warm(args.run, args.logs)) + elif args.mode in ("smoke", "resume", "final"): + audit_captures(args.run, args.logs, args.mode) + elif args.mode == "import": + import_results(args.run, args.logs) + elif args.mode == "cleanup": + cleanup(args.logs) + elif args.mode == "metrics": + metrics(args.logs) diff --git a/04-data-agent/eval/baseline_protocol.json b/04-data-agent/eval/baseline_protocol.json new file mode 100644 index 0000000..b89c50b --- /dev/null +++ b/04-data-agent/eval/baseline_protocol.json @@ -0,0 +1,102 @@ +{ + "model": "Qwen/Qwen3.5-2B", + "model_revision": "15852e8c16360a2fea060d615a32b45270f8a8fc", + "source_dataset": "HuggingEnvs/data-agent-harbor-test", + "split": "/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-baseline-20260914/dataset", + "harnesses": [ + "opencode", + "claude-code", + "codex", + "mini-swe-agent" + ], + "pass_k": 1, + "concurrency": 100, + "tp": 1, + "dp": 2, + "serving_context": 131072, + "max_output_tokens": 4096, + "temperature": 0.8, + "top_p": 1.0, + "top_k": -1, + "agent_step_limit": 17, + "agent_timeout_s": 600, + "schema_version": 1, + "status": "completed", + "metric": "pass@1", + "completed_evaluations": 1000, + "unique_test_tasks": 250, + "date": "2026-09-14", + "dataset_revision": "291c8e50bfa7e34135090071ecaa0686bd99d06f", + "baseline_run": "/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-baseline-20260914", + "baseline_job": 78215, + "cleanup_job": 78216, + "manifest_sha256": "c00e79efbe8157f1f20b614b0ad5f04923f51a5bd8fda69ed002e03a96fe27be", + "indices_sha256": "c95a768b09f910a2e1d2073c0e0d90edf85b8adade245e8c34b14eb615567316", + "correct": 146, + "average_pass_at_1": 0.146, + "harness_versions": { + "opencode": "1.18.31", + "claude-code": "2.1.270", + "codex": "0.154.0", + "mini-swe-agent": "2.4.6" + }, + "scores": { + "opencode": { + "correct": 27, + "evaluations": 250, + "pass_at_1": 0.108 + }, + "claude-code": { + "correct": 42, + "evaluations": 250, + "pass_at_1": 0.168 + }, + "codex": { + "correct": 41, + "evaluations": 250, + "pass_at_1": 0.164 + }, + "mini-swe-agent": { + "correct": 36, + "evaluations": 250, + "pass_at_1": 0.144 + } + }, + "difficulty": { + "easy": { + "tasks": 33, + "correct": 53, + "evaluations": 132, + "pass_at_1": 0.4015151515151515 + }, + "medium": { + "tasks": 118, + "correct": 68, + "evaluations": 472, + "pass_at_1": 0.1440677966101695 + }, + "hard": { + "tasks": 99, + "correct": 25, + "evaluations": 396, + "pass_at_1": 0.06313131313131314 + } + }, + "tito": { + "passed": 1000, + "audited": 1000, + "eligible_supervised_tokens": 2721986, + "retained_supervised_tokens": 2721986, + "scope": "capture and TRL sequence builder; not optimizer consumption" + }, + "evaluation": { + "concurrency": 100, + "server_concurrency": 100, + "sandbox_concurrency": 100, + "openenv_capacity": 128, + "reward_key": "correctness,reward", + "repeat": 1, + "max_retries": 3 + }, + "selection_policy": "first valid graded result per fixed task/harness cell; retry only ungraded failures" +} diff --git a/04-data-agent/eval/build_progress_report.py b/04-data-agent/eval/build_progress_report.py new file mode 100644 index 0000000..51c7715 --- /dev/null +++ b/04-data-agent/eval/build_progress_report.py @@ -0,0 +1,245 @@ +"""Snapshot the real resume chain and first-graded checkpoint evals without changing a run.""" +import argparse +from collections import Counter +from datetime import datetime, timezone +import hashlib +import json +import math +from pathlib import Path +from statistics import mean +import subprocess + +WORKSPACE = Path(__file__).resolve().parents[3] +LOGS = WORKSPACE / "experiments/async_grpo_harbor_data_agent/logs" +HARNESSES = ["opencode", "claude-code", "codex", "mini-swe-agent"] +NAMES = ["OpenCode", "Claude Code", "Codex", "Mini-SWE-Agent"] +LEVELS = ["easy", "medium", "hard"] + + +def read(path): + return json.loads(path.read_text()) + + +def records(path): + # A writer may currently be appending the last line. Never read half a record. + return [json.loads(s) for s in path.read_text().splitlines(keepends=True) if s.endswith("\n")] + + +def count(rows): + correct = int(sum(r["reward"] for r in rows)) + return {"graded": len(rows), "correct": correct, + "pass_at_1": correct / len(rows) if rows else None} + + +def cell(value): + if not value["graded"]: + return "pending" + return f"{value['pass_at_1']:.1%} ({value['correct']}/{value['graded']})" + + +def evaluation_paths(main): + paths, seen, upper = {}, set(), float('inf') + root = main + while root not in seen: + seen.add(root) + for p in (root / 'checkpoint-evals').glob('step-*'): + step = int(p.name.split('-')[1]) + if step > upper: + continue + if step in paths and paths[step] != p: + raise ValueError('Conflicting checkpoint evaluation ancestry') + paths[step] = p + resume = read(root / 'run_config.json').get('resume_state') or {} + if not resume.get('checkpoint'): + break + upper, root = resume['step'], Path(resume['checkpoint']).parents[2] + return paths + + +def matched_counts(selected): + common = set.intersection(*(set(rows) for rows in selected.values())) + return {name: count([rows[k] for k in sorted(common)]) for name, rows in selected.items()} + + +def collect(main): + base = LOGS / "multi4-baseline-20260914" + manifest = read(base / "manifest.json") + identities = [(t["name"], t["question_hash"], t["difficulty"]) for t in manifest["tasks"]] + expected = {(h, i) for h in HARNESSES for i in range(250)} + source = base / "job-78215/canonical_results.json" + selected = {"base": {(r["harness"], r["index"]): r for r in read(source)["selected"]}} + sources = {"base": str(source)} + final = {"base": True} + ungraded = {} + evaluations = {} + paths = evaluation_paths(main) + for step, directory in sorted(paths.items()): + if not (directory / "manifest.json").exists(): + continue + assert identities == [(t["name"], t["question_hash"], t["difficulty"]) + for t in read(directory / "manifest.json")["tasks"]] + rows, failed = {}, set() + for path in sorted(directory.glob("job-*/traces/*.jsonl")): + for row in records(path): + assert row.get("rep", 0) == 0 + key = (row["harness"], row["index"]) + assert key in expected + if row.get("reward") in (0, 1) and row.get("n_turns", 0) > 0: + if key in rows: + assert rows[key]["reward"] == row["reward"], "Graded result was replaced" + rows.setdefault(key, row) + else: + failed.add(json.dumps(row, sort_keys=True)) + if not rows: + continue + name = str(step) + selected[name] = rows + score = read(directory / "scores.json") if (directory / "scores.json").exists() else {} + final[name] = score.get("comparison_ready", False) + if final[name]: + assert len(rows) == 1000 and count(list(rows.values()))["correct"] == round(score["average_pass_at_1"] * 1000) + ungraded[name] = len(failed) + sources[name] = str(directory) + for name, rows in selected.items(): + evaluation = {**count(list(rows.values())), "final_audited": final[name], "harnesses": {}, "difficulty": {}} + for harness in HARNESSES: + values = [r for (h, _), r in rows.items() if h == harness] + evaluation["harnesses"][harness] = {**count(values), "difficulty": { + level: count([r for r in values if manifest["tasks"][r["index"]]["difficulty"] == level]) + for level in LEVELS}} + for level in LEVELS: + evaluation["difficulty"][level] = count([r for r in rows.values() + if manifest["tasks"][r["index"]]["difficulty"] == level]) + evaluations[name] = evaluation + latest = next(reversed(selected)) + matched = matched_counts(selected) + + # Retain only optimizer steps inherited through the actual checkpoint ancestry. + root, upper, metrics, provenance = main, float("inf"), {}, [] + current_job = str(read(main / "submission.json")["training"]) + while True: + config = read(root / "run_config.json") + job = str(read(root / "submission.json")["training"]) + path = root / f"job-{job}/audit/metrics.jsonl" + resume = config.get("resume_state") or {} + lower = resume.get("step", 0) + rows = [r for r in records(path) if "grad_norm" in r and lower < r["step"] <= upper] + for row in rows: + assert row["step"] not in metrics + metrics[row["step"]] = {**row, "source_job": job} + provenance.append({"job": job, "resume_step": lower, "file": str(path), + "sha256": hashlib.sha256(path.read_bytes()).hexdigest()}) + if not resume.get("checkpoint"): + break + upper, root = lower, Path(resume["checkpoint"]).parents[2] + rows = [metrics[k] for k in sorted(metrics)] + assert [r["step"] for r in rows] == list(range(1, rows[-1]["step"] + 1)) + current = [r for r in rows if r["source_job"] == current_job] + windows = [] + for lower, upper in [(54, 100), (101, 150), (151, 200), (201, 250), (251, 280), (281, 310), (311, rows[-1]["step"])]: + subset = [r for r in rows if lower <= r["step"] <= upper] + if subset: + windows.append({"from": lower, "to": subset[-1]["step"], "updates": len(subset), + "mean_reward": mean(r["reward"] for r in subset), + "nonzero_gradients": sum(r["grad_norm"] > 0 for r in subset), + "zero_group_variance_updates": sum(r.get("reward_std", -1) == 0 for r in subset), + "mean_step_seconds": mean(r["perf/step_s"] for r in subset if "perf/step_s" in r) + if any("perf/step_s" in r for r in subset) else None}) + audit = main / f"job-{current_job}/audit" + checkpoints = [] + for p in sorted((audit.parent / "run").glob("checkpoint-*")): + marker = p / "checkpoint.saved.json" + if marker.exists(): + m = read(marker) + checkpoints.append({"step": m["step"], "saved": True, "ready_marker": (p / "checkpoint.ready.json").exists()}) + monitor = read(main / "monitor/status.json") + training = {"job": current_job, "latest_step": rows[-1]["step"], "current_job_updates": len(current), + "nonzero_gradient_updates": sum(r["grad_norm"] > 0 for r in current), + "nonfinite_updates": sum(any(not math.isfinite(r[k]) for k in ["loss", "grad_norm", "ratio", "kl", "entropy"] + if isinstance(r.get(k), (int, float))) for r in current), + "max_observed_staleness": max(r.get("sample/staleness_max", 0) for r in current), + "stale_rollouts_dropped": sum(r.get("admission/stale_rollouts_dropped_total", 0) for r in current), + "stale_rows_dropped": sum(r.get("sample/dropped_stale_total", 0) for r in current), + "oversized_rows_dropped": sum(r.get("batch/dropped_oversize_total", 0) for r in current), + "last20_nonzero_gradients": sum(r["grad_norm"] > 0 for r in current[-20:]), + "last20_zero_group_variance": sum(r.get("reward_std", -1) == 0 for r in current[-20:]), + "last_nonzero_gradient_step": max(r["step"] for r in current if r["grad_norm"] > 0), + "windows": windows, "coverage": read(audit / "coverage.json"), + "tito": read(audit / "tito_summary.json"), + "audit_timestamp": datetime.fromtimestamp((audit / "tito_summary.json").stat().st_mtime, timezone.utc).isoformat(), + "checkpoints": sorted(checkpoints, key=lambda x: x["step"]), + "trackio": read(main / "trackio/status.json"), + "monitor_timestamp": monitor["checked_at"], "monitor_alerts": monitor["alerts"], + "supervisor": read(main / "supervisor/status.json"), "source_chain": provenance} + return {"snapshot_utc": datetime.now(timezone.utc).isoformat(), "evaluations": evaluations, + "training": training, "matched_cells": matched, "latest_checkpoint": latest, + "ungraded_attempts": ungraded, "sources": sources}, rows + + +def render(report): + ev, t = report["evaluations"], report["training"] + labels = ["Base" if k == "base" else f"Checkpoint {k}" + (" (partial)" if not v["final_audited"] else "") for k,v in ev.items()] + header = " | ".join(labels) + sep = " | ".join(["---:"] * len(labels)) + lines = [f"# Multi-harness progress — {report['snapshot_utc']}", "", + f"Trainer **{t['job']}** is at **step {t['latest_step']}** on hopper-prod. " + "Qwen3.5-2B; E2B; OpenCode, Claude Code, Codex and Mini-SWE-Agent. " + "LR 3e-6, eight generations per task, max staleness four. Saves every 50 steps plus hourly recovery saves; independent eval every 100 steps.", "", + "The fixed test set contains 250 tasks: 33 easy, 118 medium and 99 hard. Each complete checkpoint evaluation has 1,000 pass@1 cells. Partial scores remain provisional until coverage, token and harness-version audits pass.", "", + f"| Harness | {header} |", f"| --- | {sep} |"] + for h, name in zip(HARNESSES, NAMES): + lines.append("| " + name + " | " + " | ".join(cell(e["harnesses"][h]) for e in ev.values()) + " |") + lines += ["| Overall | " + " | ".join(cell(e) for e in ev.values()) + " |", "", + f"| Difficulty, all harnesses | {header} |", f"| --- | {sep} |"] + for level in LEVELS: + lines.append("| " + level + " | " + " | ".join(cell(e["difficulty"][level]) for e in ev.values()) + " |") + lines += ["", f"| Harness | Difficulty | {header} |", f"| --- | --- | {sep} |"] + for h, name in zip(HARNESSES, NAMES): + for level in LEVELS: + lines.append("| " + name + " | " + level + " | " + " | ".join(cell(e["harnesses"][h]["difficulty"][level]) for e in ev.values()) + " |") + lines += ["", "On the cells completed by every displayed checkpoint: " + "; ".join( + f"{k}: {cell(v)}" for k, v in report["matched_cells"].items()) + ".", "", + "Infrastructure attempts without a graded result are retained separately and may be retried. A scored zero is never replaced by a retry. Ungraded attempts by checkpoint: " + json.dumps(report["ungraded_attempts"]) + ".", "", + "## Training signal and reliability", "", + "| Steps | Mean logged reward | Nonzero-gradient updates | Mean step time |", + "| --- | ---: | ---: | ---: |"] + for w in t["windows"]: + seconds = f"{w['mean_step_seconds']:.1f}s" if w['mean_step_seconds'] is not None else "not logged" + lines.append(f"| {w['from']}–{w['to']} | {w['mean_reward']:.3f} | {w['nonzero_gradients']}/{w['updates']} | {seconds} |") + passed = sum(a["tito_pass"] for a in t["tito"].values()) + completed = sum(a["completed_results"] for a in t["tito"].values()) + retained = sum(a["retained_tokens"] for a in t["tito"].values()) + eligible = sum(a["eligible_tokens"] for a in t["tito"].values()) + lines += ["", "Reward is the unweighted mean of logged optimizer-update reward, not fixed-task pass@1. The changing task/harness mix affects it. Zero within-group reward variance produces no relative-advantage learning signal; high mean reward alone does not guarantee useful updates.", "", + f"The current continuation has {t['nonzero_gradient_updates']}/{t['current_job_updates']} nonzero-gradient updates and {t['nonfinite_updates']} nonfinite updates. " + f"The latest 20 have {t['last20_nonzero_gradients']} nonzero gradients and {t['last20_zero_group_variance']} zero-variance updates. " + f"Observed staleness is at most {t['max_observed_staleness']}; {int(t['stale_rollouts_dropped'])} whole rollouts ({int(t['stale_rows_dropped'])} rows) were rejected by the staleness policy; {int(t['oversized_rows_dropped'])} oversized rows were dropped.", "", + f"Latest saved checkpoints: {', '.join(str(c['step']) for c in t['checkpoints'])}. " + f"The audited captures pass TiTO on {passed}/{completed} completed rollouts, retaining {retained:,}/{eligible:,} eligible tokens. " + f"Audit timestamp: {t['audit_timestamp']}. This is capture/sequence-assembly evidence, not a claim that every captured rollout reached the optimizer.", "", + f"Continuation coverage: {t['coverage']['unique_tasks_covered']}/1,000 unique training tasks. The continuation started with a saved schedule cursor, so this count excludes the parent run’s earlier coverage; optimizer steps are not unique tasks.", "", + f"Monitor alerts: {json.dumps(t['monitor_alerts'])} at {t['monitor_timestamp']}. " + f"The latest nonzero-gradient update is step {t['last_nonzero_gradient_step']}; monitor alerts may precede newer updates. " + f"Offline logging healthy: {t['trackio'].get('local_ok')}; latest online Trackio sync successful: {t['trackio'].get('sync', {}).get('ok')} at {t['trackio'].get('checked_at')}.", "", + "Source paths, exact counts, resume-chain provenance and timestamps are preserved in the companion JSON snapshot."] + return "\n".join(lines) + "\n" + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--run", type=Path, default=LOGS / "multi4-long-prod-20260915") + p.add_argument("--out", type=Path, required=True) + args = p.parse_args() + report, rows = collect(args.run) + args.out.mkdir(parents=True, exist_ok=True) + (args.out / "snapshot.json").write_text(json.dumps(report, indent=2) + "\n") + (args.out / "training_metrics.json").write_text(json.dumps(rows) + "\n") + markdown = render(report) + (args.out / "REPORT.md").write_text(markdown) + (Path(__file__).resolve().parents[1] / "train/PROGRESS.md").write_text(markdown) + print(json.dumps({"snapshot": report["snapshot_utc"], "step": report["training"]["latest_step"], + "evaluations": {k: {q:v[q] for q in ["graded","correct","pass_at_1","final_audited"]} for k,v in report["evaluations"].items()}})) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/eval/checkpoint_evals.py b/04-data-agent/eval/checkpoint_evals.py new file mode 100644 index 0000000..c8656b4 --- /dev/null +++ b/04-data-agent/eval/checkpoint_evals.py @@ -0,0 +1,310 @@ +"""Prepare or queue fixed 250-task, four-harness pass@1 checkpoint evaluations. + +Default is one planning pass without job submission. --submit enables Slurm submission; +--watch continues polling. Each evaluation receives two separate inference GPUs. +""" +import argparse +import fcntl +import hashlib +import json +import math +import os +from pathlib import Path +import re +import shutil +import subprocess +import sys +import time + +PROJECT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(PROJECT / 'train')) +from checkpoint_artifacts import READY, SAVED, digest, finalize_saved, stage_model, write_json + +REPO = Path('/fsx/adithyaskolavi/projects/trl_prod') +TOOLS = REPO / 'experiments/async_grpo_harbor_data_agent/tools' +TERMINAL = {'COMPLETED', 'FAILED', 'CANCELLED', 'TIMEOUT', 'OUT_OF_MEMORY', + 'NODE_FAIL', 'PREEMPTED', 'BOOT_FAIL', 'DEADLINE', 'REVOKED'} + + +def freeze_eval_source(root, protocol): + """One immutable evaluator for all checkpoints handled by this watcher.""" + if not root.exists(): + temporary = root.with_name(root.name + '.preparing') + temporary.mkdir() + for relative in ['OpenEnv/src/openenv', 'OpenEnv/envs/harbor_env', 'trl/trl']: + shutil.copytree(REPO / relative, temporary / relative, symlinks=True, + ignore=shutil.ignore_patterns('__pycache__', '*.pyc')) + for name in ['launch_multi4_baseline.sh', 'finish_multi4_baseline.sh', 'baseline_checks.py', + 'eval_concurrent.py', 'eval_pass_at_k.py', 'harnesses_supported.txt', + 'smoke_multiharness_tito.py']: + shutil.copy2(TOOLS / name, temporary / name) + shutil.copy2(REPO / 'scripts/inference/vllm/serve_vllm_tunnel.sh', temporary / 'serve_vllm_tunnel.sh') + shutil.copy2(PROJECT / 'train/checkpoint_artifacts.py', temporary / 'checkpoint_artifacts.py') + write_json(temporary / 'protocol.json', protocol) + hashes = {str(p.relative_to(temporary)): digest(p) for p in temporary.rglob('*') if p.is_file()} + write_json(temporary / 'source_hashes.json', hashes) + temporary.rename(root) + if json.loads((root / 'protocol.json').read_text()) != protocol: + raise ValueError('Protocol changed within the checkpoint-evaluation series') + for name, expected in json.loads((root / 'source_hashes.json').read_text()).items(): + if digest(root / name) != expected: + raise ValueError(f'Frozen evaluator changed: {name}') + return root + + +def eligible(checkpoint, interval, *, include_final=False): + marker_file = checkpoint / READY if (checkpoint / READY).exists() else checkpoint / SAVED + if not marker_file.exists(): + return None + marker = json.loads(marker_file.read_text()) + step = int(checkpoint.name.removeprefix('checkpoint-')) + if marker['step'] != step or marker['checkpoint'] != str(checkpoint.resolve()): + raise ValueError(f'Invalid completion marker: {checkpoint}') + return marker if step > 0 and (step % interval == 0 or include_final and marker.get('final', False)) else None + + +def prepare(checkpoint, output, protocol): + baseline = Path(protocol['baseline_run']) + if digest(baseline / 'manifest.json') != protocol['manifest_sha256']: + raise ValueError('Baseline manifest changed') + if digest(baseline / 'indices.txt') != protocol['indices_sha256']: + raise ValueError('Baseline dispatch order changed') + if not (checkpoint / READY).exists(): + finalize_saved(checkpoint) + marker = json.loads((checkpoint / READY).read_text()) + if marker['base_model'] != protocol['model'] or marker['base_revision'] != protocol['model_revision']: + raise ValueError('Checkpoint base model/revision differs from the baseline') + output.mkdir(parents=True, exist_ok=False) + from huggingface_hub import snapshot_download + metadata = Path(snapshot_download(protocol['model'], revision=protocol['model_revision'], + cache_dir='/fsx/adithyaskolavi/.cache/huggingface/hub', + allow_patterns=['*tokenizer*', '*processor*', 'chat_template*', 'special_tokens_map.json', + 'vocab.json', 'merges.txt', 'added_tokens.json'])) + staged = stage_model(checkpoint, output / 'model', metadata) + manifest = json.loads((baseline / 'manifest.json').read_text()) + shutil.copytree(baseline / 'dataset', output / 'dataset') + shutil.copy2(baseline / 'indices.txt', output / 'indices.txt') + manifest['split'] = str((output / 'dataset').resolve()) + manifest['checkpoint'] = staged['checkpoint'] + write_json(output / 'manifest.json', manifest) + snapshot = freeze_eval_source(output.parent / 'eval-source', protocol) + (output / 'source-snapshot').symlink_to(snapshot.resolve(), target_is_directory=True) + write_json(output / 'eval_plan.json', {'step': marker['step'], 'checkpoint': str(checkpoint), + 'model_source': str(output / 'model'), 'model_alias': protocol['model'], + 'protocol': protocol, 'gpu_count': 2, 'tp': 1, 'dp': 2, 'concurrency': 100, + 'evaluations': 1000, 'metric': 'pass@1', 'interval_unit': 'optimizer steps'}) + return output + + +def submission_env(output, protocol): + env = {**os.environ, 'BASELINE_RUN': str(output), + 'BASELINE_SNAPSHOT': str(output / 'source-snapshot'), + 'EVAL_CODE_ROOT': str(output / 'source-snapshot'), + 'EVAL_MODEL_SOURCE': str(output / 'model'), 'EVAL_MODEL_ALIAS': protocol['model'], + 'OPENENV_HARBOR_AGENT_VERSIONS': json.dumps(protocol['harness_versions'])} + # A training snapshot must never accidentally become an evaluation source on restart. + env.pop('BASELINE_RESUME_FROM', None) + env.pop('EVAL_SMOKE_ONLY', None) + return env + + +def submit(output, protocol, partition, *, exclude_nodes=None): + env = submission_env(output, protocol) + key = hashlib.sha256(str(output).encode()).hexdigest()[:12] + command = ['sbatch', '--parsable', f'--partition={partition}', '--ntasks=1', '--gres=gpu:2', + '--cpus-per-task=8', '--mem=128G', '--time=03:00:00', '--export=ALL', + f'--job-name=multi4-ev-{key}', '--output=/fsx/%u/logs/%x-%j.out', + '--error=/fsx/%u/logs/%x-%j.err', + str(output / 'source-snapshot/launch_multi4_baseline.sh')] + if exclude_nodes: + command.insert(-1, f'--exclude={exclude_nodes}') + job = subprocess.check_output(command, env=env, text=True).strip().split(';')[0] + if not job.isdigit(): + raise RuntimeError(f'Unrecognized Slurm submission result: {job!r}') + # Persist the GPU ID before the dependent cleanup submission, so failure is resumable. + write_json(output / 'submission.json', {'job_id': job, 'cleanup_job_id': None}) + return submit_cleanup(output, protocol, job) + + +def submit_cleanup(output, protocol, job): + env = submission_env(output, protocol) + cleanup = subprocess.check_output(['sbatch', '--parsable', '--partition=hopper-cpu', + '--ntasks=1', '--cpus-per-task=2', '--mem=8G', '--time=00:20:00', '--export=ALL', + f'--dependency=afterany:{job}', f'--job-name=multi4-ev-clean-{job}', + '--output=/fsx/%u/logs/%x-%j.out', '--error=/fsx/%u/logs/%x-%j.err', + str(output / 'source-snapshot/finish_multi4_baseline.sh'), job], env=env, text=True).strip().split(';')[0] + if not cleanup.isdigit(): + raise RuntimeError('Cleanup submission failed; GPU ID is recorded in submission.json') + record = {'job_id': job, 'cleanup_job_id': cleanup} + write_json(output / 'submission.json', record) + return record + + +def slurm_state(job): + result = subprocess.check_output(['sacct', '-X', '-n', '-P', '-j', str(job), + '--format=JobID,State'], text=True) + states = {line.split('|')[0]: line.split('|')[1].split()[0].rstrip('+') + for line in result.splitlines() if '|' in line} + return states.get(str(job), 'UNKNOWN') + + +def slurm_nodes(job): + result = subprocess.check_output(['sacct', '-X', '-n', '-P', '-j', str(job), + '--format=JobID,NodeList'], text=True) + for line in result.splitlines(): + fields = line.split('|') + if len(fields) >= 2 and fields[0] == str(job) and fields[1] not in {'', 'None assigned', '(null)'}: + return fields[1] + raise RuntimeError('Cannot identify training node; leaving evaluation unsubmitted') + + +def trial_result_path(logs, trial_name): + """Resolve measured agent metadata through this checkpoint's retry ancestry.""" + if not trial_name or Path(trial_name).name != trial_name: + return None + logs = Path(logs).resolve() + checkpoint = logs.parent + seen = set() + while logs not in seen: + seen.add(logs) + native = logs / 'trials' / trial_name / 'result.json' + if native.is_file(): + return native + receipt = logs / 'resume_transport_migration.json' + if not receipt.is_file(): + return None + source = json.loads(receipt.read_text()).get('source') + if not source: + return None + logs = Path(source).resolve() + if logs.parent != checkpoint: + return None + return None + + +def summarize(output, job, protocol): + logs = output / f'job-{job}' + selected, ungraded_attempts = {}, 0 + for path in sorted((logs / 'traces').glob('*.jsonl')): + for line in path.read_text().splitlines(): + row = json.loads(line) + reward = row.get('reward') + if reward is None: + ungraded_attempts += 1 + continue + if not isinstance(reward, (int, float)) or not math.isfinite(reward) or row.get('n_turns', 0) <= 0: + continue + selected.setdefault((row['harness'], row['index'], row['rep']), row) + expected = {(h, i, 0) for h in protocol['harnesses'] for i in range(250)} + if set(selected) != expected: + return {'complete': False, 'graded_cells': len(selected), 'expected_cells': 1000} + manifest = json.loads((output / 'manifest.json').read_text()) + scores = {} + for h in protocol['harnesses']: + rows = [selected[h, i, 0] for i in range(250)] + score = sum(r['reward'] for r in rows) / 250 + categories = {} + for difficulty in ['easy', 'medium', 'hard']: + subset = [r for r in rows if manifest['tasks'][r['index']]['difficulty'] == difficulty] + categories[difficulty] = {'graded': len(subset), 'correct': sum(r['reward'] for r in subset), + 'pass_at_1': sum(r['reward'] for r in subset) / len(subset)} + scores[h] = {'graded': 250, 'correct': sum(r['reward'] for r in rows), 'pass_at_1': score, + 'delta_from_base': score - protocol['scores'][h]['pass_at_1'], 'difficulty': categories} + audit_path = logs / 'final_tito.json' + audit = json.loads(audit_path.read_text()) if audit_path.exists() else {} + versions = {h: {} for h in protocol['harnesses']} + for (h, _, _), row in selected.items(): + native = trial_result_path(logs, row.get('trial_name')) + data = json.loads(native.read_text()) if native is not None else {} + version = (data.get('agent_info') or {}).get('version') or 'unverified' + versions[h][version] = versions[h].get(version, 0) + 1 + pins_match = all(versions[h] == {protocol['harness_versions'][h]: 250} for h in protocol['harnesses']) + result = {'complete': True, 'graded_cells': 1000, 'metric': 'pass@1', 'harnesses': scores, + 'average_pass_at_1': sum(v['pass_at_1'] for v in scores.values()) / 4, + 'tito_pass': audit.get('tito_pass', False), 'ungraded_attempts': ungraded_attempts, + 'harness_versions': versions, 'harness_versions_match_baseline': pins_match, + 'comparison_ready': audit.get('tito_pass', False) and pins_match, + 'checkpoint': str(output / 'model'), 'job_id': job} + write_json(output / 'scores.json', result) + return result + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument('--checkpoints', type=Path, required=True) + p.add_argument('--output', type=Path, required=True) + p.add_argument('--protocol', type=Path, default=PROJECT / 'eval/baseline_protocol.json') + p.add_argument('--interval', type=int, default=100) + p.add_argument('--include-final', action='store_true', help='Also evaluate a final checkpoint off the interval') + p.add_argument('--max-active-evals', type=int, default=1) + p.add_argument('--partition', default='hopper-extra') + p.add_argument('--submit', action='store_true') + p.add_argument('--watch', action='store_true') + p.add_argument('--train-job', type=int, help='Stop watching once training and all queued evals finish') + p.add_argument('--poll-seconds', type=int, default=30) + args = p.parse_args() + args.checkpoints = args.checkpoints.resolve() + args.output = args.output.resolve() + args.protocol = args.protocol.resolve() + if args.interval <= 0 or args.max_active_evals <= 0 or not 1 <= args.poll_seconds <= 60: + p.error('Use a positive interval/cap and a poll interval between 1 and 60 seconds') + protocol = json.loads(args.protocol.read_text()) + args.output.mkdir(parents=True, exist_ok=True) + with (args.output / '.watcher.lock').open('w') as lock: + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + state_file = args.output / 'state.json' + state = json.loads(state_file.read_text()) if state_file.exists() else {} + while True: + active = 0 + for key, record in state.items(): + output = Path(record['output']) + saved = output / 'submission.json' + if saved.exists(): + record.update(json.loads(saved.read_text())) + if not record.get('job_id'): + raise RuntimeError(f'Submission interrupted for {key}; reconcile Slurm before retrying') + if args.submit and not record.get('cleanup_job_id'): + record.update(submit_cleanup(output, protocol, record['job_id'])) + status = slurm_state(record['job_id']) + record['slurm_state'] = status + if status not in TERMINAL: + active += 1 + elif status == 'COMPLETED' and not record.get('scores', {}).get('complete'): + record['scores'] = summarize(output, record['job_id'], protocol) + checkpoints = sorted((p for p in args.checkpoints.glob('checkpoint-*') + if re.fullmatch(r'checkpoint-\d+', p.name)), + key=lambda p: int(p.name.removeprefix('checkpoint-'))) + for checkpoint in checkpoints: + marker = eligible(checkpoint, args.interval, include_final=args.include_final) + if marker is None or str(checkpoint) in state: + continue + print(json.dumps({'eligible_checkpoint': str(checkpoint), 'step': marker['step'], + 'mode': 'submit' if args.submit else 'plan'}), flush=True) + if not args.submit or active >= args.max_active_evals: + continue + excluded = slurm_nodes(args.train_job) if args.train_job else None + output = args.output / f"step-{marker['step']:06d}" + prepare(checkpoint, output, protocol) + record = {'output': str(output), 'status': 'submitting'} + state[str(checkpoint)] = record + write_json(state_file, state) + record.update(submit(output, protocol, args.partition, + exclude_nodes=excluded)) + record['status'] = 'submitted' + write_json(state_file, state) + active += 1 + if args.submit: + write_json(state_file, state) + if not args.watch: + break + if args.train_job and slurm_state(args.train_job) in TERMINAL and active == 0: + pending = [p for p in checkpoints if eligible(p, args.interval, include_final=args.include_final) + and str(p) not in state] + if not pending: + print(json.dumps({'watch_complete': True, 'evaluations': len(state)}), flush=True) + break + time.sleep(args.poll_seconds) + + +if __name__ == '__main__': + main() diff --git a/04-data-agent/eval/eval_concurrent.py b/04-data-agent/eval/eval_concurrent.py new file mode 100644 index 0000000..a9ba8e9 --- /dev/null +++ b/04-data-agent/eval/eval_concurrent.py @@ -0,0 +1,748 @@ +#!/usr/bin/env python +"""Concurrent Harbor eval across several engines at once, with every trace kept. + +Supersedes `eval_pass_at_k.py`. Three things changed, each for a measured reason: + +1. ASYNCIO WITH LAYERED SEMAPHORES, not `ThreadPoolExecutor.map`. `pool.map` yields results in + SUBMISSION order, so one slow rollout stalls the result stream and every partial write queued + behind it. Here results are consumed as they complete. + +2. A CLIENT-SIDE CLOCK. `HarborRolloutResult.wall_s` starts inside the server's tool body, i.e. + AFTER the server admits the rollout, so any queueing upstream of that is invisible to it. That is + how a server that admitted only 40 concurrent rollouts looked like it was running 150: the + throughput figure was `concurrency / mean_duration`, an identity, not a measurement. We record + `submit_s` ourselves and report `queue_s = submit_s - wall_s`. + +3. CRASH-SAFE TRACES. One JSONL line per rollout, flushed and fsync'd the moment it returns, instead + of one JSON array at the end. A crash at task 240 of 250 used to lose all 239. + +The engine is named PER ROLLOUT, so one engineless `openenv harbor serve` can host every arm at once +and the task tree and sandbox templates are paid for once. Verified: `_resolve_and_run` takes the +`if llm_url:` branch and never consults the server's default engine. + + python tools/eval_concurrent.py --server http://HOST:8200 \ + --arms tools/arms.example.json --indices @tools/indices_250_shuffled.txt \ + --concurrency 64 --trace-dir logs/traces/ +""" + +from __future__ import annotations + +import argparse +import asyncio +import json +import logging +import math +import os +import random +import sys +import threading +import time +from collections import defaultdict +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +import eval_pass_at_k as epk # the brake + breaker live there; one source of truth + +logger = logging.getLogger("eval_concurrent") + +# Base URLs are deployment knowledge, not an abstraction — which is why this table lives in the +# driver and NOT in OpenEnv. The library's primitive is (llm_url, model, api_key, auth_header); a +# `--provider` enum there would immediately need a `--provider-url` escape hatch for vLLM and +# self-hosted routers and would have bought nothing. +PROVIDERS = { + "openai": ("https://api.openai.com/v1", "OPENAI_API_KEY", "Authorization"), + "anthropic": ("https://api.anthropic.com/v1", "ANTHROPIC_API_KEY", "Authorization"), + "hf": ("https://router.huggingface.co/v1", "HF_TOKEN", "Authorization"), +} + +# Account ceilings, per sandbox backend. ARGUMENTS, not constants: they are a property of whoever is +# running this, not of the code. Nothing in OpenEnv knows or should know these numbers. +SANDBOX_CAPS = {"e2b": 500, "modal": 100, "docker": 32, "daytona": 100} + +# A capacity rejection is the server saying "not now", answered BEFORE any sandbox is created. It is +# backpressure, not a failure: it must not touch the circuit breaker (which counts unscorable +# rollouts) nor the connection brake (which counts refusals). But it must be COUNTED — a sweep that +# reports 44 rollouts/min while silently retrying 300 rejections is the same headline-number trap +# this whole driver exists to avoid. +CAPACITY_MARKERS = ("CAPACITY_REACHED", "capacity", "SessionCapacityError") + +# SANDBOX-side failures. These look like connection errors and match the global brake's markers +# ("timed out", "stream"), but they are a property of ONE rollout's sandbox, not of the harbor +# server — and treating them as systemic halts a sweep whose server is answering /health in 2 ms. +# Measured at 281 concurrent: 36 of 50 failures were the E2B exec channel +# ("the stream didn't open within 'request_timeout' (60.0 seconds)"), the dominant hard-failure mode +# across 13,200 trials. They are retried per rollout and never counted against the brake. +SANDBOX_MARKERS = ( + "the stream didn't open", + "connection to sandbox", + "ended before the stream completed", + "Agent install failed", + "sandbox timeout", + "exit 137", # the installer was OOM-killed inside the sandbox +) + + +def _is_sandbox_failure(err: str) -> bool: + return any(m in err for m in SANDBOX_MARKERS) + + +class Arm: + """One (engine, model, credential) triple being evaluated.""" + + def __init__(self, name, base_url, model, api_key_env="", auth_header="Authorization", + concurrency=0, harness=""): + self.name, self.base_url, self.model = name, base_url, model + self.auth_header = auth_header or "Authorization" + self.api_key_env = api_key_env + # BY NAME, resolved here, never echoed. The key reaches the sandbox as nothing at all — the + # agent's credential is the capture session id. + self.api_key = os.environ.get(api_key_env, "") if api_key_env else "" + self.concurrency = concurrency + self.harness = harness + self.sem: asyncio.Semaphore | None = None + + @property + def ready(self) -> tuple[bool, str]: + if self.api_key_env and not self.api_key: + return False, f"${self.api_key_env} is unset" + return True, "" + + +def load_arms(path: str) -> list[Arm]: + spec = json.loads(Path(path).read_text()) + arms = [] + for a in spec: + prov = a.get("provider", "") + base, keyenv, auth = PROVIDERS.get(prov, ("", "", "Authorization")) + arms.append(Arm( + name=a["name"], + base_url=a.get("base_url") or base, + model=a["model"], + api_key_env=a.get("api_key_env", keyenv), + auth_header=a.get("auth_header", auth), + concurrency=int(a.get("concurrency", 0)), + harness=a.get("harness", ""), + )) + return arms + + +def selected_arms(args) -> list[Arm]: + """Expand a single endpoint into harness arms under the same global semaphore.""" + if args.arms: + engines = load_arms(args.arms) + else: + if not args.model: + raise ValueError("--model is required with --vllm-url") + engines = [Arm("model", args.vllm_url.rstrip("/"), args.model)] + requested = [h.strip() for h in (args.harnesses or "").replace("+", ",").split(",") if h.strip()] + if len(requested) != len(set(requested)): + raise ValueError("--harnesses contains duplicates") + out = [] + for engine in engines: + for harness in requested or [engine.harness or args.harness]: + name = f"{engine.name}--{harness}" if requested else engine.name + if not name or Path(name).name != name or name in (".", ".."): + raise ValueError("arm names must be plain filenames") + out.append(Arm(name, engine.base_url, engine.model, engine.api_key_env, + engine.auth_header, engine.concurrency, harness)) + if len({a.name for a in out}) != len(out): + raise ValueError("arm names must be unique") + return out + + +def parse_indices(spec: str) -> list[int]: + """Order-preserving. NEVER sorted. + + The dispatch order IS the sampling order, and on an easy->hard ordered suite a sorted partial run + is an easy-prefix score: one model read 0.830 over indices 0-146 and 0.553 over 147-249, so + "140/250 measured" hid which half. A fixed shuffle makes partial coverage an unbiased sample. + """ + if spec.startswith("@"): + spec = Path(spec[1:]).read_text() + out, seen = [], set() + for tok in spec.replace("\n", ",").split(","): + tok = tok.strip() + if not tok: + continue + i = int(tok) + if i not in seen: + seen.add(i) + out.append(i) + return out + + +class TraceWriter: + """One JSONL per arm, durable per line. + + `flush()` alone only moves bytes into the page cache; a node that dies still loses them. fsync is + ~0.1 ms against rollouts that take 26-612 s, so there is no reason to batch. + """ + + def __init__(self, root: Path): + root.mkdir(parents=True, exist_ok=True) + self.root, self._fh = root, {} + + def write(self, arm: str, row: dict) -> None: + fh = self._fh.get(arm) + if fh is None: + fh = self._fh[arm] = open(self.root / f"{arm}.jsonl", "a", buffering=1) + fh.write(json.dumps(row, default=str) + "\n") + fh.flush() + os.fsync(fh.fileno()) + + def close(self): + for fh in self._fh.values(): + try: + fh.close() + except Exception: + pass + + + + +# ── client pooling ────────────────────────────────────────────────────────────────────────────────── +# WHY. Measured: queue_s ~ 1.81 * C. That linear growth is a fixed-rate stage in front of the rollout, +# and it is the single thing capping 1000 rollouts at ~45 min no matter how high --concurrency goes. +# The cause is that `HarborSessionFactory.create()` calls `new_client()` per rollout and hands the +# session `owns_env=True`, so every rollout opens its own websocket and claims an env session, then +# closes it. Against a single-process uvicorn those connects serialise. +# +# `new_client()` is documented as overridable "so a caller can substitute a transport", which is the +# supported seam. One client per WORKER THREAD, reused across that thread's rollouts: a thread runs +# one rollout at a time, so this never trips `ConcurrencyError: cannot call recv while another +# coroutine is already running recv`, which is what a client shared across CONCURRENT rollouts causes. +# +# THE TRADE, stated plainly: a live client holds an env session for its whole life, so a pool of N +# clients pins N sessions on the server. That is bounded and predictable. The status quo is worse -- +# a killed client leaks its session and they accumulate until CAPACITY_REACHED, which is exactly how +# a 1000-rollout run fell from 82% graded to 18% today. +_TLS = threading.local() + +# Every pooled client, so they can be closed at the end of the run. Without this the websockets stay +# open until GC and each one holds an env session on the server -- the same leak that filled a server +# to 400/400 today and dropped a 1000-rollout run from 82% graded to 18%. A pool is only better than +# per-rollout clients if it is actually released. +_POOL_LOCK = threading.Lock() +_ALL_POOLED: list = [] + + +def close_pooled_clients() -> int: + """Close every pooled client. Returns how many were closed.""" + with _POOL_LOCK: + clients, _ALL_POOLED[:] = list(_ALL_POOLED), [] + n = 0 + for c in clients: + try: + c.really_close(); n += 1 + except Exception: # noqa: BLE001 - teardown must not mask the run's result + pass + return n + + +class _PooledClient: + """A HarborEnv whose `close()` is a no-op, so the session cannot dispose of a pooled client.""" + + def __init__(self, inner): + object.__setattr__(self, "_inner", inner) + + def __getattr__(self, name): + return getattr(object.__getattribute__(self, "_inner"), name) + + def close(self): # the session calls this after every rollout; pooling means ignoring it + return None + + def really_close(self): + object.__getattribute__(self, "_inner").close() + + +def _thread_factory(args, arm): + """One factory + one client per worker thread, built once. + + `indices=None` matters: with an explicit index list `tasks()` issues one `get_task` per index, + but with none it takes the `get_task_range` branch and fetches the whole split in ONE call. That + turns 2 HTTP round-trips per rollout into 2 per thread. + """ + from harbor_env.harness import HarborSessionFactory + + harness = arm.harness or args.harness + key = (arm.name, args.split, harness, args.sandbox) + if getattr(_TLS, "key", None) == key: + return _TLS.factory + + class _Pooled(HarborSessionFactory): + def new_client(self): + c = getattr(_TLS, "client", None) + if c is None: + from openenv.harbor.client import HarborEnv + c = _TLS.client = _PooledClient(HarborEnv( + base_url=self.server_url,max_message_size_mb=self._max_message_size_mb, + websocket_ping_interval_s=None,websocket_ping_timeout_s=None, + )) + with _POOL_LOCK: + _ALL_POOLED.append(c) + return c + + f = _Pooled( + args.server, split=args.split, harness=harness, sandbox=args.sandbox, + llm_url=arm.base_url, model=arm.model, api_key=arm.api_key, + auth_header=arm.auth_header, + # NO reward_key here. It is a per-TASK choice on a heterogeneous suite, and the factory is + # now shared across a thread's rollouts -- baking one in sends the whole preference string + # ("correctness,reward") as a literal key name, which every task refuses, and the retry then + # spins forever because a learned key can never reach a factory built once. + agent_timeout_sec=args.agent_timeout, agent_step_limit=args.agent_step_limit, + # An explicit policy also selects the same native harness settings as training. + sampling=({"temperature": args.temperature, "top_p": 1.0, "top_k": 0} + if getattr(args, "temperature", None) is not None else None), + ) + f.tasks() # one bulk fetch, on this thread, once + _TLS.key, _TLS.factory = key, f + _TLS.rows_by_index = {int(r["task_index"]): r for r in f.prompt_rows()} + return f + + +def _agent_conversations(result) -> list[dict]: + """The readable transcript, at EITHER tier. + + Not `fetch_proxy_trace()`: that is deliberately empty for an eval-tier rollout (there are no + token fields to train on), and reading it here is why an earlier sweep recorded "no trace" for + every cell. `result.conversations` is present either way. + + Agent-role only, and the LONGEST one — several harnesses emit one agent conversation PER STEP, + each carrying the accumulated history, so they are strictly nested prefixes and taking `[0]` + yields the SHORTEST. Measured: terminus-2 at n_turns=5 produced conversations of 2, 4, 6, 8 and + 10 messages, every pair a prefix of the next. + """ + convs = list(getattr(result, "conversations", None) or []) + agent = [c for c in convs if (getattr(c, "role", "") or (c.get("role") if isinstance(c, dict) else "")) == "agent"] + pool = agent or convs + if not pool: + return [] + + def msgs(c): + m = getattr(c, "messages", None) + if m is None and isinstance(c, dict): + m = c.get("messages") + return list(m or []) + + best = max(pool, key=lambda c: len(msgs(c))) + out = [] + for m in msgs(best): + if hasattr(m, "model_dump"): + out.append(m.model_dump()) + elif isinstance(m, dict): + out.append(m) + else: + out.append({"role": "unknown", "content": str(m)}) + return out + + +def _blocking_rollout(args, arm, index: int, rep: int) -> dict: + """One rollout, synchronous. Runs on a worker thread; the semaphores are held by the caller.""" + f = _thread_factory(args, arm) + row = _TLS.rows_by_index.get(int(index)) + if row is None: + raise KeyError(f"task index {index} is not in split {args.split!r}") + # Let OpenEnv select from the SAME verifier result. Discovering a key by + # rerunning the entire agent turns reward routing into an unintended resample. + f.reward_key = args.reward_key + session = f.create(row["prompt"]) + try: + session.wait_for_completion() + finally: + session.close() + result = session.result + if result is None: + # A failed transport cannot remain the thread's pooled connection. + client=getattr(_TLS,'client',None) + _TLS.client=None + if client is not None: + try: client.really_close() + except Exception: pass + # HarborSession returns a status code when its transport fails. Treating that as an + # empty successful result bypassed every infrastructure retry in the old driver. + raise RuntimeError("No rollout result returned by Harbor transport") + if getattr(result, "wall_s", None) is None and not getattr(result, "n_turns", 0): + client=getattr(_TLS,'client',None) + _TLS.client=None + if client is not None: + try: client.really_close() + except Exception: pass + raise RuntimeError(getattr(result, "error", "") or "No rollout result returned by Harbor transport") + reward = session.verify([]).env_reward + n_turns = getattr(result, "n_turns", 0) or 0 + + # A graded 0.0 from a rollout that made ZERO model calls is the verifier's missing-answer + # default, not a measurement of the policy. Recording it as 0.0 would drag every mean down with + # infrastructure noise, so it becomes None (excluded) and is reported separately. + infra_zero = "" + if n_turns == 0: + infra_zero = "0 turns: the agent never reached the proxy" + reward = None + + capture_file = "" + if getattr(args, "capture_dir", ""): + import uuid + root = Path(args.capture_dir) / arm.name + root.mkdir(parents=True, exist_ok=True) + path = root / f"{index}-{rep}-{uuid.uuid4().hex}.json" + temporary = path.with_suffix(".tmp") + temporary.write_text(json.dumps(result.model_dump(mode="json"))) + temporary.replace(path) + capture_file = str(path) + + return { + "arm": arm.name, "model": arm.model, "index": index, "rep": rep, + "harness": arm.harness or args.harness, "capture_file": capture_file, + "reward": None if reward is None else float(reward), + "infra_zero": infra_zero, + "n_turns": n_turns, + "ok": bool(getattr(result, "ok", False)), + "task_id": getattr(result, "task_id", "") or "", + "trial_name": getattr(result, "trial_name", "") or "", + # SERVER-side duration. Starts after admission, so it cannot see queueing — which is exactly + # why `submit_s` is recorded alongside it by the caller. + "wall_s": getattr(result, "wall_s", None), + # ALL keys, not just the chosen one: a headline built on one key should still let a + # reader see what else the verifier measured. + "rewards": dict(getattr(result, "rewards", None) or {}), + "reward_key": getattr(result, "reward_key", "") or "", + "findings": list(getattr(result, "findings", None) or [])[:5], + "rollout_type": getattr(result, "rollout_type", ""), + "capture_level": getattr(result, "capture_level", ""), + "n_trainable_tokens": getattr(result, "n_trainable_tokens", 0) or 0, + # What the provider made us change to be accepted at all. An arm that silently dropped + # `temperature` did not evaluate what was asked for, and a score without this is not + # reproducible. + "param_fixes": [str(p) for p in (getattr(result, "param_fixes", None) or [])], + "conversations": _agent_conversations(result), + "error": (getattr(result, "error", "") or "")[:400], + } + + +async def run_one(args, arm: Arm, index: int, rep: int, sems, counters) -> dict: + """Acquire OUTERMOST-FIRST, then run. Retry infrastructure, never a score.""" + attempt = 0 + while True: + attempt += 1 + halted = epk.SERVER_DOWN + if halted: + return {"arm": arm.name, "index": index, "rep": rep, "reward": None, + "skipped": True, "skip_reason": halted, "n_turns": 0, "ok": False} + + t0 = time.monotonic() + admitted_at = None + # Order matters: global -> server -> sandbox -> provider. Taking the SCARCEST (the provider's + # rate limit) last means a rollout never sits on a server session while it waits for one. + async with sems["global"]: + async with sems["server"]: + async with sems["sandbox"]: + async with arm.sem: + admitted_at = time.monotonic() + try: + row = await asyncio.to_thread(_blocking_rollout, args, arm, index, rep) + err = row.get("error") or "" + except Exception as exc: # noqa: BLE001 + row, err = None, str(exc) + + submit_s = time.monotonic() - t0 + + if row is not None and not err: + epk._note_connectivity(None, args.halt_after_conn_fails) + epk._note_result(arm.name, row.get("reward") is not None, args.pause_after) + row["submit_s"] = round(submit_s, 2) + row["admission_wait_s"] = round(admitted_at - t0, 2) + row["service_submit_s"] = round(time.monotonic() - admitted_at, 2) + row["service_overhead_s"] = round(max(0.0, row["service_submit_s"] - (row.get("wall_s") or 0.0)), 2) + row["queue_s"] = round(max(0.0, submit_s - (row.get("wall_s") or 0.0)), 2) + row["attempt"] = attempt + return row + + if any(m in err for m in CAPACITY_MARKERS): + counters["capacity_rejections"] += 1 + if attempt <= args.max_retries: + # Jittered, and requeued rather than spun on, so arm round-robin is preserved. + await asyncio.sleep(min(30.0, 2.0 * attempt) * (0.5 + random.random())) + continue + + # A sandbox failure is infrastructure for THIS rollout: retry it, and explicitly clear the + # brake's streak so it is never mistaken for the server going away. + if _is_sandbox_failure(err): + counters["sandbox_failures"] += 1 + epk._note_connectivity(None, args.halt_after_conn_fails) + if attempt <= args.max_retries: + await asyncio.sleep(min(30.0, 3.0 * attempt) * (0.5 + random.random())) + continue + + epk._note_connectivity(err, args.halt_after_conn_fails) + if attempt <= args.max_retries and ( + any(m in err for m in epk._CONN_MARKERS) + or "No rollout result returned by Harbor transport" in err + ): + await asyncio.sleep(min(30.0, 2.0 * attempt) * (0.5 + random.random())) + continue + + epk._note_result(arm.name, False, args.pause_after) + logger.warning("%s task %d rep %d failed: %s", arm.name, index, rep, err[:160]) + return {**(row or {}), "arm": arm.name, "model": arm.model, + "harness": arm.harness or args.harness, "index": index, "rep": rep, + "verifier_reward_on_failure": (row or {}).get("reward"), "reward": None, + "n_turns": (row or {}).get("n_turns", 0), "ok": False, "error": err[:400], "attempt": attempt, + "submit_s": round(submit_s, 2)} + + +def summarise(rows: list[dict], arms: list[Arm], indices: list[int], counters: dict) -> dict: + """Scores on the COMMON item set, with the drop matrix that explains what is missing. + + A headline score over "whatever each arm managed to grade" is not comparable across arms: they + rate-limit differently and therefore drop different tasks. And a common-set score with an + invisible drop matrix is exactly the number that hides its own cost. + """ + by_arm = defaultdict(list) + for r in rows: + by_arm[r["arm"]].append(r) + + graded_idx = {a.name: {r["index"] for r in by_arm[a.name] if r.get("reward") is not None} + for a in arms} + common = set.intersection(*graded_idx.values()) if graded_idx and all(graded_idx.values()) else set() + + out = {"n_requested": len(indices), "n_common": len(common), + "common_indices": sorted(common), "arms": {}, **counters} + for a in arms: + rs = by_arm[a.name] + graded = [r for r in rs if r.get("reward") is not None] + in_common = [r for r in graded if r["index"] in common] + walls = [r["wall_s"] for r in rs if r.get("wall_s")] + queues = [r["queue_s"] for r in rs if r.get("queue_s") is not None] + walls.sort() + out["arms"][a.name] = { + "model": a.model, + "harness": a.harness, + "attempted": len(rs), + "graded": len(graded), + # Never averaged as 0.0 — an ungraded rollout is an exclusion, not a wrong answer. + "ungraded": len(rs) - len(graded), + "infra_zero": sum(1 for r in rs if r.get("infra_zero")), + "score_common": (sum(r["reward"] for r in in_common) / len(in_common)) if in_common else None, + "score_all_graded": (sum(r["reward"] for r in graded) / len(graded)) if graded else None, + "mean_turns": (sum(r.get("n_turns", 0) for r in rs) / len(rs)) if rs else 0, + "rollout_types": dict(_tally(r.get("rollout_type", "") for r in rs)), + "wall_p50": walls[len(walls) // 2] if walls else None, + "wall_p90": walls[int(len(walls) * 0.9)] if walls else None, + "wall_max": walls[-1] if walls else None, + # If this is large the client believed a concurrency the server never granted. + "queue_p50": sorted(queues)[len(queues) // 2] if queues else None, + "param_fixes": sorted({p for r in rs for p in (r.get("param_fixes") or [])}), + # WHICH indices, not how many. + "graded_indices": sorted(graded_idx[a.name]), + "missing_from_common": sorted(set(indices) - graded_idx[a.name]), + } + return out + + +def _tally(it): + d = defaultdict(int) + for x in it: + d[x] += 1 + return d + + +async def amain(args) -> int: + arms = selected_arms(args) + for a in arms: + ok, why = a.ready + if not ok: + raise SystemExit(f"arm {a.name}: {why}") + indices = parse_indices(args.indices) + if not indices or min(indices) < 0: + raise ValueError("--indices must select non-negative task indices") + if args.concurrency < 1 or args.repeat < 1: + raise ValueError("--concurrency and --repeat must be positive") + if args.temperature is not None and (not math.isfinite(args.temperature) or args.temperature <= 0): + raise ValueError("explicit TiTO sampling requires a finite positive temperature") + manifest = { + "server": args.server, "split": args.split, "indices": indices, "repeat": args.repeat, + "arms": [{"name": a.name, "model": a.model, "base_url": a.base_url, + "harness": a.harness, "api_key_env": a.api_key_env} for a in arms], + "temperature": args.temperature, "agent_step_limit": args.agent_step_limit, + "agent_timeout": args.agent_timeout, "sandbox": args.sandbox, + "reward_key": args.reward_key, + } + if args.dry_run: + print(json.dumps({**manifest, "rollouts": len(arms) * len(indices) * args.repeat, + "global_concurrency": args.concurrency}, indent=2)) + return 0 + trace_root = Path(args.trace_dir) + trace_root.mkdir(parents=True, exist_ok=True) + manifest_path = trace_root / "eval_config.json" + if args.resume: + if not manifest_path.exists() or json.loads(manifest_path.read_text()) != manifest: + raise ValueError("--resume requires the same saved evaluation configuration") + elif manifest_path.exists() or any(trace_root.glob("*.jsonl")): + raise ValueError("trace directory already contains an evaluation; use --resume or a new directory") + else: + manifest_path.write_text(json.dumps(manifest, indent=2) + "\n") + + cap = args.sandbox_concurrency or SANDBOX_CAPS.get(args.sandbox, 64) + sems = { + "global": asyncio.Semaphore(args.concurrency), + "server": asyncio.Semaphore(args.server_concurrency or args.concurrency), + "sandbox": asyncio.Semaphore(cap), + } + for a in arms: + a.sem = asyncio.Semaphore(a.concurrency or args.concurrency) + + # ROUND-ROBIN across arms, not arm-major. Every arm then has partial coverage at any instant, so + # an interrupted sweep is salvageable and no arm's data comes systematically from a different + # period of server health. + jobs = [(a, i, rep) for rep in range(args.repeat) for i in indices for a in arms] + resumed = {} + if args.resume: + allowed = {(a.name, i, rep) for a, i, rep in jobs} + for arm in arms: + path = trace_root / f"{arm.name}.jsonl" + if not path.exists(): + continue + for line in path.read_text().splitlines(): + try: + row = json.loads(line) + except json.JSONDecodeError: + # Do not append to a truncated line: the existing valid results remain intact. + raise ValueError(f"incomplete JSONL record in {path}; repair the trailing record before resuming") + key = (row.get("arm"), row.get("index"), row.get("rep")) + reward = row.get("reward") + if key in allowed and isinstance(reward, (int, float)) and math.isfinite(reward) and row.get("n_turns", 0) > 0: + # Keep the first graded attempt, including reward=0. Never select best-of-retries. + resumed.setdefault(key, row) + jobs = [(a, i, rep) for a, i, rep in jobs if (a.name, i, rep) not in resumed] + for row in resumed.values(): + epk._note_result(row["arm"], True, args.pause_after) + limit = getattr(args, "max_new_rollouts", 0) + if limit < 0: + raise ValueError("--max-new-rollouts must be non-negative") + if limit: + jobs = jobs[:limit] + + print(f"arms {', '.join(f'{a.name}({a.model})' for a in arms)}") + print(f"tasks {len(indices)} x repeat {args.repeat} = {len(jobs)} rollouts") + print(f"concurrency global={args.concurrency} server={sems['server']._value} " + f"sandbox[{args.sandbox}]={cap} per-arm={[a.sem._value for a in arms]}") + print(f"traces {args.trace_dir}\n", flush=True) + + # `asyncio.to_thread` runs on the loop's DEFAULT executor, which Python sizes at + # min(32, cpu_count + 4). That is a hard ceiling of 32 concurrent rollouts no matter what + # --concurrency says, and it is invisible: the semaphores all admit, the tasks all start, and + # they queue inside the executor where nothing reports it. Measured: 32 live capture sessions + # against --concurrency 320. Same shape as the anyio CapacityLimiter(40) that capped the server. + import concurrent.futures + pool = concurrent.futures.ThreadPoolExecutor( + max_workers=args.concurrency, thread_name_prefix="rollout" + ) + asyncio.get_running_loop().set_default_executor(pool) + + counters = defaultdict(int) + writer = TraceWriter(Path(args.trace_dir)) + rows: list[dict] = list(resumed.values()) + t0 = time.monotonic() + try: + tasks = [asyncio.create_task(run_one(args, a, i, rep, sems, counters)) + for (a, i, rep) in jobs] + done_n = 0 + for fut in asyncio.as_completed(tasks): # COMPLETION order, not submission order + row = await fut + rows.append(row) + writer.write(row["arm"], row) # durable before anything else happens + done_n += 1 + if done_n % max(1, args.progress_every) == 0: + el = time.monotonic() - t0 + print(f" {done_n}/{len(jobs)} {done_n/el*60:.1f}/min " + f"capacity_rejections={counters['capacity_rejections']}", flush=True) + finally: + writer.close() + pool.shutdown(wait=False, cancel_futures=True) + freed = close_pooled_clients() + print(f"released {freed} pooled client(s) / env session(s)") + + el = time.monotonic() - t0 + summary = summarise(rows, arms, indices, dict(counters)) + # MEASURED from real completions, never `concurrency / mean_duration`. + summary["elapsed_s"] = round(el, 1) + summary["throughput_per_min"] = round((len(rows) - len(resumed)) / max(el, 1e-9) * 60, 2) + summary["resumed_rollouts"] = len(resumed) + summary["repeat"] = args.repeat + summary["expected_rollouts"] = len(indices) * len(arms) * args.repeat + summary["coverage_complete"] = all( + s["graded"] == len(indices) * args.repeat for s in summary["arms"].values() + ) + if args.repeat == 1: + for s in summary["arms"].values(): + s["pass_at_1"] = s["score_all_graded"] + s["pass_at_1_full_set"] = s["pass_at_1"] if s["graded"] == len(indices) else None + Path(args.out).parent.mkdir(parents=True, exist_ok=True) + Path(args.out).write_text(json.dumps({"summary": summary}, indent=2, default=str)) + + print(f"\n{'='*78}\nelapsed {el/60:.1f} min measured throughput {summary['throughput_per_min']}/min") + print(f"common item set: {summary['n_common']}/{summary['n_requested']}") + for name, s in summary["arms"].items(): + sc = "None" if s["score_common"] is None else f"{s['score_common']:.4f}" + print(f" {name:12s} common={sc} graded={s['graded']}/{s['attempted']} " + f"ungraded={s['ungraded']} turns={s['mean_turns']:.1f} " + f"wall p50/p90/max={s['wall_p50']}/{s['wall_p90']}/{s['wall_max']} " + f"queue_p50={s['queue_p50']} types={s['rollout_types']}") + if s["param_fixes"]: + print(f" param_fixes={s['param_fixes']}") + if summary["n_common"] < 0.9 * summary["n_requested"]: + print("\n!! common set < 90% of requested — these scores are NOT publishable as a comparison") + print(f"summary -> {args.out}") + return 2 if args.require_complete and not summary["coverage_complete"] else 0 + + +def main() -> int: + p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument("--server", required=True, help="a running `openenv harbor serve` (may be engineless)") + engine = p.add_mutually_exclusive_group(required=True) + engine.add_argument("--arms", help="JSON file of engines to evaluate") + engine.add_argument("--vllm-url", help="OpenAI-compatible /v1 endpoint; use with --model") + p.add_argument("--model", default="", help="exact model ID advertised by the endpoint") + p.add_argument("--split", default="AdithyaSK/data_agent_rl_environment_eval") + p.add_argument("--indices", default="@tools/indices_250_shuffled.txt", + help="comma-separated, or @file. ORDER IS PRESERVED and must not be sorted.") + p.add_argument("--repeat", type=int, default=1, help="samples per (arm, task)") + p.add_argument("--harness", default="mini-swe-agent") + p.add_argument("--harnesses", default="", help="comma- or plus-separated harness matrix; shares one global concurrency limit") + p.add_argument("--temperature", type=float, default=None, + help="explicit full-vocabulary TiTO policy, also selecting training harness settings") + p.add_argument("--reward-key", default="", # comma-separated preference order + help="which verifier key is the score. REQUIRED for a multi-reward suite; " + "without it every rollout is refused rather than silently combined.") + p.add_argument("--sandbox", default="e2b") + p.add_argument("--concurrency", type=int, default=32) + p.add_argument("--server-concurrency", type=int, default=0, help="0 = same as --concurrency") + p.add_argument("--sandbox-concurrency", type=int, default=0, + help=f"0 = the account default for --sandbox {SANDBOX_CAPS}") + p.add_argument("--agent-timeout", type=float, default=600.0) + p.add_argument("--agent-step-limit", type=int, default=20) + p.add_argument("--max-retries", type=int, default=3, help="infrastructure only; never a score") + p.add_argument("--halt-after-conn-fails", type=int, default=15) + p.add_argument("--pause-after", type=int, default=8) + p.add_argument("--trace-dir", default="logs/traces") + p.add_argument("--capture-dir", default="", help="retain full Harbor results, including exact token/logprob capture") + p.add_argument("--resume", action="store_true", help="reuse the first graded result for each existing cell") + p.add_argument("--require-complete", action="store_true", help="exit nonzero unless every requested cell is graded") + p.add_argument("--dry-run", action="store_true", help="print the evaluation matrix without connecting or creating sandboxes") + p.add_argument("--max-new-rollouts", type=int, default=0, + help="run only this many new cells, preserving the full manifest for --resume; 0 = all") + p.add_argument("--out", default="logs/eval_concurrent.json") + p.add_argument("--progress-every", type=int, default=10) + args = p.parse_args() + logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") + if args.repeat > 1: + # A k-sample number at temperature 0 is k copies of one trajectory. The engine must be + # sampling, or pass@k is overstated while looking healthy. + logger.warning("--repeat %d assumes the arm samples (temperature > 0); pass@k is " + "meaningless against a greedy endpoint", args.repeat) + return asyncio.run(amain(args)) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/04-data-agent/eval/eval_env.py b/04-data-agent/eval/eval_env.py new file mode 100644 index 0000000..529295c --- /dev/null +++ b/04-data-agent/eval/eval_env.py @@ -0,0 +1,301 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Evaluate a model through either data-agent environment, keeping every trace. + + python eval_env.py --env opencode --server http://127.0.0.1:8200 \ + --llm-url $VLLM/v1 --model Qwen/Qwen3.5-2B --k 4 + +WHAT THIS REPORTS, AND WHY IN THIS ORDER +The exclusion count comes BEFORE the score, because a reward of `None` is not a zero. One suite once +emitted a null reward, the type rejected it, and 86 of 250 tasks vanished from scoring while the run +printed clean numbers over a third of the data. An ungraded rollout means the infrastructure failed; +a zero means the policy was wrong, and collapsing the two flatters or damns a model for no reason. + +It also reports WHICH tasks were measured, not just how many. Coverage as a bare fraction hides +whether the missing items are random or a contiguous block: on an easy-to-hard ordered suite a 9B +once scored 0.830 on indices 0-146 and 0.553 on 147-249, so "140/250" was really an easy-prefix +score. Dispatch therefore runs in a FIXED SHUFFLED order, seeded, so that a partial run is an +unbiased sample of the split rather than its beginning. + +And it reports mean turns next to the score, which diagnoses an agent faster than the score does: +9-11 turns is working, under 7 means it quit before reading the data, over 25 means it thrashed. Two +models can score alike for opposite reasons. + +Records are written incrementally. A run that is killed keeps everything it had finished. +""" + +from __future__ import annotations + +import argparse +import json +import pathlib +import random +import statistics as st +import sys +import threading +import time +from concurrent.futures import ThreadPoolExecutor, as_completed +from typing import Any + + +def _opencode_client(server: str): + from data_agent_env import DataAgentEnv + + return DataAgentEnv(server) + + +def _harbor_client(server: str): + from openenv.harbor.client import HarborEnv + + return HarborEnv(server) + + +def _run_opencode(client, *, split, index, llm_url, model, sandbox, step_limit, timeout_s): + r = client.run_rollout( + split=split, index=index, llm_url=llm_url, model=model, sandbox=sandbox, + agent_step_limit=step_limit, agent_timeout_s=timeout_s, + # An EVAL rollout does not need token ids -- a text-only endpoint is a fine eval backend, and + # refusing it would rule out every hosted provider. Ids still come back when the engine has + # them, which is why the traces below are usable for training diagnostics too. + require_tokens=False, + ) + turns = [ + { + "turn": t.turn, + "n_prompt_ids": len(t.prompt_token_ids), + "n_completion_ids": len(t.completion_token_ids), + "n_logps": len(t.per_token_logps), + "trainable": t.trainable, + "finish_reason": t.finish_reason, + "n_tool_calls": len(t.tool_calls), + "text": t.text, + "prompt_token_ids": list(t.prompt_token_ids), + "completion_token_ids": list(t.completion_token_ids), + "per_token_logps": list(t.per_token_logps), + } + for t in r.turns + ] + return { + "reward": r.reward, + "correctness": r.correctness, + "answer": r.answer, + "answer_source": r.answer_source, + "graded_by": r.graded_by, + "rollout_type": r.rollout_type, + "n_turns": len(r.turns), + "n_tool_calls": r.n_tool_calls, + "timed_out": r.timed_out, + "findings": (r.metadata or {}).get("capture_findings") or [], + "error": (r.metadata or {}).get("error"), + "metadata": r.metadata, + }, turns + + +def _run_harbor(client, *, split, index, llm_url, model, sandbox, step_limit, timeout_s): + r = client.run_rollout( + split=split, task_index=index, harness="opencode", sandbox=sandbox, + llm_url=llm_url, model=model, agent_step_limit=step_limit, + agent_timeout_sec=timeout_s, + ) + turns = [ + { + "turn": getattr(t, "turn", i), + "n_prompt_ids": len(getattr(t, "prompt_token_ids", []) or []), + "n_completion_ids": len(getattr(t, "completion_token_ids", []) or []), + "n_logps": len(getattr(t, "per_token_logps", []) or []), + "trainable": bool(getattr(t, "trainable", False)), + "finish_reason": getattr(t, "finish_reason", None), + "prompt_token_ids": list(getattr(t, "prompt_token_ids", []) or []), + "completion_token_ids": list(getattr(t, "completion_token_ids", []) or []), + "per_token_logps": list(getattr(t, "per_token_logps", []) or []), + } + for i, t in enumerate(getattr(r, "turns", []) or []) + ] + return { + "reward": r.reward, + "correctness": r.reward, # harbor grades with the task's own verifier; reward IS the grade + "answer": None, + "answer_source": "harbor-verifier", + "graded_by": "task", + "rollout_type": getattr(r, "rollout_type", ""), + "n_turns": getattr(r, "n_turns", len(turns)), + "n_roots": getattr(r, "n_roots", None), + "n_tool_calls": None, + "timed_out": None, + "findings": list(getattr(r, "findings", []) or []), + "error": getattr(r, "error", None), + "metadata": {"trial_name": getattr(r, "trial_name", None), + "capture_level": getattr(r, "capture_level", None)}, + }, turns + + +ADAPTERS = { + "opencode": (_opencode_client, _run_opencode), + "harbor": (_harbor_client, _run_harbor), +} + + +def main() -> int: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--env", choices=sorted(ADAPTERS), required=True) + p.add_argument("--server", required=True) + p.add_argument("--llm-url", required=True) + p.add_argument("--model", required=True) + p.add_argument("--split", default="eval") + p.add_argument("--k", type=int, default=4, help="independent passes per task") + p.add_argument("--n-tasks", type=int, default=0, help="0 = the whole split") + p.add_argument("--seed", type=int, default=0, help="fixes the shuffled dispatch order") + p.add_argument("--sandbox", default="e2b") + p.add_argument("--step-limit", type=int, default=10) + p.add_argument("--timeout-s", type=float, default=600.0) + # Well under the ~200 where the capture proxy's /health starves and the 320 where it crashed. + p.add_argument("--concurrency", type=int, default=24) + p.add_argument("--out", default="") + args = p.parse_args() + + make_client, run_one = ADAPTERS[args.env] + stamp = time.strftime("%Y%m%d-%H%M%S") + out = pathlib.Path(args.out or f"logs/eval-{args.env}-{stamp}") + (out / "traces").mkdir(parents=True, exist_ok=True) + records_path = out / "records.jsonl" + + probe = make_client(args.server) + try: + n_total = probe.num_tasks(args.split) + finally: + probe.close() + n = args.n_tasks or n_total + + # FIXED SHUFFLED ORDER. A partial run must be an unbiased sample of the split, not its prefix. + order = list(range(n_total)) + random.Random(args.seed).shuffle(order) + indices = order[:n] + + # DISPATCHED IN PASSES, WITH A BARRIER BETWEEN THEM, and that is not for tidiness. + # + # E2B builds a template per task on first use, and CONCURRENT FIRST USE RACES THAT BUILD: the + # losers get `404: tag 'default' does not exist for template ...`. Running all k samples of a task + # at once is exactly that race -- k-1 of them fail in ~7 s and the whole split burns without + # scoring anything. Measured at concurrency 64 on a cold suite. + # + # Pass 0 therefore warms every template, and only then do passes 1..k-1 run. Same total work, no + # intra-task race, and it has a second benefit: after pass 0 there is already a complete pass@1 + # over the split, so a run killed partway still yields a usable number. + passes = [[(i, s) for i in indices] for s in range(args.k)] + print(f"env {args.env} server {args.server}") + print(f"model {args.model} via {args.llm_url}") + print(f"split {args.split}: {n} of {n_total} tasks, k={args.k} -> {n * args.k} rollouts" + f" in {args.k} pass(es)") + print(f"dispatch shuffled, seed {args.seed}; first indices {indices[:8]}") + print(f"out {out}") + print(f"concurrency {args.concurrency}\n", flush=True) + + lock = threading.Lock() + done = [0] + started = time.time() + + def work(job): + index, sample = job + client = make_client(args.server) # one client per rollout; a shared one cannot multiplex + rec: dict[str, Any] = {"task_index": index, "sample": sample, "model": args.model, + "env": args.env, "split": args.split} + t0 = time.time() + try: + result, turns = run_one( + client, split=args.split, index=index, llm_url=args.llm_url, model=args.model, + sandbox=args.sandbox, step_limit=args.step_limit, timeout_s=args.timeout_s, + ) + rec.update(result) + (out / "traces" / f"{index:04d}-{sample}.json").write_text( + json.dumps({"record": {k: v for k, v in rec.items() if k != "metadata"}, + "turns": turns}, indent=None) + ) + except Exception as exc: # noqa: BLE001 -- one bad rollout must not end the eval + rec["error"] = f"{type(exc).__name__}: {exc}" + rec["reward"] = None + finally: + try: + client.close() + except Exception: + pass + rec["seconds"] = round(time.time() - t0, 1) + with lock: + with open(records_path, "a") as fh: + fh.write(json.dumps(rec) + "\n") + done[0] += 1 + if done[0] % 10 == 0 or done[0] == n * args.k: + rate = done[0] / max(1e-9, time.time() - started) * 60 + print(f" {done[0]}/{n * args.k} rollouts ({rate:.1f}/min)", flush=True) + return rec + + for pass_no, jobs_in_pass in enumerate(passes): + note = " (warms every E2B template; later passes cannot race it)" if pass_no == 0 else "" + print(f"\n -- pass {pass_no + 1}/{len(passes)}{note}", flush=True) + with ThreadPoolExecutor(max_workers=args.concurrency) as pool: + futures = [pool.submit(work, j) for j in jobs_in_pass] + for f in as_completed(futures): + f.result() + + return report(records_path, out) + + +def report(records_path: pathlib.Path, out: pathlib.Path) -> int: + recs = [json.loads(l) for l in open(records_path) if l.strip()] + # EXCLUSIONS FIRST. reward=None is UNGRADED, never a zero. + excluded = [r for r in recs if r.get("reward") is None] + scored = [r for r in recs if r.get("reward") is not None] + print("\n" + "=" * 78) + print(f" rollouts {len(recs)}") + print(f" EXCLUDED {len(excluded)} ({len(excluded)/max(1,len(recs)):.1%}) -- ungraded, not zero") + reasons: dict[str, int] = {} + for r in excluded: + key = str(r.get("error") or "unknown")[:70] + reasons[key] = reasons.get(key, 0) + 1 + for k, v in sorted(reasons.items(), key=lambda kv: -kv[1])[:6]: + print(f" {v:4d} {k}") + if not scored: + print("\n nothing was graded; no score to report.") + return 1 + + by_task: dict[int, list[float]] = {} + for r in scored: + by_task.setdefault(r["task_index"], []).append(float(r["reward"])) + idx = sorted(by_task) + pass1 = st.mean([st.mean(v) for v in by_task.values()]) + passk = st.mean([1.0 if max(v) > 0 else 0.0 for v in by_task.values()]) + print(f"\n tasks measured {len(by_task)} indices {idx[0]}..{idx[-1]} " + f"(shuffled dispatch, so this is an unbiased sample)") + print(f" pass@1 (mean) {pass1:.4f}") + print(f" pass@k (any>0) {passk:.4f}") + turns = [r["n_turns"] for r in scored if r.get("n_turns") is not None] + if turns: + print(f" mean turns {st.mean(turns):.2f} " + f"(9-11 working, <7 quit early, >25 thrashing)") + tc = [r["n_tool_calls"] for r in scored if r.get("n_tool_calls") is not None] + if tc: + print(f" mean tool calls {st.mean(tc):.2f}") + noans = sum(1 for r in scored if not r.get("answer") and r.get("answer_source") in (None, "none")) + if noans: + print(f" no answer filed {noans}/{len(scored)} ({noans/len(scored):.0%})") + tiers = {r.get("rollout_type") for r in scored} + print(f" rollout_type {sorted(t for t in tiers if t)}") + finds: dict[str, int] = {} + for r in recs: + for f in r.get("findings") or []: + key = f.split(":")[0][:60] + finds[key] = finds.get(key, 0) + 1 + if finds: + print(" capture findings") + for k, v in sorted(finds.items(), key=lambda kv: -kv[1])[:5]: + print(f" {v:4d} {k}") + + size = sum(f.stat().st_size for f in out.rglob("*") if f.is_file()) + print(f"\n traces {len(list((out/'traces').glob('*.json')))} files, " + f"{size/1e6:.1f} MB under {out}") + print("=" * 78) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/04-data-agent/eval/eval_pass_at_k.py b/04-data-agent/eval/eval_pass_at_k.py new file mode 100644 index 0000000..3dc40ad --- /dev/null +++ b/04-data-agent/eval/eval_pass_at_k.py @@ -0,0 +1,644 @@ +"""pass@k on the DataAgent eval suite, through the same OpenEnv x Harbor server. + +Mirrors `experiments/rollout_control/harbor_trl/eval_harbor.py` so the numbers are comparable to the +sync run's, with three differences that matter: + + * **Same harness as training.** That script used `agent="bash"` because the sync run TRAINED on the + bash env. We train on mini-swe-agent, so evaluating with bash would measure a different agent than + the one being optimised. + * **No second server.** The engine arrives per request, so this job stands up its own vLLM and names + it; the dataset server is shared with the training run. The engine is deliberately FLAGLESS (no + `--return-tokens-as-token-ids`, no `--logprobs-mode`), so the server probes it as `eval` tier and + captures no tokens — there is nothing to train on here and pretending otherwise costs memory. + * **Infra failures are excluded, not scored 0.** `data_agent_reward` returns `None` when the verifier + never ran, which is precisely the `infra_failed` exclusion the sync script hand-rolled: a dead + sandbox is missing data, and counting it as a wrong answer silently deflates pass@k. + +`k > 1` requires temperature > 0. At temperature 0 every sample of a task is identical and pass@k +collapses to pass@1 while looking like a real k-sample number. +""" + +from __future__ import annotations + +import argparse +import json +import logging +import os +import statistics +import sys +import threading +import time +from collections import Counter, defaultdict +from concurrent.futures import ThreadPoolExecutor +from pathlib import Path + +HERE = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(HERE / "src")) +sys.path.insert(0, str(HERE.parents[1] / "OpenEnv" / "src")) +sys.path.insert(0, str(HERE.parents[1] / "OpenEnv" / "envs")) + +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") +logger = logging.getLogger("eval") + +EVAL_SPLIT = "AdithyaSK/data_agent_rl_environment_eval" + + +def _supported_harnesses() -> list[str]: + """The harnesses this CLI will run, read from tools/harnesses_supported.txt. + + A list, not a hardcoded tuple, so a support decision is made once and every stage inherits it. + Excluded harnesses are absent rather than silently accepted: `goose` cannot be budgeted (6 turns + on one rollout, 347 on another of the same task), `openclaw`'s CLI exits 1 without making a model + call, and `kimi-cli` produced no reward at all. Offering them in the CLI would invite a sweep + whose numbers are not comparable to the others. + """ + path = Path(__file__).resolve().parent / "harnesses_supported.txt" + names = [] + for line in path.read_text().splitlines(): + name = line.split("#", 1)[0].strip() + if name: + names.append(name) + if not names: + raise SystemExit(f"{path} lists no harnesses") + return names + + +SUPPORTED_HARNESSES = _supported_harnesses() + + +# ── per-harness circuit breaker ───────────────────────────────────────────────────────────────────── +# A harness that cannot produce a graded rollout at all should stop consuming sandboxes. The threshold +# counts CONSECUTIVE UNSCORABLE results only — never low rewards. A harness scoring 0.0 on hard tasks is +# working correctly and telling us something; one whose verifier never runs is broken, and the two must +# not be confused or the sweep would silently drop the weakest models instead of the broken harnesses. +_state_lock = threading.Lock() +_streak: dict[str, int] = defaultdict(int) +_graded: dict[str, int] = defaultdict(int) +DISABLED: dict[str, str] = {} + +# A GLOBAL brake, separate from the per-harness one. The per-harness breaker cannot see a systemic +# failure: when the SERVER stops accepting connections every harness fails together, each has already +# graded something, so none looks broken and the sweep burns its whole budget on refusals. That +# happened — 96 concurrent clients against a single-process server drove it to 3132 open fds until it +# stopped responding, and ~58% of 2800 rollouts came back `[Errno 111] Connection refused` while the +# per-harness breaker stayed correctly silent. +_conn_fail_streak = 0 +SERVER_DOWN: str = "" +# "timed out" belongs here: a server can be LISTENING but wedged, accepting connections and never +# answering. That produces httpx.ReadTimeout, not a refusal, and without this marker the brake stayed +# silent through exactly that phase while the sweep burned rollouts. Measured on one run: 157 refusals, +# 88 timeouts and 24 resets, and only the first group was being counted. +_CONN_MARKERS = ( + "No rollout result returned by Harbor transport", + "Connection refused", + "Errno 111", + "Connect call failed", + "ConnectionResetError", + "Errno 104", + "timed out", + "ReadTimeout", + "ConnectTimeout", +) + + +def _note_connectivity(err: str | None, threshold: int) -> None: + """Track consecutive connection-level failures across ALL harnesses.""" + global _conn_fail_streak, SERVER_DOWN + with _state_lock: + if err and any(m in err for m in _CONN_MARKERS): + _conn_fail_streak += 1 + if _conn_fail_streak >= threshold and not SERVER_DOWN: + SERVER_DOWN = ( + f"{_conn_fail_streak} consecutive connection failures — the server is refusing " + f"connections; stopping so the remaining rollouts are not wasted" + ) + logger.error("HALTING SWEEP: %s", SERVER_DOWN) + else: + _conn_fail_streak = 0 + + +def _note_result(harness: str, graded: bool, threshold: int) -> None: + with _state_lock: + if graded: + _streak[harness] = 0 + _graded[harness] += 1 + return + _streak[harness] += 1 + # Never disable a harness that has already proved it can be graded: a later run of unscorable + # results is a flaky sandbox, not a broken integration. + if _graded[harness] == 0 and _streak[harness] >= threshold and harness not in DISABLED: + DISABLED[harness] = ( + f"{_streak[harness]} consecutive unscorable rollouts and never once graded" + ) + logger.warning( + "PAUSING %s — %s; its remaining rollouts are skipped", harness, DISABLED[harness] + ) + + +def _as_plain(m): + """A message as plain JSON. Pydantic models and dicts both arrive here depending on the client.""" + if hasattr(m, "model_dump"): + return m.model_dump() + if isinstance(m, dict): + return m + return {"role": "unknown", "content": str(m)} + + +def one_sample(args, harness: str, index: int, rep: int) -> dict: + """One rollout of one task. `reward=None` marks it as infra, not as wrong.""" + from harbor_env.harness import HarborSessionFactory + + with _state_lock: + halted = SERVER_DOWN + if halted: + return {"harness": harness, "index": index, "rep": rep, "reward": None, "n_turns": 0, + "ok": False, "skipped": True, "skip_reason": halted, "messages": []} + + with _state_lock: + paused = DISABLED.get(harness) + if paused: + # Skipped, not failed: recorded distinctly so the summary can say a harness was paused rather + # than implying it was measured and scored nothing. + return {"harness": harness, "index": index, "rep": rep, "reward": None, + "n_turns": 0, "ok": False, "skipped": True, "skip_reason": paused, "messages": []} + + f = HarborSessionFactory( + args.server, + split=args.split, + harness=harness, + sandbox=args.sandbox, + llm_url=args.vllm_url, + model=args.model, + api_key=args.api_key, + auth_header=args.auth_header, + agent_timeout_sec=args.agent_timeout, + agent_step_limit=args.agent_step_limit, + indices=[index], + ) + session = None + try: + session = f.create(f.prompt_rows()[0]["prompt"]) + session.wait_for_completion() + result = session.result + reward = session.verify([]).env_reward + + # THE CONVERSATION, from `result.conversations`, not `fetch_proxy_trace()`. The proxy trace is + # deliberately empty for an eval-tier rollout (no token fields to train on), so reading it here + # is why every cell said "no trace recorded". `conversations[].messages` is the readable form — + # system prompt, every assistant turn, every tool result — and it is present at either tier. + # + # Read n_turns first: the trace checks below compare against it. + n_turns = getattr(result, "n_turns", 0) or 0 + + # Agent-role conversations only: a rollout is a tree, and auxiliary calls (title generation, + # summarisers) are not what the task was solved with. Including them would pad the trace with + # work the grader never saw. + # + # TAKING THE FIRST ONE LOSES MOST OF THE TRACE. Several harnesses report one agent + # conversation PER STEP, each carrying the accumulated history, so the conversations are + # strictly nested prefixes and the first is the SHORTEST. Measured on this suite: + # + # terminus-2 n_turns=5 -> convs of 2, 4, 6, 8, 10 messages (every pair prefix=True) + # claude-code n_turns=5 -> convs of 3, 5, 7, 9, 11 messages (every pair prefix=True) + # + # so the stored trace was 1 assistant turn out of 5. Concatenating them is equally wrong -- + # that yields 30 messages of duplicated history for a 10-message rollout. + # + # Correct reduction: drop any conversation that is a PREFIX of another, then use what remains. + # For nested harnesses exactly one survives (the longest, which contains all the others). For a + # harness with genuinely disjoint sub-conversations, several survive and are concatenated in + # order, which is right for that shape and would have been wrong for this one. n_agent_convs + # and trace_reduction are recorded so a trace can always be audited back to what produced it. + _agent_raw = [ + conv + for conv in (getattr(result, "conversations", None) or []) + if getattr(conv, "role", "agent") == "agent" + ] + agent_convs = [ + [_as_plain(m) for m in (getattr(conv, "messages", None) or [])] for conv in _agent_raw + ] + # The AGENT conversations' own turn count, which is not the rollout's. A rollout counts every + # model call it made, including calls that belong to no agent conversation -- measured on + # opencode: rollout n_turns=7 while its single agent conversation reported n_turns=6 and + # carried exactly 6 assistant messages. Comparing a trace against the rollout figure made 15 + # of 15 complete traces look short by one. This is the number a trace can actually be + # checked against. + agent_turns = sum(getattr(c, "n_turns", 0) or 0 for c in _agent_raw) + agent_convs = [c for c in agent_convs if c] + + def _sig(conv): + """Comparable shape of a conversation, for the prefix test. + + The SYSTEM message is compared by role only, never by content. claude-code re-renders its + system prompt per step with something varying inside it (identical for the first 150 + chars, divergent later), so comparing system content made five strictly-nested + conversations look disjoint and concatenated them: 13 turns became 101 assistant + messages. Everything else is compared exactly. + """ + out = [] + for m in conv: + role = m.get("role") + if role == "system": + out.append(("system", "")) + continue + c = m.get("content") + out.append((role, c if isinstance(c, str) else json.dumps(c, sort_keys=True))) + return out + + sigs = [_sig(c) for c in agent_convs] + keep = [ + i + for i in range(len(agent_convs)) + if not any( + j != i and len(sigs[j]) >= len(sigs[i]) and sigs[j][: len(sigs[i])] == sigs[i] + for j in range(len(agent_convs)) + ) + ] + messages = [m for i in keep for m in agent_convs[i]] + conv_role = "agent" if messages else "" + trace_reduction = ( + "" if len(agent_convs) <= 1 + else ("longest-of-nested" if len(keep) == 1 else f"concatenated-{len(keep)}-disjoint") + ) + + # INFLATION GUARD. If the prefix test misjudges nested conversations as disjoint, the trace + # silently multiplies -- measured once at 101 assistant messages for a 13-turn rollout. There + # is no honest reason for a trace to hold many more assistant messages than the rollout had + # turns, so say so loudly rather than writing it into the dataset unremarked. + _asst = sum(1 for m in messages if m.get("role") == "assistant") + trace_warning = "" + # Require ACTUAL DUPLICATION, not just a high count ratio. codex emits a prose message and a + # separate tool-call message every step, so ~2 assistant messages per turn is its normal + # shape; and agent_turns undercounts for it (4 turns -> 2). Ratio alone produced 13 false + # CRITICALs on traces with zero duplicated messages. Compared against the LARGER of the two + # turn counts so an undercounting field cannot trigger this by itself. + _ref_turns = max(agent_turns or 0, n_turns or 0) + _sigs = [ + (m.get("role"), m.get("content") if isinstance(m.get("content"), str) + else json.dumps(m.get("content"), sort_keys=True)) + for m in messages + ] + _dups = sum(n - 1 for s, n in Counter(_sigs).items() if n > 1 and (s[1] or "").strip()) + if _ref_turns and _asst > 1.5 * _ref_turns + 2 and _dups: + trace_warning = ( + f"trace holds {_asst} assistant messages for {_ref_turns} turns with {_dups} duplicate(s) " + f"({len(agent_convs)} agent convs, {trace_reduction or 'single'}) — likely duplicated" + ) + logger.warning("%s task %d rep %d: %s", harness, index, rep, trace_warning) + + + # A ZERO-TURN ROLLOUT IS NOT A MEASUREMENT. Both test.sh variants write a 0.0 when + # `answer.txt` is missing -- the scalar one via `echo "0.0" > reward.txt`, the json one via its + # early-exit branch -- so a harness whose CLI crashed before making a single model call comes + # back with a clean, countable 0.0 that is indistinguishable from a model that tried and was + # wrong. Measured: openclaw exited 1 under nvm and scored 0.0/0.0 at 0 turns. + # + # Excluding it is the honest reading: with no model call there is nothing about the policy to + # score. This is deliberately narrow -- turns > 0 with reward 0.0 stays a real zero, because a + # model that answered badly SHOULD score zero. + infra_zero = "" + if reward is not None and n_turns == 0: + infra_zero = ( + "0 turns: the agent made no model call, so the graded 0.0 is the verifier's " + "missing-answer default rather than a measurement" + ) + logger.warning("%s task %d rep %d excluded — %s", harness, index, rep, infra_zero) + reward = None + + return { + "harness": harness, + "index": index, + "rep": rep, + "reward": None if reward is None else float(reward), + "infra_zero": infra_zero, + "n_turns": n_turns, + "ok": bool(getattr(result, "ok", False)), + "task_id": getattr(result, "task_id", "") or "", + "trial_name": getattr(result, "trial_name", "") or "", + "wall_s": getattr(result, "wall_s", None), + "rewards": dict(getattr(result, "rewards", None) or {}), + "rollout_type": getattr(result, "rollout_type", ""), + "n_trainable_tokens": getattr(result, "n_trainable_tokens", 0) or 0, + "conversation_role": conv_role, + "n_agent_convs": len(agent_convs), + "agent_turns": agent_turns, + "trace_reduction": trace_reduction, + "trace_warning": trace_warning, + "messages": messages, + } + except Exception as exc: # noqa: BLE001 + logger.warning("%s task %d rep %d failed: %s", harness, index, rep, str(exc)[:160]) + return {"harness": harness, "index": index, "rep": rep, "reward": None, "n_turns": 0, + "ok": False, "error": str(exc)[:300], "messages": []} + finally: + if session is not None: + try: + session.close() + except Exception: # noqa: BLE001 + pass + + +def score(rows: list[dict], k: int, n_tasks: int) -> dict: + """pass@k and pass@1 for one harness. + + pass@k is over TASKS that produced at least one graded sample; pass@1 is over SAMPLES. They can + therefore invert when tasks contribute unequal numbers of graded samples — an earlier run reported + pass@1 0.375 above pass@4 0.333 for exactly that reason. Both definitions match the sync run's + eval_harbor.py, so the numbers stay comparable; the asymmetry is just worth knowing. + """ + by_task: dict[int, list[dict]] = defaultdict(list) + for r in rows: + by_task[r["index"]].append(r) + + solved = measured = all_infra = 0 + per_sample: list[float] = [] + turns: list[int] = [] + for rs in by_task.values(): + graded = [r for r in rs if r["reward"] is not None] + if not graded: + all_infra += 1 + continue + measured += 1 + if any(r["reward"] > 0 for r in graded): + solved += 1 + per_sample.extend(r["reward"] for r in graded) + turns.extend(r["n_turns"] for r in graded) + + return { + "n_tasks": n_tasks, + "n_measured": measured, + "n_all_infra": all_infra, + f"pass@{k}": round(solved / measured, 4) if measured else None, + "pass@1": round(statistics.mean(per_sample), 4) if per_sample else None, + "mean_turns": round(statistics.mean(turns), 2) if turns else None, + } + + +def load_prior(paths: list[str]) -> tuple[dict[str, list[dict]], set[tuple[str, int]]]: + """Rows already collected, and the (harness, task) pairs that need no more work. + + A pair is DONE when it has at least one GRADED attempt. Not "has rows" — a task whose every attempt + came back unscorable is exactly the work a resume exists to redo, and treating it as finished would + bake an infrastructure failure into the result permanently. + + Prior rows are carried forward so the resumed run's summary covers everything, not just the tail. + """ + rows: dict[str, list[dict]] = defaultdict(list) + for path in paths: + f = Path(path) + if not f.exists(): + logger.warning("resume source missing, skipping: %s", path) + continue + blob = json.loads(f.read_text()) + for harness, rs in (blob.get("rows") or {}).items(): + rows[harness].extend(rs) + + graded: dict[tuple[str, int], int] = defaultdict(int) + for harness, rs in rows.items(): + for r in rs: + if r.get("reward") is not None: + graded[(harness, r["index"])] += 1 + done = set(graded) + logger.info( + "resume: loaded %d prior rows across %d harness(es); %d (harness, task) pairs already graded", + sum(len(v) for v in rows.values()), len(rows), len(done), + ) + return rows, done + + +def _dump(args, rows_by_harness, started, partial: bool) -> dict: + """Write the summary as it stands. Used for the periodic partial write and the final one.""" + per_harness = { + h: score(rows, args.eval_k, args.n_tasks) for h, rows in sorted(rows_by_harness.items()) + } + with _state_lock: + paused = dict(DISABLED) + with _state_lock: + halted = SERVER_DOWN + # Recorded in the output, because a sweep that halted is not a sweep that measured low: the + # difference has to survive into whatever reads this. + summary = { + "dataset": args.split, "model": args.model, "step": args.step, "k": args.eval_k, + "elapsed_s": round(time.monotonic() - started), + "partial": partial, + "paused_harnesses": paused, + "halted": halted or None, + "harnesses": per_harness, + } + out = Path(args.out) if args.out else HERE / f"logs/eval_step{args.step}_k{args.eval_k}.json" + out.parent.mkdir(parents=True, exist_ok=True) + tmp = out.with_suffix(".tmp") + tmp.write_text(json.dumps({"summary": summary, "rows": rows_by_harness}, indent=2, default=str)) + tmp.replace(out) + return summary + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--server", required=True) + ap.add_argument("--vllm-url", required=True) + ap.add_argument("--model", required=True, help="checkpoint dir or hub id, as served by the engine") + ap.add_argument("--split", default=EVAL_SPLIT) + ap.add_argument( + "--harness", + default="mini-swe-agent", + choices=SUPPORTED_HARNESSES, + metavar="NAME", + ) + # One engine serves every harness, because `harness` is a per-request parameter on run_rollout. + # Four harnesses in one job costs one GPU, not four, and holds the model fixed so the comparison is + # between AGENTS rather than between engines. + ap.add_argument( + "--harnesses", + default="", + metavar="A,B,C", + help=( + "comma list for a multi-harness sweep; overrides --harness. Supported: " + + ", ".join(SUPPORTED_HARNESSES) + ), + ) + ap.add_argument( + "--allow-unsupported-harness", + action="store_true", + help="run a harness excluded in tools/harnesses_supported.txt anyway (for re-testing it)", + ) + # Hosted providers (Anthropic, OpenAI, the HF router) need a credential on every request the + # sandbox makes. It is read from the environment by default so a key never has to appear in a + # command line -- and so it never lands in a log, a shell history, or a saved invocation. + ap.add_argument( + "--api-key-env", + default="", + help="name of the env var holding the upstream credential, e.g. OPENAI_API_KEY", + ) + ap.add_argument( + "--auth-header", + default="", + help="header to send the credential under; defaults to Authorization: Bearer", + ) + ap.add_argument("--sandbox", default="e2b") + ap.add_argument("-k", "--eval-k", type=int, default=4, help="samples per task; needs temperature>0") + ap.add_argument("--n-tasks", type=int, default=366) + # An explicit index list beats --n-tasks: the eval suite's difficulty_level is very unevenly + # distributed (269 tasks at level 0, 12 at level 3), so the first N tasks are not a difficulty + # sample of anything. + ap.add_argument("--indices", default="", help="comma list or @file of task indices") + # The server's sandbox ceiling is set far above this (300), so this number really is the throttle + # rather than a request that gets queued behind a training run. + ap.add_argument("--concurrency", type=int, default=48) + ap.add_argument("--agent-timeout", type=float, default=300.0) + ap.add_argument("--agent-step-limit", type=int, default=12) + ap.add_argument("--resume-from", nargs="*", default=[], + help="prior sweep JSONs; (harness, task) pairs already GRADED are skipped and " + "their rows carried into this run's summary") + ap.add_argument("--halt-after-conn-fails", type=int, default=15, + help="stop the whole sweep after this many CONSECUTIVE connection-level failures; " + "a server that stops accepting fails every harness at once, which the " + "per-harness breaker cannot detect") + ap.add_argument("--pause-after", type=int, default=8, + help="pause a harness after this many CONSECUTIVE unscorable rollouts with none " + "ever graded; low rewards never count") + ap.add_argument("--partial-every", type=int, default=20, + help="write the summary every N rollouts so the run is watchable live; 0 disables") + ap.add_argument("--step", type=int, default=-1, help="training step this checkpoint came from") + ap.add_argument("--out", default="") + args = ap.parse_args() + + # Resolve the credential from the environment, never from the command line. Refuse rather than + # run unauthenticated: a hosted provider answering 401 on every call would come back as 250 + # unscorable rollouts, which reads as a broken harness rather than a missing key. + args.api_key = "" + if args.api_key_env: + args.api_key = os.environ.get(args.api_key_env, "") + if not args.api_key: + raise SystemExit(f"--api-key-env {args.api_key_env} is set but that variable is empty") + + harnesses = [h.strip() for h in args.harnesses.split(",") if h.strip()] or [args.harness] + + # Reject up front, not per-rollout. An unsupported harness discovered 200 rollouts in has already + # spent the sandboxes, and its results are not comparable with the rest of the sweep. + unsupported = [h for h in harnesses if h not in SUPPORTED_HARNESSES] + if unsupported and not args.allow_unsupported_harness: + raise SystemExit( + f"unsupported harness: {', '.join(unsupported)}\n" + f"supported: {', '.join(SUPPORTED_HARNESSES)}\n" + f"see tools/harnesses_supported.txt for why each exclusion was made, or pass " + f"--allow-unsupported-harness to re-test one." + ) + if unsupported: + logger.warning( + "running UNSUPPORTED harness(es) %s — results are not comparable with a supported sweep", + ", ".join(unsupported), + ) + if args.indices: + spec = args.indices + if spec.startswith("@"): + spec = (HERE / spec[1:]).read_text() if not Path(spec[1:]).is_absolute() else Path(spec[1:]).read_text() + # Dedupe but PRESERVE the caller's order. This used to sort, which silently destroyed the whole + # point of passing a shuffled list: the suite is ordered easy->hard, so a pass that dies before + # finishing loses a contiguous hard tail. Sorting made every partial pass an easy-prefix + # measurement -- openhands-sdk never scored a task above index 149 across four passes, and its + # 0.864 was really 0.850-on-the-easy-133 rather than a 250-task score. + _seen: set[int] = set() + indices = [] + for x in spec.replace("\n", ",").split(","): + if not x.strip(): + continue + i = int(x) + if i not in _seen: + _seen.add(i) + indices.append(i) + else: + indices = list(range(args.n_tasks)) + args.n_tasks = len(indices) + # ROUND-ROBIN by harness, not harness-major. `pool.map` consumes the list in order, so grouping by + # harness means the first workers are all one harness and the sweep completes them roughly in + # series: a run that dies at the halfway mark leaves half the harnesses with full data and half with + # none. Interleaving means every harness has partial coverage at any moment, which is both what a + # live view should show and the more useful thing to salvage from an interrupted sweep. + prior_rows, already_done = ({}, set()) + if args.resume_from: + prior_rows, already_done = load_prior(args.resume_from) + + # Skip whole (harness, task) pairs, not individual attempts. pass@k is a property of the k attempts + # of one task, so a task with 2 of 4 attempts done cannot be topped up without mixing samples taken + # under different server conditions — it is redone. + per_harness_jobs = [ + [(h, i, r) for i in indices if (h, i) not in already_done for r in range(args.eval_k)] + for h in harnesses + ] + # zip_longest, not zip: with a resume each harness has a DIFFERENT number of remaining tasks, and + # plain zip truncates to the shortest — which would silently drop the work of whichever harness has + # the most left to do, the exact opposite of what a resume is for. + from itertools import zip_longest + + jobs = [j for wave in zip_longest(*per_harness_jobs) for j in wave if j is not None] + assert len(jobs) == sum(len(x) for x in per_harness_jobs), "lost jobs while interleaving" + logger.info( + "%d harness(es) x %d tasks x k=%d = %d rollouts, %d at a time", + len(harnesses), args.n_tasks, args.eval_k, len(jobs), args.concurrency, + ) + started = time.monotonic() + + rows_by_harness: dict[str, list[dict]] = defaultdict(list) + for h, rs in prior_rows.items(): + rows_by_harness[h].extend(rs) + done = 0 + # Interleaved across harnesses on purpose: a slow harness then overlaps a fast one instead of + # serialising behind it, and a mid-run failure does not leave one harness entirely unmeasured. + with ThreadPoolExecutor(max_workers=args.concurrency) as pool: + for row in pool.map(lambda j: one_sample(args, j[0], j[1], j[2]), jobs): + rows_by_harness[row["harness"]].append(row) + _note_result(row["harness"], row.get("reward") is not None, args.pause_after) + _note_connectivity(row.get("error"), args.halt_after_conn_fails) + done += 1 + if done % 10 == 0: + with _state_lock: + paused = dict(DISABLED) + logger.info( + "%d/%d rollouts, %.0fs%s", + done, len(jobs), time.monotonic() - started, + f", paused: {sorted(paused)}" if paused else "", + ) + # Partial write, so the run is watchable while it runs instead of only at the end. Written + # to a temp file and moved, because a reader hitting a half-written JSON would look like a + # corrupt result rather than an in-progress one. + if args.partial_every and done % args.partial_every == 0: + _dump(args, rows_by_harness, started, partial=True) + + per_harness = { + h: score(rows, args.eval_k, args.n_tasks) for h, rows in sorted(rows_by_harness.items()) + } + summary = { + "dataset": args.split, + "model": args.model, + "step": args.step, + "k": args.eval_k, + "elapsed_s": round(time.monotonic() - started), + "harnesses": per_harness, + } + logger.info("--- %s", json.dumps(summary, indent=2)) + key = f"pass@{args.eval_k}" + logger.info("%-18s %8s %8s %11s %10s", "harness", key, "pass@1", "mean_turns", "measured") + for h, m in sorted(per_harness.items(), key=lambda kv: -(kv[1][key] or 0)): + logger.info( + "%-18s %8s %8s %11s %10s", + h, m[key], m["pass@1"], m["mean_turns"], f"{m['n_measured']}/{m['n_tasks']}", + ) + + summary = _dump(args, rows_by_harness, started, partial=False) + per_harness = summary["harnesses"] + logger.info("--- %s", json.dumps(summary, indent=2)) + key = f"pass@{args.eval_k}" + logger.info("%-18s %8s %8s %11s %10s", "harness", key, "pass@1", "mean_turns", "measured") + for h, m in sorted(per_harness.items(), key=lambda kv: -(kv[1][key] or 0)): + logger.info("%-18s %8s %8s %11s %10s", h, m[key], m["pass@1"], m["mean_turns"], + f"{m['n_measured']}/{m['n_tasks']}") + if summary["paused_harnesses"]: + for h, why in summary["paused_harnesses"].items(): + logger.warning("PAUSED %s: %s", h, why) + logger.info("wrote %s", args.out or "logs/") + return 0 if any(m["n_measured"] for m in per_harness.values()) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/04-data-agent/eval/eval_whitebox_bash.py b/04-data-agent/eval/eval_whitebox_bash.py new file mode 100644 index 0000000..72f6d7b --- /dev/null +++ b/04-data-agent/eval/eval_whitebox_bash.py @@ -0,0 +1,228 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Paired base-vs-checkpoint evaluation for the white-box bash/SETA environment. + +WHY THIS EXISTS RATHER THAN REUSING eval_env.py +That harness drives `opencode` or `harbor`, both of which own their own agent loop, and it scores ONE +model at a time. This environment has no agent: TRL owns the loop during training, so an evaluation +has to supply one. The loop here is deliberately the same shape TRL uses -- generate, parse tool +calls, execute, append results, repeat -- so the policy is measured under the conditions it was +trained in. Evaluating it any other way measures a different thing and the number is not comparable. + +PAIRED, ON A COMMON ITEM SET +Base and checkpoint run the SAME task indices with the SAME seed, and only tasks where BOTH produced +a grade are counted. Comparing two runs over different subsets is not a comparison; the black-box +evals in this repo are paired for the same reason and that is what makes the numbers sit beside each +other. + +THE BASE ARM IS THE VALIDITY CHECK +Base is a fixed reference. If it scores far from its known value the run measured infrastructure, not +the policy -- two q3i evals in this project reported a "collapse" that was 976 E2B 404s, with the +base arm reading 0.0000 against its true 0.06-0.08. `--expect-base` fails the run loudly instead of +publishing a plausible wrong number. +""" + +from __future__ import annotations + +import argparse +import json +import math +import os +import random +import sys +import time +from concurrent.futures import ThreadPoolExecutor, as_completed + + +def tool_schemas(env) -> list[dict]: + """OpenAI tool schemas for the env's public methods, derived the way TRL derives them.""" + import inspect + + from transformers.utils import get_json_schema + + out = [] + for name, member in inspect.getmembers(env, predicate=inspect.ismethod): + if name in {"reset", "get_reward"} or name.startswith("_"): + continue + out.append(get_json_schema(member)) + return out + + +def run_episode(make_env, *, split, index, llm_url, model, max_turns, timeout_s) -> dict: + """One episode: drive the env with `model` until it submits or runs out of turns.""" + import openai + + env = make_env() + client = openai.OpenAI(base_url=llm_url.rstrip("/") + "/v1", api_key="unused", timeout=timeout_s) + rec = {"split": split, "index": index, "turns": 0, "tool_calls": 0, + "submitted": None, "reward": None, "error": ""} + try: + task_text = env.reset(split=split, index=index) + tools = tool_schemas(env) + messages = [ + {"role": "system", "content": + "You are a data-analysis agent working in a sandbox. Use the tools to inspect the " + "files and compute the answer. When confident, call submit_solution with the value " + "itself -- not the command that would produce it."}, + {"role": "user", "content": task_text}, + ] + for _ in range(max_turns): + rec["turns"] += 1 + resp = client.chat.completions.create( + model=model, messages=messages, tools=tools, tool_choice="auto", + temperature=0.8, top_p=0.95, max_tokens=1024, + ) + msg = resp.choices[0].message + messages.append({"role": "assistant", "content": msg.content or "", + "tool_calls": [tc.model_dump() for tc in (msg.tool_calls or [])]}) + if not msg.tool_calls: + break # prose with no tool call: the agent has stopped working + for tc in msg.tool_calls: + rec["tool_calls"] += 1 + fn = tc.function.name + try: + args = json.loads(tc.function.arguments or "{}") + except (TypeError, ValueError): + args = {} + method = getattr(env, fn, None) + result = (f"[error] no such tool {fn}" if method is None + else str(method(**args))) + messages.append({"role": "tool", "tool_call_id": tc.id, "name": fn, + "content": result}) + if fn == "submit_solution": + rec["submitted"] = args.get("answer") + if rec["submitted"] is not None: + break + rec["reward"] = env.get_reward() + except Exception as exc: # one dead episode must not take the pass with it + rec["error"] = f"{type(exc).__name__}: {exc}"[:200] + return rec + + +def pass1(records: list[dict]) -> float: + """Mean reward over GRADED records. An errored episode is excluded, never scored 0.""" + vals = [r["reward"] for r in records if r.get("reward") is not None and not r.get("error")] + return sum(1.0 for v in vals if v and v >= 1.0) / len(vals) if vals else 0.0 + + +def paired(base: dict, ckpt: dict) -> dict: + """Paired comparison on the COMMON item set, with a normal-approx CI and a sign test.""" + keys = sorted(set(base) & set(ckpt)) + b = [base[k] for k in keys] + c = [ckpt[k] for k in keys] + n = len(keys) + if n == 0: + return {"n_tasks": 0, "diff": 0.0, "note": "no common graded tasks"} + diffs = [ci - bi for bi, ci in zip(b, c)] + mean = sum(diffs) / n + var = sum((d - mean) ** 2 for d in diffs) / max(n - 1, 1) + se = math.sqrt(var / n) + better = sum(1 for d in diffs if d > 0) + worse = sum(1 for d in diffs if d < 0) + # Two-sided sign test over the discordant pairs only. + m = better + worse + p = 1.0 + if m: + tail = sum(math.comb(m, i) for i in range(0, min(better, worse) + 1)) / (2 ** m) + p = min(1.0, 2 * tail) + return {"n_tasks": n, "base_pass1": sum(b) / n, "ckpt_pass1": sum(c) / n, + "diff": mean, "se": se, "ci_lo": mean - 1.96 * se, "ci_hi": mean + 1.96 * se, + "better": better, "worse": worse, "tied": n - m, "sign_p": round(p, 4)} + + +def evaluate(tag, llm_url, model, indices, k, args) -> tuple[dict, list[dict]]: + """Run every (task, sample) for one model and return per-task mean scores plus raw records.""" + from whitebox_bash import white_box_bash_env + + make_env = white_box_bash_env(args.server, toolsets=args.toolsets, step_limit=args.step_limit) + jobs = [(i, s) for i in indices for s in range(k)] + random.Random(args.seed).shuffle(jobs) # unbiased partial coverage if the pass is cut short + records: list[dict] = [] + done = 0 + with ThreadPoolExecutor(max_workers=args.concurrency) as pool: + futs = {pool.submit(run_episode, make_env, split=args.split, index=i, llm_url=llm_url, + model=model, max_turns=args.max_turns, + timeout_s=args.timeout_s): (i, s) for i, s in jobs} + for fut in as_completed(futs): + records.append(fut.result()) + done += 1 + if done % 25 == 0: + ex = sum(1 for r in records if r.get("error") or r.get("reward") is None) + print(f"[{tag}] {done}/{len(jobs)} excluded so far: {ex}", flush=True) + per_task: dict[int, float] = {} + for i in indices: + rs = [r for r in records if r["index"] == i and not r.get("error") + and r.get("reward") is not None] + if rs: + per_task[i] = sum(1.0 for r in rs if r["reward"] >= 1.0) / len(rs) + return per_task, records + + +def main() -> int: + p = argparse.ArgumentParser() + p.add_argument("--server", default=os.environ.get("WHITE_BOX_BASH_URL", "http://127.0.0.1:8000")) + p.add_argument("--base-url", required=True, help="vLLM serving the BASE model") + p.add_argument("--base-model", required=True) + p.add_argument("--ckpt-url", required=True, help="vLLM serving the CHECKPOINT") + p.add_argument("--ckpt-model", required=True) + p.add_argument("--split", default="test") + p.add_argument("--n-tasks", type=int, default=60) + p.add_argument("--k", type=int, default=4) + p.add_argument("--seed", type=int, default=0) + p.add_argument("--toolsets", default="bash,seta") + p.add_argument("--step-limit", type=int, default=14) + p.add_argument("--max-turns", type=int, default=8) + p.add_argument("--timeout-s", type=float, default=600.0) + p.add_argument("--concurrency", type=int, default=8) + p.add_argument("--out", default="") + # Base is a FIXED reference. If it lands far from this, the run measured infrastructure. + p.add_argument("--expect-base", type=float, default=0.0, + help="fail if base pass@1 is below this (0 disables)") + args = p.parse_args() + + indices = list(range(args.n_tasks)) + t0 = time.time() + base_scores, base_recs = evaluate("base", args.base_url, args.base_model, indices, args.k, args) + ckpt_scores, ckpt_recs = evaluate("ckpt", args.ckpt_url, args.ckpt_model, indices, args.k, args) + res = paired(base_scores, ckpt_scores) + res.update({"split": args.split, "k": args.k, "seed": args.seed, + "n_requested": args.n_tasks, "elapsed_s": round(time.time() - t0), + "base_model": args.base_model, "ckpt_model": args.ckpt_model, + "excluded_base": sum(1 for r in base_recs if r.get("error") or r.get("reward") is None), + "excluded_ckpt": sum(1 for r in ckpt_recs if r.get("error") or r.get("reward") is None)}) + + print("\n=== paired base vs checkpoint ===", flush=True) + print(f" common tasks : {res['n_tasks']} of {args.n_tasks} requested " + f"(excluded: base {res['excluded_base']}, ckpt {res['excluded_ckpt']} rollouts)") + print(f" base pass@1 : {res.get('base_pass1', 0):.4f}") + print(f" ckpt pass@1 : {res.get('ckpt_pass1', 0):.4f}") + print(f" diff : {res.get('diff', 0):+.4f} " + f"CI[{res.get('ci_lo', 0):+.3f},{res.get('ci_hi', 0):+.3f}] p={res.get('sign_p')}") + print(f" better/worse/tied: {res.get('better')}/{res.get('worse')}/{res.get('tied')}") + if args.out: + with open(args.out, "w") as fh: + json.dump({"summary": res, "base": base_recs, "ckpt": ckpt_recs}, fh) + print(f" wrote {args.out}") + + if args.expect_base and res.get("base_pass1", 0) < args.expect_base: + print(f"\nFATAL: base pass@1 {res.get('base_pass1', 0):.4f} < expected {args.expect_base}. " + "Base is a fixed reference -- this run measured infrastructure, not the policy. " + "Discard it.", flush=True) + return 2 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/04-data-agent/eval/eval_whitebox_bash.slurm b/04-data-agent/eval/eval_whitebox_bash.slurm new file mode 100755 index 0000000..c477f23 --- /dev/null +++ b/04-data-agent/eval/eval_whitebox_bash.slurm @@ -0,0 +1,80 @@ +#!/usr/bin/env bash +#SBATCH --job-name=wb-bash-eval +#SBATCH --partition=hopper-extra,hopper-dev +#SBATCH --gres=gpu:1 +#SBATCH --cpus-per-task=24 +#SBATCH --output=/fsx/%u/logs/%x-%j.out +#SBATCH --error=/fsx/%u/logs/%x-%j.err +#SBATCH --time=0-03:00:00 +# +# PAIRED base-vs-checkpoint eval for the white-box env. Both models are served CONCURRENTLY on ONE +# GPU (they are 2B, ~5 GB each at 0.4 utilisation) so the two arms see the same sandbox conditions AND +# the same hardware -- running them hours apart would let a change in E2B health masquerade as a +# change in the policy, which is exactly how two q3i evals produced a "collapse" that was 976 +# sandbox 404s. One GPU also stays inside the QOSMaxGRESPerJob limit that refused a 2-GPU request. +set -euo pipefail + +TRL_PROD=/fsx/adithyaskolavi/projects/trl_prod +cd "$TRL_PROD" +PY="$TRL_PROD/.venv312/bin/python" +export PATH="$TRL_PROD/.venv312/bin:$PATH" +module load cuda/12.9 2>/dev/null || module load cuda/13.0 2>/dev/null || true + +set -a; source "$TRL_PROD/experiments/.env" 2>/dev/null || true; set +a +export HF_TOKEN="${HF_TOKEN:-${HF_API_KEY:-}}" +echo "E2B_API_KEY: ${E2B_API_KEY:+present}" +[ -n "${E2B_API_KEY:-}" ] || { echo "FATAL: E2B_API_KEY unset"; exit 1; } + +export PYTHONPATH="$TRL_PROD/trl:$TRL_PROD/HuggingEnvs/04-data-agent/train/_pypath:$TRL_PROD/OpenEnv/src" +export TOKENIZERS_PARALLELISM=false HF_HUB_ENABLE_HF_TRANSFER=1 + +BASE_MODEL="${BASE_MODEL:-Qwen/Qwen3.5-2B}" +CKPT="${CKPT:?set CKPT to a checkpoint directory}" +OFF=$(( ${SLURM_JOB_ID:-0} % 120 )) +ENV_PORT=$(( 8500 + OFF )); BASE_PORT=$(( 8700 + OFF )); CKPT_PORT=$(( 8900 + OFF )) +LOGS="$TRL_PROD/HuggingEnvs/04-data-agent/envs/whitebox-bash/logs/eval-${SLURM_JOB_ID:-local}" +mkdir -p "$LOGS" + +# --max-num-seqs MUST be set for Qwen3.5 when two engines share a card. +# It is hybrid Gated-DeltaNet, so EVERY concurrent decode sequence needs its own Mamba cache block. +# At 0.40 utilisation there is room for ~417, but vLLM defaults max_num_seqs=1024 and refuses to +# start: ValueError: max_num_seqs (1024) exceeds available Mamba cache blocks (417). +# The eval runs 8 concurrent episodes, so 64 is generous. The failure is confusing because the FIRST +# engine starts fine and only the second dies -- it reads as a bad checkpoint, not a memory setting. +serve () { # $1=gpu $2=model $3=port $4=logname (both share GPU 0) + # qwen3_xml is REQUIRED: with the wrong parser vLLM returns no structured tool_calls, every + # episode makes zero tool calls, and the eval reports a model that cannot use tools. + CUDA_VISIBLE_DEVICES=$1 VLLM_USE_FLASHINFER_SAMPLER=0 VLLM_USE_DEEP_GEMM=0 VLLM_DEEP_GEMM_WARMUP=skip \ + "$PY" -m vllm.entrypoints.openai.api_server \ + --model "$2" --served-model-name "$2" --port "$3" --trust-remote-code \ + --max-model-len "${MAX_MODEL_LEN:-16384}" --gpu-memory-utilization "${VLLM_UTIL:-0.40}" \ + --max-num-seqs "${MAX_NUM_SEQS:-64}" \ + --enable-auto-tool-choice --tool-call-parser qwen3_xml --reasoning-parser qwen3 \ + --gdn-prefill-backend triton \ + --default-chat-template-kwargs '{"enable_thinking": false}' \ + > "$LOGS/$4.log" 2>&1 & +} + +echo "== env server :$ENV_PORT ==" +"$PY" -m uvicorn whitebox_bash.server.app:app --host 127.0.0.1 --port "$ENV_PORT" > "$LOGS/env.log" 2>&1 & +ENVPID=$! +echo "== vLLM base :$BASE_PORT and ckpt :$CKPT_PORT, both on GPU 0 ==" +serve 0 "$BASE_MODEL" "$BASE_PORT" vllm-base +serve 0 "$CKPT" "$CKPT_PORT" vllm-ckpt +trap 'kill $ENVPID 2>/dev/null || true; pkill -P $$ 2>/dev/null || true' EXIT + +# vLLM's /health returns 200 with an EMPTY body -- check the STATUS, parse nothing. +for url in "http://127.0.0.1:$ENV_PORT/health" "http://127.0.0.1:$BASE_PORT/health" "http://127.0.0.1:$CKPT_PORT/health"; do + for _ in $(seq 1 180); do curl -sf -o /dev/null "$url" && break; sleep 5; done + curl -sf -o /dev/null "$url" || { echo "FATAL: never healthy: $url"; tail -20 "$LOGS"/*.log; exit 1; } + echo " healthy: $url" +done + +"$PY" "$TRL_PROD/HuggingEnvs/04-data-agent/eval/eval_whitebox_bash.py" \ + --server "http://127.0.0.1:$ENV_PORT" \ + --base-url "http://127.0.0.1:$BASE_PORT" --base-model "$BASE_MODEL" \ + --ckpt-url "http://127.0.0.1:$CKPT_PORT" --ckpt-model "$CKPT" \ + --split "${SPLIT:-test}" --n-tasks "${N_TASKS:-60}" --k "${EVAL_K:-4}" \ + --concurrency "${CONCURRENCY:-8}" --max-turns "${MAX_TURNS:-8}" \ + --out "$LOGS/result.json" +echo "== eval finished ==" diff --git a/04-data-agent/eval/test_checkpoint_evals.py b/04-data-agent/eval/test_checkpoint_evals.py new file mode 100644 index 0000000..f9a57a3 --- /dev/null +++ b/04-data-agent/eval/test_checkpoint_evals.py @@ -0,0 +1,141 @@ +"""Checkpoint eligibility, score denominators and retry selection without GPU jobs.""" +import json +from pathlib import Path + +import pytest + +from checkpoint_evals import eligible, summarize, submission_env + + +@pytest.fixture +def series(tmp_path): + harnesses = ['opencode', 'claude-code', 'codex', 'mini-swe-agent'] + protocol = {'harnesses': harnesses, 'model': 'base', + 'harness_versions': {h: '1.2.3' for h in harnesses}, + 'scores': {h: {'pass_at_1': 0.0} for h in harnesses}} + tasks = [{'difficulty': 'easy' if i < 33 else 'medium' if i < 151 else 'hard'} + for i in range(250)] + (tmp_path / 'manifest.json').write_text(json.dumps({'tasks': tasks})) + logs = tmp_path / 'job-1' + (logs / 'traces').mkdir(parents=True) + rows = [] + for h in harnesses: + for i in range(250): + trial = f'{h}-{i}' + rows.append({'harness': h, 'index': i, 'rep': 0, 'reward': 0, + 'n_turns': 1, 'trial_name': trial}) + native = logs / 'trials' / trial / 'result.json' + native.parent.mkdir(parents=True) + native.write_text(json.dumps({'agent_info': {'version': '1.2.3'}})) + (logs / 'final_tito.json').write_text('{"tito_pass":true}') + return tmp_path, logs, rows, protocol + + +def test_first_graded_zero_is_retained_when_later_retry_succeeds(series): + output, logs, rows, protocol = series + rows.append({**rows[0], 'reward': 1}) + (logs / 'traces/all.jsonl').write_text(''.join(json.dumps(r) + '\n' for r in rows)) + result = summarize(output, 1, protocol) + assert result['complete'] and result['comparison_ready'] + assert result['average_pass_at_1'] == 0 + scores = result['harnesses']['opencode'] + assert scores['graded'] == 250 + assert {d: v['graded'] for d, v in scores['difficulty'].items()} == {'easy': 33, 'medium': 118, 'hard': 99} + + +def test_missing_grade_does_not_publish_full_set_score(series): + output, logs, rows, protocol = series + rows[0]['reward'] = None + (logs / 'traces/all.jsonl').write_text(''.join(json.dumps(r) + '\n' for r in rows)) + result = summarize(output, 1, protocol) + assert result == {'complete': False, 'graded_cells': 999, 'expected_cells': 1000} + assert not (output / 'scores.json').exists() + + +def test_changed_harness_version_prevents_comparable_result(series): + output, logs, rows, protocol = series + (logs / 'traces/all.jsonl').write_text(''.join(json.dumps(r) + '\n' for r in rows)) + (logs / 'trials/opencode-0/result.json').write_text('{"agent_info":{"version":"9.9.9"}}') + result = summarize(output, 1, protocol) + assert result['complete'] and result['tito_pass'] + assert not result['comparison_ready'] + + +def test_recovered_grades_use_original_measured_versions(series): + output, logs, rows, protocol = series + original = output / 'job-0' + original.mkdir() + (logs / 'trials').rename(original / 'trials') + (logs / 'resume_transport_migration.json').write_text(json.dumps({'source': str(original)})) + (logs / 'traces/all.jsonl').write_text(''.join(json.dumps(r) + '\n' for r in rows)) + result = summarize(output, 1, protocol) + assert result['comparison_ready'] and result['average_pass_at_1'] == 0 + (original / 'trials/opencode-0/result.json').write_text('{"agent_info":{"version":"9.9.9"}}') + assert not summarize(output, 1, protocol)['comparison_ready'] + + +def test_version_ancestry_cycles_and_other_checkpoints_fail_closed(tmp_path): + from checkpoint_evals import trial_result_path + first, second = tmp_path / 'step-100/job-1', tmp_path / 'step-100/job-2' + first.mkdir(parents=True) + second.mkdir() + for current, source in [(first, second), (second, first)]: + (current / 'resume_transport_migration.json').write_text(json.dumps({'source': str(source)})) + assert trial_result_path(first, 'trial') is None + other = tmp_path / 'step-200/job-3' + native = other / 'trials/trial/result.json' + native.parent.mkdir(parents=True) + native.write_text('{"agent_info":{"version":"1.2.3"}}') + (second / 'resume_transport_migration.json').write_text(json.dumps({'source': str(other)})) + assert trial_result_path(first, 'trial') is None + assert trial_result_path(first, '../trial') is None + + +def test_only_completed_interval_checkpoints_are_eligible(tmp_path): + root = tmp_path / 'checkpoint-50' + root.mkdir() + assert eligible(root, 50) is None + marker = {'checkpoint': str(root.resolve()), 'step': 50} + (root / 'checkpoint.ready.json').write_text(json.dumps(marker)) + assert eligible(root, 50) == marker + assert eligible(root, 100) is None + marker['final'] = True + (root / 'checkpoint.ready.json').write_text(json.dumps(marker)) + assert eligible(root, 100) is None + assert eligible(root, 100, include_final=True) == marker + marker['step'] = 100 + (root / 'checkpoint.ready.json').write_text(json.dumps(marker)) + with pytest.raises(ValueError, match='completion marker'): + eligible(root, 50) + + +def test_checkpoint_eval_cannot_inherit_baseline_resume(monkeypatch, tmp_path): + monkeypatch.setenv('BASELINE_RESUME_FROM', '/wrong/base/results') + monkeypatch.setenv('EVAL_SMOKE_ONLY', '1') + env = submission_env(tmp_path, {'model': 'base', 'harness_versions': {}}) + assert 'BASELINE_RESUME_FROM' not in env + assert 'EVAL_SMOKE_ONLY' not in env + assert env['EVAL_MODEL_SOURCE'] == str(tmp_path / 'model') + assert env['EVAL_CODE_ROOT'] == str(tmp_path / 'source-snapshot') + + +@pytest.mark.parametrize('step,expected', [(50, False), (100, True), (150, False), (200, True)]) +def test_saved_handoff_obeys_100_step_eval_interval(tmp_path, step, expected): + root = tmp_path / f'checkpoint-{step}' + root.mkdir() + (root / 'checkpoint.saved.json').write_text(json.dumps({'step': step, 'checkpoint': str(root)})) + assert bool(eligible(root, 100)) == expected + + +def test_gpu_eval_excludes_training_node_and_has_independent_cleanup(monkeypatch, tmp_path): + from checkpoint_evals import submit + commands = [] + def capture(command, **kwargs): + commands.append(command) + return '123\n' if len(commands) == 1 else '124\n' + monkeypatch.setattr('checkpoint_evals.subprocess.check_output', capture) + result = submit(tmp_path, {'model': 'base', 'harness_versions': {}}, 'hopper-extra', exclude_nodes='training-node') + assert result == {'job_id': '123', 'cleanup_job_id': '124'} + assert '--exclude=training-node' in commands[0] + assert '--gres=gpu:2' in commands[0] + assert '--dependency=afterany:123' in commands[1] diff --git a/04-data-agent/eval/test_progress_report.py b/04-data-agent/eval/test_progress_report.py new file mode 100644 index 0000000..4fffb83 --- /dev/null +++ b/04-data-agent/eval/test_progress_report.py @@ -0,0 +1,24 @@ +import json + +from build_progress_report import evaluation_paths, matched_counts + + +def test_checkpoint_history_survives_allocation_move(tmp_path): + parent, child = tmp_path / 'parent', tmp_path / 'child' + parent.mkdir(); child.mkdir() + (parent / 'run_config.json').write_text('{}') + (child / 'run_config.json').write_text(json.dumps({'resume_state': { + 'step': 150, 'checkpoint': str(parent / 'job-1/run/checkpoint-150')}})) + for root, steps in [(parent, [100, 200]), (child, [300])]: + for step in steps: + (root / f'checkpoint-evals/step-{step:06d}').mkdir(parents=True) + assert sorted(evaluation_paths(child)) == [100, 300] + + +def test_partial_checkpoints_compare_the_intersection(): + selected = {'base': {1: {'reward': 0}, 2: {'reward': 1}, 3: {'reward': 0}}, + '100': {1: {'reward': 1}, 2: {'reward': 1}}, + '200': {1: {'reward': 0}, 3: {'reward': 1}}} + result = matched_counts(selected) + assert all(r['graded'] == 1 for r in result.values()) + assert [r['correct'] for r in result.values()] == [0, 1, 0] diff --git a/04-data-agent/hf/README.md b/04-data-agent/hf/README.md new file mode 100644 index 0000000..ca33c10 --- /dev/null +++ b/04-data-agent/hf/README.md @@ -0,0 +1,13 @@ +# Jobs and Spaces runtime + +Use [../reproduce.md](../reproduce.md) and `../reproduce.py` as the entry point. + +`build.py` downloads a hash-verified, immutable task bundle and exports pinned OpenEnv/TRL commits. It overlays the implementations in this repository, records every packaged file hash, and archives previous outputs in `../temp/build-archive/`. It does not read a sibling `experiments/` checkout. + +`deploy.py` uploads that bundle, deploys an explicitly selected Space, or submits a Job. Jobs create two isolated environments from `locks/`: inference/training and environment serving. Long training requires verified baseline and optimizer/save/remote-resume evidence. The CPU coordinator admits only durably published checkpoints to separate A100 evaluation Jobs. + +`cluster.py` stages the same bundle for Slurm. It can create its locked venvs or use two explicitly supplied existing venvs. `local_long.py` and `local_followup.py` qualify checkpoint evaluation before admitting a long run. Use a shared filesystem for local checkpoints and a separate GPU allocation for evals. + +Environment Spaces have interactive task/model panels and per-session traces. They do not host Trackio. Training writes local metrics/Trackio artifacts and uploads them asynchronously. The shared comparison dashboard is a separate presentation layer. + +Advanced operator scripts retain the migration, checkpoint recovery, intentional-stop and dashboard workflows used by the recorded experiments. They are not prerequisites for the beginner reproduction path. Dated results and provenance are in `../results/`; superseded local notes are preserved in ignored `../temp/`. diff --git a/04-data-agent/hf/audit_native_progress.py b/04-data-agent/hf/audit_native_progress.py new file mode 100644 index 0000000..6d4e924 --- /dev/null +++ b/04-data-agent/hf/audit_native_progress.py @@ -0,0 +1,87 @@ +"""Incrementally apply the qualified native TiTO audit on a separate CPU process.""" +import argparse +import collections +import hashlib +import json +import os +from pathlib import Path +import sys +import time + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--root', type=Path, required=True) + parser.add_argument('--training', type=Path, required=True) + parser.add_argument('--out', type=Path, required=True) + args = parser.parse_args() + os.environ.update(REPRO_ROOT=str(args.root), CUDA_VISIBLE_DEVICES='', OMP_NUM_THREADS='1') + sys.path.insert(0, str(args.root / 'hf/runtime')) + from common import configure, write_json + configure() + from training_capture_audit import positions + from trl.experimental.async_grpo.openenv_harness import _turns_from_trace + from trl.experimental.async_grpo.async_rollout_worker import _chain_to_sequences + from openenv.core.harness.capture.validate import validate_training_turn + from data_agent_env import opencode_agent_turns, to_trace_entries + from data_agent_env.models import DataAgentRolloutResult + args.out.mkdir(parents=True, exist_ok=True) + cache_path = args.out / 'rollouts.json' + cache = json.loads(cache_path.read_text()) if cache_path.exists() else {} + for path in sorted((args.training / 'audit/rollouts').glob('*.json')): + stat = path.stat() + stamp = [stat.st_size, stat.st_mtime_ns] + if path.name in cache and cache[path.name]['stamp'] == stamp: + continue + encoded = path.read_bytes() + sha = hashlib.sha256(encoded).hexdigest() + if path.name in cache: + if cache[path.name]['sha256'] != sha: + raise ValueError('Previously audited rollout changed') + cache[path.name]['stamp'] = stamp + continue + record = json.loads(encoded) + if not record.get('result'): + continue + entries = opencode_agent_turns(to_trace_entries(DataAgentRolloutResult.model_validate(record['result']))) + if not entries: + continue + expected = collections.Counter() + for entry in entries: + p, c, lp, mask = (entry[k] for k in ('prompt_token_ids', 'completion_token_ids', 'per_token_logps', 'loss_mask')) + validate_training_turn(p, c, lp, mask) + expected.update(positions(p + c, mask, [0.] * len(p) + lp)) + rows, _ = _chain_to_sequences(_turns_from_trace(entries), record['episode_id'], fork_threshold=0) + retained = collections.Counter() + for row in rows: + retained.update(positions(row.input_ids, row.completion_mask, row.old_log_probs)) + if not expected or retained != expected or not any(lp < 0 for e in entries for lp in e['per_token_logps']): + raise ValueError('Supervised token/context/logprob positions were not retained exactly') + cache[path.name] = {'stamp': stamp, 'sha256': sha, 'episode_id': record['episode_id'], + 'rows': len(rows), 'eligible_tokens': sum(expected.values()), 'retained_tokens': sum(retained.values()), + 'tito_pass': True} + by_id = {r['episode_id']: r for r in cache.values()} + consumed = pending = 0 + receipt = args.training / 'audit/optimizer_rollouts.jsonl' + if receipt.exists(): + for line in receipt.read_text().splitlines(keepends=True): + if not line.endswith('\n'): + continue + for row in json.loads(line)['rollouts']: + if row['rollout_id'] not in by_id: + pending += 1 # capture can be published after this observation's directory scan + continue + report = by_id[row['rollout_id']] + if row['rows'] != report['rows'] or row['supervised_tokens'] != report['retained_tokens']: + raise ValueError('Optimizer receipt differs from exact captured supervision') + consumed += 1 + write_json(cache_path, cache) + write_json(args.out / 'summary.json', {'checked_at': time.time(), 'opencode': { + 'completed_results': len(cache), 'tito_pass': len(cache), + 'eligible_tokens': sum(r['eligible_tokens'] for r in cache.values()), + 'retained_tokens': sum(r['retained_tokens'] for r in cache.values()), + 'optimizer_rollouts_verified': consumed, 'pending_capture_publication': pending}}) + + +if __name__ == '__main__': + main() diff --git a/04-data-agent/hf/build.py b/04-data-agent/hf/build.py new file mode 100644 index 0000000..ca1d74a --- /dev/null +++ b/04-data-agent/hf/build.py @@ -0,0 +1,167 @@ +"""Build a portable runtime from pinned Hub data and reviewed Git sources. + +The internal snapshot layout stays compatible with the validated runners. No +local experiments checkout is required and previous outputs are archived. +""" +from __future__ import annotations + +import argparse +import hashlib +import json +import os +from pathlib import Path +import shutil +import subprocess +import tarfile +import time + +HERE = Path(__file__).resolve().parent +PROJECT = HERE.parent +REL_RUN = Path("experiments/daytona_harness_comparison/logs/20260915") +PORTABLE_ROOT = "/workspace/repro" + + +def sha(path): + digest = hashlib.sha256() + with Path(path).open("rb") as stream: + for block in iter(lambda: stream.read(8 * 1024 * 1024), b""): + digest.update(block) + return digest.hexdigest() + + +def preserve(path, archive): + """Move superseded material into the project-local ignored archive.""" + path = Path(path) + if path.exists(): + archive = Path(archive) + archive.mkdir(parents=True, exist_ok=True) + path.rename(archive / f"{path.name}-{time.time_ns()}") + + +def source_tree(spec, target, cache): + """Export only the committed revision, without local untracked files or credentials.""" + revision = spec["revision"] + if len(revision) != 40 or any(c not in "0123456789abcdef" for c in revision): + raise ValueError("Source revisions must be full Git commit hashes") + checkout = cache / spec["name"] + if not checkout.exists(): + subprocess.run(["git", "init", "--bare", str(checkout)], check=True, capture_output=True) + subprocess.run(["git", "-C", str(checkout), "fetch", "--depth=1", spec["url"], revision], + check=True, capture_output=True) + archive = cache / f"{spec['name']}-{revision}.tar" + with archive.open("wb") as stream: + subprocess.run(["git", "-C", str(checkout), "archive", revision], stdout=stream, check=True) + target.mkdir(parents=True) + with tarfile.open(archive) as stream: + stream.extractall(target, filter="data") + + +def verify_runtime_entrypoints(stage): + required = ["hf/runtime/ui_smoke.py", "hf/runtime/coordinator.py", "hf/runtime/job.py", + "hf/configs/deployment.json", "hf/locks/requirements-env.lock"] + missing = [name for name in required if not (stage / name).is_file()] + if missing: + raise ValueError(f"Bundle is missing runtime entry points: {missing}") + + +def _copy(source, target): + if source.is_dir(): + shutil.copytree(source, target, ignore=shutil.ignore_patterns( + "temp", "__pycache__", "*.pyc", ".git", ".env", ".venv", ".pytest_cache", ".gradio")) + else: + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(source, target) + + +def build(out, *, sources=None, token=None, seed_archive=None, config=None): + from huggingface_hub import hf_hub_download + + out = Path(out).resolve() + out.mkdir(parents=True, exist_ok=True) + sources = sources or json.loads((HERE / "configs/sources.json").read_text()) + archived = PROJECT / "temp/build-archive" + stage = out / "stage" + preserve(stage, archived) + stage.mkdir() + seed = sources["task_bundle"] + path = Path(seed_archive) if seed_archive else Path(hf_hub_download( + seed["repo"], "bundle.tar.gz", repo_type="dataset", revision=seed["revision"], token=token)) + if sha(path) != seed["sha256"]: + raise ValueError("Frozen task bundle hash mismatch") + with tarfile.open(path) as archive: + archive.extractall(stage, filter="data") + # Task names, bytes, catalog indices and curriculum order are inherited from + # this immutable bundle, rather than regenerated from a changing Hub branch. + run = stage / REL_RUN + for name, expected in sources.get("data_hashes", {}).items(): + if sha(run / name) != expected: + raise ValueError("Frozen task manifest changed: " + name) + cache = PROJECT / "temp/source-cache" + cache.mkdir(parents=True, exist_ok=True) + for spec in sources["repositories"]: + target = run / "source" / spec["directory"] + preserve(target, archived) + source_tree(spec, target, cache) + replacements = [ + (HERE, stage / "hf"), + (PROJECT / "train", run / "source/HuggingEnvs/04-data-agent/train"), + (PROJECT / "envs/blackbox-opencode", run / "source/packages/data_agent_env"), + (PROJECT / "envs/whitebox-bash", run / "source/packages/whitebox_bash"), + (PROJECT / "tools", stage / "experiments/daytona_harness_comparison/tools"), + ] + for source, target in replacements: + preserve(target, archived) + _copy(source, target) + for name in ("eval_concurrent.py", "baseline_checks.py", "eval_pass_at_k.py"): + _copy(PROJECT / "eval" / name, run / "eval-source" / name) + _copy(PROJECT / "serve/vllm.sh", run / "eval-source/serve_vllm_tunnel.sh") + for name in ("smoke_multiharness_tito.py", "audit_multiharness_training.py"): + _copy(PROJECT / "tools" / name, run / "source/tools" / name) + if name == "smoke_multiharness_tito.py": + _copy(PROJECT / "tools" / name, run / "eval-source" / name) + # The seed stores the historical operator root. Runtime strings are relocated; + # original manifest contents remain intact to retain their original hashes. + original_root = sources["historical_runtime_root"] + for base in (run / "source", run / "eval-source", stage / "experiments/daytona_harness_comparison/tools"): + for item in base.rglob("*"): + if item.is_file() and item.suffix in {".py", ".sh"}: + text = item.read_text() + relocated = text.replace(original_root, PORTABLE_ROOT) + if relocated != text: + item.write_text(relocated) + if config is not None: + shutil.copy2(config, stage / "hf/configs/deployment.json") + verify_runtime_entrypoints(stage) + paths = {str(p.relative_to(stage)): sha(p) for p in sorted(stage.rglob("*")) + if p.is_file() and p.name != "bundle_manifest.json"} + manifest = {"schema": 2, "sources": sources, "files": paths} + (stage / "bundle_manifest.json").write_text(json.dumps(manifest, indent=2) + "\n") + archive = out / "bundle.tar.gz" + preserve(archive, archived) + with tarfile.open(archive, "w:gz", compresslevel=6) as stream: + for path in sorted(stage.iterdir()): + stream.add(path, arcname=path.name) + metadata = {"sha256": sha(archive), "bytes": archive.stat().st_size, "files": len(paths), "sources": sources} + preserve(out / "bundle.json", archived) + preserve(out / "bootstrap.py", archived) + (out / "bundle.json").write_text(json.dumps(metadata, indent=2) + "\n") + shutil.copy2(HERE / "runtime/bootstrap.py", out / "bootstrap.py") + return metadata + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--out", type=Path, default=PROJECT / "temp/reproduction/bundle") + parser.add_argument("--env-file", type=Path) + parser.add_argument("--sources", type=Path, default=HERE / "configs/sources.json") + parser.add_argument("--config", type=Path, help="Deployment config to freeze in the bundle") + parser.add_argument("--seed-archive", type=Path, help="Use a previously downloaded, hash-verified task bundle") + args = parser.parse_args() + from deploy import credentials + secrets = credentials(args.env_file) + print(json.dumps(build(args.out, sources=json.loads(args.sources.read_text()), + token=secrets["HF_TOKEN"], seed_archive=args.seed_archive, config=args.config))) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/hf/cluster.py b/04-data-agent/hf/cluster.py new file mode 100644 index 0000000..096683b --- /dev/null +++ b/04-data-agent/hf/cluster.py @@ -0,0 +1,109 @@ +"""Stage an isolated copy of the verified portable runtime and submit it to a two-GPU Slurm allocation.""" +import argparse +import hashlib +import json +from pathlib import Path +import shlex +import subprocess +import tarfile + +HERE = Path(__file__).resolve().parent + + +def prepare(bundle, output, env_file, arm, phase, *, partition="hopper-prod", train_venv=None, env_venv=None): + import re + if not re.fullmatch(r"[A-Za-z0-9_-]+", partition): + raise ValueError("Invalid Slurm partition name") + root = output / "repro" + root.mkdir(parents=True, exist_ok=False) + info = json.loads((bundle / "bundle.json").read_text()) + if hashlib.sha256((bundle / "bundle.tar.gz").read_bytes()).hexdigest() != info["sha256"]: + raise ValueError("Bundle digest mismatch") + with tarfile.open(bundle / "bundle.tar.gz") as archive: + archive.extractall(root, filter="data") + # The local adapter is orchestration supplied by this launcher. Its exact + # bytes join the transformed manifest; portable trainer sources stay frozen. + import shutil + shutil.copyfile(Path(__file__).parent / "runtime/local_entry.py", root / "hf/runtime/local_entry.py") + for path in root.rglob("*"): + if path.is_file() and path.suffix in {".py", ".sh"}: + text = path.read_text() + if "/workspace/repro" in text: + path.write_text(text.replace("/workspace/repro", str(root))) + (root / "OpenEnv").mkdir(exist_ok=True) + setup = [] + if bool(train_venv) != bool(env_venv): + raise ValueError("Pass both existing venv paths, or neither to create locked environments") + if train_venv: + for source, target in [(train_venv, root / ".venv312"), (env_venv, root / "OpenEnv/.venv")]: + if not (Path(source) / "bin/python").is_file(): + raise ValueError("Existing venv has no Python interpreter: " + str(source)) + target.symlink_to(Path(source).resolve(), target_is_directory=True) + else: + for name, target in [("env", root / "OpenEnv/.venv"), ("train", root / ".venv312")]: + setup += [shlex.join(["uv", "venv", "--python", "3.12", str(target)]), + shlex.join(["uv", "pip", "sync", "--python", str(target / "bin/python"), + "--require-hashes", str(root / f"hf/locks/requirements-{name}.lock")])] + setup += [shlex.join(["uv", "pip", "install", "--python", str(root / ".venv312/bin/python"), + "--no-deps", "--no-build-isolation", "--editable", + str(root / "experiments/daytona_harness_comparison/logs/20260915/source/trl")])] + # Record every transformed source; the original source snapshot stays untouched. + manifest = json.loads((root / "bundle_manifest.json").read_text()) + manifest["files"] = {name: hashlib.sha256((root / name).read_bytes()).hexdigest() for name in manifest["files"]} + encoded = json.dumps(manifest, sort_keys=True).encode() + (root / "bundle_manifest.json").write_bytes(encoded) + local = {"base_bundle": info["sha256"], "sha256": hashlib.sha256(encoded).hexdigest(), "root": str(root)} + (root / "local_manifest.json").write_text(json.dumps(local, indent=2) + "\n") + role = "eval" if phase == "baseline" else "train" + command = [str(root / ".venv312/bin/python"), "-u", str(root / "hf/runtime/local_entry.py"), + "--role", role, "--arm", arm, "--phase", phase, "--dp", "2" if role == "eval" else "1"] + script = output / "job.slurm" + script.write_text(f'''#!/bin/bash +#SBATCH --job-name=local-{arm}-{phase} +#SBATCH --partition={partition} +#SBATCH --gres=gpu:2 +#SBATCH --cpus-per-task=8 +#SBATCH --mem=96G +#SBATCH --time=04:00:00 +#SBATCH --output={output}/slurm-%j.out +#SBATCH --error={output}/slurm-%j.err +set -euo pipefail +export REPRO_ROOT={shlex.quote(str(root))} +export LOCAL_ENV_FILE={shlex.quote(str(env_file))} +export PYTHONUNBUFFERED=1 +export OMP_NUM_THREADS=1 +export TOKENIZERS_PARALLELISM=false +{chr(10).join(setup)} +exec {shlex.join(command)} +''') + return script, local + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--bundle", type=Path, required=True) + p.add_argument("--out", type=Path, required=True) + p.add_argument("--env-file", type=Path, required=True) + p.add_argument("--arm", choices=["blackbox", "opencode", "whitebox"], required=True) + p.add_argument("--phase", choices=["baseline", "smoke"], required=True) + p.add_argument("--partition", default="hopper-prod") + p.add_argument("--train-venv", type=Path) + p.add_argument("--env-venv", type=Path) + p.add_argument("--submit", action="store_true") + p.add_argument("--dependency", help="Slurm dependency, e.g. afterok:80486") + a = p.parse_args() + a.out = a.out.resolve() + a.out.mkdir(parents=True, exist_ok=True) + script, report = prepare(a.bundle.resolve(), a.out, a.env_file.resolve(), a.arm, a.phase, partition=a.partition, train_venv=a.train_venv, env_venv=a.env_venv) + if a.submit: + command = ["sbatch", "--parsable"] + if a.dependency: + import re + if not re.fullmatch(r"afterok:\d+", a.dependency): + raise ValueError("Use a single afterok Slurm job dependency") + command += ["--dependency", a.dependency] + job = subprocess.check_output(command + [str(script)], text=True).strip() + report["slurm_job"] = job + report["dependency"] = a.dependency + (a.out / "launch.json").write_text(json.dumps(report, indent=2) + "\n") + print(json.dumps(report)) diff --git a/04-data-agent/hf/configs/cadence_validation.json b/04-data-agent/hf/configs/cadence_validation.json new file mode 100644 index 0000000..2deed7b --- /dev/null +++ b/04-data-agent/hf/configs/cadence_validation.json @@ -0,0 +1,87 @@ +{ + "historical_record": true, + "usage": "Dated experiment evidence; active reproduction settings are in deployment.json.", + "scope": "Real launcher commands and controller scheduling logic, using synthetic step metadata; no claim of 100 GPU optimizer updates", + "passed": true, + "main": { + "save_steps": 50, + "eval_steps": 100, + "simulated_saved_steps": [ + 50, + 100, + 150, + 200 + ], + "selected_evals": [ + 100, + 200 + ] + }, + "short": { + "save_steps": 2, + "eval_step": 4, + "step2_triggers_eval": false, + "step4_triggers_eval": true + }, + "training_commands": { + "opencode/smoke": { + "save_steps": 2 + }, + "opencode/long": { + "save_steps": 50 + }, + "whitebox/smoke": { + "save_steps": 2 + }, + "whitebox/long": { + "save_steps": 50 + } + }, + "independent_evaluation_allocations": true, + "live_short_validation": { + "opencode_optimizer_job": "80593", + "opencode_eval_controller": "80594", + "opencode_checkpoint4_eval": "80603", + "whitebox_local_optimizer_job": "80555", + "whitebox_hf_optimizer_job": "6aa9a487f76d6a098a70e3d2", + "whitebox_hf_eval_controller": "80567", + "whitebox_local_eval_controller": "80572", + "whitebox_local_checkpoint4_eval": "80576", + "whitebox_hf_checkpoint4_eval": "6aa9af55f76d6a098a70e52d" + }, + "live_evaluation_validation_complete": true, + "long_training_submitted": true, + "live_short_validation_results": { + "opencode": "passed: corrected optimizer80593, controller80594, checkpoint4eval80603; 8/8 TiTO and version checks, zero owned sandboxes remain", + "whitebox_local": "passed: optimizer80555, controller80572, checkpoint4eval80576; 2/2 TiTO, zero owned sandboxes remain", + "whitebox_hf": "passed: optimizer6aa9a487f76d6a098a70e3d2; four updates, three nonzero-gradient, native state/remote restore/TiTO/changed weights verified. Independent A100 checkpoint4 eval6aa9af55f76d6a098a70e52d completed 250/250, 52 correct, all TiTO at concurrency50." + }, + "local_live_evaluation_validation_complete": true, + "launch_validation": { + "focused_tests_passed": 15, + "dry_run_never_allocates_jobs": true, + "hf_main_readonly_preview_job": "80585", + "after_checkpoint_controller": "80567", + "controller_walltime_main": "36:00:00", + "hf_late_score_replay_job": "80586", + "late_logging_failure_boundary_tests_passed": 2, + "late_logging_live_validation": "passed: CPU80586 replayed checkpoint4 into finished smoke Trackio and proved idempotence; 7 events" + }, + "hf_optimizer_validation_complete": true, + "main_launches": { + "authorized_after_qualification": true, + "opencode": { + "trainer": "80626", + "controller": "80627" + }, + "whitebox": { + "trainer": "6aa9b6c9f76d6a098a70e786", + "controller": "80605" + } + }, + "main_live_cadence_evidence": { + "opencode_checkpoint50_saved_and_published": true, + "original_checkpoint700_eval_job": "80641", + "note": "Main comparison step100 evaluation is still future work; short end-to-end cadence qualifications passed." + } +} diff --git a/04-data-agent/hf/configs/deployment.json b/04-data-agent/hf/configs/deployment.json new file mode 100644 index 0000000..fb69e29 --- /dev/null +++ b/04-data-agent/hf/configs/deployment.json @@ -0,0 +1,391 @@ +{ + "schema_version": 1, + "status": "reproducible_recipes", + "namespace": "HuggingEnvs", + "private": true, + "experiment": "data-agent-daytona", + "run_id": "data-agent-reproduction-20260916", + "resources": { + "trackio_space": null, + "space_visibility": "protected", + "repro_dataset_repo": "HuggingEnvs/data-agent-daytona-repro", + "artifacts_bucket": "HuggingEnvs/data-agent-daytona-artifacts", + "trackio_bucket": "HuggingEnvs/data-agent-daytona-trackio", + "model_repos": { + "blackbox": "HuggingEnvs/qwen35-2b-daytona-opencode", + "whitebox": "HuggingEnvs/qwen35-2b-daytona-whitebox", + "opencode": "HuggingEnvs/qwen35-2b-standalone-opencode" + }, + "environment_spaces": { + "blackbox": "HuggingEnvs/data-agent-blackbox-harbor-env", + "whitebox": "HuggingEnvs/data-agent-seta-whitebox-env", + "opencode": "HuggingEnvs/data-agent-blackbox-opencode-env" + }, + "retired_spaces": [] + }, + "model": { + "id": "Qwen/Qwen3.5-2B", + "revision": "15852e8c16360a2fea060d615a32b45270f8a8fc" + }, + "data": { + "train_tasks": 1000, + "train_difficulty": { + "easy": 150, + "medium": 600, + "hard": 250 + }, + "test_tasks": 250, + "test_difficulty": { + "easy": 33, + "medium": 118, + "hard": 99 + }, + "seed": 0, + "schedule": "same frozen reference task order; all blackbox assignments become opencode; whitebox consumes the same task order", + "manifest_sha256": { + "train_manifest.json": "65ebbf8c86825597e4605ee2f75c2332a6e2144e22413bdc63aa39150cc54703", + "test_manifest.json": "38943d89f5bb0fec79db8c7a2680c4a8353cf5c53c6eabffaaef8971cb2eb964", + "reference_schedule.json": "6a6ea4a085908f3f256a0a605031aaf7f16469797c2b04ffa73092c4a44f2696", + "opencode_schedule.json": "dccc1102d76ccd2c2170239340a836f1c6158d80fe627c04a049e01b426311d6" + } + }, + "training": { + "learning_rate": 3e-06, + "num_generations": 8, + "optim": "paged_adamw_8bit", + "dtype": "bfloat16", + "gradient_checkpointing": true, + "gradient_checkpointing_use_reentrant": false, + "max_model_len": 131072, + "max_completion_length": 16384, + "save_steps": 50, + "max_steps": 1000, + "seed": 0, + "max_train_seconds": 82200, + "checkpoint_max_seconds": 3600, + "walltime": "24h", + "initialization": "pinned base, independent from disposable optimizer smoke", + "sampling": { + "temperature": 0.8, + "top_p": 1.0, + "top_k_disabled": true, + "enable_thinking": false + }, + "rollout_limits": { + "agent_step_limit": 17, + "agent_timeout_seconds": 600 + } + }, + "arms": { + "blackbox": { + "trainer": "AsyncGRPOTrainer + current atomic rollout recipe", + "training_harnesses": [ + "opencode" + ], + "sandbox": "daytona", + "eval_harnesses": [ + "opencode", + "claude-code", + "codex", + "mini-swe-agent" + ], + "evaluations_per_checkpoint": 1000, + "max_staleness": 4, + "max_outstanding_rollouts": 16, + "worker_ceiling": 32, + "token_budget": 40960, + "max_row_tokens": 131072, + "fork_threshold_tokens": 0, + "per_device_batch_size": 4, + "gradient_accumulation_steps": 4, + "atomic_rollouts": true + }, + "whitebox": { + "trainer": "synchronous GRPOTrainer", + "toolsets": [ + "bash", + "seta" + ], + "sandbox": "daytona", + "eval_harnesses": [ + "whitebox_seta" + ], + "evaluations_per_checkpoint": 250, + "beta": 0.0, + "loss_type": "dapo", + "differences_to_validate": [ + "synchronous sampling and batch semantics", + "full-episode completion budget includes masked tool results", + "tool-call submission adapter versus answer.txt", + "TiTO masks and generation ids" + ], + "per_device_batch_size": 1, + "gradient_accumulation_steps": 8, + "max_tool_calling_iterations": 16 + }, + "opencode": { + "trainer": "same atomic AsyncGRPO recipe with native DataAgentSessionFactory", + "training_harnesses": [ + "opencode" + ], + "sandbox": "daytona", + "eval_harnesses": [ + "opencode", + "claude-code", + "codex", + "mini-swe-agent" + ], + "evaluations_per_checkpoint": 1000, + "max_staleness": 4, + "max_outstanding_rollouts": 16, + "worker_ceiling": 32, + "token_budget": 40960, + "max_row_tokens": 131072, + "fork_threshold_tokens": 0, + "per_device_batch_size": 4, + "gradient_accumulation_steps": 4, + "atomic_rollouts": true, + "implementation": "standalone-opencode", + "reward_policy": "binary_correctness; no chat partial-credit or efficiency bonus" + } + }, + "compute": { + "provider": "hf_jobs", + "training_flavor": "h200x2", + "training_jobs": 3, + "trainer_gpus_per_job": 1, + "vllm_gpus_per_job": 1, + "train_tp": 1, + "train_dp": 1, + "eval_flavor": "a100-large", + "eval_tp": 1, + "eval_dp": 1, + "coordinator_flavor": "cpu-upgrade", + "job_namespace": "HuggingEnvs", + "forbidden_new_partitions": [ + "hopper-atl", + "hopper-extra" + ], + "training_hardware_candidates": [ + "h200x2", + "a100x4" + ], + "training_selection": "One independent inference GPU and one optimizer GPU per arm; separate A100 checkpoint evaluation Jobs.", + "environment_provider": "hf_spaces", + "bootstrap_image": "ghcr.io/astral-sh/uv@sha256:85d4cb1afa769a7338e095b927bee941cf5ec92266c7424b3f6c0f2748567248" + }, + "evaluation": { + "metric": "pass@1", + "interval_steps": 100, + "also_final": true, + "concurrency_per_job": 35, + "max_active_jobs_per_arm": 1, + "max_active_jobs_total": 2, + "training_priority": true, + "daytona_joint_capacity_gate": "measure live quotas and reserve both trainers before admitting evals; serialize eval jobs if required", + "base_on_hf": true, + "preserve_prior_baselines": true, + "max_output_tokens_per_call": 4096, + "max_model_calls": 17, + "episode_timeout_seconds": 600, + "first_graded_result_immutable": true, + "retry_only_ungraded_infrastructure_failures": true, + "publish_score_requires": [ + "complete fixed cell coverage", + "TiTO pass", + "matching harness pins", + "matching task hashes", + "checkpoint hash verified" + ], + "concurrency_per_arm": { + "blackbox": 35, + "opencode": 35, + "whitebox": 35 + }, + "opencode_backend_concurrency": { + "daytona": 35, + "hf": 35 + } + }, + "harness_pins": { + "opencode": "1.18.31", + "claude-code": "2.1.270", + "codex": "0.154.0", + "mini-swe-agent": "2.4.6" + }, + "serving": { + "launcher": "frozen eval-source/serve_vllm_tunnel.sh with portable paths", + "max_model_len": 131072, + "train_gpu_memory_utilization": 0.85, + "eval_gpu_memory_utilization": 0.9, + "vllm_server_dev_mode": true, + "logprobs_mode": "processed_logprobs", + "return_tokens_as_token_ids": true, + "prefix_caching": false, + "gdn_prefill_backend": "triton", + "flashinfer_sampler": false, + "tool_call_parser": "qwen3_xml", + "reasoning_parser": "qwen3", + "thinking": false, + "model_network": "authenticated HF Jobs endpoint for Space-to-vLLM; private Space accessed through a local auth bridge; capture-only tunnel for Daytona", + "shared_service": { + "transport_sessions": 1024, + "sandbox_capacity_per_arm": { + "blackbox": 64, + "whitebox": 61, + "opencode": 100 + }, + "train_reserved_per_arm": { + "blackbox": 16, + "whitebox": 8, + "opencode": 8 + }, + "status": "Deployment caps; recheck sandbox team quotas before concurrent jobs.", + "ui_sessions": 8, + "hardware": "cpu-basic", + "daytona_quota": { + "region": "eu", + "cpu": 250, + "memory_gib": 500, + "disk_gib": 2000, + "checked_utc": "2026-09-15T14:00:00Z" + } + } + }, + "durability": { + "local_training_disk": true, + "async_upload": true, + "checkpoint_upload_commit": "immutable files -> manifest verification -> ready marker last", + "persist": [ + "model", + "optimizer", + "scheduler", + "RNG", + "native rollout cursor", + "token captures", + "metrics JSONL", + "source and dependency hashes", + "eval first-graded ledger" + ], + "eval_identity_fields": [ + "run_id", + "arm", + "step", + "checkpoint_sha256", + "eval_protocol_sha256" + ], + "resume": "full native checkpoint; in-flight groups may be regenerated", + "local_checkpoint_delete_only_after_remote_verified": true + }, + "monitoring": { + "startup_interval_seconds": 120, + "stable_interval_seconds": 600, + "stable_after_optimizer_steps": 10, + "progressing_checks_required": 2, + "chat_notifications_enabled": false, + "persistent_coordinator": true, + "space_restart_must_not_stop_training": true, + "max_automatic_training_restarts_per_arm": 2, + "fatal_stop_reasons": [ + "TiTO corruption", + "non-finite model updates", + "invalid checkpoint provenance" + ] + }, + "logging": { + "trackio_version": "0.33.0", + "offline": "append-only metrics JSONL + SQLite replay backup", + "online": "Asynchronous Trackio event/database upload to the artifact bucket; optional shared dashboard replay.", + "network_in_optimizer_path": false, + "deduplicate_events": true, + "axes": [ + "optimizer step", + "wall time", + "completed rollouts", + "supervised tokens", + "unique task coverage" + ], + "projects": { + "blackbox": "daytona-blackbox-qwen35-2b", + "whitebox": "daytona-whitebox-qwen35-2b", + "opencode": "daytona-opencode-qwen35-2b" + }, + "space_private": false, + "visibility": "Training Trackio databases and events persist with run artifacts", + "scope": "training jobs only; environment Spaces have no logging dashboard" + }, + "gates": [ + "portable bundle and two locked environments", + "HF CPU import/config/artifact roundtrip preflight", + "HF Daytona task+TiTO smoke for both arms", + "HF base pass@1 and concurrency ramp", + "each arm: 2 optimizer steps -> save2 -> fresh trainer resume -> step4 -> verified checkpoint", + "each arm: full training from base", + "separate checkpoint eval jobs and durable logging verified" + ], + "historical_baselines": { + "blackbox": { + "correct": 159, + "cells": 1000, + "pass_at_1": 0.159, + "platform": "Slurm H100 / Daytona" + }, + "whitebox": { + "correct": 41, + "cells": 250, + "pass_at_1": 0.164, + "platform": "Slurm H100 / Daytona" + } + }, + "runtime_lock_adjustments": { + "reason": "resolve incompatible dependencies in the inherited trainer environment before building the HF runtime", + "numpy": "vLLM/mistral-common on Python 3.12 requires <2.4", + "websockets": "15.0.1; Harbor/Supabase requires <16", + "requires_fresh_runtime_validation": true + }, + "access": { + "org_jobs_read_verified": true, + "credential_source": "HF_API_KEY in experiments/.env, explicitly selected instead of ambient OAuth", + "credentials_uploaded_as_files": false + }, + "dependency_locks": { + "requirements-env.lock": { + "sha256": "a73c0e2d1326eff124d0b38a3203a050cbbb110ec2e4acd094f7b07ef9a4c444", + "resolved_packages": 178 + }, + "requirements-train.lock": { + "sha256": "54d630e16823edd5e1504a92deb6745afc9a53cc9514753a175ba25db7f0aa7d", + "resolved_packages": 294 + } + }, + "implementations": { + "blackbox": "Harbor adapter (historical arm key; retain as Blackbox Harbor)", + "opencode": "envs/blackbox-opencode standalone OpenCode", + "whitebox": "native SETA/bash" + }, + "scaling_note": "Standalone Daytona baseline may use100slots while Harbor/SETA are idle. Re-budget joint125Daytona capacity before parallel trainers/evals.", + "training_smoke": { + "authorized": true, + "steps": [ + 2, + 4 + ], + "save_steps": 2, + "preserve_live_spaces": true, + "space_bundle_pins": { + "opencode": "3dbab07f0abad40dadd556262cbfa5d19730d02c0a3c5e0c2b3f8cf8aba58c3e", + "whitebox": "f61d3e774ec58e8d2279fcac0611a62620c2fc3d986932d069d58a4c39d389ec" + }, + "opencode_wait_for_daytona_baseline_job": "6aa98b715527934177ee5e73", + "whitebox_sandbox_budget": 8, + "opencode_sandbox_budget": 16, + "long_launches_held": true + }, + "pipeline": { + "baseline_jobs": { + "blackbox": null, + "whitebox": null + }, + "note": "Optional legacy Harbor/SETA coordinator; reproduce.py selects one explicit recipe." + } +} diff --git a/04-data-agent/hf/configs/local_comparison.json b/04-data-agent/hf/configs/local_comparison.json new file mode 100644 index 0000000..c83be6a --- /dev/null +++ b/04-data-agent/hf/configs/local_comparison.json @@ -0,0 +1,99 @@ +{ + "historical_record": true, + "usage": "Dated experiment evidence; active reproduction settings are in deployment.json.", + "status": "native_explicit_tolerance_correction_requalification_pending", + "compute": { + "partition": "hopper-prod", + "training_gpus_per_arm": 2, + "whitebox_training_provider": "HF Jobs", + "whitebox_training_flavor": "h200x2", + "opencode_training_provider": "Slurm hopper-prod", + "trainer_gpus": 1, + "inference_gpus": 1, + "evaluation_gpus": 2, + "gpu_type": "H100", + "eval_tp": 1, + "eval_dp": 2, + "environment": "local native OpenCode service; existing native SETA HF Space for Whitebox", + "sandbox": "daytona" + }, + "model": { + "id": "Qwen/Qwen3.5-2B", + "revision": "15852e8c16360a2fea060d615a32b45270f8a8fc" + }, + "data": { + "train_tasks": 1000, + "train_difficulty": { + "easy": 150, + "medium": 600, + "hard": 250 + }, + "test_tasks": 250, + "test_difficulty": { + "easy": 33, + "medium": 118, + "hard": 99 + }, + "task_manifests": "Same frozen manifests as configs/deployment.json" + }, + "training": { + "learning_rate": 3e-06, + "num_generations": 8, + "save_steps": 50, + "max_steps": 1000, + "walltime_hours": 24, + "initialization": "pinned base; smoke checkpoints are disposable", + "opencode": { + "implementation": "native standalone OpenCode, not Harbor training", + "recipe": "same atomic AsyncGRPO recipe as original multi-harness run", + "max_staleness": 4, + "worker_ceiling": 32, + "max_outstanding_rollouts": 16, + "sandbox_budget": 16, + "smoke_job": "80549", + "prepared_main_plan": "experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-main-ready/plan.json" + }, + "whitebox": { + "implementation": "native synchronous bash/SETA GRPO", + "sandbox_budget": 8, + "smoke_job": "6aa9a487f76d6a098a70e3d2", + "additional_local_smoke_job": "80555", + "additional_local_smoke_compute": "hopper-prod, two H100s; same portable source and frozen data", + "prepared_main_plan": "experiments/daytona_harness_comparison/logs/hf-20260915/hf-whitebox-main-ready/plan.json" + } + }, + "evaluation": { + "metric": "pass@1", + "concurrency": 50, + "ramp": false, + "interval_steps": 100, + "opencode_native_baseline_cells": 250, + "opencode_training_checkpoint_harnesses": [ + "opencode", + "claude-code", + "codex", + "mini-swe-agent" + ], + "opencode_training_checkpoint_cells": 1000, + "whitebox_training_checkpoint_cells": 250, + "separate_gpu_allocations": true, + "checkpoint_policy": "immutable completed weights; no optimizer or inference synchronization with trainer", + "sandbox_capacity_policy": "Reserve both trainers before admitting evals; serialize eval jobs if joint capacity is insufficient", + "retry_policy": "Only infrastructure failures without a grade; preserve first graded zero or one", + "remaining_work": "Native corrected optimizer/checkpoint requalification, HF checkpoint evaluation, late Trackio replay, final launch previews", + "compute_by_arm": { + "opencode": "independent hopper-prod 2 H100, TP1/DP2", + "whitebox": "independent HF a100-large, TP1/DP1" + } + }, + "gates": { + "full_native_baseline": "passed: 21/250, 8.4% pass@1, 250/250 TiTO, zero ungraded attempts, job80514", + "whitebox_optimizer_save_remote_resume": "passed: 6aa9a487f76d6a098a70e3d2 completed all4 updates,3 nonzero-gradient; exact TiTO, changed weights, native optimizer state and remote restore verified", + "opencode_optimizer_save_remote_resume": "80549 passed earlier runtime; corrected explicit-zero-tolerance runtime is staging in local-opencode-smoke-v4 for requalification", + "local_checkpoint_eval_controller": "passed: OpenCode80565 (8/8 cells), Whitebox80576 (2/2 cells); independent TP1/DP2 H100s, exact TiTO, cleanup0", + "long_training_submitted": false, + "whitebox_local_optimizer_save_remote_resume": "passed:80555, all4updates,2nonzero-gradient updates, exact TiTO, changed weights, native optimizer state and remote restore; zero owned sandboxes remain", + "whitebox_hf_checkpoint_eval": "running: 6aa9af55f76d6a098a70e52d, A10080GB,250 fixed tests,pass@1,concurrency50", + "native_baseline_with_frozen_tolerances": "passed: deterministic rescore of all250 unchanged first responses;21correct,0changed grades; all1250 task grading parameters match" + } +} diff --git a/04-data-agent/hf/configs/sources.json b/04-data-agent/hf/configs/sources.json new file mode 100644 index 0000000..d883d74 --- /dev/null +++ b/04-data-agent/hf/configs/sources.json @@ -0,0 +1,29 @@ +{ + "schema_version": 1, + "historical_runtime_root": "/fsx/adithyaskolavi/projects/trl_prod", + "task_bundle": { + "repo": "HuggingEnvs/data-agent-daytona-repro", + "revision": "b4714cb652f71d519ce74c564f9fe6110cec032f", + "sha256": "f4a288eaafefddf3cb686eb89de184437a0496f75074a00f1903a9335dcd1a0d" + }, + "repositories": [ + { + "name": "openenv", + "directory": "OpenEnv", + "url": "https://github.com/adithya-s-k/OpenEnv.git", + "revision": "b13aeb9f8ecd4817e02d3a37c2a9ae15e41710e3" + }, + { + "name": "trl", + "directory": "trl", + "url": "https://github.com/adithya-s-k/trl.git", + "revision": "8e87edb45eac7c52d749256379714fa40f0eb746" + } + ], + "data_hashes": { + "train_manifest.json": "65ebbf8c86825597e4605ee2f75c2332a6e2144e22413bdc63aa39150cc54703", + "test_manifest.json": "38943d89f5bb0fec79db8c7a2680c4a8353cf5c53c6eabffaaef8971cb2eb964", + "reference_schedule.json": "6a6ea4a085908f3f256a0a605031aaf7f16469797c2b04ffa73092c4a44f2696", + "opencode_schedule.json": "dccc1102d76ccd2c2170239340a836f1c6158d80fe627c04a049e01b426311d6" + } +} diff --git a/04-data-agent/hf/configs/training_comparison_validation.json b/04-data-agent/hf/configs/training_comparison_validation.json new file mode 100644 index 0000000..a47f1ca --- /dev/null +++ b/04-data-agent/hf/configs/training_comparison_validation.json @@ -0,0 +1,44 @@ +{ + "historical_record": true, + "usage": "Dated experiment evidence; active reproduction settings are in deployment.json.", + "passed": true, + "reference_job": 79083, + "async_matched": { + "learning_rate": 3e-06, + "num_generations": 8.0, + "max_inflight": 32.0, + "max_staleness": 4.0, + "per_device_batch_size": 4.0, + "gradient_accumulation_steps": 4.0, + "token_budget": 40960.0, + "max_row_tokens": 131072.0, + "max_outstanding_rollouts": 16.0, + "max_completion_length": 16384.0, + "save_steps": 50.0 + }, + "whitebox_matched": { + "learning_rate": 3e-06, + "num_generations": 8, + "max_completion_length": 16384, + "temperature": 0.8, + "top_p": 1.0, + "optim": "paged_adamw_8bit", + "bf16": true, + "seed": 0, + "lr_scheduler_type": "constant" + }, + "data_manifest_matches": true, + "differences": [ + "SETA synchronous GRPO: batch1/GAS8, native DAPO loss and full-episode completion budget", + "Standalone OpenCode uses its native host verifier; binary success excludes chat/efficiency shaping", + "HF smoke GPU pair differs from the reference H100 pair", + "Smoke saves at2 and resumes to4; long recipe saves50/evals100" + ], + "gpu_optimizer_verified": true, + "gpu_optimizer_scope": "Native OpenCode and SETA qualified locally; SETA also qualified on HF H200", + "optimizer_proofs": { + "opencode": "experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v3/repro/outputs/local-train-opencode-80549/training_smoke_verified.json", + "whitebox_local": "experiments/daytona_harness_comparison/logs/hf-20260915/local-whitebox-smoke-v2/repro/outputs/local-train-whitebox-80555/training_smoke_verified.json", + "whitebox_hf": "experiments/daytona_harness_comparison/logs/hf-20260915/whitebox-h200-package-fix/downloads/training_smoke_verified.json" + } +} diff --git a/04-data-agent/hf/consolidate_async_runs.py b/04-data-agent/hf/consolidate_async_runs.py new file mode 100644 index 0000000..516e78a --- /dev/null +++ b/04-data-agent/hf/consolidate_async_runs.py @@ -0,0 +1,344 @@ +"""Rebuild the two-run report, figure and uniform Trackio project from audited sources.""" +import argparse +import csv +from datetime import datetime, timezone +import hashlib +import json +import math +import os +from pathlib import Path +import sys +import time +from urllib.parse import urlencode + +REPO = Path(os.environ.get("TRL_PROD", Path.cwd())) +TOOLS = Path(__file__).resolve().parents[1] / "tools" +sys.path.insert(0, str(TOOLS)) +import trackio_multi4 as native + +PROJECT = "qwen35-2b-harbor-vs-opencode-20260916" +SPACE = "HuggingEnvs/data-agent-training-comparison-trackio" +BUCKET = "HuggingEnvs/data-agent-training-comparison-trackio" +MAIN = REPO / "experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-cont-20260915" +COMPARISON = REPO / "experiments/daytona_harness_comparison/logs" +OUTPUTS = COMPARISON / "hf-20260915/local-opencode-smoke-v4/repro/outputs" +DEFAULT_OUT = REPO / "HuggingEnvs/04-data-agent/reports/async-comparison-20260916" +ARMS = ("Harbor multi-harness", "Native OpenCode") +HARNESSES = ("opencode", "claude-code", "codex", "mini-swe-agent") +LEVELS = ("easy", "medium", "hard") +COUNTS = dict(zip(LEVELS, (33, 118, 99))) +COLORS = dict(zip(ARMS, ("#4f46e5", "#ea7c24"))) + + +def read(path): + return json.loads(Path(path).read_text()) + + +def validated_score(score): + if not all(score.get(k) is True for k in ("complete", "comparison_ready", "tito_pass", "harness_versions_match_baseline")): + raise ValueError("Score has not passed all comparison gates") + if score.get("graded_cells") != 1000 or set(score["harnesses"]) != set(HARNESSES): + raise ValueError("Expected the complete four-harness cohort") + for h in HARNESSES: + s = score["harnesses"][h] + if s["graded"] != 250: + raise ValueError("Incomplete harness coverage") + if set(s["difficulty"]) != set(LEVELS): + raise ValueError("Missing difficulty category") + for d, n in COUNTS.items(): + c = s["difficulty"][d] + if c["graded"] != n or not 0 <= c["correct"] <= n: + raise ValueError("Unexpected difficulty cohort") + if not math.isclose(sum(c["correct"] for c in s["difficulty"].values()) / 250, s["pass_at_1"]): + raise ValueError("Difficulty totals do not reconcile") + if not math.isclose(sum(s["pass_at_1"] for s in score["harnesses"].values()) / 4, score["average_pass_at_1"]): + raise ValueError("Harness average does not reconcile") + return score + + +def collect(): + result = {"updated_utc": datetime.now(timezone.utc).isoformat(), "project": PROJECT, + "space": SPACE, "runs": {}, "pending": []} + protocol = read(REPO / "HuggingEnvs/04-data-agent/eval/baseline_protocol.json") + baseline_file = Path(protocol["baseline_run"]) / f"job-{protocol['baseline_job']}/canonical_results.json" + base = read(baseline_file) + assert base["coverage_complete"] + harbor_base = {"complete": True, "comparison_ready": True, "tito_pass": True, + "harness_versions_match_baseline": True, "graded_cells": 1000, + "average_pass_at_1": protocol["average_pass_at_1"], "harnesses": {}} + assert protocol["tito"]["passed"] == protocol["tito"]["audited"] == 1000 + for h in HARNESSES: + harbor_base["harnesses"][h] = {"graded": 250, "pass_at_1": protocol["scores"][h]["pass_at_1"], + "difficulty": base["harnesses"][h]["difficulty"]} + segments = native.training_lineage(MAIN, "80608") + training = [row for segment in segments for row in segment["rows"]] + sources = [{k: segment[k] for k in ("job", "start_step", "end_step", "training")} for segment in segments] + result["runs"][ARMS[0]] = {"training": training, "lineage": sources, "evaluations": {}, + "baseline_context": "Original four-harness baseline on E2B; resumed training has documented recipe changes."} + result["runs"][ARMS[1]] = {"training": [r for r in native.read_metrics(OUTPUTS / "local-train-opencode-80626/audit/metrics.jsonl") if "grad_norm" in r], + "lineage": [{"job": "80626", "start_step": 1, "end_step": 1000}], + "evaluations": {}, "baseline_context": "Four-harness Harbor/Daytona baseline; standalone native 8.4% baseline is a different evaluation and is excluded."} + command = read(OUTPUTS / "local-train-opencode-80626/training_recipe.json")["command"] + recipe = {} + for i, key in enumerate(command): + if key in {"--learning-rate", "--num-generations", "--max-inflight", "--max-staleness", "--grad-accum", + "--max-outstanding-rollouts", "--max-row-tokens", "--per-device-batch-size", "--agent-step-limit", + "--agent-timeout", "--token-budget", "--max-completion-length", "--dtype", "--top-p", "--temperature", "--save-steps"}: + recipe[key[2:].replace("-", "_")] = command[i+1] + recipe["atomic_rollouts"] = "--atomic-rollouts" in command + result["runs"][ARMS[1]]["lineage"][0]["training"] = recipe + def add(arm, step, score, path): + validated_score(score) + value = {"step": step, "score": score, "source": str(path), + "source_sha256": hashlib.sha256(Path(path).read_bytes()).hexdigest()} + prior = result["runs"][arm]["evaluations"].get(step) + if prior and prior["score"] != score: + raise ValueError("Conflicting checkpoint evaluations") + result["runs"][arm]["evaluations"][step] = value + add(ARMS[0], 0, harbor_base, baseline_file) + native_base = COMPARISON / "20260915/blackbox/canonical_scores.json" + add(ARMS[1], 0, read(native_base), native_base) + for root in native.evaluation_roots(MAIN): + for directory in sorted((root / "checkpoint-evals").glob("step-*")): + path = directory / "scores.json" + if not path.exists() or not read(path).get("comparison_ready"): + continue + if read(directory / "eval_plan.json")["protocol"] != protocol: + raise ValueError("Harbor evaluation protocol changed") + add(ARMS[0], int(directory.name.split("-")[-1]), read(path), path) + for evidence_path in sorted(OUTPUTS.glob("local-eval-opencode-*/checkpoint_evaluation.json")): + evidence = read(evidence_path) + if evidence["step"] < 50: + continue + path = evidence_path.parent / "canonical_scores.json" + if path.exists() and read(path).get("comparison_ready"): + if "/local-train-opencode-80626/" not in evidence["checkpoint_prefix"]: + raise ValueError("Native evaluation belongs to another trainer") + add(ARMS[1], evidence["step"], read(path), path) + registry = DEFAULT_OUT / 'additional_harbor_runs.json' + for arm, location in (read(registry).items() if registry.exists() else []): + if arm in result['runs']: + raise ValueError('Additional run would overwrite a reference run') + root = Path(location) + cfg = read(root/'run_config.json') + if (cfg['model'] != protocol['model'] or cfg['model_revision'] != protocol['model_revision'] + or read(Path(cfg['evaluation']['protocol_file'])) != protocol): + raise ValueError('Additional Harbor run has a different model or evaluation protocol') + receipt = read(root/'submission.json') if (root/'submission.json').exists() else {} + segments = native.training_lineage(root, receipt['training']) if receipt.get('training') else [] + result['runs'][arm] = {'training': [r for s in segments for r in s['rows']], + 'lineage': [{k:s[k] for k in ('job','start_step','end_step','training')} for s in segments], + 'evaluations': {}, 'target_steps': cfg['training']['max_steps'], + 'baseline_context': 'Fresh pinned base. Same original Harbor/E2B four-harness baseline; trains OpenCode only.'} + add(arm, 0, harbor_base, baseline_file) + for path in sorted((root/'checkpoint-evals').glob('step-*/scores.json')): + if not read(path).get('comparison_ready'): + continue + if read(path.parent/'eval_plan.json')['protocol'] != protocol: + raise ValueError('Additional checkpoint protocol differs from baseline') + add(arm, int(path.parent.name.split('-')[-1]), read(path), path) + for arm, run in result["runs"].items(): + steps = [r["step"] for r in run["training"]] + if steps != list(range(1, max(steps, default=0) + 1)): + raise ValueError("Training history contains gaps or duplicates") + for step in range(100, run.get('target_steps',max(steps,default=0)) + 1, 100): + if step not in run["evaluations"]: + result["pending"].append({"run": arm, "step": step}) + return result + + +def score_metrics(score, baseline): + metrics = {"eval/pass_at_1": score["average_pass_at_1"], + "eval/delta_from_baseline": score["average_pass_at_1"] - baseline["average_pass_at_1"], + "eval/graded_cells": score["graded_cells"]} + for h in HARNESSES: + s = score["harnesses"][h] + metrics[f"eval/harness/{h}/pass_at_1"] = s["pass_at_1"] + for d in LEVELS: + c = s["difficulty"][d] + metrics[f"eval/harness_difficulty/{h}/{d}/pass_at_1"] = c["correct"] / c["graded"] + for d in LEVELS: + cs = [score["harnesses"][h]["difficulty"][d] for h in HARNESSES] + metrics[f"eval/difficulty/{d}/pass_at_1"] = sum(c["correct"] for c in cs) / sum(c["graded"] for c in cs) + return metrics + + +def events(snapshot): + records = [] + for arm, run in snapshot["runs"].items(): + config = {"model": "Qwen/Qwen3.5-2B", "training_arm": arm, "metric_axis": "optimizer step", + "test_tasks": 250, "eval_harnesses": list(HARNESSES), "pass_k": 1, + "difficulty_tasks": COUNTS, "baseline_context": run["baseline_context"], + "training_lineage": run["lineage"], "training_configuration_changes_preserved": True, + "plot_units": "scores and rewards are fractions in [0,1]; time metrics are seconds"} + rewards, grads = [], [] + for row in run["training"]: + metrics = native.scalars({k: v for k, v in row.items() if k != "step"}, "train/") + reward = row.get("reward", row.get("rewards/harness_reward")) + if reward is not None: + rewards.append(reward) + metrics["train/reward"] = reward + metrics["train/reward_rolling20"] = sum(rewards[-20:]) / len(rewards[-20:]) + grads.append(int(row["grad_norm"] != 0)) + metrics["train/nonzero_gradient_rolling20"] = sum(grads[-20:]) / len(grads[-20:]) + records.append(native.event(PROJECT, arm, row["step"], metrics, config, identity="training")) + baseline = run["evaluations"][0]["score"] + for step, value in sorted(run["evaluations"].items()): + records.append(native.event(PROJECT, arm, step, score_metrics(value["score"], baseline), config, identity="evaluation")) + return records + + +def report(snapshot, out): + def pct(x): return f"{100*x:.1f}%" + arms = list(snapshot['runs']) + counts = '; '.join(f"{a}: {len(r['training'])} steps" for a,r in snapshot['runs'].items()) + lines = ["# Harbor and OpenCode — consolidated training and pass@1", "", f"Updated: {snapshot['updated_utc']}", "", + f"[Live Trackio dashboard](https://huggingface.co/spaces/{SPACE}) · [Overview image](comparison.png) · [Snapshot](snapshot.json)", "", + "Qwen3.5-2B; 1,000 optimizer-step target per run. Recorded training: " + counts + ". Every accepted checkpoint has 250 fixed tasks × four harnesses = 1,000 grades. Task difficulty: 33 easy, 118 medium, 99 hard (13.2% / 47.2% / 39.6%). Scores retain first graded attempts; incomplete and failed-audit evaluations are excluded. Missing scores are not estimated.", "", + "Baselines are separate measured cohorts: Harbor/E2B 14.6%; Harbor/Daytona 15.9% for the native OpenCode checkpoint evaluator. The standalone native OpenCode 8.4% baseline uses a different harness protocol and is excluded here. Infrastructure and training recipe histories differ; this is an observational comparison, not a controlled causal experiment.", "", + "## Overall checkpoint curve", "", "| Checkpoint | " + ' | '.join(arms) + ' |', "| --- | " + ' | '.join('---:' for _ in arms) + ' |'] + steps = sorted({0, *range(100,1001,100), *(s for r in snapshot["runs"].values() for s in r["evaluations"])}) + for step in steps: + cells = [pct(snapshot["runs"][a]["evaluations"][step]["score"]["average_pass_at_1"]) if step in snapshot["runs"][a]["evaluations"] else ("Pending" if step % 100 == 0 else "Not scheduled") for a in arms] + label = "0 (baseline)" if step == 0 else str(step) + (" (recovery)" if step % 100 else "") + lines.append(f"| {label} | {' | '.join(cells)} |") + for arm, run in snapshot["runs"].items(): + lines += ["", "## " + arm, "", "### Overall and difficulty", "", "| Checkpoint | Overall | Easy (132 cells) | Medium (472) | Hard (396) |", "| --- | ---: | ---: | ---: | ---: |"] + for step, value in sorted(run["evaluations"].items()): + m = score_metrics(value["score"],run["evaluations"][0]["score"]) + lines.append(f"| {step} | " + " | ".join(pct(m[k]) for k in ["eval/pass_at_1",*[f"eval/difficulty/{d}/pass_at_1" for d in LEVELS]]) + " |") + lines += ["", "### Harness × difficulty at every checkpoint", "", "| Checkpoint | Harness | Overall (250) | Easy (33) | Medium (118) | Hard (99) |", "| --- | --- | ---: | ---: | ---: | ---: |"] + for step, value in sorted(run["evaluations"].items()): + for h in HARNESSES: + s=value["score"]["harnesses"][h] + cells = [pct(s["pass_at_1"])] + [f"{pct(s['difficulty'][d]['correct']/COUNTS[d])} ({int(s['difficulty'][d]['correct'])}/{COUNTS[d]})" for d in LEVELS] + lines.append(f"| {step} | {h} | " + " | ".join(cells) + " |") + lines += ["", "### Training history", "", "| Allocation | First optimizer step | Last optimizer step |", "| --- | ---: | ---: |"] + lines += [f"| {s['job']} | {s['start_step']} | {s['end_step']} |" for s in run["lineage"]] + lines += ["", "### Score provenance", ""] + lines += [f"- Step {s}: `{v['source']}`; SHA256 `{v['source_sha256']}`." for s,v in sorted(run["evaluations"].items())] + lines += ["", "## Dashboard metric guide", "", "Both runs use identical metric names and optimizer-step axes. `eval/pass_at_1` is the overall score; `eval/difficulty/*` aggregates each difficulty; `eval/harness/*` compares each harness; `eval/harness_difficulty/*` contains all twelve intersections. `train/*` preserves recorded loss, reward, learning rate, gradient norm, entropy, KL, staleness, throughput, token, batching and rollout metrics where observed. Missing metrics are not filled with zeros. `train/reward_rolling20` and `train/nonzero_gradient_rolling20` are explicitly derived trailing windows. Raw metrics remain available. Use zero dashboard smoothing for exact checkpoint values.", "", "The independent CPU publisher refreshes every 60 seconds and admits new evaluations only after their full comparison gates pass. It never changes trainer state. Local SQLite backup, event ledger and remote exact-content verification receipts are kept alongside this report.", "", + "Storage and deployment follow the [Trackio guide](https://huggingface.co/docs/trackio/quickstart) and [environment configuration](https://huggingface.co/docs/trackio/environment_variables).", ""] + ids = ','.join(native.digest([PROJECT, a])[:32] for a in arms) + for label, pattern in [('Overview', '^(eval/pass_at_1|train/reward_rolling20)$'), + ('Difficulty', '^eval/difficulty/'), ('Harness', '^eval/harness/'), + ('Harness × difficulty', '^eval/harness_difficulty/'), + ('Optimizer diagnostics', '^train/(loss|grad_norm|entropy|kl|learning_rate|nonzero_gradient_rolling20)$'), + ('Throughput and rollout diagnostics', '^train/(perf|rollout|sample|batch)/'), + ('All metrics', '')]: + url = 'https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?' + urlencode( + dict(project=PROJECT, run_ids=ids, smoothing=0, metric_filter=pattern)) + lines.append(f'- [{label}]({url})') + lines += ['', '[Download checkpoint scores as CSV](checkpoint_scores.csv)', ''] + (out / "REPORT.md").write_text("\n".join(lines)) + with (out / "checkpoint_scores.csv").open("w") as stream: + writer = csv.writer(stream) + writer.writerow(["run", "checkpoint", "harness", "difficulty", "correct", "graded", "pass_at_1"]) + for arm, run in snapshot["runs"].items(): + for step, value in sorted(run["evaluations"].items()): + for h in HARNESSES: + for d in LEVELS: + c = value["score"]["harnesses"][h]["difficulty"][d] + writer.writerow([arm, step, h, d, int(c["correct"]), c["graded"], c["correct"]/c["graded"]]) + + +def plot(snapshot, out): + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + from matplotlib.ticker import PercentFormatter, MultipleLocator + plt.rcParams.update({"font.family":"DejaVu Sans", "font.size":10, "axes.titlesize":12, + "axes.titleweight":"bold", "axes.edgecolor":"#d5dce6", "text.color":"#18243a", + "axes.labelcolor":"#596579", "xtick.color":"#596579", "ytick.color":"#596579"}) + fig=plt.figure(figsize=(18,12),facecolor="#f5f7fb") + gs=fig.add_gridspec(3,12,left=.055,right=.98,bottom=.10,top=.85,hspace=.54,wspace=1.4,height_ratios=[1.2,1,1]) + axes=[fig.add_subplot(gs[0,:6]),fig.add_subplot(gs[0,6:])] + axes += [fig.add_subplot(gs[1,i*4:(i+1)*4]) for i in range(3)] + axes += [fig.add_subplot(gs[2,i*3:(i+1)*3]) for i in range(4)] + titles=["Overall held-out pass@1","Training reward · trailing 20 updates",*[f"{d.title()} tasks · {COUNTS[d]} per harness" for d in LEVELS],"OpenCode","Claude Code","Codex","Mini-SWE-Agent"] + for ax,title in zip(axes,titles): + ax.set_facecolor("white");ax.set_title(title,loc="left",pad=12);ax.grid(axis="y",alpha=.22) + ax.spines[['top','right']].set_visible(False);ax.set_xlim(0,1000);ax.set_ylim(0,100 if ax==axes[1] else 80) + ax.yaxis.set_major_formatter(PercentFormatter(100));ax.xaxis.set_major_locator(MultipleLocator(200));ax.set_xlabel("Optimizer step") + for arm,run in snapshot["runs"].items(): + color=COLORS.get(arm,'#159a85');points=sorted(run["evaluations"].items());x=[s for s,_ in points] + metrics=[score_metrics(v["score"],run["evaluations"][0]["score"]) for _,v in points] + keys=["eval/pass_at_1",*[f"eval/difficulty/{d}/pass_at_1" for d in LEVELS],*[f"eval/harness/{h}/pass_at_1" for h in HARNESSES]] + for ax,key in zip([axes[0],*axes[2:]],keys): + ax.plot(x,[100*m[key] for m in metrics],color=color,marker='o',markersize=4,linewidth=2.3,label=arm) + vals=[r.get("reward",r.get("rewards/harness_reward")) for r in run["training"]] + avg=[sum(vals[max(0,i-19):i+1])/len(vals[max(0,i-19):i+1]) for i in range(len(vals))] + axes[1].plot([r['step'] for r in run['training']],[100*v for v in avg],color=color,linewidth=1.6,alpha=.95) + trained_points=[v for v in points if v[0]>0] + if not trained_points: + continue + best=max(trained_points,key=lambda v:v[1]["score"]["average_pass_at_1"]) + bx=best[0];by=100*best[1]['score']['average_pass_at_1'] + axes[0].scatter([bx],[by],s=130,color=color,marker='*',zorder=5) + axes[0].annotate(f"Best {by:.1f}% · step {bx}",(bx,by),xytext=(8,12 if arm==ARMS[0] else -22),textcoords='offset points',color=color,weight='bold',fontsize=10) + axes[0].set_ylim(0,50) + fig.text(.055,.96,"Qwen3.5-2B / Training comparisons",fontsize=25,weight='bold') + fig.text(.055,.926,f"{len(snapshot['runs'])} runs · 1,000-step targets · recorded training and audited evaluations",fontsize=13,color="#596579") + handles,labels=axes[0].get_legend_handles_labels();fig.legend(handles,labels,loc='upper right',bbox_to_anchor=(.98,.962),frameon=False,fontsize=12) + when=snapshot['updated_utc'][:16].replace('T',' ') + fig.text(.055,.05,f"AUDITED PASS@1 · 250 fixed tests × 4 harnesses · Updated {when} UTC",fontsize=10,weight='bold') + fig.text(.055,.026,"Separate measured baseline cohorts (E2B / Daytona). Curves stop at the last audited checkpoint; pending evaluations are omitted. Full harness × difficulty tables accompany this figure.",fontsize=9,color="#596579") + for suffix in ('png','svg','pdf'): + fig.savefig(out/f"comparison.{suffix}",dpi=180,facecolor=fig.get_facecolor()) + plt.close(fig) + + +def sync(records, out): + from dotenv import dotenv_values + from trackio.remote_client import RemoteClient + from trackio.sqlite_storage import SQLiteStorage + values=dotenv_values(REPO/'experiments/.env') + token=values.get('HF_API_KEY') or values['HF_TOKEN'] + client=RemoteClient(SPACE,hf_token=token,httpx_kwargs={'timeout':45}) + payload=SQLiteStorage.get_all_logs_for_sync(PROJECT) + for start in range(0,len(payload),200): + client.predict(api_name='/bulk_log',logs=payload[start:start+200],hf_token=token) + configs=native.configuration_records(PROJECT) + client.predict(api_name='/bulk_log',logs=configs,hf_token=token) + proof=native.verify_remote_records(client,PROJECT,payload,timeout=120) + for entry in configs: + summary=client.predict(api_name='/get_run_summary',project=PROJECT,run_id=entry['run_id']) + if summary.get('config')!=entry['config']: + raise ValueError('Remote training configuration does not match source') + proof['configurations_verified']=len(configs) + native.write_json(out/'sync-receipt.json',{**proof,'checked_at':datetime.now(timezone.utc).isoformat(),'space':SPACE}) + + +def main(): + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('--out',type=Path,default=DEFAULT_OUT) + p.add_argument('--online',action='store_true');p.add_argument('--watch',action='store_true') + args=p.parse_args();args.out.mkdir(parents=True,exist_ok=True) + import tempfile,fcntl + with (args.out/'.publisher.lock').open('w') as lock,tempfile.TemporaryDirectory(prefix='async-comparison-trackio-') as scratch: + fcntl.flock(lock,fcntl.LOCK_EX|fcntl.LOCK_NB) + for key in native.REMOTE_ENV:os.environ.pop(key,None) + os.environ['TRACKIO_DIR']=scratch;os.environ['TRACKIO_STORAGE_MODE']='sqlite' + last=None + while True: + try: + snapshot=collect();records=events(snapshot);fingerprint=native.digest([[r['log_id'],r['config']] for r in records]) + native.import_events(records);native.backup_project(PROJECT,args.out/'trackio') + if fingerprint!=last: + native.write_json(args.out/'snapshot.json',snapshot) + (args.out/'events.jsonl').write_text(''.join(json.dumps(r)+'\n' for r in records)) + report(snapshot,args.out);plot(snapshot,args.out) + if args.online:sync(records,args.out) + last=fingerprint + state={'checked_at':datetime.now(timezone.utc).isoformat(),'ok':True,'events':len(records), + 'training_steps':{a:len(r['training']) for a,r in snapshot['runs'].items()}, + 'evaluation_steps':{a:sorted(r['evaluations']) for a,r in snapshot['runs'].items()},'pending':snapshot['pending'],'online':args.online} + except Exception as exc: + state={'checked_at':datetime.now(timezone.utc).isoformat(),'ok':False,'error':str(exc)} + if not args.watch:raise + native.write_json(args.out/'publisher-status.json',state);print(json.dumps(state),flush=True) + if not args.watch or (state.get('ok') and not state.get('pending')):break + time.sleep(60) + + +if __name__=='__main__':main() diff --git a/04-data-agent/hf/deploy.py b/04-data-agent/hf/deploy.py new file mode 100644 index 0000000..dbc3481 --- /dev/null +++ b/04-data-agent/hf/deploy.py @@ -0,0 +1,335 @@ +"""Reproducible private HuggingEnvs deployment. Never reads credentials into config files.""" +from __future__ import annotations + +import argparse +import json +import os +from pathlib import Path +import shutil +import sys +import tarfile +import time + +HERE = Path(__file__).resolve().parent +DEFAULT_OUT = HERE.parent / "temp/reproduction" + + +def credentials(env_file): + from dotenv import dotenv_values + values = dotenv_values(env_file) if env_file else {} + # Explicit file credential takes precedence over the desktop's restricted OAuth token. + token = values.get("HF_API_KEY") or values.get("HF_TOKEN") or os.environ.get("HF_TOKEN") + if not token: + raise RuntimeError("HF_TOKEN or an --env-file containing HF_API_KEY is required") + result = {"HF_TOKEN": token} + for key in ("DAYTONA_API_KEY", "DAYTONA_API_URL", "DAYTONA_TARGET", "E2B_API_KEY", "OPENAI_API_KEY"): + if values.get(key) or os.environ.get(key): + result[key] = values.get(key) or os.environ[key] + return result + + +def save(path, value): + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_suffix(".tmp") + tmp.write_text(json.dumps(value, indent=2, default=str) + "\n") + tmp.replace(path) + + +def ensure_repo(api, repo, kind, **kwargs): + api.create_repo(repo, repo_type=kind, private=True, exist_ok=True, **kwargs) + info = api.repo_info(repo, repo_type=kind) + if not info.private and not (kind == "space" and getattr(info, "protected", False)): + raise RuntimeError(f"Existing resource is public: {repo}") + + +def upload_bundle(api, config, out): + repo = config["resources"]["repro_dataset_repo"] + ensure_repo(api, repo, "dataset") + bundle = out / "bundle" + metadata = json.loads((bundle / "bundle.json").read_text()) + (bundle / "README.md").write_text("# Daytona data-agent reproduction\n\nPrivate frozen runtime, 1,000 training tasks, 250 evaluation tasks and baseline evidence.\n\nSee the HF launch scripts in the archived `hf/` directory. No credentials are included.\n") + commit = api.upload_folder(repo_id=repo, repo_type="dataset", folder_path=bundle, + allow_patterns=["bundle.tar.gz", "bundle.json", "bootstrap.py", "README.md"], + commit_message="Package frozen Daytona source, tasks and HF runtime") + value = {**metadata, "repo": repo, "revision": commit.oid} + save(out / "bundle_uploaded.json", value) + return value + + +def spaces(api, config, secrets, out, selected): + bundle = out / "bundle" + info = json.loads((bundle / "bundle.json").read_text()) + for arm in config["resources"]["environment_spaces"]: + name = arm + mode = "shared" + if selected and name not in selected: + continue + repo = config["resources"]["environment_spaces"][name] + ensure_repo(api, repo, "space", space_sdk="docker") + for key, value in secrets.items(): + api.add_space_secret(repo, key, value) + variables = {"COMPARISON_ARM": arm, "COMPARISON_MODE": mode, + "BUNDLE_SHA256": info["sha256"], + "MAX_CONCURRENT_ENVS": "1024", + "SANDBOX_CAPACITY": str(config["serving"]["shared_service"]["sandbox_capacity_per_arm"][arm]), + "TRAIN_RESERVED_SANDBOXES": str(config["serving"]["shared_service"]["train_reserved_per_arm"][arm]), + "OPENENV_HARBOR_AGENT_VERSIONS": json.dumps(config["harness_pins"])} + for key, value in variables.items(): + api.add_space_variable(repo, key, value) + stage = out / "spaces" / name + stage.mkdir(parents=True, exist_ok=True) + shutil.copy2(bundle / "bundle.tar.gz", stage / "bundle.tar.gz") + # Deploy dependencies from the selected frozen bundle, even if local work + # has since added a UI dependency for the next deployment. + with tarfile.open(bundle / "bundle.tar.gz") as archive: + (stage / "requirements-env.lock").write_bytes( + archive.extractfile("hf/locks/requirements-env.lock").read()) + oauth = ("hf_oauth: true\nhf_oauth_scopes:\n - inference-api\n" + if config["resources"].get("interactive_hf_oauth", False) else "") + (stage / "README.md").write_text(f"---\ntitle: Data Agent {'SETA Whitebox' if arm == 'whitebox' else 'Blackbox OpenCode' if arm == 'opencode' else 'Blackbox Harbor'} Env\nsdk: docker\napp_port: 7860\n{oauth}---\n\nInteractive OpenEnv environment for data-agent training and evaluation.\n") + entrypoint = "opencode_space.py" if arm == "opencode" else "space_app.py" + dockerfile = f'''FROM {config['compute']['bootstrap_image']} +USER root +RUN useradd -m -u 1000 user +WORKDIR /workspace/repro +COPY requirements-env.lock /tmp/requirements-env.lock +RUN uv venv --python 3.12 /opt/environment && uv pip sync --python /opt/environment/bin/python --require-hashes /tmp/requirements-env.lock +COPY bundle.tar.gz /tmp/bundle.tar.gz +RUN python -c "import tarfile; tarfile.open('/tmp/bundle.tar.gz').extractall('/workspace/repro', filter='data')" && rm /tmp/bundle.tar.gz +RUN chown -R user:user /workspace /opt/environment +USER user +ENV PYTHONUNBUFFERED=1 PYTHONPATH=/workspace/repro/hf/runtime REPRO_ROOT=/workspace/repro PORT=7860 +CMD ["/opt/environment/bin/python", "-u", "/workspace/repro/hf/runtime/{entrypoint}"] +''' + (stage / "Dockerfile").write_text(dockerfile) + commit = api.upload_folder(repo_id=repo, repo_type="space", folder_path=stage, + commit_message="Deploy frozen Daytona environment service") + hardware = config["serving"]["shared_service"]["hardware"] + # CPU Basic uses HF's default sleep policy; disabling sleep requires paid hardware. + hardware_options = {} if hardware == "cpu-basic" else {"sleep_time": -1} + runtime = api.request_space_hardware(repo, hardware, **hardware_options) + api.update_repo_settings(repo, repo_type="space", + visibility=config["resources"].get("space_visibility", "private")) + save(out / f"space-{name}.json", {"repo": repo, "revision": commit.oid, + "bundle_sha256": info["sha256"], "stage": str(runtime.stage), "url": f"https://huggingface.co/spaces/{repo}"}) + print(json.dumps({"space": repo, "stage": str(runtime.stage), + "visibility": config["resources"].get("space_visibility", "private")}), flush=True) + +def submit(api, config, secrets, out, args): + from huggingface_hub import Volume + uploaded = json.loads((out / "bundle_uploaded.json").read_text()) + role = args.role + qualification = (role == "train" and args.phase == "smoke") or (role == "coordinator" and args.phase == "qualify") + if config.get("training_launch_hold") and role in {"train", "coordinator"} and not qualification: + raise ValueError(config["training_launch_hold"]) + if role == "train" and args.phase not in {"smoke", "long"}: + raise ValueError("Training phase must be smoke or long") + proof = None + if role == "train" and args.phase == "long": + from launch_gates import verify + proof = verify(api, config, uploaded, args.arm, args.baseline_job, args.smoke_job, out) + if args.arm == "opencode": + from launch_gates import verify_comparison_baseline + comparison_id = getattr(args, "comparison_baseline_job", None) + proof["native_baseline_prefix"] = proof["baseline_prefix"] + proof["native_baseline_job"] = args.baseline_job + proof["comparison_baseline_job"] = comparison_id + proof["baseline_prefix"] = verify_comparison_baseline(api, config, comparison_id, out) + proof["baseline_job"] = comparison_id + if getattr(args, "checkpoint_eval_job", None): + from launch_gates import verify_checkpoint_eval + proof["checkpoint_eval"] = verify_checkpoint_eval(api, config, uploaded, args.arm, + args.smoke_job, args.checkpoint_eval_job, out) + elif getattr(args, "external_checkpoint_coordinator", False): + raise ValueError("Long training with an external coordinator requires --checkpoint-eval-job") + save(out / "launch-proofs" / args.arm / "verified.json", proof) + if role == "coordinator" and args.phase not in {"setup", "qualify"} and not args.training_job: + raise ValueError("--training-job is required for the checkpoint coordinator") + if role == "eval" and args.phase == "checkpoint": + raise ValueError("Checkpoint evaluations are submitted with verified manifests by the coordinator") + flavor = args.flavor or ("cpu-upgrade" if role in ["preflight", "coordinator"] else + config["compute"]["training_flavor"] if role == "train" else "a100-large") + if role == "train" and flavor not in {"h200x2", "a100x4"}: + raise ValueError("Training needs separate inference and optimizer GPUs") + identity = f"{role}-{args.arm}-{int(time.time())}" + env = {"ARTIFACT_BUCKET": config["resources"]["artifacts_bucket"], "RUN_ID": config["run_id"], + "RUN_OWNER": identity, "PYTHONUNBUFFERED": "1", "COMPARISON_ARM": args.arm, + "BUNDLE_SHA256": uploaded["sha256"], "JOB_FLAVOR": flavor, + "BUNDLE_REPO": uploaded["repo"], "BUNDLE_REVISION": uploaded["revision"], + "TRACKIO_MODE": "offline", "HF_JOB_NAMESPACE": config["namespace"], + "EVAL_CONCURRENCY": str(config["evaluation"]["concurrency_per_arm"][args.arm])} + if args.arm == "opencode": + limits = config["evaluation"]["opencode_backend_concurrency"] + env.update(EVAL_DAYTONA_CONCURRENCY=str(limits["daytona"]), EVAL_HF_CONCURRENCY=str(limits["hf"])) + if getattr(args, "space_bundle_sha", None): + if role != "train" or args.phase != "smoke": + raise ValueError("An independently pinned Space is currently supported only for training qualification") + env["SPACE_BUNDLE_SHA256"] = args.space_bundle_sha + if proof: + env.update(SMOKE_PREFIX=proof["smoke_prefix"], BASELINE_JOB=proof.get("comparison_baseline_job") or args.baseline_job, + SPACE_BUNDLE_SHA256=proof["space_bundle_sha256"], + BASELINE_PREFIX=proof["baseline_prefix"], + COORDINATION_PREFIX="hf://buckets/" + config["resources"]["artifacts_bucket"] + "/" + + config["run_id"] + "/coordination/" + identity) + if proof.get("native_baseline_prefix"): + env["NATIVE_BASELINE_PREFIX"] = proof["native_baseline_prefix"] + if args.training_job: + env["TRAINING_JOB"] = args.training_job + if getattr(args, "baseline_job_map", None): + if role != "coordinator" or args.phase != "setup": + raise ValueError("--baseline-job-map is for the two-arm setup pipeline") + mapping = json.loads(args.baseline_job_map) + if set(mapping) != {"blackbox", "whitebox"}: + raise ValueError("Provide blackbox and whitebox baseline IDs") + env["BASELINE_JOB_MAP"] = json.dumps(mapping) + if args.resume_eval_owner: + if args.role != "eval" or args.arm != "whitebox" or args.phase != "baseline": + raise ValueError("--resume-eval-owner requires a whitebox baseline evaluation") + if Path(args.resume_eval_owner).name != args.resume_eval_owner: + raise ValueError("Resume owner must be a single artifact directory name") + env["RESUME_EVAL_PREFIX"] = ("hf://buckets/" + config["resources"]["artifacts_bucket"] + "/" + + config["run_id"] + "/jobs/" + args.resume_eval_owner) + key = args.arm + if key in config["resources"]["environment_spaces"]: + space = config["resources"]["environment_spaces"][key] + info = api.space_info(space) + env["SPACE_URL"] = info.host if str(info.host).startswith("https://") else "https://" + info.host + if role == "train" and args.arm == "opencode": + import httpx + evaluator = api.space_info(config["resources"]["environment_spaces"]["blackbox"]) + url = str(evaluator.host) + url = url if url.startswith("https://") else "https://" + url + eval_identity = httpx.get(url + "/deployment", headers={"Authorization": "Bearer " + secrets["HF_TOKEN"]}, + timeout=60).raise_for_status().json() + if eval_identity.get("arm") != "blackbox" or eval_identity.get("test_tasks") != 250: + raise ValueError("The four-harness checkpoint evaluator has the wrong task catalog") + env.update(CHECKPOINT_EVAL_SPACE_URL=url, + CHECKPOINT_EVAL_SPACE_SHA256=eval_identity["bundle_sha256"]) + if role == "train" and args.arm in {"blackbox", "opencode"}: + from runtime.service_contract import check + save(out / f"service-contract-{args.arm}.json", + check(env["SPACE_URL"], secrets["HF_TOKEN"], args.arm)) + if proof and env["SPACE_URL"].rstrip("/") != proof["space_url"].rstrip("/"): + raise ValueError("Long training must use the environment qualified by its optimizer smoke") + command = ["python", "/bundle/bootstrap.py", "--role", role, "--arm", args.arm, + "--phase", args.phase, "--dp", str(args.dp)] + if args.limit: + command += ["--limit", str(args.limit)] + labels = {"experiment": "data-agent-daytona", "role": role, "arm": args.arm, + "phase": args.phase, "run": config["run_id"]} + if args.training_job: + labels["training_job"] = args.training_job + if getattr(args, "dry_run", False): + # Validate real proofs and Space identity above, but never allocate a job. + preview = {"dry_run": True, "submitted": False, "role": role, "arm": args.arm, + "phase": args.phase, "flavor": flavor, "timeout": args.timeout or "2h", + "command": command, "labels": labels, "environment": env, + "bundle": uploaded, "proof": proof, + "external_checkpoint_coordinator": bool(getattr(args, "external_checkpoint_coordinator", False))} + save(out / "launch-preview.json", preview) + print(json.dumps(preview), flush=True) + return preview + job = api.run_job(namespace=config["namespace"], image=config["compute"]["bootstrap_image"], + command=command, flavor=flavor, timeout=args.timeout or ("1h" if role == "preflight" else "2h"), + env=env, secrets=secrets, name=f"daytona-{identity}", + labels=labels, + volumes=[Volume(type="dataset", source=uploaded["repo"], revision=uploaded["revision"], + mount_path="/bundle", read_only=True)], expose=[8000] if role in ["eval", "train"] else None) + value = {"id": job.id, "url": job.url, "owner": identity, "role": role, "arm": args.arm, + "phase": args.phase, "flavor": flavor, "bundle": uploaded, "stage": job.status.stage} + save(out / "jobs" / f"{job.id}.json", value) + print(json.dumps(value), flush=True) + if role == "train" and args.phase == "long" and not getattr(args, "external_checkpoint_coordinator", False): + # The GPU ID is durable before the CPU submission. A failed coordinator + # launch can be retried explicitly with --role coordinator --training-job. + coordinator_args = argparse.Namespace(**{**vars(args), "role": "coordinator", "training_job": job.id, + "flavor": "cpu-upgrade", "timeout": "36h"}) + submit(api, config, secrets, out, coordinator_args) + return value + + +def retire_spaces(api, config, out): + """Pause superseded Spaces only after all associated HF Jobs have drained.""" + terminal = {"COMPLETED", "ERROR", "CANCELED", "CANCELLED", "DELETED"} + active = [j for j in api.list_jobs(namespace=config["namespace"], + labels={"experiment": "data-agent-daytona"}) if j.status.stage not in terminal] + report = [] + for repo in config["resources"].get("retired_spaces", []): + info = api.space_info(repo) + needles = [repo, str(info.host)] + users = [j.id for j in active if any(needle in str(value) for needle in needles + for value in j.environment.values())] + if users: + row = {"space": repo, "state": "draining", "active_jobs": users} + else: + runtime = api.pause_space(repo) + row = {"space": repo, "state": str(runtime.stage), "repository_preserved": True} + report.append(row) + print(json.dumps(row), flush=True) + save(out / "space-retirement.json", report) + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("action", choices=["upload", "spaces", "job", "status", "trackio", "retire"]) + p.add_argument("--env-file") + p.add_argument("--config", type=Path, default=HERE / "configs/deployment.json") + p.add_argument("--out", type=Path, default=DEFAULT_OUT) + p.add_argument("--only", help="Comma-separated arms: blackbox,whitebox") + p.add_argument("--role", default="preflight", choices=["preflight", "train", "eval", "coordinator"]) + p.add_argument("--arm", default="blackbox", choices=["blackbox", "whitebox", "opencode"]) + p.add_argument("--phase", default="smoke", choices=["smoke", "ramp", "baseline", "long", "checkpoint", "setup", "qualify"]) + p.add_argument("--flavor") + p.add_argument("--timeout") + p.add_argument("--dp", type=int, default=1) + p.add_argument("--limit", type=int, default=0) + p.add_argument("--resume-eval-owner", help="Restore the immutable first-graded ledger of a whitebox baseline") + p.add_argument("--smoke-job", help="Completed optimizer/save/resume HF Job for this arm and bundle") + p.add_argument("--baseline-job", help="Completed HF baseline with matching task and evaluation protocol") + p.add_argument("--comparison-baseline-job", help="For native OpenCode: completed shared four-harness Harbor baseline") + p.add_argument("--training-job", help="HF training Job followed by a checkpoint coordinator") + p.add_argument("--baseline-job-map", help="JSON mapping of both baseline Job IDs for --phase setup") + p.add_argument("--space-bundle-sha", help="Exact already-deployed Space bundle for a training smoke; avoids restarting active eval services") + p.add_argument("--external-checkpoint-coordinator", action="store_true", + help="Start the separately versioned hf_followup.py coordinator after submitting this training job") + p.add_argument("--checkpoint-eval-job", help="Completed independent checkpoint evaluation of --smoke-job") + p.add_argument("--dry-run", action="store_true", help="Validate and save the exact job request without allocating it") + a = p.parse_args() + from huggingface_hub import HfApi + config = json.loads(a.config.read_text()) + secrets = credentials(a.env_file) + api = HfApi(token=secrets["HF_TOKEN"]) + if a.action == "upload": + api.create_bucket(config["resources"]["artifacts_bucket"], private=True, exist_ok=True) + print(json.dumps(upload_bundle(api, config, a.out))) + elif a.action == "spaces": + spaces(api, config, secrets, a.out, set(a.only.split(",")) if a.only else None) + elif a.action == "job": + submit(api, config, secrets, a.out, a) + elif a.action == "retire": + retire_spaces(api, config, a.out) + elif a.action == "status": + for path in sorted((a.out / "jobs").glob("*.json")): + local = json.loads(path.read_text()) + j = api.inspect_job(job_id=local["id"], namespace=config["namespace"]) + print(json.dumps({"id": j.id, "role": local["role"], "arm": local["arm"], "stage": j.status.stage, "message": j.status.message})) + for name, repo in config["resources"]["environment_spaces"].items(): + try: + s = api.get_space_runtime(repo) + print(json.dumps({"space": repo, "stage": str(s.stage)})) + except Exception as e: + print(json.dumps({"space": repo, "error": type(e).__name__})) + else: + if not config["resources"].get("trackio_space"): + raise SystemExit("Trackio is configured in training jobs only; no Trackio Space is deployed") + os.environ["HF_TOKEN"] = secrets["HF_TOKEN"] + from trackio.deploy import create_space_if_not_exists + create_space_if_not_exists(config["resources"]["trackio_space"], + bucket_id=config["resources"]["trackio_bucket"], private=config["logging"].get("space_private", False)) + api.update_repo_settings(config["resources"]["trackio_space"], repo_type="space", + private=config["logging"].get("space_private", False)) + print(json.dumps({"trackio_space": config["resources"]["trackio_space"]})) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/hf/deploy_comparison_trackio.py b/04-data-agent/hf/deploy_comparison_trackio.py new file mode 100644 index 0000000..688c1af --- /dev/null +++ b/04-data-agent/hf/deploy_comparison_trackio.py @@ -0,0 +1,60 @@ +"""Deploy the public comparison dashboard using native Trackio and bucket storage.""" +import os +from pathlib import Path + +from dotenv import dotenv_values +from huggingface_hub import HfApi, CommitOperationAdd + +from consolidate_async_runs import REPO, SPACE, BUCKET, PROJECT, DEFAULT_OUT + + +def main(): + values = dotenv_values(REPO / 'experiments/.env') + os.environ['HF_TOKEN'] = values.get('HF_API_KEY') or values['HF_TOKEN'] + os.environ['TRACKIO_PLOT_ORDER'] = 'eval/pass_at_1,eval/difficulty/*,eval/harness/*,eval/harness_difficulty/*,train/reward_rolling20,train/reward,train/loss,train/grad_norm,train/nonzero_gradient_rolling20,train/entropy,train/kl,train/learning_rate' + from trackio.deploy import create_space_if_not_exists + create_space_if_not_exists(SPACE, bucket_id=BUCKET, private=False) + api = HfApi() + api.update_repo_settings(SPACE, repo_type='space', private=False) + app = f'''import os +os.environ.setdefault("TRACKIO_PLOT_ORDER", {os.environ['TRACKIO_PLOT_ORDER']!r}) +import trackio +trackio.show(project={PROJECT!r}) +''' + readme = f'''--- +title: Data Agent Training Comparison +emoji: 📈 +colorFrom: indigo +colorTo: yellow +sdk: gradio +app_file: app.py +pinned: false +--- + +# Qwen3.5-2B: Harbor and native OpenCode + +Both complete training histories (steps1–1000), checkpoint pass@1, difficulty, +harness, harness × difficulty, and optimizer/rollout diagnostics in one Trackio project. + +- [Complete checkpoint report](REPORT.md) +- [Comparison figure](comparison.png) +- [Vector figure](comparison.svg) + +Every checkpoint score shown passed full coverage, TiTO and harness-version gates. +Remaining evaluations appear automatically after validation. Baselines are separate +measured E2B and Daytona cohorts, as documented in the report. + +Trackio0.33.0, standard CPU Space, persistent HF bucket; independent CPU publisher. +Use zero smoothing to inspect exact checkpoint scores. Raw metrics and explicitly +named20-update rolling averages are both retained. +''' + operations = [CommitOperationAdd(path_in_repo='app.py',path_or_fileobj=app.encode()), + CommitOperationAdd(path_in_repo='README.md',path_or_fileobj=readme.encode())] + for name in ('REPORT.md','comparison.png','comparison.svg','comparison.pdf'): + operations.append(CommitOperationAdd(path_in_repo=name,path_or_fileobj=str(DEFAULT_OUT/name))) + result = api.create_commit(SPACE, repo_type='space', operations=operations, + commit_message='Add unified Harbor and OpenCode comparison dashboard and report') + print('space',SPACE,'commit',result.oid) + + +if __name__=='__main__':main() diff --git a/04-data-agent/hf/finalize_hf_plan.py b/04-data-agent/hf/finalize_hf_plan.py new file mode 100644 index 0000000..050de86 --- /dev/null +++ b/04-data-agent/hf/finalize_hf_plan.py @@ -0,0 +1,48 @@ +"""Verify the completed HF qualification and save an unsubmitted main job preview.""" +import argparse +import json +from pathlib import Path +import shlex +import sys + +from deploy import credentials, save, submit + + +def finalize(args): + from huggingface_hub import HfApi + output = args.out.resolve() + plan_path = output / "plan.json" + plan = json.loads(plan_path.read_text()) + record = json.loads((args.controller_output / "decisions/scores/step-000004.json").read_text()) + config = json.loads(Path(plan["config"]).read_text()) + if plan["arm"] != "whitebox" or plan.get("main_training_submitted"): + raise ValueError("Expected an unsubmitted Whitebox plan") + secrets = credentials(args.env_file) + job_args = argparse.Namespace(role="train", arm="whitebox", phase="long", flavor="h200x2", + timeout="24h", dp=1, limit=0, resume_eval_owner=None, training_job=None, + baseline_job=plan["baseline_job"], smoke_job=plan["smoke_job"], + checkpoint_eval_job=record["job_id"], external_checkpoint_coordinator=True, dry_run=True) + preview = submit(HfApi(token=secrets["HF_TOKEN"]), config, secrets, output, job_args) + if preview.get("submitted") or not preview.get("proof", {}).get("checkpoint_eval", {}).get("passed"): + raise RuntimeError("Expected a successful read-only qualification preview") + hf = Path(__file__).resolve().parent + command = [sys.executable, str(hf / "deploy.py"), "job", "--env-file", str(args.env_file.resolve()), + "--config", plan["config"], "--out", str(output), "--role", "train", "--arm", "whitebox", + "--phase", "long", "--flavor", "h200x2", "--timeout", "24h", "--dp", "1", + "--baseline-job", plan["baseline_job"], "--smoke-job", plan["smoke_job"], + "--checkpoint-eval-job", record["job_id"], "--external-checkpoint-coordinator"] + plan.update(checkpoint_eval_job=record["job_id"], status="qualification_passed_main_not_submitted", + launch_preview=str(output / "launch-preview.json"), launch_command=command, + controller_launcher=str(hf / "hf_followup.py"), + controller_note="After trainer submission, prepare and submit hf_followup.py for its returned job ID") + save(plan_path, plan) + (output / "launch-command.txt").write_text(shlex.join(command) + "\n") + print(json.dumps({"plan": str(plan_path), "qualified": True, "main_training_submitted": False})) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--out", type=Path, required=True) + parser.add_argument("--controller-output", type=Path, required=True) + parser.add_argument("--env-file", type=Path, required=True) + finalize(parser.parse_args()) diff --git a/04-data-agent/hf/hf_followup.py b/04-data-agent/hf/hf_followup.py new file mode 100644 index 0000000..c9f8564 --- /dev/null +++ b/04-data-agent/hf/hf_followup.py @@ -0,0 +1,181 @@ +"""Freeze and run a CPU coordinator for an independently pinned HF Whitebox job. + +The trainer/evaluator bundle is never rewritten. The controller is a separately +hashed artifact and carries the qualified Space pin into every evaluation job. +Preparing this controller never starts a long training run. +""" +import argparse +from contextlib import contextmanager +import fcntl +import hashlib +import json +import os +from pathlib import Path +import shlex +import shutil +import subprocess +import sys +import tarfile +import time + +HF = Path(__file__).resolve().parent +TERMINAL = {"COMPLETED", "ERROR", "CANCELED", "CANCELLED", "DELETED"} + + +def read(path): + return json.loads(Path(path).read_text()) + + +def write(path, value): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + temporary = path.with_suffix(".tmp") + temporary.write_text(json.dumps(value, indent=2) + "\n") + temporary.replace(path) + + +def credentials(path): + from dotenv import dotenv_values + values = dotenv_values(path) + os.environ["HF_TOKEN"] = values.get("HF_API_KEY") or values["HF_TOKEN"] + for key in ("DAYTONA_API_KEY", "DAYTONA_API_URL", "DAYTONA_TARGET"): + if values.get(key): + os.environ[key] = values[key] + + +def prepare(args): + from huggingface_hub import HfApi + credentials(args.env_file) + training = HfApi().inspect_job(job_id=args.training_job, namespace="HuggingEnvs") + if training.labels.get("arm") != "whitebox" or training.labels.get("role") != "train": + raise ValueError("Expected a native Whitebox HF training job") + info = read(args.bundle / "bundle.json") + archive = args.bundle / "bundle.tar.gz" + if (hashlib.sha256(archive.read_bytes()).hexdigest() != info["sha256"] or + info["sha256"] != training.environment["BUNDLE_SHA256"]): + raise ValueError("The archive must match the actual training job") + volume = next(v for v in training.volumes if v.type == "dataset" and v.mount_path == "/bundle") + out = args.out.resolve() + out.mkdir(parents=True, exist_ok=False) + root = out / "runtime" + with tarfile.open(archive) as bundle: + config = json.load(bundle.extractfile("hf/configs/deployment.json")) + logger_root = out / "logger-runtime" + for name in ("hf/runtime/common.py", "hf/runtime/checkpoint_store.py", "hf/runtime/logging_sync.py", + "experiments/daytona_harness_comparison/logs/20260915/source/tools/trackio_multi4.py", + "experiments/daytona_harness_comparison/logs/20260915/source/tools/monitor_multi4.py"): + target = logger_root / name + target.parent.mkdir(parents=True, exist_ok=True) + target.write_bytes(bundle.extractfile(name).read()) + config["evaluation"]["concurrency_per_arm"]["whitebox"] = 50 + write(root / "hf/configs/deployment.json", config) + runtime = root / "hf/runtime" + runtime.mkdir(parents=True) + for name in ("common.py", "checkpoint_store.py", "coordinator.py"): + shutil.copyfile(HF / "runtime" / name, runtime / name) + shutil.copyfile(Path(__file__), out / "hf_followup.py") + shutil.copyfile(HF / "late_hf_logging.py", out / "late_hf_logging.py") + args.coordination_dir.mkdir(parents=True, exist_ok=True) + plan = {"training_job": training.id, "namespace": "HuggingEnvs", "arm": "whitebox", + "training_phase": training.labels["phase"], "bundle_sha256": info["sha256"], + "bundle_repo": volume.source, "bundle_revision": volume.revision, + "space_bundle_sha256": training.environment.get("SPACE_BUNDLE_SHA256", info["sha256"]), + "env_file": str(args.env_file.resolve()), "root": str(root), + "logger_root": str(logger_root), "logging_python": sys.executable, + "coordination_dir": str(args.coordination_dir.resolve()), + "environment_python": str(HF.parents[2] / "OpenEnv/.venv/bin/python"), + "evaluation": {"flavor": config["compute"]["eval_flavor"], "dp": config["compute"]["eval_dp"], + "concurrency": 50, "cells": 250, "metric": "pass@1"}, + "controller_walltime": "08:00:00" if training.labels["phase"] == "smoke" else "36:00:00", + "long_training_submitted": False, + "files": {str(p.relative_to(out)): hashlib.sha256(p.read_bytes()).hexdigest() + for p in out.rglob("*") if p.is_file()}} + write(out / "plan.json", plan) + command = [sys.executable, "-u", str(out / "hf_followup.py"), "watch", "--plan", str(out / "plan.json")] + script = out / "controller.slurm" + script.write_text("#!/bin/bash\nset -euo pipefail\nexec " + shlex.join(command) + "\n") + if args.submit: + job = subprocess.check_output(["sbatch", "--parsable", "--partition=hopper-cpu", + "--cpus-per-task=2", "--mem=8G", "--time=" + plan["controller_walltime"], "--job-name=cmp-hf-whitebox-followup", + "--output=" + str(out / "controller-%j.out"), "--error=" + str(out / "controller-%j.err"), + str(script)], text=True).strip() + write(out / "submission.json", {"controller_job": job, "training_job": training.id, + "qualification_only": training.labels["phase"] == "smoke"}) + print(json.dumps({"plan": str(out / "plan.json"), "submitted": args.submit})) + + +def watch(plan_path): + from huggingface_hub import HfApi + plan_path = plan_path.resolve() + plan = read(plan_path) + out = plan_path.parent + for name, expected in plan["files"].items(): + if hashlib.sha256((out / name).read_bytes()).hexdigest() != expected: + raise ValueError("Coordinator source/configuration changed after preparation: " + name) + credentials(plan["env_file"]) + lock = (Path(plan["coordination_dir"]) / (plan["training_job"] + ".lock")).open("a") + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + os.environ["REPRO_ROOT"] = plan["root"] + sys.path.insert(0, str(Path(plan["root"]) / "hf/runtime")) + from checkpoint_store import download_json + from coordinator import run + api = HfApi() + while True: + training = api.inspect_job(job_id=plan["training_job"], namespace=plan["namespace"]) + write(out / "waiting.json", {"checked_at": time.time(), "job": training.id, "stage": training.status.stage}) + if plan["training_phase"] != "smoke": + break + if training.status.stage == "COMPLETED": + source = ("hf://buckets/" + training.environment["ARTIFACT_BUCKET"] + "/" + + training.environment["RUN_ID"] + "/jobs/" + training.environment["RUN_OWNER"]) + proof = download_json(source, "training_smoke_verified.json", out / "training_smoke_verified.json", api) + if (proof.get("arm") != "whitebox" or proof.get("bundle_sha256") != plan["bundle_sha256"] or + proof.get("optimizer_steps") != [1, 2, 3, 4] or not all(proof.get(k) for k in + ("passed", "weights_updated", "tito_pass", "remote_restore_verified", "native_optimizer_state_verified"))): + raise ValueError("HF optimizer qualification did not pass") + os.environ["QUALIFY_CHECKPOINT_STEP"] = "4" + break + if training.status.stage in TERMINAL: + raise RuntimeError("HF optimizer smoke ended without successful qualification: " + training.status.stage) + time.sleep(60) + if training.environment["BUNDLE_SHA256"] != plan["bundle_sha256"]: + raise ValueError("Training runtime identity changed") + os.environ.update(TRAINING_JOB=training.id, ARTIFACT_BUCKET=training.environment["ARTIFACT_BUCKET"], + RUN_ID=training.environment["RUN_ID"], RUN_OWNER="cpu-hf-followup-" + training.id, + BUNDLE_SHA256=plan["bundle_sha256"], BUNDLE_REPO=plan["bundle_repo"], + BUNDLE_REVISION=plan["bundle_revision"]) + + @contextmanager + def admission(): + with (Path(plan["coordination_dir"]) / "eval-admission.lock").open("a") as capacity: + fcntl.flock(capacity, fcntl.LOCK_EX) + slurm = subprocess.check_output(["squeue", "--noheader", "--user", os.environ["USER"], "--format=%j"], text=True) + active_hf = any(j.status.stage not in TERMINAL for j in api.list_jobs(namespace=plan["namespace"], + labels={"experiment": "data-agent-daytona", "role": "eval"})) + if active_hf or any(name.strip().startswith("cmp-eval-") for name in slurm.splitlines()): + yield False + else: + code = "from daytona import Daytona; print(len(list(Daytona().list())))" + occupied = int(subprocess.check_output([plan["environment_python"], "-c", code], text=True).strip()) + yield occupied + 50 + 24 <= 125 + + run(out / "output", "whitebox", admission=admission) + if plan.get("logger_root"): + subprocess.run([plan["logging_python"], str(out / "late_hf_logging.py"), + "--plan", str(plan_path)], check=True) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest="action", required=True) + preparation = commands.add_parser("prepare") + preparation.add_argument("--training-job", required=True) + preparation.add_argument("--bundle", type=Path, required=True) + preparation.add_argument("--env-file", type=Path, required=True) + preparation.add_argument("--out", type=Path, required=True) + preparation.add_argument("--coordination-dir", type=Path, required=True) + preparation.add_argument("--submit", action="store_true") + monitoring = commands.add_parser("watch") + monitoring.add_argument("--plan", type=Path, required=True) + args = parser.parse_args() + prepare(args) if args.action == "prepare" else watch(args.plan) diff --git a/04-data-agent/hf/late_hf_logging.py b/04-data-agent/hf/late_hf_logging.py new file mode 100644 index 0000000..d2e9252 --- /dev/null +++ b/04-data-agent/hf/late_hf_logging.py @@ -0,0 +1,114 @@ +"""Replay trailing HF checkpoint scores into the finished trainer's Trackio artifacts.""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import sys + + +def planned_stop_matches(job, plan): + request = plan.get("final_checkpoint") or {} + expected_source = ("hf://buckets/" + job.environment["ARTIFACT_BUCKET"] + "/" + + job.environment["RUN_ID"] + "/jobs/" + job.environment["RUN_OWNER"] + + "/run/checkpoint-" + str(request.get("step"))) + return (job.status.stage in {"CANCELED", "CANCELLED"} + and job.environment["BUNDLE_SHA256"] == plan["bundle_sha256"] + and request.get("training_job") == plan["training_job"] + and request.get("user_requested_stop") is True + and request.get("full_checkpoint_verified") is True + and type(request.get("step")) is int and request["step"] > 0 + and request.get("checkpoint") == expected_source + and len(request.get("manifest_sha256", "")) == 64) + + +def replay(plan_path): + from huggingface_hub import HfApi + from dotenv import dotenv_values + plan_path = Path(plan_path).resolve() + plan = json.loads(plan_path.read_text()) + base = plan_path.parent + for name, expected in plan["files"].items(): + if hashlib.sha256((base / name).read_bytes()).hexdigest() != expected: + raise ValueError("Prepared replay source changed: " + name) + values = dotenv_values(plan["env_file"]) + os.environ["HF_TOKEN"] = values.get("HF_API_KEY") or values["HF_TOKEN"] + api = HfApi() + job = api.inspect_job(job_id=plan["training_job"], namespace=plan["namespace"]) + intentional_stop = planned_stop_matches(job, plan) + if ((job.status.stage != "COMPLETED" and not intentional_stop) + or job.environment["BUNDLE_SHA256"] != plan["bundle_sha256"]): + raise ValueError("Replay requires a completed trainer with the qualified runtime") + source = ("hf://buckets/" + job.environment["ARTIFACT_BUCKET"] + "/" + + job.environment["RUN_ID"] + "/jobs/" + job.environment["RUN_OWNER"]) + output = base / "late-training-logs" + output.mkdir(exist_ok=True) + api.sync_bucket(source, str(output), quiet=True, + include=["status.json", "audit/metrics.jsonl", "run/metrics.jsonl", "trackio-events.jsonl"]) + status = json.loads((output / "status.json").read_text()) + if not intentional_stop and (not status.get("finished_at") or not status.get("passed")): + raise ValueError("Trainer has not published successful final status") + destination = output / "checkpoint-scores" + destination.mkdir(exist_ok=True) + steps = set() + coordinator_output = Path(plan.get("score_controller_output", base / "output")) + for path in (coordinator_output / "decisions/scores").glob("step-*.json"): + record = json.loads(path.read_text()) + evidence, scores = record["source"], record["scores"] + if intentional_stop and (record["step"] != plan["final_checkpoint"]["step"] + or evidence.get("manifest_sha256") != plan["final_checkpoint"]["manifest_sha256"]): + raise ValueError("Stopped trainer replay must use its exact authorized final checkpoint") + if (not scores.get("comparison_ready") or not scores.get("tito_pass") or + scores.get("arm") != "whitebox" or scores.get("graded_cells") != 250 or + evidence.get("bundle_sha256") != plan["bundle_sha256"] or + record["step"] != evidence["step"] or + evidence["source"] != source + "/run/checkpoint-" + str(record["step"])): + raise ValueError("Unverified checkpoint scores cannot enter the training curve") + target = destination / path.name + if target.exists() and json.loads(target.read_text()) != record: + raise ValueError("Conflicting completed scores for the same checkpoint") + target.write_text(json.dumps(record, indent=2) + "\n") + steps.add(record["step"]) + if not steps: + raise ValueError("No completed checkpoint scores to replay") + for name in ("TRACKIO_SPACE", "TRACKIO_SPACE_ID", "TRACKIO_SERVER_URL", "TRACKIO_BUCKET_ID", "TRACKIO_DATASET_ID"): + os.environ.pop(name, None) + os.environ.update(REPRO_ROOT=plan["logger_root"], TRACKIO_DIR=str(output / "trackio"), + RUN_OWNER=job.environment["RUN_OWNER"], RUN_ID=job.environment["RUN_ID"], + BUNDLE_SHA256=plan["bundle_sha256"], JOB_FLAVOR=job.flavor, + TRAINING_SMOKE="1" if job.labels["phase"] == "smoke" else "0") + for name in ("BASELINE_PREFIX", "BASELINE_JOB", "COORDINATION_PREFIX"): + if job.environment.get(name): + os.environ[name] = job.environment[name] + else: + os.environ.pop(name, None) + sys.path.insert(0, str(Path(plan["logger_root"]) / "hf/runtime")) + from common import configure + configure() + import trackio_multi4 as native + ledger = output / "trackio-events.jsonl" + # Rebuild a fresh local database from the durable event ledger. Merely + # copying the dedup ledger would hide old events from the new database. + previous = [json.loads(line) for line in ledger.read_text().splitlines() if line.strip()] + native.import_events(previous) + from logging_sync import sync_metrics + sync_metrics(output, "whitebox") + events = [json.loads(line) for line in ledger.read_text().splitlines() if line.strip()] + recorded = {r["step"] for r in events if "eval/pass_at_1" in r["metrics"]} + if not steps.issubset(recorded): + raise ValueError("Trackio replay did not retain every completed checkpoint") + # The GPU job is terminal, so its publisher cannot overwrite these late logs. + api.sync_bucket(str(output), source, quiet=True, include=["checkpoint-scores/**", + "trackio-events.jsonl", "trackio-backup/**", "trackio_verified.json"]) + receipt = {"passed": True, "training_job": job.id, "steps": sorted(steps), + "previous_events": len(previous), "current_events": len(events), + "training_artifacts": source, "offline_trackio_and_bucket": True, + "provider_training_stage": job.status.stage, "user_requested_stop": intentional_stop} + (base / "late-trackio-verified.json").write_text(json.dumps(receipt, indent=2) + "\n") + print(json.dumps(receipt)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--plan", type=Path, required=True) + replay(parser.parse_args().plan) diff --git a/04-data-agent/hf/launch_gates.py b/04-data-agent/hf/launch_gates.py new file mode 100644 index 0000000..d817af1 --- /dev/null +++ b/04-data-agent/hf/launch_gates.py @@ -0,0 +1,139 @@ +"""Verify completed HF baseline and optimizer evidence before allocating a long run.""" +import hashlib +import json +from pathlib import Path +import tarfile + + +def protocol_identity(config): + # Admission limits are deployment controls. Changing another arm's concurrency + # must not invalidate an otherwise identical completed Whitebox baseline. + evaluation = {key: config["evaluation"].get(key) for key in ( + "metric", "max_output_tokens_per_call", "max_model_calls", "episode_timeout_seconds", + "first_graded_result_immutable", "retry_only_ungraded_infrastructure_failures")} + return {"model": config["model"], "data": config["data"]["manifest_sha256"], + "pins": config["harness_pins"], "evaluation": evaluation, + "sampling": config["training"]["sampling"]} + + +def validate_proofs(config, uploaded, arm, baseline_config, baseline, smoke): + if protocol_identity(config) != protocol_identity(baseline_config): + raise ValueError("Baseline task/sampling/harness protocol differs from this deployment") + if arm == "opencode": + baseline = baseline.get("daytona", baseline) + if baseline.get("implementation") != "standalone-opencode": + raise ValueError("Native training requires a native OpenCode baseline") + baseline = {**baseline, "arm": "opencode"} + expected = 1000 if arm == "blackbox" else 250 + if not (baseline.get("comparison_ready") and baseline.get("arm") == arm and + baseline.get("graded_cells") == expected and baseline.get("tito_pass")): + raise ValueError("Full baseline coverage/TiTO/version evidence is required") + if not (smoke.get("passed") and smoke.get("arm") == arm and smoke.get("bundle_sha256") == uploaded["sha256"] + and smoke.get("remote_restore_verified") and smoke.get("tito_pass") and smoke.get("weights_updated") + and smoke.get("native_optimizer_state_verified")): + raise ValueError("Optimizer/save/resume smoke must pass with this exact runtime bundle") + + +def validate_comparison_baseline(score): + expected = {"opencode", "claude-code", "codex", "mini-swe-agent"} + harnesses = score.get("harnesses", {}) + if (not score.get("comparison_ready") or not score.get("tito_pass") + or score.get("graded_cells") != 1000 or set(harnesses) != expected + or any(value.get("graded") != 250 for value in harnesses.values())): + raise ValueError("Checkpoint comparisons require the complete four-harness baseline, not the native diagnostic") + + +def archived_config(api, job): + volume = next(v for v in job.volumes if v.mount_path == "/bundle" and v.type == "dataset") + from huggingface_hub import hf_hub_download + archive = Path(hf_hub_download(volume.source, "bundle.tar.gz", repo_type="dataset", + revision=volume.revision, token=api.token)) + if hashlib.sha256(archive.read_bytes()).hexdigest() != job.environment["BUNDLE_SHA256"]: + raise ValueError("Baseline's archived runtime checksum differs from the executed bundle") + with tarfile.open(archive) as tar: + return json.load(tar.extractfile("hf/configs/deployment.json")) + + +def verify_comparison_baseline(api, config, job_id, out): + if not job_id: + raise ValueError("Native training also needs --comparison-baseline-job from the shared four-harness evaluation") + job = api.inspect_job(job_id=job_id, namespace=config["namespace"]) + if (job.status.stage != "COMPLETED" or job.labels.get("role") != "eval" + or job.labels.get("phase") != "baseline" or job.labels.get("arm") != "blackbox"): + raise ValueError("Comparison baseline must be a completed Harbor baseline Job") + if protocol_identity(config) != protocol_identity(archived_config(api, job)): + raise ValueError("Comparison baseline task/model/sampling protocol differs") + prefix = job.environment["RUN_ID"] + "/jobs/" + job.environment["RUN_OWNER"] + target = Path(out) / "launch-proofs/opencode/comparison-baseline.json" + target.parent.mkdir(parents=True, exist_ok=True) + api.download_bucket_files(job.environment["ARTIFACT_BUCKET"], + files=[(prefix + "/canonical_scores.json", str(target))], raise_on_missing_files=True) + validate_comparison_baseline(json.loads(target.read_text())) + return "hf://buckets/" + job.environment["ARTIFACT_BUCKET"] + "/" + prefix + + +def verify(api, config, uploaded, arm, baseline_id, smoke_id, out): + if not baseline_id or not smoke_id: + raise ValueError("Long training requires --baseline-job and --smoke-job") + jobs = [] + for job_id, role, phase in [(baseline_id, "eval", "baseline"), (smoke_id, "train", "smoke")]: + j = api.inspect_job(job_id=job_id, namespace=config["namespace"]) + if j.status.stage != "COMPLETED" or any(j.labels.get(k) != v for k, v in {"arm": arm, "role": role, "phase": phase}.items()): + raise ValueError(f"Job {job_id} is not a completed {arm} {role}/{phase}") + jobs.append(j) + out = Path(out) / "launch-proofs" / arm + out.mkdir(parents=True, exist_ok=True) + proofs = [] + sources = [] + for j, filename in zip(jobs, ["canonical_scores.json", "training_smoke_verified.json"]): + prefix = j.environment["RUN_ID"] + "/jobs/" + j.environment["RUN_OWNER"] + api.download_bucket_files(j.environment["ARTIFACT_BUCKET"], + files=[(prefix + "/" + filename, str(out / filename))], raise_on_missing_files=True) + proofs.append(json.loads((out / filename).read_text())) + sources.append("hf://buckets/" + j.environment["ARTIFACT_BUCKET"] + "/" + prefix) + baseline_config = archived_config(api, jobs[0]) + validate_proofs(config, uploaded, arm, baseline_config, proofs[0], proofs[1]) + smoke_job = jobs[1] + prefix = smoke_job.environment["RUN_ID"] + "/jobs/" + smoke_job.environment["RUN_OWNER"] + api.download_bucket_files(smoke_job.environment["ARTIFACT_BUCKET"], + files=[(prefix + "/space_identity.json", str(out / "space_identity.json"))], raise_on_missing_files=True) + service = json.loads((out / "space_identity.json").read_text()) + service_pin = smoke_job.environment.get("SPACE_BUNDLE_SHA256", uploaded["sha256"]) + if service.get("bundle_sha256") != service_pin: + raise ValueError("Optimizer smoke did not verify its declared environment bundle") + report = {"passed": True, "baseline_job": baseline_id, "smoke_job": smoke_id, + "baseline_prefix": sources[0], "smoke_prefix": sources[1], "bundle_sha256": uploaded["sha256"], + "space_bundle_sha256": service_pin, "space_url": smoke_job.environment["SPACE_URL"]} + (out / "verified.json").write_text(json.dumps(report, indent=2) + "\n") + return report + + +def validate_checkpoint_eval(uploaded, arm, smoke_id, job, score, evidence): + if (job.status.stage != "COMPLETED" or + any(job.labels.get(k) != v for k, v in + {"role": "eval", "phase": "checkpoint", "arm": arm, "training_job": smoke_id}.items())): + raise ValueError("Checkpoint evaluation must have completed for the qualified smoke") + expected = 250 if arm == "whitebox" else 1000 + if (not score.get("comparison_ready") or not score.get("tito_pass") or + score.get("graded_cells") != expected or score.get("arm") != arm or + evidence.get("step") != 4 or job.environment.get("CHECKPOINT_STEP") != "4" or + evidence.get("source") != job.environment.get("CHECKPOINT_PREFIX") or + evidence.get("bundle_sha256") != uploaded["sha256"] or + evidence.get("manifest_sha256") != job.environment.get("CHECKPOINT_MANIFEST_SHA") or + job.environment.get("BUNDLE_SHA256") != uploaded["sha256"]): + raise ValueError("Checkpoint evaluation coverage, TiTO or source identity differs") + + +def verify_checkpoint_eval(api, config, uploaded, arm, smoke_id, eval_id, out): + job = api.inspect_job(job_id=eval_id, namespace=config["namespace"]) + target = Path(out) / "launch-proofs" / arm / "checkpoint-eval" + target.mkdir(parents=True, exist_ok=True) + prefix = job.environment["RUN_ID"] + "/jobs/" + job.environment["RUN_OWNER"] + values = [] + for name in ["canonical_scores.json", "checkpoint_evaluation.json"]: + api.download_bucket_files(job.environment["ARTIFACT_BUCKET"], + files=[(prefix + "/" + name, str(target / name))], raise_on_missing_files=True) + values.append(json.loads((target / name).read_text())) + validate_checkpoint_eval(uploaded, arm, smoke_id, job, *values) + return {"passed": True, "job_id": eval_id, "step": 4, + "manifest_sha256": values[1]["manifest_sha256"]} diff --git a/04-data-agent/hf/launch_qualified.py b/04-data-agent/hf/launch_qualified.py new file mode 100644 index 0000000..9a90a08 --- /dev/null +++ b/04-data-agent/hf/launch_qualified.py @@ -0,0 +1,137 @@ +"""Launch an authorized comparison after its live qualification dependency passes. + +Submission intent is durable before allocation. Repeated invocations adopt the +same HF owner; an ambiguous submission is never blindly repeated. +""" +import argparse +import fcntl +import json +from pathlib import Path +import subprocess +import sys +import time + +HF = Path(__file__).resolve().parent + + +def read(path): + return json.loads(Path(path).read_text()) + + +def write(path, value): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_suffix('.tmp') + tmp.write_text(json.dumps(value, indent=2) + '\n') + tmp.replace(path) + + +def run(command): + subprocess.run([str(x) for x in command], check=True) + + +class RecordedAPI: + def __init__(self, api, intent): + self.api, self.intent = api, intent + + def __getattr__(self, name): + return getattr(self.api, name) + + def run_job(self, **kwargs): + if self.intent.exists(): + raise RuntimeError('Submission intent already exists: reconcile its owner before allocating') + write(self.intent, {'submitted_at': time.time(), 'state': 'submitting', + 'owner': kwargs['env']['RUN_OWNER'], 'namespace': kwargs['namespace'], + 'labels': kwargs['labels'], 'bundle_sha256': kwargs['env']['BUNDLE_SHA256']}) + job = self.api.run_job(**kwargs) + write(self.intent, {**read(self.intent), 'state': 'submitted', 'training_job': job.id}) + return job + + +def whitebox(args, state): + from huggingface_hub import HfApi + from deploy import credentials, submit + plan = read(args.ready / 'plan.json') + if plan.get('status') != 'qualification_passed_main_not_submitted': + raise ValueError('HF live optimizer/checkpoint/logging qualification is not complete') + secrets = credentials(args.env_file) + api = HfApi(token=secrets['HF_TOKEN']) + intent = args.out / 'hf-submit-intent.json' + if intent.exists(): + prior = read(intent) + matches = [j for j in api.list_jobs(namespace=prior['namespace'], labels=prior['labels']) + if j.environment.get('RUN_OWNER') == prior['owner']] + if len(matches) != 1 or matches[0].environment.get('BUNDLE_SHA256') != prior['bundle_sha256']: + raise RuntimeError('Ambiguous HF submission; refusing to allocate a duplicate') + training_job = matches[0].id + else: + job_args = argparse.Namespace(role='train', arm='whitebox', phase='long', flavor='h200x2', + timeout='24h', dp=1, limit=0, resume_eval_owner=None, training_job=None, + baseline_job=plan['baseline_job'], smoke_job=plan['smoke_job'], + checkpoint_eval_job=plan['checkpoint_eval_job'], external_checkpoint_coordinator=True, + dry_run=False) + value = submit(RecordedAPI(api, intent), read(plan['config']), secrets, args.ready, job_args) + training_job = value['id'] + state.update(training_job=training_job, provider='hf', training_submitted=True) + write(args.out / 'state.json', state) + controller = args.out / 'controller' + if not (controller / 'submission.json').exists(): + if controller.exists(): + raise RuntimeError('Partial controller submission: reconcile Slurm before retrying') + run([sys.executable, HF / 'hf_followup.py', 'prepare', '--training-job', training_job, + '--bundle', args.bundle, '--env-file', args.env_file, '--coordination-dir', args.coordination_dir, + '--out', controller, '--submit']) + receipt = read(controller / 'submission.json') + state.update(controller_job=receipt['controller_job'], controller_plan=str(controller / 'plan.json')) + + +def opencode(args, state): + plan = read(args.ready / 'plan.json') + if plan.get('checkpoint_eval_gpu_validation') != 'passed': + raise ValueError('Native live checkpoint qualification is not complete') + if not plan.get('native_baseline_score'): + raise ValueError('Prepare a plan with both native diagnostic and four-harness comparison baselines') + target = args.out / 'run' + if not target.exists(): + run([sys.executable, HF / 'local_long.py', '--arm', 'opencode', + '--smoke-run', plan['smoke_run'], '--baseline-score', plan['native_baseline_score'], + '--comparison-baseline-score', plan['baseline_score'], + '--checkpoint-eval-proof', plan['checkpoint_eval_proof'], '--env-file', args.env_file, + '--coordination-dir', args.coordination_dir, '--out', target, '--submit']) + live = read(target / 'plan.json') + if not live.get('training_job') or not live.get('controller_job'): + raise RuntimeError('Partial local submission: reconcile Slurm before retrying') + state.update(training_job=live['training_job'], controller_job=live['controller_job'], provider='slurm', + training_submitted=True, controller_plan=str(target / 'plan.json'), + training_output=str(Path(live['root']) / 'outputs' / ('local-train-opencode-' + live['training_job']))) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--arm', choices=['opencode', 'whitebox'], required=True) + for name in ('ready', 'out', 'env-file', 'coordination-dir'): + parser.add_argument('--' + name, type=Path, required=True) + parser.add_argument('--bundle', type=Path) + args = parser.parse_args() + args.out.mkdir(parents=True, exist_ok=True) + with (args.out / 'launch.lock').open('a') as lock: + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + state = read(args.out / 'state.json') if (args.out / 'state.json').exists() else { + 'arm': args.arm, 'authorized': True, 'started_at': time.time(), 'training_submitted': False} + if state.get('complete'): + print(json.dumps(state)) + return + write(args.out / 'state.json', state) + try: + (whitebox if args.arm == 'whitebox' else opencode)(args, state) + state.update(complete=True, completed_at=time.time()) + except Exception as exc: + state.update(error_type=type(exc).__name__, failed_at=time.time()) + write(args.out / 'state.json', state) + raise + write(args.out / 'state.json', state) + print(json.dumps(state)) + + +if __name__ == '__main__': + main() diff --git a/04-data-agent/hf/local_followup.py b/04-data-agent/hf/local_followup.py new file mode 100644 index 0000000..6c64cd3 --- /dev/null +++ b/04-data-agent/hf/local_followup.py @@ -0,0 +1,358 @@ +"""Local checkpoint evaluations using the qualified trainer's immutable runtime. + +The training arm identifies checkpoint provenance. The evaluation suite is separate: +native OpenCode-trained weights run through all four Harbor adapters; SETA-trained +weights use the native bash/SETA evaluator. Neither operation contacts the trainer. +""" +import argparse +import fcntl +import hashlib +import json +import os +from pathlib import Path +import shlex +import signal +import subprocess +import sys +import time +from types import SimpleNamespace + +TERMINAL = {"COMPLETED", "FAILED", "CANCELLED", "TIMEOUT", "OUT_OF_MEMORY", "NODE_FAIL", "PREEMPTED"} +EVAL_ADMISSION = {"SANDBOX_CAPACITY": "58", "TRAIN_RESERVED_SANDBOXES": "8"} + + +def read(path): + return json.loads(Path(path).read_text()) + + +def write(path, value): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + temporary = path.with_suffix(".tmp") + temporary.write_text(json.dumps(value, indent=2) + "\n") + temporary.replace(path) + + +def load_runtime(plan): + root = Path(plan["root"]) + if hashlib.sha256(Path(__file__).read_bytes()).hexdigest() != plan["controller_sha256"]: + raise ValueError("Checkpoint controller source changed after preparation") + os.environ["REPRO_ROOT"] = str(root) + sys.path.insert(0, str(root / "hf/runtime")) + from common import configure, verify_bundle + configure() + verify_bundle() + manifest = (root / "bundle_manifest.json").read_bytes() + if hashlib.sha256(manifest).hexdigest() != plan["bundle_sha256"]: + raise ValueError("Qualified runtime identity changed") + from dotenv import dotenv_values + values = dotenv_values(plan["env_file"]) + os.environ["HF_TOKEN"] = values.get("HF_API_KEY") or values["HF_TOKEN"] + for key in ("DAYTONA_API_KEY", "DAYTONA_API_URL", "DAYTONA_TARGET", "E2B_API_KEY"): + if values.get(key): + os.environ[key] = values[key] + config = read(root / "hf/configs/deployment.json") + os.environ.update(LOCAL_RUNTIME="1", BUNDLE_SHA256=plan["bundle_sha256"], + ARTIFACT_BUCKET=config["resources"]["artifacts_bucket"], RUN_ID=config["run_id"], + COMPARISON_ARM=plan["arm"], HF_HOME=os.environ.get("HF_HOME", str(root / "cache/huggingface")), + JOB_FLAVOR="hopper-prod-2h100", EVAL_CONCURRENCY="50", + **EVAL_ADMISSION, + OPENENV_HARBOR_AGENT_VERSIONS=json.dumps(config["harness_pins"])) + return root, config + + +def environment(plan): + root, _ = load_runtime(plan) + # Validate the same admission policy used by actual MCP rollouts at startup. + from service_policy import admission + if admission.total - admission.train_reserve != 50: + raise ValueError("Local checkpoint service must admit exactly 50 eval sandboxes") + from common import RUN + trials = root / "outputs" / os.environ["RUN_OWNER"] / "trials" + os.environ.update(ENABLE_WEB_INTERFACE="false", MAX_CONCURRENT_ENVS="128") + if plan["arm"] in {"blackbox", "opencode"}: + os.environ.update(COMPARISON_ARM="blackbox", OPENENV_HARBOR_TRIALS_DIR=str(trials), + OPENENV_DATASETS=str(RUN / "datasets/test"), OPENENV_MODEL="Qwen/Qwen3.5-2B", + OPENENV_CAPTURE_PORT=os.environ["DATA_AGENT_CAPTURE_PORT"], OPENENV_EXPOSE="gradio", + OPENENV_MAX_OUTPUT_TOKENS="4096", OPENENV_CAPTURE_TRANSPORT="tunnel") + from harbor_service import install + install() + from harbor_env.server.app import app + from fastapi import HTTPException + + @app.get("/trial/{name}/result") + def trial_result(name: str): + if Path(name).name != name or name in {".", ".."}: + raise HTTPException(400) + target = trials / name / "result.json" + if not target.is_file(): + raise HTTPException(404) + return read(target) + else: + os.environ.update(WHITE_BOX_BASH_TASK_SOURCE="harbor-frozen", + DAYTONA_WHITEBOX_TRIALS=str(trials), WHITE_BOX_BASH_MAX_SESSIONS="50", + WHITE_BOX_BASH_MAX_CONCURRENT_ENVS="128") + from whitebox_bash.server.app import app + import uvicorn + uvicorn.run(app, host="127.0.0.1", port=int(os.environ["LOCAL_ENV_PORT"]), + ws_ping_interval=20, ws_ping_timeout=None, timeout_keep_alive=120) + + +def cleanup(plan, output): + from concurrent.futures import ThreadPoolExecutor + from daytona import Daytona, ListSandboxesQuery + labels = {"experiment": "daytona-harness-comparison", "run": "20260915", + "arm": "blackbox" if plan["arm"] in {"blackbox", "opencode"} else "whitebox", + "owner": os.environ["RUN_OWNER"]} + api = Daytona() + sandboxes = list(api.list(ListSandboxesQuery(labels=labels), request_timeout=30)) + with ThreadPoolExecutor(max_workers=8) as pool: + list(pool.map(lambda sandbox: api.delete(sandbox, timeout=60, wait=True), sandboxes)) + remaining = sandboxes + for _ in range(10): + remaining = list(api.list(ListSandboxesQuery(labels=labels), request_timeout=15)) + if not remaining: + break + time.sleep(3) + write(output / "cleanup.json", {"labels": labels, "deleted": len(sandboxes), "remaining": len(remaining)}) + if remaining: + raise RuntimeError("Evaluation-owned sandbox cleanup incomplete") + + +def validate_evaluation(plan, output, record): + """Keep a two-task execution qualification distinct from a full pass@1 eval.""" + scores = read(output / "canonical_scores.json") + qualification = plan.get("checkpoint_eval_qualification", False) + if qualification: + suite = "blackbox" if plan["arm"] in {"blackbox", "opencode"} else "whitebox" + if suite == "blackbox": + config = read(Path(plan["root"]) / "hf/configs/deployment.json") + pins = config["harness_pins"] + audit = read(output / "final_tito.json") + indices = [int(i) for i in (Path(plan["root"]) / + "experiments/daytona_harness_comparison/logs/20260915/test_indices.txt").read_text().replace(",", " ").split()[:2]] + expected = {(h, i) for h in pins for i in indices} + reports = audit.get("reports", []) + if ({(r["harness"], r["index"]) for r in reports} != expected or + len(reports) != len(expected) or not all(r["tito_pass"] for r in reports)): + raise ValueError("Checkpoint smoke must audit both tasks through all four harnesses") + if scores.get("harness_versions") != {h: {v: 2} for h, v in pins.items()}: + raise ValueError("Checkpoint smoke harness versions differ") + cells = 8 + else: + selected = {} + for line in (output / "attempts.jsonl").read_text().splitlines(): + row = json.loads(line) + if row.get("reward") in (0, 1) and row.get("tito_pass"): + selected.setdefault((row["harness"], row["index"]), row) + if len(selected) != 2 or any(h != "whitebox_seta" for h, _ in selected): + raise ValueError("Checkpoint smoke must grade two native SETA trajectories") + cells = 2 + if scores.get("graded_cells") != cells: + raise ValueError("Checkpoint smoke coverage mismatch") + else: + cells = 1000 if plan["arm"] in {"blackbox", "opencode"} else 250 + if not scores.get("comparison_ready") or scores.get("graded_cells") != cells: + raise ValueError("Checkpoint evaluation lacks complete graded/TiTO/version evidence") + proof = {"passed": True, "qualification_only": qualification, "graded_cells": cells, + "training_arm": plan["arm"], "step": record["step"], + "manifest_sha256": record["manifest_sha256"], "bundle_sha256": plan["bundle_sha256"], + "controller_sha256": plan["controller_sha256"]} + write(output / "checkpoint_eval_verified.json", proof) + return proof + + +def evaluate(plan_path, record_path): + plan, record = read(plan_path), read(record_path) + root, _ = load_runtime(plan) + job_id = os.environ["SLURM_JOB_ID"] + seed = int(job_id) % 1000 + if len(os.environ["CUDA_VISIBLE_DEVICES"].split(",")) != 2: + raise ValueError("Checkpoint eval requires two separately allocated GPUs") + owner = f"local-eval-{plan['arm']}-{job_id}" + os.environ.update(RUN_OWNER=owner, LOCAL_INFERENCE_PORT=str(12000 + seed), + LOCAL_ENV_PORT=str(14000 + seed), DATA_AGENT_CAPTURE_PORT=str(16000 + seed), + VLLM_DP_RPC_PORT=str(26000 + seed)) + output = root / "outputs" / owner + output.mkdir(parents=True, exist_ok=False) + from common import ENV_PY, ready, start + from artifacts import Publisher + from checkpoint_store import restore_model + from telemetry import Telemetry + import job + processes = [] + publisher = Publisher(output) + publisher.start() + telemetry = Telemetry(output, job.inference_url()) + telemetry.start() + status = {"passed": False, "training_arm": plan["arm"], "step": record["step"], "started_at": time.time()} + try: + model = output / "inference-model" + manifest = restore_model(record["checkpoint_prefix"], model, arm=plan["arm"], + bundle_sha256=plan["bundle_sha256"], manifest_sha256=record["manifest_sha256"]) + if manifest["step"] != record["step"]: + raise ValueError("Checkpoint step changed after dispatch") + os.environ["CHECKPOINT_MODEL"] = str(model) + write(output / "checkpoint_evaluation.json", {**record, "training_arm": plan["arm"], + "bundle_sha256": plan["bundle_sha256"], "controller_sha256": plan["controller_sha256"]}) + server = "http://127.0.0.1:" + os.environ["LOCAL_ENV_PORT"] + proc = start([ENV_PY, Path(__file__), "environment", "--plan", plan_path], output / "environment.log") + processes.append(proc) + ready(server + "/health", proc, seconds=300) + suite = "blackbox" if plan["arm"] in {"blackbox", "opencode"} else "whitebox" + args = SimpleNamespace(role="eval", arm=suite, + phase="smoke" if plan.get("checkpoint_eval_qualification") else "checkpoint", dp=2, limit=0) + _, public = job.serving(args, output, processes) + write(output / "services.json", {"server": server, "inference": public, "tp": 1, "dp": 2, + "concurrency": 50, "training_job": plan["training_job"], "evaluation_suite": suite}) + job.evaluate(args, output, server, public) + validate_evaluation(plan, output, record) + status["passed"] = True + finally: + status["finished_at"] = time.time() + write(output / "status.json", status) + telemetry.finish() + for process in reversed(processes): + if process.poll() is None: + os.killpg(process.pid, signal.SIGTERM) + # Use the environment interpreter for Daytona's pinned dependencies. + try: + subprocess.run([str(ENV_PY), str(Path(__file__)), "cleanup", "--plan", str(plan_path), + "--output", str(output)], check=True, timeout=240) + finally: + publisher.finish() + + +def state(job): + value = subprocess.check_output(["sacct", "-X", "-n", "-P", "-j", str(job), + "--format=JobID,State"], text=True) + for line in value.splitlines(): + fields = line.split("|") + if fields[0] == str(job): + return fields[1].split()[0].rstrip("+") + return "UNKNOWN" + + +def eligible_steps(manifests, finished, qualification=False): + if qualification: + # The two-phase optimizer smoke saves at2 and4; eval4 exercises the + # same save/eval2:1 cadence as the main run's50/100 settings. + return {4} if any(m["step"] == 4 for m in manifests) else set() + maximum = max((m["step"] for m in manifests), default=0) + return {m["step"] for m in manifests if m["step"] > 0 and + (m["step"] % 100 == 0 or m.get("final", False) or finished and m["step"] == maximum)} + + +def admit_eval(plan): + # Serialize comparison evals across both arms. Training retains its 24-slot reserve. + jobs = subprocess.check_output(["squeue", "--noheader", "--user", os.environ["USER"], + "--format=%j"], text=True).splitlines() + if any(name.strip().startswith("cmp-eval-") for name in jobs): + return False + root = Path(plan["root"]) + code = "from daytona import Daytona; print(len(list(Daytona().list())))" + occupied = int(subprocess.check_output([str(root / "OpenEnv/.venv/bin/python"), "-c", code], text=True).strip()) + return occupied + 50 + 24 <= 125 + + +def watch(plan_path, submit): + plan_path = Path(plan_path).resolve() + plan = read(plan_path) + root, config = load_runtime(plan) + training = root / "outputs" / f"local-train-{plan['arm']}-{plan['training_job']}" + out = plan_path.parent / "checkpoint-evals" + out.mkdir(exist_ok=True) + lock = (out / "watch.lock").open("w") + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + tracking = read(out / "state.json") if (out / "state.json").exists() else {} + while True: + training_state = state(plan["training_job"]) + published = read(training / "upload_status.json") if (training / "upload_status.json").exists() else {} + manifests = [] + for name in published.get("published_checkpoints", []): + marker = training / "run" / name / "checkpoint.hf.ready.json" + manifest = read(marker) + if manifest["arm"] != plan["arm"] or manifest["bundle_sha256"] != plan["bundle_sha256"]: + raise ValueError("Published checkpoint provenance mismatch") + manifests.append(manifest) + eligible = eligible_steps(manifests, training_state in TERMINAL, + qualification=plan.get("checkpoint_eval_qualification", False)) + active, alerts = [], [] + for key, record in tracking.items(): + if not record.get("job_id"): + raise RuntimeError("Ambiguous submission intent; reconcile Slurm before another submission") + record["state"] = state(record["job_id"]) + if record["state"] not in TERMINAL: + active.append(record["job_id"]) + elif record["state"] != "COMPLETED": + alerts.append(record) + else: + eval_output = root / "outputs" / f"local-eval-{plan['arm']}-{record['job_id']}" + scores_path = eval_output / "canonical_scores.json" + scores = read(scores_path) + proof = read(eval_output / "checkpoint_eval_verified.json") + if (not proof.get("passed") or proof.get("manifest_sha256") != record["manifest_sha256"] or + proof.get("controller_sha256") != plan["controller_sha256"] or + bool(proof.get("qualification_only")) != bool(plan.get("checkpoint_eval_qualification"))): + raise ValueError("Completed evaluation proof differs from its dispatch") + write(out / f"scores-{record['step']:06d}.json", {"evaluation": record, "scores": scores, "proof": proof}) + pending = sorted(eligible - {r["step"] for r in tracking.values()}) + capacity_lock = (Path(plan["coordination_dir"]) / "eval-admission.lock").open("a") + fcntl.flock(capacity_lock, fcntl.LOCK_EX) + if submit and pending and not active and not alerts and admit_eval(plan): + step = pending[0] + marker = training / "run" / f"checkpoint-{step}" / "checkpoint.hf.ready.json" + sha = hashlib.sha256(marker.read_bytes()).hexdigest() + key = hashlib.sha256(f"{plan['training_job']}:{sha}:{plan['controller_sha256']}".encode()).hexdigest() + record = {"step": step, "manifest_sha256": sha, + "checkpoint_prefix": published["destination"] + f"/run/checkpoint-{step}", + "status": "submitting"} + record_path = out / f"checkpoint-{step}.json" + write(record_path, record) + command = [str(root / ".venv312/bin/python"), "-u", str(Path(__file__).resolve()), "eval", + "--plan", str(plan_path), "--record", str(record_path)] + script = out / f"checkpoint-{step}.slurm" + script.write_text("#!/bin/bash\nset -euo pipefail\nexec " + shlex.join(command) + "\n") + tracking[key] = record + write(out / "state.json", tracking) + # Slurm owns separate GPU devices; the shared admission lock spans inspection and submission. + result = subprocess.check_output(["sbatch", "--parsable", "--partition=" + plan.get("gpu_partition", "hopper-prod"), + "--gres=gpu:2", "--cpus-per-task=8", "--mem=96G", "--time=04:00:00", + f"--job-name=cmp-eval-{plan['arm']}-{step}", f"--output={out}/slurm-%j.out", + f"--error={out}/slurm-%j.err", str(script)], text=True).strip().split(";")[0] + if not result.isdigit(): + raise RuntimeError("Ambiguous Slurm submission response") + record.update(job_id=result, status="submitted") + active.append(result) + pending.remove(step) + capacity_lock.close() + write(out / "state.json", tracking) + write(out / "monitor.json", {"checked_at": time.time(), "training_state": training_state, + "active_eval_jobs": active, "pending_steps": pending, "alerts": alerts}) + if alerts: + raise RuntimeError("Checkpoint evaluation failed; inspect the recorded job before retrying") + if not submit or training_state in TERMINAL and not active and not pending: + return + time.sleep(60) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("action", choices=["watch", "eval", "environment", "cleanup"]) + parser.add_argument("--plan", type=Path, required=True) + parser.add_argument("--record", type=Path) + parser.add_argument("--output", type=Path) + parser.add_argument("--submit", action="store_true") + args = parser.parse_args() + plan = read(args.plan) + if hashlib.sha256(Path(__file__).read_bytes()).hexdigest() != plan["controller_sha256"]: + raise ValueError("Checkpoint controller changed after planning") + if args.action == "watch": + watch(args.plan, args.submit) + elif args.action == "eval": + evaluate(args.plan, args.record) + elif args.action == "environment": + environment(plan) + else: + load_runtime(plan) + cleanup(plan, args.output) diff --git a/04-data-agent/hf/local_long.py b/04-data-agent/hf/local_long.py new file mode 100644 index 0000000..e9b68bd --- /dev/null +++ b/04-data-agent/hf/local_long.py @@ -0,0 +1,178 @@ +"""Prepare a local long run using the exact runtime that passed optimizer qualification. + +No jobs are submitted unless --submit is provided. Smoke and baseline evidence are +required even for planning. The CPU checkpoint controller is a separately frozen +orchestration artifact; it does not change the qualified trainer source. +""" +import argparse +import hashlib +import json +from pathlib import Path +import shlex +import shutil +import subprocess + + +def proof_for(smoke, arm): + root = smoke.parent.parent + proof = json.loads((smoke / "training_smoke_verified.json").read_text()) + identity = json.loads((root / "local_manifest.json").read_text()) + if (proof.get("arm") != arm or proof.get("bundle_sha256") != identity["sha256"] or + not all(proof.get(k) for k in ["passed", "remote_restore_verified", "tito_pass", "weights_updated", "native_optimizer_state_verified"])): + raise ValueError("No matching successful optimizer/save/remote-resume proof") + encoded = (root / "bundle_manifest.json").read_bytes() + if hashlib.sha256(encoded).hexdigest() != identity["sha256"]: + raise ValueError("Qualified source manifest changed") + for name, expected in json.loads(encoded)["files"].items(): + if hashlib.sha256((root / name).read_bytes()).hexdigest() != expected: + raise ValueError(f"Qualified source file changed: {name}") + return root, identity + + +def validate_baseline(path, arm): + score = json.loads(path.read_text()) + if arm == "opencode": + score = score.get("daytona", score) + valid = score.get("implementation") == "standalone-opencode" + else: + valid = score.get("arm") == arm + if not (valid and score.get("comparison_ready") and score.get("graded_cells") == (1000 if arm == "blackbox" else 250) and score.get("tito_pass")): + raise ValueError("A completed matching fixed-task baseline is required") + + +def validate_native_grading(root, baseline): + """Bind the native runtime to the frozen-tolerance and deterministic rescore audit.""" + proof_path = baseline.parent / "verification.json" + if not proof_path.is_file(): + raise ValueError("Native training requires the explicit-tolerance baseline verification") + proof = json.loads(proof_path.read_text()) + if (not proof.get("passed") or proof.get("parameters_verified") != 1250 or + not proof.get("original_graded_records_preserved") or len(proof.get("reports", [])) != 250): + raise ValueError("Incomplete native grading/data verification") + package = root / "experiments/daytona_harness_comparison/logs/20260915/source/packages/data_agent_env" + for name in ("task.py", "tasks.py", "verifier.py", "grader.py"): + if hashlib.sha256((package / name).read_bytes()).hexdigest() != proof["runtime_files"][name]: + raise ValueError("Native grader differs from the verified frozen-tolerance protocol: " + name) + return str(proof_path.resolve()) + + +def prepare(args): + import re + for name in ("partition", "cpu_partition"): + if not re.fullmatch(r"[A-Za-z0-9_-]+", getattr(args, name, "hopper-prod")): + raise ValueError("Invalid Slurm partition name") + smoke, output = args.smoke_run.resolve(), args.out.resolve() + root, identity = proof_for(smoke, args.arm) + validate_baseline(args.baseline_score, args.arm) + grading_proof = validate_native_grading(root, args.baseline_score) if args.arm == "opencode" else None + config = json.loads((root / "hf/configs/deployment.json").read_text()) + comparison = args.baseline_score + if args.arm == "opencode": + from launch_gates import protocol_identity, validate_comparison_baseline + comparison = getattr(args, "comparison_baseline_score", None) + if comparison is None: + raise ValueError("Native training also requires --comparison-baseline-score for the four-harness curve") + validate_comparison_baseline(json.loads(comparison.read_text())) + baseline_root = comparison.resolve().parents[2] + baseline_config = json.loads((baseline_root / "hf/configs/deployment.json").read_text()) + if protocol_identity(config) != protocol_identity(baseline_config): + raise ValueError("Comparison baseline task/model/sampling protocol differs") + output.mkdir(parents=True, exist_ok=False) + controller = output / "local_followup.py" + shutil.copyfile(Path(__file__).with_name("local_followup.py"), controller) + supervisor = output / "local_watch.py" + supervisor_source = Path(__file__).with_name("local_watch.py") + if supervisor_source.exists(): + shutil.copyfile(supervisor_source, supervisor) + args.coordination_dir.mkdir(parents=True, exist_ok=True) + plan = {"gpu_partition": getattr(args, "partition", "hopper-prod"), + "namespace": config["namespace"], + "cpu_partition": getattr(args, "cpu_partition", "hopper-cpu"), "root": str(root), "arm": args.arm, "bundle_sha256": identity["sha256"], + "env_file": str(args.env_file.resolve()), "baseline_score": str(comparison.resolve()), + "baseline_sha256": hashlib.sha256(comparison.read_bytes()).hexdigest(), + "native_baseline_score": str(args.baseline_score.resolve()) if args.arm == "opencode" else None, + "smoke_run": str(smoke), "controller_sha256": hashlib.sha256(controller.read_bytes()).hexdigest(), + "grading_verification": grading_proof, + "coordination_dir": str(args.coordination_dir.resolve()), "training_job": None, + "save_steps": 50, "eval_steps": 100, "eval_concurrency": 50, "eval_dp": 2, + "eval_cells": 1000 if args.arm in {"blackbox", "opencode"} else 250, + "long_training_submitted": False, "checkpoint_eval_gpu_validation": "pending"} + if supervisor.exists(): + plan["supervisor_sha256"] = hashlib.sha256(supervisor.read_bytes()).hexdigest() + if args.checkpoint_eval_proof: + evaluation = json.loads(args.checkpoint_eval_proof.read_text()) + if (not evaluation.get("passed") or not evaluation.get("qualification_only") or + evaluation.get("bundle_sha256") != identity["sha256"] or + evaluation.get("controller_sha256") != plan["controller_sha256"] or + evaluation.get("training_arm") != args.arm or evaluation.get("step") != 4 or + evaluation.get("graded_cells") != (8 if args.arm in {"blackbox", "opencode"} else 2)): + raise ValueError("No matching successful checkpoint-4 evaluation qualification") + plan["checkpoint_eval_gpu_validation"] = "passed" + plan["checkpoint_eval_proof"] = str(args.checkpoint_eval_proof.resolve()) + if args.qualify_checkpoint_eval: + plan["training_job"] = smoke.name.rsplit("-", 1)[-1] + if not plan["training_job"].isdigit(): + raise ValueError("Local smoke owner does not identify its Slurm job") + plan["checkpoint_eval_qualification"] = True + plan["eval_cells"] = 8 if args.arm in {"blackbox", "opencode"} else 2 + destination = ("hf://buckets/" + config["resources"]["artifacts_bucket"] + "/" + config["run_id"] + + "/jobs/" + smoke.name) + command = [str(root / ".venv312/bin/python"), "-u", str(root / "hf/runtime/local_entry.py"), + "--role", "train", "--arm", args.arm, "--phase", "long", "--dp", "1"] + script = output / "train.slurm" + script.write_text(f'''#!/bin/bash +#SBATCH --job-name=local-{args.arm}-long +#SBATCH --partition={plan["gpu_partition"]} +#SBATCH --gres=gpu:2 +#SBATCH --cpus-per-task=8 +#SBATCH --mem=96G +#SBATCH --time=24:00:00 +#SBATCH --output={output}/train-%j.out +#SBATCH --error={output}/train-%j.err +set -euo pipefail +export REPRO_ROOT={shlex.quote(str(root))} +export LOCAL_ENV_FILE={shlex.quote(str(args.env_file.resolve()))} +export SMOKE_PREFIX={shlex.quote(destination)} +export OMP_NUM_THREADS=1 PYTHONUNBUFFERED=1 TOKENIZERS_PARALLELISM=false +exec {shlex.join(command)} +''') + plan_path = output / "plan.json" + plan_path.write_text(json.dumps(plan, indent=2) + "\n") + if args.submit: + if args.qualify_checkpoint_eval or plan["checkpoint_eval_gpu_validation"] != "passed" or not supervisor.exists(): + raise RuntimeError("Long launch requires optimizer and live checkpoint-eval qualification; plan is saved") + plan["submission_state"] = "submitting" + plan_path.write_text(json.dumps(plan, indent=2) + "\n") + job = subprocess.check_output(["sbatch", "--parsable", str(script)], text=True).strip().split(";")[0] + if not job.isdigit(): + raise RuntimeError("Ambiguous training submission; reconcile Slurm before retrying") + plan.update(training_job=job, long_training_submitted=True, submission_state="submitted") + plan_path.write_text(json.dumps(plan, indent=2) + "\n") + command = [str(root / ".venv312/bin/python"), "-u", str(supervisor), "--plan", str(plan_path)] + cpu = output / "controller.slurm" + cpu.write_text("#!/bin/bash\nset -euo pipefail\nexec " + shlex.join(command) + "\n") + controller_job = subprocess.check_output(["sbatch", "--parsable", "--partition=" + plan["cpu_partition"], + "--cpus-per-task=2", "--mem=8G", "--time=36:00:00", f"--job-name=cmp-follow-{args.arm}", + f"--output={output}/controller-%j.out", f"--error={output}/controller-%j.err", str(cpu)], text=True).strip() + plan["controller_job"] = controller_job + plan_path.write_text(json.dumps(plan, indent=2) + "\n") + print(json.dumps({"plan": str(plan_path), "training_script": str(script), "submitted": args.submit})) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--smoke-run", type=Path, required=True) + parser.add_argument("--baseline-score", type=Path, required=True) + parser.add_argument("--comparison-baseline-score", type=Path) + parser.add_argument("--arm", choices=["blackbox", "whitebox", "opencode"], required=True) + parser.add_argument("--env-file", type=Path, required=True) + parser.add_argument("--out", type=Path, required=True) + parser.add_argument("--coordination-dir", type=Path, required=True) + parser.add_argument("--partition", default="hopper-prod") + parser.add_argument("--cpu-partition", default="hopper-cpu") + parser.add_argument("--submit", action="store_true") + parser.add_argument("--checkpoint-eval-proof", type=Path, + help="Passing live checkpoint-4 evaluation proof required before --submit") + parser.add_argument("--qualify-checkpoint-eval", action="store_true", + help="Follow the completed smoke's checkpoint with a separate two-task eval job") + prepare(parser.parse_args()) diff --git a/04-data-agent/hf/local_watch.py b/04-data-agent/hf/local_watch.py new file mode 100644 index 0000000..be88b54 --- /dev/null +++ b/04-data-agent/hf/local_watch.py @@ -0,0 +1,136 @@ +"""Run the qualified local eval controller and feed completed scores to Trackio.""" +import argparse +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import subprocess +import sys +import time + + +def admit_with_hf(original, plan, api=None): + """The caller holds the joint admission lock across inspection/submission.""" + from huggingface_hub import HfApi + terminal = {"COMPLETED", "ERROR", "CANCELED", "CANCELLED", "DELETED"} + jobs = (api or HfApi()).list_jobs(namespace=plan.get("namespace", "HuggingEnvs"), + labels={"experiment": "data-agent-daytona", "role": "eval"}) + if any(job.status.stage not in terminal for job in jobs): + return False + return original(plan) + + +def dispatch(plan_path): + """Wrap admission only; keep the live-qualified evaluator source unchanged.""" + plan_path = Path(plan_path).resolve() + plan = json.loads(plan_path.read_text()) + if hashlib.sha256(Path(__file__).read_bytes()).hexdigest() != plan["supervisor_sha256"]: + raise ValueError("Score supervisor changed after preparation") + spec = importlib.util.spec_from_file_location("qualified_local_followup", plan_path.parent / "local_followup.py") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + original = module.admit_eval + module.admit_eval = lambda p: admit_with_hf(original, p) + module.watch(plan_path, True) + + +def forward_scores(plan_path): + plan_path = Path(plan_path) + plan = json.loads(plan_path.read_text()) + training = Path(plan["root"]) / "outputs" / f"local-train-{plan['arm']}-{plan['training_job']}" + destination = training / "checkpoint-scores" + destination.mkdir(parents=True, exist_ok=True) + changed = False + if plan.get("baseline_sha256"): + encoded = Path(plan["baseline_score"]).read_bytes() + if hashlib.sha256(encoded).hexdigest() != plan["baseline_sha256"]: + raise ValueError("Qualified baseline score changed") + event = {"step": 0, "scores": json.loads(encoded), + "source": {"baseline_sha256": plan["baseline_sha256"]}} + target = destination / "step-000000.json" + if target.exists() and json.loads(target.read_text()) != event: + raise ValueError("Conflicting baseline for the training curve") + if not target.exists(): + temporary = target.with_suffix(".tmp") + temporary.write_text(json.dumps(event, indent=2) + "\n") + temporary.replace(target) + changed = True + for path in (plan_path.parent / "checkpoint-evals").glob("scores-*.json"): + record = json.loads(path.read_text()) + proof, evaluation, scores = record["proof"], record["evaluation"], record["scores"] + if proof.get("qualification_only"): + continue + if (not proof.get("passed") or not scores.get("comparison_ready") or + proof.get("training_arm") != plan["arm"] or + proof.get("graded_cells") != (1000 if plan["arm"] in {"opencode", "blackbox"} else 250) or + proof["bundle_sha256"] != plan["bundle_sha256"] or + proof["manifest_sha256"] != evaluation["manifest_sha256"] or + proof["step"] != evaluation["step"]): + raise ValueError("Unverified checkpoint scores cannot enter the training curve") + event = {"step": evaluation["step"], "scores": scores, + "source": {"job_id": evaluation["job_id"], "manifest_sha256": proof["manifest_sha256"]}} + target = destination / f"step-{evaluation['step']:06d}.json" + if target.exists(): + if json.loads(target.read_text()) != event: + raise ValueError("Conflicting completed scores for the same checkpoint") + continue + temporary = target.with_suffix(".tmp") + temporary.write_text(json.dumps(event, indent=2) + "\n") + temporary.replace(target) + changed = True + return plan, training, changed + + +def watch(plan_path): + from dotenv import dotenv_values + plan_path = Path(plan_path).resolve() + plan = json.loads(plan_path.read_text()) + root = Path(plan["root"]) + if hashlib.sha256(Path(__file__).read_bytes()).hexdigest() != plan["supervisor_sha256"]: + raise ValueError("Score supervisor changed after preparation") + python = root / ".venv312/bin/python" + values = dotenv_values(plan["env_file"]) + os.environ["HF_TOKEN"] = values.get("HF_API_KEY") or values["HF_TOKEN"] + process = subprocess.Popen([str(python), "-u", str(Path(__file__).resolve()), + "--dispatch", "--plan", str(plan_path)]) + while True: + _, training, changed = forward_scores(plan_path) + late_synced = False + if (training / "trackio-stop").exists(): + # The training collector has stopped. Replay late eval metrics once + # on CPU, using the exact logger shipped with the qualified runtime. + status = json.loads((training / "status.json").read_text()) + version = {p.name: hashlib.sha256(p.read_bytes()).hexdigest() + for p in (training / "checkpoint-scores").glob("step-*.json")} + receipt = plan_path.parent / "late-trackio-scores.json" + imported = json.loads(receipt.read_text()) if receipt.exists() else {} + if status.get("finished_at") and version and version != imported: + environment = {**os.environ, "REPRO_ROOT": str(root), "RUN_OWNER": training.name, + "BUNDLE_SHA256": plan["bundle_sha256"], "TRACKIO_DIR": str(training / "trackio"), + "JOB_FLAVOR": "hopper-prod-2h100"} + subprocess.run([str(python), str(root / "hf/runtime/logging_sync.py"), + "--out", str(training), "--arm", plan["arm"]], env=environment, check=True) + receipt.write_text(json.dumps(version, indent=2) + "\n") + late_synced = True + if changed or late_synced: + from huggingface_hub import HfApi + config = json.loads((root / "hf/configs/deployment.json").read_text()) + destination = ("hf://buckets/" + config["resources"]["artifacts_bucket"] + "/" + + config["run_id"] + "/jobs/" + training.name) + HfApi().sync_bucket(str(training), destination, quiet=True, + include=["checkpoint-scores/**", "trackio-events.jsonl", "trackio-backup/**", "trackio_verified.json"]) + code = process.poll() + if code is not None: + if code: + raise RuntimeError(f"Checkpoint controller exited with status {code}") + return + time.sleep(30) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--plan", type=Path, required=True) + parser.add_argument("--dispatch", action="store_true") + args = parser.parse_args() + (dispatch if args.dispatch else watch)(args.plan) diff --git a/04-data-agent/hf/locks/requirements-env.in b/04-data-agent/hf/locks/requirements-env.in new file mode 100644 index 0000000..0c5ee32 --- /dev/null +++ b/04-data-agent/hf/locks/requirements-env.in @@ -0,0 +1,23 @@ +harbor==0.22.0 +daytona==0.203.0 +fastmcp==3.4.5 +mcp==1.29.0 +gradio[oauth]==6.22.0 +pydantic==2.13.4 +fastapi==0.141.1 +uvicorn==0.52.1 +httpx==0.28.1 +websockets==15.0.1 +huggingface_hub==1.26.0 +python-dotenv==1.2.2 +typer==0.27.0 +rich==14.3.4 +pyyaml==6.0.3 +requests==2.34.2 +openai==2.52.0 +tomli==2.4.1 +tomli-w==1.2.0 +aiohttp==3.14.3 +numpy==2.5.1 + +e2b==2.36.0 diff --git a/04-data-agent/hf/locks/requirements-env.lock b/04-data-agent/hf/locks/requirements-env.lock new file mode 100644 index 0000000..4d11c34 --- /dev/null +++ b/04-data-agent/hf/locks/requirements-env.lock @@ -0,0 +1,3835 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile HuggingEnvs/04-data-agent/hf/locks/requirements-env.in --constraint HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock --python-version 3.12 --generate-hashes --output-file /tmp/standalone-env.lock +aiofile==3.12.3 \ + --hash=sha256:5c1bcc9e929c50834608e8cc1a4cc1d7503eb60c15a535b779fd39e2f372c017 \ + --hash=sha256:caa6aa746b5e47e2165f7abd741b6415e49cf4d44fddc0f61844612cc3924d41 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # py-key-value-aio +aiofiles==25.1.0 \ + --hash=sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2 \ + --hash=sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +aiohappyeyeballs==2.7.1 \ + --hash=sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d \ + --hash=sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # aiohttp +aiohttp==3.14.3 \ + --hash=sha256:03cd2bde3d7f085b64e549c985f4bb928cad7e8ecf5323bfca320db548d81b39 \ + --hash=sha256:041badb8f84396357c4d3ad26de6afd7a32b112f43d3c63045c0c8278cfd2043 \ + --hash=sha256:0a5ff2dfbb9ce645fa5b8ef3e02c6c0b9cc3f6030ff863d0c51fffc50cb5541b \ + --hash=sha256:0fdea2281997af69da84c77ffa6f5938a0285f21fb3887c249d67419ca865b3d \ + --hash=sha256:11fb37ef075669eee52ab1928fbf6e1741fada40409fa309ebde9607a962aebf \ + --hash=sha256:134ac5ddcf61c6fad984b9a5727d83492ada43d63471db20fb73042c13fca62f \ + --hash=sha256:152516815ef926786a0b6ae2b8f1fd2e0c71582dee0b435636865316fd4891b7 \ + --hash=sha256:1576145bdceeb92382d899751e12743a3a5b8e460a841e3e50543859e54864dc \ + --hash=sha256:16100ad3ab8d649fdfbee87602d9d2dcdca9df0b9eda8a1b5fdc0d41f96da559 \ + --hash=sha256:16ea7e24c309fb7c0bbd505d149abe4fe4dccfb8db911db7dbec0921bc889a6f \ + --hash=sha256:18c441d0a8fca6de8d1f546849b9f0ab20d435993e2c5b59562b2fae6be2f929 \ + --hash=sha256:18cb43369747b2ae007bd2655fb8e63a099c2ff1d207962943636dac989b3147 \ + --hash=sha256:1b59533861b70a2185c8f4f350f791f39d64358ef6944ce71c5240c9ec0982c9 \ + --hash=sha256:1c5281acc88b92396f88c7e1e2748f8466689df22b80170e4f51efa712fb47a8 \ + --hash=sha256:1c5ec8fb1bcc31a8466f74aaf26c345d5c386fa4bd08a3f0eb9c7a4a3fe8b5bf \ + --hash=sha256:1caa7b0d05f3e3a36f87788c59e970a7ee1cefcfcbb924a9f138c4a6551c9cb7 \ + --hash=sha256:21c016079415ed3fd676963e9793700a566d85dbbd6bfc564b9b2d209147dcc8 \ + --hash=sha256:2498f0fe69ead802f9675beca44a7c21c62fdaa4ec5145ea1c3ad6edbee29f85 \ + --hash=sha256:25bd2708db6bdf6a6630dd37bdcdfcb47c4434d22ac69c64665b802910140b30 \ + --hash=sha256:270d3dace9ca2f10f0da5d8ebe519b7a310fc6112ed916e32df5866df0888553 \ + --hash=sha256:2e1161602f45a54de2ce0905243a95f58cb42dcd378402f3697f5e0b21e9d2e7 \ + --hash=sha256:2e9878ae68e4a5f1c0abe4dd497dbc3d51946f5837b56759e2a02e78fa90ef86 \ + --hash=sha256:30402d03a7c0ff52bce290b57e564e9079fd9d0cb545c8aba73f86a103162d2e \ + --hash=sha256:33a2d7c28d33797a2e99923dffa63f83d908a19b6bf26cfe80fa790aa5e1a75a \ + --hash=sha256:362a3fd481769cac1a824514bcd86fda51c65e8fe6e051099e008fddde6db17c \ + --hash=sha256:38901a84da3ce22249f6e860bf8f90d141bcab7da090cc398f8bb58c0e44b7da \ + --hash=sha256:39aded8c7f3b935b54aab1d8d73c70ec0ee2d3ec3b943e0e86611bc150ba47f5 \ + --hash=sha256:3a26434dafe408229ff3403458ca58de24fb51936504decac49ce6755f77e59d \ + --hash=sha256:3ae5b3a59436d089b5395d910121a390feed4d00578eb95a0fd1a329fe963100 \ + --hash=sha256:3d4f72af88ac2474bb5bca640030320e3d38a0163a1d7533500e87be458eef71 \ + --hash=sha256:3f42e9b78301f11c8f861746175d8b9c1ccef713fcad9eab396e2f6db8ed4a22 \ + --hash=sha256:42a67efc36300d052fb4508a53e8b6901b9284b599ae63945c377569c5fcc1e1 \ + --hash=sha256:48d67b87db6279c044760787eb01f6413032c2e6f3ba1cafaa492b1c8e578479 \ + --hash=sha256:498c6c623134f8e09a3c4e60bcd607a0b4590dd7dbf08dd40851b27cbb520ccb \ + --hash=sha256:49f7325beb0f85ef4aef5f48f490269575f83e6e2acad00a1d80b807eb027062 \ + --hash=sha256:4e3ac92d90e92773b2362d506068e9a948192bd553e743c5b2429e28527c8661 \ + --hash=sha256:530125ee1163c4219af35dc3aa1206e541e7b31b6efc1a3f93b70a136f65d427 \ + --hash=sha256:5373dc80ad1aa2fb9ad95c83f24eef418bbda3a61375f128e5b0192e4f3f9b32 \ + --hash=sha256:53e5179d8abb5710f8e83ba207c41c8d1261fcffd4616500e15ca2b7a33be10a \ + --hash=sha256:53e7b4ce82b54a8bcc71b3b67a5cbd177ca1d7f592cbc92cd38b7349f73482db \ + --hash=sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42 \ + --hash=sha256:54cfcdee2770dac994417cbb0ee1f3eb0e7cb6b30c79bf44f2c02ff79ec5124a \ + --hash=sha256:55bdcc472aafe2de4a253045cc128007a64f1e0264fb675791e132ea5edaa3bd \ + --hash=sha256:56f355e79f71aef2a85c80305cc915f894b170dba76de5fe84f6351939b83c06 \ + --hash=sha256:5895ef58c4620afe02fa16044f023dc4dafec08158f9d08874a46a7dbc0341b8 \ + --hash=sha256:5bcb6ff3fdab1258a192679ff1a05d44f59626430aa05cd1a9d2447423599228 \ + --hash=sha256:5f08ec777f35ee70720233b8b9811d3bb5d728137f30ac91b7457709c3261ac0 \ + --hash=sha256:614c61d478b83953e261d02bb2df750f17227cd33ef8002945bf5aebbde21919 \ + --hash=sha256:617105e2c3018ee38d0c8ce5ee3c84f621a6d8b9f723202aacaff28449ca91ee \ + --hash=sha256:6debfa7312ff9d4c124dc71d72e9a0a4b9e0879e48ba6fcb42bef5c3300289e2 \ + --hash=sha256:7041d52c3a7fa20c9e8c182b534704abb19502c8bdcbde7ab23bfda6f642394f \ + --hash=sha256:70c987b27534f9ae1a723f47ae921571d616da21d3208282bf4c52af5164ac43 \ + --hash=sha256:74ab5b6a9fb13e873e5a90946588baecaf488745e1db1a4a5c433f971f035098 \ + --hash=sha256:78253b573e6ffab5028924fc98bc281aae05445969982a10864bc360dea2016c \ + --hash=sha256:7a75aa63cbf9b21cfaf60dc2657e19df2c2867d91707d653fee171ffeedd1371 \ + --hash=sha256:8800c996b01c2772a783e3e46f3e1abd5823029adca0df54231960de9bfefa5b \ + --hash=sha256:89176250f686cb9853c0fb7ead90e639e915b84a6f43eedc2a4e7ec21f1037f0 \ + --hash=sha256:8a5fd34f7f7410d1730d5c2ba873cacb2eed3fede366feb268a70ba22581ed8f \ + --hash=sha256:8b3b60de05f3dcb6f6a00f818bb2ec781cee4de0645f59ccaf99b1d1823b6100 \ + --hash=sha256:8f2f1c4c032c7cedd7d8da6f54c97b70266c6570c3108d3fdffee7188bb70529 \ + --hash=sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc \ + --hash=sha256:9aa6e61fdf20105c4144e755bd586008ff450791d67b1c8146fdc15959c4d51c \ + --hash=sha256:9d9edccfe496b476db5f398d97b865e9a6752bcf8aec4eef8390ce20fb64bb41 \ + --hash=sha256:9fc7b5bfec6573f3ae844f457fdde5adeb713f8b8e4a81ad64fc207b49383716 \ + --hash=sha256:a0dc483c00da8b673abbb367eb6f8d8f4bcec30eb58529ea13cb42e7fd2dfa33 \ + --hash=sha256:a3a8296e7ab5c295f53f1041487cb088e1480775aafbf7fe545d93b770a0f96f \ + --hash=sha256:a3e22975f905b89a55a488c2a08f2fdb2186175349e917d48985cc468a3d4c6e \ + --hash=sha256:a4af35c443e0b1a1bd6a8af3f3485d7fda15c142751a00f3ff8090f0b93346fa \ + --hash=sha256:a94dbaae5ae27bd849c93570669bff91e0510f33a80805738e3de72a7be0447b \ + --hash=sha256:ac74facc01463f138b0da5580329cfcc82818dea5656e83ddcd11268fc12ff80 \ + --hash=sha256:ad4c8b7488d745d2ca4838ebd8ae5ba9b56341d30b1da43640e4ce87f9f49646 \ + --hash=sha256:b014a6ed7cf912e787149fdc529166d3ceabac23f26efeea3158c9aba2354e7e \ + --hash=sha256:b20032766aedf6261c7a566585a40867d092ac03a0d81592d5370ef9b054f99b \ + --hash=sha256:b2466434105a4e03113c36ec775cc2ebe6676b62eae326fa670bb607ef788c1c \ + --hash=sha256:b304db572b4368edd8dda8a2274f73156fe15558fca4a917cb8a09fc47af5963 \ + --hash=sha256:ba59d59aba08ac02fc03b0c8983ccd5ee39a199d0552ce9e6d2b4845b34d59ae \ + --hash=sha256:bd52f811e65f6fb634b1047159657c98f52b407f8efec907bcfc09da9a4c0a25 \ + --hash=sha256:bdd0e2834dce1a26c1bbe26464861e16bbe217042cbff619247c11594472518c \ + --hash=sha256:c23ec8ee9d5ab2f5421f9c7fffce208435607af27fd46d4a44e031954352838f \ + --hash=sha256:c39846c3aad97a8530c89d7a3869a8f8e9e3762c6ac0504481e5c80948f7e807 \ + --hash=sha256:c3c200cf9757edd785051dc699c7ecbec22110dbfcb3fefc7a9f9695eda8ea7a \ + --hash=sha256:c7d3a97c678d34fc5b59da671ee9cd630096ddc643e7b5a30d54a2a6f3574d3f \ + --hash=sha256:c8653fd547c93a61aadc612007790f5555cdd18946fa48cf45e26d8ea4ea473d \ + --hash=sha256:cc7cb243a68167172f48c1fd43cee91ec4b1d40cefd190edd43369d1a6bc9c82 \ + --hash=sha256:ccd4893707b3e2a13e39c90d43cf80edf2e4d0457935bcc103bf2346214c3f15 \ + --hash=sha256:cd817772b2fcf2b8c0905795318485f9ec16eae60b29feb7f4c77085311637f0 \ + --hash=sha256:cda5fd5c95ad7a125a2e8464acc78b98b94c475a3780d6aa0aa157c93f470f4d \ + --hash=sha256:cef89a58e628c4efcac3275c2d68083f82426dcdc89c1492a6f654f9f7ea6ab9 \ + --hash=sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19 \ + --hash=sha256:d6088ec9894113802bddb3c09e974929aed2c7b3a8c456219b8aab4481f1a239 \ + --hash=sha256:d6218d92e450824e9b4881f44e8c09f1853b490f9a64130801024a4793b1b3b0 \ + --hash=sha256:d77640cc618c1d99fc4f8589c0f24a730adfa54eb1e57ef7bf0c8dfb78da898c \ + --hash=sha256:d7d2deec16eeedf55f2c7cf75b521ea3856a5177e123844f8fd0f114ce252cb5 \ + --hash=sha256:db332af25642007330fca8be5c4d194caf2bea7a7fc84415aff3497af5dfee6b \ + --hash=sha256:dd54d0e8717de95939766febac482ac0474d8ac3b048115f9f2b1d23a16e7db4 \ + --hash=sha256:ddcac3c6b382e81f1dd0499199d4136b877beb4cb5ef770bbbfba56c4b8f55d2 \ + --hash=sha256:df82f3787c940c94986b34222d59c9e38843fba85139f36e85255a82ad5355a9 \ + --hash=sha256:dfa68deb2a443bdaa3ea5297b0699c1464f08aef3812b486d1348eee61b07dc0 \ + --hash=sha256:dff9461ec275f22135650d5ba4b4931a11f3958df7dfbb8db630000d4dee0883 \ + --hash=sha256:e1e74298bab6ee0d6e749ed4fd1901c7e604bdda32c03d787a2cc71c46d0433d \ + --hash=sha256:e2667f0bbe7eb6c74eae5e9691441ad186e5845ca3cff63230fc09c4e7514f5d \ + --hash=sha256:e3be98a7c30b8c25d573dafba7171d66dfb05ee6a9070fc46535464ff97700a6 \ + --hash=sha256:e568e14940c09955aa51f4e645b6daa18a581c5dcfcd73744dcc86a856e3ced3 \ + --hash=sha256:e72ee89e28d907a18f46959b4eb0bb06701cc7f8cf4366e00029e2ccfaaf5924 \ + --hash=sha256:e92eb8acc45eb6a9f4935071a77edf5b85cc6f8dfad5cd99e97653c26593cdde \ + --hash=sha256:ea05e1f97ceea523942d9b2a7d7c0359d781d683d6b043f5943a602b14da4787 \ + --hash=sha256:eac645b09bcfdf73df7536331f0678c1086ea250981118ddb5199e17ccef72bb \ + --hash=sha256:eb0495d778817619273c108784292be161a924b9f5ae5cbbc70a2caa6838250b \ + --hash=sha256:ebe8e504f058fe91223351cecd2d9d6946c9d241bb0250d898ffbdf584cc72b0 \ + --hash=sha256:ed099d105449c4f9e84f24af203cd131349d4761d8813fa7e02c32e7128cd910 \ + --hash=sha256:f0f177d1b195b9e06376cfd7d308d8a1b920909a609d03ac82a8c73bbb16d3b9 \ + --hash=sha256:f3d2669fe7dec7fc359ecdb5984b29b50d85d5d00f8c1cb61de4f4a24ee42627 \ + --hash=sha256:f4e05329faa0ea1a404b37de4f034fd2c2defcca06a68dc6745e4e56c88e8a48 \ + --hash=sha256:f53bcd52f585e1ac3e590d61434eb61f9a88c38df041b4ea126d97144344a77b \ + --hash=sha256:f55119f7bf25f49ed210f6096090715da24f2943c62102448915fde3c62877ce \ + --hash=sha256:f631fe87a6f30df5fbe6d79640b25e4cffb38c31c7fb6f10871517b84b0f8c1a \ + --hash=sha256:f8fb78a83c9e5f741ca3a68cfb455c1f5bb83b4e7249a3848b3cd78d0a8563b0 \ + --hash=sha256:fa9467a8113aa69d3d7c55a70ef0b7c636010a40993f3df9d9d0d73b3eb7ef24 \ + --hash=sha256:fd51ebf9d3a00c074df4ede271023f4d2dba289bcc740b88191872716014e3c5 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # aiohttp-retry + # daytona + # daytona-analytics-api-client-async + # daytona-api-client-async + # daytona-toolbox-api-client-async + # litellm + # python-socketio +aiohttp-retry==2.9.1 \ + --hash=sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54 \ + --hash=sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona-analytics-api-client-async + # daytona-api-client-async + # daytona-toolbox-api-client-async +aiosignal==1.4.0 \ + --hash=sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e \ + --hash=sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # aiohttp +annotated-doc==0.0.5 \ + --hash=sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101 \ + --hash=sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastapi + # typer +annotated-types==0.8.0 \ + --hash=sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7 \ + --hash=sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # pydantic +anyio==4.15.1 \ + --hash=sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101 \ + --hash=sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio + # httpx + # httpx-ws + # mcp + # openai + # py-key-value-aio + # sse-starlette + # starlette + # watchfiles +attrs==26.1.0 \ + --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ + --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # aiohttp + # cyclopts + # e2b + # jsonschema + # jsonschema-path + # referencing + # scantree +authlib==1.8.0 \ + --hash=sha256:88aebbd9af6757e14e912d5dc007ae1dc1f3e27e3b2152ce7c552ee2c3b3c121 \ + --hash=sha256:f3ecd5f1da737262fb53bf1a4d95c4ea1ad9dd509316587a255c99ab1838a4f0 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim + # gradio +beartype==0.22.9 \ + --hash=sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f \ + --hash=sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # py-key-value-aio +bidict==0.24.1 \ + --hash=sha256:4dca6c17f0b01700e9f24359daa5ebabf7be022d99f4cb2a257b6af2a5076c88 \ + --hash=sha256:fd3eaa737917d8a14f4baa391670c433c4e3f6f5fd2cd99d4bf436437f432364 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # python-socketio +boto3==1.43.94 \ + --hash=sha256:2534bf331acd2f448b9cf8317f4eed453c65d9e0b7de254e77c99d390ac57aec \ + --hash=sha256:aa832d88e85035cebb83f04700c65623328b88fe828ff7ed9539483bd9d608c5 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # litellm +botocore==1.43.94 \ + --hash=sha256:1dfb86603a87fdaebda2540db56aef5b226ec58ab72c13ca56737e4d66dea9ab \ + --hash=sha256:cb97cedec5fceee3261f7fff75d0b0aa2cf68b32eeb2e8c2649e4657419773a6 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # boto3 + # s3transfer +bracex==3.0.1 \ + --hash=sha256:4e38e32392e4a4780fe15d644bfc7c8514057cfc3861e060b11814ce829c25e4 \ + --hash=sha256:6523ad83aeb5098a4ee597cff0f964442ff74e460bd3fafaffab6a013ff2288c + # via wcmatch +brotli==1.2.0 \ + --hash=sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24 \ + --hash=sha256:072e7624b1fc4d601036ab3f4f27942ef772887e876beff0301d261210bca97f \ + --hash=sha256:09ac247501d1909e9ee47d309be760c89c990defbb2e0240845c892ea5ff0de4 \ + --hash=sha256:0bbd5b5ccd157ae7913750476d48099aaf507a79841c0d04a9db4415b14842de \ + --hash=sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c \ + --hash=sha256:14ef29fc5f310d34fc7696426071067462c9292ed98b5ff5a27ac70a200e5470 \ + --hash=sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744 \ + --hash=sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a \ + --hash=sha256:1b557b29782a643420e08d75aea889462a4a8796e9a6cf5621ab05a3f7da8ef2 \ + --hash=sha256:1b71754d5b6eda54d16fbbed7fce2d8bc6c052a1b91a35c320247946ee103502 \ + --hash=sha256:1ce223652fd4ed3eb2b7f78fbea31c52314baecfac68db44037bb4167062a937 \ + --hash=sha256:1e68cdf321ad05797ee41d1d09169e09d40fdf51a725bb148bff892ce04583d7 \ + --hash=sha256:260d3692396e1895c5034f204f0db022c056f9e2ac841593a4cf9426e2a3faca \ + --hash=sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6 \ + --hash=sha256:2881416badd2a88a7a14d981c103a52a23a276a553a8aacc1346c2ff47c8dc17 \ + --hash=sha256:29b7e6716ee4ea0c59e3b241f682204105f7da084d6254ec61886508efeb43bc \ + --hash=sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b \ + --hash=sha256:2d39b54b968f4b49b5e845758e202b1035f948b0561ff5e6385e855c96625971 \ + --hash=sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe \ + --hash=sha256:3173e1e57cebb6d1de186e46b5680afbd82fd4301d7b2465beebe83ed317066d \ + --hash=sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac \ + --hash=sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd \ + --hash=sha256:35d382625778834a7f3061b15423919aa03e4f5da34ac8e02c074e4b75ab4f84 \ + --hash=sha256:3b90b767916ac44e93a8e28ce6adf8d551e43affb512f2377c732d486ac6514e \ + --hash=sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18 \ + --hash=sha256:3ebe801e0f4e56d17cd386ca6600573e3706ce1845376307f5d2cbd32149b69a \ + --hash=sha256:3f3c908bcc404c90c77d5a073e55271a0a498f4e0756e48127c35d91cf155947 \ + --hash=sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a \ + --hash=sha256:465a0d012b3d3e4f1d6146ea019b5c11e3e87f03d1676da1cc3833462e672fb0 \ + --hash=sha256:4735a10f738cb5516905a121f32b24ce196ab82cfc1e4ba2e3ad1b371085fd46 \ + --hash=sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48 \ + --hash=sha256:50b1b799f45da91292ffaa21a473ab3a3054fa78560e8ff67082a185274431c8 \ + --hash=sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5 \ + --hash=sha256:5732eff8973dd995549a18ecbd8acd692ac611c5c0bb3f59fa3541ae27b33be3 \ + --hash=sha256:598e88c736f63a0efec8363f9eb34e5b5536b7b6b1821e401afcb501d881f59a \ + --hash=sha256:640fe199048f24c474ec6f3eae67c48d286de12911110437a36a87d7c89573a6 \ + --hash=sha256:66c02c187ad250513c2f4fce973ef402d22f80e0adce734ee4e4efd657b6cb64 \ + --hash=sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c \ + --hash=sha256:6be67c19e0b0c56365c6a76e393b932fb0e78b3b56b711d180dd7013cb1fd984 \ + --hash=sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21 \ + --hash=sha256:71a66c1c9be66595d628467401d5976158c97888c2c9379c034e1e2312c5b4f5 \ + --hash=sha256:7274942e69b17f9cef76691bcf38f2b2d4c8a5f5dba6ec10958363dcb3308a0a \ + --hash=sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b \ + --hash=sha256:7a47ce5c2288702e09dc22a44d0ee6152f2c7eda97b3c8482d826a1f3cfc7da7 \ + --hash=sha256:7a61c06b334bd99bc5ae84f1eeb36bfe01400264b3c352f968c6e30a10f9d08b \ + --hash=sha256:7ad8cec81f34edf44a1c6a7edf28e7b7806dfb8886e371d95dcf789ccd4e4982 \ + --hash=sha256:7e9053f5fb4e0dfab89243079b3e217f2aea4085e4d58c5c06115fc34823707f \ + --hash=sha256:7fa18d65a213abcfbb2f6cafbb4c58863a8bd6f2103d65203c520ac117d1944b \ + --hash=sha256:81da1b229b1889f25adadc929aeb9dbc4e922bd18561b65b08dd9343cfccca84 \ + --hash=sha256:82676c2781ecf0ab23833796062786db04648b7aae8be139f6b8065e5e7b1518 \ + --hash=sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d \ + --hash=sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae \ + --hash=sha256:865cedc7c7c303df5fad14a57bc5db1d4f4f9b2b4d0a7523ddd206f00c121a16 \ + --hash=sha256:88ef7d55b7bcf3331572634c3fd0ed327d237ceb9be6066810d39020a3ebac7a \ + --hash=sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f \ + --hash=sha256:8d4f47f284bdd28629481c97b5f29ad67544fa258d9091a6ed1fda47c7347cd1 \ + --hash=sha256:92edab1e2fd6cd5ca605f57d4545b6599ced5dea0fd90b2bcdf8b247a12bd190 \ + --hash=sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7 \ + --hash=sha256:95db242754c21a88a79e01504912e537808504465974ebb92931cfca2510469e \ + --hash=sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e \ + --hash=sha256:96fbe82a58cdb2f872fa5d87dedc8477a12993626c446de794ea025bbda625ea \ + --hash=sha256:99cfa69813d79492f0e5d52a20fd18395bc82e671d5d40bd5a91d13e75e468e8 \ + --hash=sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3 \ + --hash=sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab \ + --hash=sha256:9fe11467c42c133f38d42289d0861b6b4f9da31e8087ca2c0d7ebb4543625526 \ + --hash=sha256:a1778532b978d2536e79c05dac2d8cd857f6c55cd0c95ace5b03740824e0e2f1 \ + --hash=sha256:a387225a67f619bf16bd504c37655930f910eb03675730fc2ad69d3d8b5e7e92 \ + --hash=sha256:a56ef534b66a749759ebd091c19c03ef81eb8cd96f0d1d16b59127eaf1b97a12 \ + --hash=sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03 \ + --hash=sha256:ac27a70bda257ae3f380ec8310b0a06680236bea547756c277b5dfe55a2452a8 \ + --hash=sha256:acec55bb7c90f1dfc476126f9711a8e81c9af7fb617409a9ee2953115343f08d \ + --hash=sha256:adedc4a67e15327dfdd04884873c6d5a01d3e3b6f61406f99b1ed4865a2f6d28 \ + --hash=sha256:af43b8711a8264bb4e7d6d9a6d004c3a2019c04c01127a868709ec29962b6036 \ + --hash=sha256:b232029d100d393ae3c603c8ffd7e3fe6f798c5e28ddca5feabb8e8fdb732997 \ + --hash=sha256:b35c13ce241abdd44cb8ca70683f20c0c079728a36a996297adb5334adfc1c44 \ + --hash=sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8 \ + --hash=sha256:b908d1a7b28bc72dfb743be0d4d3f8931f8309f810af66c906ae6cd4127c93cb \ + --hash=sha256:ba76177fd318ab7b3b9bf6522be5e84c2ae798754b6cc028665490f6e66b5533 \ + --hash=sha256:bba6e7e6cfe1e6cb6eb0b7c2736a6059461de1fa2c0ad26cf845de6c078d16c8 \ + --hash=sha256:c0d6770111d1879881432f81c369de5cde6e9467be7c682a983747ec800544e2 \ + --hash=sha256:c16ab1ef7bb55651f5836e8e62db1f711d55b82ea08c3b8083ff037157171a69 \ + --hash=sha256:c1702888c9f3383cc2f09eb3e88b8babf5965a54afb79649458ec7c3c7a63e96 \ + --hash=sha256:c25332657dee6052ca470626f18349fc1fe8855a56218e19bd7a8c6ad4952c49 \ + --hash=sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f \ + --hash=sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63 \ + --hash=sha256:d206a36b4140fbb5373bf1eb73fb9de589bb06afd0d22376de23c5e91d0ab35f \ + --hash=sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888 \ + --hash=sha256:d8c05b1dfb61af28ef37624385b0029df902ca896a639881f594060b30ffc9a7 \ + --hash=sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a \ + --hash=sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3 \ + --hash=sha256:e80a28f2b150774844c8b454dd288be90d76ba6109670fe33d7ff54d96eb5cb8 \ + --hash=sha256:e813da3d2d865e9793ef681d3a6b66fa4b7c19244a45b817d0cceda67e615990 \ + --hash=sha256:e85190da223337a6b7431d92c799fca3e2982abd44e7b8dec69938dcc81c8e9e \ + --hash=sha256:e99befa0b48f3cd293dafeacdd0d191804d105d279e0b387a32054c1180f3161 \ + --hash=sha256:eda5a6d042c698e28bda2507a89b16555b9aa954ef1d750e1c20473481aff675 \ + --hash=sha256:ef87b8ab2704da227e83a246356a2b179ef826f550f794b2c52cddb4efbd0196 \ + --hash=sha256:f16dace5e4d3596eaeb8af334b4d2c820d34b8278da633ce4a00020b2eac981c \ + --hash=sha256:f8d635cafbbb0c61327f942df2e3f474dde1cff16c3cd0580564774eaba1ee13 \ + --hash=sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361 \ + --hash=sha256:ff09cd8c5eec3b9d02d2408db41be150d8891c5566addce57513bf546e3d6c6d + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +cachetools==7.1.8 \ + --hash=sha256:1221d547a0b24b7f26fa891d40d488b5258beab9aebd8ed68c729be3af849c43 \ + --hash=sha256:a81e3844acaa7355b6567f97bd67a94a14ec3a9bc2cbbdae45b9592cc036775b + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # py-key-value-aio +caio==0.12.4 \ + --hash=sha256:0d8d2826bf622644fc374a9f53ac00918b489d03599783953fcbe79af19123f5 \ + --hash=sha256:134d9d145d75f454de5ece9e87595bad433639b51061b693bafc369f689f8742 \ + --hash=sha256:17a41de51203fc4d787d8d577bd56f169167aa9822f14fada015504688791aa4 \ + --hash=sha256:1c1a387f8784a86a56a8912ca42e1ad67599f16406f497b5b655b461a1b89426 \ + --hash=sha256:1e1d5570fd0ec75b1b2c2877c5545ed9f0738b5d23b000e2b6a8fc830dc8b310 \ + --hash=sha256:20acd9a8df90d25b63bc0b7dc264e2a09b99082a420b5d640c76056027ee6b52 \ + --hash=sha256:20f1d3fe05be285d82413b00bd8061eeb62d3027d3f565adaf7db10bebc594dc \ + --hash=sha256:27ec5671ac05650abc7ac1fb12e95ad09417ae495ce9be565927786688edd6c5 \ + --hash=sha256:32d8e9f3e2099c8db29446679252766c9bcd806eb88b4fb60ad274f73df2a5e9 \ + --hash=sha256:413565d77dfdf2dd841ac100571ef1cc710f9c56137312f3ec51356350b4f3a5 \ + --hash=sha256:4189104e5579553031340a677762398c130b81ddb7d7c9b69eebffddaecc4f58 \ + --hash=sha256:456a93868ff007cea65d916d96d192cda8b9992c6eee046cb0159040f9446ab5 \ + --hash=sha256:4854a029e359e5ddfb5589ab66157a34af6a0dda0acdaa43f5e640d60182c1ea \ + --hash=sha256:4f2e0b6d393dc0ea78cfd13a9a4321fc35fd6a2db802e5f64651317e859fc929 \ + --hash=sha256:51b8600eadc8756751dfc152514852040c6c28deb6bdcd6bfbdddb2a38e424e1 \ + --hash=sha256:53605636e3b1eaeca368b475f1471ad9774736c6502a4b58b8b6b8d74b531770 \ + --hash=sha256:67b725641fea682a2e9b1d3b2a150d21f1a25383e3ec8351bd7677ff857fa982 \ + --hash=sha256:67f1bddc997bef281db36600e3e58d6b16d6486b3540eb62c9e54c6584f137fd \ + --hash=sha256:710ffe4a6f3d69dbfe98e9f856c24a544e4d639c7bd210f9f5503a26e62502f4 \ + --hash=sha256:759b952df48a5bbdb16e90631c748c877fb0ec7ca9d64eef1066569925ccdd30 \ + --hash=sha256:780465251a0680039b29c990bca73b40b843e39d06505c62e353ed56490e4576 \ + --hash=sha256:7a5e231bbf81eaed269f99afa9ef46457f51f9407952a1795c0795b3a62c23c0 \ + --hash=sha256:8a926d562f8c06767774a91239770ebaa93c10a24074e21ea741db208df9cc1d \ + --hash=sha256:8e9eda6a88f309a0a53bcba844471638da01a92758015e2df099fa361891dc72 \ + --hash=sha256:97bff8f7da696321552b42629d1869983a8ced2f010bac9731b2719c1195a20c \ + --hash=sha256:9dc0fd6f09ef72d18d3f43ca3d1140295222d925c583114ab9b1e8843a109a6e \ + --hash=sha256:b0444d20067642bb272e423de3cc8ef05bf134d335d57fa5bddcfcc98e04313e \ + --hash=sha256:cd1d00ed1867a3b7a4cffb64b1bd8644de4b6f88cb25240cf82a9cc160ace975 \ + --hash=sha256:ceee64265a55b9aba9c38c1d01a159b99f1e36eb56938022018199d2e7fe1742 \ + --hash=sha256:ddf8d946b795ebd7c75b331b6dcbab4808fdd2c9f16ca76012b4757512861133 \ + --hash=sha256:e22ce2e69d94e4c80e0c11c871e547b3c2d147f977632894cb2527ddb6e87a8d \ + --hash=sha256:ee9de68d6ede7ed69e55a1c09073ce146d9f9470088796d55238c485a93fe203 \ + --hash=sha256:f063624a98a64bab387430c5eaea61ef3825a98399104a297b8da4741c15139f + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # aiofile +certifi==2026.7.22 \ + --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 \ + --hash=sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # httpcore + # httpx + # requests +cffi==2.1.1 \ + --hash=sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e \ + --hash=sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66 \ + --hash=sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2 \ + --hash=sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0 \ + --hash=sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6 \ + --hash=sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971 \ + --hash=sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c \ + --hash=sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d \ + --hash=sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9 \ + --hash=sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517 \ + --hash=sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735 \ + --hash=sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80 \ + --hash=sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f \ + --hash=sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1 \ + --hash=sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29 \ + --hash=sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8 \ + --hash=sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c \ + --hash=sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e \ + --hash=sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48 \ + --hash=sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813 \ + --hash=sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac \ + --hash=sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632 \ + --hash=sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6 \ + --hash=sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1 \ + --hash=sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659 \ + --hash=sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688 \ + --hash=sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004 \ + --hash=sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0 \ + --hash=sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062 \ + --hash=sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779 \ + --hash=sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94 \ + --hash=sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50 \ + --hash=sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab \ + --hash=sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac \ + --hash=sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6 \ + --hash=sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676 \ + --hash=sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1 \ + --hash=sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9 \ + --hash=sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf \ + --hash=sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13 \ + --hash=sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e \ + --hash=sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e \ + --hash=sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973 \ + --hash=sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527 \ + --hash=sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72 \ + --hash=sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890 \ + --hash=sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c \ + --hash=sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990 \ + --hash=sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd \ + --hash=sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9 \ + --hash=sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94 \ + --hash=sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3 \ + --hash=sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80 \ + --hash=sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41 \ + --hash=sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5 \ + --hash=sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c \ + --hash=sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a \ + --hash=sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4 \ + --hash=sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e \ + --hash=sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6 \ + --hash=sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98 \ + --hash=sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b \ + --hash=sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1 \ + --hash=sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03 \ + --hash=sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af \ + --hash=sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231 \ + --hash=sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2 \ + --hash=sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3 \ + --hash=sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836 \ + --hash=sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5 \ + --hash=sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399 \ + --hash=sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96 \ + --hash=sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e \ + --hash=sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be \ + --hash=sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf \ + --hash=sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc \ + --hash=sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455 \ + --hash=sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0 \ + --hash=sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12 \ + --hash=sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b \ + --hash=sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7 \ + --hash=sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692 \ + --hash=sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54 \ + --hash=sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3 \ + --hash=sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b \ + --hash=sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be \ + --hash=sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d \ + --hash=sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358 \ + --hash=sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a \ + --hash=sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7 \ + --hash=sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc \ + --hash=sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960 \ + --hash=sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125 \ + --hash=sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb \ + --hash=sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a \ + --hash=sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa \ + --hash=sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf \ + --hash=sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3 \ + --hash=sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4 \ + --hash=sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # cryptography +charset-normalizer==3.5.1 \ + --hash=sha256:00668ebb0609751758682eb0b5857e7c35b9f00e84dfdef062e103244ec94d45 \ + --hash=sha256:012a22b88a77ca2e59b98ac5889b0deb604147666032f45e6d6e217634d2550d \ + --hash=sha256:01e93745f7f219b703b60ba7afead36cfc4242782be5af484673fc500df12da5 \ + --hash=sha256:04368edf83514385ffc3e1cfd4546e595f4f1272dd23ba437a93a9cc3741d47b \ + --hash=sha256:0722590aabf9dc6a6c0343d523c05458fa2b5047dbe6302fd526bb570600753f \ + --hash=sha256:07ffd07412fc5d5e84cd8952acf9ff7e4ed7a708e69d1bada19d8ba91711353f \ + --hash=sha256:09a7bba9f739468c8e78c36a75c33768e53cb1959fc638f510454c14683f00d5 \ + --hash=sha256:0b2b1b3fa5670c127b246df1d0c059defd41f689a868a3b9d79df9b1cac42d22 \ + --hash=sha256:0c6dfb5ca6723eeed15aa8e564a014d69fcb8812f94eef11fe3631e0508199f5 \ + --hash=sha256:0d929fc574b4d6fd9e7c0f5c2ede8716a41911923aa7fa5fce38e0818aa4a1ac \ + --hash=sha256:13e3afe97712e8887cd516e960c63f0b93122971e5b5e4b2622fe7701771e838 \ + --hash=sha256:15f024313246a4ed976c60f440bb8d257815513a681d212ff74fd46f7d715a90 \ + --hash=sha256:195ce897c6153c0700078142cf8efe3e6454ca4cf4357499e4078dfd83396626 \ + --hash=sha256:19a3dd5aa73cef1c99687c4fc57db016a9c17104ae1185da88ba566a5d3bebe4 \ + --hash=sha256:1d1c7a53a6c2103925cdd6d7229f8c567379f211c869793df679f2e9f738c369 \ + --hash=sha256:1f5883d77fd409a261abb5dc8ccbe335720d798b1de4abb3b1d47ccbbc76b53b \ + --hash=sha256:21b82d8082f6f5e7f456ef0bd16323d08de1266efbfeb476e64b2a91d1471a4e \ + --hash=sha256:252d099029bcbea642f2a06c4ed5046bdf8b5a8150b64afa5e027e88b106e5ee \ + --hash=sha256:256dd4d85d9e4dc595e2bc983c980e73f62ddeb3165c58b4c3dfe78c5c8548c1 \ + --hash=sha256:26422d45fd13551cf564c58932f7d72b4f58b93b0fcf18c35ba6be12b46bb102 \ + --hash=sha256:2679de311c7946dde5d3b6f44941844133ff5c7cb86099c0061ab1e8901c20a8 \ + --hash=sha256:29880d17a8eb0b5cfdfd8944b468322928059aa35f1f5fa8ff22b149ec0b42f8 \ + --hash=sha256:2bced4061f000f7187254a02ad3433ae17eaf991747ceea2f478422590a5bba9 \ + --hash=sha256:2e9cf9253119d8e5d111f05d71626786fd3d6193817316eab1ca088cdb8593cf \ + --hash=sha256:2f06b7eae9dbe77fe1d644ca244dad508de8d302870a43f3c559b521270938a0 \ + --hash=sha256:2f293479cce755c75f1697e87c409b7ae4c555c7dfecb6e988ad13abba943031 \ + --hash=sha256:329fc3ccb63ad22d867d84c2adea759a64079a37ba4a343433b02c7a2816871e \ + --hash=sha256:343fb4f2821043bd87095f7b08a1a181febc8e36ac64212143bbfd0a0e1bc235 \ + --hash=sha256:3588e376b3ea2eea84976f67273d679f229e24c66dce7b82ae45aef04ff6e072 \ + --hash=sha256:35aea775dc2bd5f54cd84a1cd2696cc3207c479cb9cf0bd346f0d343e4300ddb \ + --hash=sha256:35fe081843b35aad20ffeccec3eeffbe637b15d14f3fb22cc1b59cd8ec17e93c \ + --hash=sha256:36047af20e17097c3bb9476c2b7655f2f7aa51322c0ba58c07695bedf755a950 \ + --hash=sha256:3617ac3cfd8b9888f145ad89dd6e692285834b0201c6074a5eeaad3fd4d668c2 \ + --hash=sha256:366ec70f5547c640d3ce1985722490f23faf4eb5216a7eeba78277490e78dacb \ + --hash=sha256:394fea06235c8543390050ed5f529187074b029fb027213f6c46ac11ab5d950e \ + --hash=sha256:3d27167433c0d5f18dc850f07d0b3816221984fecdc405d6c157a6f0b8f8e9e6 \ + --hash=sha256:3e5e1224c0a6a90e05843e07adfec669edebec17801c67072f51e59561d63c0b \ + --hash=sha256:41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2 \ + --hash=sha256:433c5a81eade63b47e522303bad236f59dba55ea6951746f5558355eeed8c75d \ + --hash=sha256:4582c27e8c889d64811987b5967fbd3ae0c823fe1fd933b543d55ac20bb475fa \ + --hash=sha256:485a0d363cafefcd2538a73c7c838daa2035f09b2c9f9b5e3133f80c6aeb84c2 \ + --hash=sha256:494b70049a4d69aec6e8137c13af4cf8db8c9f9820a1392ac293b0dd2987a818 \ + --hash=sha256:496846868fea80e479324862fa877f02411f2fd0f83b79ccee2607aa68b2a032 \ + --hash=sha256:4abdc5f9ad448c1ecbfae2974b820535d6bc6e7eef63babbab3d81cf46968c71 \ + --hash=sha256:4b599739b93b2cbeded49645ae3c8d1405c29ddfbceac1545c87a3f9580a9e96 \ + --hash=sha256:4bea7f8ebe90bbd7f0e4a2de42ca6924ba23e3e76418c408ff82f1d46fabd687 \ + --hash=sha256:4c4fb141a727957c93edfe5c32a26ceb6b5f6461d67146e2d39f51e16170bea8 \ + --hash=sha256:4c9548dc78002099910abaebc0a72ac58b7d30931869e0351c09b507dff4ece3 \ + --hash=sha256:4d26f14f041e83dd8edfd61f4cd4fa7285d31798b5bf1f28e70c367ba6c41d61 \ + --hash=sha256:4f298bdadb8f0b9e5672877f647d1be9373ef5320c9e2f049795e26cad28b6a9 \ + --hash=sha256:52ec005752a56ae79547a05c0139ca2501a0c866390b6115008456b9f0e7cde1 \ + --hash=sha256:55261ac0d2941c42f196dd576f543d87a8ee03cd6f5e30dfb4d807b2e3b9121a \ + --hash=sha256:56490c595a28b1bb27dfc583e816152a9767721ef58b2c03b13f954d2f707420 \ + --hash=sha256:58d3e12c88e0950bca850ae1f7c256055c097639c2edb9eb123af9807d8b15e4 \ + --hash=sha256:58d4aa13a59c969dbfdf9e6a9560e242cbfd9e8a8f50c2747714df1a423adf65 \ + --hash=sha256:59171c6e45bf07d0d5cab3b0bf81d945035530f6873398b3b531c31184d46663 \ + --hash=sha256:5b6d1386bf0096d26d3a863dc0a487a5b4eb9aa93cf5ba69683d29dde6b9d60f \ + --hash=sha256:5c0ea61a470e070686aa30892fed79e297d2c8d0ab46b8bcdf027d38c51da591 \ + --hash=sha256:5c84bec0ab5ae0c64bfe73a7d2adcb5ce73b467523fc27fd6a28ab2aa6cbe35a \ + --hash=sha256:5ca0555312ae2fe82715cada7fac375530c2f3349e1eaa1bcb33d0283ac79a18 \ + --hash=sha256:5d8531a6569d025f68e2321e7638fb7978f23db58e5f69f56913837aae03816e \ + --hash=sha256:5e2d0e146dcb57034f8b97dc58d2d512cb90aba253960ce449f695fec6a82c6f \ + --hash=sha256:5fc45d653ea8c9a20479167e11d4a0f8cb2fa3470737ab6f9c827532313187b7 \ + --hash=sha256:6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3 \ + --hash=sha256:6199d5606e2bbf2b096cf64d03f8b6790c91081d5ac866b8e7bb6422738cc60c \ + --hash=sha256:62b55f6722735a6c472f88361cde6640608773d9443cebdbb51abf436a1fcdd3 \ + --hash=sha256:687c9ca3035544b113bea2055e180af96fb63c0c476e22a9180f51925186e7b7 \ + --hash=sha256:6b7430cf5728e68f6c462254009a6ef4086e1bea43cf2f57aa9c55fb4f50ff96 \ + --hash=sha256:6ba32c4d2abf1d2fe7cf27d280f4cca5664233b0f885549c7761719eb977f486 \ + --hash=sha256:6c9cdde8becb25a7fde49924511aa2644d6f8081cc8df8e9452724303348d8e3 \ + --hash=sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6 \ + --hash=sha256:6e2912d4babbc65196ac13c2f53468dc57fb8b9c25ef913e8c59ddf7c6dc0e1b \ + --hash=sha256:6e5e4d73d588ca5ed09df1b7dcd1b203d1df3c542e3f50d126c947d432b10731 \ + --hash=sha256:70055ff39b97c99e7ae40ea3e393fb62aa2e44dbd9b29f8d14f42fb0025c3959 \ + --hash=sha256:706bfd38730a5ac7a365793269a00f4e988178cec121391f4248d84ad8c972e9 \ + --hash=sha256:7235dc28fc6dd9d832ac7c7bce95367dedb85929f17368a0c2bee1e080b9acbf \ + --hash=sha256:774d157f112367ff4abd29019f38f023c24e00e56edc7829c20e358a5a913ad8 \ + --hash=sha256:77efcff2b23071c349402ac1066667a3d011f62398d81408c9b88ad991747c9e \ + --hash=sha256:789b8982559ae28dad2356519f841655756cdcd96616410590ae0b17454ee64f \ + --hash=sha256:7ac76cf9afd34929d76eb7fcb63be476a4853d8a96f0dcf2d0db68a0cbdf9885 \ + --hash=sha256:7c0c10730342b0c9b35dd1d619beb8214e520bd96a1f870f452680b238aab3e0 \ + --hash=sha256:823f82903d189af463d7df250ef1f7f696f3cee08cc8d91deb565e8d425f6506 \ + --hash=sha256:838648accb3a7fd9803fd45c87bce8509648eb0c11bc34e216141300977244f2 \ + --hash=sha256:854066be00447fa8de2ccbbe893e2ffc4b123ef16d897af794c1e18bd4a714b0 \ + --hash=sha256:85d5855daafc240cc045c026d7a15fd198a09b0fc8ff6f5ecbb5297b509cb11e \ + --hash=sha256:85de3134b5379856e323ba37c19c9256d39425f7b76a63af52b09fb4664c2e8f \ + --hash=sha256:87e4f41d375c0b9be2fb5251aee4b8a689169e134535aed81bf085c3b647451e \ + --hash=sha256:88ca277405c2d3b71c4e1c2ee0e7966e807bcba86a69d11e19ba199d18ae4491 \ + --hash=sha256:88e85ab89cb822c1e635f51d6d32e488f94e002e70e2f492bdb8b945543f345a \ + --hash=sha256:8ac8c94b6539074e0f40899301273ac8402b9b3e01c7b7ba269ff30340aaaf20 \ + --hash=sha256:8fe532b3c966d1fb794e0698e4589d0444017ae77fc0b31edea13c0e35bcc449 \ + --hash=sha256:9085f87b0e38a2b92b8923059b4e8789fe40d9279712d15dcc670048d77079af \ + --hash=sha256:90b7481fb62fbe172c558bc6fd1c4c98d82004a54a7551f20e11ac9bf0b8708c \ + --hash=sha256:92caef967d287a407085d61176fce4012b1dd62daed4eb6d5ceb26d3d2538712 \ + --hash=sha256:9362dd90aa7dab48c0054a21187791ccf05473f7dba5d92b8033ae62164675e7 \ + --hash=sha256:94d78ecec2605a8d0398b0f365d5f12a63248438516f5dac536a5eff7337df4a \ + --hash=sha256:94fbf1c0c6cc0d3d5e50f9a9313a8cdca90dd696d34b381cd1704f8c9e939f20 \ + --hash=sha256:950f23cb393f85543777b0433f082cddd25b51ab398eac7971146495679efe5f \ + --hash=sha256:96eefc178f8636b9c760c5829345307fd81cfae9ab1e80997dbddeb0f54ee9a3 \ + --hash=sha256:96fef3e886d6a9874b14f27fc193fbdc69d5d8035783d86aa4e1cea594e695f9 \ + --hash=sha256:977cdbd483a9cff38179bea4fd754289a6f2195c7abd414aba85410b3e66cc5e \ + --hash=sha256:978eab16f55b4ab2c2a745be9a0a840bf8f09a7f227d9c76eb30214d078865a5 \ + --hash=sha256:994e883d17c559cdfd38c84003c8b27d25424a1077272a17e7cd27bfe0bf57b2 \ + --hash=sha256:9ac4444d8d4fd4c4bd08bf451ed3167aa9e7ec6cdb41b648794f1d1103652e36 \ + --hash=sha256:9b5db6052055d34d41230fb78d7c439c23dc536a9896f6cb039e8dd92cfc1263 \ + --hash=sha256:9d9a0dc7cbe9bec24c3f767c9122c41fe5a1bc43f47cd099d00d393e09769de4 \ + --hash=sha256:9dbdd9205662134957cf0c324f639bdc5031c0ca056e2369e238db75187c0f11 \ + --hash=sha256:9eea3ab2597a5e65fe65296e2d6a84570845a6b55532d90333d740d48bbc850a \ + --hash=sha256:a2028475ba855475b8b4d3cfeb4994269c967aea8b9892dfba907f4263a863a3 \ + --hash=sha256:a3a370082ce34d0612f421e15fe011c53bb1feff21a26d06ad4fb244dab5a375 \ + --hash=sha256:a545775cfe815855ea32d7c27731d79da358ef2055b4a25830231b1622dd18aa \ + --hash=sha256:a5cbd90ecf0fc62e64726917ad083b73001f0563657a87ec3c0b504e277dc90d \ + --hash=sha256:a6d095662e73e74f0a49988e0593373e243e3a52e27bfeea0a859e88acf4a0f5 \ + --hash=sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99 \ + --hash=sha256:a951ad59cad9145664a730d3036b40b844e74d2d3683da40111463cd3a83845d \ + --hash=sha256:aa1099b956fb795e686d073568f6dc002a0bb89765ea6d5b055dd7d9bf1b116c \ + --hash=sha256:aa2bb0b37202dca27175591f761108b5d34096ade1191ffe4808bdf6b1571488 \ + --hash=sha256:aae2ee51122d3ae968a3837d97dc24a0aeebb0dea23694422cd172bd30017cd6 \ + --hash=sha256:ab743e9bc90c1f73552ec33e10e3331315acd2c397b36065b591b0181de533cc \ + --hash=sha256:ac00177c4831ffa650f8609e4bdddd5fe09c03b1c0c47acece7e6ea20421598b \ + --hash=sha256:ac13b004224fb341e1e25a1ed5e19d32f57cdb2a403e01f003b46f051a550f6f \ + --hash=sha256:acaf604462bf330b0d07e7a07c1d6e4adac79e5fb13e9c5140590542cafacc00 \ + --hash=sha256:ae31a1a1db2ee6cc2942fccaf695c934bc7f3db9f2133a3fef1f367cf1a4ab10 \ + --hash=sha256:ae4a097991662cd4fff0ddc74e0fe7874f82e00042fa0ea00855645ed0c79598 \ + --hash=sha256:aea996a6aba25260827c9ea511d1addfde2da9eb686ac961838509086188b7e6 \ + --hash=sha256:b39b69b347e5e47a3b5b8cfc005c68c1ba347474e3960236c4944a8ecd174962 \ + --hash=sha256:b54e7e13267d49ffbfe68e25b3cbd774dab38fa37238f71265e91b36146eb21c \ + --hash=sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08 \ + --hash=sha256:ba2f37ee79e6338845261a3c5b1784e5d1acdff2c0785b284f1b633033d136ab \ + --hash=sha256:ba501e667c17d8411f98e67a022d9604ef179aff0e459b7e292c796837c13573 \ + --hash=sha256:baf3775a2635e5a11fbd5e4e64ee69c7e86875d224a5c72aca4c141064589a90 \ + --hash=sha256:bb57753e36e4855b8ca375069482250a6246372331a3e4f3407eaebb007443f5 \ + --hash=sha256:bd6c173f04743d483881bffa1478d5a4624475b8cd1d2194956a75548e191c18 \ + --hash=sha256:be47f99644b208bff7766314013f9acf57b056b04191d570d68ad14022cf5b1d \ + --hash=sha256:c010f5581d9c612804cc59fcf7b524b707fbcb72828551237ab545bb5c7034af \ + --hash=sha256:c1dcc36dcb96abc02236e182d17e0f71430152a6c2c7447421da2d2dc144edea \ + --hash=sha256:c428c6c31eb5f4277d7f8eccaf767fbd548ddd5ce3c8b4f4cbbfab3d96b5904c \ + --hash=sha256:c658c50ac0c98cd755a2dd50b7977d3bca7df401dcc47fbdfa87db53ef7d4e8b \ + --hash=sha256:c71fb0d56c920c269cd3e2e3fe7c610e3f1fdb21a6ce60efa6430ff63676cea6 \ + --hash=sha256:c7b742bf31c88566b4bb6335a7f393bb322e580b6bb98df7bd0c25e6e3519ce8 \ + --hash=sha256:cc0329df4caaceb950d2f580b5ac716a377f7059624a0bafaeaf8a218c6ed774 \ + --hash=sha256:cc5d36d96478aa9c60654bd932525bf32964c62a7281eafdf16d85003a8d6004 \ + --hash=sha256:ce854f5f478050ade5a238731c4ca985a7d3b3cb53ff600a9b5c3b689b5f0a7a \ + --hash=sha256:ced3fdd71aaa83ce593746c2edb42b7a59cb4c19c8b5c407781c72e493aae55a \ + --hash=sha256:cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2 \ + --hash=sha256:cfa1c0cc3a8f9f53f1243a5a99ac36fd003880199383b37672e86ddda9cb07e2 \ + --hash=sha256:d1ee1e296209fdce05b81b663250eefa02213a2da7b41bf26f7829b8ba3545aa \ + --hash=sha256:d59b75732e9b6f27388e10c14b0259cc5f2e48c78627d185e6a177b58ad3cffe \ + --hash=sha256:d63600d620ad0064c3a748b950ac5ea38a80190e5498532efefa4b7b3f1da1f3 \ + --hash=sha256:dd732602a7009217f658d5863d12d79d373a4de0eebc111094bcdd3bb8e0a6cc \ + --hash=sha256:e06efa066f7dbadbc84ebc126a97c452a6451dfcf589d89d788484949e1cf795 \ + --hash=sha256:e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d \ + --hash=sha256:e4b018dc5a0eee4676e38fe84a47a427816c590b93b55d9025274ec4d6ffc2dc \ + --hash=sha256:e6621fb2a4988d6e53eedc455e5903e2679f3967b8acb3d639f1b63c14a2e893 \ + --hash=sha256:e71c909f353863b2b89c83de2ebed71ea6d0df8a6ef65a128193c5e650766bef \ + --hash=sha256:e90251c0c7bdd54a100a0dce3c07b7e637278c93af29dbf78ebb89a58c4bac7d \ + --hash=sha256:e9fbdce1e47394b09bc9f26ab117dfc8d6491977a11d86f592bb42c779db2fda \ + --hash=sha256:eb12fb2ba69ffa05f8695f61c69e591dc4b4a12ac3757ac8af8adb259bf56d17 \ + --hash=sha256:eda059b6bc8bc0812d626fd91a7ce01bf583df0a61296eff390fd94141a34e30 \ + --hash=sha256:f03ac127268b43ef4fe9e6ab6794a6794b49485a0cc0c1db79876d2f33f75bc7 \ + --hash=sha256:f298e218441525d3794428b4c8b8fb8662c6d3ea79925d4807ee6b9a96a3bca5 \ + --hash=sha256:f5542f9b941279d82d41eb0aa9f98eba36fe4df5c7086c651df7944935b37182 \ + --hash=sha256:f6f7deae3feb4edfa2efaf7c574fe88cbf055038a6abdb40188e4fff66d5699f \ + --hash=sha256:f9b1e28d0e8dbfa858abdba91d6b547beaf2df1a59bec6da6faae7b96a4991a9 \ + --hash=sha256:f9f8405c2c758532c74fed975dbee57be1f31a6e865c031870c79a6ed3212ada \ + --hash=sha256:fa48b1b63d639f9483e0633e092f5851e2348c352f1f9bb6c8182f87884ef876 \ + --hash=sha256:fb78f6e7fcd8ad785d28cd577168bc1aaee827b25bb8755638f694794ea98f0a \ + --hash=sha256:fbc597639158fd7c14d55e808718848319540f51b0e6746e3eefa59723a4a348 \ + --hash=sha256:fce8cbd4997efeb450bd298b54f755dcdff18d496f7a5ddbb4867c6d7c88fdc3 \ + --hash=sha256:fd0350afdc3aabd5576f60ea109228bd5538139713c7b094c5cd27c73a98bc6f \ + --hash=sha256:fd0a274c0e5f9a21565cd9d3dd749b61f96b7aa1e20a93aa1ba4029518f2e5c0 \ + --hash=sha256:fdb8a068947befafba9952162645dc2fecaeb400e64584829ed5e9b2fbe21a7f + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # requests +click==8.5.0 \ + --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 \ + --hash=sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # huggingface-hub + # litellm + # uvicorn +connectrpc==0.11.1 \ + --hash=sha256:18277f7838847b4271ca38d40c7d2387b5a2ea6a29f240689c19e1ec84aaff66 \ + --hash=sha256:8a52e2e92a485fa9681c1101a79a5ebeb31807e3ea3d5aabd41f484a6398bc7b + # via e2b +cryptography==50.0.1 \ + --hash=sha256:01f41478cf33fc605a6a089cd56d28b45c6c0b45a1928b61797f2621a04bac71 \ + --hash=sha256:05ba322c4da95b262a212c345af888ef2c37c88c0509756ea00a0e6d68850f23 \ + --hash=sha256:16c5ecd954b3330ebfb6605eca4fd952da8bef376551d5cc264534e3770a9ee6 \ + --hash=sha256:2a93d05e34d5f67fba6f891fe85d929999baa7195e853923ea6d7576c9e68c5e \ + --hash=sha256:2b34d76a652ea2b6faf777c35df230c5637842cd904e04f16230c3f9f03e4361 \ + --hash=sha256:2ebbfb0f1fed745e91796e3e1080a1440423fdae8ece1b995a1d80883a409054 \ + --hash=sha256:30a125032e5642a21ff816e021152bd4e7e94f03eff3f4b7fca41cd22bc3110f \ + --hash=sha256:330fbb252391c596f1ae42c5754449dc924e6ad012dca8efe0d703f9f2d12ec6 \ + --hash=sha256:359e62deae718bce96170e223fdcb6357e4fbd3bb7a3a75f4430763532560e49 \ + --hash=sha256:407fe2b6db00939c05c0e945e9914238f2f0a430974839429dafc82b1ee6bee5 \ + --hash=sha256:42be3bb70596b3abe4ac097b75be223e8b3ab614a0e5de068e3dcc54d71d6149 \ + --hash=sha256:4c4188f7c0cf655be5c06342b817ed0f9595b69ffa2b12026e5353eed29dea88 \ + --hash=sha256:51593d180cf6d179bde5c5d065bed81386b1f381656ae7d042b7ffc87a9895ad \ + --hash=sha256:51afcfceb15597cf2635068e4ac9a56b2abde622edde17f37d85fd7b5306497a \ + --hash=sha256:53e279950892dc102c6b4e52af03ae5ea92fac572a1ddab78ca73a997f62b69f \ + --hash=sha256:55d16b1ef3ee0958d893a977b19777887e546c9954ea81b200c3301a864013f2 \ + --hash=sha256:5dd9bda1c12b4162f6ff568eeb5e0ff956c28d14406e875cfe8a63a2d414ff20 \ + --hash=sha256:5fe002589592ed749ce77fe0695fcbd3500dd61d7d6db5858a7544c612fa8e45 \ + --hash=sha256:5fe939deeb161024a6be98229c953b6591fef1f41214497a78fe793a244c017f \ + --hash=sha256:693c99b49bd37d0d096e4334c10232c77248c415b98d35236094cdf96d57258b \ + --hash=sha256:76de83fbd91ac49c0feaaa983d0748fd7a53176afac5fb3bf7478d244f0eb527 \ + --hash=sha256:79bf008d1f9af6071c797ad133e39915dfee7614f18f18f4db9072eb715064a3 \ + --hash=sha256:804728ce710890870f3aaa344b2e161172d258d768ac139d02cfd9092d0d94e6 \ + --hash=sha256:8921d58f426793c5f1b47f0b59575780de9a095214958d0eb37d909593db8367 \ + --hash=sha256:8df2de9102026855887e4587084f6eabd80ed0f345b8ad8a7ac27ab9bf4723e0 \ + --hash=sha256:9cb3cb952cf5a8abd50c782a98a89d71699715e802fe349704b47f2425b42a94 \ + --hash=sha256:9dde0a357190eb3b1da1bb9ab750e9c85cba82ca5977aa0836cbb94e92611239 \ + --hash=sha256:9ebcdd5519be9b652a46f507817a74591774fc3d6923ac364e4dfa64e36b291b \ + --hash=sha256:a0b1a59e3a089064a0ec309e9428c8e3ae4e161419d20ac33600767e83fc658a \ + --hash=sha256:a255449073358275b64b67d3f595f268bbef70e72b6edb65e0c70c735bf739c9 \ + --hash=sha256:a8f40ea47330e71b594a7e246898f93177c259490c63183dbaf9e571d71ed9a5 \ + --hash=sha256:ac02b07824d4d1001bd4367599f839c19cb171924c796e52c23508ac14c2c0cc \ + --hash=sha256:aed8db4f6d71c51efb89530e12d9464e7bf2923d46c3205dc794a2a93f8c0648 \ + --hash=sha256:b8f852c65863251b9e3a1b8c150ce21e59b522dbb6a7d4bc80e680d38388e986 \ + --hash=sha256:be224a65493ec5b74a158ff22a5522ce4a5ca1e543c647a3a4730d4a09e5f959 \ + --hash=sha256:ca83d00d9e69cd5eb63f2e69c3a5a59e0cecae5ae14c6ae0b35830fe3b37bad0 \ + --hash=sha256:cbf74a81765ee67413503ca6e26dcc4f6f5a519822436cc0a1b97aab6c1b8a17 \ + --hash=sha256:d63ae8f6481fec907ac0f588eee8a90aefde112c633131fe540e5711ddbb5a4e \ + --hash=sha256:e22dfed744bd4002e909464cb23d2f0b05c6f3113a79ef2e9864a53db737c733 \ + --hash=sha256:e2ca8fd1b6b4b82a1c4cb02841d0837e3c12336c2e24b520ab8ab3b969733d8f \ + --hash=sha256:e74591e283fe6eb956416c929eb58262a719fe0311fd9054c62c3350ed8760d8 \ + --hash=sha256:f74455bb086a85d5e81246412602aaa97ed095e504cd40dd261ef50be42205bf \ + --hash=sha256:fb4b9672d389c738b175c4166e78310f8a70358886aacd9173ee03a85ffdc671 \ + --hash=sha256:fc3ed7ebd2a8c96f5b166de0ab9b624996bef3b07bbeb19364dfb78222c22c80 \ + --hash=sha256:fd3718b960d0b5dd213cdf03f3bcb7000e69dda0de8b956061947ff6bcff5558 \ + --hash=sha256:ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # authlib + # joserfc + # pyjwt + # secretstorage +cyclopts==4.25.2 \ + --hash=sha256:0776bc1fa796cd351646c345b7420279e58d6c2c4a8f5d5dd54dea85bdb2de8f \ + --hash=sha256:51b42513eea5e4ba6a08b68acf187b81f1191032cf77b9631086cfa2c88fa7a4 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim +daytona==0.203.0 \ + --hash=sha256:bfe2527225c612a5f7889679398bdc00aa1d5997e2f3c467d93d654cb9485905 \ + --hash=sha256:c8c1eb44fbb710e6c35fc0c436a0e7d897db67bb5627006dcf5ed96801469d95 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in +daytona-analytics-api-client==0.203.0 \ + --hash=sha256:8b975fb897878e18d51c9b9c363273940d8b4d2f35124385e4f73c14d5e73380 \ + --hash=sha256:e17315b0be51b59a8165653134ffb6f847b01107577d912667a2c2b37f38de84 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +daytona-analytics-api-client-async==0.203.0 \ + --hash=sha256:25eb902f09679b32955db729f31af39c6f882b6fb006e9f650b7fe10adc33f20 \ + --hash=sha256:9ee0afff2b84255ccbd3d9a1fb1b4306f7bffcdca06347832ba59e1bcb8ddeb6 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +daytona-api-client==0.203.0 \ + --hash=sha256:461e6ef12ba3d570114a5878822b69ea22a22a2c3787b946ad7afc66ee747905 \ + --hash=sha256:e6c5c2b469d282682e39cfe0c5ecd13ec94a5e65f0401add678139a96a39d0ad + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +daytona-api-client-async==0.203.0 \ + --hash=sha256:2ecdfc0c12f1eb6e92954c5d23d97b7a3a32e0560d075cde38f4a96b8ebc333e \ + --hash=sha256:7dbe3df925bfc3b00e2ade7cbbf45cbe77f254b6d93202e7b10e2a518d7c7e3e + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +daytona-toolbox-api-client==0.203.0 \ + --hash=sha256:a2f35c55d9e582edda47dfa545b6732c655478c4ccf37a780c192139ba1f342d \ + --hash=sha256:b443ada7142b23653ee8f3aeda8834421e0d6d13a1f8eb455f8f7511a4ba5e88 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +daytona-toolbox-api-client-async==0.203.0 \ + --hash=sha256:3d7ad4131200d93dc6a37ac697800957675edb48a72089c06e924e7a8753986e \ + --hash=sha256:948f03d1527994e33df5d805f6d13d30743c39fa079f40e1643b442a8df41b04 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +deprecated==1.3.1 \ + --hash=sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f \ + --hash=sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +deprecation==2.1.0 \ + --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \ + --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # postgrest + # storage3 +dirhash==0.5.0 \ + --hash=sha256:523dfd6b058c64f45b31604376926c6e2bd2ea301d0df23095d4055674e38b09 \ + --hash=sha256:e60760f0ab2e935d8cb088923ea2c6492398dca42cec785df778985fd4cd5386 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # harbor +distro==1.9.0 \ + --hash=sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed \ + --hash=sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # openai +dnspython==2.8.0 \ + --hash=sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af \ + --hash=sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # email-validator +dockerfile-parse==2.0.1 \ + --hash=sha256:3184ccdc513221983e503ac00e1aa504a2aa8f84e5de673c46b0b6eee99ec7bc \ + --hash=sha256:bdffd126d2eb26acf1066acb54cb2e336682e1d72b974a40894fac76a4df17f6 + # via e2b +docstring-parser==0.18.0 \ + --hash=sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015 \ + --hash=sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # cyclopts +e2b==2.36.0 \ + --hash=sha256:0f3ab9f49e33d1f8cda70669e43e5a7550403649b0ff2960af6b3478154863c3 \ + --hash=sha256:136fefc38b4b942a5ed7c80e4dad83e1b0962468edc7b1b1cac5aace8bd5f3a6 + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in +email-validator==2.3.0 \ + --hash=sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4 \ + --hash=sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # pydantic +exceptiongroup==1.3.1 \ + --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \ + --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim +fastapi==0.141.1 \ + --hash=sha256:bfb91aa2d334c61cb35ba9a116fc123b3d3df31640b801cf57a7a78ec3f603b3 \ + --hash=sha256:e8822fc40db1e1858054d7a949a888695bc9bdce70139178e33bd2871a453ca1 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # gradio + # harbor +fastmcp==3.4.5 \ + --hash=sha256:5d3d438eb2917e63e6faf53e8cb8fe26d887ec3232f848093a4eecad7fa34861 \ + --hash=sha256:a95f2bc876bef42e8b50f7872f24f3f2fe3b1d37408c734e8b9d9e03014b72d3 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in +fastmcp-slim==3.4.5 \ + --hash=sha256:5badc3bceee61f61297eeb9494f499325f3ce1cafabf4611b31f6c3e9d7dff59 \ + --hash=sha256:bc31217827c4999812543c83ee95ed9a47f3ed1e3fd0bd4f64371e375b748eca + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp +fastuuid==0.14.0 \ + --hash=sha256:05a8dde1f395e0c9b4be515b7a521403d1e8349443e7641761af07c7ad1624b1 \ + --hash=sha256:0737606764b29785566f968bd8005eace73d3666bd0862f33a760796e26d1ede \ + --hash=sha256:089c18018fdbdda88a6dafd7d139f8703a1e7c799618e33ea25eb52503d28a11 \ + --hash=sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995 \ + --hash=sha256:09378a05020e3e4883dfdab438926f31fea15fd17604908f3d39cbeb22a0b4dc \ + --hash=sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796 \ + --hash=sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed \ + --hash=sha256:12ac85024637586a5b69645e7ed986f7535106ed3013640a393a03e461740cb7 \ + --hash=sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab \ + --hash=sha256:139d7ff12bb400b4a0c76be64c28cbe2e2edf60b09826cbfd85f33ed3d0bbe8b \ + --hash=sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00 \ + --hash=sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26 \ + --hash=sha256:193ca10ff553cf3cc461572da83b5780fc0e3eea28659c16f89ae5202f3958d4 \ + --hash=sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219 \ + --hash=sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75 \ + --hash=sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714 \ + --hash=sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b \ + --hash=sha256:1e690d48f923c253f28151b3a6b4e335f2b06bf669c68a02665bc150b7839e94 \ + --hash=sha256:2b29e23c97e77c3a9514d70ce343571e469098ac7f5a269320a0f0b3e193ab36 \ + --hash=sha256:2dce5d0756f046fa792a40763f36accd7e466525c5710d2195a038f93ff96346 \ + --hash=sha256:2ec3d94e13712a133137b2805073b65ecef4a47217d5bac15d8ac62376cefdb4 \ + --hash=sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8 \ + --hash=sha256:2fc37479517d4d70c08696960fad85494a8a7a0af4e93e9a00af04d74c59f9e3 \ + --hash=sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87 \ + --hash=sha256:3964bab460c528692c70ab6b2e469dd7a7b152fbe8c18616c58d34c93a6cf8d4 \ + --hash=sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8 \ + --hash=sha256:448aa6833f7a84bfe37dd47e33df83250f404d591eb83527fa2cac8d1e57d7f3 \ + --hash=sha256:47c821f2dfe95909ead0085d4cb18d5149bca704a2b03e03fb3f81a5202d8cea \ + --hash=sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6 \ + --hash=sha256:5816d41f81782b209843e52fdef757a361b448d782452d96abedc53d545da722 \ + --hash=sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a \ + --hash=sha256:6fbc49a86173e7f074b1a9ec8cf12ca0d54d8070a85a06ebf0e76c309b84f0d0 \ + --hash=sha256:73657c9f778aba530bc96a943d30e1a7c80edb8278df77894fe9457540df4f85 \ + --hash=sha256:73946cb950c8caf65127d4e9a325e2b6be0442a224fd51ba3b6ac44e1912ce34 \ + --hash=sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021 \ + --hash=sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a \ + --hash=sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d \ + --hash=sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a \ + --hash=sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09 \ + --hash=sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8 \ + --hash=sha256:84b0779c5abbdec2a9511d5ffbfcd2e53079bf889824b32be170c0d8ef5fc74c \ + --hash=sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176 \ + --hash=sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4 \ + --hash=sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc \ + --hash=sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad \ + --hash=sha256:a6f46790d59ab38c6aa0e35c681c0484b50dc0acf9e2679c005d61e019313c24 \ + --hash=sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f \ + --hash=sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f \ + --hash=sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f \ + --hash=sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741 \ + --hash=sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5 \ + --hash=sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4 \ + --hash=sha256:af5967c666b7d6a377098849b07f83462c4fedbafcf8eb8bc8ff05dcbe8aa209 \ + --hash=sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470 \ + --hash=sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad \ + --hash=sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057 \ + --hash=sha256:bbb0c4b15d66b435d2538f3827f05e44e2baafcc003dd7d8472dc67807ab8fd8 \ + --hash=sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe \ + --hash=sha256:c0a94245afae4d7af8c43b3159d5e3934c53f47140be0be624b96acd672ceb73 \ + --hash=sha256:c0eb25f0fd935e376ac4334927a59e7c823b36062080e2e13acbaf2af15db836 \ + --hash=sha256:c3091e63acf42f56a6f74dc65cfdb6f99bfc79b5913c8a9ac498eb7ca09770a8 \ + --hash=sha256:c501561e025b7aea3508719c5801c360c711d5218fc4ad5d77bf1c37c1a75779 \ + --hash=sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b \ + --hash=sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d \ + --hash=sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022 \ + --hash=sha256:cd5a7f648d4365b41dbf0e38fe8da4884e57bed4e77c83598e076ac0c93995e7 \ + --hash=sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070 \ + --hash=sha256:d31f8c257046b5617fc6af9c69be066d2412bdef1edaa4bdf6a214cf57806105 \ + --hash=sha256:d55b7e96531216fc4f071909e33e35e5bfa47962ae67d9e84b00a04d6e8b7173 \ + --hash=sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397 \ + --hash=sha256:de01280eabcd82f7542828ecd67ebf1551d37203ecdfd7ab1f2e534edb78d505 \ + --hash=sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a \ + --hash=sha256:e0976c0dff7e222513d206e06341503f07423aceb1db0b83ff6851c008ceee06 \ + --hash=sha256:e150eab56c95dc9e3fefc234a0eedb342fac433dacc273cd4d150a5b0871e1fa \ + --hash=sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06 \ + --hash=sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8 \ + --hash=sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad \ + --hash=sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # litellm +filelock==3.32.6 \ + --hash=sha256:3f16ecd0117feae0dfc147e8c62eb5daeccd8bd800378c3ddf416de9b4feb6b1 \ + --hash=sha256:a3f55a18af3652a94d8f47d6055df434f254ca1d02ef2524850c6d249ca2512c + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # harbor + # huggingface-hub +frozenlist==1.8.0 \ + --hash=sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686 \ + --hash=sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0 \ + --hash=sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121 \ + --hash=sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd \ + --hash=sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7 \ + --hash=sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c \ + --hash=sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84 \ + --hash=sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d \ + --hash=sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b \ + --hash=sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79 \ + --hash=sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967 \ + --hash=sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f \ + --hash=sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4 \ + --hash=sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7 \ + --hash=sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef \ + --hash=sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9 \ + --hash=sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3 \ + --hash=sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd \ + --hash=sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087 \ + --hash=sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068 \ + --hash=sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7 \ + --hash=sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed \ + --hash=sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b \ + --hash=sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f \ + --hash=sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25 \ + --hash=sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe \ + --hash=sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143 \ + --hash=sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e \ + --hash=sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930 \ + --hash=sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37 \ + --hash=sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128 \ + --hash=sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2 \ + --hash=sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675 \ + --hash=sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f \ + --hash=sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746 \ + --hash=sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df \ + --hash=sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8 \ + --hash=sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c \ + --hash=sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0 \ + --hash=sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad \ + --hash=sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82 \ + --hash=sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29 \ + --hash=sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c \ + --hash=sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30 \ + --hash=sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf \ + --hash=sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62 \ + --hash=sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5 \ + --hash=sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383 \ + --hash=sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c \ + --hash=sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52 \ + --hash=sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d \ + --hash=sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1 \ + --hash=sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a \ + --hash=sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714 \ + --hash=sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65 \ + --hash=sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95 \ + --hash=sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1 \ + --hash=sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506 \ + --hash=sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888 \ + --hash=sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6 \ + --hash=sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41 \ + --hash=sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459 \ + --hash=sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a \ + --hash=sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608 \ + --hash=sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa \ + --hash=sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8 \ + --hash=sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1 \ + --hash=sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186 \ + --hash=sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6 \ + --hash=sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed \ + --hash=sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e \ + --hash=sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52 \ + --hash=sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231 \ + --hash=sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450 \ + --hash=sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496 \ + --hash=sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a \ + --hash=sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3 \ + --hash=sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24 \ + --hash=sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178 \ + --hash=sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695 \ + --hash=sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7 \ + --hash=sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4 \ + --hash=sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e \ + --hash=sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e \ + --hash=sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61 \ + --hash=sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca \ + --hash=sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad \ + --hash=sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b \ + --hash=sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a \ + --hash=sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8 \ + --hash=sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51 \ + --hash=sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011 \ + --hash=sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8 \ + --hash=sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103 \ + --hash=sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b \ + --hash=sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda \ + --hash=sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806 \ + --hash=sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042 \ + --hash=sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e \ + --hash=sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b \ + --hash=sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef \ + --hash=sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d \ + --hash=sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567 \ + --hash=sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a \ + --hash=sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2 \ + --hash=sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0 \ + --hash=sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e \ + --hash=sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b \ + --hash=sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d \ + --hash=sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a \ + --hash=sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52 \ + --hash=sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47 \ + --hash=sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1 \ + --hash=sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94 \ + --hash=sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f \ + --hash=sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff \ + --hash=sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822 \ + --hash=sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a \ + --hash=sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11 \ + --hash=sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581 \ + --hash=sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51 \ + --hash=sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565 \ + --hash=sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40 \ + --hash=sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92 \ + --hash=sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2 \ + --hash=sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5 \ + --hash=sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4 \ + --hash=sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93 \ + --hash=sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027 \ + --hash=sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # aiohttp + # aiosignal +fsspec==2026.7.0 \ + --hash=sha256:b57ddbafedfaef7018c1ecab32aa200a9d7ca26b77965f64e48b70061249d279 \ + --hash=sha256:c803c40f4cf860b49dea58ee3e1c33cb9c790520e233537e1340049f89b82a88 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio-client + # huggingface-hub +googleapis-common-protos==1.75.3 \ + --hash=sha256:57c435ac2c68b108999b6db075d9053e4d7a936ba57b4a3d45667b1346f1738a \ + --hash=sha256:a018d2bf098ca9fb6faa08d5bb780e2a2c2f73c566f069761331386c9596d3f2 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # opentelemetry-exporter-otlp-proto-http +gradio==6.22.0 \ + --hash=sha256:81c58b4ff3e6bd2c01c51767a9f5288126bcadbeda9512c8158fb7f7dcac0f63 \ + --hash=sha256:9bed11d8f410d5e47529060c3c09da0bc83caa960eb7458cc8fbac7d45e5c2a6 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in +gradio-client==2.6.0 \ + --hash=sha256:4493a6425560dd23d1eb5427a13290819135c664c5ed9f184eb517b1ea263291 \ + --hash=sha256:e648110efa31347bb8b1abda150a7a975b40a9658fdd8562803e2ad6a300d033 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio + # hf-gradio +griffelib==2.3.0 \ + --hash=sha256:1b8f9cd525681c26b1d6d574faa1371651e8459ca51d209684f50b8096ae06e0 \ + --hash=sha256:7b0952caf5bca6afa4bb5ee8c6a2d183fe3f21b62efc5f6c7243cb2b26d2d115 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim +groovy==0.1.2 \ + --hash=sha256:25c1dc09b3f9d7e292458aa762c6beb96ea037071bf5e917fc81fb78d2231083 \ + --hash=sha256:7f7975bab18c729a257a8b1ae9dcd70b7cafb1720481beae47719af57c35fa64 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # httpcore + # uvicorn + # wsproto +h2==4.4.1 \ + --hash=sha256:0e25f1462b23c9cb82d9eb02e28bc706dac2a68cb457c6a0d74d63c8a2a5d0e6 \ + --hash=sha256:4e866ffb1a869ae14dd9b5e6beb5c24a13da0495ad72b65925ded182521c1516 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # e2b + # httpx +harbor==0.22.0 \ + --hash=sha256:4c4c6571b3d160ed0cb45b82918136751fb08e7b8596412723ac00dde12eeabb \ + --hash=sha256:becf0ce354026cc37899855e0a0d2687cd5188034a43635849245069aad0938b + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in +hf-gradio==0.4.1 \ + --hash=sha256:76b8cb8be6abe62d74c1ad2d35b42f0629db89aa9e1a8d033cecfe7c856eeab3 \ + --hash=sha256:a017d942618f0d495a58ee4563047fa04bef614c00e0cb789a9a6d0633cffa7b + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +hf-xet==1.6.0 \ + --hash=sha256:0e6e21fa3cdfcdcd76748564bf593870a5e013f47d97cf10aed63aa222cff5b7 \ + --hash=sha256:23379c2f9ec8696d952b16414a2bae72cad86a52df869b050698ba60f538c675 \ + --hash=sha256:2e58454a340b3556dfa4972d5451aff4fba8dd42a236600ba1a1d2b1514f0fef \ + --hash=sha256:35cec30d75c6f9eb9c16a77cef68e85a103b72e24d4b473714ec9ff06428bab9 \ + --hash=sha256:3dc3e35441ba395006af5aaacc40ef2e603c51ef46c3530b9156185f00935ea3 \ + --hash=sha256:4fc74352a17015bd0ee90038bc9efe38db894cde45f268b6712b04fce8cd0acb \ + --hash=sha256:5153e6bb103ad49d6ea9f1b2e230db5a2ea32551ad09a706d2f61d7c7c80d80e \ + --hash=sha256:5789835d7c6bc9436962853192082374297fb72d7eff7e7762ec25ceb7e25338 \ + --hash=sha256:633dc0cd71d32da58ab8c03ad38e2fac452c15c2b0a2866ebf6ededfe0a5061d \ + --hash=sha256:70cbb9c896901600128cb9b6f06e132954fbede1db30f31f7c6c63f84cb7c31d \ + --hash=sha256:75765820ce4700db3750c94acc8fe27c5fae4c9ec000a0dbac3ca082acf97765 \ + --hash=sha256:8fb4f71cba6129110c3374a33f919001ff130488fc23553698e34cc1c2a1198c \ + --hash=sha256:948f15d3a9545cfe5932f6bd8b440f6ae630aee108f14b7bd6c561f7c2dcc522 \ + --hash=sha256:d62671bb130879cef0ee4c9ebe47a14af6c66ec53e6d84dc15936e5ffdfac82f \ + --hash=sha256:f0906082d9932ae0c0057fa194041c22b4e2cdb46b2592ef3b91f020d62a081a \ + --hash=sha256:f2f7278c05c22fd60cb436cda1269649b3e81db65ecdc8496e5e164aa4143e7b \ + --hash=sha256:fb4fadde1b2b70bf4c0c14a6dccbe7194b1c28947fefd5bbe3fed9d940676c3b + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # huggingface-hub +hpack==4.2.0 \ + --hash=sha256:0895cfa3b5531fc65fe439c05eb65144f123bf7a394fcaa56aa423548d8e45c0 \ + --hash=sha256:858ac0b02280fa582b5080d68db0899c62a80375e0e5413a74970c5e518b6986 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # h2 +httpcore==1.0.9 \ + --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \ + --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # httpx + # httpx-ws +httpx==0.28.1 \ + --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \ + --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # daytona + # e2b + # fastmcp-slim + # gradio + # gradio-client + # harbor + # httpx-ws + # huggingface-hub + # litellm + # mcp + # openai + # postgrest + # safehttpx + # storage3 + # supabase + # supabase-auth + # supabase-functions +httpx-sse==0.4.3 \ + --hash=sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc \ + --hash=sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # mcp +httpx-ws==0.9.0 \ + --hash=sha256:71640d2fb1bf9a225775015b33cd755cfd4c5f7e21c885192fe3adc4c387b248 \ + --hash=sha256:797373326f70eec1ae96f6e43ae9f12002fd7d73aee139a4985eaab964338a08 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +huggingface-hub==1.26.0 \ + --hash=sha256:c8cd4e2df1ba9402f77fce9b509ec1d52debb502551789473f34016acc14e361 \ + --hash=sha256:e8cca670caa5d8dfa7e45bf45e86b466698198cd8150c021bcdb4a86b9252364 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # gradio + # gradio-client + # tokenizers +hyperframe==6.1.0 \ + --hash=sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5 \ + --hash=sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # h2 +idna==3.19 \ + --hash=sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15 \ + --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # anyio + # email-validator + # httpx + # requests + # yarl +importlib-metadata==8.9.0 \ + --hash=sha256:58850626cef4bd2df100378b0f2aea9724a7b92f10770d547725b047078f99ee \ + --hash=sha256:e0f761b6ea91ced3b0844c14c9d955224d538105921f8e6754c00f6ca79fba7f + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # litellm +itsdangerous==2.2.0 \ + --hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \ + --hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +jaraco-classes==3.4.0 \ + --hash=sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd \ + --hash=sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # keyring +jaraco-context==6.1.2 \ + --hash=sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535 \ + --hash=sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # keyring +jaraco-functools==4.6.0 \ + --hash=sha256:880c577ec9720b3a052d5bc611fb9f2269b3d87902ef42440df443b88e443280 \ + --hash=sha256:99e3dc0060c5cbe8fcd1cdb36258e2a65ca40f1566b2033b12abb1bb44dd3c30 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # keyring +jeepney==0.9.0 \ + --hash=sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683 \ + --hash=sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # keyring + # secretstorage +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio + # harbor + # litellm +jiter==0.17.0 \ + --hash=sha256:00b5a98df3e3a3e8cf7b619f4ac2f8bf975bbf3d95d02c5d17b8dbfe5c8b8245 \ + --hash=sha256:00d783a779c5664e16dbad5e3a3c3a75e128b07dd5f4765159658d9210a50ca5 \ + --hash=sha256:0239520085cac678e77a606fd7e3f1c60c371d719790c5e3807388d3da4354c2 \ + --hash=sha256:02a360707033d8cef53f7f3480817a1489177a259ec6ec01e98c37e0b922ddca \ + --hash=sha256:02adebb7ce6413c44d40af9ad59d1c1cd79630ccdcb6f7bdd2d461e48c03d8f9 \ + --hash=sha256:03e432f226a453851079fb84cd17c6da9991eab723e28d716f14ae3d906e0c12 \ + --hash=sha256:0619d806e260ecf0c2a64521942c94af5d547c9ec99b55ae4f51b538b5576a76 \ + --hash=sha256:073dc68c1a700c8fc480e877864a6b6ffc887533e261f4380c08c16bf09d057a \ + --hash=sha256:0b52d52035b3907c5b1f6277857b29c1cbfc965e24e0f27330dbed83edb591ec \ + --hash=sha256:10c5349312e5cb02b7a21e123a57665afa895953f05bf252a9dd4c13a572b7ab \ + --hash=sha256:10cd64a5720ad7f809ac5466ff1705813f1b6b510f195a73acafba0ac0e1f675 \ + --hash=sha256:10f5558eed511b830488003449d942bd75829ad6257dc58cb9a03e596a7777b1 \ + --hash=sha256:11902505d401691720f5785c15b02204248526edee11b635cd6c40cd52b81599 \ + --hash=sha256:155be7355bdb7ca76ab0961be8982c225f964a5c073a83984183f22391cc29fc \ + --hash=sha256:16dd0c1baf098ae70b8f3616574eb3fedf34e26670b89e16a7e67561f737ed2d \ + --hash=sha256:1b18434638228c0c184281609bf3d9459026a0f1ea48fb76c205e3ef72069caa \ + --hash=sha256:29f49b325e0234e4ad9ecca5b861ffbd09b95ccac9bd46fa55841b6e56eea5fe \ + --hash=sha256:2c45ad7c973ef33fe5114a953377b35a95240f4542c0724d9f781e47dc24bac7 \ + --hash=sha256:300ce01ab0215e3dea4d00090143c909aedc65c0f809b3c07983e1d038f291b9 \ + --hash=sha256:30793a24a31e968969757c9e08d830cbb15a2cd3c4959b4498b38f4b1c2258eb \ + --hash=sha256:30c692d567ba206c7cca38c9d1d0ccc70c9786290173c184d871ca12e9981ed7 \ + --hash=sha256:32aaaa764604496610a3ad2d98503ae88ccb2fbe769e892ff4533e778e85f708 \ + --hash=sha256:362bb47423886d45a9f705d2d9d4008c6eedd4e41eb1bab4e96fb6daa06b33fd \ + --hash=sha256:36ee6e69027396664e59995b9a635a947a5304ee9837279584a0bb8145c8f6b8 \ + --hash=sha256:370d8fe5bf201dc6925e8a84c81ac7291f74d9fd1778234fc79d517064a5c76b \ + --hash=sha256:37150a9e02e869475854fa20b7d0d5e26d18d0f8bc17293999973ff27e99ae7a \ + --hash=sha256:37f33d327900bf2879613b3363fd48df97b4232d0c41f54bcf2e790c2fc40a71 \ + --hash=sha256:3ad556afc289f15d2b181b941982d01f06190863c07440185b9f354e1bd2def3 \ + --hash=sha256:3bf4dc2b84a464117fb097d15a25c58d100d2692888e3b0d92df5b48ed16b7c0 \ + --hash=sha256:3c1a5336c04a41b1f1cf9572e294aec27cc569767ff73de7bf87a91f0bea7cb9 \ + --hash=sha256:3e05f5adbf68c4bd11e1610f394034d984152988e84be6f8314235ce6f2139e5 \ + --hash=sha256:40d2c240f8f80b5b0f201b29f0ae129c81448c60c772227a41747b5e0026f6a2 \ + --hash=sha256:42b0260445251b1bc520a63baa94a32d88e0f931fba234f1764db7feb7c72174 \ + --hash=sha256:454c4997d73cc466c71fd565d91e603b0274e48ea0c6b0b7a7aee6967e4ceb7c \ + --hash=sha256:455e4ab35cb2a4a91a8404e08fd3c621bae433922e59bf1c494fe20a426b013b \ + --hash=sha256:4607ec7d93355fbc25b8dc5189153cf21d66063b9f9cd04dd2774e6e783f9b6a \ + --hash=sha256:470e1b1e4c42f1ead2189166a299691871a2df5056c976e7fb96feafaf5f9d44 \ + --hash=sha256:492f37230bbf9581ab2c17bcda862c249afb9ae2e3ab2dd6db59943bc4cc3153 \ + --hash=sha256:4dfbfe5a6e1e80a7082af559f66386405025ec278833e0c649f69cbc6e1004cc \ + --hash=sha256:4e3f052c671d5f425cca5ea5901cf11a831369fba4a55a3862cab93c323b4c3b \ + --hash=sha256:5078ab00664307fab2019b522a93aeb191122789f085daf5fd9e362154021d4a \ + --hash=sha256:51e1519d676a9f14dad9c2a411170d43b022ddb7989562df4e849b261ce127b2 \ + --hash=sha256:523c499235fb65add25d4bb01b1c4709ce695efdc7deb6c0a7bc515b5c44e0fb \ + --hash=sha256:545c36a0f3b2238c242cc9785439d3242a871b7bc39fe3f441bcaa07bf3aa83e \ + --hash=sha256:55d0e0e613a3f9ad600cf436e0e2b8057d1b52bcf1d91b2d36ac53451231e6a8 \ + --hash=sha256:5888fe5abc1ca2fa834a3e1b4c7ef0dcece286a7d7e95a609ef0934b777b9fc9 \ + --hash=sha256:58df29268a95e910f17db7ec9178eb7f15aa8619aaca3575275c4e6b3f4fe4c5 \ + --hash=sha256:59bddbe6f9ffecc68d641e1e2d619ce64cf8a9e9eeb74e5c518f74fc87abf1b0 \ + --hash=sha256:5a52a430d04225ffde633e6840bf2381d34c019ff98526b5929755b9052fb199 \ + --hash=sha256:5bf350452a43173e69e1fc74847c57a60e3d7515807287f29849baa2a85d8718 \ + --hash=sha256:5c23849235d2142ce444b2b8c6eceee9f82f4cc0bd5c9081602e4155c6197807 \ + --hash=sha256:61aed66ee042b3b49ef85fdf75714234d055d89d8496ac1c6e47f89e7a30d5e4 \ + --hash=sha256:6219adaf59711ba7063a52496e8ec6d3fa3e209d7827d83eee3b2abc780a1744 \ + --hash=sha256:64846211a2debe7c071d2146d2283d2b0c1c93dc8fd5fb7794faac2ca6061b5c \ + --hash=sha256:686c93d86f2b426c803024b805bd161a6cd10e9627c23e901640eab646c0ad8a \ + --hash=sha256:6871973bfbd4408f7f1c632b30bbb5bbd9671c1bc8650af6823e24b7be13709b \ + --hash=sha256:6af5b74073bd25bae695e6d00919f6a9be7ed5a9f8836d981eb1ffe84139e6fb \ + --hash=sha256:6b303d88e6a0bda789ec4b7801c7bad68e27230ba1fe4baffc756d1fbd32dc9d \ + --hash=sha256:6cb41cd1432f1dc19a231cf70b54d42b2c9f05085155859263fce06fa4d41388 \ + --hash=sha256:6cf564d43c4388149ca58ee571d0f5ccf875e20d1fd4662fd94cc0d1ea3b10ef \ + --hash=sha256:6eb6aedeb7352b8f3b6af9cbd67983840165c00428e63f1b420a85885128ea31 \ + --hash=sha256:70f19a2ca8429f91e82eeffb2f51cb87bc2d6e953b009b91a92d29c3a16ccb03 \ + --hash=sha256:71dbd74314c5df52a1bccf7b8bca46d14e943af7a2012e73b23f49977ef194c8 \ + --hash=sha256:73b64e69c4150748e020356d958af94bec33c70a0a93d665cfa8f6d580fe1a63 \ + --hash=sha256:746243a080b4ca790b8499af3d7cf9825d5f5987933950cd818e767ee353d826 \ + --hash=sha256:755079792868ce5d4938e83b91a0939b34fb858a1ca65a104f2d771bea57faa1 \ + --hash=sha256:7573e80232c5bcf80c24c038cf7e53a463f5c3b1dd1dd4109d66304f4dccc233 \ + --hash=sha256:76eb4a5c20e86f9f848286f167024890f2862258a965d254774deb7fc1545ca1 \ + --hash=sha256:77f6aac0137309b31448c1bdcda4c6c77077664a6d018ece8d94019c68a5a5b9 \ + --hash=sha256:785a216bbaf8f15fc974e964ced7322cd3d774bb0e86949edd78c6bffd6ba35b \ + --hash=sha256:7b68d3495d95da120651a5628c7ebadee84ed001a1b76e6afc325c42482f15b5 \ + --hash=sha256:8079849db9a1371bfd90bad088458a8fb836261879df2233cc9632464ecf64e1 \ + --hash=sha256:81c83c0abe614446a283d994d2c07c4f58632dea2cdf66ba9e2921bb8ccd593e \ + --hash=sha256:826871c42cebaae22f0a2b5673a4a1a75c851bb2d13b3c17764a630a6b298984 \ + --hash=sha256:84963d3f395ef5e9a32ce47155e08a7962fa292c159a10cb98b931cef1416925 \ + --hash=sha256:84ac78df457e1ee3f7e733bd114823302ae8c5ad5542d7e6647d92ffaa090a04 \ + --hash=sha256:86d703d9faa1ffc8ae4e9de0fa007712ed2171b5c0d93811a8e2e105ac729b0d \ + --hash=sha256:86f3f9343a288eb85a81ef20a752b2f84564296636db54a9fff0b5c8deaf1df2 \ + --hash=sha256:8adca2e793288e5f1bb29279bb439d0d3cfbb50eddca7e7e6ffd42ff4f482406 \ + --hash=sha256:8c21265b251d99bbb40080d178a8953e35601d3a1564e05c4de4c0d2ca616797 \ + --hash=sha256:8c286860abfe8b100cac1c02e225e5776eb9216edd71ba17cdb237da4af32bc9 \ + --hash=sha256:8f770b0c77e5fac482e1ba03ca1a7e18286bfb213d749932a00a7e4cd5de5e06 \ + --hash=sha256:93946d89fa04d5ba64dd323a8dd8d901676cb8a3c81d99ae4f6c051a9b4c3f2f \ + --hash=sha256:96b8b0c6dc5d78682f54a450785e075aa929cde768304cad363cd4efba5a82ac \ + --hash=sha256:9bd3caac219df476dd0cc3fe01d2f1581ed588906feac767abd9614c1c12f8b3 \ + --hash=sha256:a277f97eba7d66b1ee27eb5dab5b774ff46a10c78d89a1d3dcce04ce1357c8ca \ + --hash=sha256:a3cebb1fe4a1abb00465f3f8a17e09112603e8b7c59e5c3adbcd9f7815a64acd \ + --hash=sha256:ac3c6ee3264d6f5c44c617f90bc7e8b9e1587e7d6708c9d8f811cb65582ee312 \ + --hash=sha256:af2f7501580f274b63c4b2283bc425f5df7edf06ae5b171e5f87d912ff359a20 \ + --hash=sha256:b550585523339b71cb852b811aae49d08d7601ad8ffe9f5dc1562f4c3d22fd87 \ + --hash=sha256:b75f85660108965a94be77911a25a253429307294d9415b3c597118977a614de \ + --hash=sha256:b847b18d066c46b3b7ae49d6c94a7634c5e4a8983146ee25562a092000f5e3ad \ + --hash=sha256:bcc064f99183a9cbe7f26ed648c352031a74145cd61ed75d34632c73eb46a5a8 \ + --hash=sha256:c19b9357309b8cc6de8a48fca8e44a8c9c2feaaa2f5896d037fa505d48fcab80 \ + --hash=sha256:c4289293e5278d9314b00f15c37f2120fa51d3d68565292e715524c750e775a9 \ + --hash=sha256:cfafd7be8b16ceadd298db542cead37cddc211c4c49e04ad2596924df18625b1 \ + --hash=sha256:d0ce4feb52493e3513335b2accdcd75605652e4632772d3c8c2f7b86954d7f39 \ + --hash=sha256:d2c0bf24c72fd0491405dce5d40194f2070e9021ce648c1a1d46234b93d848ff \ + --hash=sha256:d47687806f9c54c84ea38733507081337922beca90ce819c7d852dd485bc0f23 \ + --hash=sha256:d85c558c9f8532bba287a990ac63767c7daf756f0d8c030219f62499b1fa228a \ + --hash=sha256:da139721f4b7cafdbff580a4f511ea24cb91f4909330c6b926a1ca53836c0a59 \ + --hash=sha256:dbbfe4e3c21c8166980cddc5bee1a315df082454f007947dfb6fb73800768165 \ + --hash=sha256:dc0288ce39190ee33fe6e4ec73161eed34e7e2da509b525546ca061778d62b64 \ + --hash=sha256:e088612ff90ebc9247e1a43074b72835804261c47e6a6c01cb3ddcb55360d688 \ + --hash=sha256:e654b6b04e39c9cb19cb8b04c6ddf1f2db07751fa14156413969fd78bad0e5cb \ + --hash=sha256:eaba834b72d573547b9d966465b3394b749d5e14208cc70acb63aca37619ab33 \ + --hash=sha256:eae86b1f027031e39db2e0e9c4842221edb7b8cd474d23f87a79b3bd4b651768 \ + --hash=sha256:eb2295da7c3769f6719b227a237aa6a5cfa6550e478bc838001b592c57e16575 \ + --hash=sha256:ebf918dfd6a74adc1b9ad71f63c4ab00902fcd3b7fd39f2e24d871db8d713b91 \ + --hash=sha256:ec89771f4272b989487a6364e519db6bbaba323e8bbf949ac89a45ea9c18b7a3 \ + --hash=sha256:ed1a24005daac667d577402d75a2922f9775a165b146b883ff1ad3602d8be689 \ + --hash=sha256:efe9f61bb30174d2f5c8396445c360c96c44e78164d0815dfe627ccf57849574 \ + --hash=sha256:f0bc7f684b65bcda9c20434267577db71bf9905ceddd32b60d1d93278d8c8d3a \ + --hash=sha256:f3d7f7b34114f7ddc6d72a8e882d49de636b35d9fd12b4d420d3c5729f6c9812 \ + --hash=sha256:f753eb70b1474a29e635e7542ff7312e6d6b951e0b25e8a2e8c34eeb1ddcd478 \ + --hash=sha256:fa13acf1046f95df808c64b1310705e143fab87aee73ae00cc42d640867fd2c1 \ + --hash=sha256:fd7790aa79c8b518e512ebcdfce9f11d8ef5f30efd43720c8a19a548b39fa489 \ + --hash=sha256:fe15ddf316f1f1f643347d3a474e74ce61880c79a11ec5dca53df20c071bd3e8 \ + --hash=sha256:ffa0380ad091de7d3fc33e17a97ff479851ee18a0a2a3ee56ff3215cdc886656 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # openai +jmespath==1.1.0 \ + --hash=sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d \ + --hash=sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # boto3 + # botocore +joserfc==1.7.5 \ + --hash=sha256:add2c2c84e8373b084d526a8b53daba5d7a513a118cd2dcd9fc9f979d0922159 \ + --hash=sha256:d5ff536e658e17664f8c1b1ab60dc4aa62aa973fcef1edd33cc44bda45d6f5ea + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # authlib + # fastmcp-slim +jsonref==1.1.0 \ + --hash=sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552 \ + --hash=sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim +jsonschema==4.26.0 \ + --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \ + --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # litellm + # mcp +jsonschema-path==0.5.0 \ + --hash=sha256:2790a070bc7abb08ea3dbe4d340ece4efadf639223001f020c7503229ba068e2 \ + --hash=sha256:493b156ba895c97602655b620a8456caa2ce08c1aa389f5a7addec065e6e855c + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim +jsonschema-specifications==2025.9.1 \ + --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ + --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # jsonschema +keyring==25.7.0 \ + --hash=sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f \ + --hash=sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # py-key-value-aio +litellm==1.101.0 \ + --hash=sha256:140ee0997324b8fd2405f7f8c26f42c0f364996dc4030187344a90cf27916667 \ + --hash=sha256:734ab2b8cad6a3b582d52d9c9c5fcab759eb382b93935ef808fda0e16d822ac3 \ + --hash=sha256:7cc623a224c6f11a04367a682b095a1e08e5f6da75c990a650910db5f9777819 \ + --hash=sha256:85d88053148c5c6e016e495273eeaf8f3a29779b60d18977ec42e2a6bbd2d8eb \ + --hash=sha256:ad013161074676fc91b91d828f696300132cf3936d3f386c4701a41710d70aa3 \ + --hash=sha256:cdf3351e394206e785bf339f1a39d9358ddf4e4f38129750bfdb5e4f5ceab8ef \ + --hash=sha256:d4064024151ff2877e542b56c6bb6a39e0c3e6651abf4639af9346a678586e52 \ + --hash=sha256:e5601ae404b0f1e38ce1e65c75f880254485942d9c824453e3926f2323457fd5 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # harbor +markdown-it-py==4.2.0 \ + --hash=sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49 \ + --hash=sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # rich +markupsafe==3.0.3 \ + --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ + --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ + --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ + --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ + --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ + --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ + --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ + --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ + --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ + --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ + --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ + --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ + --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ + --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ + --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ + --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ + --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ + --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ + --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ + --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ + --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ + --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ + --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ + --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ + --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ + --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ + --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ + --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ + --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ + --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ + --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ + --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ + --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ + --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ + --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ + --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio + # jinja2 +mcp==1.29.0 \ + --hash=sha256:52d01f334de1868cc3bb2d6604931126a67631f99a6c5d3b82ba47290315ec36 \ + --hash=sha256:f5a075bb611f23d6f4d080c6a1699fa62772eebc562ba9e66b306ddde1c755f7 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # fastmcp-slim +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # markdown-it-py +more-itertools==11.1.0 \ + --hash=sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d \ + --hash=sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # jaraco-classes + # jaraco-functools +multidict==6.8.0 \ + --hash=sha256:003a3bddb32915c3f67096ea41d24e53edf710edb65a1f5d0c70ab40b0e4d20b \ + --hash=sha256:00be37bde741bf60871082cd347a093218c44886e99231b7516671c70f2c280d \ + --hash=sha256:029897732a9c798737457e382bf84e8c64237eff224a90aea2639f4413c45e4e \ + --hash=sha256:05c2e90c5289c5f7436ba2c25812a5fbdaa1c1bc11c8d8d3bbf64f5cd7c633dd \ + --hash=sha256:071da134651b04a8507dfb331ac0988f376337c2aea59486bf20989fb5b5a64e \ + --hash=sha256:088b04a66b3c1fce6fe4d771ec184a0426262d0b86709c908477b4ac7965df40 \ + --hash=sha256:093167d22a8c95af30f597b8a5686f20a14512989942d4be804d119899caca20 \ + --hash=sha256:0935971bffd0b479fc90c4811ca787703e93fcb6afea939a375dfc80285ab368 \ + --hash=sha256:095f62ea4e7a3be2f6c567ab695ce10e950f2adb905c1bec82281593e0b2d2ad \ + --hash=sha256:0b143d53590e89f43153d81d505a8448d4d57354354385aef8a51d67ffefa27e \ + --hash=sha256:0c1c4debad7337627b86837abdf0237ca3cb3d7e17de7eab0177c263878546d4 \ + --hash=sha256:0eca15d627e942ce186a935061f1568cc46c02e97c419c8da802df2be9f917d8 \ + --hash=sha256:0ef606c15cac6c90279acf34120784b6f36662cbf382defd3955cd8f1115336b \ + --hash=sha256:10456943903744ae1249728161c96bd9d2f7eb5ee17fcc2ffda2dc32e1bb36c7 \ + --hash=sha256:11d71490bf4bbff1141b14b93af419ad68c56b60bea9277fcb3f94dcca4796eb \ + --hash=sha256:122adc7c46ac1e31ecfc7f81b2530533dccafdba70f5d741649f87e336c63384 \ + --hash=sha256:13967dca8b2f33230a1427b52438326bb1c9101a1df22a3309ed3fcbbb3c96f0 \ + --hash=sha256:13e26f59f0eecfc5f67c663ad550ffdaf62c0f657547cde387f6c86af1c9449e \ + --hash=sha256:15db8e6cab5f4cc9241bc56e69fdf3452cf49c10ee3c7977c742e68a275b3786 \ + --hash=sha256:18f0e06360c3e451a3ab800355773c8d125a758238d780c800b0ee5e90ee903c \ + --hash=sha256:1969971900b0871530f9b62280dcc2d75688e74d2a69262bc01faf2b96c78f04 \ + --hash=sha256:1b8986d4313dcee7c932837d16a535f1840b827bac1ea7c5c4c80751d0423794 \ + --hash=sha256:1bdb9b8fba5a9aef673ec90db3f55b1ce743f2fbdea4d37dc04d14ccdfc153ff \ + --hash=sha256:1f57c414be82490bc0e0305fdb834186229b2d9b6a35fa0afd1eb1a772d125ab \ + --hash=sha256:1f66fe6a021173d0d47968491791966b9f3e6d61115f2491744aa0c07a6e67af \ + --hash=sha256:202436df907c15adbb94360296c425ea53cf8968a5d2cff9b5b9790ae1972b33 \ + --hash=sha256:2196ba6df392c3574acadd14ef87550f3611349c8618564de324b806a7a31cee \ + --hash=sha256:22a310ad37672a261e55a8b5e28d0ae08cfb68abb1f46418ccd19835c3b8e836 \ + --hash=sha256:23c9ee89967b6a9b4048acb3b93b660ed714ce9c8bf3bbe652959bc120dc02dc \ + --hash=sha256:2622fe114c0bd66ca5c461859357587f5a5e35ee5ff49fc5643d1bc78dbb41c6 \ + --hash=sha256:26a7aafc992e78872e2c8c1f7248c0e01139cf9020a7781b0c064fa566832712 \ + --hash=sha256:27747162712e85c84598d364425dbf1714ff335bdb6ba3171c4e5081196e8916 \ + --hash=sha256:29631224698de1e42abc8fa7658d830e0aed0029785144b5832b695da5adef2f \ + --hash=sha256:29b6e7bc4442a56cf8e0dc1cabf3fdc77cd533568d6829fc76a1effd2ce332ec \ + --hash=sha256:29be9fd289e9ab8f480996ea2f686e1654b80242033843cb11691688329423f1 \ + --hash=sha256:2ba9933e8f35fe4a70f540b837254c4055da82dc3a9e500a8f95e61498083a15 \ + --hash=sha256:2cc66abb85e2108c9ff8a1c0d20fa260bf690bbb33caef4ff3ecb2c2cbdfff5d \ + --hash=sha256:2cd560498ae8e1bcc955643c1d78eb8e338226d07a983c656ea8c4443d3eec0f \ + --hash=sha256:2f79cc3e8039a8cf5c77e0811b0807953fd52d0863b9b76970b20d696dc64a78 \ + --hash=sha256:2f8a4b0b4d639d525928c7f30de527bfdf9ead6e44a5e8cb9c50aced5e4590cb \ + --hash=sha256:307c1acd812fe897e7fbe10c6758822e8c04be4e7c60a9f54901cdf8b5ab8bc3 \ + --hash=sha256:3126f2a96704505aa4e92a72d6e8a5d7f29d40a987ced8bf69e29d71dfc71fbc \ + --hash=sha256:31e8901637e20ccb3cf8f8848b5d0f7a00462bf5b34f7cf3dcbb2753b18e8b39 \ + --hash=sha256:346ac52e56bcda320c0dcdfdd081947ed7cada33afea4e2284bef7b0733bff9b \ + --hash=sha256:348bb85e2038b40c007383616d73f734869063772372519549ebd7da1723d1a4 \ + --hash=sha256:3533a03e4e789baf6a286e7b0b1b6da3f3d7c3eab569686ee29ee1d8b52e2cb4 \ + --hash=sha256:35977263d9bf506dbc65349f63b3b8c91606d4abc110990945e3b94bc671319c \ + --hash=sha256:397599503b718f0137f26d3f6532d6955069cd2e5917c47ef581495bc2529ff8 \ + --hash=sha256:3bafff8598f0528017ddc74194e5451d5c22d046c98935f8f86247b0f286e4f8 \ + --hash=sha256:3d1f48582686a0a3b81e9b43234766cc96697df72081af3f48107bd3f34d34e5 \ + --hash=sha256:4261863fc8b5ab1b815ede94e592e94c6af5b04616014929057e61859e7382a9 \ + --hash=sha256:43a4b56555bbcf8af161e7c7682bd93eec10f068c95844511864c018c8e5e13b \ + --hash=sha256:45cc39ba50fb0754a4359b90f8229ae08598fe2266abe3521b4e5a9ba916534a \ + --hash=sha256:46029e6e27a3ec0dc55b53f58df82d10f04c5e111f78248279b530bedad2c30a \ + --hash=sha256:48ea524a25a1cd5972cf293bc95713918cba0bcd6fa9b992d906c857c546abe2 \ + --hash=sha256:4ee953a5ebaeed38dc21cc032ed17a9d9782802e00042200497ab4b01b0bf7c0 \ + --hash=sha256:54af1266710cb0f305127ae0b970aff8d208057f8a29cd6e1db99b0114947035 \ + --hash=sha256:560b211fc3bd4a1e1c6de44f6d38113bf5b410dfc89a4c0d2a3c0edbf1a0dfb8 \ + --hash=sha256:563661919f603374c40cf45ffcd25535c12b8954203569a2ab1cee5265871cf4 \ + --hash=sha256:563d6500ca80dac7bba6f48a78e0ffd87e21a7d4d24642c6503a2ddccd70c110 \ + --hash=sha256:59e539c4eb4d3a53b0e630a6ba2b2f2824732b5e73f90e30a280f12fde157b15 \ + --hash=sha256:5bbbb696c8024475b1877d14ce20d5f1cc05b8f6d786cea0fe3aa7fedc02e891 \ + --hash=sha256:5caf684986a2490628f059a99dd107b566a2d34cf947f8eb8387e0500a1f90c5 \ + --hash=sha256:5cd4637ce76312ba1e05eb9c5193fec231f64fee0944e135fa1e951242355b37 \ + --hash=sha256:610c7637bc36b90f39e6c66f710f93d57018f83d53e1e187caaa218c6892b95f \ + --hash=sha256:628ff11e6720f90acd0c305dfa3339f04a783a20de8cda6ac333ba46447261e8 \ + --hash=sha256:62b8e291a4f7edbf7cde7a43d831d893ba443a1b627498b53581943b0e348feb \ + --hash=sha256:6300d5176647145ba1e22991c924fb29743e54b4d7b8bc85a0d3ec0e55e189cb \ + --hash=sha256:64eaeda36ee8d88f9e8616a587a8c66a663283cf6e0dcf013c1ddd8c758e4aef \ + --hash=sha256:658f5a1895b804423d97b22d06fc0d0b171c7c01dcc3aa9c8faf0c0e26a249a5 \ + --hash=sha256:65c85c79f5a2c04fbbc18f006c014674dc5fdf270cb978d8862c82c6f694e60c \ + --hash=sha256:68186a2d4051c8ffd17be33553bea2ec9bbc8ef860fe2980a221d96126296f31 \ + --hash=sha256:68d40b2bace413f3231f5729d3fcfb1837fd31c4907e241b5d43211bfd76f3c2 \ + --hash=sha256:69708fecaa88bcb2341397b49fc95057a835b02a3670c551b37f95dd79e64e3a \ + --hash=sha256:69b3e519a132bb943b0daae15fc8c2168706b17f826481d32a32a5e784b129e3 \ + --hash=sha256:6b62b7e0025aa48dec11e125e655d1157985a5fdcec04b1ad500101ad072b891 \ + --hash=sha256:714597cb5d5e15a8a449d2ae23c45b486a9e8fa33c462c7a33d7f35b65d92943 \ + --hash=sha256:758233648ac47b07c575224c4eadd73c8929c3b4c31e2afcfea935fde1cda735 \ + --hash=sha256:75daa15ca16d6285eb2e104b2f05ee6f8d9836c68da3ce5c85f615a0450eed0e \ + --hash=sha256:77745725125d01fd613b6db043362aa7c6bfbfdb23d45dbfc3d92bf58160af62 \ + --hash=sha256:7941ef106ca1f2c62314a13c7ed913bcf49641f3efdc12864d588e17870920ac \ + --hash=sha256:7a2573d0fd34f361a4a14e54d8cda3a91ac4e55fbf0d719698024f3b09c5b147 \ + --hash=sha256:7a62e302fc8cd6aa8972207e7e951d1fdee7c1dda18568305041d19f0e2c00f5 \ + --hash=sha256:7bb0dad75068fee80fcb60f88569722c199d8656a16706702dc6e3b786819c90 \ + --hash=sha256:7bc7003991ebd368a20d05228137a37b3d3066751f3ea1e4f7b8efe8e752f2f5 \ + --hash=sha256:7d26dc8f070c0ec5579e987fa615ffd6883086106eefdff9e10d160fc5630630 \ + --hash=sha256:8125e60f3c70e323ac07dd8b3635f7b3bbc5c3a9ac04ae5988f668ff7ae28a18 \ + --hash=sha256:8180b635290a75af8478f1b3e9810135381ae24833293fe77b85c1c21ff842ab \ + --hash=sha256:82780eb8bf59e8fb25dd081fde6e058805045d6374a7f2f877effc826ca4434b \ + --hash=sha256:835d5a90b11d1f5f8200ff3cc8316bded76eebebc92436398947a27657e645e7 \ + --hash=sha256:83ff054b04915be5c15680da6c6012474a2cc2bf534129a0e8c6a99f17ba7238 \ + --hash=sha256:8457aff3c12a89a8e1c4674de5c777857fbc429f40fe117a3d29538547cbc364 \ + --hash=sha256:847d6082ae694dc95e548acb201bc100e1cfa96513bc71fdcb86f709dad6c435 \ + --hash=sha256:883284137e25318ed9735b742ae46341a864888fae28e8b6314c4f84da080f08 \ + --hash=sha256:887f9a975996032c686719eb7b3e1e7942fab5079c2b778bbd9afe9a9d78244f \ + --hash=sha256:8890c89d662560e51c55ac1304d6f919b23942abe9ae1127cb1de9aa6132fa52 \ + --hash=sha256:88a6df88567680504ae28bfa7a1f2f64243d91e79a40b2c92ef42efc531e23da \ + --hash=sha256:8d1046b5427dcafe6e8a0e07527dd74f1ee694006160162f53f3a17f15aad3b4 \ + --hash=sha256:8daafaa0b2eb43f76898ced78b1e0fb91b38c4fa50da516c18067f2a2d578c20 \ + --hash=sha256:8dc2d9c3a924ed14166e63650b2cf9f59e7821743bdd50b23802bd97ca09bde5 \ + --hash=sha256:90c10b22860dbd09982d0b8993b66231a861bea2993d4a817ff35273f6ea285a \ + --hash=sha256:91fa75d0a693832106d98f66c849f034f21c828d14437f1fb97d3784aab89e84 \ + --hash=sha256:930c6058047410e3edff445f5a6e4457f2e089042dede00e2d18ce06f3ceae2e \ + --hash=sha256:9442b14eec262a1f74369bbd07e75bc5155105164649a4b9fbc1ebc7b8fb0b14 \ + --hash=sha256:95c27b4f3f04320fc44e338573f40c5c956b504a7fcf081a157fd0b02579311c \ + --hash=sha256:9606f583e7acaf61e7b3f56074e14037b9af7cb194590edfc0114b3ae5931ff7 \ + --hash=sha256:962f18c59a000f30b084ea2e6b8001521bb315efd4e5f10acf9fb36f366b7882 \ + --hash=sha256:9caef53b20a105c0d66518a34be2f71b2783de8d091767575ef86f6ea422236d \ + --hash=sha256:9e37024b41d7a7e7e9cce14b248d54707c21c2a2ea30a47b71bdcefcafec00f2 \ + --hash=sha256:a5a7ee1217949ddd43c6b7bcf70d5c22193bb50e8c695386de5905325e93ce9f \ + --hash=sha256:a5e1583c14775580da05641240ce0d93f36ce3ddef3d5083a827468b0bcfe874 \ + --hash=sha256:a9e246f67ac038568b854ed7c5578e4c6af1f742359901a8fcc3603ff1358df6 \ + --hash=sha256:ab83fdd8cf307353edba9c427c17a3a021c2522d690f5633dd9f72d28b48ccca \ + --hash=sha256:ac746cb365bac1c462da9e3e6ab8904a8efe2217a56b0b2e3d9480f41d2b2602 \ + --hash=sha256:ad474c11d851b6fc97cb625e4822bc0cbd567fc07dc2602e28faec5a36b42bbb \ + --hash=sha256:b03ca066b47b18b205cc080dca6f76cbd159f8cdd33a02a0700164c13b37e463 \ + --hash=sha256:b1cd4d66ce894a45482e1ac2837c31d0bd447df35065e542b60055aa2d00404b \ + --hash=sha256:b25426f9f6ed402835617c8f23609a47045f91ecff365eb6734817e039a8ed25 \ + --hash=sha256:b367c342327717d644db4c0ddb37ceb655c84822215ea0773a3a36911b74b71d \ + --hash=sha256:b7e62b8fc7bd6cad007b9f2e0ad9c8d4854c06350d5f51e1a439dd18b510ecac \ + --hash=sha256:b8b7aa75146266fd3e2a2437cf69ae188688c04ab8665b163d4257b46c1e0c83 \ + --hash=sha256:bb36381e1f9f9d06eba2f10bdd438e5d20c07d5b55e1a3eee30b9f44cbf52316 \ + --hash=sha256:bb8c7da8c861391f7ae48e3593762be2dabe405109e01aec520fbe1a6d15d14b \ + --hash=sha256:bb9a60b7faa5d37c426fa91cf4d6738182a1f2755b9fab7c9c64cd466c4ce51e \ + --hash=sha256:be007d1aee2cbd530347dcafedb400891a3b5f1bd7135f95cf5d5b330b5219ee \ + --hash=sha256:be569fff1d85cd29391c431c5641c8772acb75bbdc61e60a8e82fceb9023d385 \ + --hash=sha256:bea7df027015856ba5d0a88e3b4777ff8cb5c66b58fc108050fe79d4dd9d4d2d \ + --hash=sha256:c0fe437a6d2f36aac2b49517057776575b5bf359df314cca20d230a6e139c089 \ + --hash=sha256:c2b2a96cf1dd99fe7867be4c013314225f4d5786e6685906e29932d42aca6f11 \ + --hash=sha256:c2c5fd0fd39574ccd58e1a52565b341aff522c5c836f1b3eb7605c371e61f52c \ + --hash=sha256:c46a08bf070d6849fed483e9d9833f9d06aecb8382ed985be0b38508b3ae958e \ + --hash=sha256:c5f3a2af441670d80ce5fdf13b6c1b421fc1fc7fc5182d58ac7486738bb2b742 \ + --hash=sha256:c60e50bc5b07faac92fd3a20fa21cc8cf3e3f7204d2867b206c73293ebc19101 \ + --hash=sha256:c68e0c0649d17c2d0339e3674e86a4aeba4a7e6b21c1e394cf947a95433b31d0 \ + --hash=sha256:c9c98d2f0126ba84cb45601eed97ff67ff767e19ae6eb3c31b02827b54d700e5 \ + --hash=sha256:ca52b9ec80851366197577154c862c4c4c7036ca76ae94cef5cb59c5cfeab944 \ + --hash=sha256:cbd86f9787c5e2f5fd27d8b21458222f107347c6731c4e93dde68f554b466a2d \ + --hash=sha256:d0264f8d5cb0a803f650a6a8572dfa0cd1e099a2234c588dc8fb220b415b865f \ + --hash=sha256:d0be2b832435001bc623ca7f1499ca1a853d4f082fb61221a80ce71132f50b26 \ + --hash=sha256:d244cf6b52b5ba1c34c3832f4652a668ebb36d95949b96eed9a1c54d916a90dd \ + --hash=sha256:d2d236b8a44ae91536a12ebcb996bdb31cf27425f36b4d05c87f2ba2716050ba \ + --hash=sha256:d3da668e903c934ed0b587ecacfed6901f6ae6384a6e975887592b61845e78bc \ + --hash=sha256:d6dc7804c50fabd28644d4d18a4b20aad3681b3e64f3acd3182b330ca73f7a32 \ + --hash=sha256:d7e5ba0a0153e35fbce9c51df530c8b4cb0c3012b46a04ff9a048441a269c2ed \ + --hash=sha256:d8a5ac357ac283490a8d1899b0383355fd1f8634b14ba0d59e4c0dd97db85556 \ + --hash=sha256:da1c112c5784ccd9d32cd90be6739fee32644e874eff6ae8f0497cba3e352e58 \ + --hash=sha256:dc911ae6152e455b16a2a1a626aa6cd612fa01efb9d0a4ab3f5cf328b911483d \ + --hash=sha256:e0db3a4d1e264e225037a6023888972c25206a96e016021a5bea41c9a939f2a9 \ + --hash=sha256:e192018b732f7b168e6604cbdf40fa8e05c996693b9eb445a0d8a73f4b77c5d3 \ + --hash=sha256:e37b744849fb631bb52e3dadde35ffeee365a6c41cf71257b5b7acc9cd83fd38 \ + --hash=sha256:e41226ecf607f062fe34a2f4cf64ad3a89e3a0180dc800b463b6b14c06dd10dc \ + --hash=sha256:e418ec99574ca24365ca96546af285c2b021a1a072478a79f0e3cc3b08837154 \ + --hash=sha256:e6ec7d37841609a691b96a10b4fde386c7cd93ebbb939f59c9f23325ee788395 \ + --hash=sha256:e886ef8c9879105fe4fc99417447b3a5f35d1131412ce839470bd2089fe2043f \ + --hash=sha256:e8e1e895e23818d343e4ae7dd95a0a556fdeaf8b471acf1c0a39b93c6f54d478 \ + --hash=sha256:e9dc7b4ff6ef184504b49ef9a4113d49a646653b2ce89f5f48c1f57cdf6ba081 \ + --hash=sha256:ea880d441be7c510106bc56064be39266d948aef94ad4955e8784690019a5d9f \ + --hash=sha256:eabb03dc3e4ed6333ecd1cc9826ec80e7a98b5506deeb832d7260c8e44166d23 \ + --hash=sha256:ec0a4d066356054d569a66e0a94691a2058b680be5e710298f61db11a3c4609f \ + --hash=sha256:edda19aff836ec515caafc09ea53d2ab144a041f09ee9a7cefcbd3ae4e976256 \ + --hash=sha256:f1f4a220db6ed7c8fd16b6d644ffd1f082651693204daf3275e049fadc849e39 \ + --hash=sha256:f25b61a708bd276e8cbb6afcbbf1b8e793a3be70ba0a842d0b8692020f83b706 \ + --hash=sha256:f2fa3d3b1c933d4bcb8fd2018700d5e7235c52f2ab8c88d22286965c5c0f00f8 \ + --hash=sha256:f3071e6515cc63714d014da8f738ae9fa3997c476203f3cd46de380c2376ed7b \ + --hash=sha256:f3a0a31189acf6703307397c6139ddabd734c20c5ef92649fc93e473df6615a3 \ + --hash=sha256:f7eefd0233a7c33ca980a5cfef26f1e9b5e2137839e752a99963696729f12d91 \ + --hash=sha256:f8b09b25e0f4dc2ea9e2adbb1cc3ba11a94d6fa3dd978ae659c8743052e1afbc \ + --hash=sha256:f8d7b66c9e09c0bb0add2b5895e646b62a0849e71155066f215523de6b95cbe6 \ + --hash=sha256:fa6c2880709c84457de104385b704fc28860f27e442ad13966fc4af8e714fe9c \ + --hash=sha256:fc5460940f50dff00731b4132366840ba9685286ea88ea104b661899084f3fea \ + --hash=sha256:fd789a294d8e098528be29b2669b83005ce569339f8cef167fc0274c3115c34c + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # aiohttp + # yarl +numpy==2.5.1 \ + --hash=sha256:08d60c810432eb83360958dea0999ac4cfb94531ea8efcbf0b7f277c2068aeb2 \ + --hash=sha256:09e9bfd8d2cf479c7d174804fb3811c53a8e9f20a37444008606b57d6b7a826d \ + --hash=sha256:0bfebd8695f9863592fe744be833a258120b14a9f39da255e8aa8fade2c0ddd1 \ + --hash=sha256:17a25e09640602e10bc8de0e6fa2b3fd68eedd84ba6d7842dc8f32f9ab87bd0b \ + --hash=sha256:1c6759f538fb912fc46de0a6b1758ccf7b57bc7c7ebebc23974fdac3de8db0cd \ + --hash=sha256:224ca51130ef7da85bea2191625181cb4f337f9cb64b471f10c1a12aa8b60077 \ + --hash=sha256:24d0eb82c0541d3415a33425db64ae439dffccd7b4dbcb30e7c35120205c506a \ + --hash=sha256:2ae0ca40bcb22d6ba59c1dfd5446f49940b0f2d821fde133f10dda11f816b84e \ + --hash=sha256:2c889b56fe48b1018f764b0eec8df59ab654e9148aa91faa12596043500de277 \ + --hash=sha256:30b44a6b53a7ae63c54c089a8726e5563ed302716c5b7ccc85afade40b0e7ff6 \ + --hash=sha256:32985c896d897419ef8da6917872d80b78ad0ea26d85b23245c7366ffde76d75 \ + --hash=sha256:3935f3b419b244a02732676fa5317a9193cc596a4c0646db07e5b421229ac9f7 \ + --hash=sha256:4939237038ada79308dda3204ac6462df056b5672b2e25db1149cf873668b3e1 \ + --hash=sha256:4b4ff1608417eb7a59da7b967bbb798cacfe071d2caf526a24281cd562072ed9 \ + --hash=sha256:54ad769f17bc2d833b620851989f62054fb9ab93c969d9e1dc3c8e3d56beea21 \ + --hash=sha256:59fda5e192b570217ec2580c96f00e9a7e12ef6866a900eb089b62c1a32545ca \ + --hash=sha256:5a4c988b38d261deeeaad9954e3deb091ad905c94e8bb6708654ef1d97f286b0 \ + --hash=sha256:5a6db61f9aaa57e369905c67d852045d3c4f7126405b29d09b19dec118e9c9cb \ + --hash=sha256:6165343f81b56ef8f514f396989e529b61d9dc709b99421b07e9f3e698e2287d \ + --hash=sha256:61ac47e772e6b8ea489e1d2f441a34c5c3ac17327e7ce294cbdf535795ad4e75 \ + --hash=sha256:6c3fe51bc6a16453d452997053454f309e8e0ed7b42d6b361ce4ac8c32913d74 \ + --hash=sha256:6eab239876581b2b3c5a242281b6007bbdbcd1c7085d7709bb57c5929b11e6bf \ + --hash=sha256:78798bd5b9ad744056af8efa90e3b9ddaa53272a0848a483084a1cc0a13b2dc0 \ + --hash=sha256:7c786fe9a5bbe360022e584c5a34cf6b54265c71bd7ec8ac3d8fec38968071f8 \ + --hash=sha256:83ce9c80d5b521b0d77ddcbe5447c218d247929b6cc056ca5351342accfff0af \ + --hash=sha256:9726558e8db4a5bf7929a70ae50f63abda4daf0efe810e3bfbab95976f75fc1a \ + --hash=sha256:99d5095fa265a0c4152e7bb12759e14381ef5496152f1ce58f44bdf55c44beb4 \ + --hash=sha256:a33276be12fa045805f477f22482088b66bb758ffbe89a9d21457de863a32e22 \ + --hash=sha256:a48a113e6afea91f5608793bafa7ef2ad481fefbda87ec5069f483de61cb9fa3 \ + --hash=sha256:ab451b59c5643c570974c43aef780703ef1d3b4965d2be07afd530615a9358d1 \ + --hash=sha256:ab84dc6b074fa881cae55bea94cc4f68e285181ba7f32497bf7dee6b1496165b \ + --hash=sha256:ab87a91b3cc3382b8956095bd8f95e00cf679bb81554339be1a2ba404a1473c1 \ + --hash=sha256:c12afb53450fa976d4c681c50a7423729a4c51c0465ed9f32b8a9cabbc472373 \ + --hash=sha256:caf3e317d33d60c37986b452613f4ab51246d0691350c03d0cb4a898627f4a95 \ + --hash=sha256:dc932a65ded7ce9013d120845a2514dcccb1a67bfc8deb8d37633762951904a6 \ + --hash=sha256:e68d8dd1e7eba712948f2053a29ec86917bc70ba1358df869d9f06649ef9cf09 \ + --hash=sha256:e824c2acf8862052246be5a44c15da1777940c60d010dd2aab897824d9c430f9 \ + --hash=sha256:e8c11c405efc5ff6816d5983c96cdfa215bab3428961243af3ff59b228490438 \ + --hash=sha256:efd736408cc97c79b9e6917338dfc8f06013b2274f992e96b1d9a81a71e2a2c2 \ + --hash=sha256:f089d7b00756190aacf1f5d34bdf38c3c430ac82b4f868f8cede73380460fce7 \ + --hash=sha256:f2479a47f8d5932d1718168a681ad6e536a9df484c83cfcf9de365e164537ace \ + --hash=sha256:f7119ebff1a9829e9f431a4f9d28e703023bb6b9fe7c8f724467dbfc27c94ab3 \ + --hash=sha256:f7d60026c0bdb1380e83bfa7a0419c4577ee4b9a08880afcb6dadeb74c649fa2 \ + --hash=sha256:f7feb014281029e628ba2d5a007407443b06e418b6fe451d1e2adcbc8eba0107 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # gradio + # pandas +obstore==0.11.1 \ + --hash=sha256:066cb4190e957b172b269affb98bee5a8f5cf6d0949b5b57ffe7f8cfaa540779 \ + --hash=sha256:0844ab75c8413c0af2d0fd2d2f6aa42d166809b6ac4be478151cd946ce7e5d69 \ + --hash=sha256:093152daa5c32b70a032f231bbc6a7eff76dda4285eed5a81d272b4485032dab \ + --hash=sha256:128da07f3a1b9c70159e2b2be9e27f458a9d531d57b1856dd7c4ddb73b4c9937 \ + --hash=sha256:13bb0b6a40931ab2da93f93ad843865d483d95acec1bd586df79313daa5a50af \ + --hash=sha256:159a50d0f4cc53afe6f5c695313bcaf6e92ac81d7847692c8279153483272bfa \ + --hash=sha256:2a9f3d66dbf3c073dd6b6033c0787ef14edf78d97bff95595d6f6979692da264 \ + --hash=sha256:2ba3bceec4263b3a70eea873abedb82e03da9599f2326e80d6505cab0c10d401 \ + --hash=sha256:2d80248f106bf9a2860a4b21add7d14c95eaa5785c7fe11e56d28a4602c7a07c \ + --hash=sha256:2eab3231ce66bc08e57b686b795d4a1333133c28b2e50c11f9986311dec69899 \ + --hash=sha256:307b5f9d64a7c00cd13371e376c05e3914216bd0818a19ff2ae05bc0135e01dc \ + --hash=sha256:32dcdae4207656ef353f949f927342e31757f926c58f014873b2d90c70276cb7 \ + --hash=sha256:3901f539d764cd2ec74c4e900dd461d89d765b2cacf06379ac7b7014e8d132ca \ + --hash=sha256:3ad616bd597a453dcb225ef6bec2af0c71b24b22c314a4328bcc216f774d6b9a \ + --hash=sha256:3b9cb988e03ff963914cf2176aee293eaaff7dcf4efcb905ce6834264b4b0884 \ + --hash=sha256:3e657c600b90f465ef43d00e6dd921e676a1bec34713dad1fc09ef6715e30658 \ + --hash=sha256:446dfe0019740da25394917585b8b0c116ae79454a633ee83aeb0c973c8812d2 \ + --hash=sha256:46122e585f48ab3f2e4fed51401a4e866279a3a3d1aee2372d598ab85ba2c539 \ + --hash=sha256:48983a143de69b11de49212caa79b5c39acbff7f592f9e85d156a0133a0e5796 \ + --hash=sha256:50de3116af69b6f1669cb443cc200ccdc1523a790a41f00854331b48f716cf8f \ + --hash=sha256:614a36f19963cfefedd52f06c5370c034a6dc5b0a2be2e4602fe6ac8e52e97a6 \ + --hash=sha256:63be447e5fdaf0c31517a66906030148697ab3b1f1611fe03e30125a56199ccc \ + --hash=sha256:6680094928da4587dfd03be4e9c17ce7eaa1ec287f49519c75896819c0767247 \ + --hash=sha256:67b8acfbbab960c2cb14c34294a96556caa67fcff15ee77c716d5c1bd1558606 \ + --hash=sha256:68adc24a822536148a3c12be0b7cac091239b3f99c0589b16bcdba5a6ccf58e4 \ + --hash=sha256:6f89647953b4ea50bab3f66591f7f462cd454a5d2fbbeefd885716a2c54e13ce \ + --hash=sha256:7275e75228059b2b30772b2ba3e41562a4a92ebdd3a96619d300f98ef152119f \ + --hash=sha256:73284fc8a9804596baf4d80b55c0e71a6007487bfa28d6924acd85264d5be81f \ + --hash=sha256:75a12f1d0d38fd2923e972e417797d7bf123ff7d13e2b6c5919d2e9a3c6653cc \ + --hash=sha256:7b1b769fda200cbee559da2100586b3b7810bc9f207fe41b9a00fd749c997799 \ + --hash=sha256:801f57c06df5d81eaff8ed553129cedadad12cf1d76fb7499fd6daa1d996c72b \ + --hash=sha256:8a399e7c816e8d7bfe5e992a04e25326bd0779822b65bfd3dbc943b8a62192dc \ + --hash=sha256:8a3f93310422b153629af929d9e88d1fc826d5e32a456de4d3a1926a770ae09c \ + --hash=sha256:955f2348bd17beb80f3f96bb52b119e61eee99c97d1f0b103aa61a0840b7c862 \ + --hash=sha256:97850f68c8417f7167549bdb705c362c825ed470298c6b04faf52258c5e9234e \ + --hash=sha256:a1dd9d7ddc10bbf17cea0e84553a665a822878bd199380557a6df2aa1e60ce34 \ + --hash=sha256:a5afe8b99e3b20cdc9133be7a1b381259acf0d470029f6b2fc79c3f9947ad436 \ + --hash=sha256:a8cc4681d45196645b0567cf7259730fcb0edba751ecad8c4c3a6ccc63fabe24 \ + --hash=sha256:b3ab26d158a096c750759981f75f279f6568d945b8fe640e0cebfdff8d601044 \ + --hash=sha256:b895230ad67a7b9a2c7dfff7c7c9801570a9dfe71f7e383013f831d35b74ea2b \ + --hash=sha256:b97ee10456e65f166c030b5fbde8380ca508621e23b84efd77aad83afe99315a \ + --hash=sha256:bdc2d273b7656d862c7295d2dab45987a2c22197d60f1cad14c8b86c5af0b452 \ + --hash=sha256:c71e619d9965375241566beda8358031fdce8cb136310d4f5b71faa88f278ad1 \ + --hash=sha256:cbe509350d66249fc9e65ece4e7b1855d650f18e477721887128452b95c44b24 \ + --hash=sha256:d5c50b755d781efebe4f9c70ee6d00858e44d95f0229b8b5174358f861d9bd7c \ + --hash=sha256:d666690f0a53ae3df4be2c18af6820369a74c9c5b8960e8760f0f8c9a8f15b36 \ + --hash=sha256:e23ea15cebe5f5be5d11005043d7b5ff56848e39499681ef52f8227bd385bd51 \ + --hash=sha256:e956fa8a953b7eb8580658d68cdf37e410356032c17697a06e44d0e8c4084a0f \ + --hash=sha256:eae71e1c5944ade976ce8cd1e780bed9b5c31d6d4f89cb7263e8dcff78f6cd8e \ + --hash=sha256:ebfb22202530863aaf00ebb9352fd17cdd1066aae50986006d76b10c1e8506e9 \ + --hash=sha256:ed098bea084f8d626d91facfebdc8fe49115b96a339465db6fea681635fc7440 \ + --hash=sha256:ff634b5edbbf76c56ae81397aa41500a66ad0aa48b1856dcc0cf31b159172dc0 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +openai==2.52.0 \ + --hash=sha256:7c736d592f81471ce1f734838390983c4d8c8aecff23dcd36e600a58e5032d9c \ + --hash=sha256:f97e231d9a8fa69ab55897df1080f02d99913fb0a30e3ee56ea16a1eb6c2d434 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # litellm +openapi-pydantic==0.5.1 \ + --hash=sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146 \ + --hash=sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim +opentelemetry-api==1.44.0 \ + --hash=sha256:67647e5e9566edcf421166fdf022b3537f818635daa852b289e34604dc6fb33a \ + --hash=sha256:94b98c893a91b88657eaac1e3ba89618cdb85be6918196705354f34728b2cdef + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona + # fastmcp-slim + # opentelemetry-exporter-otlp-proto-http + # opentelemetry-instrumentation + # opentelemetry-instrumentation-aiohttp-client + # opentelemetry-sdk + # opentelemetry-semantic-conventions + # pyqwest +opentelemetry-exporter-otlp-proto-common==1.44.0 \ + --hash=sha256:9a9fe61bba73d802904bc989f1d6b4a7b1ee40f06c40e98d6f85af65aaebb694 \ + --hash=sha256:dc87a5a5bc58f149a56d1547e4691588fa12994cdc3bc039a694ccb3375862ac + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # opentelemetry-exporter-otlp-proto-http +opentelemetry-exporter-otlp-proto-http==1.44.0 \ + --hash=sha256:838592fce774c1c8bb7b9a0a7facbfa82e17be5a8a4e94cef10cb84ae026bae3 \ + --hash=sha256:c633d7270ad6b57cd4cfbe8b0007a9e2e7c0cb50bd6c50fe2a7b245f721a09d8 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +opentelemetry-instrumentation==0.65b0 \ + --hash=sha256:071d9d9eced9bd6460444ec3b0c77229870ed05a881c22c84fdede58e4eed09b \ + --hash=sha256:ea967a72b9939b5fcfdad572753b4306c59dcb99e3f382d95dae04286805e137 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # opentelemetry-instrumentation-aiohttp-client +opentelemetry-instrumentation-aiohttp-client==0.65b0 \ + --hash=sha256:3a060efa53fa44d02ba7372a7ed2b42cdfa6be6df81b089845067ad840e25729 \ + --hash=sha256:85906a2806ee5641756b5c33274e9aa75c3cc2441e3b830aa5804cf0e1fa9dd1 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +opentelemetry-proto==1.44.0 \ + --hash=sha256:898b155a0e1557afd867478fb6158e8122a46329ca0bb8dc53cc55e98f017f56 \ + --hash=sha256:c547a79c2f8c0c515d31509154682e5921c7cfd5ca67b70e1f9266e2c3e103f3 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-http +opentelemetry-sdk==1.44.0 \ + --hash=sha256:cebe7f65dc12f26ead75c6064de12fd2a9052e5060c0272d402cfa203aae123b \ + --hash=sha256:df081c4c6bcfdb1211e3e86140376792643128a25f8d72d1d27675936e7e96ad + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona + # opentelemetry-exporter-otlp-proto-http +opentelemetry-semantic-conventions==0.65b0 \ + --hash=sha256:1cacde7b0ad306f84c5ef08c3dbe1bbaf20165bba6f8bff43b670e555a086bcb \ + --hash=sha256:f9b2b81e9d5b64f11bc952075e7e9c7fb0aab075c7fd1c46d597f1b919852d60 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # opentelemetry-instrumentation + # opentelemetry-instrumentation-aiohttp-client + # opentelemetry-sdk +opentelemetry-util-http==0.65b0 \ + --hash=sha256:7553b606f963097cb190536dc30556cce85090692e471a422fff30ca29b04348 \ + --hash=sha256:84f82d826978bba416ab453460ff6a7391cdc3534c93a786595e4068680016b7 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # opentelemetry-instrumentation-aiohttp-client +orjson==3.12.0 \ + --hash=sha256:010811c1b69773450a01cef97727a67b223242f350b77d4ca000e59a9ef2155a \ + --hash=sha256:01efac2074fffb4cb1ea3fab7861e9d0f2a26913854a972f5ac760525dbdaf6e \ + --hash=sha256:03091c8a64db4be38746597ceea68f33c238e27acd9bfe99fb59420224ae7a55 \ + --hash=sha256:08231552159be266a7269555bd9f7c016aee7d9ad6dab06eb58796c5ccb7101c \ + --hash=sha256:0b1ac5bf6609b2716c7954011c5fef6254922df029f45d032ee4ebf5d363cbed \ + --hash=sha256:103b5db66aa53c1f9e88c2524be4f383e831ba7dfd5f9f5af6336a177c622f11 \ + --hash=sha256:1192a7021b6d071aaf909864f6e924d6a2675ca360485b972b8401749311750b \ + --hash=sha256:11edb4660a6680abee9788a3a9072208a2c96538cc1322bd79542065229d8e54 \ + --hash=sha256:18a87929f31d94a77f7dc93cf527e91f39ce7fe7813d588a4de2507efd32a387 \ + --hash=sha256:1c680706fc8396d95e7c4c1f9482563f552137aef91b57237a3ad5aaf64629df \ + --hash=sha256:2b7bcefb9f40fa242fa6b06377232c048e655747790829609168c01162f60578 \ + --hash=sha256:2bb3ce43203936072dd8b4917b01d3aecfc02329bfb42510cb7cfb24708adc9c \ + --hash=sha256:2d3a9da945a4d96ae758fdaaca56742e6b73b6fd554c5d8876f252a6dad70b83 \ + --hash=sha256:2eb5c56e534127b2b8fa38d2363c8b1b8190367ee0d1d16c041517d880843b94 \ + --hash=sha256:31ed278a36304390adc3eec5d7f6fd593a7c3e99e5a06cd07866396c4b1b4710 \ + --hash=sha256:33efefcf5d88eaf400b47e2eba02f91f319bb9951be61ca500b7d536d3f2079d \ + --hash=sha256:3bb17a06f9bd15237b3216c044209fe92597379124018cfc196fbb846cde64df \ + --hash=sha256:3dbce9b6b3074b31a5d5dd322a9c4e5b16f206091ece4194c2e36952847a105e \ + --hash=sha256:40f92192227505acca4e2533ce565f8e6b9535f7d0d09b0968452f18b7376b38 \ + --hash=sha256:477ecaf6b9f88f873341b91fcc736119ca81b5e002a9f7f308ff5b4f2ce2a70e \ + --hash=sha256:50fae885cb073eac7556353ff3df93312b0d5137b0a5056b2bb63f97ed9a93c7 \ + --hash=sha256:532ff8cd4bd59a327a953a7dcde922c7fc25b85e29721bb8633265430d3a3873 \ + --hash=sha256:53c0c474a9d9aff9aebfc0c88de1f28f843d940e6e3a80729abdf6a20274356f \ + --hash=sha256:58c58e1de0006ffb580368d6793c36c7b0b021db066479cf281bf5061e732328 \ + --hash=sha256:5a0fdbc216388f653d3752ff310e710f59253bd4ed6a2bfb3f4f06b84714bbd8 \ + --hash=sha256:61318b6de893c7a9d9f3e5ecbadccbfc26a7eb417ccc7bbf0771de3b4d72f868 \ + --hash=sha256:644d005bc82f917337a95ce270c9f6f92f9834c2bed7b1477572f8db00784222 \ + --hash=sha256:6a2a79c89984dc719817d388c8709e0efc2a2795a934eaa746b4882eb6045adc \ + --hash=sha256:6a31348d7dfa64cd9c78bd1f510ff44c48fe64d71094e6b90e364dba3b55949e \ + --hash=sha256:747843254519dd43b93eee3153a19e5a509334320c4d2f823ec879232db5c796 \ + --hash=sha256:784106539f4b9d4b930e0b4eb8d45168507dae001945e71b4675a367f1e5e806 \ + --hash=sha256:7c2ad193c8004254f34b499f3bd2c80f043d10754aff2b38f93da574f4883f98 \ + --hash=sha256:83445adc40cba26d6d621185a45128ce455b766af368cad2ab64b970603a7978 \ + --hash=sha256:859fc4196855890150bb08e649b30d2c93b249b3e3edd0d3bb2231abf8aa8adc \ + --hash=sha256:8c3bb86dd10f39b3fbf434b7d5dc7cac77d6fc8ac572ae30a10731ede2c4b647 \ + --hash=sha256:8e29957429c35bbb5a185a119c523aa2428b7bbf1a293724c7b9375ed8f892a3 \ + --hash=sha256:8e386b0bc0ddd7cd2056f884b5a0af33592bd01ac66a7ca4b42a65a7e7774a13 \ + --hash=sha256:92ffc09e07233a6ab6d4e067f7841edcbcc134cb4812155cf171ea5255a421d7 \ + --hash=sha256:9a36ec60f1796f9a3f13e3b98390295e17a1c7c10155b448d264098bf9ee5900 \ + --hash=sha256:9caf3d09f47c3c70c4451ada20ef9bc4a4cdffa26f49862cf0a253b329aae2d5 \ + --hash=sha256:9e6fee342a48760e854d743e7a81534d8e2925a6f46e09f750cf56b50fd1de5d \ + --hash=sha256:a15f9a891bce5f5cc5d210e3ad8614d4d1b489a56448c099d6d2a7168b2d954a \ + --hash=sha256:a696529ec96a90d9a5f9570207efe403c8b08f8e4aa2783ee3403511e2fdfa10 \ + --hash=sha256:a6cf4b18e7de173f209f2084ffbd736dd72389a396326ee80a7022168be232e5 \ + --hash=sha256:a791f793b287bbc135b8e87c34e35c8bfc693e2a8a620fab1ae682b925f9a32e \ + --hash=sha256:a94f0f0c6fcbb2b5bd9734c57a489c7584a732bbdf04a39e8c83b861e9d03e92 \ + --hash=sha256:aa3e43a6846e91d7bde3d5a9c66090fcd8744f569a9b6cffc5e1ca38f6a461c0 \ + --hash=sha256:ad0422b92d5195443a39f80c3bcf731cc2e00f153bd32063a47b73b057bd0f03 \ + --hash=sha256:ad29eece0c601737f2a60edc2752a84e7a0785df3efb62e3012834700a5afe0d \ + --hash=sha256:b85931be5b6763c31283805c9bdaae1ca03ad9f6f12a15f1cbf6745b907932c2 \ + --hash=sha256:b9dca132b1fda5565088e65a6b6e742285e0aeceb6fae549fa8863e16c7d3998 \ + --hash=sha256:bc7a872f03522d90e0429e6c0c5cd23084f767bedcb4c58048eec19294613344 \ + --hash=sha256:bd57d79aefa3f84eec851d6de7a366795b9345cfaf17f82b4820430a7a5fa241 \ + --hash=sha256:bf44e374aadde77b1f6109f1030be51433eb61984379852766b6f4e187db7b1e \ + --hash=sha256:c6b11be792c3d2c6a4be2af4ebf97a68d0bf5f580aca6e86a418a354f6cc846a \ + --hash=sha256:d14203fb1aae2ad9b3d52f8a0e82aeb10197ef1c9bc61da7f358bd70b00123d5 \ + --hash=sha256:d39f3f5c3927e2dc0913fe5bbc1a2f6b1b9d1bba1de6358340d0ad0d0c00ca92 \ + --hash=sha256:d8e78d3d93705e3d27cc17cdb209e44d7a8ea203010cac6ce9c7ffc1ae1996f1 \ + --hash=sha256:dce0166feb0a737ab84f598c9a338cbc0b764a036617aa686194f53c7eba0c3e \ + --hash=sha256:e4ac5059baab4b3acbd99485de019ff8cda0fdf34b61fa74f7197a53db78bfe8 \ + --hash=sha256:e9683ee9ea0659da64f36574ef675b8a86330c34c19ea75db1fb93c3ff99e0ef \ + --hash=sha256:ed4ca42bd55955aa34deedcfdfd0e0c31abf51143aae158ae2bc3520b626e517 \ + --hash=sha256:f06dd838d1e07d9b1de0932ec0485ec92c4d5f5d1ad4817a656268c3e88be1e1 \ + --hash=sha256:f3c0683136acdc29afdf88a5bc2f7d3d0e34087788d1d63c0144b805a87a196f \ + --hash=sha256:fb2539159dfe8d371914f354360fa50e4a577cc89222a3828b9650a5e5040252 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +packaging==26.3 \ + --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ + --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # deprecation + # e2b + # fastmcp-slim + # gradio + # gradio-client + # harbor + # huggingface-hub + # opentelemetry-instrumentation +pandas==3.0.5 \ + --hash=sha256:08d24fe11a17dc33bd6e937dc9c665f9cba08fbdc9f657f405713515febe300d \ + --hash=sha256:0d298e951f23016ce4699951d044ae6418dbc91bf68cefca0f77666fcbb4e5c6 \ + --hash=sha256:0fac0010c75e4efb6b99e249c183a8993ce0dc95c240f9b120a5e67c727b7928 \ + --hash=sha256:1c10461f6eeb35d8f05b6184c65c8b9991663b66c46b1d559b682cb34ae7c6ea \ + --hash=sha256:25ff585b972a18ef1fe9ffa3ac6544d9950508aa76832e5147640b6022821e49 \ + --hash=sha256:2946e77e4a53cd248cbde631a12f0e51c8324ce354c3eba4d20147c1ad6f4282 \ + --hash=sha256:2a29c53d85ea98c5e792c59ef82ee9fbe6ca902c0d0adb6b23f45ef894cd7bf6 \ + --hash=sha256:2c0cf1dd9b55a22d105fc46c1b489af3bd42264fcba7c66297bf47a9a1d9c78a \ + --hash=sha256:2f264fc46911cc8131a7322a16199bbf8e353d27c10bb211f5bd0c814324dc36 \ + --hash=sha256:303da736987d481074ca720ada325f8bd80c64ebc2d45ed79b29df3aaa4a26ca \ + --hash=sha256:3b2801bbb049d0136f6c213eae02b5fca969384fc2064dd728d8620552aa49da \ + --hash=sha256:3c5015fd1730fbf883647e88068176c839c102cea883ba1769a6f4593bfc1f8c \ + --hash=sha256:3c5ed2e7c06e91d340dfd091d7934f9bc82e4a36b95f647f090b9d1c9ac649da \ + --hash=sha256:4b11c36e218331d0387cbe3a0a5f75162357a1d92d57b2b08a336ff94b19b2be \ + --hash=sha256:5183427f5a8156d480f30333777bc978be93650a49a7c01db26adffe95b31e85 \ + --hash=sha256:53730687fcd161883b24e10411c06d6a4c0f2275d2faf3bb2bc25deb4ba8007c \ + --hash=sha256:66266d3442a5e8b3c90274c2b8b230bee42dd1c286bc822cc2f9f2c7e12b883e \ + --hash=sha256:679f4e85b30ddb1515458ab1e788d3e260eae369b1f78da7a3aa4cac8ebf4a2a \ + --hash=sha256:71ecc8fb7ed1a7aa4392316b5309a6347e8e7f832f38fd897846b3a1457a9298 \ + --hash=sha256:73fa87b08a7ef706f8aafda39ddaccf2a99047bea62d8c88a0361bcafb2237bc \ + --hash=sha256:80a611068e8a3ac23f7398c6c14eb46dc974e5cc9997f653e2dcfd1da74edd41 \ + --hash=sha256:960d3ebcf249f75206899fcd2c6de53f736b7265759ced0d3e559df0b8b709b0 \ + --hash=sha256:9e94c2c5ca43bd3ca32bf64d32308887b65e5f9bfd8023ea52755107a999f93b \ + --hash=sha256:a5ad3b02ed6bc7d7ae9b70804b2c6aa31827489d150f8e623ce82491b82085d7 \ + --hash=sha256:b1261758dfb6cf12c3cff8300e21cefad30e7ec709abb4c24ac7318e6a52462a \ + --hash=sha256:b173f5951ff6b8b0ec7675e20dff3c97b7e7a57dfcce387c2d7c5afe87cb7899 \ + --hash=sha256:b2acb4650527eec6822c3dadb2b771277b65e7dae7a267d4bccf65fd1bb3fbce \ + --hash=sha256:b58b1b39d46a5862e3fb18f50d1a201398619d16a0f9f73f57eea5583cf0e63c \ + --hash=sha256:b86765f268b56f7e665b93bce9d5df69dee7f99e595cf8fb839483ab315942a3 \ + --hash=sha256:c1c05a767fe8e5b4fe9e1c29806829c582052eaedb9120a3da83ba3f69e24a5b \ + --hash=sha256:c2e26bb46934b8a2ca0c3de1d3d606fc5f6746584791b2db264d58cf370e08dc \ + --hash=sha256:c597ecf5616b5c420372c1d4d4c00dbbfba7398bea857dcc984347e1ea48417b \ + --hash=sha256:cce3a9d11d2b1f82c69a27ec1f4948a170e2c403c4bbfa8cca62e3fdebe2ef3a \ + --hash=sha256:cd8f7c6dc98527058ee6264219343f5392240a6f1bfa654fc5d79023020d0c92 \ + --hash=sha256:cf52e1f61d229496da17dc7ab54acdee627357e7008fd4fecba3d0ba2937fa58 \ + --hash=sha256:d373ce03ffd84010ed9839fa73672a9c8256990532e158440c0085db7d914b34 \ + --hash=sha256:db172144bb56422bd157812f3b021eacc255451470b31e2c633c349490a1cfee \ + --hash=sha256:dca3734d6ab7c906e6730f0788b0a1dbb9f2467731f9711f77995c8e9d62d712 \ + --hash=sha256:e2759e890db96dfcffdbd9b86c3c2cb6afaf58def482820317e06163ec1066cd \ + --hash=sha256:e819dd5f62966b481a8cb649d3299ebd886a1ea91ed5a99bf7ce77c98d18ab94 \ + --hash=sha256:ef01af4d8dc6cd2c8d6c7736f149574ef93fe043811eeb5e445f2647154b5040 \ + --hash=sha256:fa290c16964d4963fbfbc358928239cf3bd755b20e988ce944877def2f44471d + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +pathable==0.6.0 \ + --hash=sha256:6404b8b82aef5ff0fd478934137128b99b12212ba35afdde5525ca4f8388ea58 \ + --hash=sha256:82c4ca6c98c502ad12e0d4e9779b6210afee93c38990988c8c5d1b49bdcdf566 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # jsonschema-path +pathspec==1.1.1 \ + --hash=sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a \ + --hash=sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # harbor + # scantree +pillow==12.3.0 \ + --hash=sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756 \ + --hash=sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a \ + --hash=sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59 \ + --hash=sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45 \ + --hash=sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3 \ + --hash=sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df \ + --hash=sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139 \ + --hash=sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b \ + --hash=sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39 \ + --hash=sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e \ + --hash=sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8 \ + --hash=sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1 \ + --hash=sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8 \ + --hash=sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89 \ + --hash=sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5 \ + --hash=sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130 \ + --hash=sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd \ + --hash=sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d \ + --hash=sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b \ + --hash=sha256:25b9b82bb22e6e2b3cd07b39c68b7b862001226cb3dff7130d1cb914121b39ed \ + --hash=sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace \ + --hash=sha256:300557495eb45ebb8aec96c2da9c4be642fbf7cd937278b4013ba894ea8eb0eb \ + --hash=sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931 \ + --hash=sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510 \ + --hash=sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6 \ + --hash=sha256:37dc8f7bbb66efe481bb60defacef820c950c24713fb44962ed6aa2a50966de1 \ + --hash=sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce \ + --hash=sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385 \ + --hash=sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e \ + --hash=sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c \ + --hash=sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7 \ + --hash=sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace \ + --hash=sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c \ + --hash=sha256:514435a37670e3e5e08f3945b68718b6ed329bb84367777e16f9f4dfe1e61a0f \ + --hash=sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64 \ + --hash=sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f \ + --hash=sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a \ + --hash=sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827 \ + --hash=sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17 \ + --hash=sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4 \ + --hash=sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a \ + --hash=sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701 \ + --hash=sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e \ + --hash=sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91 \ + --hash=sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66 \ + --hash=sha256:85f998ea1848bc6757289e739cfbdda3a04adfd58b02fc018ce54d754a5ce468 \ + --hash=sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217 \ + --hash=sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658 \ + --hash=sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418 \ + --hash=sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a \ + --hash=sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c \ + --hash=sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330 \ + --hash=sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402 \ + --hash=sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09 \ + --hash=sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930 \ + --hash=sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f \ + --hash=sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec \ + --hash=sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a \ + --hash=sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94 \ + --hash=sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468 \ + --hash=sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b \ + --hash=sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965 \ + --hash=sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8 \ + --hash=sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd \ + --hash=sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7 \ + --hash=sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c \ + --hash=sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777 \ + --hash=sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35 \ + --hash=sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9 \ + --hash=sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f \ + --hash=sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f \ + --hash=sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0 \ + --hash=sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c \ + --hash=sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71 \ + --hash=sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3 \ + --hash=sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838 \ + --hash=sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf \ + --hash=sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321 \ + --hash=sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26 \ + --hash=sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec \ + --hash=sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9 \ + --hash=sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65 \ + --hash=sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5 \ + --hash=sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e \ + --hash=sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d \ + --hash=sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198 \ + --hash=sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +platformdirs==4.11.8 \ + --hash=sha256:52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081 \ + --hash=sha256:f23abafea7dd4276d1f29104b83598d7dcc567cafd07c9c951e66665645437fc + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim + # harbor +postgrest==2.31.0 \ + --hash=sha256:2f395d84b2ee34fc57622ff2f711df603e2ede625f98e5015240741888f7bd0c \ + --hash=sha256:c2fd47c94e13ee8335111c4f03c9a24ea9766ce9d35fc3cd7330057c9e7ea0c3 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # supabase +propcache==0.5.2 \ + --hash=sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427 \ + --hash=sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5 \ + --hash=sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa \ + --hash=sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7 \ + --hash=sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a \ + --hash=sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0 \ + --hash=sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660 \ + --hash=sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94 \ + --hash=sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917 \ + --hash=sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42 \ + --hash=sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3 \ + --hash=sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa \ + --hash=sha256:1ca071adabaab6e9219924bbe00af821f1ee7de113a9eca1cdc292de3d120f4d \ + --hash=sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33 \ + --hash=sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a \ + --hash=sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511 \ + --hash=sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0 \ + --hash=sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84 \ + --hash=sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c \ + --hash=sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66 \ + --hash=sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821 \ + --hash=sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb \ + --hash=sha256:2f8ea531c794b9d6274acd4e8d2c2ebcac590a4361d27482edd3010b79f1325e \ + --hash=sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853 \ + --hash=sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56 \ + --hash=sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55 \ + --hash=sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6 \ + --hash=sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704 \ + --hash=sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82 \ + --hash=sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f \ + --hash=sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64 \ + --hash=sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999 \ + --hash=sha256:4621064bbf28fa77ff64dd5d94367c04684c67d3a5bf1dff25f0cd0d98a38f3b \ + --hash=sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb \ + --hash=sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d \ + --hash=sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4 \ + --hash=sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab \ + --hash=sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f \ + --hash=sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03 \ + --hash=sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5 \ + --hash=sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba \ + --hash=sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979 \ + --hash=sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b \ + --hash=sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144 \ + --hash=sha256:5fcb98e7598b1ee0addab320d90f65b530297a867dbfe9de52ea838077e16e3d \ + --hash=sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e \ + --hash=sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67 \ + --hash=sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117 \ + --hash=sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa \ + --hash=sha256:6bf3be92233808fcd338eba0fb4d0b59ec5772af4f4ecfcec450d1bfc0f8b5eb \ + --hash=sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96 \ + --hash=sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5 \ + --hash=sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476 \ + --hash=sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191 \ + --hash=sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78 \ + --hash=sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078 \ + --hash=sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837 \ + --hash=sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a \ + --hash=sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba \ + --hash=sha256:8114f28879e0904748e831c3a7774261bd9e75f49be089f389a76f959dcd13fe \ + --hash=sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c \ + --hash=sha256:823581fd5cb08b12a48bfa11fe962a7916766b6170c17b028fbdf762b85eb9bf \ + --hash=sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c \ + --hash=sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9 \ + --hash=sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8 \ + --hash=sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe \ + --hash=sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031 \ + --hash=sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913 \ + --hash=sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d \ + --hash=sha256:949c91d1a990cf3b2e8188dfcfb25005e0b834a06c63fa4ef9f360878ce21ecf \ + --hash=sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f \ + --hash=sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539 \ + --hash=sha256:a0e399a2eccb91ed18721f86aa85757727400b6865c89e88934781deb9c8498b \ + --hash=sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285 \ + --hash=sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959 \ + --hash=sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d \ + --hash=sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4 \ + --hash=sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f \ + --hash=sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836 \ + --hash=sha256:b05d643f944a8c3c4bd86d65ffd87bf3264b617f87791940302bc474d2ff5274 \ + --hash=sha256:b96db7141a592cbc968daf1feea83a118e6ab378af4abbc72b248c895414c22d \ + --hash=sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f \ + --hash=sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e \ + --hash=sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe \ + --hash=sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1 \ + --hash=sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a \ + --hash=sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39 \ + --hash=sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7 \ + --hash=sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a \ + --hash=sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164 \ + --hash=sha256:cc1177027eda740fdb152706bd215a3f124e3eea15afc39f2cb9fe351b50619e \ + --hash=sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2 \ + --hash=sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0 \ + --hash=sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0 \ + --hash=sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335 \ + --hash=sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568 \ + --hash=sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4 \ + --hash=sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80 \ + --hash=sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2 \ + --hash=sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370 \ + --hash=sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4 \ + --hash=sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b \ + --hash=sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42 \ + --hash=sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a \ + --hash=sha256:decfca4c79dd53ebab484b00cc4b6717d8c369f86e74aa4ca395a64ac651495e \ + --hash=sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757 \ + --hash=sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825 \ + --hash=sha256:e4294d04a94dcab1b3bccd8b66d962dcad411a1d19414b2a41d1445f1de32ad0 \ + --hash=sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27 \ + --hash=sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf \ + --hash=sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f \ + --hash=sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d \ + --hash=sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366 \ + --hash=sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc \ + --hash=sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c \ + --hash=sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7 \ + --hash=sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702 \ + --hash=sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098 \ + --hash=sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751 \ + --hash=sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e \ + --hash=sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # aiohttp + # yarl +protobuf==7.36.1 \ + --hash=sha256:0b53ce95272aad50ad25d7ff03373743209822e8ba42ea7fad27d2bee1547d00 \ + --hash=sha256:39c518c05586c016d7874ff6079ee115bcec1ea5fbb1d177fbf7867ef4c67e44 \ + --hash=sha256:3cf2ee25d006cee57294a1196ea43b37feb78e0dcd1e8af5c1aeddb777655aca \ + --hash=sha256:43d3d37b1eb24c113b9b7d02008cac44e423f00b611b7781ae998d7623972969 \ + --hash=sha256:51139351435d9b43d88a55eaa49fb6f737fbb478fb0cbf2cf694d1a04a9d3363 \ + --hash=sha256:7d951e46b3f963d6c264c367c437921de9d5aedd9c3f9612b9077736b4e3ad5c \ + --hash=sha256:97198b77e369a0abd8e262b8f6c7266c55ddb796a3a12c76d7b8881188ed83aa \ + --hash=sha256:d0f6470f0ce2b84e3feaea2d4b816378b37ba4d4aa08a274305373de93e2d524 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # googleapis-common-protos + # opentelemetry-proto +protobuf-py==0.1.1 \ + --hash=sha256:6bd08ac4d8f1661965bbe2685429d79043704cdd1ee720a7a89617331742240b \ + --hash=sha256:efc4f50f275ed6dae10a1f30bb81ad1a75368557b3ff22a532b7a472050368f1 + # via + # connectrpc + # e2b +protobuf-py-ext==0.1.1 \ + --hash=sha256:10992141a8282a71ac3e3530d1a489efb27618d84000b9a47918cf70e5816d9b \ + --hash=sha256:1610865622e2e27568277ca63d8d2d23dcc55eaa767398865c21539ddf4ce24d \ + --hash=sha256:182798e4861aba72d05855bd06febe4926aa7265e6f444a1b8af5252beee4f7e \ + --hash=sha256:21572764f625d829604fc4d83635f533f35775f11c6da142345b3f3c8d64bd09 \ + --hash=sha256:2a4cc478eef7a2acc1daaebcde479a3ac2396d47e6bdc7e776ca4c4147ba8b4c \ + --hash=sha256:310039e03cb15181781a0b78017419f6d4ee302e988c3c70b87f1facdf05532d \ + --hash=sha256:359dccdc1c3eafed2a913c570bb082b8df848a1d27d548ce8385f246a7d68be2 \ + --hash=sha256:4411ffe0e06a774b83c5c71c546ce097640a25f596c45f95f53d3e3148e3f22d \ + --hash=sha256:53a6b3590f6aa7f97b8ed60f62fef9b096babfeae82f7283fd3a4c405827d4f8 \ + --hash=sha256:55a17d80ea419501ff221a6627523f38a431bb33e6aa3de81ae3a7f271c49c75 \ + --hash=sha256:6b0c615c48e95acc53cf33e9310eeaff8b30d2d7555bf93e7bca8fb4f40e9a5c \ + --hash=sha256:72956cd0af5dee24b41c6f5ba5e42622d17e6d555002b5efc1634e27a1446de2 \ + --hash=sha256:79eee3bcbb289d6ea114eb8fe3a1469c5b59bf53e207238469f567d9c53ba56f \ + --hash=sha256:8995efba9476e1ea18ef9873306871dba697308994bc2766558fec3387acc3de \ + --hash=sha256:89ec8348d1ba045f79b2fedd14e40cca36f2a41b52f2c4fdf55a60c58add2353 \ + --hash=sha256:8a4adc65ab6a5e4885c67fc808bcacb83d755d05b566d312a0e10c2a873f2ad4 \ + --hash=sha256:91c38b4a10a306366443273ee03ca554537a0965bf05b7ada8e7dbdee04cc93e \ + --hash=sha256:9a9c2f026096ca4c595c89a297067ef371e241d3ff8e1f6d7c779aa8419cdca7 \ + --hash=sha256:9dbb518b5638403ceaa08ac4fc7dac626f45ed9b856b3517de3906cf3de4d632 \ + --hash=sha256:9eb25c3a329c0551cc86b209a5e5d8ecb8d834b9924a3aa019377853a703b6d3 \ + --hash=sha256:aaecbde82bef10c7c40578cbb61b7a19896bf7fa450972050a3bb302acb7d5d6 \ + --hash=sha256:ae4373845cbb85bdade3ef5368cc2f4b5f80bf173383afc1ab063f95644e5599 \ + --hash=sha256:ba61d49dace8f874361583030a7c48139b42eb37c9ffbb1e7e8a227a51576f44 \ + --hash=sha256:c6f0cd58620f415a3534d195358338f4999a774e12510f91c592b38d13d37388 \ + --hash=sha256:cf78707a040b9294e5e1ec4a1875f0046acfe52e92150cea27de4e9fc9db39bb \ + --hash=sha256:e7f14f00c2678bfbe7ad46f057b9f9938c1677bcf39e405e163e272c6f9814b8 \ + --hash=sha256:e85bfdfdb3ed50634db8ccc7429dd9286520109489c735463971a418707b4fef \ + --hash=sha256:e97f45c49676efacfb8e95bfcb1a002bc337e618a6780be1e55df2ba5ebd2f1e + # via protobuf-py +py-key-value-aio==0.4.5 \ + --hash=sha256:ab862adbcb8c72547d1c57821f22cbbb71ab86509039c96f36e914e0336c8dd7 \ + --hash=sha256:c6563a2c6abe5da5e20f4f9e875c2a9b425a2244a54fadbf46cf140a9eea45d7 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim +pycparser==3.0 \ + --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ + --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # cffi +pydantic==2.13.4 \ + --hash=sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba \ + --hash=sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # daytona + # daytona-analytics-api-client + # daytona-analytics-api-client-async + # daytona-api-client + # daytona-api-client-async + # daytona-toolbox-api-client + # daytona-toolbox-api-client-async + # fastapi + # fastmcp-slim + # gradio + # harbor + # litellm + # mcp + # openai + # openapi-pydantic + # postgrest + # pydantic-settings + # realtime + # storage3 + # supabase-auth +pydantic-core==2.46.4 \ + --hash=sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0 \ + --hash=sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262 \ + --hash=sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda \ + --hash=sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0 \ + --hash=sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e \ + --hash=sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b \ + --hash=sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594 \ + --hash=sha256:10e17cbb10a330363733efc4d7c4d0dd827ac0909b8f6a6542298fed1ea62f29 \ + --hash=sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2 \ + --hash=sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c \ + --hash=sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d \ + --hash=sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398 \ + --hash=sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d \ + --hash=sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3 \ + --hash=sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f \ + --hash=sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb \ + --hash=sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7 \ + --hash=sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5 \ + --hash=sha256:228ee9bae8bef5b1e97ec58302f80357c37199e0d0a99174e138d28e6957b9d9 \ + --hash=sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462 \ + --hash=sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4 \ + --hash=sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b \ + --hash=sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d \ + --hash=sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df \ + --hash=sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2 \ + --hash=sha256:3447661d99f75a3683a4cf5c87da72f2161964611864dbbeac7fbb118bb4bfc0 \ + --hash=sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519 \ + --hash=sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd \ + --hash=sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7 \ + --hash=sha256:3be77f45df024d789a672ae34f8b06fb346c4f9f46ea714956660ea4862e89ac \ + --hash=sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6 \ + --hash=sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565 \ + --hash=sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898 \ + --hash=sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb \ + --hash=sha256:432c179df7874eeb73307aad2df0755e1ae0efa61ff0ea89b93e194411ae3928 \ + --hash=sha256:4a05d69cba51d852c5c3e92758653245a50c0b646ced0cf05bd793ed592839d6 \ + --hash=sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3 \ + --hash=sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a \ + --hash=sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596 \ + --hash=sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987 \ + --hash=sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e \ + --hash=sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d \ + --hash=sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712 \ + --hash=sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008 \ + --hash=sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd \ + --hash=sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1 \ + --hash=sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be \ + --hash=sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea \ + --hash=sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292 \ + --hash=sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33 \ + --hash=sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3 \ + --hash=sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4 \ + --hash=sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b \ + --hash=sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826 \ + --hash=sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac \ + --hash=sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7 \ + --hash=sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d \ + --hash=sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf \ + --hash=sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4 \ + --hash=sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc \ + --hash=sha256:8b9bab013d1c7a79d3501ff86d0bc9c31bf587db4551677b96bec07df78c6b15 \ + --hash=sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3 \ + --hash=sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b \ + --hash=sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914 \ + --hash=sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04 \ + --hash=sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c \ + --hash=sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b \ + --hash=sha256:91a06d2e259ecfbd8c901d70c3c507900458498142b3026a296b7de4d1322cc9 \ + --hash=sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce \ + --hash=sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4 \ + --hash=sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a \ + --hash=sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f \ + --hash=sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424 \ + --hash=sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894 \ + --hash=sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9 \ + --hash=sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76 \ + --hash=sha256:9f444c499b3eefd3a92e348059471ea0c3a6e303d9c1cec09fa748fd9f895201 \ + --hash=sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb \ + --hash=sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109 \ + --hash=sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4 \ + --hash=sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848 \ + --hash=sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526 \ + --hash=sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0 \ + --hash=sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01 \ + --hash=sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458 \ + --hash=sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e \ + --hash=sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba \ + --hash=sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a \ + --hash=sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39 \ + --hash=sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c \ + --hash=sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000 \ + --hash=sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b \ + --hash=sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf \ + --hash=sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4 \ + --hash=sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd \ + --hash=sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28 \ + --hash=sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9 \ + --hash=sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30 \ + --hash=sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983 \ + --hash=sha256:d80ee3d731373b24cebbc10d689ca4ee1875caf0d5703a245db18efd4dd37fc1 \ + --hash=sha256:d995260fdf4e1db774581b4900e0f832abe3c7c84996726bbc161b19c8f29e76 \ + --hash=sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5 \ + --hash=sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4 \ + --hash=sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7 \ + --hash=sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c \ + --hash=sha256:e68b7a074f65a2fd746c52a7ce6142ab7006074ac269ace0c25cd8ba171f8066 \ + --hash=sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3 \ + --hash=sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02 \ + --hash=sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89 \ + --hash=sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50 \ + --hash=sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76 \ + --hash=sha256:f13a646d65d09fbf1bc6b3a9635d30095c8e7e5cc419ff35ecc563c5fd04cd49 \ + --hash=sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b \ + --hash=sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d \ + --hash=sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7 \ + --hash=sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4 \ + --hash=sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c \ + --hash=sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e \ + --hash=sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff \ + --hash=sha256:fd8b3d9fd264be37976686c7f65cd52a83f5e84f4bfd2adf9c1d469676bbb6ae + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # pydantic +pydantic-settings==2.15.0 \ + --hash=sha256:0ba092c291c94baceb5eff768aa0d56400a457585bc0175925a5a5510303da42 \ + --hash=sha256:694b793e84f766ba76a90ebdefc01d0a9a045dab0382bee70393da93712ad117 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim + # litellm + # mcp +pydub==0.25.1 \ + --hash=sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6 \ + --hash=sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +pygments==2.21.0 \ + --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 \ + --hash=sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # rich + # rich-rst +pyjwt==2.14.0 \ + --hash=sha256:77283c83fb56ecf566a886c757a714bc83668e38156de2cce8263302f42e0b86 \ + --hash=sha256:ad0cef71c756a56e74863c2919cf0985f72decbcfcb550ee2f422e7c62b5eedc + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # harbor + # mcp + # supabase-auth +pyperclip==1.11.0 \ + --hash=sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6 \ + --hash=sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim +pyqwest==0.7.0 \ + --hash=sha256:005ddd2a777d30ca7ce4de12df1336760e14d46394ab56299a9e81626d78b991 \ + --hash=sha256:02fd606a35be7803a770071f642a375e55df4c2025e49b10b859430f424c4492 \ + --hash=sha256:02ff96a7746de208a4b8c78abe780c413d66576146a4ec64efd348b0c8a8328c \ + --hash=sha256:03da0797bac5c2eb1a40f02afccd4b5aad5ad0d375bb993df2f5e0c692fc0c6c \ + --hash=sha256:0a826f1b492a7497f469c8467bd51faf582733654bb2bc9fcdd4fa502f3e6ca1 \ + --hash=sha256:0d11128ccc382f64fcdf92d3bce6be7191489c1b3a9a3f0dbdf89e55451638de \ + --hash=sha256:0f82c971810695f5fd7962859a2469616c83b7aca6664d8f147287ee5ff430cd \ + --hash=sha256:196aa73fb7eee4b6c052d6f4172a4321904a7208331f4322ccee3ee7d0adbc78 \ + --hash=sha256:1bc2c569481ade1bc0b89b07daf1b49b20a1337a53207ffe0ef325260f509404 \ + --hash=sha256:2159c7e2eaf2563cdadfda17314e2b1747c30603979bf73db8daef311247db82 \ + --hash=sha256:2ea1ce4d630c92cb2cc6b3bf91ada0053b051dfd5c0662d89957ebd829cfdcce \ + --hash=sha256:31176abbbcbe6d03740967b5c0241deaa60b328792644cb2e317a34d6b076433 \ + --hash=sha256:37623b492aea7ccd3b6cfe3179110643d28c2cdc83c765aac3d207b4321995d2 \ + --hash=sha256:418e8c3a67ca6226bfb6147b8668203c74d9c872657117086f4c264a674d7980 \ + --hash=sha256:4988fa1c368072886dce48f52fcbabe9f25784c6e189a9a6f2b42f6e9f383973 \ + --hash=sha256:4bb3cf0975ee829b6c76e1c42f01863122ce71b1a952e507e260a9d29eb9b183 \ + --hash=sha256:4e4e79187c3e4ebd07d663d8dc7a7cba865c72020332e41807426a7e3526fda0 \ + --hash=sha256:537f71f97a533fa355d02c15ed9f0cc05fb8915996cb921caa87fe7310b457ee \ + --hash=sha256:5832373c7bcccfbc3bb79c44b592e0871631139a2ef5aef771b8fe2b7bd4301d \ + --hash=sha256:5ef1bfbdca9ec9c8a7b223268f5ef8d45694da7226929454b0cb40f2e3d1ddd5 \ + --hash=sha256:72d001892ed570df1dcc489ef8b0a03bc7abd4fbdca10625c358a87e66b226ad \ + --hash=sha256:7330070c4497e564007985716ac347cb9a7500eba5c9610500653a2ce4cfe86e \ + --hash=sha256:7b2339f3b55ae179e0cb55bbd5792ae1c954d31f80de7b4dde0e95b03576b6be \ + --hash=sha256:837e18aef4490eed25b4194d49f16732ccf1abf2cdd0845e75d75d2a4428b98c \ + --hash=sha256:847e4468b5379a219b91a13dd3c92dd3b7b3d9f59af23e4c6776e663594cb241 \ + --hash=sha256:86d84871cb0a572700dece3a6c45c294721f655f3d0b85477333209b487ecef1 \ + --hash=sha256:9dc2f405803b94525ab030c01cdac7093bcc1a5da6802de3345a868a0f51b3da \ + --hash=sha256:a599ddac7ded32ed62d15ca90bc9c77652ba34b225cf17a404821cec92a189fa \ + --hash=sha256:a807835c6a0777f0cc57c321c78c7bf162f6354698844d78db6e03ba9edd83dc \ + --hash=sha256:ac65f2243f3e814e7f4aad3f2fcfe78f89aad2de2a825eaaabf4c102f1937843 \ + --hash=sha256:bebc51e3c9c6339d81964c6e60516a5f5c9fe793c82da57ef7c2d87a55741829 \ + --hash=sha256:c002e0fc31a96f1c47986299710f49ed4551e4a912a7cdb69aba6fd94db6d544 \ + --hash=sha256:ca02089249c292ab9c148fa86c06927701897090226a13e392a6ec53312380ea \ + --hash=sha256:ca19e96b5e902a6d35e18cee7f5e90612fca6ab169378d42242cdcb638578cee \ + --hash=sha256:d1c71327c323a19dc90a0dafb1d68fb13f4f775a2498a26bf95f17165e64da9f \ + --hash=sha256:da3bc117c1380e9577994da15b8236f7c3bb400111d6be4b57a9b2e4f30c9a79 \ + --hash=sha256:da43c7e86bee9e74ff474d4829cd398fb9220ff0d84d633094ea6797fdfe03a1 \ + --hash=sha256:dfb61138c802c7317d840a274fa24f9d878301f693268e9186a9896452017a71 \ + --hash=sha256:e1548708dbeb29a60db199c70b9e93733d6b334e7efa1dfa5e3846a4f890db6d \ + --hash=sha256:e21102f9c13234a0066d42bb5429dc5c311d7fd99401878837d9675a7f6e03b9 \ + --hash=sha256:e4c2f6a6830becfa1c5bb94aa169ad854da5749a95a440aee759932d5965c213 \ + --hash=sha256:f6fdc0601590bdc547007828627082f0dc0e445e92d900dccb227e51898d7243 \ + --hash=sha256:fd2e9bfa81198db7023202962a94920986a9a3dd12fe2f9d310f0cfcdddc092c + # via + # connectrpc + # e2b +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # botocore + # daytona-analytics-api-client + # daytona-analytics-api-client-async + # daytona-api-client + # daytona-api-client-async + # daytona-toolbox-api-client + # daytona-toolbox-api-client-async + # e2b + # pandas +python-dotenv==1.2.2 \ + --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ + --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # daytona + # fastmcp-slim + # harbor + # litellm + # pydantic-settings +python-engineio==4.14.0 \ + --hash=sha256:9f0fe275fb7d67bfc1a632421adf22949fd4843bd9c458c004b0a89cede302a2 \ + --hash=sha256:eaa1e386baf9c2c7959eef7f9d9165c5ea910c5b392f5316e78d29ed073cb43d + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # python-socketio +python-multipart==0.0.32 \ + --hash=sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e \ + --hash=sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona + # fastmcp-slim + # gradio + # mcp +python-socketio==5.17.0 \ + --hash=sha256:b5826fd2f8aa02e11347816349b74ac6b53e8a4f4e4b1cf1388e1aff19b7f3f4 \ + --hash=sha256:c3bbfc4937dcfea7c4d1b182afa94d4a30335d153987e8f2078b344beacf95a0 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona +pytz==2026.3.post1 \ + --hash=sha256:2211d3fcf9a797d3405cac96ac7f61d80e6a644f72a3309607282fe8a2010c5d \ + --hash=sha256:dd95840dd199baea12d9cc096a1d452caa6596a1c1e4b5f3dbd1541855d5e815 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # fastmcp-slim + # gradio + # harbor + # huggingface-hub + # jsonschema-path +realtime==2.31.0 \ + --hash=sha256:9e641cb4d77ca0fe768515f8cf9f83550c79f49ce1550a95afc2dc0e252be8c9 \ + --hash=sha256:f6e494b53d6a6e80b6efcee6711c8dd40413a52e766271de1bce8ced6c36cc1d + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # supabase +referencing==0.37.0 \ + --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \ + --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # jsonschema + # jsonschema-path + # jsonschema-specifications +regex==2026.9.10 \ + --hash=sha256:030fa9e23624e39b3b94e46b90a5abd1a1678eb2f58fcdd3fd6c27526bf91c7e \ + --hash=sha256:032da15431c890d376f53547f0a6219f4f4cd19f3e4f11bdc321453b5bd207e4 \ + --hash=sha256:044bd4639b6bb409ec9e5d8b7accd57e02b4c4a4e2eafde916f8ae8006b3e40b \ + --hash=sha256:048a89ee797db10160bd2bd519286577a6b43a100279bd4b7d8456a3d69c80a0 \ + --hash=sha256:05fb018cfe7144585fc83882405906ff84994a2d154afc2509ecc7752c51f864 \ + --hash=sha256:07b45ba5c94b8fcb30cb6c56a11f715c57533a3017964504322ea52690a27b72 \ + --hash=sha256:0aa7589394230e0f0a422ab6b90841ff12c87e855e7aaf75d192a54a5f124548 \ + --hash=sha256:0acee94b480dd853e39434aa9a575f95385b1b4b8fa3feae56db363ca5cad782 \ + --hash=sha256:0b9ba3b2765cdfe18f0f561a69f78a69701f2896654a81c711108d35d14e5099 \ + --hash=sha256:0c32480f3371b75068decaf9e5da72c224e953830dd71e36e06cf80e30ea39d8 \ + --hash=sha256:1270cdec69248592bbe38a0b263ed58d907b891bd2b93703e225c317e421bda1 \ + --hash=sha256:13c52fc377792675f604a207a2ae5958c080f6854f7698d40d9ff034d95b1e76 \ + --hash=sha256:14caa05ce39ec70437af5aac8814c50ee6628f4a90353871c059692f448a164f \ + --hash=sha256:1562aabd9d4eb09bd88a62ad97ed06800094b529ac43419e43020b9cefec79b0 \ + --hash=sha256:175cf49ce7a994c88b8f15e3cb17cdb66a48ebb2d36de736b8205033db950f89 \ + --hash=sha256:1aa309ab7ba89a62d6cf70dbd38d4176440bce3c7001ab86256704cf4c18c6eb \ + --hash=sha256:1ad10a135fa0b4e4a462a61d07c6654d7518cfdb5cb8da08f9ff7d61384af1fe \ + --hash=sha256:1b891f77554bff991804cee24b78b40789f7d5993a24c7907bc7025fd2a70c8d \ + --hash=sha256:1e321e2c84f0e52c457f5ea5944f796d6e8e09cb99738ea98dcc1bfe402a128d \ + --hash=sha256:1e954e246466d5a1a78f563ce8364b5d7cb19e7adb0ccdec8f9c9610083187bc \ + --hash=sha256:1f0a8b4928823bc8b217a1ab7bf3d90598909dec9a70fbbfe9a52cc4eca55990 \ + --hash=sha256:1fbc8314436353e097c050e11b01a6c11433579437ed0579730157676ef59e2f \ + --hash=sha256:20e8bfb07ad79a282f8b95b56fe67f9750b1b7f775724e4ba1f23cb296115ce4 \ + --hash=sha256:217e98ba5fc8908ed8ffd4ebac04753a0c831067cbfb495b9821b94cc61eaa76 \ + --hash=sha256:239620b0e0681669367c0e218c8eb2551d9f8fe3b9fccfc8d0003377804e8348 \ + --hash=sha256:23ac9a28180f274d7dd7651fa131ad5b02d343b75df4b040737f0356223895dd \ + --hash=sha256:2479171edccced52ef02b899558f88ab2c235fe05b93180fdcae1670aacd89e1 \ + --hash=sha256:24d12a625a37c89c2b09303402a06942f55f071b95a7916a49c17034c3d47cd5 \ + --hash=sha256:2dd9286093c71afc8f55ef035c5b9d2776641fd72c6535f1febc92d0b0be9666 \ + --hash=sha256:2e67f8843f0e4b931f1fa860bf3bbe4134b714c0155cc5c7c0d7ea450230aae0 \ + --hash=sha256:31e4df2b11d48f61d511019bc1ee9b477055f17c352b68fe72db7a98b14d603c \ + --hash=sha256:3264132d576847ab5f88bb83e7debe67854bf165b3ea613bd467312b6099536a \ + --hash=sha256:3540734dbe241ebb3b87d5713781f6749a3e4d45480f506aa5fb5cbb0c37d249 \ + --hash=sha256:35ba3bab0c45079735f55ac61526774de1d84bc4a0333cc554e1a4ab74913924 \ + --hash=sha256:3a66e40a1a20de96a2fee00ed67e11012b62d85b277688258677fd19997addb7 \ + --hash=sha256:3bdeed3318a8eb2bbadc9c56347e0ff651639e934a47e168d05a3b12929fd0e7 \ + --hash=sha256:3fb4ae8cf83ef4e9addd43b2da31a9f45be816a8036fae8af59c8998b72718e2 \ + --hash=sha256:4971776b4f2bd7fd9a83eceb2cb2592cbe2924f639fe8045e6a9de5ba4bfcf25 \ + --hash=sha256:4a761ea45f2ad74c575ef5850ea514cef97302a552d3c7c9d1a1a870d4661d6c \ + --hash=sha256:4c66d54042a14a503907d81861b8a5235e6d1f03d4fbc1d8767f652eaf957ac1 \ + --hash=sha256:4db7d00c4afbfbb55b8e17b1e371da11418ea9389b030acec63c1fa4c7ad4b86 \ + --hash=sha256:4f0407474ffac8e5e89d93ca41d60891e29f0ab8423eb66ff292d850a86a0843 \ + --hash=sha256:53e182b6b04d0011909b47d51a2d72d908de07c7b1c7f16b3adda2204d723bc1 \ + --hash=sha256:5847e22bbf959764d776937d791d034cc2d19b787e361c88d97e859e8dc68502 \ + --hash=sha256:58c01f7b81079cf0817ba831ff4d9eff5d28be4a3ac76c353e6f09bd63f4c386 \ + --hash=sha256:58da726d3e766c0b3f5a3997dfaf0275898a1107b8191cdd6b0437fe45fd817d \ + --hash=sha256:5bef622850cf760154719d4e0d74b0a855962432995168e250069899ae12fe8f \ + --hash=sha256:5ccd139b2061132e7b265cfb4b4721baeb9f8928b81415304abf1ec7e3181c26 \ + --hash=sha256:5cef9f3d14796500ea834c41dbe688f1f6b23c7024dc23e8a794d7ebaf5d71d0 \ + --hash=sha256:63bb62cf62217dc38c8a6b2b61b165b0e4eb8fa93b0aba12139251c0986a8fa3 \ + --hash=sha256:681ed38664b64c6617d3c3c332018d1948c77e139c5ea667c1886efa671e426f \ + --hash=sha256:6888065672b341e5246f391ec16dc258a29218ac784172fd67c30d941544755b \ + --hash=sha256:6aebdd9a946de328b3f6f61dbf48dd064a36eb6dddf96e34ae6651d37f6e9383 \ + --hash=sha256:6afcad14310f1311d077553ed374b42a5e538f85a8c884b4e38e52de091c8077 \ + --hash=sha256:6b34a778c695d24e77c140e3b4c95da69282e34f2f6b02b55656aa4a0379f643 \ + --hash=sha256:6fd555fc9abef50c530869690b2daca054c8811a7aff632d11f9a7b2590b2742 \ + --hash=sha256:71879292c9c7ac67b1680345b16daba1be937cb027362cfa04e68f65db2dcfdd \ + --hash=sha256:75242f44a3e283106077be4ab717bc535e4701c9d54ad69e195945c22f137a1d \ + --hash=sha256:75aa39d3f4f1650eea84e46b0d8cefe77dd5478c10e3d0aaf0b0f00493475a7a \ + --hash=sha256:75f9297b16fcb588a1f8d8a55dabef3c0c20b0c7bac43c87ceaaaf1a825c12f4 \ + --hash=sha256:79e9432995e14c749d34209413de5e621ec8e67789bf4f46dbfabea9d06a2406 \ + --hash=sha256:7abb38b8c40f3a235235a44da452c64b7b5c1d650ec6351027db0e090804f2e5 \ + --hash=sha256:7dcad477c49c4c626a6c4fcd71b39a971aa217060cc40a6569fd24edcc0fa509 \ + --hash=sha256:7e6c0b5ec6ddee4032247585dc491b0fa58627745b66a705728703a3f0331231 \ + --hash=sha256:7f8f10015866608fe4c043cec2e4fe4c39a94bb50e45091de4cdf4004b9ae4b0 \ + --hash=sha256:866de9f98df0611d7b62b3a8729d3284a64c0cc6edd90bb95a533e443a4939cb \ + --hash=sha256:87f5f75c109f08f5c602d68e1af54cead8165189c727b6ac946b30b9833a3ba4 \ + --hash=sha256:880ac684c27176464c00c3fdc456116364f5ebc70da07aad0c2d4a7ba45e98db \ + --hash=sha256:88b02aa8d0ec9b6189fe933d425775882271c23700ac11fd26d1779b0f56fde3 \ + --hash=sha256:8ba1f78bd4fef2d8f84b894ec28ac3481afe6cc07aaa253ad4717ef7b3fe6bcb \ + --hash=sha256:8c07021a4faa3f092869adbd1f35cdc7a592276c807aeebc3ceb8ff1a638f0b4 \ + --hash=sha256:8d5c4518235a2ec1611e57af85fa488d529c1106aacff12adadcedf8687012cd \ + --hash=sha256:8e127d9a80cbf1c3276bb465c6d047e8705e97b58c2b8f2f0c0a69c336b44b37 \ + --hash=sha256:94c5ce3bc41d226b4eb89ca3f842b2e28c031487fb1f34eb2153d98235831325 \ + --hash=sha256:94d096369b7cd96d15343fef5257fe39eff9d0e8758b92a0e15e358b92cdb2fc \ + --hash=sha256:968c1e33edd9a104d1bf24c8d476c72de7e3839ae7f894b37e9e4f4739fdeeca \ + --hash=sha256:990797e765d89a423880052c68b61c31afe701de94a8c060f61c40605ca6c727 \ + --hash=sha256:9ce239acb15843ab03976626af810a4424b0409689ec2bbc52088ab5479ab487 \ + --hash=sha256:9d772586951d7d6a5d162d48f414065e483b1c81ab38fd8ed97c78b05883421a \ + --hash=sha256:9fbd2e5d8002dc49a6129fb321ec51c57a025e752ed525ddce0ba9223c4350a7 \ + --hash=sha256:a41693eb3fc4b92e6127d113813c6c395237f7edd3224abf67609af48c690d11 \ + --hash=sha256:abbfc1c33bf8efddcc43844aba61e036d74a918680dc3ce8ce2538b004eda0f9 \ + --hash=sha256:b298cdc33c5cc6969ff07f0fba19cc73e0fd8576373c50935feadaca2f6b4405 \ + --hash=sha256:b43456de605c8ee77eb75f07bc1ee44ba27f9cee22207deb77d495e954b7d953 \ + --hash=sha256:b71649169a9fcf30b395ee01047fa7ad6654a4c900ca75b23c04dedcce6a1f8c \ + --hash=sha256:b91c37551bf39d75116c02b146956f65b9aa0337a4a652f4ae186983789d4001 \ + --hash=sha256:b9d36b03dc362aa40ffaaec9d9bd75e87763529563ec008c43b0e07782f5be7a \ + --hash=sha256:bafa41b0dd63669e5c0f8adf3d24819efeb73c847f492eb011212eb352e69041 \ + --hash=sha256:bb7774924f8cd69f49cba0b3c2d679a6326f777e0e67d130ad5203e4df53f0d3 \ + --hash=sha256:bf29611e5376fec8f795879bb5c6153a76c3a292573d173c26784042b01eb840 \ + --hash=sha256:c014641157e9049b0603b8daa5343bd408d9b757b709aaa0f373cd3fab2d7944 \ + --hash=sha256:c103b3b14e011774af4fb7e4617ad4d72b9171905cd3b231a70a4efd76e477d7 \ + --hash=sha256:c22df8dd6373bbe3898e77429ffc85594300e39d752fd0e68a31e59d37899376 \ + --hash=sha256:c25a754bb81a2edcfc3b65eda50f017d736f818112ed43e8aafd595cb00678ae \ + --hash=sha256:c32818b28bcd153b25b63038348a9fe9b9fbcddb60df43f204c3ab55eeb57f77 \ + --hash=sha256:c37fa93bf18bf4f90b01c0fa9f11ea567ee4b7dd8bf96e63663e5edc37aa38cf \ + --hash=sha256:c3d95d7d9538b5b726dd6fcd7b6117a71e6565202f6d64f5845fb4d8f203f533 \ + --hash=sha256:c8fbd9cb30c68c1686b94029b9ef845d5870d3d65baf66cb126b676849b9d72b \ + --hash=sha256:cb76a9c4e07a6a47849726af0ed14c41741a182f097f134a8cf29c1bc0f4dde8 \ + --hash=sha256:ce7c118cb102975f974585688357a717ffbf9dddd64ab0bb1bc93eb5b367cf95 \ + --hash=sha256:cf377960d2ac37d987394a9dbaa75e91338c41a46d41e1d25e90125e7b3ee2dc \ + --hash=sha256:d278ad30ec83b6b9202685b0f80b741a51ea3ca7f0595ebda96e7628b6398876 \ + --hash=sha256:d2d377fd1cad611b806cdd732d86b65f536c768209890cb442556548daa65a23 \ + --hash=sha256:d414c411c06fe0009eac33488fb1591c66b5c2673e342e452e7bb2fe63da8194 \ + --hash=sha256:d8c668af8f7bdb1d18739c27d30cd9f4b371495a883f75a002fb7a39d740fecd \ + --hash=sha256:dce932f8e3ba936475ea3d0d8b59f7b050a9e206e994f53f8fd80299871e87da \ + --hash=sha256:debc629e98b95abaea1cf3057ca296151f348c697c9b8a59d18013adb302c0dd \ + --hash=sha256:e0dc78251154b66dc60211563fc115345da332eaa881e4e2523fb1edae3772f4 \ + --hash=sha256:e5e4a6e0734a685d13b9685622bb503bdbb2927f8b0df025a5085f0ea067475b \ + --hash=sha256:e6b99181d184d0f5c7b36b8d12b94d1e9499cce6246594331f9edc5d2ea9fceb \ + --hash=sha256:e7327795089ddb44912dce1434e1d7244be2e9fb48fcc2d6782936af7a3062db \ + --hash=sha256:ebb2ba68e4641a994061f70bf44ed448fba0b9b1d18c94ffb9efc1cca805b39b \ + --hash=sha256:ec8855f08c17895a26fbf5f19ed829722e19b34a96629e49a43c92974924026b \ + --hash=sha256:ecb2e7acb18f8cc4a67f0ad986c0af291ea4dd385d0614ba9bc09d7f8bbb478c \ + --hash=sha256:ef4c0a9dfdc90581b90b1b95a8c3d1557f8ff8f5a2a53536d26314de699d1468 \ + --hash=sha256:ef4ce69ff97fbb44b46751cfea5e859ad0b66d1a50abf34954f0645f51e81671 \ + --hash=sha256:ef5a059ea1c6ee5d1c7e99a2484e628608d010921efe876c6f0e2029d2f35eca \ + --hash=sha256:f0e2e5d23448b660d60a6ed85c46cc03b4b48bd276b8f4041d4a5fe2a4a0626b \ + --hash=sha256:f2374c27deb189b282ec7e16106752c22ad39b056bbd8018960b1e4cc95d67a1 \ + --hash=sha256:f2f43bf4e47ff7ce9e585558706d698c6204d0f80bf2207766382ed817c8e9f4 \ + --hash=sha256:f5c629df03adec31ee505dda3c8988f106c9390e4cbd343600036eb8b3d6724f \ + --hash=sha256:f70b9f0e39c2dba1d9da6bf7ef7c377cad7277f8440e9a69be05ede529ff024c \ + --hash=sha256:f7d4656e17ab736e9415a6442a345bfc97bb8b7dcce47884bb74a37f70f08d0c \ + --hash=sha256:f8bdec659a8fa7af51a32b224b3b7c02bc415d54ffd35187b1d224176b17d607 \ + --hash=sha256:faa911fbbcf8ac90bda0e0657d60768e3390954ef0588211d63a22add1cb1cd1 \ + --hash=sha256:fbc4e2f3cb7ce8436154e6483079e7d35eeb321a952fa936e180300630d8b873 \ + --hash=sha256:fd6bd89b9fc06018d35851cab0240adb7dd84d51941b19f6574ac90cd54e3ae5 \ + --hash=sha256:ff4d7b14ea19e50c8d9d6d83f45bd9b45cbb624c07ac1fa54db0a019049abed7 \ + --hash=sha256:ff6b3267318661dfddf6b3628663e00e5946bd0a5c8fa678537a1401f0388f91 \ + --hash=sha256:ffc2da104e43db716ce30cef9f28049a1faa6aca385dd8771b033268d0730b07 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # tiktoken +requests==2.34.2 \ + --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ + --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # harbor + # opentelemetry-exporter-otlp-proto-http + # python-socketio + # tiktoken +rich==14.3.4 \ + --hash=sha256:07e7adb4690f68864777b1450859253bed81a99a31ac321ac1817b2313558952 \ + --hash=sha256:817e02727f2b25b40ef56f5aa2217f400c8489f79ca8f46ea2b70dd5e14558a9 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # cyclopts + # e2b + # fastmcp-slim + # harbor + # rich-rst + # typer +rich-rst==2.1.0 \ + --hash=sha256:7ecd1343ee12c879d0e7ae74c3eb6d263b023d2929c6d114212eb1fd91057255 \ + --hash=sha256:f4d117b49697f338769759fa5cacf5197da4888b347b9fda2e50aef5cd8d93bd + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # cyclopts +rpds-py==2026.6.3 \ + --hash=sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5 \ + --hash=sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680 \ + --hash=sha256:127e08c0642d880cf32ca47ec2a4a77b901f7e2dd1ad9762adb13955d72ffcc9 \ + --hash=sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538 \ + --hash=sha256:168c733a7112e071bb7a66460e667edfcff06c017a3c523f7a8a8e08d0140804 \ + --hash=sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf \ + --hash=sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4 \ + --hash=sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97 \ + --hash=sha256:1e5822dfc2f0d4ab7e745eaa6d85945069329beeccef965af3f3bb26058fcab6 \ + --hash=sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96 \ + --hash=sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a \ + --hash=sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187 \ + --hash=sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975 \ + --hash=sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f \ + --hash=sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703 \ + --hash=sha256:2bfd04c19ddbd6640de0b51894d764bd2758854d5b75bd102d2ef10cb9c293a9 \ + --hash=sha256:2c54a076ca4d370980ab57bc0e31df57bbe8d41340436a90ef8b1219a3cbb127 \ + --hash=sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f \ + --hash=sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa \ + --hash=sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05 \ + --hash=sha256:30c6dc199b24a5e3e81d50da0f00858c5bbdb2617a750395687f4339c5818171 \ + --hash=sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba \ + --hash=sha256:3a83ae6c67b7676b9878378547ca8e93ed77a580037bcbcd1d32f739e1e6089c \ + --hash=sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223 \ + --hash=sha256:421aba32367055614287a4292b6a17f1939c9452299f7a0209c117e990b646d4 \ + --hash=sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885 \ + --hash=sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698 \ + --hash=sha256:4cf2d36a2357e4d07bb5a4f98801265327b48256867816cfd2ceb001e9754a8f \ + --hash=sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7 \ + --hash=sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed \ + --hash=sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f \ + --hash=sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf \ + --hash=sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e \ + --hash=sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f \ + --hash=sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24 \ + --hash=sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a \ + --hash=sha256:5e8d07bddee435a2ff6f1920e18feff28d0bc4533e42f4bf6927fbd073312c41 \ + --hash=sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc \ + --hash=sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d \ + --hash=sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146 \ + --hash=sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e \ + --hash=sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e \ + --hash=sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4 \ + --hash=sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12 \ + --hash=sha256:7b689145a1485c335569bd056464f3243a29af7ed3871c7be31ad624ba239bc7 \ + --hash=sha256:7f88d653e7b3b779d71ae7454e20dcc9b6bae903f33c269db9f2be41bda3f261 \ + --hash=sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6 \ + --hash=sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5 \ + --hash=sha256:83e35b57523816c8613fd0776b40cd8bb9f596b37ddd2692eb4a6bb5ab2f8c93 \ + --hash=sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7 \ + --hash=sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda \ + --hash=sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8 \ + --hash=sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342 \ + --hash=sha256:8bb68f03f395eb793220b45c097bd4d8c32944393da0fad8b999efac0868fc8c \ + --hash=sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb \ + --hash=sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0 \ + --hash=sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77 \ + --hash=sha256:8d2294a31386bfa251d8c8a39472beee17db67d4f1a6eabea665d35c9a4461c3 \ + --hash=sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885 \ + --hash=sha256:8e65860d238379ed982fd9ba690579b5e95af2f4840f99c772816dbe573cb826 \ + --hash=sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617 \ + --hash=sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb \ + --hash=sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577 \ + --hash=sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80 \ + --hash=sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e \ + --hash=sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945 \ + --hash=sha256:9891e594296ab9dada6551c8e7b387b2721f27a67eecd528412e8906247a7b90 \ + --hash=sha256:9c1255b302953c86a486b81d330d5ee1d5bd937691ce271b6be0ef0e299eaab7 \ + --hash=sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0 \ + --hash=sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140 \ + --hash=sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822 \ + --hash=sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba \ + --hash=sha256:a3450b693fde92133e9f51060568a4c31fcca76d5e53bbd611e689ca446517e9 \ + --hash=sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4 \ + --hash=sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a \ + --hash=sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8 \ + --hash=sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf \ + --hash=sha256:acc992ab27b15f852c76755eb2ab7dce86585ddadba6fa5946e58556088845b4 \ + --hash=sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324 \ + --hash=sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53 \ + --hash=sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b \ + --hash=sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41 \ + --hash=sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9 \ + --hash=sha256:b5c2dc92304aa48a4a60443b548bb12f12e119d4b72f314015e67b9e1be97fca \ + --hash=sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1 \ + --hash=sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d \ + --hash=sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690 \ + --hash=sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107 \ + --hash=sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2 \ + --hash=sha256:ca6546b66be9dc4738b1b043d5ebd5488c66c578c5ff0fd0e8065313fe3afb76 \ + --hash=sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d \ + --hash=sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af \ + --hash=sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6 \ + --hash=sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db \ + --hash=sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369 \ + --hash=sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd \ + --hash=sha256:db08f45aecde626498fb3df07bcf6d2ec040af42e859a4f5040d79c200342911 \ + --hash=sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504 \ + --hash=sha256:de3eceba0b683bcbb1ab93da016d0270df1f9ae7be716b40214c5dafac6ea45a \ + --hash=sha256:dfcc8b909769d19db55c7cc9541eb64b9b774b1057ffffb4f1048070475bb9f9 \ + --hash=sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13 \ + --hash=sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc \ + --hash=sha256:e52655eaf81e32593abedaa4bfe33170c8cfedf3365ed9be6e11e07f148f0278 \ + --hash=sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868 \ + --hash=sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2 \ + --hash=sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd \ + --hash=sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4 \ + --hash=sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6 \ + --hash=sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9 \ + --hash=sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00 \ + --hash=sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f \ + --hash=sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e \ + --hash=sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442 \ + --hash=sha256:f8f23ead891a3b762f35ab3b04623da7056545b48aa60d59957e6789914545da \ + --hash=sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90 \ + --hash=sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # jsonschema + # referencing +s3transfer==0.19.2 \ + --hash=sha256:ba0309fd86be3c27dbf78cdd813c13c5e1df16e5874b99d2535ebbdfb9892993 \ + --hash=sha256:d8168eccca828cbb2cd573675333f3bddd254313a9c42494b84c76b539e8ba25 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # boto3 +safehttpx==0.1.7 \ + --hash=sha256:c4f4a162db6993464d7ca3d7cc4af0ffc6515a606dfd220b9f82c6945d869cde \ + --hash=sha256:db201c0978c41eddb8bb480f3eee59dd67304fdd91646035e9d9a720049a9d23 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +scantree==0.0.4 \ + --hash=sha256:15bd5cb24483b04db2c70653604e8ea3522e98087db7e38ab8482f053984c0ac \ + --hash=sha256:7616ab65aa6b7f16fcf8e6fa1d9afaa99a27ab72bba05c61b691853b96763174 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # dirhash +secretstorage==3.5.0 \ + --hash=sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137 \ + --hash=sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # keyring +semantic-version==2.10.0 \ + --hash=sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c \ + --hash=sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +shellingham==1.5.4 \ + --hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 \ + --hash=sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # typer +shortuuid==1.0.13 \ + --hash=sha256:3bb9cf07f606260584b1df46399c0b87dd84773e7b25912b7e391e30797c5e72 \ + --hash=sha256:a482a497300b49b4953e15108a7913244e1bb0d41f9d332f5e9925dba33a3c5a + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # harbor +simple-websocket==1.1.0 \ + --hash=sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c \ + --hash=sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # python-engineio +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # python-dateutil +sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # openai +sse-starlette==3.4.11 \ + --hash=sha256:1bae716c02f3e6f294be41ff333220692dae7c3cbab077c900f159676719dade \ + --hash=sha256:c7b2244bdff016fe7f64e10075e89a3e6bbf899649cc89b0fe884b5545042453 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # mcp +starlette==1.6.0 \ + --hash=sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c \ + --hash=sha256:d4e3ac5e546444960c710297a3c9fc3f7ebae1b7e963f3d36173b49da535be9b + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastapi + # fastmcp-slim + # gradio + # mcp + # sse-starlette +storage3==2.31.0 \ + --hash=sha256:4bf46e8bea320743179a6beafdc7531c5242495e00e0cc22af7c7a9d69d4ed84 \ + --hash=sha256:d2161e2ea650dc115a1787c30e09b118365589ac772f4dd8643e3a503ecfc667 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # supabase +strenum==0.4.15 \ + --hash=sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff \ + --hash=sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # supabase-functions +supabase==2.31.0 \ + --hash=sha256:25f2a99207a75f2d9377e2332783b4389cf56b02cbebdaf0c1743112dcbb704e \ + --hash=sha256:3467b09d00482b9a0138235bdbde7a350426f93cf2a1342372eaddfc669f1206 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # harbor +supabase-auth==2.31.0 \ + --hash=sha256:0945b33fa96239c76dc8eaf96d7d2c94991950d24b4cfe4a5c2da9aa5e909663 \ + --hash=sha256:5e9c8b4ecdee6af04dbcb06455ce78cb15674806fcb6b425170455307d70b0ee + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # supabase +supabase-functions==2.31.0 \ + --hash=sha256:3fdc4c4766152bfda63bdd0e286fc8a06f50e1280711fae4a1dfc9b7e9ebabc6 \ + --hash=sha256:4ad027b3ae3bd28b31233339f4db1da6965affd3546f655b421baf40cee2690f + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # supabase +tenacity==9.1.4 \ + --hash=sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55 \ + --hash=sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # harbor +tiktoken==0.14.0 \ + --hash=sha256:087538c080e5ff421abd3a0785ed63c5111d06af98e6cd0d374dbe5969147ca3 \ + --hash=sha256:10f31e63e40313f2e518d87f7086cfa44e45f64cc14d8ae14103b41220c30a14 \ + --hash=sha256:11d8211b290855d2721334ff17dd9b3a17bfb26872be01f25d73612ef7ece890 \ + --hash=sha256:144a3fc369f92b7d548995217c5d6e84038d3572157a0f6f34080d65291d0f78 \ + --hash=sha256:149d97453c4c98c04b081d64a85e635921269b532710d6faf81e9e82b790e7d3 \ + --hash=sha256:14b47e3674f2624803a8acc8fb367b7e24fc53055f9df3296482fe9a3a34a232 \ + --hash=sha256:151d37a150c8f3dfc5f4345597b10e101876bd1bd13494e0185af6b508758d2e \ + --hash=sha256:18a1b651c4b032004bf7b4f1713391a54b2a341a52c6e8a2b59acae9d16e13c7 \ + --hash=sha256:19d643d701fdaa70e5b9c7f8f96abcaffe77ca5e482a3a1a7dde46feb4284695 \ + --hash=sha256:1b6e4adcfd285c44502aed51df98aaaca4f0fea028165dbf8a9e857b9f98d8ea \ + --hash=sha256:1f83081065ee5833d35b49e9180f3d8d15622a603dd1c435da0da6cc12b3662f \ + --hash=sha256:2157f52e4b4d7ac5ecc7457b3716834706e7ef9a46f5144029bfeb7cf71f4e06 \ + --hash=sha256:231dec90efcdccf1b565a1416107736f1e09b1a08fe736ef9d6363e626d03874 \ + --hash=sha256:26cc4b4840fa0e9f4b72ed489883e12f57e00d1021ca794720e3c29a12f0edef \ + --hash=sha256:26e60f6a956ee171ab728b37b8439905d7ea1db435c30f9822f291e9861c861d \ + --hash=sha256:2cc19ac87b41c9493c9778ff5847f0c8bbcf5bd0ec6b87ce06c1c802adc8a771 \ + --hash=sha256:2ea70afba6b9eddbf22c165142e5f0a2ad7aa36a452873c48b57bb2aeb8492ae \ + --hash=sha256:2ec16eb585332c55d022d86354e209ddf27326b1ea3477585ab248e7776d3b1f \ + --hash=sha256:2fc834fbe3f6a0736905c36ab709537e6840dbd63b982dc9e0216ae7d305ba1a \ + --hash=sha256:380873f330b741c4435574f37edb20813d04603ace2d53e0a63560e1fec83010 \ + --hash=sha256:3b12e54f8bec91433e41aff65d8d1f209a4f678081163747079806e5361f6c91 \ + --hash=sha256:3c5349c9f916283bba32bec8af69b763e4faa304dc004d0eaaea66a3cf004c1f \ + --hash=sha256:3de75343041a1c57333b1e707ac8a9769738241d7d6a55d39e12cf84548337c6 \ + --hash=sha256:3fd7c14b1cb45b486c39fc9b3443bb341f3e2fc7e6f31247f3435a5836651632 \ + --hash=sha256:447ada49af4898b5e992f0b5799d2f3af385921102c211947ce3fe960dd919da \ + --hash=sha256:4d8d91d68353bd167fdf26467e5ff9e56aaa5f87d6410c0238608629e4dc0d33 \ + --hash=sha256:50a7e5646cbac2a8f7c3e8c0934ffda1a4357ee9c44b652434b23c3ed54d0900 \ + --hash=sha256:561e7580f84a79859af1ef6f676968e9030fcc3fe195700b15235bca64f009c9 \ + --hash=sha256:60c47ca69ddda0dea8256fffd12e1b86f4b59734a20e4a70c61f63cc5f021df4 \ + --hash=sha256:6eb94895c45f26bb8f5546e5fd8a069efcf6e3f108ea9d5cbe3bf6f7f3983438 \ + --hash=sha256:728303a072163130c5b477b1f20d6211895569c1d5302c24ffc93a3009160871 \ + --hash=sha256:78571efc311c30b73f31eb949a921d6dac39a5d9dc42d1cfa8f8db157b3447b1 \ + --hash=sha256:7896eea257fe497a2b7134474d909156c6744ce8da35bce88011a960e008aa0d \ + --hash=sha256:7aab286a020660a039097912a088236b985d18a3090d73f136c4413d29d37ca0 \ + --hash=sha256:7b7acbb7a4b8383707bce22ad3c162006478c27b56368acd3e1fcb1658a80425 \ + --hash=sha256:7db45b98e94adf4173a5cd7422b150999a7ee11ff847783a14f6e1b80cc38cb6 \ + --hash=sha256:86951a971c53979ec857bd8c4a32dc227ab0fd33f6c12a3bd62d3fbf5f0bfcaa \ + --hash=sha256:86f66c85e796f5d05d5c4a60ec1d40cbfebc47a32464053528c797163fa9ab89 \ + --hash=sha256:8e947aefe98ef74cce94923f90e48c98fe34eb1ec0a6bfdfadfc5a96359bfc36 \ + --hash=sha256:90a762670c7f968184723769a06ed51f5cf5ce5dcd1e30164f25c72d85c2d1f1 \ + --hash=sha256:94f77b60a8ab23580db19ae822744c9716c1720020d2179ca5605112d12326f1 \ + --hash=sha256:979c1524f753b662b0f3cd261b135afe6659cce33caaa7a5ea00dd1756b3055c \ + --hash=sha256:a140e83317fef02faeeb78d9a8efac623887f2feaf0055c55dcdb2b17f0226ad \ + --hash=sha256:aa428a559d5fd02ae619aacaace86c7474a1f2702d2c01fc828908dd60f20f7a \ + --hash=sha256:b950248272f1b303dc32986396e2dccfa10cf6d1e83ec8f0bba1776660305482 \ + --hash=sha256:c2edf09b381fafbc014ae8e018ed25087abb9a3dafa8465a0ea63c6558c47a79 \ + --hash=sha256:c3093001ddce822b4587e6e94bf6de36a5f97b3f31de1c9fc8d4fda144c59ff4 \ + --hash=sha256:c6cb9896a82b9ee44e15ba0b5c8044072f2e4d48acaa704c8d3feeef5ad9487c \ + --hash=sha256:c77d4a3e1deb2707819df92046b89aad1ac81d27e07616b797cbff3f62c037da \ + --hash=sha256:ca4db6ff5c5bf600f9b7761a0070ed44dfe5797a76bd432fb978bc480ef40c58 \ + --hash=sha256:cbe2cc3bba939bcdaf103e03df9d5039d33887080b315624be28ec69059e5f94 \ + --hash=sha256:cd8ca1305c1c902fe42c486165f2e4808d9997625c98ffb05b9e0366d99d3948 \ + --hash=sha256:d0781223705199b289faa59601bb9c2441712d4c600dd13c43d8fd6a33d22cd5 \ + --hash=sha256:d6cebe67765569df3dafac8474e4eccf5c19d24140492567a5e58a11445732a4 \ + --hash=sha256:e067f4cbcc5d036e8aff7fe7a6b530a8f4de2e4616ad9005a24a1879e24e6450 \ + --hash=sha256:e2eca764c53490f8930dbce329e0769f11108d87d908282a80c5c130e26e7037 \ + --hash=sha256:e3442bbb2f0c588cec876061e37ae67b455b9df9978b003c8fe30e45f2ef5b42 \ + --hash=sha256:e4ddf863b59347deaa92302dcd90e5eb003cdc9be06ec2b692c38d1bdd9efd49 \ + --hash=sha256:e9c5fe393aab56469f04e432ff851216d3def3436cf5f07e442a240164bf500f \ + --hash=sha256:eceeff0c62419bc78d4b6e70a4762a4d25df3ae8f2d5946e3853ce93e7a57098 \ + --hash=sha256:f2af4a336ea56d6c14f27741a0e1d8294a35dd0b038bcf990d232ebb54eb994b \ + --hash=sha256:f3d6cf93fbe2e7117eb7bedca684216fbe328a41f0843ce34245451d8eb2df1c \ + --hash=sha256:f5e7665f6624e052e5e7f6a36919ab69279decdc976d7b16b4fa15e1897d0513 \ + --hash=sha256:f702e0aeeb6506e57687e881c59e844ebe8f0a6a097ddafe20e3ab25f387be4e + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # litellm +tokenizers==0.23.2 \ + --hash=sha256:12f0835dc2ee694746a76adf7b1567d4346a4a502ebe93fb1f5f80ea49799b78 \ + --hash=sha256:2e96f5699d5249c9c64aa8412e044f727aae3a4098cf830f9901ec1afc361cde \ + --hash=sha256:325fee2e0418a9dc6c9ecf736a5f5f0db7875183ace9549ae339da76f7a1fbb7 \ + --hash=sha256:41c2f84d172449b4dadb9cdc508e3e364076613c35b16e76ecfe47a60d1e3305 \ + --hash=sha256:43e4f2071e3cc8d5d86421c874aebc82659bb51a68bcdef5a0da75ee89511ccb \ + --hash=sha256:5c56bda1511921587789163e524d196ed8284174ac23abd7685d5ea8da6c4718 \ + --hash=sha256:7b7e37ba198f24150f523e1242e83c4970de4a525480586be5dcc24d9add32c5 \ + --hash=sha256:7f0f085686b9de0d0079e6f874ae053600db64c5d13049e0bbc0119926d25aac \ + --hash=sha256:85a9a357a3764aecc904ee76bdaf8cf1ad8e5a67a1b929a487c4a39b49ed0e90 \ + --hash=sha256:950d7c9426fa72406a0ffeacdbc0bb9985f5db20eb8b263f29c79aaf83105703 \ + --hash=sha256:986670e43691469dcee610ea0f846f91a8f84e91fc6f7a48d4c064414c0ec2bf \ + --hash=sha256:a37039b5dfc4af84eb3ef0a92f4307e28936c8f9adccba2629d36f652e9bf7a2 \ + --hash=sha256:bef235815a067b2648caf6dcc7a71091b0b0fff9ee8057f6451eb9335fae52ef \ + --hash=sha256:debf978920d93ba9c219bd67cc4bbfaf912c9039e41e7a28b91ec15e3728c95a \ + --hash=sha256:e49c394456dd9985787fec76132438ba3fb8911f857b1bf3d40119f9292d41aa \ + --hash=sha256:eb2f9c8a24da020ea8c11a01a19c1c2547912d92121ae4a01cfbca46125dee40 \ + --hash=sha256:f486f402f6f9abee5bb032553736813af0c710a86b2e0ca592634c55cea1f835 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # litellm +toml==0.10.2 \ + --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ + --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona + # harbor +tomli==2.4.1 \ + --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \ + --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ + --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \ + --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \ + --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \ + --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \ + --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \ + --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \ + --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \ + --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \ + --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \ + --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \ + --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \ + --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \ + --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \ + --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \ + --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \ + --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \ + --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \ + --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \ + --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \ + --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \ + --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \ + --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \ + --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \ + --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \ + --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \ + --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \ + --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \ + --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \ + --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \ + --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \ + --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \ + --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \ + --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \ + --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \ + --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \ + --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \ + --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \ + --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \ + --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \ + --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \ + --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \ + --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \ + --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \ + --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \ + --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in +tomli-w==1.2.0 \ + --hash=sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90 \ + --hash=sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in +tomlkit==0.14.0 \ + --hash=sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680 \ + --hash=sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # gradio +tqdm==4.70.1 \ + --hash=sha256:c293e525e6fef9c20e8728fd4612df02a0aa31bb5fe91ecd93e123b1b7bffa73 \ + --hash=sha256:cefd0eca11b2a37a3aee776544d4f4ae913f02688135b5556b8788dfa474afc4 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # huggingface-hub + # openai +typer==0.27.0 \ + --hash=sha256:629bd12ea5d13a17148125d9a264f949eb171fb3f120f9b04d85873cab054fa5 \ + --hash=sha256:6f4b27631e47f077871b7dc30e933ec0131c1390fbe0e387ea5574b5bac9ccf1 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # gradio + # harbor + # hf-gradio +typing-extensions==4.16.0 \ + --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \ + --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # aiohttp + # aiosignal + # anyio + # daytona + # daytona-analytics-api-client + # daytona-analytics-api-client-async + # daytona-api-client + # daytona-api-client-async + # daytona-toolbox-api-client + # daytona-toolbox-api-client-async + # e2b + # exceptiongroup + # fastapi + # fastmcp-slim + # gradio + # gradio-client + # huggingface-hub + # mcp + # obstore + # openai + # opentelemetry-api + # opentelemetry-exporter-otlp-proto-http + # opentelemetry-sdk + # opentelemetry-semantic-conventions + # py-key-value-aio + # pydantic + # pydantic-core + # realtime + # referencing + # starlette + # typing-inspection +typing-inspection==0.4.4 \ + --hash=sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47 \ + --hash=sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastapi + # mcp + # pydantic + # pydantic-settings +uncalled-for==0.4.0 \ + --hash=sha256:16c4bb3337532e4bd5569adc192285976f3ad5305402256d34c67a12b5c968bd \ + --hash=sha256:335b95bd2422332ec210d518f314a16e4c640921c39fc8bf2ad095bd3538f4af + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim +urllib3==2.7.0 \ + --hash=sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c \ + --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # botocore + # daytona + # daytona-analytics-api-client + # daytona-api-client + # daytona-toolbox-api-client + # requests +uvicorn==0.52.1 \ + --hash=sha256:112ec661814189acbccd3f7b86460147cc065fc92c0821afa78918780e4354dd \ + --hash=sha256:e4403f9d93188cf9d1088e9f40e3acd12630e2df8675316704379a7fc20fff6a + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # fastmcp-slim + # gradio + # harbor + # mcp +watchfiles==1.2.0 \ + --hash=sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9 \ + --hash=sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98 \ + --hash=sha256:027ae72bfdfd254862065d8b3e2a815c6ab9b1853ce41e6648ece84afd34a551 \ + --hash=sha256:03b14855c6f35539e2d95c442ae9530a75762f1e26567152b9ed05f96534a74d \ + --hash=sha256:054dc20fd2e3132b4c3883b4a00d72fd6e1f56fdaf89fccd12e8057d74cd74d7 \ + --hash=sha256:094b9b70103d4e963499bdea001ee3c2697b144cd9ae6218a62c0f89ec9e31db \ + --hash=sha256:0a105bc2283f67e8fbec74253ec2d94925de92ed72c0393f1206bf326b7b7b69 \ + --hash=sha256:0a37faaed405c67e28e6be45a1fa4f206ef5a2860f27c237db9fa30704c38242 \ + --hash=sha256:0c4997d4e4a55f0d02b6cde327322daf3a0400e5df6c6b15948994bf72497925 \ + --hash=sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f \ + --hash=sha256:0d191c054d0715c3c95c99df9b8dbf6fd096d8c1e021e8f212e1bd8bc444ccb5 \ + --hash=sha256:0e831a271c035d89789cffc386b6aa1375f39f1cd25eb7ca0997e4970d152fc5 \ + --hash=sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427 \ + --hash=sha256:11743adfa510bfffebe97659fb280182b5c9b238708f667e866f308c3430dc19 \ + --hash=sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4 \ + --hash=sha256:204f299afcbd65918ab78dbc52626b0ae45e9d8cef403fdbf33ecf9e40eac66e \ + --hash=sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa \ + --hash=sha256:23282a321c8baf9b3a3c4afff673f9fe65eb7fdc2338d765ccad9d3d1916a5ba \ + --hash=sha256:24b2405c0a46738dd9e1cf7135aa5dbdb9d42d024628651b3b13d5117e99f8df \ + --hash=sha256:2581a94056e55d7d0a31a823ea92bf73749c489ca2285bfdc0fbe6b2bb49d50c \ + --hash=sha256:2995c176de7692b86a2e4c58d9ec718f753150a979cb4a754e2b4ffa38e70906 \ + --hash=sha256:2b37d10b5a63bd4d87e18472d80fa525bd670586fae62e5dd580452764879b65 \ + --hash=sha256:2cb93af48550faf1cea04c303107c8b75833de7013e57ce27d3b8d21d8d0f58c \ + --hash=sha256:2d95ddc1eb6914154253d239089900813f6a767e174b8e6a50e7fdacb7e4236c \ + --hash=sha256:3416ff151bb6b5a8d8d11664974fbef4d9305b9b2957839ab5a270468fd8df30 \ + --hash=sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077 \ + --hash=sha256:37a6721cdf3f65dbb13aa9503510ccb4451603ac837e44d265d7992a597e1374 \ + --hash=sha256:41bc1199f7523b3f82843c88cbb979180c949caef0342cf90968f178e5d49b01 \ + --hash=sha256:43d818978d06062d9b22c4fab2ebe44cf5213d42dc8e62bda8c2760cfa2eeb33 \ + --hash=sha256:4429f3b105524a10b72c3a819b091c495d2811d419c1e1e8df773a5a5974f831 \ + --hash=sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9 \ + --hash=sha256:4674d49eb94706dfe666c069fc0a1b646ffcf920473492e209f6d5f60d3f0cc2 \ + --hash=sha256:4c887eba18b7945ac73067a8b4a66f21cd46c2539b2bc68588f7be6c7eb6d26b \ + --hash=sha256:4e4ff8e37f99cf1da89e255e07c9c4b37c214038c4283707bdec308cb1b0ea1f \ + --hash=sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658 \ + --hash=sha256:5327989a465505f05cfe06f04fa9d0c2fd5432bb243e10e6f012b1bdca3c8579 \ + --hash=sha256:53b2290c92e0506d102cd448fbc610d87079553f86caa39d67440856a8b8bba5 \ + --hash=sha256:56d8641cf834c2836922899105bd3ce3d0dfc69291d52edf0b4d0436829b34c0 \ + --hash=sha256:57a2d9fa4fb4c2ecae57b13dfff2c7ab53e21a2ba674fe9f05506680fcdcc0d7 \ + --hash=sha256:63ac26eefbf4af1741247d6fb68b11c49a25b2f7413fbd318a83a12aaa9cf666 \ + --hash=sha256:6543cf55d170003296d185c0af981f3e1311564907e1f4e08671fc7693a890a5 \ + --hash=sha256:704fd259e332e01f9b9c178f4bce9e49027e5587cc2600eeeaf8e76e1c846201 \ + --hash=sha256:71283b39fd17e5408eb123bd37aeecfd9d54c81fc184421943208aadb879d103 \ + --hash=sha256:71cd71740ed2c15211ebb237ced4e39a1cdf6f80566e5fe95428da1626f4fde6 \ + --hash=sha256:7571e4464cb6e434958f867f7f730b8ab0b75e3f8e5eac0499168486ab3c33a8 \ + --hash=sha256:772b80df316480d894a0e3165fdd19cf77f5d17f9a787f94029465ad0e3529d1 \ + --hash=sha256:77a0feab9af4c021c581f695258c642b3d10c5fd4c676e33a0d8606425d82631 \ + --hash=sha256:7a2cffd17d27d2ecbb310c2b1d8174f222a5495b1a721894afa88ec11e25b898 \ + --hash=sha256:7a7ce236284f002a156f70add88efe5c70879cccbb658be0822c54b1306fc09d \ + --hash=sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44 \ + --hash=sha256:8520a4ab0e37f770afc34459c4f8f7019e153f9124dc101c15538365875d1ab2 \ + --hash=sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5 \ + --hash=sha256:89d8c2394a065ca86f5d2910ff263ae67c127e1376ccc4f9fc35c71db879f80a \ + --hash=sha256:8c520725602756229f045b032a1ff33d7ef0f7404189d62f6c2438cb6d8ef6a1 \ + --hash=sha256:8f200104103feb097de4cab8fe4f5dd18a2026934c7dea98c55a2f5fd6d5a33b \ + --hash=sha256:8f70d8b291ef6e88d19b1f297a6905ddb978888d9272b0d05e6f53309856bcfc \ + --hash=sha256:8fa585ede612ee9f9e91b18bebf9ba11b9ae29a4e3a0d0cf6fca3e382133f0d5 \ + --hash=sha256:922c0e019fe68b3ae392965a766b02a71ba1168c932cebc3733cd52c5fe5b377 \ + --hash=sha256:9342472aff9b093c5acd4f6d8f70ae0937964ab56542502bcf5579782da69ae8 \ + --hash=sha256:9649193aa27bd9ff2e80ff29bfaa93085496c7a3a377592823cc58b77ee88add \ + --hash=sha256:9f04b092229ad2c50126dd3c922c8822e51e605993764a33058d4a791ab42281 \ + --hash=sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9 \ + --hash=sha256:a16ffe19bf5cf9f5edaa1ad1dd830c5a816e8feec430c522302ab55483a4b994 \ + --hash=sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0 \ + --hash=sha256:a711b51aec4370d0dcda5b6c09463206f133a5759341d7744b953a7b62e1100e \ + --hash=sha256:a88fc94e647bc4eec523f1caa540258eb71d14278b9daf72fa1e2658a98df0f0 \ + --hash=sha256:ae99b14c5f21e026e0e9d96f40e07d8570ebee6cafd9d8fc318354606daa7a28 \ + --hash=sha256:b0ef001f8c25ad0fa9529f914c1600647ecd0f542d11c19b7894768c67b6acb7 \ + --hash=sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55 \ + --hash=sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb \ + --hash=sha256:b62f042afde2dde21ec1d2c1a74361e804673df86f51e418a999c9acfe671b07 \ + --hash=sha256:b718bf356bbc15e559bd8ef41782b573b8ae0e3f177ab244b440568d7ea02cfb \ + --hash=sha256:b8c8358484d5fa12ef34f05b7f4168eaf1932f408725ff6d023c33ec17bd79d4 \ + --hash=sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0 \ + --hash=sha256:b9909cc2b48468b575eefa944919e1fe8a36c5849d5c7c168f80a8c1db69398e \ + --hash=sha256:b9f732dc58b2dbe69e464ccf8fff7a03b0dd0be439da4c0720d3558527d3d6b4 \ + --hash=sha256:bb68bf4df85abebe5efddc53cf2075520f243a59868d9b3973278b23e76962a9 \ + --hash=sha256:bb7e52ecf68ba46d22df23467b87cffeb2146908aa523ebfe803019618cfda06 \ + --hash=sha256:bc13eb17538be00c874699dc0abe4ee2bc8d50bb1166a6b9e175ef3fd7eb8f26 \ + --hash=sha256:c0db965c5f79aa49fe672d297cf1febc5ad149b658594944f49a54a2b96270a7 \ + --hash=sha256:c16cb06dd17d43b9d185094268459eac92c9538356f050e55b54e82cf700e1d4 \ + --hash=sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3 \ + --hash=sha256:c5c19526f4e54a00f2666a6c0e9e40d582c09e865055ea7378bf0009aab857b3 \ + --hash=sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838 \ + --hash=sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71 \ + --hash=sha256:cee9d5efd929efdac5f7e58f72b3376f676b64050a91c5b99a7094c5b2317488 \ + --hash=sha256:d158cd89df6053823533e06fb1d73c549133bff5f0396170c0e53d9559340717 \ + --hash=sha256:d20029a60a71a052a24c4db7673bc4de39ab89adbaccbfb5d67987c5d73f424d \ + --hash=sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44 \ + --hash=sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2 \ + --hash=sha256:d516b3283a758e087841aedb8031549fb41ced08f3db10aa6d2bf32dc042525b \ + --hash=sha256:d73a585accffa5ae39c17264c36ec3166d2fad7000c780f5ef83b2722afb9dd2 \ + --hash=sha256:dbd6c97045dad81227c8d040173da044c1de08de64a5ea8b555da4aee1d5fa22 \ + --hash=sha256:e0618518f282c4ebff60f5e5b1247b6d91bb8b9f4476947563a1e74acc66f3c6 \ + --hash=sha256:e140ed30ebde76796b686e67c182cff10ea2fbab186fafd1560f74bb5a473a6e \ + --hash=sha256:e1cfd51e97e13ff3bd047c140764d277fc9b95b7cb5da59e46a47d167adab310 \ + --hash=sha256:e2ca07fa7d89195ec0865d3d285666286740bfa83d83e5cee204043a31ecc165 \ + --hash=sha256:e53a384f76b631c3ae5334ce6a52f0baa3a911eb94a4eac7f160079868b716d5 \ + --hash=sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799 \ + --hash=sha256:eb72919d93e3a16fc451d3aa3d4b1698423daca1b382d3d959c9ac51297c12a8 \ + --hash=sha256:ecb47f183a8025b2aa18b546725c3657e542112ae9c0613a2af79b4fa8d04ad7 \ + --hash=sha256:f155b3a1b2a5fc89cdc70d47ee5d54e3b75e88efa34982028a35daef9ba00379 \ + --hash=sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925 \ + --hash=sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72 \ + --hash=sha256:f88af53d6ddaf72179ef613ddc905e6f4785f712b49b80b3bef9f3525e6194b4 \ + --hash=sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08 \ + --hash=sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # fastmcp-slim +wcmatch==10.2.1 \ + --hash=sha256:2d775395b93f233af66690f62cb9d52b084ec159a31cc4084f4069d72f437acd \ + --hash=sha256:ecac70a5c70e62ba854b78318d3a1408e8651f8f1c96e5837743b71aa6a4fb92 + # via e2b +websocket-client==1.9.2 \ + --hash=sha256:0fcb57545848be86992e128218fd96dd87a6769ffdb1a968dff79632b85604d0 \ + --hash=sha256:e1a673830a9c7bfa47b1cd3d5e4178f4c9651d80a4eab02c9c23a1c3ec6250ce + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # python-socketio +websockets==15.0.1 \ + --hash=sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2 \ + --hash=sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9 \ + --hash=sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5 \ + --hash=sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3 \ + --hash=sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8 \ + --hash=sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e \ + --hash=sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1 \ + --hash=sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256 \ + --hash=sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85 \ + --hash=sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880 \ + --hash=sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123 \ + --hash=sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375 \ + --hash=sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065 \ + --hash=sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed \ + --hash=sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41 \ + --hash=sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411 \ + --hash=sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597 \ + --hash=sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f \ + --hash=sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c \ + --hash=sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3 \ + --hash=sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb \ + --hash=sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e \ + --hash=sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee \ + --hash=sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f \ + --hash=sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf \ + --hash=sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf \ + --hash=sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4 \ + --hash=sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a \ + --hash=sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665 \ + --hash=sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22 \ + --hash=sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675 \ + --hash=sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4 \ + --hash=sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d \ + --hash=sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5 \ + --hash=sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65 \ + --hash=sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792 \ + --hash=sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57 \ + --hash=sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9 \ + --hash=sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3 \ + --hash=sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151 \ + --hash=sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d \ + --hash=sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475 \ + --hash=sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940 \ + --hash=sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431 \ + --hash=sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee \ + --hash=sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413 \ + --hash=sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8 \ + --hash=sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b \ + --hash=sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a \ + --hash=sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054 \ + --hash=sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb \ + --hash=sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205 \ + --hash=sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04 \ + --hash=sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4 \ + --hash=sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa \ + --hash=sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9 \ + --hash=sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122 \ + --hash=sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b \ + --hash=sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905 \ + --hash=sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770 \ + --hash=sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe \ + --hash=sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b \ + --hash=sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562 \ + --hash=sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561 \ + --hash=sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215 \ + --hash=sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931 \ + --hash=sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9 \ + --hash=sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f \ + --hash=sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-env.in + # fastmcp-slim + # realtime +wrapt==2.4.1 \ + --hash=sha256:03b5598edd435373278731d0d53449ce7a9626bc48d5548e4a71124ee3e526a1 \ + --hash=sha256:094a606d0bf1c4b847b3a743d22fc69cb164015b8c70cf6f20a534d30889ed47 \ + --hash=sha256:0aacc1af512e040fb0b3c4593a8bded9aaa04235025f08a54eda9d8c1103d3c6 \ + --hash=sha256:0c974c36e8205255a3947dad9c2fe000431b57dd522946e56c192174a7f92a0f \ + --hash=sha256:0cbee8d00840ac89846f6567c75506260a15fbb4dc74de180678fafe09458d47 \ + --hash=sha256:0f5990f5db090f069fcd577cc61cc8d463db73cde132abba9033b0a264fc06d0 \ + --hash=sha256:1e84ec5d89a0a07a0ef6bcd343f5c8ecdc95601d71de3058cdc63274e86c193c \ + --hash=sha256:1f1851be0e593d65e68d7c1bc9d3b971e19fefa219f617e490d75e80e467ee14 \ + --hash=sha256:1fe758b9c2d49138231ec3efabd106fec665f86fec50b3d02d7edd75f08a69ca \ + --hash=sha256:20327e162ef7953fae56b31a43ca457f94ed1fc7a206d01e3d5c8412d1b91572 \ + --hash=sha256:2286e8e4a937966706463d1bcea30dfefee529e6e73e097a18e9e066a07ae6c2 \ + --hash=sha256:24389748f0b9d5b67e478fad4fc8b3f1108422ef80716e48eead6cebcebbff08 \ + --hash=sha256:28cb1c2713b4377bf03ddcb3e76d8216e4bb334199066c6122be7eed2f72de8c \ + --hash=sha256:2945f4bce1518e09eb25458dbd09800f7aa3fee81a5a5cefe66c4e82fe8cb69c \ + --hash=sha256:2b21924949dedc3ac63725e09b9b0a130e771975f03432789344ed3422c46008 \ + --hash=sha256:2cedb743dbdfb9b6d4f11acd8cb6329460264429777e81ea2e86b1b72ea503af \ + --hash=sha256:2e2e694057bcfd46f43e28f7b50fad8f5fec7113cc6445fdf251219539a08f28 \ + --hash=sha256:2f725af353bb3319c528ee69dbff838599426974288b281a3258d5397b18cb63 \ + --hash=sha256:2f86e328c482bc5383b4eda5094be0bed3617fc3076aa9225ff1a9eb6372de9b \ + --hash=sha256:30d11c289b013bf384ff1a1a6553f150d0b855901708a9bef667a5680f8247c9 \ + --hash=sha256:3152b2e94d733a9bd70dbd1c95f148266f079a70e9ffca2a9c5dc421ade1b4e6 \ + --hash=sha256:355e766371657ceca791041b82a7943aa3c8a0d879359c112e63bf3c1c8cfea7 \ + --hash=sha256:3593b43fabab6b59fe77e38f7e40974aae120c30cea3fd1ceaea6621ee96be00 \ + --hash=sha256:37ff91b390400463ecd4d080ea823314510b6843ac53e6e35cc09bba1805d466 \ + --hash=sha256:38819761401baa2d11916d7265b82f23265f8fe5a31c431dd7c24a8863c65f88 \ + --hash=sha256:3bc7ab495e564449f39db2f54c1033d7190c944736d525f3bd06cefaf0134df0 \ + --hash=sha256:3bfc6907ebed560d2d3f677c3b17bf6199679163b6c6e475035c9dca497d1697 \ + --hash=sha256:3d5c8f3b4eae814213d4097f134be03aeb23816e6358213454ec5e62e8449fb6 \ + --hash=sha256:42d01574bd4bcafc3476e77a95c6c0dd6167101991401325fc5591e2db21a91d \ + --hash=sha256:432f402f9b6014403cacf9fd18a6bf089c77964330eae951a155131ab0414f5d \ + --hash=sha256:47c267617551e906de72f6e7265aa3bce84c63d44513d2d2e735e943422aa0a2 \ + --hash=sha256:4935ad7324d8637715f7cefc35ad65be80900f91d1631947b01bf70651daa328 \ + --hash=sha256:4d48f1720569b4f6e2df97783fd270554abc4c6ebd74b2e5f15e5110283aaf78 \ + --hash=sha256:4dc92697444ee380544fbb43c86612d8486529aadf917c22b5524141d4af074c \ + --hash=sha256:52e474424ea1863199e8635a3915b279164929e71b81af97f2105cab1ce78ef2 \ + --hash=sha256:53e15cd74bd6b84d7fa90b93dda7334d85f4641fae97632de8aa61d268dfd145 \ + --hash=sha256:55f36bb1461f93beaf18d818e568b5de343dd8fade7456773d201a38ee723bd3 \ + --hash=sha256:5a54744b1193505f19016194979b773ee3754a199e9ad90db18f2e9a18fffa16 \ + --hash=sha256:5c0217b8c12952bf4d137a19ff2c61cc9b626f52c09c43f0289769c9f5f808aa \ + --hash=sha256:5d83e412665aeb1e854eefbf1564d0d67872d9994b502a0bce96e6ff7f4970b7 \ + --hash=sha256:68a403adbeb4dd2654d6d108e43e69f0f90e6d34cb7588e0e6589109f6985e67 \ + --hash=sha256:69fa244ddc1f9c3e8d390e169ebbf018eee53c2e05bda61c980418df89585ccf \ + --hash=sha256:6b9df84f0a96763159cccb8e3b0ed83cc950c7c8bf82d6e45428372a805b3224 \ + --hash=sha256:6f32a45d0e883918387aabf5384952e5a93ae40be0a6fb5f1eddc8281af4c714 \ + --hash=sha256:6f5577fdc702427698c81c8c9c3b8479cbecb3dfd0f697be8c1d09c89f5838cc \ + --hash=sha256:7033c3c5eb7cd301d9bea8a01660fbe9561c9a3a069d1733e9a712917def0aa2 \ + --hash=sha256:707d2bef68deddd0fc74a81103d91b286a69ac5a9ff0f0a6dad66f8787e86697 \ + --hash=sha256:730195aa58afaa7dbb5d351e8728cec6763be2605ef82eec9f134950e3c8618e \ + --hash=sha256:78af62413095d0a57077606654ce85e273deda8e2bfa28fdb04257b962d94ef2 \ + --hash=sha256:7aaff952dd6930fc87b52d44bf72ac8426e45e90058fe0f2da446889738eff44 \ + --hash=sha256:7c89bdfe72c2b96bbe431764a90a25723570a896b605d08e08bca6fe150b8a33 \ + --hash=sha256:7cb3035b332bc9d21478600ba82c7c71e2d074ed9b4e76364297f54598792227 \ + --hash=sha256:7e86fbc2ac8a363ea04abf631fad82720e16b17a25020f32dbe9b24a2ed2b0e3 \ + --hash=sha256:7ff549316d94c404999f96bb0a6993222566231ff4bede1b6f68908d1958a08f \ + --hash=sha256:80afa3b7010e82899044a2a189c468a0cd8c89980398a59ba3b96b451a6dc5e9 \ + --hash=sha256:8342f332dada211f64b74609e332d727b13315e9a83177f7918bf68c59f815f2 \ + --hash=sha256:898513db90d55a4ed3009312d41c12932b8163edbae3535280046d47aaff774f \ + --hash=sha256:8c4b44e4be7680fc496816e824b6ed134d781c4da296e44b75399196e5c248f1 \ + --hash=sha256:8d67b916f2f777a6d31067e842598e059f1a0e29022c86454564fc9efb9a0c19 \ + --hash=sha256:8e82a1669d63b79a2b53041bbb6ea096b5763cab3ca698ed7ac244b3acb7cd51 \ + --hash=sha256:8ec7aeb92e956810eedc268fe75f8c11f05d6c3d864d0995a53eb6623b3b6a51 \ + --hash=sha256:9356dbb59199a0e4709de35fa4a1ac1a88ef6da99711a397f5b009233faff326 \ + --hash=sha256:96a5023fa63ca2f095f7776c8bfaa1694547577f762646c375abafd8f8ae649b \ + --hash=sha256:9ecdeb8a1ec13397421e6f925412186bd87ab86d63517e6825b6d7bccf781f26 \ + --hash=sha256:a0c3b217332cf0c4df085fec41c126bf7507d6c1ca0efb3ba8d2b3fd234d4e73 \ + --hash=sha256:a1e4870d3368c6c918f38e308d5dcea970ea5b908ce889c21412f3a517ebf0c3 \ + --hash=sha256:a23b89621cfeb3329b1a290596bf402e61d7d5a647a65ca8ea735e48771b71d4 \ + --hash=sha256:a524ca32f0bcdde2b728d9f81f9527d4dd24b13f15d180f05d08cdc01d1cebe0 \ + --hash=sha256:a7ce62ee24eddb76c317ec790c44f4fbe9f34ee0a5dae37b30c65a2b8f3e3604 \ + --hash=sha256:ac1939ccf3e1c33f463706fbf52db5075f5eefb6042bcc1f9cf48c2c20ef478c \ + --hash=sha256:b0d38d9cc23e9781e6584f303e5c5a9f0d45b85de92ad45678c4dde8d49d9535 \ + --hash=sha256:b0ee076be124406a7f97ca663c4a3ba32b6bcdd9102ca82497feaca79e9cb33c \ + --hash=sha256:b19e71c914c435d2c5652caea386c9bf2807979e957183f5bb06d5ed5fa8b55e \ + --hash=sha256:b4e7efdd476ac631a0181551fd9aace844765ea3ce2b5133b194fae4421e8ad0 \ + --hash=sha256:b6ea396edcfb305698f44ef0a47b9adaca15d30386ce093ca606844f776d7236 \ + --hash=sha256:b78dd8d058468156003ee8c212dab334b6eea18cd9c5db3b81f6c90a9408afcc \ + --hash=sha256:b86bfe741840cec6d8d5858b1eb53a325f564f08d7e6fb5694981961edcec230 \ + --hash=sha256:ba66eccadd4b857a845e309c08457bb71fa8300000e508e3a86cd695bf9c7515 \ + --hash=sha256:bc67d4872af5ab2dc1b88904097b92ac00e7658fdf010dee36807807fe882ac4 \ + --hash=sha256:be6cdd7121adc89a6f52e3c2f4e26a2d4dcdc1c0fde47e3156234db8939e4cdc \ + --hash=sha256:c15c4ede3fde08723cabab0a892d4b75d35b5a6f0a51c84e6542ac0bdc0507aa \ + --hash=sha256:c2ab24ed5d3a19f31fb815419a61b27904b5b0222eaacac5e1853d5361035df8 \ + --hash=sha256:c57abea6aa7de8c584b32958d8f606a08d659d3a5a1296ba2038ea9c185a417e \ + --hash=sha256:c6c35541cc729964c65c2b9b1f9cf317811039abc2da13b4557f20f21cdc292b \ + --hash=sha256:d2d6f9abaa52de05090a2b4a4c1d0e858a268c4de69eec277506af9fbcccff91 \ + --hash=sha256:d60702ebc914d0bb01aa48f5c1785ceaaaa505e0a841b444a69d6ceb5de4097e \ + --hash=sha256:de5d2d7f12557c9e994b4037330efa5700fccb9f2b9df0bd4ccf384eaf73c254 \ + --hash=sha256:e0a518cac3e789443af54fc77f23e66f17d80192c281b160b42058f11fabccc5 \ + --hash=sha256:e32c5951c36fed88b6c603dc0bab209a62dc3217bd8762413634725fe28f2f3c \ + --hash=sha256:e59f1d5db7bba65f57a2b121eea839214465e878d957d9ab29ad039279ec2a66 \ + --hash=sha256:e5b347c2184906daedae9a2f9fac67cf02c61f7b6498f09cafdcdd6b404ea9be \ + --hash=sha256:e78c0f6d66a564bab245faf170d55595df6fc237117b99d97fd838678f0906f0 \ + --hash=sha256:eb7c0f8bdd21e954bad89d554cfb7431c2f8179842853f199841ab90cbebe914 \ + --hash=sha256:ebf3b703752b53366fd02b7fbd8c447428e0349c9af3fc17c7a05076dbdd749a \ + --hash=sha256:edd03758a7578526642508b8833d43496fdfba0f64e0025dfca153a7c1777735 \ + --hash=sha256:ee437bd7fd050823ef731aad20e968ca8fe670b95e1841f5d201bfdbad4a4e96 \ + --hash=sha256:ef19a2590b195ac294deff8ee350a027a479afdd2ef2170ce3900af92406e110 \ + --hash=sha256:f1556a96b20d5bfdc9cb5dcd0a3ec65cbbac8a4eebcd3cd34efdc91c9519f660 \ + --hash=sha256:fb5b3f94258bcf71db902795f4a71151c7a74d9fe21fffa00752d2bd286866f8 \ + --hash=sha256:fd6390aab9e8aa40c52eff3c180f098e8d9f5894b1fd4c4fd2c207067b33ed16 \ + --hash=sha256:fdc997819a6df4c65bdb0a1c5601c98b479ee34cc2f94ffa98a767034ad6366d + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # deprecated + # opentelemetry-instrumentation + # opentelemetry-instrumentation-aiohttp-client +wsproto==1.3.2 \ + --hash=sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584 \ + --hash=sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # daytona + # httpx-ws + # simple-websocket +yarl==1.24.5 \ + --hash=sha256:0055afc45e864b92729ac7600e2d102c17bef060647e74bca75fa84d66b9ff36 \ + --hash=sha256:0465ec8cedc2349b97a6b595ace64084a50c6e839eca40aa0626f38b8350e331 \ + --hash=sha256:0ebfaffe1a16cb72141c8e09f18cc76856dbe58639f393a4f2b26e474b96b871 \ + --hash=sha256:16a2f5010280020e90f5330257e6944bc33e73593b136cc5a241e6c1dc292498 \ + --hash=sha256:17f57620f5475b3c69109376cc87e42a7af5db13c9398e4292772a706ff10780 \ + --hash=sha256:2120b96872df4a117cde97d270bac96aea7cc52205d305cf4611df694a487027 \ + --hash=sha256:240cbec09667c1fed4c6cd0060b9ec57332427d7441289a2ed8875dc9fb2b224 \ + --hash=sha256:24e861e9630e0daddcb9191fb187f60f034e17a4426f8101279f0c475cd74144 \ + --hash=sha256:2729fcfc4f6a596fb0c50f32090400aa9367774ac296a00387e65098c0befa76 \ + --hash=sha256:2c1fe720934a16ea8e7146175cba2126f87f54912c8c5435e7f7c7a51ef808d3 \ + --hash=sha256:2cabe6546e41dabe439999a23fcb5246e0c3b595b4315b96ef755252be90caeb \ + --hash=sha256:2dbe06fc16bc91502bca713704022182e5729861ae00277c3a23354b40929740 \ + --hash=sha256:3363fcc96e665878946ad7a106b9a13eac0541766a690ef287c0232ac768b6ec \ + --hash=sha256:377fe3732edbaf78ee74efdf2c9f49f6e99f20e7f9d2649fda3eb4badd77d76e \ + --hash=sha256:3ac6aff147deb9c09461b2d4bbdf6256831198f5d8a23f5d37138213090b6d8a \ + --hash=sha256:3f45789ce415a7ec0820dc4f82925f9b5f7732070be1dec1f5f23ec381435a24 \ + --hash=sha256:4103b77b8a8225e413107d2349b65eb3c1c52627b5cc5c3c4c1c6a798b218950 \ + --hash=sha256:4377407001ca3c057773f44d8ddd6358fa5f691407c1ba92210bd3cf8d9e4c95 \ + --hash=sha256:46c2f213e23a04b93a392942d782eb9e413e6ef6bf7c8c53884e599a5c174dcb \ + --hash=sha256:47e98aab9d8d82ff682e7b0b5dded33bf138a32b817fcf7fa3b27b2d7c412928 \ + --hash=sha256:4a36f9becdd4c5c52a20c3e9484128b070b1dcfc8944c006f3a528295a359a9c \ + --hash=sha256:4af7b7e1be0a69bee8210735fe6dcfc38879adfac6d62e789d53ba432d1ffa41 \ + --hash=sha256:4d97a951a81039050e45f04e96689b58b8243fa5e62aa14fe67cb6075300885e \ + --hash=sha256:4db9aecb141cb7a5447171b57aa1ed3a8fee06af40b992ffc31206c0b0121550 \ + --hash=sha256:53e549287ef628fecba270045c9701b0c564563a9b0577d24a4ec75b8ab8040f \ + --hash=sha256:56b149b22de33b23b0c6077ab9518c6dcb538ad462e1830e68d06591ccf6e38b \ + --hash=sha256:570fec8fbd22b032733625f03f10b7ff023bc399213db15e72a7acaef28c2f4e \ + --hash=sha256:5b8ee53be440a0cffc991a27be3057e0530122548dbe7c0892df08822fce5ede \ + --hash=sha256:5ba4f78df2bcc19f764a4b26a8a4f5049c110090ad5825993aacb052bf8003ad \ + --hash=sha256:5c55256dee8f4b27bfbf636c8363383c7c8db7890c7cba5217d7bd5f5f21dab6 \ + --hash=sha256:5c88e5815a49d289e599f3513aa7fde0bc2092ff188f99c940f007f90f53d104 \ + --hash=sha256:5fede79c6f73ff2c3ef822864cb1ada23196e62756df53bc6231d351a49516a2 \ + --hash=sha256:65be18ec59496c13908f02a2472751d9ef840b4f3fb5726f129306bf6a2a7bba \ + --hash=sha256:66410eb6345d467151934b49bfa70fb32f5b35a6140baa40ad97d6436abea2e9 \ + --hash=sha256:665b0a2c463cc9423dd647e0bfd9f4ccc9b50f768c55304d5e9f80b177c1de12 \ + --hash=sha256:6b8536851f9f65e7f00c7a1d49ba7f2be0ffe2c11555367fc9f50d9f842410a1 \ + --hash=sha256:6c95b17fe34ed802f17e205112e6e10db92275c34fee290aa9bdc55a9c724027 \ + --hash=sha256:6e73e7fe93f17a7b191f52ec9da9dd8c06a8fe735a1ecbd13b97d1c723bff385 \ + --hash=sha256:6efbccc3d7f75d5b03105172a8dc86d82ba4da86817952529dd93185f4a88be2 \ + --hash=sha256:709f1efed56c4a145793c046cd4939f9959bcd818979a787b77d8e09c57a0840 \ + --hash=sha256:79af890482fc94648e8cde4c68620378f7fef60932710fa17a66abc039244da2 \ + --hash=sha256:7bcbe0fcf850eae67b6b01749815a4f7161c560a844c769ad7b48fcd99f791c4 \ + --hash=sha256:7c0494a31a1ac5461a226e7947a9c9b78c44e1dc7185164fa7e9651557a5d9bc \ + --hash=sha256:7ce27823052e2013b597e0c738b13e7e36b8ccb9400df8959417b052ab0fd92c \ + --hash=sha256:7f72c74aa99359e27a2ee8d6613fefa28b5f76a983c083074dfc2aaa4ab46213 \ + --hash=sha256:7fa5e51397466ea7e98de493fa2ff1b8193cfef8a7b0f9b4842f92d342df0dba \ + --hash=sha256:82632daed195dcc8ea664e8556dc9bdbd671960fb3776bd92806ce05792c2448 \ + --hash=sha256:82f75e05912e84b7a0fe57075d9c59de3cb352b928330f2eb69b2e1f54c3e1f0 \ + --hash=sha256:841f0852f48fefea3b12c9dfec00704dfa3aef5215d0e3ce564bb3d7cd8d57c6 \ + --hash=sha256:874019bd513008b009f58657134e5d0c5e030b3559bd0553976837adf52fe966 \ + --hash=sha256:88f50c94e21a0a7f14042c015b0eba1881af78562e7bf007e0033e624da59750 \ + --hash=sha256:89a1bbb58e0e3f7a283653d854b1e95d65e5cfd4af224dac5f02629ec1a3e621 \ + --hash=sha256:8a6987eaad834cb32dd57d9d582225f0054a5d1af706ccfbbdba735af4927e13 \ + --hash=sha256:8ac73abdc7ab75610f95a8fd994c6457e87752b02a63987e188f937a1fc180f0 \ + --hash=sha256:8ccf9aca873b767977c73df497a85dbedee4ee086ae9ae49dc461333b9b79f58 \ + --hash=sha256:90333fd89b43c0d08ac85f3f1447593fc2c66de18c3d6378d7125ea118dc7a54 \ + --hash=sha256:92ab3e11448f2ff7bf53c5a26eff0edc086898ec8b21fb154b85839ce1d88075 \ + --hash=sha256:9335a099ad87287c37fe5d1a982ff392fa5efe5d14b40a730b1ec1d6a41382b4 \ + --hash=sha256:96d30286dd02679e32a39aa8f0b7498fc847fcda46cfc09df5513e82ce252440 \ + --hash=sha256:9baafc71b04f8f4bb0703b21d6fc9f0c30b346c636a532ff16ec8491a5ea4b1f \ + --hash=sha256:9d1216a7f6f77836617dba35687c5b78a4170afc3c3f18fc788f785ba26565c4 \ + --hash=sha256:9d399bdcfb4a0f659b9b3788bbc89babe63d9a6a65aacdf4d4e7065ff2e6316c \ + --hash=sha256:9e4e16c73d717c5cf27626c524d0a2e261ad20e46932b2670f64ad5dde23e26f \ + --hash=sha256:9f4d8cf085a4c6a40fb97ea0f46938a8df43c85d31f9d45e2a8867ea9293790d \ + --hash=sha256:a33700d13d9b7d84fd10947b09ff69fb9a792e519c8cb9764a3ca70baa6c23a7 \ + --hash=sha256:a3732e66413163e72508da9eff9ce9d2846fde51fae45d3605393d3e6cd303e9 \ + --hash=sha256:a4582acf7ef76482f6f511ebaf1946dae7f2e85ec4728b81a678c01df63bd723 \ + --hash=sha256:a61834fb15d81322d872eaafd333838ae7c9cea84067f232656f75965933d047 \ + --hash=sha256:a7cff474ab7cd149765bb784cf6d78b32e18e20473fb7bda860bce98ab58e9da \ + --hash=sha256:a8fe66b8f300da93798025a785a5b90b42f3810dc2b72283ff84a41aaaebc293 \ + --hash=sha256:a929d878fec099030c292803b31e5d5540a7b6a31e6a3cc76cb4685fc2a2f51b \ + --hash=sha256:ad5d8201d310b031e6cd839d9bac2d4e5a01533ce5d3d5b50b7de1ef3af1de61 \ + --hash=sha256:af3aefa655adb5869491fa907e652290386800ae99cc50095cba71e2c6aefdca \ + --hash=sha256:c0ebc836c47a6477e182169c6a476fc691d12b518894bf7dd2572f0d59f1c7ed \ + --hash=sha256:c687ed078e145f5fd53a14854beff320e1d2ab76df03e2009c98f39a0f68f39a \ + --hash=sha256:cbb833ccacdb5519eff9b8b71ee618cc2801c878e77e288775d77c3a2ced858a \ + --hash=sha256:cf139c02f5f23ef6532040a30ff662c00a318c952334f211046b8e60b7f17688 \ + --hash=sha256:d46b86567dd4e248c6c159fcbcdcce01e0a5c8a7cd2334a0fff759d0fa075b16 \ + --hash=sha256:d693396e5aea78db03decd60aec9ece16c9b40ba00a587f089615ff4e718a81d \ + --hash=sha256:d897129df1a22b12aeed2c2c98df0785a2e8e6e0bde87b389491d0025c187077 \ + --hash=sha256:daba5e594f06114e37db186efd2dd916609071e59daca901a0a2e71f02b142ce \ + --hash=sha256:dd625535328fd9882374356269227670189adfcc6a2d90284f323c05862eecbd \ + --hash=sha256:e006d3a974c4ee19512e5f058abedb6eef36a5e553c14812bdeba1758d812e6d \ + --hash=sha256:e1ae548a9d901adca07899a4147a7c826bbcc06239d3ce9a59f57886a28a4c88 \ + --hash=sha256:e2935f8c39e3b03e83519292d78f075189978f3f4adc15a78144c7c8e2a1cba5 \ + --hash=sha256:e42d75862735da90e7fc5a7b23db0c976f737113a54b3c9777a9b665e9cbff75 \ + --hash=sha256:e7d42c531243450ef0d4d9c172e7ed6ef052640f195629065041b5add4e058d1 \ + --hash=sha256:e81b83143bee16329c23db3c1b2d82b29892fcbcb849186d2f6e98a5abe9a57f \ + --hash=sha256:e8ffa78582120024f476a611d7befc123cee59e47e8309d470cf667d806e613b \ + --hash=sha256:ebb0ec7f17803063d5aeb982f3b1bd2b2f4e4fae6751226cbd6ba1fcfe9e63ff \ + --hash=sha256:f08c7513ecef5aad65687bfdf6bc601ae9fccd04a42904501f8f7141abad9eb9 \ + --hash=sha256:f0a658a6d3fafee5c6f63c58f3e785c8c43c93fbc02bf9f2b6663f8185e0971f \ + --hash=sha256:f0e466ed7511fe9d459a819edbc6c2585c0b6eabde9fa8a8947552468a7a6ef0 \ + --hash=sha256:f141474e85b7e54998ec5180530a7cda99ab29e282fa50e0756d89981a9b43c5 \ + --hash=sha256:f4239bbec5a3577ddb49e4b50aeb32d8e5792098262ae2f63723f916a29b1a25 \ + --hash=sha256:f540c013589084679a6c7fac07096b10159737918174f5dfc5e11bf5bca4dfe6 \ + --hash=sha256:f9f3e9c8a9ecffa57bef8fb4fa19e5fa4d2d8307cf6bac5b1fca5e5860f4ba00 \ + --hash=sha256:fa139875ff98ab97da323cfadfaff08900d1ad42f1b5087b0b812a55c5a06373 \ + --hash=sha256:fcd3b77e2f17bbe4ca56ec7bcb07992647d19d0b9c05d84886dcd6f9eb810afd \ + --hash=sha256:fd8c81f346b58f45818d09ea11db69a8d5fd34a224b79871f6d44f12cd7977b1 \ + --hash=sha256:fe7b7bb170daccbba19ad33012d2b15f1e7942296fd4d45fc1b79013da8cc0f2 \ + --hash=sha256:ff330d3c30db4eb6b01d79e29d2d0b407a7ecad39cfd9ec993ece57396a2ec0d \ + --hash=sha256:ff405d91509d88e8d44129cd87b18d70acd1f0c1aeabd7bc3c46792b1fe2acba \ + --hash=sha256:ffcd54362564dc1a30fb74d8b8a6e5a6b11ebd5e27266adc3b7427a21a6c9104 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # aiohttp + # postgrest + # storage3 + # supabase + # supabase-functions +zipp==4.1.0 \ + --hash=sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f \ + --hash=sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602 + # via + # -c HuggingEnvs/04-data-agent/hf/locks/requirements-env.lock + # importlib-metadata diff --git a/04-data-agent/hf/locks/requirements-train.in b/04-data-agent/hf/locks/requirements-train.in new file mode 100644 index 0000000..c6aee7e --- /dev/null +++ b/04-data-agent/hf/locks/requirements-train.in @@ -0,0 +1,33 @@ +# Direct pins from the frozen comparison trainer environment. Vendored TRL/OpenEnv are supplied separately. +torch==2.11.0 +vllm==0.25.1 +transformers==5.14.1 +datasets==5.0.0 +huggingface_hub==1.24.0 +accelerate==1.14.0 +bitsandbytes==0.49.2 +kernels==0.15.2 +trackio==0.33.0 +gradio==6.20.0 +httpx==0.28.1 +# Harbor -> Supabase requires websockets <16; matches the validated environment service. +websockets==15.0.1 +harbor==0.22.0 +daytona==0.203.0 +pydantic==2.13.4 +fastapi==0.136.3 +uvicorn==0.51.0 +mcp==1.28.1 +fastmcp==3.4.4 +tomli==2.4.1 +tomli-w==1.2.0 +ninja==1.13.0 +peft==0.19.1 +# vLLM's Numba requires <2.5; mistral-common on Python 3.12 requires <2.4. +numpy>=2.2,<2.4 +safetensors==0.8.0 +rich==15.0.0 +typer==0.27.0 +python-dotenv==1.2.2 +packaging==26.2 +jinja2==3.1.6 diff --git a/04-data-agent/hf/locks/requirements-train.lock b/04-data-agent/hf/locks/requirements-train.lock new file mode 100644 index 0000000..0458054 --- /dev/null +++ b/04-data-agent/hf/locks/requirements-train.lock @@ -0,0 +1,6368 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile HuggingEnvs/04-data-agent/hf/locks/requirements-train.in --python-version 3.12 --generate-hashes -o HuggingEnvs/04-data-agent/hf/locks/requirements-train.lock +accelerate==1.14.0 \ + --hash=sha256:41b9c4377a54e0b460a959b0defa1b736e4ca0a2373252d9a539964c2afe3c8d \ + --hash=sha256:e94390c2863b873be18f623f9df48a0d8fe5eff13ea7f1a00092b0a7904888c6 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # peft +agent-detector==2.0.0 \ + --hash=sha256:19c8ae185dbdca23c24abdcd8ac2cf58b8e84dc284c6f6db536656601f9ffc3f \ + --hash=sha256:22c6a1c9c23894a12f55aea625f060b8d3330fba774de72d02d5442bb580417f + # via fastapi-cloud-cli +aiofile==3.12.3 \ + --hash=sha256:5c1bcc9e929c50834608e8cc1a4cc1d7503eb60c15a535b779fd39e2f372c017 \ + --hash=sha256:caa6aa746b5e47e2165f7abd741b6415e49cf4d44fddc0f61844612cc3924d41 + # via py-key-value-aio +aiofiles==25.1.0 \ + --hash=sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2 \ + --hash=sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695 + # via daytona +aiohappyeyeballs==2.7.1 \ + --hash=sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d \ + --hash=sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472 + # via aiohttp +aiohttp==3.14.3 \ + --hash=sha256:03cd2bde3d7f085b64e549c985f4bb928cad7e8ecf5323bfca320db548d81b39 \ + --hash=sha256:041badb8f84396357c4d3ad26de6afd7a32b112f43d3c63045c0c8278cfd2043 \ + --hash=sha256:0a5ff2dfbb9ce645fa5b8ef3e02c6c0b9cc3f6030ff863d0c51fffc50cb5541b \ + --hash=sha256:0fdea2281997af69da84c77ffa6f5938a0285f21fb3887c249d67419ca865b3d \ + --hash=sha256:11fb37ef075669eee52ab1928fbf6e1741fada40409fa309ebde9607a962aebf \ + --hash=sha256:134ac5ddcf61c6fad984b9a5727d83492ada43d63471db20fb73042c13fca62f \ + --hash=sha256:152516815ef926786a0b6ae2b8f1fd2e0c71582dee0b435636865316fd4891b7 \ + --hash=sha256:1576145bdceeb92382d899751e12743a3a5b8e460a841e3e50543859e54864dc \ + --hash=sha256:16100ad3ab8d649fdfbee87602d9d2dcdca9df0b9eda8a1b5fdc0d41f96da559 \ + --hash=sha256:16ea7e24c309fb7c0bbd505d149abe4fe4dccfb8db911db7dbec0921bc889a6f \ + --hash=sha256:18c441d0a8fca6de8d1f546849b9f0ab20d435993e2c5b59562b2fae6be2f929 \ + --hash=sha256:18cb43369747b2ae007bd2655fb8e63a099c2ff1d207962943636dac989b3147 \ + --hash=sha256:1b59533861b70a2185c8f4f350f791f39d64358ef6944ce71c5240c9ec0982c9 \ + --hash=sha256:1c5281acc88b92396f88c7e1e2748f8466689df22b80170e4f51efa712fb47a8 \ + --hash=sha256:1c5ec8fb1bcc31a8466f74aaf26c345d5c386fa4bd08a3f0eb9c7a4a3fe8b5bf \ + --hash=sha256:1caa7b0d05f3e3a36f87788c59e970a7ee1cefcfcbb924a9f138c4a6551c9cb7 \ + --hash=sha256:21c016079415ed3fd676963e9793700a566d85dbbd6bfc564b9b2d209147dcc8 \ + --hash=sha256:2498f0fe69ead802f9675beca44a7c21c62fdaa4ec5145ea1c3ad6edbee29f85 \ + --hash=sha256:25bd2708db6bdf6a6630dd37bdcdfcb47c4434d22ac69c64665b802910140b30 \ + --hash=sha256:270d3dace9ca2f10f0da5d8ebe519b7a310fc6112ed916e32df5866df0888553 \ + --hash=sha256:2e1161602f45a54de2ce0905243a95f58cb42dcd378402f3697f5e0b21e9d2e7 \ + --hash=sha256:2e9878ae68e4a5f1c0abe4dd497dbc3d51946f5837b56759e2a02e78fa90ef86 \ + --hash=sha256:30402d03a7c0ff52bce290b57e564e9079fd9d0cb545c8aba73f86a103162d2e \ + --hash=sha256:33a2d7c28d33797a2e99923dffa63f83d908a19b6bf26cfe80fa790aa5e1a75a \ + --hash=sha256:362a3fd481769cac1a824514bcd86fda51c65e8fe6e051099e008fddde6db17c \ + --hash=sha256:38901a84da3ce22249f6e860bf8f90d141bcab7da090cc398f8bb58c0e44b7da \ + --hash=sha256:39aded8c7f3b935b54aab1d8d73c70ec0ee2d3ec3b943e0e86611bc150ba47f5 \ + --hash=sha256:3a26434dafe408229ff3403458ca58de24fb51936504decac49ce6755f77e59d \ + --hash=sha256:3ae5b3a59436d089b5395d910121a390feed4d00578eb95a0fd1a329fe963100 \ + --hash=sha256:3d4f72af88ac2474bb5bca640030320e3d38a0163a1d7533500e87be458eef71 \ + --hash=sha256:3f42e9b78301f11c8f861746175d8b9c1ccef713fcad9eab396e2f6db8ed4a22 \ + --hash=sha256:42a67efc36300d052fb4508a53e8b6901b9284b599ae63945c377569c5fcc1e1 \ + --hash=sha256:48d67b87db6279c044760787eb01f6413032c2e6f3ba1cafaa492b1c8e578479 \ + --hash=sha256:498c6c623134f8e09a3c4e60bcd607a0b4590dd7dbf08dd40851b27cbb520ccb \ + --hash=sha256:49f7325beb0f85ef4aef5f48f490269575f83e6e2acad00a1d80b807eb027062 \ + --hash=sha256:4e3ac92d90e92773b2362d506068e9a948192bd553e743c5b2429e28527c8661 \ + --hash=sha256:530125ee1163c4219af35dc3aa1206e541e7b31b6efc1a3f93b70a136f65d427 \ + --hash=sha256:5373dc80ad1aa2fb9ad95c83f24eef418bbda3a61375f128e5b0192e4f3f9b32 \ + --hash=sha256:53e5179d8abb5710f8e83ba207c41c8d1261fcffd4616500e15ca2b7a33be10a \ + --hash=sha256:53e7b4ce82b54a8bcc71b3b67a5cbd177ca1d7f592cbc92cd38b7349f73482db \ + --hash=sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42 \ + --hash=sha256:54cfcdee2770dac994417cbb0ee1f3eb0e7cb6b30c79bf44f2c02ff79ec5124a \ + --hash=sha256:55bdcc472aafe2de4a253045cc128007a64f1e0264fb675791e132ea5edaa3bd \ + --hash=sha256:56f355e79f71aef2a85c80305cc915f894b170dba76de5fe84f6351939b83c06 \ + --hash=sha256:5895ef58c4620afe02fa16044f023dc4dafec08158f9d08874a46a7dbc0341b8 \ + --hash=sha256:5bcb6ff3fdab1258a192679ff1a05d44f59626430aa05cd1a9d2447423599228 \ + --hash=sha256:5f08ec777f35ee70720233b8b9811d3bb5d728137f30ac91b7457709c3261ac0 \ + --hash=sha256:614c61d478b83953e261d02bb2df750f17227cd33ef8002945bf5aebbde21919 \ + --hash=sha256:617105e2c3018ee38d0c8ce5ee3c84f621a6d8b9f723202aacaff28449ca91ee \ + --hash=sha256:6debfa7312ff9d4c124dc71d72e9a0a4b9e0879e48ba6fcb42bef5c3300289e2 \ + --hash=sha256:7041d52c3a7fa20c9e8c182b534704abb19502c8bdcbde7ab23bfda6f642394f \ + --hash=sha256:70c987b27534f9ae1a723f47ae921571d616da21d3208282bf4c52af5164ac43 \ + --hash=sha256:74ab5b6a9fb13e873e5a90946588baecaf488745e1db1a4a5c433f971f035098 \ + --hash=sha256:78253b573e6ffab5028924fc98bc281aae05445969982a10864bc360dea2016c \ + --hash=sha256:7a75aa63cbf9b21cfaf60dc2657e19df2c2867d91707d653fee171ffeedd1371 \ + --hash=sha256:8800c996b01c2772a783e3e46f3e1abd5823029adca0df54231960de9bfefa5b \ + --hash=sha256:89176250f686cb9853c0fb7ead90e639e915b84a6f43eedc2a4e7ec21f1037f0 \ + --hash=sha256:8a5fd34f7f7410d1730d5c2ba873cacb2eed3fede366feb268a70ba22581ed8f \ + --hash=sha256:8b3b60de05f3dcb6f6a00f818bb2ec781cee4de0645f59ccaf99b1d1823b6100 \ + --hash=sha256:8f2f1c4c032c7cedd7d8da6f54c97b70266c6570c3108d3fdffee7188bb70529 \ + --hash=sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc \ + --hash=sha256:9aa6e61fdf20105c4144e755bd586008ff450791d67b1c8146fdc15959c4d51c \ + --hash=sha256:9d9edccfe496b476db5f398d97b865e9a6752bcf8aec4eef8390ce20fb64bb41 \ + --hash=sha256:9fc7b5bfec6573f3ae844f457fdde5adeb713f8b8e4a81ad64fc207b49383716 \ + --hash=sha256:a0dc483c00da8b673abbb367eb6f8d8f4bcec30eb58529ea13cb42e7fd2dfa33 \ + --hash=sha256:a3a8296e7ab5c295f53f1041487cb088e1480775aafbf7fe545d93b770a0f96f \ + --hash=sha256:a3e22975f905b89a55a488c2a08f2fdb2186175349e917d48985cc468a3d4c6e \ + --hash=sha256:a4af35c443e0b1a1bd6a8af3f3485d7fda15c142751a00f3ff8090f0b93346fa \ + --hash=sha256:a94dbaae5ae27bd849c93570669bff91e0510f33a80805738e3de72a7be0447b \ + --hash=sha256:ac74facc01463f138b0da5580329cfcc82818dea5656e83ddcd11268fc12ff80 \ + --hash=sha256:ad4c8b7488d745d2ca4838ebd8ae5ba9b56341d30b1da43640e4ce87f9f49646 \ + --hash=sha256:b014a6ed7cf912e787149fdc529166d3ceabac23f26efeea3158c9aba2354e7e \ + --hash=sha256:b20032766aedf6261c7a566585a40867d092ac03a0d81592d5370ef9b054f99b \ + --hash=sha256:b2466434105a4e03113c36ec775cc2ebe6676b62eae326fa670bb607ef788c1c \ + --hash=sha256:b304db572b4368edd8dda8a2274f73156fe15558fca4a917cb8a09fc47af5963 \ + --hash=sha256:ba59d59aba08ac02fc03b0c8983ccd5ee39a199d0552ce9e6d2b4845b34d59ae \ + --hash=sha256:bd52f811e65f6fb634b1047159657c98f52b407f8efec907bcfc09da9a4c0a25 \ + --hash=sha256:bdd0e2834dce1a26c1bbe26464861e16bbe217042cbff619247c11594472518c \ + --hash=sha256:c23ec8ee9d5ab2f5421f9c7fffce208435607af27fd46d4a44e031954352838f \ + --hash=sha256:c39846c3aad97a8530c89d7a3869a8f8e9e3762c6ac0504481e5c80948f7e807 \ + --hash=sha256:c3c200cf9757edd785051dc699c7ecbec22110dbfcb3fefc7a9f9695eda8ea7a \ + --hash=sha256:c7d3a97c678d34fc5b59da671ee9cd630096ddc643e7b5a30d54a2a6f3574d3f \ + --hash=sha256:c8653fd547c93a61aadc612007790f5555cdd18946fa48cf45e26d8ea4ea473d \ + --hash=sha256:cc7cb243a68167172f48c1fd43cee91ec4b1d40cefd190edd43369d1a6bc9c82 \ + --hash=sha256:ccd4893707b3e2a13e39c90d43cf80edf2e4d0457935bcc103bf2346214c3f15 \ + --hash=sha256:cd817772b2fcf2b8c0905795318485f9ec16eae60b29feb7f4c77085311637f0 \ + --hash=sha256:cda5fd5c95ad7a125a2e8464acc78b98b94c475a3780d6aa0aa157c93f470f4d \ + --hash=sha256:cef89a58e628c4efcac3275c2d68083f82426dcdc89c1492a6f654f9f7ea6ab9 \ + --hash=sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19 \ + --hash=sha256:d6088ec9894113802bddb3c09e974929aed2c7b3a8c456219b8aab4481f1a239 \ + --hash=sha256:d6218d92e450824e9b4881f44e8c09f1853b490f9a64130801024a4793b1b3b0 \ + --hash=sha256:d77640cc618c1d99fc4f8589c0f24a730adfa54eb1e57ef7bf0c8dfb78da898c \ + --hash=sha256:d7d2deec16eeedf55f2c7cf75b521ea3856a5177e123844f8fd0f114ce252cb5 \ + --hash=sha256:db332af25642007330fca8be5c4d194caf2bea7a7fc84415aff3497af5dfee6b \ + --hash=sha256:dd54d0e8717de95939766febac482ac0474d8ac3b048115f9f2b1d23a16e7db4 \ + --hash=sha256:ddcac3c6b382e81f1dd0499199d4136b877beb4cb5ef770bbbfba56c4b8f55d2 \ + --hash=sha256:df82f3787c940c94986b34222d59c9e38843fba85139f36e85255a82ad5355a9 \ + --hash=sha256:dfa68deb2a443bdaa3ea5297b0699c1464f08aef3812b486d1348eee61b07dc0 \ + --hash=sha256:dff9461ec275f22135650d5ba4b4931a11f3958df7dfbb8db630000d4dee0883 \ + --hash=sha256:e1e74298bab6ee0d6e749ed4fd1901c7e604bdda32c03d787a2cc71c46d0433d \ + --hash=sha256:e2667f0bbe7eb6c74eae5e9691441ad186e5845ca3cff63230fc09c4e7514f5d \ + --hash=sha256:e3be98a7c30b8c25d573dafba7171d66dfb05ee6a9070fc46535464ff97700a6 \ + --hash=sha256:e568e14940c09955aa51f4e645b6daa18a581c5dcfcd73744dcc86a856e3ced3 \ + --hash=sha256:e72ee89e28d907a18f46959b4eb0bb06701cc7f8cf4366e00029e2ccfaaf5924 \ + --hash=sha256:e92eb8acc45eb6a9f4935071a77edf5b85cc6f8dfad5cd99e97653c26593cdde \ + --hash=sha256:ea05e1f97ceea523942d9b2a7d7c0359d781d683d6b043f5943a602b14da4787 \ + --hash=sha256:eac645b09bcfdf73df7536331f0678c1086ea250981118ddb5199e17ccef72bb \ + --hash=sha256:eb0495d778817619273c108784292be161a924b9f5ae5cbbc70a2caa6838250b \ + --hash=sha256:ebe8e504f058fe91223351cecd2d9d6946c9d241bb0250d898ffbdf584cc72b0 \ + --hash=sha256:ed099d105449c4f9e84f24af203cd131349d4761d8813fa7e02c32e7128cd910 \ + --hash=sha256:f0f177d1b195b9e06376cfd7d308d8a1b920909a609d03ac82a8c73bbb16d3b9 \ + --hash=sha256:f3d2669fe7dec7fc359ecdb5984b29b50d85d5d00f8c1cb61de4f4a24ee42627 \ + --hash=sha256:f4e05329faa0ea1a404b37de4f034fd2c2defcca06a68dc6745e4e56c88e8a48 \ + --hash=sha256:f53bcd52f585e1ac3e590d61434eb61f9a88c38df041b4ea126d97144344a77b \ + --hash=sha256:f55119f7bf25f49ed210f6096090715da24f2943c62102448915fde3c62877ce \ + --hash=sha256:f631fe87a6f30df5fbe6d79640b25e4cffb38c31c7fb6f10871517b84b0f8c1a \ + --hash=sha256:f8fb78a83c9e5f741ca3a68cfb455c1f5bb83b4e7249a3848b3cd78d0a8563b0 \ + --hash=sha256:fa9467a8113aa69d3d7c55a70ef0b7c636010a40993f3df9d9d0d73b3eb7ef24 \ + --hash=sha256:fd51ebf9d3a00c074df4ede271023f4d2dba289bcc740b88191872716014e3c5 + # via + # aiohttp-retry + # daytona + # daytona-analytics-api-client-async + # daytona-api-client-async + # daytona-toolbox-api-client-async + # fsspec + # litellm + # python-socketio + # vllm +aiohttp-retry==2.9.1 \ + --hash=sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54 \ + --hash=sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1 + # via + # daytona-analytics-api-client-async + # daytona-api-client-async + # daytona-toolbox-api-client-async +aiosignal==1.4.0 \ + --hash=sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e \ + --hash=sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7 + # via aiohttp +annotated-doc==0.0.5 \ + --hash=sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101 \ + --hash=sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb + # via + # fastapi + # typer +annotated-types==0.8.0 \ + --hash=sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7 \ + --hash=sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0 + # via pydantic +anthropic==1.5.0 \ + --hash=sha256:b25f87f5758861f25993383a5c9bf274eb6e0f1b010c84019ad91854cb4e1bc6 \ + --hash=sha256:d9ce04b29ad1f7025dda3e3bc478cde8d2924d2de5417d2d4a4bb0378ecbc2a6 + # via vllm +anyio==4.15.1 \ + --hash=sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101 \ + --hash=sha256:9f28306018cbd6d329e64a36d58256edff76dd996fe423bc957326e578b82a94 + # via + # anthropic + # gradio + # httpx + # httpx-ws + # httpx2 + # mcp + # openai + # py-key-value-aio + # sse-starlette + # starlette + # watchfiles +apache-tvm-ffi==0.1.9 \ + --hash=sha256:01ebb1308b2666c206aa9a4015eb48f03a5d98ea2e9cfb002bd5e2ca0b9c7ef3 \ + --hash=sha256:076ae2b4bb1dbc5bd3ad8cb5699460fb87c06c0d320659b75a2d651f3f090525 \ + --hash=sha256:0e326ff3ccea0f9b03a7270ab5203ebed9c4cbfc55b6c2a77610bc2fe98ee7b7 \ + --hash=sha256:1401693fc2bd4504878e19c4e87910c46029954f63890a48cc80dfc21f8db31e \ + --hash=sha256:1c8dd4018420c0d14bace688594710909ce198056ff8ac2ad1cd462b30fe1bdd \ + --hash=sha256:21365abd2a2a1a6d3b4e6e4f048309651125becfa795440c3607f3cc27d30ac7 \ + --hash=sha256:28241371934ea8af10d5067087ba1229ebddded7b2c02d33a258ec2a96df8c46 \ + --hash=sha256:3ba2b01f7eb2d9bd7e243d48c74fd93387b236fa8ccae27ba14bc01f423e4ce3 \ + --hash=sha256:3c0581dd6bfbce7b017ef85cfda08bbe38891cc4b3afbcfaa8bc2d383728e426 \ + --hash=sha256:429be745bc6a9301cb48f16f97c3f07723e959cf7094b9d571a07a66284ae244 \ + --hash=sha256:44e7967fa0c6493b6f81756688409bc465af620b1731e4940f0ad70d0e8a7ce6 \ + --hash=sha256:49e52350b0470654847de752e65603b604a4d3323e7e9f5e8a982f44acc4c143 \ + --hash=sha256:6845535b79e089743befe1e67c6cf8687a8aa548c23ab3e73228e2c656d0b007 \ + --hash=sha256:6f16d73a82a9e68a439b7d233d48b1b929be17fe92df4bbf1ee2274e573144a3 \ + --hash=sha256:707edfb9c1a50534a14527b341fbd496a6c0f9f3b209e51faafc81fd2aab2923 \ + --hash=sha256:71fe51ca8993baed194d0506b91e6266e6abde5550d65dfff2324f569ee7f37d \ + --hash=sha256:7445d767c8561adfe81c4697e1781269f8e84e001b86e153f916e3c77210d55e \ + --hash=sha256:7d503029e66c43b1a1cb1a42a1e9bb428c8a28dcbdec31c28e705472ca648a3a \ + --hash=sha256:7dfa14be2a49347791ef21222a8225ce7f99bfec17104a676cb4f1bf3a107088 \ + --hash=sha256:7f6bc8846d570b8ce38692fc91b530b44cd6ae092c805a844da23970e81b12c0 \ + --hash=sha256:87cacce81df55685fc6a76e1e3c5db1200e85e87bf5974b692c59d131b7bc622 \ + --hash=sha256:932d94e29595a47109f0ef6e0b4209a934451582954ea8b426e758d6b3e307e3 \ + --hash=sha256:9ee710a9fba3d9ff9747870bbd7e2175eb8d5b9c791f17fd645f35f6dab3f8aa \ + --hash=sha256:a42d7ca27dce83efbdce7ec970fe3e773a69c31d928730ee5d9badb1229d106c \ + --hash=sha256:b4133c7c8f31ce7dd0fa8278f17bc9c8e72efd8aee0be4e12312330eb4639236 \ + --hash=sha256:c0449fc3802987c3652bea266ffda2934a6f69c80bba791a3f55b91040656a18 \ + --hash=sha256:c3349f72ddb8ce206472d0380a729f213017a2180707096f8d57114b81097dd1 \ + --hash=sha256:d1dcf4c041d5ec05e3da1d545800c33cdbb95c113baa7705085ff79fa262752b \ + --hash=sha256:d1f4d2b7ec7b1213632e9a104e9330bfc3dec48decffa62114c33aa188c9f43a \ + --hash=sha256:d2d402587e8906de0a07f4746aa78f3d452c7efe3625d4bb39ac2ad693bce530 \ + --hash=sha256:d911cbbc83bf12a0d9ec03e5315ff1bb92d95702fe912cd7a050393274382e71 \ + --hash=sha256:dd58da01331826fbe6c064d6f0c9bbc2d62883b78df8d15baa8ea21d37507e4d \ + --hash=sha256:e0fc70020d5ae953c04ae279272df7647eaf16fc988c9c5783aa550102942d1d \ + --hash=sha256:e4f01d16ba53fe118e363f7257253f07003797e4abe6fc9567f23b6a930dbff2 \ + --hash=sha256:eefcd17f61bf503ff0f4ad429e03ef6c241c7d13682f58281d883218b854c9bd \ + --hash=sha256:f3ec9149f207a7af3ea3531cad7a0b0d04ded06df4f51a547479d5eb489428dd \ + --hash=sha256:f45eb43499acac45ff6c93564f0ff2d3ca27b69656d540fd56ce59d51c0b4c65 + # via + # flashinfer-python + # quack-kernels + # tilelang + # tokenspeed-mla + # vllm + # xgrammar +astor==0.8.1 \ + --hash=sha256:070a54e890cefb5b3739d19f30f5a5ec840ffc9c50ffa7d23cc9fc1a38ebbfc5 \ + --hash=sha256:6a6effda93f4e1ce9f618779b2dd1d9d84f1e32812c23a29b3fff6fd7f63fa5e + # via depyf +attrs==26.1.0 \ + --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ + --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 + # via + # aiohttp + # cyclopts + # jsonschema + # jsonschema-path + # referencing + # scantree +authlib==1.8.0 \ + --hash=sha256:88aebbd9af6757e14e912d5dc007ae1dc1f3e27e3b2152ce7c552ee2c3b3c121 \ + --hash=sha256:f3ecd5f1da737262fb53bf1a4d95c4ea1ad9dd509316587a255c99ab1838a4f0 + # via fastmcp-slim +beartype==0.22.9 \ + --hash=sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f \ + --hash=sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2 + # via py-key-value-aio +bidict==0.24.1 \ + --hash=sha256:4dca6c17f0b01700e9f24359daa5ebabf7be022d99f4cb2a257b6af2a5076c88 \ + --hash=sha256:fd3eaa737917d8a14f4baa391670c433c4e3f6f5fd2cd99d4bf436437f432364 + # via python-socketio +bitsandbytes==0.49.2 \ + --hash=sha256:2e0ddd09cd778155388023cbe81f00afbb7c000c214caef3ce83386e7144df7d \ + --hash=sha256:2fc0830c5f7169be36e60e11f2be067c8f812dfcb829801a8703735842450750 \ + --hash=sha256:54b771f06e1a3c73af5c7f16ccf0fc23a846052813d4b008d10cb6e017dd1c8c \ + --hash=sha256:87be5975edeac5396d699ecbc39dfc47cf2c026daaf2d5852a94368611a6823f + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +blake3==1.0.9 \ + --hash=sha256:01e136de1078a9011c82beb4a1cb400644794068c0e80877415328bb46d93b47 \ + --hash=sha256:021309d760b390706fecf13498f9a25aa8f689bbb65a0896029b8fa223aae18b \ + --hash=sha256:09a69fcedf06785bb81d4d3d39f95ee65dbaf2cb246e174cfc9ff64d027f7551 \ + --hash=sha256:09deb024cd75cb200e7f647cd038800e6edc8f190c8188e0c69ec1c2b920e125 \ + --hash=sha256:0aba416bb2e3ef0c65e74d5eba21062483c714cd78e7e303c9d03c547fc7d015 \ + --hash=sha256:0cdfeff65488089ef86f7587c76055ff72b28d28d10e427b547f5711477c376d \ + --hash=sha256:128a62136c9a39c7cb9fdaa5fb38471f2418853da7f5a89f31495735d0ba6f2c \ + --hash=sha256:15566065ff90ab3da46ec0be1417406f00507af902b6fb0fbc6563e77f02fc42 \ + --hash=sha256:172d44245a19dfec08ab771c1b7a506b97783163cdc65f559fe020007e403c99 \ + --hash=sha256:188127f98106309e4c477977d29ee8588ba4b153ea5a7eb9bc202d5b9f05d0f9 \ + --hash=sha256:1bbdff61e049297ef3180867ce1f079cea7e5b372fd76953c3183da5b8124206 \ + --hash=sha256:1bd981dc318c05375c3160a99df493b7cc4c83fffa1a34d14b18a071b47b262b \ + --hash=sha256:1ff480ca2bee74f56d90658a44176f0abdd9b766a181586b995fee358a9c40e7 \ + --hash=sha256:216977b1d592a60150cd5de64d5853dc6afb0eb522cb387723ae7f78f380d947 \ + --hash=sha256:249e5964fa9e768924bc7cc3d4efe75a425bb5dd3fb7671c3eda8eeddfa50591 \ + --hash=sha256:24acb1e6f31021fa08b7eb31433035facfcf0d82e964170d5eb85a30ce913ba9 \ + --hash=sha256:28404301de485e9546365d01b30f65eaa835520c4211d6ef61242975b6722b60 \ + --hash=sha256:28528d1f29e6f3d45faf3482e1197e5e175730eef38bdc74e56ee11b68e0ad0d \ + --hash=sha256:2a800b87433955f37691b5f361ad29c7dd3ee089c9cd109adc5aea8e24bc4c1f \ + --hash=sha256:2ee96daaa850700fd342a811fa10a8780fd2e8464a71b83a1779c7b6becd3dd5 \ + --hash=sha256:2f4ce45da903f3d0a7e342fa70c7cce9c10cef6b529eadb4d6213be0ab0eaf84 \ + --hash=sha256:2fd000708662b04be211a22c1095b65fe399d7276e9f3bb2fd1ef8aacc545791 \ + --hash=sha256:2fff822c0a14b1378769f26eede9577e1230b289fa9a2e9d15c751b6d5a81cd5 \ + --hash=sha256:32bd4521ec2d477627ad93eb70f9ac4d01e12d1489024159bcaeff79466332f6 \ + --hash=sha256:34c947a95a68ec103846525e3a99193f9fcf5e762e40dcce80adbb8e398abef7 \ + --hash=sha256:38e61d3b0386af16b3c03a18e0db82b626d63796274637a1fef855fd1c778d82 \ + --hash=sha256:3cbe7f190164896dc3908e920716ee66bc31d40f1a0fb603ed59ac53290fb9cf \ + --hash=sha256:40bcc18675d583e23722d6c106ea3068ba705f7575f874c1ca14a6af0728ccb6 \ + --hash=sha256:42609e4adc4b2d7423137f2cb35135bca598b925c5af09d2bc0a2c368b25aeb1 \ + --hash=sha256:42fa57bf462285ef16400601b0fd32214c248ba92505bbb94b1221ab9af5a092 \ + --hash=sha256:4865a8cfb2b3d7c0baf5267f2fa6816a3384e836cd1bd0caf359f406cb1e8fba \ + --hash=sha256:4c9a8b093d0e57a9fb612dac768b329a0719c09357e7ce1e5403106a7533b5ef \ + --hash=sha256:508ccaf8f9377cc47e6026c2897fdc37de61faeb1420dc023b6379cc2474eb65 \ + --hash=sha256:54e35f3b71d5e2f5b1434a01f44cf386be2ff3dcd0ed6589eeb52a0076b2e016 \ + --hash=sha256:5b35abe24a66a7b3db423eb4f8668ed7be1a362aa9c0024ab6483ec0b2c16058 \ + --hash=sha256:5ea0c60dd9c1e3d05610606579e4bf80f562854c46ed55f9ee8545e18987a480 \ + --hash=sha256:5fa374fa5070ca084368776c19b420157eb0f2d3f091343d6bc59189929d62e2 \ + --hash=sha256:65c0c20014df687694af5ccf0cec3bdb194511da8ebd50c30b0fd55c83fa4fd5 \ + --hash=sha256:66aa1c40ad019fbb5e93d603300c4478a59b98c40c7ea13ac036676c26dfb1c7 \ + --hash=sha256:66c84dbc2a31eda88b55bbf5c5b711037bf0698eba0fd1faf06bdaf313c39048 \ + --hash=sha256:6879739e7904b9c42afbedbcc2e8c36cebe140fb3fc3f5c492993579cf5cd516 \ + --hash=sha256:689a7e4069de681d9c5d9445b8b6473ee880ad04d7960a6789c60bd788980250 \ + --hash=sha256:69ff5aebc7650954443aa701feff2028d7c7ea5b5e18ee265f15e2104e892328 \ + --hash=sha256:6c99afb0459c82dd13e456b6b68d45c4768b539ca998dacd3ed726f1e75e91dc \ + --hash=sha256:6edeb3d49a24c307995899b70dd47aa901d0e9ad51d2f8a79aba4f074f32d8c5 \ + --hash=sha256:6f2dd643166dfeb7cf4ad53eb2d801f944d247212d3481950b4d5b4a20551461 \ + --hash=sha256:6f5f60072fc7191d9918395bf2e701bcc60b44aeda0bf0a173abafcf49e10cff \ + --hash=sha256:72cdecf088a9d25e6ec79948a578995649b0dbee407e7a46c543a9ecc0f6f281 \ + --hash=sha256:73a48f7e9f0e047f51a445d9b0361ab1907bdc72b6857815a84dacd2e59556f8 \ + --hash=sha256:766f1555cbe614f14f399c2fbec0983568d20edb36837ba04040807eb9e1a609 \ + --hash=sha256:78833070b782bbd2b987a2a239c796f525e74d338d8cbc23557945548d5488f6 \ + --hash=sha256:7a760e8f8ac8b49761fc6a28ed5bbf6bbc8008026134498ec2b8b20f26850499 \ + --hash=sha256:8114fb2a1f6cba9cba5411d62cbcb283b2205b154d0076f20b77e22592eb2719 \ + --hash=sha256:82ecade6ac425fdfc39a4371d6d9232fd6e5c28748fd8d3489016ead17407014 \ + --hash=sha256:837e116ebedf06e48491f4c44a5e8804a864dad07cd7b897ebb507e410fed740 \ + --hash=sha256:83b4a2336105af3800f7e17ac4b943f293a3927a2d66a6308d50dba944a6953e \ + --hash=sha256:85262504baa150d64a599563d7a7bf19331fd7a2cc4c7b050cb31edfd69edbee \ + --hash=sha256:8a5917733828841e5bf6b9b7e63d62e0f70ec2cf262f5bd189db51c5a5508068 \ + --hash=sha256:8a99f896e7718050ed033a888245098aab3d6a5338f91cc9450c563b53f90ad5 \ + --hash=sha256:8adb0b0032e53919ee95b3d4f911448d3268316c28cd7df232ff2a1e7c9a4ba4 \ + --hash=sha256:8cd10c6a421a7d3c81136658e52e9ef58bfcc1df04193466664eb24981784f4c \ + --hash=sha256:91db52a809b68b5bebe7c413ddcd230e1f759398e7fa7a873104595a4fa648b6 \ + --hash=sha256:94d99e6ca7d5ca684bc48f3aa9ec6e78c72c775ae223d131b9efb217389f0195 \ + --hash=sha256:95fc3545f80901b0dcd0508d16bc40f15ae39556709fa6cf86675f742d4f3c9c \ + --hash=sha256:964b642631a3c8fe117b3439c8ae64a9a0981af9444e409656d1f1e464bfa125 \ + --hash=sha256:9767f16199b99aa022b61ff825ac4dbd39864bf637ae712605a2ce1f8b6a55e0 \ + --hash=sha256:9c0570338b4e3f6dc7943196edb029e0e4f74fd55d9703a40028f5b6c9c09dc5 \ + --hash=sha256:9cef6d4d07a7de0c44f5ba17f6383d55276d9efc8d601f75113538fcaa35008b \ + --hash=sha256:a288664d08dee154cc496e06e62517fc9e655ecec12b0d7db538d244ac79edf1 \ + --hash=sha256:a517f0460007edec3767595115c520ed1f157ddd0ed23dddbf6b9d8b0082afb6 \ + --hash=sha256:a70c20542d5e7960983a0ff32999049a2b0e5ef1f22dbbbdfb51cf04828a4156 \ + --hash=sha256:aa6e5c7533c915a24d840ae4be787e9a6059be7e77944b005b3d967a0257a17d \ + --hash=sha256:ab0c030cf6644c30e786b0e785bde4e4596013ae9ea6ce9877e39d52383e25d7 \ + --hash=sha256:ae47c3d5729ff89baa6ddf6de47fcfcc915985d39eb1bfcd6db653331f3c6fcc \ + --hash=sha256:b25ccde5a64be070f20e5c7a81da70292db40b164b6c77588cbd6230856badbb \ + --hash=sha256:b27550ada40f839aca64c66127940e4318bb6ef3e291890ef913017f6f637448 \ + --hash=sha256:b4102ba86b86c992a931b4a88c58a632d6097461e14a1e63ebd2ecb98ff0898f \ + --hash=sha256:b5d5bf0f68cd77108a942c95db98e960d9c3d5643b95172f783822ce22667759 \ + --hash=sha256:b5e1f21b49492d01fa5a02084894c491ab9e7a1867fced107f7126c80d067c94 \ + --hash=sha256:b985eb08db76550ec97444e03b10acd737baa03fd98aaf3b8455a1c644c8f5d6 \ + --hash=sha256:b9f7205531b41674316de9f15592c13ad3ebcedadb562fda8bbd8641c6f6ef8f \ + --hash=sha256:baebf1ebe6ccad936730a651dd74d40433debc6e19cbb34357222050fbf31189 \ + --hash=sha256:bb5e6c80e7c0825aa7223bca0dd2c6e7ea506db7ec734094631ce5efdae7a554 \ + --hash=sha256:bca166d0b01c00dcf2a936f790ed947bd9079b0a0a7df1b76746f201aa4f4ac4 \ + --hash=sha256:bcd56a7a972c4185070f7042ccc20166927eec3c0f98b8405f375d007b604a0b \ + --hash=sha256:c32a1f7ffa0a88c4f8dfb75f87297e8f076d3a63f714da99f7e763d8c435e14f \ + --hash=sha256:c755044ba7bec3d03dae44b968194112f0eb0e8c4523465f3dd9e1a87e178d89 \ + --hash=sha256:c7f648fa425138452d1e585ac625c7aefddb946d9765906c4c12d564a1523cd8 \ + --hash=sha256:ca7dfe8fb197ff8a3f5c915424183ccd52a99e8afb12680f51b2e1f4c9c6c97f \ + --hash=sha256:caded2806d2cbeed638c5e2517ed8b2a94165b3452fda35e72896142d22070e0 \ + --hash=sha256:cfaa671b07eb73883162ca940442193868358b0b904cfa266e4b74131ce966da \ + --hash=sha256:d1ea0bf17b184b03444007646d902207d2b4d4f3e91a0cac3836552d83db74b9 \ + --hash=sha256:d819457dccfd82fe34684ec99e36725f747bd5761a0e17f537387fb31d121193 \ + --hash=sha256:d9d13051df059730efe83c6df142ba5a2ad67b82d50f9a62f3a556d6c2af201e \ + --hash=sha256:dab59b324aa65c09e937d6c43de5de85ec9581627f4e79dcc9806d85b54a1c34 \ + --hash=sha256:dad0a8a716dd201860f8e82011a340e6bdd5ee37a8eb4357b48ac64c4e6de1c2 \ + --hash=sha256:e448a4f985b3e4709541dbce057e5bb073527001613f8eeb1ce2705988262f9d \ + --hash=sha256:e779710aa52a0fe4ad96cd6bcbcd5aee1bcee7a748221394e4a39a58b21d0bac \ + --hash=sha256:e9e1d0392624c2f9d049d786f0dc547ce818d2f2b356bcf1c4d74b6f9cc026b4 \ + --hash=sha256:eca281fedcbe5c56655bd5a4176e6036eddbbe57df96114a03838fce08b1e0ca \ + --hash=sha256:f169519c7ef25ef2c446b05e2f08e7e59fae312d569f98a3134b38d4caf7abd4 \ + --hash=sha256:f5c9d57f0dcb92243b6ae575c3065793edc9df9008d0ebd98d8245cdeb7c3f84 \ + --hash=sha256:f65d77eb05331495485048f6804f53885b192b998acb7e6fe1487d941bf08435 \ + --hash=sha256:f9d8b6c6af52a8ff499debf5fd15d7395e0ed837ec2a80fd828b4c6b970de3ed \ + --hash=sha256:fe3c59f8a7b5460fd374b264559c5da7af0a33657a9eca95fff6d843f02cde9e + # via vllm +boto3==1.43.94 \ + --hash=sha256:2534bf331acd2f448b9cf8317f4eed453c65d9e0b7de254e77c99d390ac57aec \ + --hash=sha256:aa832d88e85035cebb83f04700c65623328b88fe828ff7ed9539483bd9d608c5 + # via litellm +botocore==1.43.94 \ + --hash=sha256:1dfb86603a87fdaebda2540db56aef5b226ec58ab72c13ca56737e4d66dea9ab \ + --hash=sha256:cb97cedec5fceee3261f7fff75d0b0aa2cf68b32eeb2e8c2649e4657419773a6 + # via + # boto3 + # s3transfer +brotli==1.2.0 \ + --hash=sha256:022426c9e99fd65d9475dce5c195526f04bb8be8907607e27e747893f6ee3e24 \ + --hash=sha256:072e7624b1fc4d601036ab3f4f27942ef772887e876beff0301d261210bca97f \ + --hash=sha256:09ac247501d1909e9ee47d309be760c89c990defbb2e0240845c892ea5ff0de4 \ + --hash=sha256:0bbd5b5ccd157ae7913750476d48099aaf507a79841c0d04a9db4415b14842de \ + --hash=sha256:0cf8c3b8ba93d496b2fae778039e2f5ecc7cff99df84df337ca31d8f2252896c \ + --hash=sha256:14ef29fc5f310d34fc7696426071067462c9292ed98b5ff5a27ac70a200e5470 \ + --hash=sha256:15b33fe93cedc4caaff8a0bd1eb7e3dab1c61bb22a0bf5bdfdfd97cd7da79744 \ + --hash=sha256:1b1d6a4efedd53671c793be6dd760fcf2107da3a52331ad9ea429edf0902f27a \ + --hash=sha256:1b557b29782a643420e08d75aea889462a4a8796e9a6cf5621ab05a3f7da8ef2 \ + --hash=sha256:1b71754d5b6eda54d16fbbed7fce2d8bc6c052a1b91a35c320247946ee103502 \ + --hash=sha256:1ce223652fd4ed3eb2b7f78fbea31c52314baecfac68db44037bb4167062a937 \ + --hash=sha256:1e68cdf321ad05797ee41d1d09169e09d40fdf51a725bb148bff892ce04583d7 \ + --hash=sha256:260d3692396e1895c5034f204f0db022c056f9e2ac841593a4cf9426e2a3faca \ + --hash=sha256:26e8d3ecb0ee458a9804f47f21b74845cc823fd1bb19f02272be70774f56e2a6 \ + --hash=sha256:2881416badd2a88a7a14d981c103a52a23a276a553a8aacc1346c2ff47c8dc17 \ + --hash=sha256:29b7e6716ee4ea0c59e3b241f682204105f7da084d6254ec61886508efeb43bc \ + --hash=sha256:2a7f1d03727130fc875448b65b127a9ec5d06d19d0148e7554384229706f9d1b \ + --hash=sha256:2d39b54b968f4b49b5e845758e202b1035f948b0561ff5e6385e855c96625971 \ + --hash=sha256:2e1ad3fda65ae0d93fec742a128d72e145c9c7a99ee2fcd667785d99eb25a7fe \ + --hash=sha256:3173e1e57cebb6d1de186e46b5680afbd82fd4301d7b2465beebe83ed317066d \ + --hash=sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac \ + --hash=sha256:350c8348f0e76fff0a0fd6c26755d2653863279d086d3aa2c290a6a7251135dd \ + --hash=sha256:35d382625778834a7f3061b15423919aa03e4f5da34ac8e02c074e4b75ab4f84 \ + --hash=sha256:3b90b767916ac44e93a8e28ce6adf8d551e43affb512f2377c732d486ac6514e \ + --hash=sha256:3e1b35d56856f3ed326b140d3c6d9db91740f22e14b06e840fe4bb1923439a18 \ + --hash=sha256:3ebe801e0f4e56d17cd386ca6600573e3706ce1845376307f5d2cbd32149b69a \ + --hash=sha256:3f3c908bcc404c90c77d5a073e55271a0a498f4e0756e48127c35d91cf155947 \ + --hash=sha256:40d918bce2b427a0c4ba189df7a006ac0c7277c180aee4617d99e9ccaaf59e6a \ + --hash=sha256:465a0d012b3d3e4f1d6146ea019b5c11e3e87f03d1676da1cc3833462e672fb0 \ + --hash=sha256:4735a10f738cb5516905a121f32b24ce196ab82cfc1e4ba2e3ad1b371085fd46 \ + --hash=sha256:4ecdb3b6dc36e6d6e14d3a1bdc6c1057c8cbf80db04031d566eb6080ce283a48 \ + --hash=sha256:50b1b799f45da91292ffaa21a473ab3a3054fa78560e8ff67082a185274431c8 \ + --hash=sha256:54a50a9dad16b32136b2241ddea9e4df159b41247b2ce6aac0b3276a66a8f1e5 \ + --hash=sha256:5732eff8973dd995549a18ecbd8acd692ac611c5c0bb3f59fa3541ae27b33be3 \ + --hash=sha256:598e88c736f63a0efec8363f9eb34e5b5536b7b6b1821e401afcb501d881f59a \ + --hash=sha256:640fe199048f24c474ec6f3eae67c48d286de12911110437a36a87d7c89573a6 \ + --hash=sha256:66c02c187ad250513c2f4fce973ef402d22f80e0adce734ee4e4efd657b6cb64 \ + --hash=sha256:67a91c5187e1eec76a61625c77a6c8c785650f5b576ca732bd33ef58b0dff49c \ + --hash=sha256:6be67c19e0b0c56365c6a76e393b932fb0e78b3b56b711d180dd7013cb1fd984 \ + --hash=sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21 \ + --hash=sha256:71a66c1c9be66595d628467401d5976158c97888c2c9379c034e1e2312c5b4f5 \ + --hash=sha256:7274942e69b17f9cef76691bcf38f2b2d4c8a5f5dba6ec10958363dcb3308a0a \ + --hash=sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b \ + --hash=sha256:7a47ce5c2288702e09dc22a44d0ee6152f2c7eda97b3c8482d826a1f3cfc7da7 \ + --hash=sha256:7a61c06b334bd99bc5ae84f1eeb36bfe01400264b3c352f968c6e30a10f9d08b \ + --hash=sha256:7ad8cec81f34edf44a1c6a7edf28e7b7806dfb8886e371d95dcf789ccd4e4982 \ + --hash=sha256:7e9053f5fb4e0dfab89243079b3e217f2aea4085e4d58c5c06115fc34823707f \ + --hash=sha256:7fa18d65a213abcfbb2f6cafbb4c58863a8bd6f2103d65203c520ac117d1944b \ + --hash=sha256:81da1b229b1889f25adadc929aeb9dbc4e922bd18561b65b08dd9343cfccca84 \ + --hash=sha256:82676c2781ecf0ab23833796062786db04648b7aae8be139f6b8065e5e7b1518 \ + --hash=sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d \ + --hash=sha256:844a8ceb8483fefafc412f85c14f2aae2fb69567bf2a0de53cdb88b73e7c43ae \ + --hash=sha256:865cedc7c7c303df5fad14a57bc5db1d4f4f9b2b4d0a7523ddd206f00c121a16 \ + --hash=sha256:88ef7d55b7bcf3331572634c3fd0ed327d237ceb9be6066810d39020a3ebac7a \ + --hash=sha256:898be2be399c221d2671d29eed26b6b2713a02c2119168ed914e7d00ceadb56f \ + --hash=sha256:8d4f47f284bdd28629481c97b5f29ad67544fa258d9091a6ed1fda47c7347cd1 \ + --hash=sha256:92edab1e2fd6cd5ca605f57d4545b6599ced5dea0fd90b2bcdf8b247a12bd190 \ + --hash=sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7 \ + --hash=sha256:95db242754c21a88a79e01504912e537808504465974ebb92931cfca2510469e \ + --hash=sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e \ + --hash=sha256:96fbe82a58cdb2f872fa5d87dedc8477a12993626c446de794ea025bbda625ea \ + --hash=sha256:99cfa69813d79492f0e5d52a20fd18395bc82e671d5d40bd5a91d13e75e468e8 \ + --hash=sha256:9c79f57faa25d97900bfb119480806d783fba83cd09ee0b33c17623935b05fa3 \ + --hash=sha256:9e5825ba2c9998375530504578fd4d5d1059d09621a02065d1b6bfc41a8e05ab \ + --hash=sha256:9fe11467c42c133f38d42289d0861b6b4f9da31e8087ca2c0d7ebb4543625526 \ + --hash=sha256:a1778532b978d2536e79c05dac2d8cd857f6c55cd0c95ace5b03740824e0e2f1 \ + --hash=sha256:a387225a67f619bf16bd504c37655930f910eb03675730fc2ad69d3d8b5e7e92 \ + --hash=sha256:a56ef534b66a749759ebd091c19c03ef81eb8cd96f0d1d16b59127eaf1b97a12 \ + --hash=sha256:aa47441fa3026543513139cb8926a92a8e305ee9c71a6209ef7a97d91640ea03 \ + --hash=sha256:ac27a70bda257ae3f380ec8310b0a06680236bea547756c277b5dfe55a2452a8 \ + --hash=sha256:acec55bb7c90f1dfc476126f9711a8e81c9af7fb617409a9ee2953115343f08d \ + --hash=sha256:adedc4a67e15327dfdd04884873c6d5a01d3e3b6f61406f99b1ed4865a2f6d28 \ + --hash=sha256:af43b8711a8264bb4e7d6d9a6d004c3a2019c04c01127a868709ec29962b6036 \ + --hash=sha256:b232029d100d393ae3c603c8ffd7e3fe6f798c5e28ddca5feabb8e8fdb732997 \ + --hash=sha256:b35c13ce241abdd44cb8ca70683f20c0c079728a36a996297adb5334adfc1c44 \ + --hash=sha256:b63daa43d82f0cdabf98dee215b375b4058cce72871fd07934f179885aad16e8 \ + --hash=sha256:b908d1a7b28bc72dfb743be0d4d3f8931f8309f810af66c906ae6cd4127c93cb \ + --hash=sha256:ba76177fd318ab7b3b9bf6522be5e84c2ae798754b6cc028665490f6e66b5533 \ + --hash=sha256:bba6e7e6cfe1e6cb6eb0b7c2736a6059461de1fa2c0ad26cf845de6c078d16c8 \ + --hash=sha256:c0d6770111d1879881432f81c369de5cde6e9467be7c682a983747ec800544e2 \ + --hash=sha256:c16ab1ef7bb55651f5836e8e62db1f711d55b82ea08c3b8083ff037157171a69 \ + --hash=sha256:c1702888c9f3383cc2f09eb3e88b8babf5965a54afb79649458ec7c3c7a63e96 \ + --hash=sha256:c25332657dee6052ca470626f18349fc1fe8855a56218e19bd7a8c6ad4952c49 \ + --hash=sha256:c8565e3cdc1808b1a34714b553b262c5de5fbda202285782173ec137fd13709f \ + --hash=sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63 \ + --hash=sha256:d206a36b4140fbb5373bf1eb73fb9de589bb06afd0d22376de23c5e91d0ab35f \ + --hash=sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888 \ + --hash=sha256:d8c05b1dfb61af28ef37624385b0029df902ca896a639881f594060b30ffc9a7 \ + --hash=sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a \ + --hash=sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3 \ + --hash=sha256:e80a28f2b150774844c8b454dd288be90d76ba6109670fe33d7ff54d96eb5cb8 \ + --hash=sha256:e813da3d2d865e9793ef681d3a6b66fa4b7c19244a45b817d0cceda67e615990 \ + --hash=sha256:e85190da223337a6b7431d92c799fca3e2982abd44e7b8dec69938dcc81c8e9e \ + --hash=sha256:e99befa0b48f3cd293dafeacdd0d191804d105d279e0b387a32054c1180f3161 \ + --hash=sha256:eda5a6d042c698e28bda2507a89b16555b9aa954ef1d750e1c20473481aff675 \ + --hash=sha256:ef87b8ab2704da227e83a246356a2b179ef826f550f794b2c52cddb4efbd0196 \ + --hash=sha256:f16dace5e4d3596eaeb8af334b4d2c820d34b8278da633ce4a00020b2eac981c \ + --hash=sha256:f8d635cafbbb0c61327f942df2e3f474dde1cff16c3cd0580564774eaba1ee13 \ + --hash=sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361 \ + --hash=sha256:ff09cd8c5eec3b9d02d2408db41be150d8891c5566addce57513bf546e3d6c6d + # via + # gradio + # trackio +cachetools==7.1.8 \ + --hash=sha256:1221d547a0b24b7f26fa891d40d488b5258beab9aebd8ed68c729be3af849c43 \ + --hash=sha256:a81e3844acaa7355b6567f97bd67a94a14ec3a9bc2cbbdae45b9592cc036775b + # via + # py-key-value-aio + # vllm +caio==0.12.4 \ + --hash=sha256:0d8d2826bf622644fc374a9f53ac00918b489d03599783953fcbe79af19123f5 \ + --hash=sha256:134d9d145d75f454de5ece9e87595bad433639b51061b693bafc369f689f8742 \ + --hash=sha256:17a41de51203fc4d787d8d577bd56f169167aa9822f14fada015504688791aa4 \ + --hash=sha256:1c1a387f8784a86a56a8912ca42e1ad67599f16406f497b5b655b461a1b89426 \ + --hash=sha256:1e1d5570fd0ec75b1b2c2877c5545ed9f0738b5d23b000e2b6a8fc830dc8b310 \ + --hash=sha256:20acd9a8df90d25b63bc0b7dc264e2a09b99082a420b5d640c76056027ee6b52 \ + --hash=sha256:20f1d3fe05be285d82413b00bd8061eeb62d3027d3f565adaf7db10bebc594dc \ + --hash=sha256:27ec5671ac05650abc7ac1fb12e95ad09417ae495ce9be565927786688edd6c5 \ + --hash=sha256:32d8e9f3e2099c8db29446679252766c9bcd806eb88b4fb60ad274f73df2a5e9 \ + --hash=sha256:413565d77dfdf2dd841ac100571ef1cc710f9c56137312f3ec51356350b4f3a5 \ + --hash=sha256:4189104e5579553031340a677762398c130b81ddb7d7c9b69eebffddaecc4f58 \ + --hash=sha256:456a93868ff007cea65d916d96d192cda8b9992c6eee046cb0159040f9446ab5 \ + --hash=sha256:4854a029e359e5ddfb5589ab66157a34af6a0dda0acdaa43f5e640d60182c1ea \ + --hash=sha256:4f2e0b6d393dc0ea78cfd13a9a4321fc35fd6a2db802e5f64651317e859fc929 \ + --hash=sha256:51b8600eadc8756751dfc152514852040c6c28deb6bdcd6bfbdddb2a38e424e1 \ + --hash=sha256:53605636e3b1eaeca368b475f1471ad9774736c6502a4b58b8b6b8d74b531770 \ + --hash=sha256:67b725641fea682a2e9b1d3b2a150d21f1a25383e3ec8351bd7677ff857fa982 \ + --hash=sha256:67f1bddc997bef281db36600e3e58d6b16d6486b3540eb62c9e54c6584f137fd \ + --hash=sha256:710ffe4a6f3d69dbfe98e9f856c24a544e4d639c7bd210f9f5503a26e62502f4 \ + --hash=sha256:759b952df48a5bbdb16e90631c748c877fb0ec7ca9d64eef1066569925ccdd30 \ + --hash=sha256:780465251a0680039b29c990bca73b40b843e39d06505c62e353ed56490e4576 \ + --hash=sha256:7a5e231bbf81eaed269f99afa9ef46457f51f9407952a1795c0795b3a62c23c0 \ + --hash=sha256:8a926d562f8c06767774a91239770ebaa93c10a24074e21ea741db208df9cc1d \ + --hash=sha256:8e9eda6a88f309a0a53bcba844471638da01a92758015e2df099fa361891dc72 \ + --hash=sha256:97bff8f7da696321552b42629d1869983a8ced2f010bac9731b2719c1195a20c \ + --hash=sha256:9dc0fd6f09ef72d18d3f43ca3d1140295222d925c583114ab9b1e8843a109a6e \ + --hash=sha256:b0444d20067642bb272e423de3cc8ef05bf134d335d57fa5bddcfcc98e04313e \ + --hash=sha256:cd1d00ed1867a3b7a4cffb64b1bd8644de4b6f88cb25240cf82a9cc160ace975 \ + --hash=sha256:ceee64265a55b9aba9c38c1d01a159b99f1e36eb56938022018199d2e7fe1742 \ + --hash=sha256:ddf8d946b795ebd7c75b331b6dcbab4808fdd2c9f16ca76012b4757512861133 \ + --hash=sha256:e22ce2e69d94e4c80e0c11c871e547b3c2d147f977632894cb2527ddb6e87a8d \ + --hash=sha256:ee9de68d6ede7ed69e55a1c09073ce146d9f9470088796d55238c485a93fe203 \ + --hash=sha256:f063624a98a64bab387430c5eaea61ef3825a98399104a297b8da4741c15139f + # via aiofile +cbor2==6.1.4 \ + --hash=sha256:01ecc79a28f33d17331943ce508fc1e21f4b06553c73f874f4c77120d72b2ef9 \ + --hash=sha256:032ce71cbdc9267e9cec42132f6ac6c40f441ec27ebc87ca9dd209dcab6804ee \ + --hash=sha256:0859a0837e6e2d4fe5f5b849f6475797e4db545da98c19db4b1d3487bd47aa22 \ + --hash=sha256:164ca22b509408435b2d8236c80c964e4fc77c085ab034569cd04c40d5cc8883 \ + --hash=sha256:179a794bf4be1d46ff190695929f65f0b42019c156919846ae539d2a7ec42e54 \ + --hash=sha256:1fc15061553e4494dc10883237501e3402c645fe509248dd698e1faf2460d68b \ + --hash=sha256:2310f07db3f9ba26f2a623774ff9f3dc7185af54f732ea119785a6b1bf7e1e7e \ + --hash=sha256:239db0f92d537fd29eaec4e40195fc3b2b48bc34a5887059658162489a9eb6ae \ + --hash=sha256:24da0a481294ac416e1e369e2d204b2b1d993cbd082d0d99fa3d6f5f27ae5e69 \ + --hash=sha256:28fa5db05a7eae8fd80709959988d8a7f12838c6d4e5c58ec951414058641195 \ + --hash=sha256:310f3dfb296ba48fe9b63c5cf26e691e3548a1eae6901d2f0c18e941d151f220 \ + --hash=sha256:316e217a496640418d3137483279d0e70053b000cdd4b52a4dbf20ea478bc40a \ + --hash=sha256:32a4663425fbca4a4a7aa918eb5789d844c406439e58424cf34511f79f559242 \ + --hash=sha256:36ae16d64b1f7b620c1af748e7b6947e20069ef80eee56871c5fbb84cc635905 \ + --hash=sha256:3cfb7eec57406b86aab47183f6dc90ece888e03721d37a4d33315e79e486898d \ + --hash=sha256:3f4a434c36bb0d33aeb48ddae8e8b673ca7e1f14545ee7cf4a4c7c39380ea9a2 \ + --hash=sha256:41aae72e8863ac0d50705ea8290b72c49db90a6d953110feaa91360ae526fa8d \ + --hash=sha256:4903f24e0f9087275a0b6606c8b0aa586277001d51e4844fcdbc5b7211330aa8 \ + --hash=sha256:4bd29f21529e279d50fc14f1a811f7b05b4d8e66a7969163cce98983b6817245 \ + --hash=sha256:553a46bda7d09552631a714e22b91e6ff2c867ecd91511596ce290d8879b8d5b \ + --hash=sha256:598710183daae69cbdeb177a870ec64aa601de8138a61491fd256826d15a860f \ + --hash=sha256:5b99305d4013867e059f147752b95f728680682ab03d75a3f4dcfbb270d8dfe9 \ + --hash=sha256:5e6c76004d674ad1c620660cb0bc5a8a0b72a5d8c7b70926d8e09e6d7e87332f \ + --hash=sha256:69978901302ecbc8cda57b520487c5c5240ed217de783eb7728fceb258311d76 \ + --hash=sha256:6abcf072b8c0fdc8ad7902ee26a906cafbf3427d026b662ff21166a253f85e18 \ + --hash=sha256:71fcf4f237d68bf4445bf45070f36f82b333f2e6a62612aa2c256683b51378a9 \ + --hash=sha256:7336ff4cb7d161ec43b65eef43bf3e9bcab44bd152efb54dd637b7afe711254f \ + --hash=sha256:7b697c2a18eab9a3326447fe0f1c927db065a096febc57f064f0f5ed67fe96fc \ + --hash=sha256:7deccc50fd0b55c4c7dd265b144c5358a645121e457c0ae3722b5ad59832b257 \ + --hash=sha256:8156fdeb73c3ff6c8cf67ad414fb5c887cd708ff0af6d61f62629f41cb4c17b2 \ + --hash=sha256:855764e02dc60ab9413acd044e997c3170000fdea6155d6c43a923a1d966dbe6 \ + --hash=sha256:8f1019494b0ec81a3df3ebb01b6acb446d5b946fe35845b1726379abd66a71da \ + --hash=sha256:9b904b8d0f4ddac9259197d21d121fae4cb8b555700d65bc12c5d46a2e6c2025 \ + --hash=sha256:ad4efa23fee6447e56a269191044e06eb39e809458bcd674e164fe9445feafd0 \ + --hash=sha256:ae14eb3f2f251102d72625ab02a28c83a8d11adf3d0383bd6d7bd9d672e18119 \ + --hash=sha256:bd20ecc5c8ece24db952e48a91c8c47319eaa6358af707c85ac2bb388a79abc8 \ + --hash=sha256:c08b9c7d2ea013e24a0cb819b872b0119dde404f64a1182c0b24095b7bba781f \ + --hash=sha256:c0f5f2d6d3b58e44146860c049f3c082207a4005588b8926d51bf937ab66773c \ + --hash=sha256:c204a75f91f8cd9ed0881f6b88ec395c59aeac9fcf4d08155e7f899db2a1c46e \ + --hash=sha256:c435720a25c1de9b241df883b5890b35aff3bf64365efc8037d4d26df085724e \ + --hash=sha256:c48a7c938fc5fa5300ff82b5df09068dcb4838685ae8556b5ee8279d74f97ab4 \ + --hash=sha256:c6b28b928c5f2dbf47dffa12dce9c8e36fe6ac1c1358bc326499c0736263b66f \ + --hash=sha256:cc8cd300e236e9797b2e1ce306109dc481fcccf78bfa2682bf36d99e6eab1ec6 \ + --hash=sha256:d2560c2ba6a95904ba2a0ca257af878c4344409d9b46d8e646d8ebb617b1e0dd \ + --hash=sha256:d9ada5a6ccfbb8ea7a3aa2aeb028421b52d8e0cd9323f0a2aeaa9c09d25fbce2 \ + --hash=sha256:deff027a56e04caebfee649029bfd1836f4fb468d2cf66d2a396ae1018f655db \ + --hash=sha256:e1fe2d62c50df290576280b18247ec63486f78be73e285bae269c2456c6ddff0 \ + --hash=sha256:e8097e8259b78c8feb5dd5b833b23f83ed588e821ef44cf1455082cfc9434440 \ + --hash=sha256:f3fc7d15cba4174373df2496070faa4a927fe3ed772130d281808120aec7b61c + # via vllm +certifi==2026.7.22 \ + --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 \ + --hash=sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55 + # via + # httpcore + # httpx + # requests + # sentry-sdk +cffi==2.1.1 \ + --hash=sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e \ + --hash=sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66 \ + --hash=sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2 \ + --hash=sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0 \ + --hash=sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6 \ + --hash=sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971 \ + --hash=sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c \ + --hash=sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d \ + --hash=sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9 \ + --hash=sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517 \ + --hash=sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735 \ + --hash=sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80 \ + --hash=sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f \ + --hash=sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1 \ + --hash=sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29 \ + --hash=sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8 \ + --hash=sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c \ + --hash=sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e \ + --hash=sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48 \ + --hash=sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813 \ + --hash=sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac \ + --hash=sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632 \ + --hash=sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6 \ + --hash=sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1 \ + --hash=sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659 \ + --hash=sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688 \ + --hash=sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004 \ + --hash=sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0 \ + --hash=sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062 \ + --hash=sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779 \ + --hash=sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94 \ + --hash=sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50 \ + --hash=sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab \ + --hash=sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac \ + --hash=sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6 \ + --hash=sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676 \ + --hash=sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1 \ + --hash=sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9 \ + --hash=sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf \ + --hash=sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13 \ + --hash=sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e \ + --hash=sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e \ + --hash=sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973 \ + --hash=sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527 \ + --hash=sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72 \ + --hash=sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890 \ + --hash=sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c \ + --hash=sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990 \ + --hash=sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd \ + --hash=sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9 \ + --hash=sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94 \ + --hash=sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3 \ + --hash=sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80 \ + --hash=sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41 \ + --hash=sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5 \ + --hash=sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c \ + --hash=sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a \ + --hash=sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4 \ + --hash=sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e \ + --hash=sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6 \ + --hash=sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98 \ + --hash=sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b \ + --hash=sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1 \ + --hash=sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03 \ + --hash=sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af \ + --hash=sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231 \ + --hash=sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2 \ + --hash=sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3 \ + --hash=sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836 \ + --hash=sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5 \ + --hash=sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399 \ + --hash=sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96 \ + --hash=sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e \ + --hash=sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be \ + --hash=sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf \ + --hash=sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc \ + --hash=sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455 \ + --hash=sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0 \ + --hash=sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12 \ + --hash=sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b \ + --hash=sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7 \ + --hash=sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692 \ + --hash=sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54 \ + --hash=sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3 \ + --hash=sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b \ + --hash=sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be \ + --hash=sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d \ + --hash=sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358 \ + --hash=sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a \ + --hash=sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7 \ + --hash=sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc \ + --hash=sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960 \ + --hash=sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125 \ + --hash=sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb \ + --hash=sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a \ + --hash=sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa \ + --hash=sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf \ + --hash=sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3 \ + --hash=sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4 \ + --hash=sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264 + # via cryptography +charset-normalizer==3.5.1 \ + --hash=sha256:00668ebb0609751758682eb0b5857e7c35b9f00e84dfdef062e103244ec94d45 \ + --hash=sha256:012a22b88a77ca2e59b98ac5889b0deb604147666032f45e6d6e217634d2550d \ + --hash=sha256:01e93745f7f219b703b60ba7afead36cfc4242782be5af484673fc500df12da5 \ + --hash=sha256:04368edf83514385ffc3e1cfd4546e595f4f1272dd23ba437a93a9cc3741d47b \ + --hash=sha256:0722590aabf9dc6a6c0343d523c05458fa2b5047dbe6302fd526bb570600753f \ + --hash=sha256:07ffd07412fc5d5e84cd8952acf9ff7e4ed7a708e69d1bada19d8ba91711353f \ + --hash=sha256:09a7bba9f739468c8e78c36a75c33768e53cb1959fc638f510454c14683f00d5 \ + --hash=sha256:0b2b1b3fa5670c127b246df1d0c059defd41f689a868a3b9d79df9b1cac42d22 \ + --hash=sha256:0c6dfb5ca6723eeed15aa8e564a014d69fcb8812f94eef11fe3631e0508199f5 \ + --hash=sha256:0d929fc574b4d6fd9e7c0f5c2ede8716a41911923aa7fa5fce38e0818aa4a1ac \ + --hash=sha256:13e3afe97712e8887cd516e960c63f0b93122971e5b5e4b2622fe7701771e838 \ + --hash=sha256:15f024313246a4ed976c60f440bb8d257815513a681d212ff74fd46f7d715a90 \ + --hash=sha256:195ce897c6153c0700078142cf8efe3e6454ca4cf4357499e4078dfd83396626 \ + --hash=sha256:19a3dd5aa73cef1c99687c4fc57db016a9c17104ae1185da88ba566a5d3bebe4 \ + --hash=sha256:1d1c7a53a6c2103925cdd6d7229f8c567379f211c869793df679f2e9f738c369 \ + --hash=sha256:1f5883d77fd409a261abb5dc8ccbe335720d798b1de4abb3b1d47ccbbc76b53b \ + --hash=sha256:21b82d8082f6f5e7f456ef0bd16323d08de1266efbfeb476e64b2a91d1471a4e \ + --hash=sha256:252d099029bcbea642f2a06c4ed5046bdf8b5a8150b64afa5e027e88b106e5ee \ + --hash=sha256:256dd4d85d9e4dc595e2bc983c980e73f62ddeb3165c58b4c3dfe78c5c8548c1 \ + --hash=sha256:26422d45fd13551cf564c58932f7d72b4f58b93b0fcf18c35ba6be12b46bb102 \ + --hash=sha256:2679de311c7946dde5d3b6f44941844133ff5c7cb86099c0061ab1e8901c20a8 \ + --hash=sha256:29880d17a8eb0b5cfdfd8944b468322928059aa35f1f5fa8ff22b149ec0b42f8 \ + --hash=sha256:2bced4061f000f7187254a02ad3433ae17eaf991747ceea2f478422590a5bba9 \ + --hash=sha256:2e9cf9253119d8e5d111f05d71626786fd3d6193817316eab1ca088cdb8593cf \ + --hash=sha256:2f06b7eae9dbe77fe1d644ca244dad508de8d302870a43f3c559b521270938a0 \ + --hash=sha256:2f293479cce755c75f1697e87c409b7ae4c555c7dfecb6e988ad13abba943031 \ + --hash=sha256:329fc3ccb63ad22d867d84c2adea759a64079a37ba4a343433b02c7a2816871e \ + --hash=sha256:343fb4f2821043bd87095f7b08a1a181febc8e36ac64212143bbfd0a0e1bc235 \ + --hash=sha256:3588e376b3ea2eea84976f67273d679f229e24c66dce7b82ae45aef04ff6e072 \ + --hash=sha256:35aea775dc2bd5f54cd84a1cd2696cc3207c479cb9cf0bd346f0d343e4300ddb \ + --hash=sha256:35fe081843b35aad20ffeccec3eeffbe637b15d14f3fb22cc1b59cd8ec17e93c \ + --hash=sha256:36047af20e17097c3bb9476c2b7655f2f7aa51322c0ba58c07695bedf755a950 \ + --hash=sha256:3617ac3cfd8b9888f145ad89dd6e692285834b0201c6074a5eeaad3fd4d668c2 \ + --hash=sha256:366ec70f5547c640d3ce1985722490f23faf4eb5216a7eeba78277490e78dacb \ + --hash=sha256:394fea06235c8543390050ed5f529187074b029fb027213f6c46ac11ab5d950e \ + --hash=sha256:3d27167433c0d5f18dc850f07d0b3816221984fecdc405d6c157a6f0b8f8e9e6 \ + --hash=sha256:3e5e1224c0a6a90e05843e07adfec669edebec17801c67072f51e59561d63c0b \ + --hash=sha256:41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2 \ + --hash=sha256:433c5a81eade63b47e522303bad236f59dba55ea6951746f5558355eeed8c75d \ + --hash=sha256:4582c27e8c889d64811987b5967fbd3ae0c823fe1fd933b543d55ac20bb475fa \ + --hash=sha256:485a0d363cafefcd2538a73c7c838daa2035f09b2c9f9b5e3133f80c6aeb84c2 \ + --hash=sha256:494b70049a4d69aec6e8137c13af4cf8db8c9f9820a1392ac293b0dd2987a818 \ + --hash=sha256:496846868fea80e479324862fa877f02411f2fd0f83b79ccee2607aa68b2a032 \ + --hash=sha256:4abdc5f9ad448c1ecbfae2974b820535d6bc6e7eef63babbab3d81cf46968c71 \ + --hash=sha256:4b599739b93b2cbeded49645ae3c8d1405c29ddfbceac1545c87a3f9580a9e96 \ + --hash=sha256:4bea7f8ebe90bbd7f0e4a2de42ca6924ba23e3e76418c408ff82f1d46fabd687 \ + --hash=sha256:4c4fb141a727957c93edfe5c32a26ceb6b5f6461d67146e2d39f51e16170bea8 \ + --hash=sha256:4c9548dc78002099910abaebc0a72ac58b7d30931869e0351c09b507dff4ece3 \ + --hash=sha256:4d26f14f041e83dd8edfd61f4cd4fa7285d31798b5bf1f28e70c367ba6c41d61 \ + --hash=sha256:4f298bdadb8f0b9e5672877f647d1be9373ef5320c9e2f049795e26cad28b6a9 \ + --hash=sha256:52ec005752a56ae79547a05c0139ca2501a0c866390b6115008456b9f0e7cde1 \ + --hash=sha256:55261ac0d2941c42f196dd576f543d87a8ee03cd6f5e30dfb4d807b2e3b9121a \ + --hash=sha256:56490c595a28b1bb27dfc583e816152a9767721ef58b2c03b13f954d2f707420 \ + --hash=sha256:58d3e12c88e0950bca850ae1f7c256055c097639c2edb9eb123af9807d8b15e4 \ + --hash=sha256:58d4aa13a59c969dbfdf9e6a9560e242cbfd9e8a8f50c2747714df1a423adf65 \ + --hash=sha256:59171c6e45bf07d0d5cab3b0bf81d945035530f6873398b3b531c31184d46663 \ + --hash=sha256:5b6d1386bf0096d26d3a863dc0a487a5b4eb9aa93cf5ba69683d29dde6b9d60f \ + --hash=sha256:5c0ea61a470e070686aa30892fed79e297d2c8d0ab46b8bcdf027d38c51da591 \ + --hash=sha256:5c84bec0ab5ae0c64bfe73a7d2adcb5ce73b467523fc27fd6a28ab2aa6cbe35a \ + --hash=sha256:5ca0555312ae2fe82715cada7fac375530c2f3349e1eaa1bcb33d0283ac79a18 \ + --hash=sha256:5d8531a6569d025f68e2321e7638fb7978f23db58e5f69f56913837aae03816e \ + --hash=sha256:5e2d0e146dcb57034f8b97dc58d2d512cb90aba253960ce449f695fec6a82c6f \ + --hash=sha256:5fc45d653ea8c9a20479167e11d4a0f8cb2fa3470737ab6f9c827532313187b7 \ + --hash=sha256:6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3 \ + --hash=sha256:6199d5606e2bbf2b096cf64d03f8b6790c91081d5ac866b8e7bb6422738cc60c \ + --hash=sha256:62b55f6722735a6c472f88361cde6640608773d9443cebdbb51abf436a1fcdd3 \ + --hash=sha256:687c9ca3035544b113bea2055e180af96fb63c0c476e22a9180f51925186e7b7 \ + --hash=sha256:6b7430cf5728e68f6c462254009a6ef4086e1bea43cf2f57aa9c55fb4f50ff96 \ + --hash=sha256:6ba32c4d2abf1d2fe7cf27d280f4cca5664233b0f885549c7761719eb977f486 \ + --hash=sha256:6c9cdde8becb25a7fde49924511aa2644d6f8081cc8df8e9452724303348d8e3 \ + --hash=sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6 \ + --hash=sha256:6e2912d4babbc65196ac13c2f53468dc57fb8b9c25ef913e8c59ddf7c6dc0e1b \ + --hash=sha256:6e5e4d73d588ca5ed09df1b7dcd1b203d1df3c542e3f50d126c947d432b10731 \ + --hash=sha256:70055ff39b97c99e7ae40ea3e393fb62aa2e44dbd9b29f8d14f42fb0025c3959 \ + --hash=sha256:706bfd38730a5ac7a365793269a00f4e988178cec121391f4248d84ad8c972e9 \ + --hash=sha256:7235dc28fc6dd9d832ac7c7bce95367dedb85929f17368a0c2bee1e080b9acbf \ + --hash=sha256:774d157f112367ff4abd29019f38f023c24e00e56edc7829c20e358a5a913ad8 \ + --hash=sha256:77efcff2b23071c349402ac1066667a3d011f62398d81408c9b88ad991747c9e \ + --hash=sha256:789b8982559ae28dad2356519f841655756cdcd96616410590ae0b17454ee64f \ + --hash=sha256:7ac76cf9afd34929d76eb7fcb63be476a4853d8a96f0dcf2d0db68a0cbdf9885 \ + --hash=sha256:7c0c10730342b0c9b35dd1d619beb8214e520bd96a1f870f452680b238aab3e0 \ + --hash=sha256:823f82903d189af463d7df250ef1f7f696f3cee08cc8d91deb565e8d425f6506 \ + --hash=sha256:838648accb3a7fd9803fd45c87bce8509648eb0c11bc34e216141300977244f2 \ + --hash=sha256:854066be00447fa8de2ccbbe893e2ffc4b123ef16d897af794c1e18bd4a714b0 \ + --hash=sha256:85d5855daafc240cc045c026d7a15fd198a09b0fc8ff6f5ecbb5297b509cb11e \ + --hash=sha256:85de3134b5379856e323ba37c19c9256d39425f7b76a63af52b09fb4664c2e8f \ + --hash=sha256:87e4f41d375c0b9be2fb5251aee4b8a689169e134535aed81bf085c3b647451e \ + --hash=sha256:88ca277405c2d3b71c4e1c2ee0e7966e807bcba86a69d11e19ba199d18ae4491 \ + --hash=sha256:88e85ab89cb822c1e635f51d6d32e488f94e002e70e2f492bdb8b945543f345a \ + --hash=sha256:8ac8c94b6539074e0f40899301273ac8402b9b3e01c7b7ba269ff30340aaaf20 \ + --hash=sha256:8fe532b3c966d1fb794e0698e4589d0444017ae77fc0b31edea13c0e35bcc449 \ + --hash=sha256:9085f87b0e38a2b92b8923059b4e8789fe40d9279712d15dcc670048d77079af \ + --hash=sha256:90b7481fb62fbe172c558bc6fd1c4c98d82004a54a7551f20e11ac9bf0b8708c \ + --hash=sha256:92caef967d287a407085d61176fce4012b1dd62daed4eb6d5ceb26d3d2538712 \ + --hash=sha256:9362dd90aa7dab48c0054a21187791ccf05473f7dba5d92b8033ae62164675e7 \ + --hash=sha256:94d78ecec2605a8d0398b0f365d5f12a63248438516f5dac536a5eff7337df4a \ + --hash=sha256:94fbf1c0c6cc0d3d5e50f9a9313a8cdca90dd696d34b381cd1704f8c9e939f20 \ + --hash=sha256:950f23cb393f85543777b0433f082cddd25b51ab398eac7971146495679efe5f \ + --hash=sha256:96eefc178f8636b9c760c5829345307fd81cfae9ab1e80997dbddeb0f54ee9a3 \ + --hash=sha256:96fef3e886d6a9874b14f27fc193fbdc69d5d8035783d86aa4e1cea594e695f9 \ + --hash=sha256:977cdbd483a9cff38179bea4fd754289a6f2195c7abd414aba85410b3e66cc5e \ + --hash=sha256:978eab16f55b4ab2c2a745be9a0a840bf8f09a7f227d9c76eb30214d078865a5 \ + --hash=sha256:994e883d17c559cdfd38c84003c8b27d25424a1077272a17e7cd27bfe0bf57b2 \ + --hash=sha256:9ac4444d8d4fd4c4bd08bf451ed3167aa9e7ec6cdb41b648794f1d1103652e36 \ + --hash=sha256:9b5db6052055d34d41230fb78d7c439c23dc536a9896f6cb039e8dd92cfc1263 \ + --hash=sha256:9d9a0dc7cbe9bec24c3f767c9122c41fe5a1bc43f47cd099d00d393e09769de4 \ + --hash=sha256:9dbdd9205662134957cf0c324f639bdc5031c0ca056e2369e238db75187c0f11 \ + --hash=sha256:9eea3ab2597a5e65fe65296e2d6a84570845a6b55532d90333d740d48bbc850a \ + --hash=sha256:a2028475ba855475b8b4d3cfeb4994269c967aea8b9892dfba907f4263a863a3 \ + --hash=sha256:a3a370082ce34d0612f421e15fe011c53bb1feff21a26d06ad4fb244dab5a375 \ + --hash=sha256:a545775cfe815855ea32d7c27731d79da358ef2055b4a25830231b1622dd18aa \ + --hash=sha256:a5cbd90ecf0fc62e64726917ad083b73001f0563657a87ec3c0b504e277dc90d \ + --hash=sha256:a6d095662e73e74f0a49988e0593373e243e3a52e27bfeea0a859e88acf4a0f5 \ + --hash=sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99 \ + --hash=sha256:a951ad59cad9145664a730d3036b40b844e74d2d3683da40111463cd3a83845d \ + --hash=sha256:aa1099b956fb795e686d073568f6dc002a0bb89765ea6d5b055dd7d9bf1b116c \ + --hash=sha256:aa2bb0b37202dca27175591f761108b5d34096ade1191ffe4808bdf6b1571488 \ + --hash=sha256:aae2ee51122d3ae968a3837d97dc24a0aeebb0dea23694422cd172bd30017cd6 \ + --hash=sha256:ab743e9bc90c1f73552ec33e10e3331315acd2c397b36065b591b0181de533cc \ + --hash=sha256:ac00177c4831ffa650f8609e4bdddd5fe09c03b1c0c47acece7e6ea20421598b \ + --hash=sha256:ac13b004224fb341e1e25a1ed5e19d32f57cdb2a403e01f003b46f051a550f6f \ + --hash=sha256:acaf604462bf330b0d07e7a07c1d6e4adac79e5fb13e9c5140590542cafacc00 \ + --hash=sha256:ae31a1a1db2ee6cc2942fccaf695c934bc7f3db9f2133a3fef1f367cf1a4ab10 \ + --hash=sha256:ae4a097991662cd4fff0ddc74e0fe7874f82e00042fa0ea00855645ed0c79598 \ + --hash=sha256:aea996a6aba25260827c9ea511d1addfde2da9eb686ac961838509086188b7e6 \ + --hash=sha256:b39b69b347e5e47a3b5b8cfc005c68c1ba347474e3960236c4944a8ecd174962 \ + --hash=sha256:b54e7e13267d49ffbfe68e25b3cbd774dab38fa37238f71265e91b36146eb21c \ + --hash=sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08 \ + --hash=sha256:ba2f37ee79e6338845261a3c5b1784e5d1acdff2c0785b284f1b633033d136ab \ + --hash=sha256:ba501e667c17d8411f98e67a022d9604ef179aff0e459b7e292c796837c13573 \ + --hash=sha256:baf3775a2635e5a11fbd5e4e64ee69c7e86875d224a5c72aca4c141064589a90 \ + --hash=sha256:bb57753e36e4855b8ca375069482250a6246372331a3e4f3407eaebb007443f5 \ + --hash=sha256:bd6c173f04743d483881bffa1478d5a4624475b8cd1d2194956a75548e191c18 \ + --hash=sha256:be47f99644b208bff7766314013f9acf57b056b04191d570d68ad14022cf5b1d \ + --hash=sha256:c010f5581d9c612804cc59fcf7b524b707fbcb72828551237ab545bb5c7034af \ + --hash=sha256:c1dcc36dcb96abc02236e182d17e0f71430152a6c2c7447421da2d2dc144edea \ + --hash=sha256:c428c6c31eb5f4277d7f8eccaf767fbd548ddd5ce3c8b4f4cbbfab3d96b5904c \ + --hash=sha256:c658c50ac0c98cd755a2dd50b7977d3bca7df401dcc47fbdfa87db53ef7d4e8b \ + --hash=sha256:c71fb0d56c920c269cd3e2e3fe7c610e3f1fdb21a6ce60efa6430ff63676cea6 \ + --hash=sha256:c7b742bf31c88566b4bb6335a7f393bb322e580b6bb98df7bd0c25e6e3519ce8 \ + --hash=sha256:cc0329df4caaceb950d2f580b5ac716a377f7059624a0bafaeaf8a218c6ed774 \ + --hash=sha256:cc5d36d96478aa9c60654bd932525bf32964c62a7281eafdf16d85003a8d6004 \ + --hash=sha256:ce854f5f478050ade5a238731c4ca985a7d3b3cb53ff600a9b5c3b689b5f0a7a \ + --hash=sha256:ced3fdd71aaa83ce593746c2edb42b7a59cb4c19c8b5c407781c72e493aae55a \ + --hash=sha256:cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2 \ + --hash=sha256:cfa1c0cc3a8f9f53f1243a5a99ac36fd003880199383b37672e86ddda9cb07e2 \ + --hash=sha256:d1ee1e296209fdce05b81b663250eefa02213a2da7b41bf26f7829b8ba3545aa \ + --hash=sha256:d59b75732e9b6f27388e10c14b0259cc5f2e48c78627d185e6a177b58ad3cffe \ + --hash=sha256:d63600d620ad0064c3a748b950ac5ea38a80190e5498532efefa4b7b3f1da1f3 \ + --hash=sha256:dd732602a7009217f658d5863d12d79d373a4de0eebc111094bcdd3bb8e0a6cc \ + --hash=sha256:e06efa066f7dbadbc84ebc126a97c452a6451dfcf589d89d788484949e1cf795 \ + --hash=sha256:e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d \ + --hash=sha256:e4b018dc5a0eee4676e38fe84a47a427816c590b93b55d9025274ec4d6ffc2dc \ + --hash=sha256:e6621fb2a4988d6e53eedc455e5903e2679f3967b8acb3d639f1b63c14a2e893 \ + --hash=sha256:e71c909f353863b2b89c83de2ebed71ea6d0df8a6ef65a128193c5e650766bef \ + --hash=sha256:e90251c0c7bdd54a100a0dce3c07b7e637278c93af29dbf78ebb89a58c4bac7d \ + --hash=sha256:e9fbdce1e47394b09bc9f26ab117dfc8d6491977a11d86f592bb42c779db2fda \ + --hash=sha256:eb12fb2ba69ffa05f8695f61c69e591dc4b4a12ac3757ac8af8adb259bf56d17 \ + --hash=sha256:eda059b6bc8bc0812d626fd91a7ce01bf583df0a61296eff390fd94141a34e30 \ + --hash=sha256:f03ac127268b43ef4fe9e6ab6794a6794b49485a0cc0c1db79876d2f33f75bc7 \ + --hash=sha256:f298e218441525d3794428b4c8b8fb8662c6d3ea79925d4807ee6b9a96a3bca5 \ + --hash=sha256:f5542f9b941279d82d41eb0aa9f98eba36fe4df5c7086c651df7944935b37182 \ + --hash=sha256:f6f7deae3feb4edfa2efaf7c574fe88cbf055038a6abdb40188e4fff66d5699f \ + --hash=sha256:f9b1e28d0e8dbfa858abdba91d6b547beaf2df1a59bec6da6faae7b96a4991a9 \ + --hash=sha256:f9f8405c2c758532c74fed975dbee57be1f31a6e865c031870c79a6ed3212ada \ + --hash=sha256:fa48b1b63d639f9483e0633e092f5851e2348c352f1f9bb6c8182f87884ef876 \ + --hash=sha256:fb78f6e7fcd8ad785d28cd577168bc1aaee827b25bb8755638f694794ea98f0a \ + --hash=sha256:fbc597639158fd7c14d55e808718848319540f51b0e6746e3eefa59723a4a348 \ + --hash=sha256:fce8cbd4997efeb450bd298b54f755dcdff18d496f7a5ddbb4867c6d7c88fdc3 \ + --hash=sha256:fd0350afdc3aabd5576f60ea109228bd5538139713c7b094c5cd27c73a98bc6f \ + --hash=sha256:fd0a274c0e5f9a21565cd9d3dd749b61f96b7aa1e20a93aa1ba4029518f2e5c0 \ + --hash=sha256:fdb8a068947befafba9952162645dc2fecaeb400e64584829ed5e9b2fbe21a7f + # via requests +click==8.5.0 \ + --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 \ + --hash=sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34 + # via + # flashinfer-python + # huggingface-hub + # litellm + # rich-toolkit + # uvicorn +cloudpickle==3.1.2 \ + --hash=sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414 \ + --hash=sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a + # via + # tilelang + # vllm +compressed-tensors==0.17.0 \ + --hash=sha256:15c20d06bdbcf35b51fc99fd125e7b9be1e1855567c33b7a46dfac26ad6fb126 \ + --hash=sha256:4a1b89b508f7efb8ffb4eee8a6e69e0452d9b080cae130146025c64fbe9fa9aa + # via vllm +cryptography==50.0.1 \ + --hash=sha256:01f41478cf33fc605a6a089cd56d28b45c6c0b45a1928b61797f2621a04bac71 \ + --hash=sha256:05ba322c4da95b262a212c345af888ef2c37c88c0509756ea00a0e6d68850f23 \ + --hash=sha256:16c5ecd954b3330ebfb6605eca4fd952da8bef376551d5cc264534e3770a9ee6 \ + --hash=sha256:2a93d05e34d5f67fba6f891fe85d929999baa7195e853923ea6d7576c9e68c5e \ + --hash=sha256:2b34d76a652ea2b6faf777c35df230c5637842cd904e04f16230c3f9f03e4361 \ + --hash=sha256:2ebbfb0f1fed745e91796e3e1080a1440423fdae8ece1b995a1d80883a409054 \ + --hash=sha256:30a125032e5642a21ff816e021152bd4e7e94f03eff3f4b7fca41cd22bc3110f \ + --hash=sha256:330fbb252391c596f1ae42c5754449dc924e6ad012dca8efe0d703f9f2d12ec6 \ + --hash=sha256:359e62deae718bce96170e223fdcb6357e4fbd3bb7a3a75f4430763532560e49 \ + --hash=sha256:407fe2b6db00939c05c0e945e9914238f2f0a430974839429dafc82b1ee6bee5 \ + --hash=sha256:42be3bb70596b3abe4ac097b75be223e8b3ab614a0e5de068e3dcc54d71d6149 \ + --hash=sha256:4c4188f7c0cf655be5c06342b817ed0f9595b69ffa2b12026e5353eed29dea88 \ + --hash=sha256:51593d180cf6d179bde5c5d065bed81386b1f381656ae7d042b7ffc87a9895ad \ + --hash=sha256:51afcfceb15597cf2635068e4ac9a56b2abde622edde17f37d85fd7b5306497a \ + --hash=sha256:53e279950892dc102c6b4e52af03ae5ea92fac572a1ddab78ca73a997f62b69f \ + --hash=sha256:55d16b1ef3ee0958d893a977b19777887e546c9954ea81b200c3301a864013f2 \ + --hash=sha256:5dd9bda1c12b4162f6ff568eeb5e0ff956c28d14406e875cfe8a63a2d414ff20 \ + --hash=sha256:5fe002589592ed749ce77fe0695fcbd3500dd61d7d6db5858a7544c612fa8e45 \ + --hash=sha256:5fe939deeb161024a6be98229c953b6591fef1f41214497a78fe793a244c017f \ + --hash=sha256:693c99b49bd37d0d096e4334c10232c77248c415b98d35236094cdf96d57258b \ + --hash=sha256:76de83fbd91ac49c0feaaa983d0748fd7a53176afac5fb3bf7478d244f0eb527 \ + --hash=sha256:79bf008d1f9af6071c797ad133e39915dfee7614f18f18f4db9072eb715064a3 \ + --hash=sha256:804728ce710890870f3aaa344b2e161172d258d768ac139d02cfd9092d0d94e6 \ + --hash=sha256:8921d58f426793c5f1b47f0b59575780de9a095214958d0eb37d909593db8367 \ + --hash=sha256:8df2de9102026855887e4587084f6eabd80ed0f345b8ad8a7ac27ab9bf4723e0 \ + --hash=sha256:9cb3cb952cf5a8abd50c782a98a89d71699715e802fe349704b47f2425b42a94 \ + --hash=sha256:9dde0a357190eb3b1da1bb9ab750e9c85cba82ca5977aa0836cbb94e92611239 \ + --hash=sha256:9ebcdd5519be9b652a46f507817a74591774fc3d6923ac364e4dfa64e36b291b \ + --hash=sha256:a0b1a59e3a089064a0ec309e9428c8e3ae4e161419d20ac33600767e83fc658a \ + --hash=sha256:a255449073358275b64b67d3f595f268bbef70e72b6edb65e0c70c735bf739c9 \ + --hash=sha256:a8f40ea47330e71b594a7e246898f93177c259490c63183dbaf9e571d71ed9a5 \ + --hash=sha256:ac02b07824d4d1001bd4367599f839c19cb171924c796e52c23508ac14c2c0cc \ + --hash=sha256:aed8db4f6d71c51efb89530e12d9464e7bf2923d46c3205dc794a2a93f8c0648 \ + --hash=sha256:b8f852c65863251b9e3a1b8c150ce21e59b522dbb6a7d4bc80e680d38388e986 \ + --hash=sha256:be224a65493ec5b74a158ff22a5522ce4a5ca1e543c647a3a4730d4a09e5f959 \ + --hash=sha256:ca83d00d9e69cd5eb63f2e69c3a5a59e0cecae5ae14c6ae0b35830fe3b37bad0 \ + --hash=sha256:cbf74a81765ee67413503ca6e26dcc4f6f5a519822436cc0a1b97aab6c1b8a17 \ + --hash=sha256:d63ae8f6481fec907ac0f588eee8a90aefde112c633131fe540e5711ddbb5a4e \ + --hash=sha256:e22dfed744bd4002e909464cb23d2f0b05c6f3113a79ef2e9864a53db737c733 \ + --hash=sha256:e2ca8fd1b6b4b82a1c4cb02841d0837e3c12336c2e24b520ab8ab3b969733d8f \ + --hash=sha256:e74591e283fe6eb956416c929eb58262a719fe0311fd9054c62c3350ed8760d8 \ + --hash=sha256:f74455bb086a85d5e81246412602aaa97ed095e504cd40dd261ef50be42205bf \ + --hash=sha256:fb4b9672d389c738b175c4166e78310f8a70358886aacd9173ee03a85ffdc671 \ + --hash=sha256:fc3ed7ebd2a8c96f5b166de0ab9b624996bef3b07bbeb19364dfb78222c22c80 \ + --hash=sha256:fd3718b960d0b5dd213cdf03f3bcb7000e69dda0de8b956061947ff6bcff5558 \ + --hash=sha256:ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef + # via + # authlib + # joserfc + # pyjwt + # secretstorage +cuda-bindings==13.4.1 \ + --hash=sha256:0947a6c491a622b076e9afcbc0444eefd9cbe001630558bbcba8d0a90dab7fe7 \ + --hash=sha256:0e2c357698588b06ebd65811ee2013b6650dd0a10d16d899924aabad0d606d76 \ + --hash=sha256:11bce22fe70a19eb6e727ae7126940245160e0223b3f1cb3e209a313449f75ea \ + --hash=sha256:154e7be4002ab8822e5c7a0b2275155bd4d065c5a7cf4b2479b291c73dc37cbf \ + --hash=sha256:2a95d8e231d219c20beed1c3c97f58603375a3f2d1883d076a6584190606c9b0 \ + --hash=sha256:506457cc214f3dac300044431a36b3f608f7a8c495c5bb40b9008d162ff1d5d3 \ + --hash=sha256:51e730e80d997b6037566033a79aae3995e1981654054536f512e5c091679c70 \ + --hash=sha256:56c1552e207b291c321cef9952fbfaf8591c2a14d9c6e5215071020f541a04e5 \ + --hash=sha256:62df23df11074e9833bf348bbcf0b8eec2fcbded4f305c6fbaa3ed067433e97d \ + --hash=sha256:6e4627fedff4fb96b90c19f178ea1c607316eda7f2daa9f8a44973515e2388c8 \ + --hash=sha256:73d86523ee2f749cf312fdada7be83160c84ddf4845232769f044695ec1c0e68 \ + --hash=sha256:76c10f8ad5cf55676518b2cb3d1e3d867826389b29d8418b402d23ba4df7d655 \ + --hash=sha256:7f7fcf1ac97f5b7cd0fb6c91556d4963509f82b1d1d11310e067b33b3dfc7a04 \ + --hash=sha256:84ee88862e2e6ac39a5434c061f7f4389fbefbc418487d0670c86601d517d038 \ + --hash=sha256:9f29c65d30826c335aa59031e5dfec5ae306a9b259c9cc64d440e9a1ef9e3159 \ + --hash=sha256:9fa1c6b6bf49fa37b2fe5b99fe353787f74a9ef1fbdafe6b4119634eee36f81e \ + --hash=sha256:b601c0cbf0dffb648f68e56a60b320738a20210293f33896a1964a6438cc65f1 \ + --hash=sha256:c945befbc9c9d1337b69568ccc73e1c5a6c695ce061f9c9fac2472e97660dc00 \ + --hash=sha256:ce78039ca08698e741eff1bcb142c4cb6000ee9fd0864549545babdc5a7ad6c9 \ + --hash=sha256:d7df2dddb81feb15787e8c4a13b7aa3f4c23eabeec7746037e94dbbed065fc6c \ + --hash=sha256:e5dc0f13cfd14cd62206fede462f91c497d0c484b8236f4158531d20377066cd \ + --hash=sha256:f444d7e488cbc47e79b7be0d1cfe97201f3e1f186a18ee10f2e4da3265975b16 \ + --hash=sha256:fb66fa7ea3f257a0451d54edc4b5a4a052eb29c32ba15b104f465ecb24907ffb + # via + # cuda-python + # humming-kernels + # torch +cuda-core==1.2.0 \ + --hash=sha256:0dddba476d990c62a17b58b2c198a3b2768d575987577febda8f3e88140df192 \ + --hash=sha256:23073bfe5087d55b6b3e674481b546efb3bfe106642a868972084b95b7860e7b \ + --hash=sha256:248117d2d8160ba052b49c60ed4e6c970e8fcdc311ef9523a086dc4574d40c77 \ + --hash=sha256:29ec78a8a45ad029e8d31b0ee67fe658964efc97a0176eb9d7ad354677116923 \ + --hash=sha256:2a77d40f613e4d151f4466818d1baab271237122c7240450fe66fe8a071ff86f \ + --hash=sha256:419e884cbe26d85936c8ffea27865a6f2589677f34a81eecb0fe1ec3715c06a0 \ + --hash=sha256:4206c19f3c787c6dd4fdce3625e842686dc73ba665d46d16d6c57659a94db0ac \ + --hash=sha256:72b2466e547e177a04ddfc036e3abf51a20b2281febe5d1fb5af3e7fc07ddce5 \ + --hash=sha256:755125ec04d0a153a726b040e3048325b2f1c6d495ef9a124584689e219774f6 \ + --hash=sha256:78b38f478775de6894c4c239ff0cdd5e0464165a2905a5bbf0359d8d9e5acd11 \ + --hash=sha256:c4228d27af1993f8516b51db6c74846ebd1f772bb34132ee721da61369b7febd \ + --hash=sha256:c80d766c86d2c9e43674d4ba734450b7708eab3fab52f3757be339b8d6948466 \ + --hash=sha256:ccfd9905e43dc5ef0f88e26a3e3f1509d84c0306710dec351c5ab1db0da5752e \ + --hash=sha256:cd36063535f75a88c4eb44ba9a678441491a5f67a72f62c2e3b797e8a052d438 \ + --hash=sha256:e138be12af795c69c1cb61e38562ea70e5a38616b2753e2a265231f1941394ef \ + --hash=sha256:f19593699a49a52d08300d44c1c84db8629dc01702b36336401c0ba87fe5067f \ + --hash=sha256:f4be442b2f79f3c8232824e0499fd6a8ef30244fd0b344e14ab75034b3e50de1 \ + --hash=sha256:f81b3728fedcab49daba69bc666e1307c62c1ad8d82b0d6616f6b878b2e20c5f + # via cuda-python +cuda-pathfinder==1.8.1 \ + --hash=sha256:ae0137ff9e56ea97499bcbf54f5f2778ec25f3266715ac86da192a795af982a8 + # via + # cuda-bindings + # cuda-core + # cuda-python +cuda-python==13.4.1 \ + --hash=sha256:b6b114882beb8325139a318a02bcf8feb821dfd724f6f6104c262e2d1e2035ef + # via + # nvidia-cutlass-dsl-libs-base + # nvidia-cutlass-dsl-libs-cu13 +cuda-tile==1.3.0 \ + --hash=sha256:2888d6b89fae053a53ca7bb703c508a5cf90671d266934573c5b6c25978022c4 \ + --hash=sha256:339769f95b3a5453b7f416da6d1285f24d0daf3a700a895b68dee3fa6fc93e8f \ + --hash=sha256:375316b64c51ee7cfadb2f170a30c1547bc41eb39f1e233a6556713857d2e81f \ + --hash=sha256:59d9843fa723ceb4d680ec246e12e3ded857266e4c2bf5c5d21e530d6d765060 \ + --hash=sha256:791b363251fbc64db4402d92153ba3d14bc0aaa4d218cea66562af02a7a76bd9 \ + --hash=sha256:8a9bd4dae193cddf438f55d617b6f25b4b0b0fcf4ac4acde7d2695898e396c30 \ + --hash=sha256:8c71c2fd9b96c054c126a218f9927c8c8dde72441a532464551b865b416d452a \ + --hash=sha256:93e20ed31e46e5bf704fb31d13e1c08338d2177838798876f7ee9ec4384b75ba \ + --hash=sha256:a44a81e255fdb7bf8e1f7511fe3a019e6045024574509ea8548e0f71f25f8473 \ + --hash=sha256:c55616c648f06f84808648a521c67f2d7c790574d6b53ddf8c3bfbc995d36d45 \ + --hash=sha256:e4865acbff1172aaee304bf9c550586088d8b4545a384423597a590899386709 \ + --hash=sha256:efcb93c25563fe23d6aa083c22893fd703122eaf684b0d36874982d28a6dad0b + # via flashinfer-python +cuda-toolkit==13.0.2 \ + --hash=sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb + # via torch +cyclopts==4.25.2 \ + --hash=sha256:0776bc1fa796cd351646c345b7420279e58d6c2c4a8f5d5dd54dea85bdb2de8f \ + --hash=sha256:51b42513eea5e4ba6a08b68acf187b81f1191032cf77b9631086cfa2c88fa7a4 + # via fastmcp-slim +datasets==5.0.0 \ + --hash=sha256:7dd34927a0fd7046e98aad5cb9430e699c373238a15befa7b9bf22b991a7fee6 \ + --hash=sha256:83dbbbdb07a33b82192b8c419deb18739b138ee2ce1a322d55ce6b100954ec1a + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +daytona==0.203.0 \ + --hash=sha256:bfe2527225c612a5f7889679398bdc00aa1d5997e2f3c467d93d654cb9485905 \ + --hash=sha256:c8c1eb44fbb710e6c35fc0c436a0e7d897db67bb5627006dcf5ed96801469d95 + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +daytona-analytics-api-client==0.203.0 \ + --hash=sha256:8b975fb897878e18d51c9b9c363273940d8b4d2f35124385e4f73c14d5e73380 \ + --hash=sha256:e17315b0be51b59a8165653134ffb6f847b01107577d912667a2c2b37f38de84 + # via daytona +daytona-analytics-api-client-async==0.203.0 \ + --hash=sha256:25eb902f09679b32955db729f31af39c6f882b6fb006e9f650b7fe10adc33f20 \ + --hash=sha256:9ee0afff2b84255ccbd3d9a1fb1b4306f7bffcdca06347832ba59e1bcb8ddeb6 + # via daytona +daytona-api-client==0.203.0 \ + --hash=sha256:461e6ef12ba3d570114a5878822b69ea22a22a2c3787b946ad7afc66ee747905 \ + --hash=sha256:e6c5c2b469d282682e39cfe0c5ecd13ec94a5e65f0401add678139a96a39d0ad + # via daytona +daytona-api-client-async==0.203.0 \ + --hash=sha256:2ecdfc0c12f1eb6e92954c5d23d97b7a3a32e0560d075cde38f4a96b8ebc333e \ + --hash=sha256:7dbe3df925bfc3b00e2ade7cbbf45cbe77f254b6d93202e7b10e2a518d7c7e3e + # via daytona +daytona-toolbox-api-client==0.203.0 \ + --hash=sha256:a2f35c55d9e582edda47dfa545b6732c655478c4ccf37a780c192139ba1f342d \ + --hash=sha256:b443ada7142b23653ee8f3aeda8834421e0d6d13a1f8eb455f8f7511a4ba5e88 + # via daytona +daytona-toolbox-api-client-async==0.203.0 \ + --hash=sha256:3d7ad4131200d93dc6a37ac697800957675edb48a72089c06e924e7a8753986e \ + --hash=sha256:948f03d1527994e33df5d805f6d13d30743c39fa079f40e1643b442a8df41b04 + # via daytona +deprecated==1.3.1 \ + --hash=sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f \ + --hash=sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223 + # via daytona +deprecation==2.1.0 \ + --hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \ + --hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a + # via + # postgrest + # storage3 +depyf==0.20.0 \ + --hash=sha256:d31effad4261cebecb58955d832e448ace88f432328f95f82fd99c30fd9308d4 \ + --hash=sha256:fb7683bd72c44f67b56029df2c47721e9a02ffa4d7b19095f1c54c4ebf797a98 + # via vllm +detect-installer==0.2.1 \ + --hash=sha256:85f889d4d19c1caf5bef89ef389eb920cd8c6c2a868e1eeceec24527b93021e7 \ + --hash=sha256:87e2ce7d05cb108b178f8e45bb80d3d88c0cb4b397983345c18ad6562e02e620 + # via fastapi-cloud-cli +dill==0.4.1 \ + --hash=sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d \ + --hash=sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa + # via + # datasets + # depyf + # multiprocess +dirhash==0.5.0 \ + --hash=sha256:523dfd6b058c64f45b31604376926c6e2bd2ea301d0df23095d4055674e38b09 \ + --hash=sha256:e60760f0ab2e935d8cb088923ea2c6492398dca42cec785df778985fd4cd5386 + # via harbor +diskcache==5.6.3 \ + --hash=sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc \ + --hash=sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19 + # via vllm +distro==1.9.0 \ + --hash=sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed \ + --hash=sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2 + # via openai +dnspython==2.8.0 \ + --hash=sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af \ + --hash=sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f + # via email-validator +docstring-parser==0.18.0 \ + --hash=sha256:292510982205c12b1248696f44959db3cdd1740237a968ea1e2e7a900eeb2015 \ + --hash=sha256:b3fcbed555c47d8479be0796ef7e19c2670d428d72e96da63f3a40122860374b + # via + # anthropic + # cyclopts +einops==0.8.2 \ + --hash=sha256:54058201ac7087911181bfec4af6091bb59380360f069276601256a76af08193 \ + --hash=sha256:609da665570e5e265e27283aab09e7f279ade90c4f01bcfca111f3d3e13f2827 + # via + # flashinfer-python + # quack-kernels + # vllm +email-validator==2.3.0 \ + --hash=sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4 \ + --hash=sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426 + # via + # fastapi + # pydantic +exceptiongroup==1.3.1 \ + --hash=sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219 \ + --hash=sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598 + # via fastmcp-slim +fastapi==0.136.3 \ + --hash=sha256:3d2a69bdf04b7e9f3afa292c3bc7a98816bbfafa10bc9b45f3f3700d2f761620 \ + --hash=sha256:e487fae93ad408e6f47641ee4dfe389864fd7bec92e547ea8498fc13f43e83ab + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # gradio + # harbor + # model-hosting-container-standards + # vllm +fastapi-cli==0.0.32 \ + --hash=sha256:38024d2345275e1b37ce8848727a580d84901b570e96b3256d9d36a9a5039424 \ + --hash=sha256:8dcc286fa32f01bbd3f65dd09cfd5a2540ed5f2230b77db7fd30978d6165f3c4 + # via fastapi +fastapi-cloud-cli==0.26.0 \ + --hash=sha256:5fd64c26228c8ead803461e8b58ae64ffd586e960d97ab1fc848efac2d9539be \ + --hash=sha256:94029f92f3dc4289b376e1213f0d6cc7c30d4f32de7e47b5effc6dc754ab9ce9 + # via fastapi-cli +fastar==0.12.0 \ + --hash=sha256:00104297bee6bec0c20aae27e82e62b33db4329575e4cfd3e824745128b9933a \ + --hash=sha256:00cda9a3f11871261a4e77a3b8f0eede85c9730fb7516811bcbcf96a2bb3b75b \ + --hash=sha256:01d90bb64a83843a3ae07e33da300723de7ce50864d6bb47a58bc1112d68ecbf \ + --hash=sha256:042fd43c4e0c3f3ba3f7b8a083694d1c4bd77d60ce266090d4eb96cb8a8021c2 \ + --hash=sha256:067094312cbea82ef2efa3999dc31318ac539805965c9505d99654f01775cac1 \ + --hash=sha256:07d861c7ddf31bccb9615a0ba4c9f06987d1373a6b357183526d68cf9c3f5552 \ + --hash=sha256:09e6a5d4b111c5d40f7dc0813357260ee060b74b4e14bfdc66f5c3ae37f9832c \ + --hash=sha256:0a30eced7e4c711a272336e8c6214ca23f30eaf57b06c85eb6f67cbf50e207b7 \ + --hash=sha256:0df33b22848b96beb8b229869abcc6592592d654f25b6cb1856c08717aaf1544 \ + --hash=sha256:0e16b0d029411989b9459adc65b9b553a7051909a96e4432e4a151490e42f0de \ + --hash=sha256:1155e1dd9c60cf636b6b3d35edfe242348f47286724fa84b5b4055c03d7fdbf6 \ + --hash=sha256:12484b5f87529e9c0af4299a5db779c92c0c393b95867977e3ea02d22024dc96 \ + --hash=sha256:13c2df8db1b7a4d783429ffa1125c53f7dd9534baae7218eb49273797691e2a8 \ + --hash=sha256:1450e0325897e87594c29837fdd9e338b1d281f6c81d14d7a75b6776285b5b6e \ + --hash=sha256:15d89116b102fb7d4c47b6b723dc32d5c12122e7ffeb41b7a8b22cc777c9eca5 \ + --hash=sha256:1664bad53ed42a657d1c9b8a939d3a1b2716ec9feb00a65b12c5c9668e3128f2 \ + --hash=sha256:172ab2b39a9307b0fc56e603b57d01d98f92bcd19cc78c1d3451142d00b3eee3 \ + --hash=sha256:1c2926264f000e88e214a23c1a308a4851325ee72569c27b4188d8d44262fa56 \ + --hash=sha256:1eaf9306c98e22aa7650c6ec02522d77b61c539bc81583897ca351de125ade14 \ + --hash=sha256:1f0432d1138e7f3b65a3afc324ac373706751d1b8554541fe33d5e62ab08ff11 \ + --hash=sha256:20d5e52c45e75a55ed27e7952487506269e9a64559d6cbfaf6977529db81298f \ + --hash=sha256:21256ccb3946730e3d601b6a9c9de61a127855957a7f0852cb14b0dbe15f8aeb \ + --hash=sha256:22c882f1096199d5e63f3ea4d9313e10799fcbc70166d315aa07576148601326 \ + --hash=sha256:27142e096eb5765b4dca66e4dbd29a9c61a0a4bc4c0ca649f6e661475da2bf8d \ + --hash=sha256:29b87474b2e7c9e64549b87aeb2c1d68a94e78c887a3a8d88bf7b804ddbcc0dc \ + --hash=sha256:2dc6149218fa5e34526714aefabc148b25fc7d27c2f25c8b942f1567875511a9 \ + --hash=sha256:2eef75f01b874968ac340be98d44a583ded3b50f29483fd3ec5fd86cce41db7d \ + --hash=sha256:304152fb06608208581e3340f51e5f8d9658fd1ea66f204647ab931eb3525b24 \ + --hash=sha256:3053bb800b5375fbca8f96d256654ae3489c439f1b55766896d1c703d8281804 \ + --hash=sha256:3060521ae526301acadd84fbc3bdf50faa63e3f06f7faecdb286c7e4e4d8b22d \ + --hash=sha256:310c99a33f83f83f49917cee153cdd49ac0a3e42f78ad685a84a700ede3aece5 \ + --hash=sha256:31196ca910579d0317fdbcefd7bbe82a08b5c933bd0a61269b66ca1feeb5c82d \ + --hash=sha256:3766bf0aeeb6a03d114b185472593a2a3f0ff43f1b56c40fe0cda4283f9f4351 \ + --hash=sha256:37e0d61c64eaf5eae1970c0e73a56c596b3487b14bd6cede3c6426d981cb2bf5 \ + --hash=sha256:39dad3351f1399cd28e2e649b6651299ef857df6c745e598b00b4167dcf93dbc \ + --hash=sha256:3d47478fc6ac75e6a2f7a0adf7d5d0636f36fbd2b1269bf87530c5bc404af1c1 \ + --hash=sha256:3fa5fd057b4f4537f04dd4e4f13be92433bf47bc479309335755531c5af34560 \ + --hash=sha256:41d7d26a09ccc0e6a939194a640aa934ae5e263581fb0127c44b0b1dd720f071 \ + --hash=sha256:423d2b0ff5a43405c9776f1ef0779502c547bdb2d7fa7857b4f7434c95035556 \ + --hash=sha256:4468813c739aaf498afc6f553fe082b485d39c3ddd4fa4b1ca130ce6d82057fd \ + --hash=sha256:4497098ebdf3c1f89dbeafe44ebcd7f143f8a774641bbfa13d1d793104464c41 \ + --hash=sha256:45164f7138613b76918101ea28842cb69c10ff441c1ba2d56d7c6b28053f28e2 \ + --hash=sha256:4587a08d6de2e62611278fc4cd36186a3ebbf6609d9e49df34d79a407d66f599 \ + --hash=sha256:46feb733e23f2a85f097f5bab710e49ad3e4d9ad63d6a6c5b4c6d470239c6217 \ + --hash=sha256:471a2f3c7295c296252463ae2eadd87f258dec33a7fec053a553c90930e03392 \ + --hash=sha256:474fa88aab2aa2ea32682edfe46cd4d24a5f75acaa2021b40d1e247e33e21b7f \ + --hash=sha256:47739deed9e4ccf6514821d1c7a79c9188e4a5029d80b618078739d91e7ed9f0 \ + --hash=sha256:4851974bf24c1d993617f5c045a323315b632ad48358dfc95f126d822937292a \ + --hash=sha256:4af6be78f7ec6ef8e6da7d162361e4198be10fe81bfc95112f635c2c14e12922 \ + --hash=sha256:4b395c3d4375809d0d55b5ae297f6bc037b90b382f35f056e453732e4f6f523c \ + --hash=sha256:4da24effffd801bfa5d39c7b68c59a1124df8ce8ddb4048e1a5bbff4a915248b \ + --hash=sha256:4e71715c64695bc80fd8fed2a82af30acb49b6bf085c06876cbfc2116b53cc7c \ + --hash=sha256:56cb3b3c46edf26f054f2420573c7c69c03997203a7a867ca835bc305b4a0f30 \ + --hash=sha256:5aee7a7354425a9841991b8032d94509c022b509cb0e47f484c7fb56df5874e3 \ + --hash=sha256:5d1e50c423cd064f29f11c98f6d995b8fa7df7bbc19f3fdb9f081859afd8e00b \ + --hash=sha256:5fa463374dde07fbb7a46fddc75288554ce9b89f5d0129fa073028e2e13d6db3 \ + --hash=sha256:60de8b81a04cda78e5281d72ff6d0c50e4925621cc5eb55d99c2e344d1b291f4 \ + --hash=sha256:6109ec55528a975ab3644dc1c9ccccb2c2315daa66ca34f54e1e3dca60afa757 \ + --hash=sha256:613466f628667af03de8f914de58a07bfb3ee1bd0347e3532ec9790df92a1e72 \ + --hash=sha256:61f1eec258b328182c6b6258641d33264ac3080fb7fcbf40ea1e326fc855d917 \ + --hash=sha256:644f6d06e312bc47dd315aa36fba723854859601e184113de7b887613ed9a5ad \ + --hash=sha256:654165090cdcac7ff13d43ee4012c366f0f2061ddf46658bc0ad248c8aa3960a \ + --hash=sha256:682a8c91d3ee82179e44ffd7673f4a6a08e7465970b75d19c38df41c39e2dbc2 \ + --hash=sha256:682c531ac174d63919374eaab6fca91f92432cbf6a2262acb72719ba2e2a694d \ + --hash=sha256:6857a79691c5c033a31d76c62ad02f2c92d173a0e1fb2fac7fcb7ac686108bd3 \ + --hash=sha256:685d3d45943b43c32c71c8470552a615c90e06ca532db1b7a5633f01aa108f0d \ + --hash=sha256:689fed84ab320e428fb7ee1ee7ad5dae856f8d75c12df6860258fa94497eae7e \ + --hash=sha256:69391964bbf1b170eeed382fd05d490ba8f6b00fb0b72627ba00cf7d037b9d75 \ + --hash=sha256:6958a332c55052dd8090b03de238ca59190d625f4ba9c292b34e938ac64105ea \ + --hash=sha256:6a83ae278bcc718dd155219fbbd552a16bd8c178effc5021600c3be2806a01cf \ + --hash=sha256:6aac6e5b0c9f140a3ee0bfd1037b3c178cf5b6f7f1ac37c002006bb34d8c90d8 \ + --hash=sha256:6b05c0c739c43b9228e57fc59eb68c38660c62030bcdea3a032b269df71f5bb8 \ + --hash=sha256:6b57afcac03ef59dc3b26b0669b8da7f61d0d8961896b5a809c3d6b31f664ec0 \ + --hash=sha256:6bc9c294f2aca5590b8188ab09e04099b727444cee7e56bd89d73c57adcdb2b8 \ + --hash=sha256:6c068844bbfbc26b42c4142c53d994df6b6bb71146f7f860a7135d49d916e01a \ + --hash=sha256:6e94e2881c3aa53da5d9161e2e64c0d698c66506a62024d5900943098220ebc7 \ + --hash=sha256:6f25c1aa6d55a457d95dc2163bbc27942e1541ca6792d4bf323a922688b8597e \ + --hash=sha256:70077a6d327ffa5d54f9d7bad6e6583c010530a5b120f5ad71a83b0a14d26ad9 \ + --hash=sha256:70fb44c26eb993bd676ccec24ce45b70354e4d1c0e35750a8a49233934049d7d \ + --hash=sha256:7116a770a4e47262734fafe06d3e835a23b833e81b45b3054558301385a0f2a7 \ + --hash=sha256:74858c4648bdc4450a66f3d6ec16a4e61ba48c16c9898a88a061d3272f82c65b \ + --hash=sha256:74c928183d7ca19056bc0eb24d8c1907c115cfb2382ad4a7c32ccbfb04ca0a0b \ + --hash=sha256:75ea567751a7eebbc5cee86e3df5f0e699afe984e6b76eed91fc304485b0808a \ + --hash=sha256:76115050e8f076fb96f1aa7662dc502b5232d21b610aec08035957a93a830cb1 \ + --hash=sha256:787e3c8018848b56b7e47918f9a9ec0e13a68e39af6e4d386cf083b2973d254b \ + --hash=sha256:7b362e5404dab262e85f0d93bd950933a0935dac6a9f5f0516bba6c703c440ec \ + --hash=sha256:7c9cad467ca85bb2629a5e4d52bc60997221fe91d19e93ca8c05735d23f72de9 \ + --hash=sha256:7d875d99946a11538f7ecb183f0a885d1d0a0495a6f1d6d2aa1de9b5fe6e5e9d \ + --hash=sha256:7ec2100cea7274ec4b1ec77cd5613318a2d9fc9f7a291a6acf642a26e3856a93 \ + --hash=sha256:7f00ba54a5b44eeb6075005915a8a11c93ff04cf82ac620c7d33105cf4f7ad3e \ + --hash=sha256:7f59d3243d8913db385ab822be8f111f188218ea73f9f14f5d70c869a33ff4d1 \ + --hash=sha256:7fd86318da844a9553e02d0d1dfdeb459b4d516c49c8ed66264ae59a40e3b7aa \ + --hash=sha256:81534df96e775ccaa37fcd1f45e06f48c245ff77e30d3bcb0d3c1101da9399e8 \ + --hash=sha256:8383404eebbff9832652ddcd32f91bb4acd8e038b27597dc12aef33c4e72f7b7 \ + --hash=sha256:84caa362865cac75807c51afbeff2e9b313fc45f89e0865f7c8bf627ea721f4b \ + --hash=sha256:860516a52a3bdcaa746be42fc1bdbbbd48e05c7579f9f61e30d78a7e065835bd \ + --hash=sha256:868259b82a03d90eb7b618478a5f5721f293f477b9a1f2037546e9edec21792a \ + --hash=sha256:883f1e06c0d9649a2e54b767b3384b47306098ea73be3ca288d562c4d73dbcaa \ + --hash=sha256:8fa10e692ca23aafb88ab441bb280724328da7b8cebddf3296c751b4e06324ed \ + --hash=sha256:91d6929f821729abc10b754180675dcd2e5328ac12d28abef4409afebd1a8c59 \ + --hash=sha256:930d355b98304984b70b30b8a678c782ecfa958a9c710134c71ea54db995a21f \ + --hash=sha256:9612513fbf7fa906bb186c3e30f51edb4b6baf27088a7d4b4ef0c6e8732b16b8 \ + --hash=sha256:968d64c203d10d257c2f2268cfc97d94f273fdfc64b37a4739fdb6e2cf2c3f03 \ + --hash=sha256:96ae27bbb807e39e05097bdc2a6cba6820f31dd053aaf8cf0a045e5969041778 \ + --hash=sha256:a2819b9061cee89da560156b77230d4ffe81e75b8f64b9732ca34d7bc546e49e \ + --hash=sha256:a3de985d942247fa924e185ff2744a6da0005dcedbb39ae1b811bec11710e572 \ + --hash=sha256:a47a68d1b9bd59062af41d9809a340739ab1ba13cb5b4beb23466a621d6479f2 \ + --hash=sha256:a57a4b3ea38937f95c8ac76432992dc29f672b265e021a4c8c7191a767188297 \ + --hash=sha256:a7d9fd762e7eab2262ec004aa9c714b53d303e11cca7c814b7b634d9d2424691 \ + --hash=sha256:a843704912dc3b20e152743bd5fa3e225bf9cc23c34fea0debeceefead477e78 \ + --hash=sha256:a84c2c04e77559363e7da7b3a99f94a48d938043de09bfc13042f4437ae1f86f \ + --hash=sha256:a8a8130f236a5dc2ceab88486f77bbdd516d08dc949d0f04194305845cf44c19 \ + --hash=sha256:ad38b27e93ba9c7de076ad694d224153bf8d66695acc27a114cba8087078fd54 \ + --hash=sha256:ad8185a7b379e5cd81ef65209d21db4c63e8a62bcaa2d17fe40a8e50fcb28427 \ + --hash=sha256:adfc7981f3b3aaafcebc555437e66a37bafbe48d4e3e2129b782b52ed96b6c06 \ + --hash=sha256:aeb69fe64537deec4902f45ad9634b85d44ebb42ee1a33725d6584e8d9b33927 \ + --hash=sha256:aef3e8f913fcf2150e664bfbdca9462c6472ad1c1c553f4b58c1b5d2b34807f5 \ + --hash=sha256:b16451d5b50579e4eb7dc1761946bc6f6186df44fa84310a06f455c26eb4442e \ + --hash=sha256:b1d56e2a52bebd3e379d0cacc2b018b819a0b99f0dc19b4453f304c4e2fce5b3 \ + --hash=sha256:b21937917620122291e65ed1644e8b8acb519fc7df44d2ea3b5b9963a5d804e9 \ + --hash=sha256:b2fcbdc9b49fb240965941a21fd45ac9f7f75bf87acc33106e1ee70ab912cfdd \ + --hash=sha256:b3a2b7ec287bfe7110a1c047698d681a0a1c4b888c7f4aa93862de9b541928d9 \ + --hash=sha256:b3f28f54bde75366cc1b9357ff739593fa61cd81736f0f31883ea026623108b2 \ + --hash=sha256:b4a7b6ca4a04e269aa26533ca8bfd0c674e4ee7328b0d3d80d45ab979a7e613c \ + --hash=sha256:b79e69b2c9d0df293cda11881a1b27ab2893460a2b68229b20f7757fb5f5fe2d \ + --hash=sha256:b9dbdd0d9a7082d924489411bf84d8dd392f18bbfb4379a60fee97275e6d8c1f \ + --hash=sha256:bba71522eae6a7627a5514ffdd4ac9645ef27d82e23931d79fd974bb49c3f2ad \ + --hash=sha256:bc98215d7cdd32b70d2ab8123448071f33e5cc4a4af2ddef67af37beb420dae2 \ + --hash=sha256:bec95651f31caa9f505bbeb2019187e2f0ec561548ae0cd33cfa753ef59492ac \ + --hash=sha256:bfa2891ee4bd86b71721b0afa6c410c2917c86522dffc13a5aba2038d147e81f \ + --hash=sha256:c03a61a149eaa857a4af8bf6c0bc0895bf668b8d5685130337a1ca42e60f9828 \ + --hash=sha256:c048c732e3ca28a132732f83130ccdab58d9b27dd36bb26bdeb42c2d48827da9 \ + --hash=sha256:c19e0d943e68ee9748610e510639e438c07d4dda13743cbb2d928689aad15599 \ + --hash=sha256:c46cf6767d71506b6917cddcc903b0b110169d5254717c95ea00e2920a85df3c \ + --hash=sha256:c5755332572756061b29766a9ceff2c837d52d96828e58f908cb46cc49123bd7 \ + --hash=sha256:c745781d4836c794800fdda4641f9d685b14e1981ed9d898c7ac152ac62cd1dd \ + --hash=sha256:ca63fee43f07408efec09e1c0ae34a1b29ae52b8c1adc31bd6434ccc9e1741cb \ + --hash=sha256:cbf3d5bfd73516b506a916f6809b90a4ad73ff5840c0af6cbe0142417a03b014 \ + --hash=sha256:cc892486eb242ac55dde185d547d2723289da50c6cdc06614868416576ba5c4f \ + --hash=sha256:cdbb38929383fefe38909421eb0bf9d0ae058c541b93e80383b678755eede190 \ + --hash=sha256:ce9a7e7757074a3d0920a8bc3936cf4164f63786d8e1b64e425d2957ddcceace \ + --hash=sha256:d03e32e47a98f02c6a99d894a07e54efea0b9c3eb6fa8aed4f81b075530b2c7f \ + --hash=sha256:d3f780bd6d4e39df5b21862d23df636ad03fea45d73e105bba2493f467fe2b86 \ + --hash=sha256:d7a37d672233031bd41b000852e2520d34a1dd362c2e3b9000b6400a16b3bba4 \ + --hash=sha256:d9e403be00e17d340131327f97789c6b640ac516926e1217cfbe522c24b5120e \ + --hash=sha256:deb0b3882626832361dd0fcee1f4ef1c6e2a7aaa4165e51c86ae0bab95caccd6 \ + --hash=sha256:dfc34a941607a44563ec4b4f0f5c7af7f237bceab73c372f1987d505ecd6165c \ + --hash=sha256:dfc8a2bfb1b52ea812eee3bf34f7c76ecc72b74e3346b46fc308212fa27c1f4a \ + --hash=sha256:e2b8394d4c417dbc05c3a5c45e6c9742d5e78ce07e6eaf18894218c0a9dae5d0 \ + --hash=sha256:e2bfad69679111e4567d4bad41fd795071c9335cd94bc0f26e24b7d19e95c9b1 \ + --hash=sha256:e33b9b67d9fbcda49559c653390698a3e4d89c4d9cb9974fbbdd8a8aa0bfe3b5 \ + --hash=sha256:e54319e21ea309e8f2141ed88ace0cb34af72bbd5865eb6a819773a170230e27 \ + --hash=sha256:e7d7512b5c747edfce129448a72c8f6223323748ca3e98cc54401241bff70ee5 \ + --hash=sha256:e8056fb0f3ff2213eb00234d32b2d701cd288b763d3430b67033a393b8d33b47 \ + --hash=sha256:e8e0fb057b5c271f46f3300b539b0d3dab8c8cb2515205a37c818c2f68d16806 \ + --hash=sha256:e8fe9f8e8b6b4df05f1a3561ff9a5348553b93fa6fabc7e31334d1ee720c00af \ + --hash=sha256:ea6c5addb01f206bd75f784540a8a11bbddc451293318a823e3fecaf0d62cc3c \ + --hash=sha256:eb75898c166ff6d232bceb68a5a7dc4a8f17239fb40925d169649a3967b76c4a \ + --hash=sha256:ebe324ffcb3e8efb2308255f60de911bfa4ccf10f087b6e60c62606f091f1807 \ + --hash=sha256:ed84522ddffb4c41c247f3e6823c763faf45af2ba1bf887eaab7f907e74e0bbe \ + --hash=sha256:f0d60682ed24fc6063b18e76a947f1cc0fcd0777709357b801b3e3458a87c2e5 \ + --hash=sha256:f1f3e70447f45e54b488eea8bd401cd3774b7b688d5b507915ae415058d8ac86 \ + --hash=sha256:f5e9cec09cf46ba60ae78bdaf22548b6128d4142c274632faf6c8d2a7a6abf0f \ + --hash=sha256:f62411993976d5c5a0d1090f8c809f96e0671d824ee0d844e5b5256bf31fe7a9 \ + --hash=sha256:f69400ebb83a8d754aa7735165c12f8029ac577c3c08eb6d174eedc5901b7cb2 \ + --hash=sha256:f8da75b5eca0d3b540e50ab6b7dfe4b148d7ae8a0b444a9781fd1219392859a0 \ + --hash=sha256:f95a59a11ee59701a6c0048c90601063894102343c4bb09462eed5df0fab0077 \ + --hash=sha256:fbeb5fce858248d6b9b1fbef12c3e14d174432728c6e6eb1e2a63447432571c2 \ + --hash=sha256:fc7f09c635194e5e673703cffbd1eda80773edb8000faa42da158b8ea1ce5018 \ + --hash=sha256:fce60bd91fd982bf52e9a4c87820a44f92ac0d896bd64544891d6995fa6b8b98 \ + --hash=sha256:fd7a28985d86a3d8582d6ce69d356fc498035d717938b5029df057d599718888 + # via + # fastapi + # fastapi-cloud-cli +fastmcp==3.4.4 \ + --hash=sha256:378202e26ec15b23819d9a1c0d1b0ebda096bc712720532010a0b82a45c2b1df \ + --hash=sha256:f86f208713212260068cf55c32936839eee856fefc7808e18a032f31eb0f718e + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +fastmcp-slim==3.4.4 \ + --hash=sha256:9d3a6327b9ee835188eb7323fc3b5d4cd061631b48da8ece56794bb538972505 \ + --hash=sha256:dcaa3e0be2127d7eacdce592c2ef0039204923dc0ec396454615cb4a3275b078 + # via fastmcp +fastsafetensors==0.4.0 \ + --hash=sha256:0a178417cdea34308148c25062eb5382067491847862715b2928f2e0de1310ac \ + --hash=sha256:0b41cc718d31741238cea3a813be35d5a755b517306a179bf244dd15fced63e4 \ + --hash=sha256:0e7d0e0aabb7bdcaab4604f7493c5dfc0273700611490e6a47fc61836940f2be \ + --hash=sha256:15c779a83f8aa6255a950beaa3237f3da7cb2ef6c0534cffea6ac8be75c5725a \ + --hash=sha256:2ce238466938cb8fab5026fcaa156c74be59d4b1c1f56db89c6b0ed863824a5a \ + --hash=sha256:3ddf4edcf0c6e424099aa18094d65c417ed49e8726f102ce9ee2b77cacd2256d \ + --hash=sha256:42464a5db5969defc6c809e12baa52d7f2964c2bbc58acddc53251fc9b699a56 \ + --hash=sha256:7a474f110c5448e73cc6e3bd1d1776f88956b99bd226efcf2ba8cba7ffa91727 \ + --hash=sha256:7a6d6c2658566b03d1d29bda8f0c52ea0ee9d686851c79c3643bda3751d583d2 \ + --hash=sha256:7c4a784cc3f49b50484ed069d74470aedbbcd26b6549a885fcb758aa8c4a6803 \ + --hash=sha256:891e75cddd59bdbabd4e710b136cd1c629cc2036eecc1c5ce0cc369048c30445 \ + --hash=sha256:8ccfe43f3d66147f009975ed5d58e16a89715ba441917c0577083d3efb1ed704 \ + --hash=sha256:92aa2c043943b5d52f9531f4a49d136b2ba32b01cd43ca3153477b5433994b40 \ + --hash=sha256:9d430d2123ce0a7266ad2303c6cfea98650584a87bea19246c1aa09f61bac321 \ + --hash=sha256:b0efe9bc9603d25590736ce742987ccc3d516e08359245ebb215248f2899e2f5 \ + --hash=sha256:b577a1bc098277bcd33633455483bb01ba0dd4caf285216d75bd64d341807750 \ + --hash=sha256:b9201c0bf8f5a8099deec67150951d274a0dcc696aff5f246abdf8ac363901e9 \ + --hash=sha256:cc91a5298286ad491ceda483bc879c09cbf4ca5deefc858ef4809f8bf6f70365 \ + --hash=sha256:d1ecbb199d98d222c827ae1cc9c7b09dd9ef2d34aa0eb01baa8f81f70345dc0a \ + --hash=sha256:d53e72d456de8b0ea953ebe17b75f6f222b90aae3d4343cf5a28f405eb24e67b \ + --hash=sha256:df8966a556aa56fc2536efb86f32b5e8b44012f71430a4d99aab1cce16bd2852 + # via vllm +fastuuid==0.14.0 \ + --hash=sha256:05a8dde1f395e0c9b4be515b7a521403d1e8349443e7641761af07c7ad1624b1 \ + --hash=sha256:0737606764b29785566f968bd8005eace73d3666bd0862f33a760796e26d1ede \ + --hash=sha256:089c18018fdbdda88a6dafd7d139f8703a1e7c799618e33ea25eb52503d28a11 \ + --hash=sha256:09098762aad4f8da3a888eb9ae01c84430c907a297b97166b8abc07b640f2995 \ + --hash=sha256:09378a05020e3e4883dfdab438926f31fea15fd17604908f3d39cbeb22a0b4dc \ + --hash=sha256:0c9ec605ace243b6dbe3bd27ebdd5d33b00d8d1d3f580b39fdd15cd96fd71796 \ + --hash=sha256:0df14e92e7ad3276327631c9e7cec09e32572ce82089c55cb1bb8df71cf394ed \ + --hash=sha256:12ac85024637586a5b69645e7ed986f7535106ed3013640a393a03e461740cb7 \ + --hash=sha256:1383fff584fa249b16329a059c68ad45d030d5a4b70fb7c73a08d98fd53bcdab \ + --hash=sha256:139d7ff12bb400b4a0c76be64c28cbe2e2edf60b09826cbfd85f33ed3d0bbe8b \ + --hash=sha256:13ec4f2c3b04271f62be2e1ce7e95ad2dd1cf97e94503a3760db739afbd48f00 \ + --hash=sha256:178947fc2f995b38497a74172adee64fdeb8b7ec18f2a5934d037641ba265d26 \ + --hash=sha256:193ca10ff553cf3cc461572da83b5780fc0e3eea28659c16f89ae5202f3958d4 \ + --hash=sha256:1a771f135ab4523eb786e95493803942a5d1fc1610915f131b363f55af53b219 \ + --hash=sha256:1bf539a7a95f35b419f9ad105d5a8a35036df35fdafae48fb2fd2e5f318f0d75 \ + --hash=sha256:1ca61b592120cf314cfd66e662a5b54a578c5a15b26305e1b8b618a6f22df714 \ + --hash=sha256:1e3cc56742f76cd25ecb98e4b82a25f978ccffba02e4bdce8aba857b6d85d87b \ + --hash=sha256:1e690d48f923c253f28151b3a6b4e335f2b06bf669c68a02665bc150b7839e94 \ + --hash=sha256:2b29e23c97e77c3a9514d70ce343571e469098ac7f5a269320a0f0b3e193ab36 \ + --hash=sha256:2dce5d0756f046fa792a40763f36accd7e466525c5710d2195a038f93ff96346 \ + --hash=sha256:2ec3d94e13712a133137b2805073b65ecef4a47217d5bac15d8ac62376cefdb4 \ + --hash=sha256:2fb3c0d7fef6674bbeacdd6dbd386924a7b60b26de849266d1ff6602937675c8 \ + --hash=sha256:2fc37479517d4d70c08696960fad85494a8a7a0af4e93e9a00af04d74c59f9e3 \ + --hash=sha256:33e678459cf4addaedd9936bbb038e35b3f6b2061330fd8f2f6a1d80414c0f87 \ + --hash=sha256:3964bab460c528692c70ab6b2e469dd7a7b152fbe8c18616c58d34c93a6cf8d4 \ + --hash=sha256:3acdf655684cc09e60fb7e4cf524e8f42ea760031945aa8086c7eae2eeeabeb8 \ + --hash=sha256:448aa6833f7a84bfe37dd47e33df83250f404d591eb83527fa2cac8d1e57d7f3 \ + --hash=sha256:47c821f2dfe95909ead0085d4cb18d5149bca704a2b03e03fb3f81a5202d8cea \ + --hash=sha256:4edc56b877d960b4eda2c4232f953a61490c3134da94f3c28af129fb9c62a4f6 \ + --hash=sha256:5816d41f81782b209843e52fdef757a361b448d782452d96abedc53d545da722 \ + --hash=sha256:6e6243d40f6c793c3e2ee14c13769e341b90be5ef0c23c82fa6515a96145181a \ + --hash=sha256:6fbc49a86173e7f074b1a9ec8cf12ca0d54d8070a85a06ebf0e76c309b84f0d0 \ + --hash=sha256:73657c9f778aba530bc96a943d30e1a7c80edb8278df77894fe9457540df4f85 \ + --hash=sha256:73946cb950c8caf65127d4e9a325e2b6be0442a224fd51ba3b6ac44e1912ce34 \ + --hash=sha256:77a09cb7427e7af74c594e409f7731a0cf887221de2f698e1ca0ebf0f3139021 \ + --hash=sha256:77e94728324b63660ebf8adb27055e92d2e4611645bf12ed9d88d30486471d0a \ + --hash=sha256:7a3c0bca61eacc1843ea97b288d6789fbad7400d16db24e36a66c28c268cfe3d \ + --hash=sha256:7f2f3efade4937fae4e77efae1af571902263de7b78a0aee1a1653795a093b2a \ + --hash=sha256:808527f2407f58a76c916d6aa15d58692a4a019fdf8d4c32ac7ff303b7d7af09 \ + --hash=sha256:83cffc144dc93eb604b87b179837f2ce2af44871a7b323f2bfed40e8acb40ba8 \ + --hash=sha256:84b0779c5abbdec2a9511d5ffbfcd2e53079bf889824b32be170c0d8ef5fc74c \ + --hash=sha256:9579618be6280700ae36ac42c3efd157049fe4dd40ca49b021280481c78c3176 \ + --hash=sha256:9a133bf9cc78fdbd1179cb58a59ad0100aa32d8675508150f3658814aeefeaa4 \ + --hash=sha256:9bd57289daf7b153bfa3e8013446aa144ce5e8c825e9e366d455155ede5ea2dc \ + --hash=sha256:a0809f8cc5731c066c909047f9a314d5f536c871a7a22e815cc4967c110ac9ad \ + --hash=sha256:a6f46790d59ab38c6aa0e35c681c0484b50dc0acf9e2679c005d61e019313c24 \ + --hash=sha256:a8a0dfea3972200f72d4c7df02c8ac70bad1bb4c58d7e0ec1e6f341679073a7f \ + --hash=sha256:aa75b6657ec129d0abded3bec745e6f7ab642e6dba3a5272a68247e85f5f316f \ + --hash=sha256:ab32f74bd56565b186f036e33129da77db8be09178cd2f5206a5d4035fb2a23f \ + --hash=sha256:ab3f5d36e4393e628a4df337c2c039069344db5f4b9d2a3c9cea48284f1dd741 \ + --hash=sha256:ac60fc860cdf3c3f327374db87ab8e064c86566ca8c49d2e30df15eda1b0c2d5 \ + --hash=sha256:ae64ba730d179f439b0736208b4c279b8bc9c089b102aec23f86512ea458c8a4 \ + --hash=sha256:af5967c666b7d6a377098849b07f83462c4fedbafcf8eb8bc8ff05dcbe8aa209 \ + --hash=sha256:b2fdd48b5e4236df145a149d7125badb28e0a383372add3fbaac9a6b7a394470 \ + --hash=sha256:b852a870a61cfc26c884af205d502881a2e59cc07076b60ab4a951cc0c94d1ad \ + --hash=sha256:b9a0ca4f03b7e0b01425281ffd44e99d360e15c895f1907ca105854ed85e2057 \ + --hash=sha256:bbb0c4b15d66b435d2538f3827f05e44e2baafcc003dd7d8472dc67807ab8fd8 \ + --hash=sha256:bcc96ee819c282e7c09b2eed2b9bd13084e3b749fdb2faf58c318d498df2efbe \ + --hash=sha256:c0a94245afae4d7af8c43b3159d5e3934c53f47140be0be624b96acd672ceb73 \ + --hash=sha256:c0eb25f0fd935e376ac4334927a59e7c823b36062080e2e13acbaf2af15db836 \ + --hash=sha256:c3091e63acf42f56a6f74dc65cfdb6f99bfc79b5913c8a9ac498eb7ca09770a8 \ + --hash=sha256:c501561e025b7aea3508719c5801c360c711d5218fc4ad5d77bf1c37c1a75779 \ + --hash=sha256:c7502d6f54cd08024c3ea9b3514e2d6f190feb2f46e6dbcd3747882264bb5f7b \ + --hash=sha256:caa1f14d2102cb8d353096bc6ef6c13b2c81f347e6ab9d6fbd48b9dea41c153d \ + --hash=sha256:cb9a030f609194b679e1660f7e32733b7a0f332d519c5d5a6a0a580991290022 \ + --hash=sha256:cd5a7f648d4365b41dbf0e38fe8da4884e57bed4e77c83598e076ac0c93995e7 \ + --hash=sha256:d23ef06f9e67163be38cece704170486715b177f6baae338110983f99a72c070 \ + --hash=sha256:d31f8c257046b5617fc6af9c69be066d2412bdef1edaa4bdf6a214cf57806105 \ + --hash=sha256:d55b7e96531216fc4f071909e33e35e5bfa47962ae67d9e84b00a04d6e8b7173 \ + --hash=sha256:d9e4332dc4ba054434a9594cbfaf7823b57993d7d8e7267831c3e059857cf397 \ + --hash=sha256:de01280eabcd82f7542828ecd67ebf1551d37203ecdfd7ab1f2e534edb78d505 \ + --hash=sha256:df61342889d0f5e7a32f7284e55ef95103f2110fee433c2ae7c2c0956d76ac8a \ + --hash=sha256:e0976c0dff7e222513d206e06341503f07423aceb1db0b83ff6851c008ceee06 \ + --hash=sha256:e150eab56c95dc9e3fefc234a0eedb342fac433dacc273cd4d150a5b0871e1fa \ + --hash=sha256:e23fc6a83f112de4be0cc1990e5b127c27663ae43f866353166f87df58e73d06 \ + --hash=sha256:ec27778c6ca3393ef662e2762dba8af13f4ec1aaa32d08d77f71f2a70ae9feb8 \ + --hash=sha256:f54d5b36c56a2d5e1a31e73b950b28a0d83eb0c37b91d10408875a5a29494bad \ + --hash=sha256:f74631b8322d2780ebcf2d2d75d58045c3e9378625ec51865fe0b5620800c39d + # via litellm +filelock==3.32.6 \ + --hash=sha256:3f16ecd0117feae0dfc147e8c62eb5daeccd8bd800378c3ddf416de9b4feb6b1 \ + --hash=sha256:a3f55a18af3652a94d8f47d6055df434f254ca1d02ef2524850c6d249ca2512c + # via + # datasets + # harbor + # huggingface-hub + # torch + # vllm +flashinfer-cubin==0.6.13 \ + --hash=sha256:41e4848c2d09d220e8394489b2fb6cfec6b6ad09f897b5ab8b39fc23055f6c24 + # via vllm +flashinfer-python==0.6.13 \ + --hash=sha256:239e6ddc3cbbaf0bee251861a8c7c69438b1171830d69ddfa133ddea4494850d \ + --hash=sha256:8a6d7d3708c7c87952390ec4e3aabe6e1c356defa8c7211b26bccaa355a61c59 + # via vllm +frozenlist==1.8.0 \ + --hash=sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686 \ + --hash=sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0 \ + --hash=sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121 \ + --hash=sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd \ + --hash=sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7 \ + --hash=sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c \ + --hash=sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84 \ + --hash=sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d \ + --hash=sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b \ + --hash=sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79 \ + --hash=sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967 \ + --hash=sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f \ + --hash=sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4 \ + --hash=sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7 \ + --hash=sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef \ + --hash=sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9 \ + --hash=sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3 \ + --hash=sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd \ + --hash=sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087 \ + --hash=sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068 \ + --hash=sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7 \ + --hash=sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed \ + --hash=sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b \ + --hash=sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f \ + --hash=sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25 \ + --hash=sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe \ + --hash=sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143 \ + --hash=sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e \ + --hash=sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930 \ + --hash=sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37 \ + --hash=sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128 \ + --hash=sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2 \ + --hash=sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675 \ + --hash=sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f \ + --hash=sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746 \ + --hash=sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df \ + --hash=sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8 \ + --hash=sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c \ + --hash=sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0 \ + --hash=sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad \ + --hash=sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82 \ + --hash=sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29 \ + --hash=sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c \ + --hash=sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30 \ + --hash=sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf \ + --hash=sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62 \ + --hash=sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5 \ + --hash=sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383 \ + --hash=sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c \ + --hash=sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52 \ + --hash=sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d \ + --hash=sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1 \ + --hash=sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a \ + --hash=sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714 \ + --hash=sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65 \ + --hash=sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95 \ + --hash=sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1 \ + --hash=sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506 \ + --hash=sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888 \ + --hash=sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6 \ + --hash=sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41 \ + --hash=sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459 \ + --hash=sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a \ + --hash=sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608 \ + --hash=sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa \ + --hash=sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8 \ + --hash=sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1 \ + --hash=sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186 \ + --hash=sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6 \ + --hash=sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed \ + --hash=sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e \ + --hash=sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52 \ + --hash=sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231 \ + --hash=sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450 \ + --hash=sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496 \ + --hash=sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a \ + --hash=sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3 \ + --hash=sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24 \ + --hash=sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178 \ + --hash=sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695 \ + --hash=sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7 \ + --hash=sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4 \ + --hash=sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e \ + --hash=sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e \ + --hash=sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61 \ + --hash=sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca \ + --hash=sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad \ + --hash=sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b \ + --hash=sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a \ + --hash=sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8 \ + --hash=sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51 \ + --hash=sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011 \ + --hash=sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8 \ + --hash=sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103 \ + --hash=sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b \ + --hash=sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda \ + --hash=sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806 \ + --hash=sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042 \ + --hash=sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e \ + --hash=sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b \ + --hash=sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef \ + --hash=sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d \ + --hash=sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567 \ + --hash=sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a \ + --hash=sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2 \ + --hash=sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0 \ + --hash=sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e \ + --hash=sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b \ + --hash=sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d \ + --hash=sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a \ + --hash=sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52 \ + --hash=sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47 \ + --hash=sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1 \ + --hash=sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94 \ + --hash=sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f \ + --hash=sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff \ + --hash=sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822 \ + --hash=sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a \ + --hash=sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11 \ + --hash=sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581 \ + --hash=sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51 \ + --hash=sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565 \ + --hash=sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40 \ + --hash=sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92 \ + --hash=sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2 \ + --hash=sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5 \ + --hash=sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4 \ + --hash=sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93 \ + --hash=sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027 \ + --hash=sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd + # via + # aiohttp + # aiosignal +fsspec==2026.4.0 \ + --hash=sha256:11ef7bb35dab8a394fde6e608221d5cf3e8499401c249bebaeaad760a1a8dec2 \ + --hash=sha256:301d8ac70ae90ef3ad05dcf94d6c3754a097f9b5fe4667d2787aa359ec7df7e4 + # via + # datasets + # gradio-client + # huggingface-hub + # torch +googleapis-common-protos==1.75.3 \ + --hash=sha256:57c435ac2c68b108999b6db075d9053e4d7a936ba57b4a3d45667b1346f1738a \ + --hash=sha256:a018d2bf098ca9fb6faa08d5bb780e2a2c2f73c566f069761331386c9596d3f2 + # via + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +gradio==6.20.0 \ + --hash=sha256:8672866e9225a1f8297325a6742bb525a3a0ddc8aa4d75d99beb10a27ea2915f \ + --hash=sha256:91d2ec29c37917d55b18dba87aa15bb15c4796d84dce9a9ef4ec7d87453a7fee + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +gradio-client==2.5.0 \ + --hash=sha256:4cde99bad62149595c30c90876ca2e405e3a13687ecf895474f3412cb476673d \ + --hash=sha256:d43e2179c29076292a76485ad7ed2e6eaa19d14ac58283bd7f5beabfe4ca958c + # via + # gradio + # hf-gradio + # trackio +griffelib==2.3.0 \ + --hash=sha256:1b8f9cd525681c26b1d6d574faa1371651e8459ca51d209684f50b8096ae06e0 \ + --hash=sha256:7b0952caf5bca6afa4bb5ee8c6a2d183fe3f21b62efc5f6c7243cb2b26d2d115 + # via fastmcp-slim +groovy==0.1.2 \ + --hash=sha256:25c1dc09b3f9d7e292458aa762c6beb96ea037071bf5e917fc81fb78d2231083 \ + --hash=sha256:7f7975bab18c729a257a8b1ae9dcd70b7cafb1720481beae47719af57c35fa64 + # via gradio +grpcio==1.84.0 \ + --hash=sha256:026d757df86c5b7a41de8200b9a2cda454aaa5004cb0c7e3374c66eb82f61499 \ + --hash=sha256:06619ba1515e5ee69fb2a514e95dd8be05ce74cb3928d5b34f87f87c86fe3c27 \ + --hash=sha256:08735e3d08d24ab3132cf87e2e5dea8746cabcc7d676c2b0b7362f195feef9d9 \ + --hash=sha256:0d532ade4486dad9b302ffa4d4683d67561051c26d17c4023322845e9fa10140 \ + --hash=sha256:158c1c11cfb61b4849c3caf4d52de6f5ecd376e14446feb4a90dc95a90d616f5 \ + --hash=sha256:15bb76489e337fc492685c9758e2fd4d4ab516b901ad830dc5a91987decf00be \ + --hash=sha256:19aaf172fc2edbefccce3f6e92c5150975dbe56c45744e9e87cf72ebdf85bfbe \ + --hash=sha256:209414080da8c20af94df1395b635da52dd57b5edc9e917e1deca0dc1c4bb55e \ + --hash=sha256:210e4c32f907045eb8158273e60c6ab69a3947697df6245dbda381f26c59485b \ + --hash=sha256:23e6e8e8a75cff88e0a793bfd3becea03a13e2763ae90c1ff573bc19ca5b429a \ + --hash=sha256:27b8b36200a9fbee6e120246f4a8a41657549107ef19fb2c819c4b2fd524f39a \ + --hash=sha256:28d2609691da93051e998495108bbddd2a9f7a561253bae94828d81290f30c15 \ + --hash=sha256:2c024da73b296f040b8360e60bd73a659b230093684a438da0e1260f34cc724e \ + --hash=sha256:393d8a78bff6731ecc5ad2151a821f8fbc1709b137ebb9c25a4ef399fbdcc914 \ + --hash=sha256:3d6a82c4fc6c85f2fb7572c86bdb86f84c97b6580e5f6599f711800bac48a5d8 \ + --hash=sha256:3de427b05f244ba2c2a9bdc67e7a6731c8340811524ecc4435466549f8af1d17 \ + --hash=sha256:406583b4e8fb2282ebd392e12b963e601c1f82e07125a8c2cb5b144e7e024796 \ + --hash=sha256:4119efa6519871719ad81f33bc95ab87857dcb1c5801f30a6e592f2c41164169 \ + --hash=sha256:42959bd50dd660ffc3f2a9bec15a6da4f9aaa0dda555d59ff2d2e80b908456a8 \ + --hash=sha256:455ed6083353b8e938f1d58c765eab2fbb165731e5b507be30fee344915a2a11 \ + --hash=sha256:465eef3d17e59ad22a556fc0138f7c7c799df426734344daec42c797d49fda99 \ + --hash=sha256:47ecf0d9b81d981f07b61bd89eced9d2582f5eaacc3aaa36ad27f81aef70a27f \ + --hash=sha256:49717e857899f4136d7657bf5aded61ac479110a075438290923a4d86af7cd02 \ + --hash=sha256:4aaeceeb7fa7d824c322d1ec3208c8495c88478a927295553235435fc49043ad \ + --hash=sha256:57dc36a5ab0e676f5f6e171de2917fd0aef73f32a9aaf23956bfe19997a30bd1 \ + --hash=sha256:5933a052946873d01a42119a05420d669bdca436aeba2d1851988ccb12b421c0 \ + --hash=sha256:5deda5b4bf62769eb98c119cca43d40e1231e34846b19db5cdea821d446a2253 \ + --hash=sha256:61386101ecaa096b694d0dd278caf99a56aeec78440cc17e918eef0b50f2d567 \ + --hash=sha256:659728f20fc7a0933ed7b1945435e31014b97ab8a5a7edcbaa70da4794aeb191 \ + --hash=sha256:70bb4ce8be0c5606bec259cbd7152374470396413b7863a658a08c849e6b29ff \ + --hash=sha256:71fd60e6e426d293d0a2f685115ad0a0845117602cf13605a4be7524fb5f7bba \ + --hash=sha256:756ea5c2da00fa65c930284892d2a9706828704ca3ba40b4c51c4834eb39fcfd \ + --hash=sha256:800b7e00d92553313c0463c200087930aa78678ec1d528193aeb50906f55989b \ + --hash=sha256:82da34ae4f639c73ac46e521e00c0a49bf86f717b9fb1f405f133e98731e38dc \ + --hash=sha256:8e1a45d174b6b8589f51dce1cea804aa6c1f72c9c80cba91ae2caabeb6d90540 \ + --hash=sha256:8e3f508d0e9e6236ba2f08d56e33355e434e785e813149a1b8477d3edf69779d \ + --hash=sha256:986e9751d416d7a6eaa2fecdac38da63153d63a4b340ba7d624889c490451500 \ + --hash=sha256:9b73836ba0e16fcbb57c31cf6cbc2907c8d8c790b83679df454b74bd15e0be04 \ + --hash=sha256:9bab4cf571653a8afffb83ce21aa27b51dfe629b526b7b6adec35491fe1fc2ea \ + --hash=sha256:a71d24f40b0cc6798feaa978c7411dc1135b7018e9fc0442db611c139bf58344 \ + --hash=sha256:a9383401d9f116f98cacd4eba6c505a6edb80ba65badfc8e8ed8ae64983bcc44 \ + --hash=sha256:b44f0a0fc7bc6677d38cc80bca1a32814ce6c8f200fb8b3c1a61c9d77eaefbf3 \ + --hash=sha256:b5c6f20d657ae09ae4e30d9d3a21edd13f1219d58cc6f999b9d1bb63be9c1baa \ + --hash=sha256:b61692f0069b3eee2fc8a3a1b7f6c044df9e03fede6ce69b3ca832e1c39f26c5 \ + --hash=sha256:b8c62888c3e49debf37ad9773e3c02f77b0c1e811f8fb0962f2b6c3bbab5b97a \ + --hash=sha256:bd8ea8eb3817b226057cc1c0e7ec4b378dcda52043b972b6ff12b1152178967d \ + --hash=sha256:c5559b492007dc09b4de9b95dab05f0b5e53547aad230cf07e46c7dd017a3be5 \ + --hash=sha256:d0fdd25faece8a1f95e8a3a8006e29701b5cf8dadb4a8132e68f3134637004a5 \ + --hash=sha256:e094dd21f077af8194923fc263cad872eaa1802bb0156fd7e5ae18e99cd86715 \ + --hash=sha256:e41c3993eee896c617dbd8a505085d28b6e84a0445ed9a1f40f95808473cf678 \ + --hash=sha256:e88d304f094f4937bc27ec6a435e218a084168f11ec630c8d5d39b431d08d81d \ + --hash=sha256:e90e3bdf7b5eac005fef631adae9cafde16f922def207b80a7c46b253c18ad20 \ + --hash=sha256:ed2c1493c44d0932f1e55fdb5d1ead658c68288ec5d51b8c4928422d98633ef9 \ + --hash=sha256:edb6f87fc60ff438557291501b3e16c7a77c3b01a52d782cf276dccc7c5dd89c \ + --hash=sha256:efb29f8633bf6630dc89de4fe0353ac3d7e4b70ef7b6e29fb40f00e68c127fa5 \ + --hash=sha256:f6c972474ce691aca74e58d17625450cef153dc4760364cadeb167983ea6d589 \ + --hash=sha256:f6d178ba6dc8e82976c184b65fddde172d054c17237993a3e083efe4f134d55b \ + --hash=sha256:f9a456bdbed52a01c9ab8423bdebab04a5363c78676edc55ab9b58bd13bdf9e1 \ + --hash=sha256:fbdbcd06986ede3ce584083b1dc2afe6808e8943e5cf50ad11183c03aceda25a \ + --hash=sha256:fc66cb50c93554b86db0b6625ab5c6e9051dbf8847c08d93c84918e02e413fb7 \ + --hash=sha256:fff5ef3fe1bba7d6147e5f19e01e5e122ac2c076486887ddcb8d42e663400fbe + # via opentelemetry-exporter-otlp-proto-grpc +h11==0.16.0 \ + --hash=sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1 \ + --hash=sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86 + # via + # httpcore + # httpcore2 + # uvicorn + # wsproto +h2==4.4.1 \ + --hash=sha256:0e25f1462b23c9cb82d9eb02e28bc706dac2a68cb457c6a0d74d63c8a2a5d0e6 \ + --hash=sha256:4e866ffb1a869ae14dd9b5e6beb5c24a13da0495ad72b65925ded182521c1516 + # via httpx +harbor==0.22.0 \ + --hash=sha256:4c4c6571b3d160ed0cb45b82918136751fb08e7b8596412723ac00dde12eeabb \ + --hash=sha256:becf0ce354026cc37899855e0a0d2687cd5188034a43635849245069aad0938b + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +hf-gradio==0.4.1 \ + --hash=sha256:76b8cb8be6abe62d74c1ad2d35b42f0629db89aa9e1a8d033cecfe7c856eeab3 \ + --hash=sha256:a017d942618f0d495a58ee4563047fa04bef614c00e0cb789a9a6d0633cffa7b + # via gradio +hf-xet==1.6.0 \ + --hash=sha256:0e6e21fa3cdfcdcd76748564bf593870a5e013f47d97cf10aed63aa222cff5b7 \ + --hash=sha256:23379c2f9ec8696d952b16414a2bae72cad86a52df869b050698ba60f538c675 \ + --hash=sha256:2e58454a340b3556dfa4972d5451aff4fba8dd42a236600ba1a1d2b1514f0fef \ + --hash=sha256:35cec30d75c6f9eb9c16a77cef68e85a103b72e24d4b473714ec9ff06428bab9 \ + --hash=sha256:3dc3e35441ba395006af5aaacc40ef2e603c51ef46c3530b9156185f00935ea3 \ + --hash=sha256:4fc74352a17015bd0ee90038bc9efe38db894cde45f268b6712b04fce8cd0acb \ + --hash=sha256:5153e6bb103ad49d6ea9f1b2e230db5a2ea32551ad09a706d2f61d7c7c80d80e \ + --hash=sha256:5789835d7c6bc9436962853192082374297fb72d7eff7e7762ec25ceb7e25338 \ + --hash=sha256:633dc0cd71d32da58ab8c03ad38e2fac452c15c2b0a2866ebf6ededfe0a5061d \ + --hash=sha256:70cbb9c896901600128cb9b6f06e132954fbede1db30f31f7c6c63f84cb7c31d \ + --hash=sha256:75765820ce4700db3750c94acc8fe27c5fae4c9ec000a0dbac3ca082acf97765 \ + --hash=sha256:8fb4f71cba6129110c3374a33f919001ff130488fc23553698e34cc1c2a1198c \ + --hash=sha256:948f15d3a9545cfe5932f6bd8b440f6ae630aee108f14b7bd6c561f7c2dcc522 \ + --hash=sha256:d62671bb130879cef0ee4c9ebe47a14af6c66ec53e6d84dc15936e5ffdfac82f \ + --hash=sha256:f0906082d9932ae0c0057fa194041c22b4e2cdb46b2592ef3b91f020d62a081a \ + --hash=sha256:f2f7278c05c22fd60cb436cda1269649b3e81db65ecdc8496e5e164aa4143e7b \ + --hash=sha256:fb4fadde1b2b70bf4c0c14a6dccbe7194b1c28947fefd5bbe3fed9d940676c3b + # via huggingface-hub +hpack==4.2.0 \ + --hash=sha256:0895cfa3b5531fc65fe439c05eb65144f123bf7a394fcaa56aa423548d8e45c0 \ + --hash=sha256:858ac0b02280fa582b5080d68db0899c62a80375e0e5413a74970c5e518b6986 + # via h2 +httpcore==1.0.9 \ + --hash=sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55 \ + --hash=sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8 + # via + # httpx + # httpx-ws +httpcore2==2.13.0 \ + --hash=sha256:2adc8be4fb285fbcd6d894298db3b52c177e74b6674eda3a76bd36be3292a3db \ + --hash=sha256:35ae5be347aa40467b4a5dc032ac67ebb6d27189fc97e8cebcf99616f6a1bb9e + # via httpx2 +httptools==0.8.0 \ + --hash=sha256:0770728beb05094c809b98e814edff5fef69d26ad7d21185f2f6d5884a0ba683 \ + --hash=sha256:0ea897f0c729581ebf72131a438a7932d9b14efef72d75ada966700cac3caaeb \ + --hash=sha256:159e9ab5f701ccd42e555a12f1ad8ff69702910fc1c996cf2bb66e5fcb7a231b \ + --hash=sha256:19d1ee275bb59ba2643ba9a3a1e51cc0c788caf2b8df506368e03f56fdd08527 \ + --hash=sha256:20b4aac66ff65f7db06a375808b78f42a94970aa22e826b3cb2b43eb09174124 \ + --hash=sha256:2a021c3a8e65cc125390d72f59b968afca3bdcaff25bd67965e0a055a14946ca \ + --hash=sha256:2c032fa028f46871ec7e1fc59fc15e8023eab3e6bbe6ece786a1611719a5d081 \ + --hash=sha256:2d689918c15a013c65ef52d9fd495d766893ab831a2c8d89f2ac5940a5df847c \ + --hash=sha256:384c17174464c8e873398b7af24f0b1f44d992c820328413951a625323155d77 \ + --hash=sha256:425f83884fd6343828d8c565f046cb72b6d19063f6924093e11bcd8e1548cd09 \ + --hash=sha256:48774d39cbb70e2b1f71f88852a3087ae1d3a1eb80482bb48c13067ab080c14f \ + --hash=sha256:52dd695b865fe96d9d2b16b64a895f3f57bf3cb064e8383cd3b5713a069e8085 \ + --hash=sha256:57278e6fa0424c42a8a3e454828ab4f0aff27b40cddf9679579b98c6dce6a376 \ + --hash=sha256:5931891fb7b441b8a3853cf1b85c82c903defce084dd5f6771ca46e31bf862c5 \ + --hash=sha256:5d7fa4ba7292c1139c0526f0b5aad507c6263c948206ea1b1cbca015c8af1b62 \ + --hash=sha256:5eb911c515b96ee44bbd861e42cbefc488681d450545b1d02127f6136e3a86f5 \ + --hash=sha256:614ceea8ea606848bece2338ac03b3ce5324bcb4be8dc7d377ed708012fa4db8 \ + --hash=sha256:6a43c9dd399758ccc0531acb0a3c4a6c299ee893ee9400e9c893b7bdcfae0681 \ + --hash=sha256:6b2a32f18d97e16e90827d7a819ffa8dbd8cc245fc4e1fa9d1095b54ef4bd999 \ + --hash=sha256:7685df791fad561384bfb139e77fde27a1ffd93134e016f95a0db424ffbf77b1 \ + --hash=sha256:7b71e7d7031928c650e1006e6c03e911bf967f7c69c011d37d541c3e7bf55005 \ + --hash=sha256:880490234c10f70a9830743097e8958d6e4b9f5a0ffc24515023afeef984054d \ + --hash=sha256:88bdd940f2b5d487b4d032c6afa5489a7dc4694410d43de3c38c4fb3af0dc45d \ + --hash=sha256:88eead8ec8680a9f146c655bc88445a325bd7921cfd8194c7337e9467282427d \ + --hash=sha256:9518c406d7b310f05adb1a37f80acabac40504a575d7c0da6d3e365c695ac20d \ + --hash=sha256:9878eb2785ba5eb70631ad269b37976f73d647955e26c91d490eb8a4edfda4ba \ + --hash=sha256:9fc1644f415372cec4f8a5be3a64183737398f10dbb1263602a036427fe75247 \ + --hash=sha256:a1afd7c9fbff0d9f5d489c4ce2768bd09c84a46ddefc7161e6aa82ae35c85745 \ + --hash=sha256:a1b4c8e7a489a0d750d91894e9a8cdc295838f1924c0ca903ae993456fddec07 \ + --hash=sha256:a3b7387147361c3fd47a0bde763c5c91b5b4cd4dc9989b8ece84ff436c99843b \ + --hash=sha256:a6f21e2a3b0067bbe7f67e34cfd16276af556e5e52f4c7503be0cb5f90e905e4 \ + --hash=sha256:b15fc622b0f869d19207c4089a501d9bcc63ca5e071ffdd2f03f922df882dcb2 \ + --hash=sha256:b205e5f5523fa039679da0dfe5a10132b2a4abeae6a86fdd1ddc035f7f836557 \ + --hash=sha256:bbb8caadb2b742d293169d2b458b5c001ef70e3158704aa3d3ef9597624c5d1d \ + --hash=sha256:bf3b6f807c8541503cecfbb8a8dffb385640d0d96102f3d112aa8740f9b7c826 \ + --hash=sha256:c08ffe3e79756e0963cbc8fe410139f38a5884874b6f2e17761bef6563fdcd9b \ + --hash=sha256:c0d726cc107fceb7d45f978483b4b70dd8caa836f5914d3434bb18628eb73813 \ + --hash=sha256:c4a9f1707e4823d54dfec6c33fa3697d302aed536ed352a7ebb5a061ddb869d0 \ + --hash=sha256:cd96f29b4bab1d42fa6e3d008711c75e0f79e94e06827330160e3a304227f150 \ + --hash=sha256:d76ad7b951387e3632c8716a9bb03ac5b45c5f16119aa409db0459520887944e \ + --hash=sha256:da684f2e1aa2ee9bdcb083f3f3a68c5956750b375bc5df864d3a5f0c42a40b77 \ + --hash=sha256:de1ed58a974e75d56560acc7e7fed01a454994429456f65209789992e41f2568 \ + --hash=sha256:de242a49b5d18e0a8776e654e9f6bf6d89f3875a5c35b425a0e7ce940feb3fd6 \ + --hash=sha256:df31ef5494f406ab6cf827b7e64a22841c6e2d654100e6a116ea15b46d02d5e8 \ + --hash=sha256:e93c227b595c6926c1acee96891dd9da4be338cfbe82e5cd3bb9d8dd7dc4ac0b \ + --hash=sha256:eb3028cca2fc0a6d720e52ef61d8ebb62fcbfeb1de56874546d858d3f25a26b7 \ + --hash=sha256:ed377e64805bdba4943c82717333f8f8603a13b09aff9cead2717c6c817fb168 \ + --hash=sha256:ef7c3c97f4311c7be57e2986629df89d49cb434dbff78eafcd48c2bff986b15a \ + --hash=sha256:f256d6ce930c52ca1cb2a960b7da03548c454e7d28b06059ad41bfe789036ce0 \ + --hash=sha256:fe2a4c95aeba2209434e7b31172da572846cae8ca0bf1e7013e61b99fbbf5e72 + # via uvicorn +httpx==0.28.1 \ + --hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \ + --hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # datasets + # daytona + # fastapi + # fastapi-cloud-cli + # fastmcp-slim + # gradio + # gradio-client + # harbor + # httpx-ws + # huggingface-hub + # litellm + # mcp + # model-hosting-container-standards + # openai + # postgrest + # safehttpx + # storage3 + # supabase + # supabase-auth + # supabase-functions +httpx-sse==0.4.3 \ + --hash=sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc \ + --hash=sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d + # via mcp +httpx-ws==0.9.0 \ + --hash=sha256:71640d2fb1bf9a225775015b33cd755cfd4c5f7e21c885192fe3adc4c387b248 \ + --hash=sha256:797373326f70eec1ae96f6e43ae9f12002fd7d73aee139a4985eaab964338a08 + # via daytona +httpx2==2.13.0 \ + --hash=sha256:81bd07dc67a3701729ef1f777a3c00c915d4539604fdb5afd327f8682f6b7b44 \ + --hash=sha256:fc12720cedf72faa26cca6b4ca394e05c894e7d7933fc45cafe767960804e49a + # via anthropic +huggingface-hub==1.24.0 \ + --hash=sha256:18431ff4daae0749aa9ba102fc952e314c98e1d30ebdec5319d85ca0a83e1ae5 \ + --hash=sha256:6ed4120a84a6beec900640aa7e346bd766a6b7341e41526fef5dc8bd81fb7d59 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # accelerate + # datasets + # gradio + # gradio-client + # kernels + # peft + # tokenizers + # trackio + # transformers +humming-kernels==0.1.10 \ + --hash=sha256:4ded0998ff085afeddde70baf93f97c2929969ec3d4a63a52cfec5072bc972b4 \ + --hash=sha256:da3e46fb9fc9eba2a9327c2e8135ead68e390c955acd7449f97ee7c71666c8b1 + # via vllm +hyperframe==6.1.0 \ + --hash=sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5 \ + --hash=sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08 + # via h2 +idna==3.19 \ + --hash=sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15 \ + --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4 + # via + # anyio + # email-validator + # httpx + # httpx2 + # requests + # yarl +ijson==3.5.1 \ + --hash=sha256:05eba5268a38809ba1c3dbfa44ea67336e2c353fc11768acc9c6442fe0ccac50 \ + --hash=sha256:0663f718c6123899c6bfd9c449ec195cd8c67666b7ea2c7b36fa0cc0dcb13e17 \ + --hash=sha256:077b1b0bcb6a622d460c6674fe6647c7af5a3b06503e1996d1efcf9f78c94512 \ + --hash=sha256:0a682954b60fcd0c23d504df6fb1ebde051305e41c9b350f39a3b8bfb168def7 \ + --hash=sha256:0ade373dd765b057b1dec05d7711bfeb5a36f1e825259466d9f545cfd8ef3ba3 \ + --hash=sha256:0b184180d45f85fd4479659582749b109e49f4a29c21ac700ccc9c2280fe015e \ + --hash=sha256:0d7c5025a820f36f3e0e64f4b0232b338c690664c12b497e205cf64dcc64fc12 \ + --hash=sha256:11c1d7d36a13054b5872ecd5d745dc4009d9abdbcba2312de69e66c2f92a46d2 \ + --hash=sha256:12aa7fcf46f0fdc8e9e7cf37541e1dc20ac3f9243a23f4d346ab5395f72b0fe2 \ + --hash=sha256:1321495807dcdaca002cb45f24033208ce1d9f5ffc0c5a5584c5f466d0dcbbd5 \ + --hash=sha256:1356bca96d015948b601b013defb2d5631e4330e8f5880e4d7c933d472a90c34 \ + --hash=sha256:170cc4c209f57decc9b7ee5fd340f2a1602d54020fa222846482ff1c99e88fdc \ + --hash=sha256:1a38d503ce343952e88edfd9a27296a4ec96af7073a9db58b3df6233367f75fc \ + --hash=sha256:1a680122d0c384381f26ef3b89bdda0154f47c2571eb6e503571630aa2bb143d \ + --hash=sha256:1be3a586c8821ecab9ea8b256f39305c8a0cc33222fe393bcc1fb9221470732b \ + --hash=sha256:1de3de278b0ffb40338374ad2a730e1c56f933e0706b1815ebeb07b82239b1a3 \ + --hash=sha256:21e1a250b254edba2f0dd7272a4c56f0a879aabe328d9e306dd1fc115f560e74 \ + --hash=sha256:2699e838099d056818c5f8e4ba702b345d0304e58847bdc79c5c1616d5d750a5 \ + --hash=sha256:292648aa123904d4b40ae50cac21840123b8c2cf36a2c1d0620859581ceecdd2 \ + --hash=sha256:29eb8f0c77a296a10843a1714ad4a5d561e604cda3c88585e9012cf2c1729b0a \ + --hash=sha256:2aa9d0cf21d4de89fb633e5ec27e9ad02c3f9a4ffa3940d120b23b8aed3acffc \ + --hash=sha256:2f41982c73896acab4a2a14faa14e152e444bd69f37c3139204429fd3fe65a10 \ + --hash=sha256:3060b141ef758be3742315d44476109460c265b88247e3a4e479949f8b134eac \ + --hash=sha256:322c783f3ee0c6b383bbd4db88370b10172168808cc2a0bf811f1253f7435602 \ + --hash=sha256:32f64051be2f990d8ae7b614b5abdf4a7bead510ce3666568d7403c6c46ce4d8 \ + --hash=sha256:3321fede2b638d400de0036889a3a25c3bb689feb8df45e70a393346aad6194f \ + --hash=sha256:350caea815e53151994b597abc80cf669454276b5ac6aadcec69ef6d48f7e90b \ + --hash=sha256:3ab6378d9c19f01f206f27f762837ad3979330cabd7864e1b17934c03de6056c \ + --hash=sha256:3c0556d628443d3e871f414855313b2ae6cd9faa0104de3316bd8db03aab1589 \ + --hash=sha256:40ddd236c80a667dd6a1f6b625d18ddac68b8719ff795761b7542f2e1f78e4a4 \ + --hash=sha256:42bfda7858d99ee9777ec28cb6d347928249eefeb577f9b0a67503c18f7ebb6a \ + --hash=sha256:451901c36e12fa87cbb1cafe661bd25c08c6bd7900cc738279614f71cea07048 \ + --hash=sha256:4b75b6bf4b0dbb0df24947db6722cd5723ce8d6e6b13fddbfc98db312ba82237 \ + --hash=sha256:4e99de6fd49b44a05eeaadc857e443a9235c2a2057c4e66809e8b2dced31d2a4 \ + --hash=sha256:534a6c1a9da92a3755bfa6a1024995e840335ad5994c8f2d1f38623ba54ede4f \ + --hash=sha256:539e8d6cca079bcbb68c390e55148f908e0a943a34f7dd321248637c6272adca \ + --hash=sha256:65974568748678165d7e90e3e7ce2f7c233cfe4de6c37fbb0760941c97e14632 \ + --hash=sha256:69b5eef70240e9734c5a2fb5cc3742cae411fc833a66b9a50722b9eedb1e27de \ + --hash=sha256:69d5b74760cb50588e21bfab710a16d89e5b2f0a8fbd9594ad750fd7773a0a7f \ + --hash=sha256:6d581a071dae8dbee61f8d962e892787707bad6e641e2f6fb30dd89d3e896939 \ + --hash=sha256:6ee1e6d59c800aa819952f6cb5ff08707ecd576b29cc9c3d00e33c2b371a92ce \ + --hash=sha256:70542d4542f079c394e525559188d69e3ccfbfd9bab899acd0bf1dbc7323ddd5 \ + --hash=sha256:77b68e91f95fb16ac2e7819903cd545db6cffa308c28833cc34911e6b21e91dd \ + --hash=sha256:85997568d6b304cfa59d5c3f2b04f95b92e9a8c7f57d312343a7989cf8dfff85 \ + --hash=sha256:882bc0bdd25d41eae90a15695cd50707edde0978b8b72a2532e30442dd8fd04c \ + --hash=sha256:8b4ed62287feee41b90b55ae2800ef56d6bdfd2fbfa02b4fd0634cd4524bc995 \ + --hash=sha256:8cb5db5bc122da64efb24ce358752d5e097ab41d224ce2992536a0f9073fe4fd \ + --hash=sha256:904e8cf9ca69f5de5b6bb405a4a075ce3da3413ad50c11f6813f1201e14a8e45 \ + --hash=sha256:936f28671f018f8ac4d3f003ae9fa01d0467ab4ef4cfd0c97f23beda485b61c6 \ + --hash=sha256:94a95065b1ac67602af0cec852b07505abc37b77e3774d1c801d935d05e48f82 \ + --hash=sha256:94def0c5f9997bdc6c2f923c9fdd15e400c901979156bea3c255622db7a43f8d \ + --hash=sha256:9708c0a3d1f86056049de631933aef8ec57f2008d4cb55ce241790c7ed557428 \ + --hash=sha256:9a0b25c750a6bde14a0b31f1dcbfc86368e50767e3eaa73bb138e54128055edd \ + --hash=sha256:9c077fad5420f52cfdc906a7dffa622cb9d55c21f3bf0b4e756c6354d800598d \ + --hash=sha256:9f8c4c673d00115ced7422b6e67ae5e6ffc46ae53195877fd66932a6197decae \ + --hash=sha256:9fac9284d62c4317d541274e15a6a6ab6f6d22561579f6570967e3a6eaafaebc \ + --hash=sha256:a19413a092d458a57aaa574fec08e265851d3b5c6e018377f426cd5e70b91280 \ + --hash=sha256:a889228d3c287ef273c7b55177395de64abcf4950b637744dee928685bbb5760 \ + --hash=sha256:a96066d8c12a18ce2fa90579f2bbf991377cb71725874932e4a5d855226c162a \ + --hash=sha256:a96ab35d7ce2129dfde49c4c807596443410e260d7f7a4ca8fe4d0035553b589 \ + --hash=sha256:aa7a2c94e43c02e0482088e6ff997e2bd7b9a76e6f1d0fd70891b4b5ff51318f \ + --hash=sha256:abd724af41688035719b9f39a926876b9810808947421999b2dc6db34944a4e6 \ + --hash=sha256:af40bd1a85f55db0b8b30715c858761306bd92d5590148636f75c3309e6e76bd \ + --hash=sha256:af6ddbd10ac9bce87a835f2de3ec61455ec435c54e7e0ba7b17c31c66de6f164 \ + --hash=sha256:affb85eb75fa03a21d1f790bbf26a0e66e5701672062a30dc5c3c6a29c5c0a63 \ + --hash=sha256:b70b5da6b0571da8f601a437c4fba2d35bc27739637d85f3acdc8f88916ce68e \ + --hash=sha256:b9517efbe6604bce16f3e50d49b0cd1bdc58917f98cf2eab026599c5c0422991 \ + --hash=sha256:bad5d55c99c89de8cd0a4cded51f86427ba3353c4dccca37ec2e32e06f26b437 \ + --hash=sha256:bc0ed6a336d11b9311171eebd7a8467077291bc61b03de89ae7249bba5fa70ce \ + --hash=sha256:bc16d618a0a8f7a78735acd14628fd9f66bd4dbe80db3c522a51bee3200eb720 \ + --hash=sha256:bd756f7b22df745ac14b7bc2ab9ed7c190a222e4c8e1bef26ef1162af8e54d0f \ + --hash=sha256:c2b83b24be73f0c7a301807a4c3081939524421c7ae1556eb6eac7cff50ddfa7 \ + --hash=sha256:c2e2509dc7f2fa5a2ac9ba7d15dd901f4093bd36b0784f65e04b681b7956651c \ + --hash=sha256:c388f85cbb9eec022b2bdedd23ffacfe7ab100c1200b1f47bee6e6ea2c3309fa \ + --hash=sha256:c4b9a28e9719d1aebebe93ad8dc2ba87f4e2d9035043b196c1c07ef8530b44cc \ + --hash=sha256:c8a36a19b92cb7172c6448ab94f446033cfa3129dc4894aebe205f96b3fabf42 \ + --hash=sha256:cae04eff4006fc36bf0b030b38e2646a97092d87d933d20cfe7262e26ed32321 \ + --hash=sha256:cd0dfc5a788d0b0c2f1eab258b9dabdeefc631ca8ef87644a999f633b0b2555a \ + --hash=sha256:d78f362f51c8691798758a9e6ac3c9d385ee1228cb82987c91562a2fae235cd3 \ + --hash=sha256:e01f95433725e2df62d682ff88e4a57bb694385ff2362bc364adec961167ae04 \ + --hash=sha256:e035cdfb2a1446b13881f0dfc0eecd1541cbb17a27a938ded2160ae6ce25051b \ + --hash=sha256:e2ac204b59f09e38e16d277f906240e9fd38780e42076599419265af183dc4b4 \ + --hash=sha256:e353891d33a2e6aa5caf72c2a5fbadd7a46f5f9b32dcfd0c84113b2444c255b8 \ + --hash=sha256:e3c5f660658f2ebfba5d4dfe4bafe8cd3a0defcda410ec08d2205fe08c398940 \ + --hash=sha256:e4fcebfe1685bb7ba06a8255a5d428ea6b4b895d7acf979cb637d8bbc9db2f47 \ + --hash=sha256:e6cf9e49902f28af7a2e2f8b35c201195c0f0d5c170a5786e0c0a1b8492a4e37 \ + --hash=sha256:e8dbf71b21e65cb7f0d4d387c07fe73be820168070c3be05a0763a80f424f1c7 \ + --hash=sha256:ea4fd7bec203a600b1cc88a492dfe6b75ce4b1b87488a66adcd5406022213f64 \ + --hash=sha256:ee60c7741012671867678eae71c51872cac938b76f3d4ca40a778e6c361774d2 \ + --hash=sha256:eeb2fb2daa5dd30326f93db465d0855b34aa6b1f52a7c0ff94522aec5ad57dfb \ + --hash=sha256:ffba9bce60be21b496afc67a05ab8e3f431f87f0282fd6ce3c62004c951a1428 + # via vllm +importlib-metadata==8.9.0 \ + --hash=sha256:58850626cef4bd2df100378b0f2aea9724a7b92f10770d547725b047078f99ee \ + --hash=sha256:e0f761b6ea91ced3b0844c14c9d955224d538105921f8e6754c00f6ca79fba7f + # via litellm +interegular==0.3.3 \ + --hash=sha256:b0c07007d48c89d6d19f7204972d369b2a77222722e126b6aa63aa721dc3b19c \ + --hash=sha256:d9b697b21b34884711399ba0f0376914b81899ce670032486d0d048344a76600 + # via lm-format-enforcer +jaraco-classes==3.4.0 \ + --hash=sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd \ + --hash=sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790 + # via keyring +jaraco-context==6.1.2 \ + --hash=sha256:bf8150b79a2d5d91ae48629d8b427a8f7ba0e1097dd6202a9059f29a36379535 \ + --hash=sha256:f1a6c9d391e661cc5b8d39861ff077a7dc24dc23833ccee564b234b81c82dfe3 + # via keyring +jaraco-functools==4.6.0 \ + --hash=sha256:880c577ec9720b3a052d5bc611fb9f2269b3d87902ef42440df443b88e443280 \ + --hash=sha256:99e3dc0060c5cbe8fcd1cdb36258e2a65ca40f1566b2033b12abb1bb44dd3c30 + # via keyring +jeepney==0.9.0 \ + --hash=sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683 \ + --hash=sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732 + # via + # keyring + # secretstorage +jinja2==3.1.6 \ + --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \ + --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # fastapi + # gradio + # harbor + # humming-kernels + # litellm + # torch +jiter==0.17.0 \ + --hash=sha256:00b5a98df3e3a3e8cf7b619f4ac2f8bf975bbf3d95d02c5d17b8dbfe5c8b8245 \ + --hash=sha256:00d783a779c5664e16dbad5e3a3c3a75e128b07dd5f4765159658d9210a50ca5 \ + --hash=sha256:0239520085cac678e77a606fd7e3f1c60c371d719790c5e3807388d3da4354c2 \ + --hash=sha256:02a360707033d8cef53f7f3480817a1489177a259ec6ec01e98c37e0b922ddca \ + --hash=sha256:02adebb7ce6413c44d40af9ad59d1c1cd79630ccdcb6f7bdd2d461e48c03d8f9 \ + --hash=sha256:03e432f226a453851079fb84cd17c6da9991eab723e28d716f14ae3d906e0c12 \ + --hash=sha256:0619d806e260ecf0c2a64521942c94af5d547c9ec99b55ae4f51b538b5576a76 \ + --hash=sha256:073dc68c1a700c8fc480e877864a6b6ffc887533e261f4380c08c16bf09d057a \ + --hash=sha256:0b52d52035b3907c5b1f6277857b29c1cbfc965e24e0f27330dbed83edb591ec \ + --hash=sha256:10c5349312e5cb02b7a21e123a57665afa895953f05bf252a9dd4c13a572b7ab \ + --hash=sha256:10cd64a5720ad7f809ac5466ff1705813f1b6b510f195a73acafba0ac0e1f675 \ + --hash=sha256:10f5558eed511b830488003449d942bd75829ad6257dc58cb9a03e596a7777b1 \ + --hash=sha256:11902505d401691720f5785c15b02204248526edee11b635cd6c40cd52b81599 \ + --hash=sha256:155be7355bdb7ca76ab0961be8982c225f964a5c073a83984183f22391cc29fc \ + --hash=sha256:16dd0c1baf098ae70b8f3616574eb3fedf34e26670b89e16a7e67561f737ed2d \ + --hash=sha256:1b18434638228c0c184281609bf3d9459026a0f1ea48fb76c205e3ef72069caa \ + --hash=sha256:29f49b325e0234e4ad9ecca5b861ffbd09b95ccac9bd46fa55841b6e56eea5fe \ + --hash=sha256:2c45ad7c973ef33fe5114a953377b35a95240f4542c0724d9f781e47dc24bac7 \ + --hash=sha256:300ce01ab0215e3dea4d00090143c909aedc65c0f809b3c07983e1d038f291b9 \ + --hash=sha256:30793a24a31e968969757c9e08d830cbb15a2cd3c4959b4498b38f4b1c2258eb \ + --hash=sha256:30c692d567ba206c7cca38c9d1d0ccc70c9786290173c184d871ca12e9981ed7 \ + --hash=sha256:32aaaa764604496610a3ad2d98503ae88ccb2fbe769e892ff4533e778e85f708 \ + --hash=sha256:362bb47423886d45a9f705d2d9d4008c6eedd4e41eb1bab4e96fb6daa06b33fd \ + --hash=sha256:36ee6e69027396664e59995b9a635a947a5304ee9837279584a0bb8145c8f6b8 \ + --hash=sha256:370d8fe5bf201dc6925e8a84c81ac7291f74d9fd1778234fc79d517064a5c76b \ + --hash=sha256:37150a9e02e869475854fa20b7d0d5e26d18d0f8bc17293999973ff27e99ae7a \ + --hash=sha256:37f33d327900bf2879613b3363fd48df97b4232d0c41f54bcf2e790c2fc40a71 \ + --hash=sha256:3ad556afc289f15d2b181b941982d01f06190863c07440185b9f354e1bd2def3 \ + --hash=sha256:3bf4dc2b84a464117fb097d15a25c58d100d2692888e3b0d92df5b48ed16b7c0 \ + --hash=sha256:3c1a5336c04a41b1f1cf9572e294aec27cc569767ff73de7bf87a91f0bea7cb9 \ + --hash=sha256:3e05f5adbf68c4bd11e1610f394034d984152988e84be6f8314235ce6f2139e5 \ + --hash=sha256:40d2c240f8f80b5b0f201b29f0ae129c81448c60c772227a41747b5e0026f6a2 \ + --hash=sha256:42b0260445251b1bc520a63baa94a32d88e0f931fba234f1764db7feb7c72174 \ + --hash=sha256:454c4997d73cc466c71fd565d91e603b0274e48ea0c6b0b7a7aee6967e4ceb7c \ + --hash=sha256:455e4ab35cb2a4a91a8404e08fd3c621bae433922e59bf1c494fe20a426b013b \ + --hash=sha256:4607ec7d93355fbc25b8dc5189153cf21d66063b9f9cd04dd2774e6e783f9b6a \ + --hash=sha256:470e1b1e4c42f1ead2189166a299691871a2df5056c976e7fb96feafaf5f9d44 \ + --hash=sha256:492f37230bbf9581ab2c17bcda862c249afb9ae2e3ab2dd6db59943bc4cc3153 \ + --hash=sha256:4dfbfe5a6e1e80a7082af559f66386405025ec278833e0c649f69cbc6e1004cc \ + --hash=sha256:4e3f052c671d5f425cca5ea5901cf11a831369fba4a55a3862cab93c323b4c3b \ + --hash=sha256:5078ab00664307fab2019b522a93aeb191122789f085daf5fd9e362154021d4a \ + --hash=sha256:51e1519d676a9f14dad9c2a411170d43b022ddb7989562df4e849b261ce127b2 \ + --hash=sha256:523c499235fb65add25d4bb01b1c4709ce695efdc7deb6c0a7bc515b5c44e0fb \ + --hash=sha256:545c36a0f3b2238c242cc9785439d3242a871b7bc39fe3f441bcaa07bf3aa83e \ + --hash=sha256:55d0e0e613a3f9ad600cf436e0e2b8057d1b52bcf1d91b2d36ac53451231e6a8 \ + --hash=sha256:5888fe5abc1ca2fa834a3e1b4c7ef0dcece286a7d7e95a609ef0934b777b9fc9 \ + --hash=sha256:58df29268a95e910f17db7ec9178eb7f15aa8619aaca3575275c4e6b3f4fe4c5 \ + --hash=sha256:59bddbe6f9ffecc68d641e1e2d619ce64cf8a9e9eeb74e5c518f74fc87abf1b0 \ + --hash=sha256:5a52a430d04225ffde633e6840bf2381d34c019ff98526b5929755b9052fb199 \ + --hash=sha256:5bf350452a43173e69e1fc74847c57a60e3d7515807287f29849baa2a85d8718 \ + --hash=sha256:5c23849235d2142ce444b2b8c6eceee9f82f4cc0bd5c9081602e4155c6197807 \ + --hash=sha256:61aed66ee042b3b49ef85fdf75714234d055d89d8496ac1c6e47f89e7a30d5e4 \ + --hash=sha256:6219adaf59711ba7063a52496e8ec6d3fa3e209d7827d83eee3b2abc780a1744 \ + --hash=sha256:64846211a2debe7c071d2146d2283d2b0c1c93dc8fd5fb7794faac2ca6061b5c \ + --hash=sha256:686c93d86f2b426c803024b805bd161a6cd10e9627c23e901640eab646c0ad8a \ + --hash=sha256:6871973bfbd4408f7f1c632b30bbb5bbd9671c1bc8650af6823e24b7be13709b \ + --hash=sha256:6af5b74073bd25bae695e6d00919f6a9be7ed5a9f8836d981eb1ffe84139e6fb \ + --hash=sha256:6b303d88e6a0bda789ec4b7801c7bad68e27230ba1fe4baffc756d1fbd32dc9d \ + --hash=sha256:6cb41cd1432f1dc19a231cf70b54d42b2c9f05085155859263fce06fa4d41388 \ + --hash=sha256:6cf564d43c4388149ca58ee571d0f5ccf875e20d1fd4662fd94cc0d1ea3b10ef \ + --hash=sha256:6eb6aedeb7352b8f3b6af9cbd67983840165c00428e63f1b420a85885128ea31 \ + --hash=sha256:70f19a2ca8429f91e82eeffb2f51cb87bc2d6e953b009b91a92d29c3a16ccb03 \ + --hash=sha256:71dbd74314c5df52a1bccf7b8bca46d14e943af7a2012e73b23f49977ef194c8 \ + --hash=sha256:73b64e69c4150748e020356d958af94bec33c70a0a93d665cfa8f6d580fe1a63 \ + --hash=sha256:746243a080b4ca790b8499af3d7cf9825d5f5987933950cd818e767ee353d826 \ + --hash=sha256:755079792868ce5d4938e83b91a0939b34fb858a1ca65a104f2d771bea57faa1 \ + --hash=sha256:7573e80232c5bcf80c24c038cf7e53a463f5c3b1dd1dd4109d66304f4dccc233 \ + --hash=sha256:76eb4a5c20e86f9f848286f167024890f2862258a965d254774deb7fc1545ca1 \ + --hash=sha256:77f6aac0137309b31448c1bdcda4c6c77077664a6d018ece8d94019c68a5a5b9 \ + --hash=sha256:785a216bbaf8f15fc974e964ced7322cd3d774bb0e86949edd78c6bffd6ba35b \ + --hash=sha256:7b68d3495d95da120651a5628c7ebadee84ed001a1b76e6afc325c42482f15b5 \ + --hash=sha256:8079849db9a1371bfd90bad088458a8fb836261879df2233cc9632464ecf64e1 \ + --hash=sha256:81c83c0abe614446a283d994d2c07c4f58632dea2cdf66ba9e2921bb8ccd593e \ + --hash=sha256:826871c42cebaae22f0a2b5673a4a1a75c851bb2d13b3c17764a630a6b298984 \ + --hash=sha256:84963d3f395ef5e9a32ce47155e08a7962fa292c159a10cb98b931cef1416925 \ + --hash=sha256:84ac78df457e1ee3f7e733bd114823302ae8c5ad5542d7e6647d92ffaa090a04 \ + --hash=sha256:86d703d9faa1ffc8ae4e9de0fa007712ed2171b5c0d93811a8e2e105ac729b0d \ + --hash=sha256:86f3f9343a288eb85a81ef20a752b2f84564296636db54a9fff0b5c8deaf1df2 \ + --hash=sha256:8adca2e793288e5f1bb29279bb439d0d3cfbb50eddca7e7e6ffd42ff4f482406 \ + --hash=sha256:8c21265b251d99bbb40080d178a8953e35601d3a1564e05c4de4c0d2ca616797 \ + --hash=sha256:8c286860abfe8b100cac1c02e225e5776eb9216edd71ba17cdb237da4af32bc9 \ + --hash=sha256:8f770b0c77e5fac482e1ba03ca1a7e18286bfb213d749932a00a7e4cd5de5e06 \ + --hash=sha256:93946d89fa04d5ba64dd323a8dd8d901676cb8a3c81d99ae4f6c051a9b4c3f2f \ + --hash=sha256:96b8b0c6dc5d78682f54a450785e075aa929cde768304cad363cd4efba5a82ac \ + --hash=sha256:9bd3caac219df476dd0cc3fe01d2f1581ed588906feac767abd9614c1c12f8b3 \ + --hash=sha256:a277f97eba7d66b1ee27eb5dab5b774ff46a10c78d89a1d3dcce04ce1357c8ca \ + --hash=sha256:a3cebb1fe4a1abb00465f3f8a17e09112603e8b7c59e5c3adbcd9f7815a64acd \ + --hash=sha256:ac3c6ee3264d6f5c44c617f90bc7e8b9e1587e7d6708c9d8f811cb65582ee312 \ + --hash=sha256:af2f7501580f274b63c4b2283bc425f5df7edf06ae5b171e5f87d912ff359a20 \ + --hash=sha256:b550585523339b71cb852b811aae49d08d7601ad8ffe9f5dc1562f4c3d22fd87 \ + --hash=sha256:b75f85660108965a94be77911a25a253429307294d9415b3c597118977a614de \ + --hash=sha256:b847b18d066c46b3b7ae49d6c94a7634c5e4a8983146ee25562a092000f5e3ad \ + --hash=sha256:bcc064f99183a9cbe7f26ed648c352031a74145cd61ed75d34632c73eb46a5a8 \ + --hash=sha256:c19b9357309b8cc6de8a48fca8e44a8c9c2feaaa2f5896d037fa505d48fcab80 \ + --hash=sha256:c4289293e5278d9314b00f15c37f2120fa51d3d68565292e715524c750e775a9 \ + --hash=sha256:cfafd7be8b16ceadd298db542cead37cddc211c4c49e04ad2596924df18625b1 \ + --hash=sha256:d0ce4feb52493e3513335b2accdcd75605652e4632772d3c8c2f7b86954d7f39 \ + --hash=sha256:d2c0bf24c72fd0491405dce5d40194f2070e9021ce648c1a1d46234b93d848ff \ + --hash=sha256:d47687806f9c54c84ea38733507081337922beca90ce819c7d852dd485bc0f23 \ + --hash=sha256:d85c558c9f8532bba287a990ac63767c7daf756f0d8c030219f62499b1fa228a \ + --hash=sha256:da139721f4b7cafdbff580a4f511ea24cb91f4909330c6b926a1ca53836c0a59 \ + --hash=sha256:dbbfe4e3c21c8166980cddc5bee1a315df082454f007947dfb6fb73800768165 \ + --hash=sha256:dc0288ce39190ee33fe6e4ec73161eed34e7e2da509b525546ca061778d62b64 \ + --hash=sha256:e088612ff90ebc9247e1a43074b72835804261c47e6a6c01cb3ddcb55360d688 \ + --hash=sha256:e654b6b04e39c9cb19cb8b04c6ddf1f2db07751fa14156413969fd78bad0e5cb \ + --hash=sha256:eaba834b72d573547b9d966465b3394b749d5e14208cc70acb63aca37619ab33 \ + --hash=sha256:eae86b1f027031e39db2e0e9c4842221edb7b8cd474d23f87a79b3bd4b651768 \ + --hash=sha256:eb2295da7c3769f6719b227a237aa6a5cfa6550e478bc838001b592c57e16575 \ + --hash=sha256:ebf918dfd6a74adc1b9ad71f63c4ab00902fcd3b7fd39f2e24d871db8d713b91 \ + --hash=sha256:ec89771f4272b989487a6364e519db6bbaba323e8bbf949ac89a45ea9c18b7a3 \ + --hash=sha256:ed1a24005daac667d577402d75a2922f9775a165b146b883ff1ad3602d8be689 \ + --hash=sha256:efe9f61bb30174d2f5c8396445c360c96c44e78164d0815dfe627ccf57849574 \ + --hash=sha256:f0bc7f684b65bcda9c20434267577db71bf9905ceddd32b60d1d93278d8c8d3a \ + --hash=sha256:f3d7f7b34114f7ddc6d72a8e882d49de636b35d9fd12b4d420d3c5729f6c9812 \ + --hash=sha256:f753eb70b1474a29e635e7542ff7312e6d6b951e0b25e8a2e8c34eeb1ddcd478 \ + --hash=sha256:fa13acf1046f95df808c64b1310705e143fab87aee73ae00cc42d640867fd2c1 \ + --hash=sha256:fd7790aa79c8b518e512ebcdfce9f11d8ef5f30efd43720c8a19a548b39fa489 \ + --hash=sha256:fe15ddf316f1f1f643347d3a474e74ce61880c79a11ec5dca53df20c071bd3e8 \ + --hash=sha256:ffa0380ad091de7d3fc33e17a97ff479851ee18a0a2a3ee56ff3215cdc886656 + # via + # anthropic + # openai +jmespath==1.1.0 \ + --hash=sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d \ + --hash=sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64 + # via + # boto3 + # botocore + # model-hosting-container-standards +joserfc==1.7.5 \ + --hash=sha256:add2c2c84e8373b084d526a8b53daba5d7a513a118cd2dcd9fc9f979d0922159 \ + --hash=sha256:d5ff536e658e17664f8c1b1ab60dc4aa62aa973fcef1edd33cc44bda45d6f5ea + # via + # authlib + # fastmcp-slim +jsonref==1.1.0 \ + --hash=sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552 \ + --hash=sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9 + # via fastmcp-slim +jsonschema==4.26.0 \ + --hash=sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326 \ + --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce + # via + # litellm + # mcp + # mistral-common + # vllm +jsonschema-path==0.5.0 \ + --hash=sha256:2790a070bc7abb08ea3dbe4d340ece4efadf639223001f020c7503229ba068e2 \ + --hash=sha256:493b156ba895c97602655b620a8456caa2ce08c1aa389f5a7addec065e6e855c + # via fastmcp-slim +jsonschema-specifications==2025.9.1 \ + --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ + --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d + # via jsonschema +kernels==0.15.2 \ + --hash=sha256:2a66497d94152896b74c841089e3ee685708b8ddc21d4f4751a0505a0b693c91 \ + --hash=sha256:5af4fab4a2d4eb67f01522dffabfdc3a4bcfc167ce68f7549c0d2828a73cbc19 + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +kernels-data==0.16.2 \ + --hash=sha256:0804a8a26892139645f83d3edd38e7c1481dc138319af0596ef2a11f9ab3277d \ + --hash=sha256:13843175dbb99ea62526a559c4ad93e0f9f6c2b03441e6209f4796d7f46c7235 \ + --hash=sha256:13e83df253509e4907375b9810b0759c5421b0ccf312fa0940b1860cf0fa2b31 \ + --hash=sha256:155e533c43a53866f4b91e553ee5de19b58a480aaf27189ca878f5b9622e4dfc \ + --hash=sha256:193e7ba92fede87bb55b5dace305b24998c453085443a03e448ee71b12aaa7f5 \ + --hash=sha256:25252b34728b50104d8142ba5b43676bcea4c6277d987c3d49cfdc59f96f2e27 \ + --hash=sha256:32ae8ca6bf6124a73f287e1301dce7ef94adce666fc8a8cad03597e3fe4527c6 \ + --hash=sha256:3c8839dd192a0dcdd4454c65ce15194b83c67c9f0eda15509569d512feb4ff86 \ + --hash=sha256:3fda2aa837823263c63183ff9cf8351f0be2a9bc935aad243932f8f15ac1105d \ + --hash=sha256:670f5934830da17679e5f2f3776599629de0828252199b01b2954aa10e90baf3 \ + --hash=sha256:6db1edbac94160d93d73c2c259e4952721dd547c4e7fd0ac2b9a0995a9a39650 \ + --hash=sha256:7f2182f00afa7ff9710a76f400a70b9e89607d25a3721e965f12c060a3e1f5a9 \ + --hash=sha256:830a109ad5501040682ad630976186ec7a2c3c6b110950401e29b19e0c19790d \ + --hash=sha256:9526362517c7b128f7baec080301890875cc3f5284a0f125b082c19737e5c1d5 \ + --hash=sha256:955c6dfb065b11c311111e0fd59c91b8ce2c32a6fe78fe6dcfab856fd60d4061 \ + --hash=sha256:9d1bfa6333ebca5bfc31cd53e26e1d368f7a33fd999f7a61a968a44838a2df13 \ + --hash=sha256:a08bf4eb36ae81704fdd851e77c5add8fd069c1f724f19e0d137f2638fb718f8 \ + --hash=sha256:a938ec83f4b178d2519cf219a554f67b789e7a3b8889de92e586736b6741e711 \ + --hash=sha256:afb953a34b34f6b958fc2fc90b3a3f6008c2f74b810775d250e8f6b9c9fcca93 \ + --hash=sha256:bf925dbd1046515e0eeeee310a2d1f516304ad6c1e4cbf37e50edad064e1bddd \ + --hash=sha256:c45c88b8c49b8d8130615b5ea78b51621b5c5c94ea47c902d6002750abe6108a \ + --hash=sha256:c65bef1cfd2073c9ae504926a109f17641ee2759035dedcd601db59c29dcf5a4 \ + --hash=sha256:cceb236e4123573653a679b134038496daf86c5375168c6798c954f7cad4a06f \ + --hash=sha256:d265b052ed19d9aeacbc725f918f3ec9f6a882c68ee346e2c75ea5d690ac11ea \ + --hash=sha256:d50502de4a256fbc8358693856728f34db08113d4c014fddb3631419dce808f0 \ + --hash=sha256:db2dfdcb66588cbb8aad1f47be06d3861b6edcec7f81ee97cbd91e450c8c0412 \ + --hash=sha256:e3597d2644d8464c17918f0350692f3176be510626aa139d090a1d06aeb6a693 \ + --hash=sha256:ec9263534b085f6a840bc1570fbebe656e50af3ba81b5e901ce63e67fb99f9af \ + --hash=sha256:fb4b6f351746a964667fa410e60ae5e1a20f16c2d5cbdec7ca44c0db692eedca \ + --hash=sha256:fb4cadb0d7b0d78049b9c9a02bf6db70ca87368d6208c34e6aa89b917da0d571 + # via kernels +keyring==25.7.0 \ + --hash=sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f \ + --hash=sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b + # via py-key-value-aio +lark==1.2.2 \ + --hash=sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c \ + --hash=sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80 + # via vllm +litellm==1.101.0 \ + --hash=sha256:140ee0997324b8fd2405f7f8c26f42c0f364996dc4030187344a90cf27916667 \ + --hash=sha256:734ab2b8cad6a3b582d52d9c9c5fcab759eb382b93935ef808fda0e16d822ac3 \ + --hash=sha256:7cc623a224c6f11a04367a682b095a1e08e5f6da75c990a650910db5f9777819 \ + --hash=sha256:85d88053148c5c6e016e495273eeaf8f3a29779b60d18977ec42e2a6bbd2d8eb \ + --hash=sha256:ad013161074676fc91b91d828f696300132cf3936d3f386c4701a41710d70aa3 \ + --hash=sha256:cdf3351e394206e785bf339f1a39d9358ddf4e4f38129750bfdb5e4f5ceab8ef \ + --hash=sha256:d4064024151ff2877e542b56c6bb6a39e0c3e6651abf4639af9346a678586e52 \ + --hash=sha256:e5601ae404b0f1e38ce1e65c75f880254485942d9c824453e3926f2323457fd5 + # via harbor +llguidance==1.7.6 \ + --hash=sha256:0444020249cde1292f13acf786e35c245fd3572d466877d2734824a9026e55aa \ + --hash=sha256:0fda51daa7951217ca164f735e96a1929d9aefb804a0b28ee43b16173e1c7325 \ + --hash=sha256:1158cfce353d331859054aad80a5543167da8b45e01c18f93272027a155df449 \ + --hash=sha256:30be5939340f008b5093286f0bbbb9804f58e292ecca5f8b144823d43ff5068b \ + --hash=sha256:4e4f2a489c1c3943bb1b3c206b45794153cb6954f45cd3de8e02198319ddc6b1 \ + --hash=sha256:7def42f7866239b3b940982ed1dcae6b142c212fbd68b57107c1560d778f94f8 \ + --hash=sha256:9c54c899db8cb4b4fba128a7d844730066576c70d806c95ada92b2bd2d6ab498 \ + --hash=sha256:ace7e81cd31950a87186356ab24bd7f75fbc10a05ca9d9f7f8748f931963f763 \ + --hash=sha256:c88787845b94d301d91c4e9ad27eac9d05c334a1ba2c7ff29cca66f26d5b5c3c \ + --hash=sha256:ceec951d29a74309984e3be0fe7f5f56c1362434cd937abd517b259a60908b1e \ + --hash=sha256:d0e1f5402bbc2688bc790d56995f0263978b55771493fceddc09b805dacc83b6 \ + --hash=sha256:db7febbe412ed2015501904646750071d7e00e6df7f85c4b956ad4f206fd2df7 \ + --hash=sha256:e70fa25ed550c2b50c2fd70baa9e2808b4ecb859d01e453bd5459aff62ba38c3 \ + --hash=sha256:e9f68206e0f3f89aceabb90aa1f8ed570db22fb7cb1fd9ebf96fa7727a65af55 \ + --hash=sha256:eabf4572c8731734c0444c353b9ea06bc5c156986d2ff0a4ec0499159271381f \ + --hash=sha256:ef907a562d91f32e13cb3131ee5e1574b9ba5beac5bceedd795f8316a16d94d6 + # via vllm +llvmlite==0.47.0 \ + --hash=sha256:003bcf7fa579e14db59c1a1e113f93ab8a06b56a4be31c7f08264d1d4072d077 \ + --hash=sha256:12a69d4bb05f402f30477e21eeabe81911e7c251cecb192bed82cd83c9db10d8 \ + --hash=sha256:166b896a2262a2039d5fc52df5ee1659bd1ccd081183df7a2fba1b74702dd5ea \ + --hash=sha256:2699a74321189e812d476a43d6d7f652f51811e7b5aad9d9bba842a1c7927acb \ + --hash=sha256:306a265f408c259067257a732c8e159284334018b4083a9e35f67d19792b164f \ + --hash=sha256:41270b0b1310717f717cf6f2a9c68d3c43bd7905c33f003825aebc361d0d1b17 \ + --hash=sha256:5853bf26160857c0c2573415ff4efe01c4c651e59e2c55c2a088740acfee51cd \ + --hash=sha256:62031ce968ec74e95092184d4b0e857e444f8fdff0b8f9213707699570c33ccc \ + --hash=sha256:6842cf6f707ec4be3d985a385ad03f72b2d724439e118fcbe99b2929964f0453 \ + --hash=sha256:694e3c2cdc472ed2bd8bd4555ca002eec4310961dd58ef791d508f57b5cc4c94 \ + --hash=sha256:6c6951e2b29930227963e53ee152441f0e14be92e9d4231852102d986c761e40 \ + --hash=sha256:74090f0dcfd6f24ebbef3f21f11e38111c4d7e6919b54c4416e1e357c3446b07 \ + --hash=sha256:92ec8a169a20b473c1c54d4695e371bde36489fc1efa3688e11e99beba0abf9c \ + --hash=sha256:9ea5cfb04a6ab5b18e46be72b41b015975ba5980c4ddb41f1975b83e19031063 \ + --hash=sha256:a3c6a735d4e1041808434f9d440faa3d78d9b4af2ee64d05a66f351883b6ceec \ + --hash=sha256:c2e9adf8698d813a9a5efb2d4370caf344dbc1e145019851fee6a6f319ba760e \ + --hash=sha256:c37d6eb7aaabfa83ab9c2ff5b5cdb95a5e6830403937b2c588b7490724e05327 \ + --hash=sha256:ca14f02e29134e837982497959a8e2193d6035235de1cb41a9cb2bd6da4eedbb \ + --hash=sha256:d4a7b778a2e144fc64468fb9bf509ac1226c9813a00b4d7afea5d988c4e22fca \ + --hash=sha256:ddbccff2aeaff8670368340a158abefc032fe9b3ccf7d9c496639263d00151aa \ + --hash=sha256:de966c626c35c9dff5ae7bf12db25637738d0df83fc370cf793bc94d43d92d14 \ + --hash=sha256:f3079f25bdc24cd9d27c4b2b5e68f5f60c4fdb7e8ad5ee2b9b006007558f9df7 \ + --hash=sha256:f6725179b89f03b17dabe236ff3422cb8291b4c1bf40af152826dfd34e350ae8 \ + --hash=sha256:f9d118bc1dd7623e0e65ca9ac485ec6dd543c3b77bc9928ddc45ebd34e1e30a7 \ + --hash=sha256:fa1cbd800edd3b20bc141521f7fd45a6185a5b84109aa6855134e81397ffe72b + # via numba +lm-format-enforcer==0.11.3 \ + --hash=sha256:cf586350875def1ae7a8fba84fcbbfc8371424b6c9d05c1fcba70aa233fbf06f \ + --hash=sha256:e68081c108719cce284a9bcc889709b26ffb085a1945b5eba3a12cfa96d528da + # via vllm +loguru==0.7.3 \ + --hash=sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6 \ + --hash=sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c + # via compressed-tensors +markdown-it-py==4.2.0 \ + --hash=sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49 \ + --hash=sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a + # via rich +markupsafe==3.0.3 \ + --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \ + --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \ + --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \ + --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \ + --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \ + --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \ + --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \ + --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \ + --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \ + --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \ + --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \ + --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \ + --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \ + --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \ + --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \ + --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \ + --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \ + --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \ + --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \ + --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \ + --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \ + --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \ + --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \ + --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \ + --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \ + --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \ + --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \ + --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \ + --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \ + --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \ + --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \ + --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \ + --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \ + --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \ + --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \ + --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \ + --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \ + --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \ + --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \ + --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \ + --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \ + --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \ + --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \ + --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \ + --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \ + --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \ + --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \ + --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \ + --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \ + --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \ + --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \ + --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \ + --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \ + --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \ + --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \ + --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \ + --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \ + --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \ + --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \ + --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \ + --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \ + --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \ + --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \ + --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \ + --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \ + --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \ + --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \ + --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \ + --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \ + --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \ + --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \ + --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \ + --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \ + --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \ + --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \ + --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \ + --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \ + --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \ + --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \ + --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \ + --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \ + --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \ + --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \ + --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \ + --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \ + --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \ + --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \ + --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \ + --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50 + # via + # gradio + # jinja2 +mcp==1.28.1 \ + --hash=sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df \ + --hash=sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # fastmcp-slim + # vllm +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via markdown-it-py +mistral-common==1.11.7 \ + --hash=sha256:a9511b88eacacbe7dacddd9d3498c1739f56847b7fdddbd5a22e7844fd9def95 \ + --hash=sha256:d3b79583595cf6d96a2ab33e42cb8449768383147b8c56cac5a4f193be19d20d + # via vllm +ml-dtypes==0.6.0 \ + --hash=sha256:008382aeab529df5d3f00501ad9a7dcd64494d4b5b1971fc4c79019e6c1f5010 \ + --hash=sha256:03ce583adfce34ad33aa9e1fc7a8344dcf90ea776cc4ef0e5a48d4eae84e5d20 \ + --hash=sha256:084dfe51a7ad58b171f05115f8226ed4233a454a1611371947e806e76f0c638d \ + --hash=sha256:26b1f1fa4f0435a2946859823f6e2bf06796f1e9f10f5a05b08a5e3c8f46ff69 \ + --hash=sha256:28d676428b104bb9717b0928bc5c5129f2d6b51b6727587cc4289e7bf8713cb5 \ + --hash=sha256:2a3e9d53925597fbffafd2a37048dadeddd0bdaba58058f6ae0869ed709a184d \ + --hash=sha256:3035518e3e19add1a4cac9236ab22888b208a4074912514313ccb2d6d242cde8 \ + --hash=sha256:317be9967fb84b0ce4e80e6b1bf71213d21971621cf6f1e501a63602a95297bf \ + --hash=sha256:31f1ce979d31a357e95aa81812f20412c8c954fa43c44ee3ead1e1c8a78575ef \ + --hash=sha256:37da32aa97749251025666d62372775019594577b9c9e9cfda83bed48d778fdb \ + --hash=sha256:3b4a480aa8fd54a1805b8ac10f3f91763926a74f73c0c364c10f9231854f4170 \ + --hash=sha256:3be9911d953f97cddded4b9961d7b650473b7e55806d20f6176f8356dfe7b38e \ + --hash=sha256:3e169214e0d80ff1c038e1b3017e33c23e43bdf948d42d31de8283111c7e2fa3 \ + --hash=sha256:488c99ab181a2f59d9ec3b12c5fa11ec904e92be2c4ba18cded54dd7501208fe \ + --hash=sha256:5359c588cc62de6f78d7430f06b65853d884955494d86d6ad90b6dd64a3f3a08 \ + --hash=sha256:573b11f3c327e17ef3826d266e676cf1149a1f3016f822a05f2306c55d8246bf \ + --hash=sha256:57ed0d6b4ac5e7868361303a9c57fbcf63b768236ee14456f585dfcf260d0292 \ + --hash=sha256:5a519c9e95a216fbcb8e759793ef7fb40793fc803ed839142d6dc5be9be5bc89 \ + --hash=sha256:5e60251d32ced5598972e4d5e06a2f044341f9291402551a3f6f0ec44f9299b0 \ + --hash=sha256:6c8e39b53e90afda8ce52859c93de4dba3e02b76d85dcf091cc469f9184c6dae \ + --hash=sha256:6eaed129a4afe90694b8685e2f9b6294849f5eda4af9a15be83a4326eeebd775 \ + --hash=sha256:6ec0d244a5bba12239025389ad88bbfb45f9f10e25ab4f678e9a4768ebd47532 \ + --hash=sha256:7728c0420ec1c338564fc8b01015ff2d58567e70f17fedce5a0a7c0308c0d5b9 \ + --hash=sha256:84fa136b8602c8c39e3b6cb24918960cd6f36cade7a70376f56770729cd56510 \ + --hash=sha256:8f490c003369ce60e514a0c3b12374f05274c101fee1bead6740ec8a564032b0 \ + --hash=sha256:9c6ad60af4102789a5c09824004beade2f7f28cd1cd581ee5c170d9dc2fbb00e \ + --hash=sha256:b1b503864fada3f74fabf8d9fee7b4c1cbe956301e6fdece975d5f77c2fce958 \ + --hash=sha256:b76fa1d3f92967d58289ac47ab7458ede66e6f3527fff3e59142aee57d9307cd \ + --hash=sha256:bad8d1dd5bed060a29332b99d63d0e5c2969081e1c6ea54adfbccfdfa783be44 \ + --hash=sha256:ce7563e0b1a4482cbc1b4a6272145e54e4489e54fe7428f94908c3d87103abfa \ + --hash=sha256:d4f1b9329a251e4affe3bb58f4d3e2db22a714396fd7ffb40d0b5db423c24d17 \ + --hash=sha256:d574c2b28921dc72e869df248f1a278f6eee176a1f237c8642e1a71eb15f3977 \ + --hash=sha256:de9d14748dbf3968951436ef514a29c9d1fe438aa680d110134ee2f7a9f9df18 \ + --hash=sha256:e25bb3b0ad1217b60626e4ed45b10ca170c41d99fbe44a12bebc1e07ec4aad55 \ + --hash=sha256:e2d6149f3a57f405bcad5fb41e03218b8373936253f23e1ca84c0108abbc3392 \ + --hash=sha256:e74266ca8e97874a937b7646378c178025650a236584f7474d10d8086a6edea3 \ + --hash=sha256:f4adb4af61516510d786cf8c01851a66f6d3ddfa79e1144deaa5b40d8507231e \ + --hash=sha256:f4f59f83c82ab480e924b988e7b1b4eb4de836dfcf5390c6f59148d1a00e1d02 \ + --hash=sha256:f6cb525101b6b903779188c1e9e9490c343b455ab822883e02cf01e5547338d2 \ + --hash=sha256:fb87f46b4f7ad7b5d3ad8f4b452b024bd4229d44c8ff934798c1fe656210387a + # via tilelang +model-hosting-container-standards==0.1.16 \ + --hash=sha256:47f4f65713120bc3a69feb022981a38db9e557aedf88dbd72077f20588caa12b \ + --hash=sha256:d34589633900e53c3ee5f7c78280a7cf7e4f6532c35e763341a262fc85cbe84a + # via vllm +more-itertools==11.1.0 \ + --hash=sha256:48e8f4d9e7e5878571ecf6f2b4e57634f93cd474cc8cfbd2376f2d11b396e30d \ + --hash=sha256:4b65538ae22f6fed0ce4874efd317463a7489796a0939fa66824dd542125a192 + # via + # jaraco-classes + # jaraco-functools +mpmath==1.3.0 \ + --hash=sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f \ + --hash=sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c + # via sympy +msgspec==0.21.1 \ + --hash=sha256:0d03867786e5d7ba25d666df4b11320c27170f4aeafcb8e3a8b0a50a4fb742ca \ + --hash=sha256:0d1009f6715f5bff3b54d4ff5c7428ad96197e0534e1645b8e9b955890c84664 \ + --hash=sha256:0d2cc73df6058d811a126ac3a8ad63a4dfa210c82f9cf5a004802eaf4712de90 \ + --hash=sha256:15f523d51c00ebad412213bfe9f06f0a50ec2b93e0c19e824a2d267cabb48ea2 \ + --hash=sha256:1bf17cbd7b28a5dffc7e764c654eed8ccde5e0f1de7970628608304640d4ce4e \ + --hash=sha256:21995e74b5c598c2e004110ad66ec7f1b8c20bf2bcf3b2de8fd9a3094422d3ff \ + --hash=sha256:2313508e394b0d208f8f56892ca9b2799e2561329de9763b19619595a6c0f72c \ + --hash=sha256:344c7cd0eaed1fb81d7959f99100ef71ec9b536881a376f11b9a6c4803365697 \ + --hash=sha256:38fe93e86b61328fe544cb7fd871fad5a27c8734bfda90f65e5dbe288ae50f61 \ + --hash=sha256:3cb779ea0c35bc807ff941d415875c1f69ca0be91a2e907ab99a171811d86a9a \ + --hash=sha256:3d6b9dc50948eaf65df54d2fd0ff66e6d8c32f116037209ee861810eb9b676cb \ + --hash=sha256:42bb1241e0750c1a4346f2aa84db26c5ffd99a4eb3a954927d9f149ff2f42898 \ + --hash=sha256:4692b7c1609155708c4418f88e92f63c13fdf08aa095c84bae82bad75b53389b \ + --hash=sha256:48943e278b3854c2f89f955ddc6f9f430d3f0784b16e47d10604ee0463cd21f5 \ + --hash=sha256:49880fd20fdbcfe1b793f07dd83f12572bab679c9800352c8b2240289aa46a06 \ + --hash=sha256:4e47390360583ba3d5c6cb44cf0a9f61b0a06a899d3c2c00627cedebb2e2884b \ + --hash=sha256:5102c7e9b3acff82178449b85006d96310e690291bb1ea0142f1b24bcb8aabcb \ + --hash=sha256:52c5e21930942302394429c5a582ce7e6b62c7f983b3760834c2ce107e0dd6df \ + --hash=sha256:5666b1b560b97b6ec2eb3fca8a502298ebac56e13bbca1f88523538ce83d01ea \ + --hash=sha256:5d2d4116ebe3035a78d9ec76e99a9d64e5fa6d44fe61a9c5de7fd1acf54bcc69 \ + --hash=sha256:5f8e9dfcd98419cf7568808470c4317a3fb30bef0e3715b568730a2b272a20d7 \ + --hash=sha256:6129f0cca52992e898fd5344187f7c8127b63d810b2fd73e36fca73b4c6475ee \ + --hash=sha256:628aaa35c74950a8c59da330d7e98917e1c7188f983745782027748ee4ca573e \ + --hash=sha256:68604db36b3b4dd9bf160e436e12798a4738848144cea1aca1cb984011eb160f \ + --hash=sha256:6badc03b9725352219cca017bfe71c61f2fbd0fb5982b410ac17c97c213deb30 \ + --hash=sha256:72d9cd03241b8b2edb2e12dcc66c500fa480d8cbd71a8bac105809d468882064 \ + --hash=sha256:740fbf1c9d59992ca3537d6fbe9ebbf9eaf726a65fbf31448e0ecbc710697a63 \ + --hash=sha256:764173717a01743f007e9f74520ed281f24672c604514f7d76c1c3a10e8edb66 \ + --hash=sha256:846758412e9518252b2ac9bffd6f0e54d9ff614f5f9488df7749f81ff5c80920 \ + --hash=sha256:8bc666331c35fcce05a7cd2d6221adbe0f6058f8e750711413d22793c080ac6a \ + --hash=sha256:92d89dfad13bd1ea640dc3e37e724ed380da1030b272bdf5ecafb983c3ad7c75 \ + --hash=sha256:a9aa659ebb0101b1cbc31461212b87e341d961f0ab0772aaf068a99e001ec4aa \ + --hash=sha256:abbb39d65681fa24ed394e01af3d59d869068324f900c61d06062b7fb9980f2f \ + --hash=sha256:ae0162e22849a5e91eaad907766525107523b0daea3df267a9fcb5ba4e0936ae \ + --hash=sha256:b504b6e7f7a22a24b27232b73034421692147865162daaec9f3bf62439007c87 \ + --hash=sha256:c6faffe5bb644ec884052679af4dfd776d4b5ca90e4a7ec7e7e319e4e6b93a6e \ + --hash=sha256:d3124010b3815451494c85ff345e693cb9fe5889cfcbbef39ed8622e0e72319c \ + --hash=sha256:d4248cf0b6129b7d230eacd493c17cc2d4f3989f3bb7f633a928a85b7dcfa251 \ + --hash=sha256:d4ab834a054c6f0cbeef6df9e7e1b33d5f1bc7b86dea1d2fd7cad003873e783d \ + --hash=sha256:d8b8578e4c83b14ceea4cef0d0b747e31d9330fe4b03b2b2ad4063866a178f93 \ + --hash=sha256:dd677e3001fdfed9186de72eab434da2976303cd5eb9550921d3d0c3e3e168ce \ + --hash=sha256:ed2ab278200e743a1d2610a4e0c8fc74f6cecb8548544cdec43f927bd9265238 \ + --hash=sha256:ee9e3f11fa94603f7d673bf795cfa31b549c4a2c723bc39b45beb1e7f5a3fb99 \ + --hash=sha256:ef3ec2296248d1f8b9231acb051b6d471dfde8f21819e86c9adaaa9f42918521 \ + --hash=sha256:f041a2279f31e3a53319005e4d60ba77c085cfcbe394cdc7ce803c2d01fe9449 \ + --hash=sha256:f60800e6299b798142dc40b0644da77ceac5ea0568be58228417eae14135c847 \ + --hash=sha256:f667b90b37fad734a91671abd68e0d7f4d066862771b87e91c53996dcb7a9027 \ + --hash=sha256:f7b27d1a8ead2b6f5b0c4f2d07b8be1ccfcc041c8a0e704781edebe3ae13c484 \ + --hash=sha256:fab48eb45fdbfbdb2c0edfec00ffc53b6b6085beefc6b50b61e01659f9f8757f + # via vllm +multidict==6.8.0 \ + --hash=sha256:003a3bddb32915c3f67096ea41d24e53edf710edb65a1f5d0c70ab40b0e4d20b \ + --hash=sha256:00be37bde741bf60871082cd347a093218c44886e99231b7516671c70f2c280d \ + --hash=sha256:029897732a9c798737457e382bf84e8c64237eff224a90aea2639f4413c45e4e \ + --hash=sha256:05c2e90c5289c5f7436ba2c25812a5fbdaa1c1bc11c8d8d3bbf64f5cd7c633dd \ + --hash=sha256:071da134651b04a8507dfb331ac0988f376337c2aea59486bf20989fb5b5a64e \ + --hash=sha256:088b04a66b3c1fce6fe4d771ec184a0426262d0b86709c908477b4ac7965df40 \ + --hash=sha256:093167d22a8c95af30f597b8a5686f20a14512989942d4be804d119899caca20 \ + --hash=sha256:0935971bffd0b479fc90c4811ca787703e93fcb6afea939a375dfc80285ab368 \ + --hash=sha256:095f62ea4e7a3be2f6c567ab695ce10e950f2adb905c1bec82281593e0b2d2ad \ + --hash=sha256:0b143d53590e89f43153d81d505a8448d4d57354354385aef8a51d67ffefa27e \ + --hash=sha256:0c1c4debad7337627b86837abdf0237ca3cb3d7e17de7eab0177c263878546d4 \ + --hash=sha256:0eca15d627e942ce186a935061f1568cc46c02e97c419c8da802df2be9f917d8 \ + --hash=sha256:0ef606c15cac6c90279acf34120784b6f36662cbf382defd3955cd8f1115336b \ + --hash=sha256:10456943903744ae1249728161c96bd9d2f7eb5ee17fcc2ffda2dc32e1bb36c7 \ + --hash=sha256:11d71490bf4bbff1141b14b93af419ad68c56b60bea9277fcb3f94dcca4796eb \ + --hash=sha256:122adc7c46ac1e31ecfc7f81b2530533dccafdba70f5d741649f87e336c63384 \ + --hash=sha256:13967dca8b2f33230a1427b52438326bb1c9101a1df22a3309ed3fcbbb3c96f0 \ + --hash=sha256:13e26f59f0eecfc5f67c663ad550ffdaf62c0f657547cde387f6c86af1c9449e \ + --hash=sha256:15db8e6cab5f4cc9241bc56e69fdf3452cf49c10ee3c7977c742e68a275b3786 \ + --hash=sha256:18f0e06360c3e451a3ab800355773c8d125a758238d780c800b0ee5e90ee903c \ + --hash=sha256:1969971900b0871530f9b62280dcc2d75688e74d2a69262bc01faf2b96c78f04 \ + --hash=sha256:1b8986d4313dcee7c932837d16a535f1840b827bac1ea7c5c4c80751d0423794 \ + --hash=sha256:1bdb9b8fba5a9aef673ec90db3f55b1ce743f2fbdea4d37dc04d14ccdfc153ff \ + --hash=sha256:1f57c414be82490bc0e0305fdb834186229b2d9b6a35fa0afd1eb1a772d125ab \ + --hash=sha256:1f66fe6a021173d0d47968491791966b9f3e6d61115f2491744aa0c07a6e67af \ + --hash=sha256:202436df907c15adbb94360296c425ea53cf8968a5d2cff9b5b9790ae1972b33 \ + --hash=sha256:2196ba6df392c3574acadd14ef87550f3611349c8618564de324b806a7a31cee \ + --hash=sha256:22a310ad37672a261e55a8b5e28d0ae08cfb68abb1f46418ccd19835c3b8e836 \ + --hash=sha256:23c9ee89967b6a9b4048acb3b93b660ed714ce9c8bf3bbe652959bc120dc02dc \ + --hash=sha256:2622fe114c0bd66ca5c461859357587f5a5e35ee5ff49fc5643d1bc78dbb41c6 \ + --hash=sha256:26a7aafc992e78872e2c8c1f7248c0e01139cf9020a7781b0c064fa566832712 \ + --hash=sha256:27747162712e85c84598d364425dbf1714ff335bdb6ba3171c4e5081196e8916 \ + --hash=sha256:29631224698de1e42abc8fa7658d830e0aed0029785144b5832b695da5adef2f \ + --hash=sha256:29b6e7bc4442a56cf8e0dc1cabf3fdc77cd533568d6829fc76a1effd2ce332ec \ + --hash=sha256:29be9fd289e9ab8f480996ea2f686e1654b80242033843cb11691688329423f1 \ + --hash=sha256:2ba9933e8f35fe4a70f540b837254c4055da82dc3a9e500a8f95e61498083a15 \ + --hash=sha256:2cc66abb85e2108c9ff8a1c0d20fa260bf690bbb33caef4ff3ecb2c2cbdfff5d \ + --hash=sha256:2cd560498ae8e1bcc955643c1d78eb8e338226d07a983c656ea8c4443d3eec0f \ + --hash=sha256:2f79cc3e8039a8cf5c77e0811b0807953fd52d0863b9b76970b20d696dc64a78 \ + --hash=sha256:2f8a4b0b4d639d525928c7f30de527bfdf9ead6e44a5e8cb9c50aced5e4590cb \ + --hash=sha256:307c1acd812fe897e7fbe10c6758822e8c04be4e7c60a9f54901cdf8b5ab8bc3 \ + --hash=sha256:3126f2a96704505aa4e92a72d6e8a5d7f29d40a987ced8bf69e29d71dfc71fbc \ + --hash=sha256:31e8901637e20ccb3cf8f8848b5d0f7a00462bf5b34f7cf3dcbb2753b18e8b39 \ + --hash=sha256:346ac52e56bcda320c0dcdfdd081947ed7cada33afea4e2284bef7b0733bff9b \ + --hash=sha256:348bb85e2038b40c007383616d73f734869063772372519549ebd7da1723d1a4 \ + --hash=sha256:3533a03e4e789baf6a286e7b0b1b6da3f3d7c3eab569686ee29ee1d8b52e2cb4 \ + --hash=sha256:35977263d9bf506dbc65349f63b3b8c91606d4abc110990945e3b94bc671319c \ + --hash=sha256:397599503b718f0137f26d3f6532d6955069cd2e5917c47ef581495bc2529ff8 \ + --hash=sha256:3bafff8598f0528017ddc74194e5451d5c22d046c98935f8f86247b0f286e4f8 \ + --hash=sha256:3d1f48582686a0a3b81e9b43234766cc96697df72081af3f48107bd3f34d34e5 \ + --hash=sha256:4261863fc8b5ab1b815ede94e592e94c6af5b04616014929057e61859e7382a9 \ + --hash=sha256:43a4b56555bbcf8af161e7c7682bd93eec10f068c95844511864c018c8e5e13b \ + --hash=sha256:45cc39ba50fb0754a4359b90f8229ae08598fe2266abe3521b4e5a9ba916534a \ + --hash=sha256:46029e6e27a3ec0dc55b53f58df82d10f04c5e111f78248279b530bedad2c30a \ + --hash=sha256:48ea524a25a1cd5972cf293bc95713918cba0bcd6fa9b992d906c857c546abe2 \ + --hash=sha256:4ee953a5ebaeed38dc21cc032ed17a9d9782802e00042200497ab4b01b0bf7c0 \ + --hash=sha256:54af1266710cb0f305127ae0b970aff8d208057f8a29cd6e1db99b0114947035 \ + --hash=sha256:560b211fc3bd4a1e1c6de44f6d38113bf5b410dfc89a4c0d2a3c0edbf1a0dfb8 \ + --hash=sha256:563661919f603374c40cf45ffcd25535c12b8954203569a2ab1cee5265871cf4 \ + --hash=sha256:563d6500ca80dac7bba6f48a78e0ffd87e21a7d4d24642c6503a2ddccd70c110 \ + --hash=sha256:59e539c4eb4d3a53b0e630a6ba2b2f2824732b5e73f90e30a280f12fde157b15 \ + --hash=sha256:5bbbb696c8024475b1877d14ce20d5f1cc05b8f6d786cea0fe3aa7fedc02e891 \ + --hash=sha256:5caf684986a2490628f059a99dd107b566a2d34cf947f8eb8387e0500a1f90c5 \ + --hash=sha256:5cd4637ce76312ba1e05eb9c5193fec231f64fee0944e135fa1e951242355b37 \ + --hash=sha256:610c7637bc36b90f39e6c66f710f93d57018f83d53e1e187caaa218c6892b95f \ + --hash=sha256:628ff11e6720f90acd0c305dfa3339f04a783a20de8cda6ac333ba46447261e8 \ + --hash=sha256:62b8e291a4f7edbf7cde7a43d831d893ba443a1b627498b53581943b0e348feb \ + --hash=sha256:6300d5176647145ba1e22991c924fb29743e54b4d7b8bc85a0d3ec0e55e189cb \ + --hash=sha256:64eaeda36ee8d88f9e8616a587a8c66a663283cf6e0dcf013c1ddd8c758e4aef \ + --hash=sha256:658f5a1895b804423d97b22d06fc0d0b171c7c01dcc3aa9c8faf0c0e26a249a5 \ + --hash=sha256:65c85c79f5a2c04fbbc18f006c014674dc5fdf270cb978d8862c82c6f694e60c \ + --hash=sha256:68186a2d4051c8ffd17be33553bea2ec9bbc8ef860fe2980a221d96126296f31 \ + --hash=sha256:68d40b2bace413f3231f5729d3fcfb1837fd31c4907e241b5d43211bfd76f3c2 \ + --hash=sha256:69708fecaa88bcb2341397b49fc95057a835b02a3670c551b37f95dd79e64e3a \ + --hash=sha256:69b3e519a132bb943b0daae15fc8c2168706b17f826481d32a32a5e784b129e3 \ + --hash=sha256:6b62b7e0025aa48dec11e125e655d1157985a5fdcec04b1ad500101ad072b891 \ + --hash=sha256:714597cb5d5e15a8a449d2ae23c45b486a9e8fa33c462c7a33d7f35b65d92943 \ + --hash=sha256:758233648ac47b07c575224c4eadd73c8929c3b4c31e2afcfea935fde1cda735 \ + --hash=sha256:75daa15ca16d6285eb2e104b2f05ee6f8d9836c68da3ce5c85f615a0450eed0e \ + --hash=sha256:77745725125d01fd613b6db043362aa7c6bfbfdb23d45dbfc3d92bf58160af62 \ + --hash=sha256:7941ef106ca1f2c62314a13c7ed913bcf49641f3efdc12864d588e17870920ac \ + --hash=sha256:7a2573d0fd34f361a4a14e54d8cda3a91ac4e55fbf0d719698024f3b09c5b147 \ + --hash=sha256:7a62e302fc8cd6aa8972207e7e951d1fdee7c1dda18568305041d19f0e2c00f5 \ + --hash=sha256:7bb0dad75068fee80fcb60f88569722c199d8656a16706702dc6e3b786819c90 \ + --hash=sha256:7bc7003991ebd368a20d05228137a37b3d3066751f3ea1e4f7b8efe8e752f2f5 \ + --hash=sha256:7d26dc8f070c0ec5579e987fa615ffd6883086106eefdff9e10d160fc5630630 \ + --hash=sha256:8125e60f3c70e323ac07dd8b3635f7b3bbc5c3a9ac04ae5988f668ff7ae28a18 \ + --hash=sha256:8180b635290a75af8478f1b3e9810135381ae24833293fe77b85c1c21ff842ab \ + --hash=sha256:82780eb8bf59e8fb25dd081fde6e058805045d6374a7f2f877effc826ca4434b \ + --hash=sha256:835d5a90b11d1f5f8200ff3cc8316bded76eebebc92436398947a27657e645e7 \ + --hash=sha256:83ff054b04915be5c15680da6c6012474a2cc2bf534129a0e8c6a99f17ba7238 \ + --hash=sha256:8457aff3c12a89a8e1c4674de5c777857fbc429f40fe117a3d29538547cbc364 \ + --hash=sha256:847d6082ae694dc95e548acb201bc100e1cfa96513bc71fdcb86f709dad6c435 \ + --hash=sha256:883284137e25318ed9735b742ae46341a864888fae28e8b6314c4f84da080f08 \ + --hash=sha256:887f9a975996032c686719eb7b3e1e7942fab5079c2b778bbd9afe9a9d78244f \ + --hash=sha256:8890c89d662560e51c55ac1304d6f919b23942abe9ae1127cb1de9aa6132fa52 \ + --hash=sha256:88a6df88567680504ae28bfa7a1f2f64243d91e79a40b2c92ef42efc531e23da \ + --hash=sha256:8d1046b5427dcafe6e8a0e07527dd74f1ee694006160162f53f3a17f15aad3b4 \ + --hash=sha256:8daafaa0b2eb43f76898ced78b1e0fb91b38c4fa50da516c18067f2a2d578c20 \ + --hash=sha256:8dc2d9c3a924ed14166e63650b2cf9f59e7821743bdd50b23802bd97ca09bde5 \ + --hash=sha256:90c10b22860dbd09982d0b8993b66231a861bea2993d4a817ff35273f6ea285a \ + --hash=sha256:91fa75d0a693832106d98f66c849f034f21c828d14437f1fb97d3784aab89e84 \ + --hash=sha256:930c6058047410e3edff445f5a6e4457f2e089042dede00e2d18ce06f3ceae2e \ + --hash=sha256:9442b14eec262a1f74369bbd07e75bc5155105164649a4b9fbc1ebc7b8fb0b14 \ + --hash=sha256:95c27b4f3f04320fc44e338573f40c5c956b504a7fcf081a157fd0b02579311c \ + --hash=sha256:9606f583e7acaf61e7b3f56074e14037b9af7cb194590edfc0114b3ae5931ff7 \ + --hash=sha256:962f18c59a000f30b084ea2e6b8001521bb315efd4e5f10acf9fb36f366b7882 \ + --hash=sha256:9caef53b20a105c0d66518a34be2f71b2783de8d091767575ef86f6ea422236d \ + --hash=sha256:9e37024b41d7a7e7e9cce14b248d54707c21c2a2ea30a47b71bdcefcafec00f2 \ + --hash=sha256:a5a7ee1217949ddd43c6b7bcf70d5c22193bb50e8c695386de5905325e93ce9f \ + --hash=sha256:a5e1583c14775580da05641240ce0d93f36ce3ddef3d5083a827468b0bcfe874 \ + --hash=sha256:a9e246f67ac038568b854ed7c5578e4c6af1f742359901a8fcc3603ff1358df6 \ + --hash=sha256:ab83fdd8cf307353edba9c427c17a3a021c2522d690f5633dd9f72d28b48ccca \ + --hash=sha256:ac746cb365bac1c462da9e3e6ab8904a8efe2217a56b0b2e3d9480f41d2b2602 \ + --hash=sha256:ad474c11d851b6fc97cb625e4822bc0cbd567fc07dc2602e28faec5a36b42bbb \ + --hash=sha256:b03ca066b47b18b205cc080dca6f76cbd159f8cdd33a02a0700164c13b37e463 \ + --hash=sha256:b1cd4d66ce894a45482e1ac2837c31d0bd447df35065e542b60055aa2d00404b \ + --hash=sha256:b25426f9f6ed402835617c8f23609a47045f91ecff365eb6734817e039a8ed25 \ + --hash=sha256:b367c342327717d644db4c0ddb37ceb655c84822215ea0773a3a36911b74b71d \ + --hash=sha256:b7e62b8fc7bd6cad007b9f2e0ad9c8d4854c06350d5f51e1a439dd18b510ecac \ + --hash=sha256:b8b7aa75146266fd3e2a2437cf69ae188688c04ab8665b163d4257b46c1e0c83 \ + --hash=sha256:bb36381e1f9f9d06eba2f10bdd438e5d20c07d5b55e1a3eee30b9f44cbf52316 \ + --hash=sha256:bb8c7da8c861391f7ae48e3593762be2dabe405109e01aec520fbe1a6d15d14b \ + --hash=sha256:bb9a60b7faa5d37c426fa91cf4d6738182a1f2755b9fab7c9c64cd466c4ce51e \ + --hash=sha256:be007d1aee2cbd530347dcafedb400891a3b5f1bd7135f95cf5d5b330b5219ee \ + --hash=sha256:be569fff1d85cd29391c431c5641c8772acb75bbdc61e60a8e82fceb9023d385 \ + --hash=sha256:bea7df027015856ba5d0a88e3b4777ff8cb5c66b58fc108050fe79d4dd9d4d2d \ + --hash=sha256:c0fe437a6d2f36aac2b49517057776575b5bf359df314cca20d230a6e139c089 \ + --hash=sha256:c2b2a96cf1dd99fe7867be4c013314225f4d5786e6685906e29932d42aca6f11 \ + --hash=sha256:c2c5fd0fd39574ccd58e1a52565b341aff522c5c836f1b3eb7605c371e61f52c \ + --hash=sha256:c46a08bf070d6849fed483e9d9833f9d06aecb8382ed985be0b38508b3ae958e \ + --hash=sha256:c5f3a2af441670d80ce5fdf13b6c1b421fc1fc7fc5182d58ac7486738bb2b742 \ + --hash=sha256:c60e50bc5b07faac92fd3a20fa21cc8cf3e3f7204d2867b206c73293ebc19101 \ + --hash=sha256:c68e0c0649d17c2d0339e3674e86a4aeba4a7e6b21c1e394cf947a95433b31d0 \ + --hash=sha256:c9c98d2f0126ba84cb45601eed97ff67ff767e19ae6eb3c31b02827b54d700e5 \ + --hash=sha256:ca52b9ec80851366197577154c862c4c4c7036ca76ae94cef5cb59c5cfeab944 \ + --hash=sha256:cbd86f9787c5e2f5fd27d8b21458222f107347c6731c4e93dde68f554b466a2d \ + --hash=sha256:d0264f8d5cb0a803f650a6a8572dfa0cd1e099a2234c588dc8fb220b415b865f \ + --hash=sha256:d0be2b832435001bc623ca7f1499ca1a853d4f082fb61221a80ce71132f50b26 \ + --hash=sha256:d244cf6b52b5ba1c34c3832f4652a668ebb36d95949b96eed9a1c54d916a90dd \ + --hash=sha256:d2d236b8a44ae91536a12ebcb996bdb31cf27425f36b4d05c87f2ba2716050ba \ + --hash=sha256:d3da668e903c934ed0b587ecacfed6901f6ae6384a6e975887592b61845e78bc \ + --hash=sha256:d6dc7804c50fabd28644d4d18a4b20aad3681b3e64f3acd3182b330ca73f7a32 \ + --hash=sha256:d7e5ba0a0153e35fbce9c51df530c8b4cb0c3012b46a04ff9a048441a269c2ed \ + --hash=sha256:d8a5ac357ac283490a8d1899b0383355fd1f8634b14ba0d59e4c0dd97db85556 \ + --hash=sha256:da1c112c5784ccd9d32cd90be6739fee32644e874eff6ae8f0497cba3e352e58 \ + --hash=sha256:dc911ae6152e455b16a2a1a626aa6cd612fa01efb9d0a4ab3f5cf328b911483d \ + --hash=sha256:e0db3a4d1e264e225037a6023888972c25206a96e016021a5bea41c9a939f2a9 \ + --hash=sha256:e192018b732f7b168e6604cbdf40fa8e05c996693b9eb445a0d8a73f4b77c5d3 \ + --hash=sha256:e37b744849fb631bb52e3dadde35ffeee365a6c41cf71257b5b7acc9cd83fd38 \ + --hash=sha256:e41226ecf607f062fe34a2f4cf64ad3a89e3a0180dc800b463b6b14c06dd10dc \ + --hash=sha256:e418ec99574ca24365ca96546af285c2b021a1a072478a79f0e3cc3b08837154 \ + --hash=sha256:e6ec7d37841609a691b96a10b4fde386c7cd93ebbb939f59c9f23325ee788395 \ + --hash=sha256:e886ef8c9879105fe4fc99417447b3a5f35d1131412ce839470bd2089fe2043f \ + --hash=sha256:e8e1e895e23818d343e4ae7dd95a0a556fdeaf8b471acf1c0a39b93c6f54d478 \ + --hash=sha256:e9dc7b4ff6ef184504b49ef9a4113d49a646653b2ce89f5f48c1f57cdf6ba081 \ + --hash=sha256:ea880d441be7c510106bc56064be39266d948aef94ad4955e8784690019a5d9f \ + --hash=sha256:eabb03dc3e4ed6333ecd1cc9826ec80e7a98b5506deeb832d7260c8e44166d23 \ + --hash=sha256:ec0a4d066356054d569a66e0a94691a2058b680be5e710298f61db11a3c4609f \ + --hash=sha256:edda19aff836ec515caafc09ea53d2ab144a041f09ee9a7cefcbd3ae4e976256 \ + --hash=sha256:f1f4a220db6ed7c8fd16b6d644ffd1f082651693204daf3275e049fadc849e39 \ + --hash=sha256:f25b61a708bd276e8cbb6afcbbf1b8e793a3be70ba0a842d0b8692020f83b706 \ + --hash=sha256:f2fa3d3b1c933d4bcb8fd2018700d5e7235c52f2ab8c88d22286965c5c0f00f8 \ + --hash=sha256:f3071e6515cc63714d014da8f738ae9fa3997c476203f3cd46de380c2376ed7b \ + --hash=sha256:f3a0a31189acf6703307397c6139ddabd734c20c5ef92649fc93e473df6615a3 \ + --hash=sha256:f7eefd0233a7c33ca980a5cfef26f1e9b5e2137839e752a99963696729f12d91 \ + --hash=sha256:f8b09b25e0f4dc2ea9e2adbb1cc3ba11a94d6fa3dd978ae659c8743052e1afbc \ + --hash=sha256:f8d7b66c9e09c0bb0add2b5895e646b62a0849e71155066f215523de6b95cbe6 \ + --hash=sha256:fa6c2880709c84457de104385b704fc28860f27e442ad13966fc4af8e714fe9c \ + --hash=sha256:fc5460940f50dff00731b4132366840ba9685286ea88ea104b661899084f3fea \ + --hash=sha256:fd789a294d8e098528be29b2669b83005ce569339f8cef167fc0274c3115c34c + # via + # aiohttp + # yarl +multiprocess==0.70.19 \ + --hash=sha256:02e5c35d7d6cd2bdc89c1858867f7bde4012837411023a4696c148c1bdd7c80e \ + --hash=sha256:0d4b4397ed669d371c81dcd1ef33fd384a44d6c3de1bd0ca7ac06d837720d3c5 \ + --hash=sha256:1bbf1b69af1cf64cd05f65337d9215b88079ec819cd0ea7bac4dab84e162efe7 \ + --hash=sha256:1c3dce098845a0db43b32a0b76a228ca059a668071cfeaa0f40c36c0b1585d45 \ + --hash=sha256:3a56c0e85dd5025161bac5ce138dcac1e49174c7d8e74596537e729fd5c53c28 \ + --hash=sha256:5be9ec7f0c1c49a4f4a6fd20d5dda4aeabc2d39a50f4ad53720f1cd02b3a7c2e \ + --hash=sha256:79576c02d1207ec405b00cabf2c643c36070800cca433860e14539df7818b2aa \ + --hash=sha256:8d5eb4ec5017ba2fab4e34a747c6d2c2b6fecfe9e7236e77988db91580ada952 \ + --hash=sha256:928851ae7973aea4ce0eaf330bbdafb2e01398a91518d5c8818802845564f45c \ + --hash=sha256:952021e0e6c55a4a9fe4cd787895b86e239a40e76802a789d6305398d3975897 \ + --hash=sha256:97404393419dcb2a8385910864eedf47a3cadf82c66345b44f036420eb0b5d87 \ + --hash=sha256:c6b6d78d43a03b68014ca1f0b7937d965393a670c5de7c29026beb2258f2f896 \ + --hash=sha256:d6db91ca6391eebc139c352f34578cea382df6bfa03d3b4146ed12b18b01cc14 \ + --hash=sha256:e5e7dc3e3e1732e88c07aaec17eeb9917f9ed1107d9e60d5ab985cdc14bac43a \ + --hash=sha256:e6c0674d34b8adac22533f6786576b3de4e396aaeda9e0c15378af9b8ada2702 \ + --hash=sha256:e8cc7fbdff15c0613f0a1f1f8744bef961b0a164c0ca29bdff53e9d2d93c5e5f + # via datasets +networkx==3.6.1 \ + --hash=sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509 \ + --hash=sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762 + # via torch +ninja==1.13.0 \ + --hash=sha256:11be2d22027bde06f14c343f01d31446747dbb51e72d00decca2eb99be911e2f \ + --hash=sha256:1c97223cdda0417f414bf864cfb73b72d8777e57ebb279c5f6de368de0062988 \ + --hash=sha256:3c0b40b1f0bba764644385319028650087b4c1b18cdfa6f45cb39a3669b81aa9 \ + --hash=sha256:3d00c692fb717fd511abeb44b8c5d00340c36938c12d6538ba989fe764e79630 \ + --hash=sha256:3d7d7779d12cb20c6d054c61b702139fd23a7a964ec8f2c823f1ab1b084150db \ + --hash=sha256:4a40ce995ded54d9dc24f8ea37ff3bf62ad192b547f6c7126e7e25045e76f978 \ + --hash=sha256:4be9c1b082d244b1ad7ef41eb8ab088aae8c109a9f3f0b3e56a252d3e00f42c1 \ + --hash=sha256:5f8e1e8a1a30835eeb51db05cf5a67151ad37542f5a4af2a438e9490915e5b72 \ + --hash=sha256:60056592cf495e9a6a4bea3cd178903056ecb0943e4de45a2ea825edb6dc8d3e \ + --hash=sha256:6739d3352073341ad284246f81339a384eec091d9851a886dfa5b00a6d48b3e2 \ + --hash=sha256:8cfbb80b4a53456ae8a39f90ae3d7a2129f45ea164f43fadfa15dc38c4aef1c9 \ + --hash=sha256:aa45b4037b313c2f698bc13306239b8b93b4680eb47e287773156ac9e9304714 \ + --hash=sha256:b4f2a072db3c0f944c32793e91532d8948d20d9ab83da9c0c7c15b5768072200 \ + --hash=sha256:be7f478ff9f96a128b599a964fc60a6a87b9fa332ee1bd44fa243ac88d50291c \ + --hash=sha256:d741a5e6754e0bda767e3274a0f0deeef4807f1fec6c0d7921a0244018926ae5 \ + --hash=sha256:e8bad11f8a00b64137e9b315b137d8bb6cbf3086fbdc43bf1f90fd33324d2e96 \ + --hash=sha256:fa2a8bfc62e31b08f83127d1613d10821775a0eb334197154c4d6067b7068ff1 \ + --hash=sha256:fb46acf6b93b8dd0322adc3a4945452a4e774b75b91293bafcc7b7f8e6517dfa \ + --hash=sha256:fb8ee8719f8af47fed145cced4a85f0755dd55d45b2bddaf7431fa89803c5f3e + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # flashinfer-python + # vllm +numba==0.65.0 \ + --hash=sha256:032b0b8e879512cd424d79eed6d772a1399c6387ded184c2cf3cc22c08d750a6 \ + --hash=sha256:05c0a9fdf75d85f57dee47b719e8d6415707b80aae45d75f63f9dc1b935c29f7 \ + --hash=sha256:15d159578e59a39df246b83480f78d7794b0fca40153b5684d3849a99c48a0fb \ + --hash=sha256:194a243ba53a9157c8538cbb3166ec015d785a8c5d584d06cdd88bee902233c7 \ + --hash=sha256:28e547d0b18024f19cbaf9de02fc5c145790213d9be8a2c95b43f93ec162b9e4 \ + --hash=sha256:46fd679ae4f68c7a5d5721efbd29ecee0b0f3013211591891d79b51bfdf73113 \ + --hash=sha256:4c894c94afa5ffd627c7e3b693df10cb0d905bd5eb06de3dfc31775140cf4f89 \ + --hash=sha256:5046c63f783ca3eb6195f826a50797465e7c4ce811daa17c9bea47e310c9b964 \ + --hash=sha256:583680e0e8faf124d362df23b4b593f3221a8996341a63d1b664c122401bec2f \ + --hash=sha256:59bb9f2bb9f1238dfd8e927ba50645c18ae769fef4f3d58ea0ea22a2683b91f5 \ + --hash=sha256:6254e73b9c929dc736a1fbd3d6f5680789709a5067cae1fa7198707385129c04 \ + --hash=sha256:71e72e9ca2f619df4768f9c3962bfec60191a5a26fe2b6a8c6a07532b6146169 \ + --hash=sha256:a52d92ffd297c10364bce60cd1fcb88f99284ab5df085f2c6bcd1cb33b529a6f \ + --hash=sha256:add297d3e1c08dd884f44100152612fa41e66a51d15fdf91307f9dde31d06830 \ + --hash=sha256:af143d823624033a128b5950c0aaf9ffc2386dfe954eb757119cf0432335534c \ + --hash=sha256:af96c0cba53664efcb361528b8c75e011a6556c859c7e08424c2715201c6cf7a \ + --hash=sha256:b27ee4847e1bfb17e9604d100417ee7c1d10f15a6711c6213404b3da13a0b2aa \ + --hash=sha256:b7325b1aab88f0339057288ee32f39dc660e14f93872a6fda14fa6eb9f95b047 \ + --hash=sha256:b8a9008411615c69d083d1dcf477f75a5aa727b30beb16e139799e2be945cdfd \ + --hash=sha256:c6334094563a456a695c812e6846288376ca02327cf246cdcc83e1bb27862367 \ + --hash=sha256:c7fa502960f7a2f3f5cb025bc7bff888a3551277b92431bfdc5ba2f11a375749 \ + --hash=sha256:da8e371e328c06d0010c3d8b44b21858652831b85bcfba78cb22c042e22dbd8e \ + --hash=sha256:dff9fd5fbc9a35c517359c5823ea705d9b65f01fb46e42e35a2eabe5a52c2e96 \ + --hash=sha256:edad0d9f6682e93624c00125a471ae4df186175d71fd604c983c377cdc03e68b \ + --hash=sha256:ee336b398a6fca51b1f626034de99f50cb1bd87d537a166275158a3cee744b82 + # via vllm +numpy==2.3.5 \ + --hash=sha256:00dc4e846108a382c5869e77c6ed514394bdeb3403461d25a829711041217d5b \ + --hash=sha256:0472f11f6ec23a74a906a00b48a4dcf3849209696dff7c189714511268d103ae \ + --hash=sha256:04822c00b5fd0323c8166d66c701dc31b7fbd252c100acd708c48f763968d6a3 \ + --hash=sha256:052e8c42e0c49d2575621c158934920524f6c5da05a1d3b9bab5d8e259e045f0 \ + --hash=sha256:09a1bea522b25109bf8e6f3027bd810f7c1085c64a0c7ce050c1676ad0ba010b \ + --hash=sha256:0cd00b7b36e35398fa2d16af7b907b65304ef8bb4817a550e06e5012929830fa \ + --hash=sha256:0d8163f43acde9a73c2a33605353a4f1bc4798745a8b1d73183b28e5b435ae28 \ + --hash=sha256:1062fde1dcf469571705945b0f221b73928f34a20c904ffb45db101907c3454e \ + --hash=sha256:11e06aa0af8c0f05104d56450d6093ee639e15f24ecf62d417329d06e522e017 \ + --hash=sha256:17531366a2e3a9e30762c000f2c43a9aaa05728712e25c11ce1dbe700c53ad41 \ + --hash=sha256:1978155dd49972084bd6ef388d66ab70f0c323ddee6f693d539376498720fb7e \ + --hash=sha256:1ed1ec893cff7040a02c8aa1c8611b94d395590d553f6b53629a4461dc7f7b63 \ + --hash=sha256:2dcd0808a421a482a080f89859a18beb0b3d1e905b81e617a188bd80422d62e9 \ + --hash=sha256:2e2eb32ddb9ccb817d620ac1d8dae7c3f641c1e5f55f531a33e8ab97960a75b8 \ + --hash=sha256:2feae0d2c91d46e59fcd62784a3a83b3fb677fead592ce51b5a6fbb4f95965ff \ + --hash=sha256:3095bdb8dd297e5920b010e96134ed91d852d81d490e787beca7e35ae1d89cf7 \ + --hash=sha256:30bc11310e8153ca664b14c5f1b73e94bd0503681fcf136a163de856f3a50139 \ + --hash=sha256:3101e5177d114a593d79dd79658650fe28b5a0d8abeb8ce6f437c0e6df5be1a4 \ + --hash=sha256:396084a36abdb603546b119d96528c2f6263921c50df3c8fd7cb28873a237748 \ + --hash=sha256:3997b5b3c9a771e157f9aae01dd579ee35ad7109be18db0e85dbdbe1de06e952 \ + --hash=sha256:414802f3b97f3c1eef41e530aaba3b3c1620649871d8cb38c6eaff034c2e16bd \ + --hash=sha256:51c1e14eb1e154ebd80e860722f9e6ed6ec89714ad2db2d3aa33c31d7c12179b \ + --hash=sha256:51c55fe3451421f3a6ef9a9c1439e82101c57a2c9eab9feb196a62b1a10b58ce \ + --hash=sha256:5ee6609ac3604fa7780e30a03e5e241a7956f8e2fcfe547d51e3afa5247ac47f \ + --hash=sha256:612a95a17655e213502f60cfb9bf9408efdc9eb1d5f50535cc6eb365d11b42b5 \ + --hash=sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42 \ + --hash=sha256:63c0e9e7eea69588479ebf4a8a270d5ac22763cc5854e9a7eae952a3908103f7 \ + --hash=sha256:66f85ce62c70b843bab1fb14a05d5737741e74e28c7b8b5a064de10142fad248 \ + --hash=sha256:6cf9b429b21df6b99f4dee7a1218b8b7ffbbe7df8764dc0bd60ce8a0708fed1e \ + --hash=sha256:70b37199913c1bd300ff6e2693316c6f869c7ee16378faf10e4f5e3275b299c3 \ + --hash=sha256:727fd05b57df37dc0bcf1a27767a3d9a78cbbc92822445f32cc3436ba797337b \ + --hash=sha256:74ae7b798248fe62021dbf3c914245ad45d1a6b0cb4a29ecb4b31d0bfbc4cc3e \ + --hash=sha256:784db1dcdab56bf0517743e746dfb0f885fc68d948aba86eeec2cba234bdf1c0 \ + --hash=sha256:86945f2ee6d10cdfd67bcb4069c1662dd711f7e2a4343db5cecec06b87cf31aa \ + --hash=sha256:86d835afea1eaa143012a2d7a3f45a3adce2d7adc8b4961f0b362214d800846a \ + --hash=sha256:872a5cf366aec6bb1147336480fef14c9164b154aeb6542327de4970282cd2f5 \ + --hash=sha256:8b973c57ff8e184109db042c842423ff4f60446239bd585a5131cc47f06f789d \ + --hash=sha256:8cba086a43d54ca804ce711b2a940b16e452807acebe7852ff327f1ecd49b0d4 \ + --hash=sha256:8f7f0e05112916223d3f438f293abf0727e1181b5983f413dfa2fefc4098245c \ + --hash=sha256:900218e456384ea676e24ea6a0417f030a3b07306d29d7ad843957b40a9d8d52 \ + --hash=sha256:93eebbcf1aafdf7e2ddd44c2923e2672e1010bddc014138b229e49725b4d6be5 \ + --hash=sha256:9c75442b2209b8470d6d5d8b1c25714270686f14c749028d2199c54e29f20b4d \ + --hash=sha256:9ee2197ef8c4f0dfe405d835f3b6a14f5fee7782b5de51ba06fb65fc9b36e9f1 \ + --hash=sha256:a414504bef8945eae5f2d7cb7be2d4af77c5d1cb5e20b296c2c25b61dff2900c \ + --hash=sha256:a4b9159734b326535f4dd01d947f919c6eefd2d9827466a696c44ced82dfbc18 \ + --hash=sha256:a80afd79f45f3c4a7d341f13acbe058d1ca8ac017c165d3fa0d3de6bc1a079d7 \ + --hash=sha256:aa5bc7c5d59d831d9773d1170acac7893ce3a5e130540605770ade83280e7188 \ + --hash=sha256:acfd89508504a19ed06ef963ad544ec6664518c863436306153e13e94605c218 \ + --hash=sha256:aeffcab3d4b43712bb7a60b65f6044d444e75e563ff6180af8f98dd4b905dfd2 \ + --hash=sha256:afaffc4393205524af9dfa400fa250143a6c3bc646c08c9f5e25a9f4b4d6a903 \ + --hash=sha256:b0c7088a73aef3d687c4deef8452a3ac7c1be4e29ed8bf3b366c8111128ac60c \ + --hash=sha256:b46b4ec24f7293f23adcd2d146960559aaf8020213de8ad1909dba6c013bf89c \ + --hash=sha256:b501b5fa195cc9e24fe102f21ec0a44dffc231d2af79950b451e0d99cea02234 \ + --hash=sha256:bf06bc2af43fa8d32d30fae16ad965663e966b1a3202ed407b84c989c3221e82 \ + --hash=sha256:c804e3a5aba5460c73955c955bdbd5c08c354954e9270a2c1565f62e866bdc39 \ + --hash=sha256:c8a9958e88b65c3b27e22ca2a076311636850b612d6bbfb76e8d156aacde2aaf \ + --hash=sha256:cc0a57f895b96ec78969c34f682c602bf8da1a0270b09bc65673df2e7638ec20 \ + --hash=sha256:cc8920d2ec5fa99875b670bb86ddeb21e295cb07aa331810d9e486e0b969d946 \ + --hash=sha256:ccc933afd4d20aad3c00bcef049cb40049f7f196e0397f1109dba6fed63267b0 \ + --hash=sha256:ce581db493ea1a96c0556360ede6607496e8bf9b3a8efa66e06477267bc831e9 \ + --hash=sha256:d0f23b44f57077c1ede8c5f26b30f706498b4862d3ff0a7298b8411dd2f043ff \ + --hash=sha256:d21644de1b609825ede2f48be98dfde4656aefc713654eeee280e37cadc4e0ad \ + --hash=sha256:d6889ec4ec662a1a37eb4b4fb26b6100841804dac55bd9df579e326cdc146227 \ + --hash=sha256:de5672f4a7b200c15a4127042170a694d4df43c992948f5e1af57f0174beed10 \ + --hash=sha256:e6a0bc88393d65807d751a614207b7129a310ca4fe76a74e5c7da5fa5671417e \ + --hash=sha256:ed89927b86296067b4f81f108a2271d8926467a8868e554eaf370fc27fa3ccaf \ + --hash=sha256:ee3888d9ff7c14604052b2ca5535a30216aa0a58e948cdd3eeb8d3415f638769 \ + --hash=sha256:f0963b55cdd70fad460fa4c1341f12f976bb26cb66021a5580329bd498988310 \ + --hash=sha256:f16417ec91f12f814b10bafe79ef77e70113a2f5f7018640e7425ff979253425 \ + --hash=sha256:f28620fe26bee16243be2b7b874da327312240a7cdc38b769a697578d2100013 \ + --hash=sha256:f4255143f5160d0de972d28c8f9665d882b5f61309d8362fdd3e103cf7bf010c \ + --hash=sha256:ffac52f28a7849ad7576293c0cb7b9f08304e8f7d738a8cb8a90ec4c55a998eb \ + --hash=sha256:ffe22d2b05504f786c867c8395de703937f934272eb67586817b46188b4ded6d \ + --hash=sha256:fffe29a1ef00883599d1dc2c51aa2e5d80afe49523c261a74933df395c15c520 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # accelerate + # bitsandbytes + # cuda-core + # datasets + # flashinfer-python + # gradio + # humming-kernels + # mistral-common + # ml-dtypes + # numba + # nvidia-cutlass-dsl-libs-base + # nvidia-cutlass-dsl-libs-cu13 + # opencv-python-headless + # pandas + # peft + # tilelang + # torchvision + # trackio + # transformers + # vllm + # xgrammar +nvidia-cublas==13.1.0.3 \ + --hash=sha256:2a3b94a37def342471c59fad7856caee4926809a72dd5270155d6a31b5b277be \ + --hash=sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2 \ + --hash=sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171 + # via + # cuda-toolkit + # nvidia-cudnn-cu13 + # nvidia-cusolver +nvidia-cuda-cccl==13.3.4.2.1 \ + --hash=sha256:1acac53f4af7766d7ab7cc7be5c89d9533d545dbef4910f3b5aab5ae4ef4da48 \ + --hash=sha256:2b932d5419e7d2e03aee621a550191d67d74649fb8ba0adb940a6917994df438 \ + --hash=sha256:36dd7c32e77fc97909fad2b1c1d07eda30783611994471b6a4efd5c018a0e9b8 \ + --hash=sha256:4a78adf03982c941708aa9cc7967d73370a9c59ad1ac4e63a1ca7509bc54e392 + # via humming-kernels +nvidia-cuda-crt==13.4.59 \ + --hash=sha256:02e408d082f5b5a653b1ce8d97c7fabd76fa68c8770cc11d04bbc2440dfdd7a8 \ + --hash=sha256:32a48e6b08d1c00f10068edd2e212b9c0e9a1a784a0403cdd715fa81551759f9 \ + --hash=sha256:cf51a97c180dfff25b63a8dd0d4a760e8c6ffa2e934b5b3348873fefd0cd0a2c \ + --hash=sha256:d573b3c663585b2d97e6edddd4b71407d38dfda5046aa7ab370759dc8698c409 + # via nvidia-cuda-nvcc +nvidia-cuda-cupti==13.0.85 \ + --hash=sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8 \ + --hash=sha256:683f58d301548deeefcb8f6fac1b8d907691b9d8b18eccab417f51e362102f00 \ + --hash=sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151 + # via cuda-toolkit +nvidia-cuda-nvcc==13.2.86 \ + --hash=sha256:4171face482ef8ca35b5b2d59cfd25d2d0e2a2e2534fe34d2ce20695437868d9 \ + --hash=sha256:7d3f56c8d705bad35bbba69eb0470d90856fd5d4dc48c6a6173aaf1e9f887cf5 \ + --hash=sha256:909140a1f942b943982b2eff120e618c94e29d75d9e33f5cd074f0e64eb411e8 + # via + # cuda-tile + # humming-kernels + # nvidia-cuda-tileiras +nvidia-cuda-nvrtc==13.0.88 \ + --hash=sha256:6bcd4e7f8e205cbe644f5a98f2f799bef9556fefc89dd786e79a16312ce49872 \ + --hash=sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575 \ + --hash=sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b + # via + # cuda-toolkit + # humming-kernels +nvidia-cuda-runtime==13.0.96 \ + --hash=sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548 \ + --hash=sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55 \ + --hash=sha256:f79298c8a098cec150a597c8eba58ecdab96e3bdc4b9bc4f9983635031740492 + # via + # cuda-toolkit + # humming-kernels + # nvidia-cuda-nvcc +nvidia-cuda-tileiras==13.2.86 \ + --hash=sha256:5f79c6a9bf8583dae105cd67b985555f39f4576416664a86383ba892e8c346c9 \ + --hash=sha256:832f360aa8ce478ff878c4e6630cd767349a597ea54382e7c07a835b0daead96 \ + --hash=sha256:f1434762898ac914585aaa651ad0fe170c57bcdf8d878ba38343772998bb7ff3 + # via cuda-tile +nvidia-cudnn-cu13==9.19.0.56 \ + --hash=sha256:40d8c375005bcb01495f8edf375230b203a411a0c05fb6dc92a3781edcb23eac \ + --hash=sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4 \ + --hash=sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf + # via torch +nvidia-cudnn-frontend==1.28.0 \ + --hash=sha256:04b209c40cf5295ea6141be8c74c2e65449b619a0911ceb2ae27cd914124d376 \ + --hash=sha256:060b0c021f6841312ad26dd837a51b4941d4e0d2a547d87455bd974c173da075 \ + --hash=sha256:248e2d81fb670e7b591246eeb626fc336e2bad29551afa84daf062d63aaf76b3 \ + --hash=sha256:26cd299d202e832a1d562510d6844dc7c6f256cb1b4f0f29d5b2fa0b34f05e98 \ + --hash=sha256:2c92499c388665ab93a492705c591856f8c04014f5dac0e2fb593355bc0d888f \ + --hash=sha256:30e20178e46a5a5049c2358f4147d1c5414b45b2753a455da05bab914807a44d \ + --hash=sha256:335c916be758a8d5533104e04fc4a15f9c30a419b103ecd2b6fd0d23e1d532e4 \ + --hash=sha256:33884611aa659687138d6abf3a10a5e121cd76018192a14c69be43e8898da384 \ + --hash=sha256:3d4a7a8b33b6c32018162b10b0f8b2d5046ac224c7fcf4765f4562419c3e559e \ + --hash=sha256:5173437c88b3accb7cc6855b42a64d583d7962ce5a90b148257e0a0d3c9c1eaa \ + --hash=sha256:56c596054d7ff929ba95ae560bcf1f41d4f0113a9f8b693401c0d508262a4717 \ + --hash=sha256:735f2acb13ea32592cd12f8eda1295efa197aa1ee0110a3a04fbe05f23542400 \ + --hash=sha256:851fde83b08026f33fc83b2c656232833e4c79ad886c937ed5a035473ad02e1d \ + --hash=sha256:8fe9ab83ac4e14069d0904b5e3dc8307904c583ebe53ce941dacd534b7e8df6e \ + --hash=sha256:92d52770600d9b175e3098faed570477cf31c78646dc224cd92b2149d62afe0e \ + --hash=sha256:bf585cebdbf2aebd2aacfb9b0630a2db2432b4efe9be8c6cd103980c931e2c31 \ + --hash=sha256:c9d2358dcee1d94676f74147996f4d5397124a6569da52f84f0088183811a17f \ + --hash=sha256:cc146a5e414e82582f1fce4367e76e787fe444ef459067462103324bf927fdc4 \ + --hash=sha256:e1929bde04d0172828b65584317b5e2de3e60743768e5f634875f69eeab6b97e \ + --hash=sha256:e9a22fbd886c0c0e2687281ac52524568c242a60c18013e9d4a49eec595c20c5 \ + --hash=sha256:fcd16141cae7b013ad0a9fd4beda74bb8ee15b66841ebecacaa4179af0975f4b + # via + # flashinfer-python + # vllm +nvidia-cufft==12.0.0.61 \ + --hash=sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5 \ + --hash=sha256:2abce5b39d2f5ae12730fb7e5db6696533e36c26e2d3e8fd1750bdd2853364eb \ + --hash=sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3 + # via cuda-toolkit +nvidia-cufile==1.15.1.6 \ + --hash=sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44 \ + --hash=sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1 + # via cuda-toolkit +nvidia-curand==10.4.0.35 \ + --hash=sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a \ + --hash=sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc \ + --hash=sha256:65b1710aa6961d326b411e314b374290904c5ddf41dc3f766ebc3f1d7d4ca69f + # via cuda-toolkit +nvidia-cusolver==12.0.4.66 \ + --hash=sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2 \ + --hash=sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112 \ + --hash=sha256:16515bd33a8e76bb54d024cfa068fa68d30e80fc34b9e1090813ea9362e0cb65 + # via cuda-toolkit +nvidia-cusparse==12.6.3.3 \ + --hash=sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b \ + --hash=sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c \ + --hash=sha256:cbcf42feb737bd7ec15b4c0a63e62351886bd3f975027b8815d7f720a2b5ea79 + # via + # cuda-toolkit + # nvidia-cusolver +nvidia-cusparselt-cu13==0.8.0 \ + --hash=sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd \ + --hash=sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c \ + --hash=sha256:e80212ed7b1afc97102fbb2b5c82487aa73f6a0edfa6d26c5a152593e520bb8f + # via torch +nvidia-cutlass-dsl==4.5.2 \ + --hash=sha256:68ed1b63ca74aae87955012da9dfd7fdaae471329d0028b229b841c7192ccf52 + # via + # flashinfer-python + # quack-kernels + # tokenspeed-mla + # vllm +nvidia-cutlass-dsl-libs-base==4.5.2 \ + --hash=sha256:12c29f7c1f1f82851092ba3869264dafafb035228c0d9827a8db08b884fb80ca \ + --hash=sha256:15ef6a59193667e663934ef4873f8ccad37455e9b7c3c419c3072113b8aedf61 \ + --hash=sha256:216eee6aa8107d35569f9451b66b03a3c53167841d1af9b630b966ef8d966e19 \ + --hash=sha256:386e832427e3670479049a1560e4d8d2e565d8c0f37a6852c6d7043d046548f1 \ + --hash=sha256:395bd77cf642aeef311313453e6582f11c9357a4b81fe620ea3daccd1fccab9b \ + --hash=sha256:5aca392063ffbc7da30442a267928b22d4a2d37f9ea1db32e4487aa31b0fcc33 \ + --hash=sha256:9117900cba53d3c21a8dacba6bbf3d6e5f269e427a526c320fb44707a0d57363 \ + --hash=sha256:abab8a0d2f3f5661533c366df78f973052b86a3b52b868d997a95dce5aa8f17b \ + --hash=sha256:b62807bc5ea13bbdef648212893fac407ed943f940cece56b880d44af243e075 \ + --hash=sha256:cbb555a95c7011e4b3ca328be407299c77d289660adbea22ed515d4406e6949c \ + --hash=sha256:d2a3c412287e356fbe48fe9f845d6d33cd35dea5e20d7e4f628c20957967cacd \ + --hash=sha256:e59da7d89e5e4f8514c6530843f910f9d8734d8042dcaa079c9d9c5063eb3514 + # via + # nvidia-cutlass-dsl + # nvidia-cutlass-dsl-libs-cu13 +nvidia-cutlass-dsl-libs-cu13==4.5.2 \ + --hash=sha256:1d255f4a308eb0d228d2466a415a8489b8337db1d322f5d8428e60139b41a317 \ + --hash=sha256:3032405dff28892340f96b467e744a822079cae454dce534fc17b77e85190e42 \ + --hash=sha256:587494d0ab615b805fac86b43a3c1b855182f455681c9cc4ddb1b8973f44a7cc \ + --hash=sha256:64e994554af4da59f75754b9df1a2b1bdfdb96b58c2457802da13d586fb58cde \ + --hash=sha256:696c65ca03995713b6719bc59b7df06f8ec1d263d7eb6ac77aa011201e142bd5 \ + --hash=sha256:80f0cd402e0f1d1571e5aed33bfa17dbc9cb90cc5b1352f0f806b4788558e80e \ + --hash=sha256:888edad4fe1e9b683fddcbc6969437527ccd0eb8740e60dce8f29f6a3a22c825 \ + --hash=sha256:aabd41c980083db94950a4010c2c1ca156d4ab56701605739a3fba388ac9736b \ + --hash=sha256:c4d3ea9080c5a92f8f4a69451ef7036f43bfc3d7f8a426dd70258f0e237c05fb \ + --hash=sha256:c7a5ce1c01616fc4c3ac492e011c543a79c3dde86aaf20a8af55e9d40ef2b2e6 \ + --hash=sha256:df61430d6110eea872acb39257042814bf02dcbb1f8d55ea0c5681bb7ce5836a \ + --hash=sha256:f4a7b72147c2efdc7963c64475eac4ed67eb1dd5fdf5b0300daf79319fe9a38a + # via nvidia-cutlass-dsl +nvidia-ml-py==13.610.43 \ + --hash=sha256:65437eb73d68d0c62c931ca4d45038472faff03bd0b8729abba4b899f70d60f2 \ + --hash=sha256:f13c72698edef492f985cc225f14faafe68ae065a2e407f45bdf6f4b9b43fde8 + # via + # flashinfer-python + # humming-kernels +nvidia-nccl-cu13==2.28.9 \ + --hash=sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643 \ + --hash=sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42 + # via torch +nvidia-nvjitlink==13.0.88 \ + --hash=sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b \ + --hash=sha256:634e96e3da9ef845ae744097a1f289238ecf946ce0b82e93cdce14b9782e682f \ + --hash=sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c + # via + # cuda-toolkit + # nvidia-cuda-tileiras + # nvidia-cufft + # nvidia-cusolver + # nvidia-cusparse +nvidia-nvshmem-cu13==3.4.5 \ + --hash=sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80 \ + --hash=sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9 + # via torch +nvidia-nvtx==13.0.85 \ + --hash=sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4 \ + --hash=sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6 \ + --hash=sha256:d66ea44254dd3c6eacc300047af6e1288d2269dd072b417e0adffbf479e18519 + # via cuda-toolkit +nvidia-nvvm==13.2.86 \ + --hash=sha256:7d140c2dd2d177af71240a31bbca20f40b4aee9a4e331156083e387855222b49 \ + --hash=sha256:a502dcc2859f17a925adba495c222d44a221b9eb10e7d111a7046dc2cc883b69 \ + --hash=sha256:a9244f3209922d655612c11ebb4117ea1f80983cb2b215c9cccded127282cebc + # via + # cuda-tile + # nvidia-cuda-nvcc + # nvidia-cuda-tileiras +nvtx==0.2.15 \ + --hash=sha256:02bca69ee55e0be41eabf908de9dbcdd18e702c7f49f9aa63fd396ce684ff5d5 \ + --hash=sha256:10749686633f880ad53dcdbb2179fad41b45dcf5b7631d4a1070a577577bd386 \ + --hash=sha256:157b80ea9b4db6c8f47f8dbe2fa2e81e7a7f1445bb87f8268f43dec9210b78a1 \ + --hash=sha256:2287d3be05b85661deb386f878d1f536c2e532774aa9ec7a50c434942ed81ae5 \ + --hash=sha256:25813ead4fff4d3a6e04f69a72507b096a6bdbecefa369f1100b0e584767bca8 \ + --hash=sha256:2cc530cd0f1a2c14a3a7e683833db509888ac5ed4ead94e5c9e2c7317c6937a7 \ + --hash=sha256:3741edac4678b92f03d22a3f0a2dfd469f422f85e63db71b038e02525b2404ad \ + --hash=sha256:3c6d0f27d4f8a2f479eb64a6b842c13aee32120348a1715d995b9bb9f75b35cf \ + --hash=sha256:3ca8030a6d197952318013dd1c12c22da1d4b9feb76ba72e0fcd449961183c2c \ + --hash=sha256:58653bf6fd8453947b9e5153da2ad7aeb0ceafa030de7f133efb3eada5da7ca7 \ + --hash=sha256:5f3362f0db4252514719326c9d5662b0f93d254659ba97b9c8dbe556286e0e3e \ + --hash=sha256:70a1e768964e0520b68ccabc4df391cc227537c45936a7eba6507bc65e617e00 \ + --hash=sha256:8be06c3c8c267eba56a0396366b9593092e0b75ea8d3702b303d48c0a1662f0e \ + --hash=sha256:9934fad0b441cfa6e896a848b092498ba23e2ff205c2b9a7b60520ff8367ffef \ + --hash=sha256:9d1d10db4fb4a3b0ffd6ed37bf25f0a966a3b4d34b3c9abb1f6572732959a6e5 \ + --hash=sha256:a4f50832fd90a1b480a9deef6e4cd48015b61869095b54dd1a7afe87b4138c6a \ + --hash=sha256:a6650b029263d12f8427a4dee8bd59cb9c91bccb60543bfcb20bc2b00fdcd672 \ + --hash=sha256:aca61135c76b8107ae3c994325613afa661e1336a991c59cc9c6176829b3b32c \ + --hash=sha256:b5171b8283dd3ea9ae688a86d16901b4c2c142c4eb0a4bdbf6c222f5f67f9524 \ + --hash=sha256:c6c9e76d40eae128b6ad0bd44123df516dfcc26e2d6bb65ddd9336b35caeec7f \ + --hash=sha256:d28660d9c46f8ba750d781572b6aa5a1e6221abba224ab32d7fb32c2d0fd67df \ + --hash=sha256:d2ec75546d37738ae41dea026bbd04ccf61c11a9acbf96b744a9478aa93a8c2f \ + --hash=sha256:d71f934e580d4572f382712b6da464ab69e4c212981506f781f927d5c6d935d6 \ + --hash=sha256:dbe41f78f5a811bd4cdad0a237e5b41a4937d8c2c6c9abdd161091671a598bc0 \ + --hash=sha256:f862e22017b43edf7eaf8379091a034e88d99631bc16f3b6b11abf6d09a1177f + # via vllm +obstore==0.11.1 \ + --hash=sha256:066cb4190e957b172b269affb98bee5a8f5cf6d0949b5b57ffe7f8cfaa540779 \ + --hash=sha256:0844ab75c8413c0af2d0fd2d2f6aa42d166809b6ac4be478151cd946ce7e5d69 \ + --hash=sha256:093152daa5c32b70a032f231bbc6a7eff76dda4285eed5a81d272b4485032dab \ + --hash=sha256:128da07f3a1b9c70159e2b2be9e27f458a9d531d57b1856dd7c4ddb73b4c9937 \ + --hash=sha256:13bb0b6a40931ab2da93f93ad843865d483d95acec1bd586df79313daa5a50af \ + --hash=sha256:159a50d0f4cc53afe6f5c695313bcaf6e92ac81d7847692c8279153483272bfa \ + --hash=sha256:2a9f3d66dbf3c073dd6b6033c0787ef14edf78d97bff95595d6f6979692da264 \ + --hash=sha256:2ba3bceec4263b3a70eea873abedb82e03da9599f2326e80d6505cab0c10d401 \ + --hash=sha256:2d80248f106bf9a2860a4b21add7d14c95eaa5785c7fe11e56d28a4602c7a07c \ + --hash=sha256:2eab3231ce66bc08e57b686b795d4a1333133c28b2e50c11f9986311dec69899 \ + --hash=sha256:307b5f9d64a7c00cd13371e376c05e3914216bd0818a19ff2ae05bc0135e01dc \ + --hash=sha256:32dcdae4207656ef353f949f927342e31757f926c58f014873b2d90c70276cb7 \ + --hash=sha256:3901f539d764cd2ec74c4e900dd461d89d765b2cacf06379ac7b7014e8d132ca \ + --hash=sha256:3ad616bd597a453dcb225ef6bec2af0c71b24b22c314a4328bcc216f774d6b9a \ + --hash=sha256:3b9cb988e03ff963914cf2176aee293eaaff7dcf4efcb905ce6834264b4b0884 \ + --hash=sha256:3e657c600b90f465ef43d00e6dd921e676a1bec34713dad1fc09ef6715e30658 \ + --hash=sha256:446dfe0019740da25394917585b8b0c116ae79454a633ee83aeb0c973c8812d2 \ + --hash=sha256:46122e585f48ab3f2e4fed51401a4e866279a3a3d1aee2372d598ab85ba2c539 \ + --hash=sha256:48983a143de69b11de49212caa79b5c39acbff7f592f9e85d156a0133a0e5796 \ + --hash=sha256:50de3116af69b6f1669cb443cc200ccdc1523a790a41f00854331b48f716cf8f \ + --hash=sha256:614a36f19963cfefedd52f06c5370c034a6dc5b0a2be2e4602fe6ac8e52e97a6 \ + --hash=sha256:63be447e5fdaf0c31517a66906030148697ab3b1f1611fe03e30125a56199ccc \ + --hash=sha256:6680094928da4587dfd03be4e9c17ce7eaa1ec287f49519c75896819c0767247 \ + --hash=sha256:67b8acfbbab960c2cb14c34294a96556caa67fcff15ee77c716d5c1bd1558606 \ + --hash=sha256:68adc24a822536148a3c12be0b7cac091239b3f99c0589b16bcdba5a6ccf58e4 \ + --hash=sha256:6f89647953b4ea50bab3f66591f7f462cd454a5d2fbbeefd885716a2c54e13ce \ + --hash=sha256:7275e75228059b2b30772b2ba3e41562a4a92ebdd3a96619d300f98ef152119f \ + --hash=sha256:73284fc8a9804596baf4d80b55c0e71a6007487bfa28d6924acd85264d5be81f \ + --hash=sha256:75a12f1d0d38fd2923e972e417797d7bf123ff7d13e2b6c5919d2e9a3c6653cc \ + --hash=sha256:7b1b769fda200cbee559da2100586b3b7810bc9f207fe41b9a00fd749c997799 \ + --hash=sha256:801f57c06df5d81eaff8ed553129cedadad12cf1d76fb7499fd6daa1d996c72b \ + --hash=sha256:8a399e7c816e8d7bfe5e992a04e25326bd0779822b65bfd3dbc943b8a62192dc \ + --hash=sha256:8a3f93310422b153629af929d9e88d1fc826d5e32a456de4d3a1926a770ae09c \ + --hash=sha256:955f2348bd17beb80f3f96bb52b119e61eee99c97d1f0b103aa61a0840b7c862 \ + --hash=sha256:97850f68c8417f7167549bdb705c362c825ed470298c6b04faf52258c5e9234e \ + --hash=sha256:a1dd9d7ddc10bbf17cea0e84553a665a822878bd199380557a6df2aa1e60ce34 \ + --hash=sha256:a5afe8b99e3b20cdc9133be7a1b381259acf0d470029f6b2fc79c3f9947ad436 \ + --hash=sha256:a8cc4681d45196645b0567cf7259730fcb0edba751ecad8c4c3a6ccc63fabe24 \ + --hash=sha256:b3ab26d158a096c750759981f75f279f6568d945b8fe640e0cebfdff8d601044 \ + --hash=sha256:b895230ad67a7b9a2c7dfff7c7c9801570a9dfe71f7e383013f831d35b74ea2b \ + --hash=sha256:b97ee10456e65f166c030b5fbde8380ca508621e23b84efd77aad83afe99315a \ + --hash=sha256:bdc2d273b7656d862c7295d2dab45987a2c22197d60f1cad14c8b86c5af0b452 \ + --hash=sha256:c71e619d9965375241566beda8358031fdce8cb136310d4f5b71faa88f278ad1 \ + --hash=sha256:cbe509350d66249fc9e65ece4e7b1855d650f18e477721887128452b95c44b24 \ + --hash=sha256:d5c50b755d781efebe4f9c70ee6d00858e44d95f0229b8b5174358f861d9bd7c \ + --hash=sha256:d666690f0a53ae3df4be2c18af6820369a74c9c5b8960e8760f0f8c9a8f15b36 \ + --hash=sha256:e23ea15cebe5f5be5d11005043d7b5ff56848e39499681ef52f8227bd385bd51 \ + --hash=sha256:e956fa8a953b7eb8580658d68cdf37e410356032c17697a06e44d0e8c4084a0f \ + --hash=sha256:eae71e1c5944ade976ce8cd1e780bed9b5c31d6d4f89cb7263e8dcff78f6cd8e \ + --hash=sha256:ebfb22202530863aaf00ebb9352fd17cdd1066aae50986006d76b10c1e8506e9 \ + --hash=sha256:ed098bea084f8d626d91facfebdc8fe49115b96a339465db6fea681635fc7440 \ + --hash=sha256:ff634b5edbbf76c56ae81397aa41500a66ad0aa48b1856dcc0cf31b159172dc0 + # via daytona +openai==2.54.0 \ + --hash=sha256:89089789197ccdb87f173a03145ed1598d00795220c93e96cf712b1cbf5e5f2b \ + --hash=sha256:e3e6f8bc1ba30ddf381ace1a14340eed381cb984a1a59bd0f34b5be3b5d49cfa + # via + # litellm + # vllm +openai-harmony==0.0.8 \ + --hash=sha256:007b0476a1f331f8130783f901f1da6f5a7057af1a4891f1b6a31dec364189b5 \ + --hash=sha256:029ec25ca74abe48fdb58eb9fdd2a8c1618581fc33ce8e5653f8a1ffbfbd9326 \ + --hash=sha256:0a3a16972aa1cee38ea958470cd04ac9a2d5ac38fdcf77ab686611246220c158 \ + --hash=sha256:39d44f0d8f466bd56698e7ead708bead3141e27b9b87e3ab7d5a6d0e4a869ee5 \ + --hash=sha256:5cfcfd963b50a41fc656c84d3440ca6eecdccd6c552158ce790b8f2e33dfb5a9 \ + --hash=sha256:6e43f98e6c242fa2de6f8ea12eab24af63fa2ed3e89c06341fb9d92632c5cbdf \ + --hash=sha256:772922a9bd24e133950fad71eb1550836f415a88e8c77870e12d0c3bd688ddc2 \ + --hash=sha256:8565d4f5a0638da1bffde29832ed63c9e695c558611053add3b2dc0b56c92dbc \ + --hash=sha256:a9b5f893326b28d9e935ade14b4f655f5a840942473bc89b201c25f7a15af9cf \ + --hash=sha256:b4d5cfa168e74d08f8ba6d58a7e49bc7daef4d58951ec69b66b0d56f4927a68d \ + --hash=sha256:c007d277218a50db8839e599ed78e0fffe5130f614c3f6d93ae257f282071a29 \ + --hash=sha256:cbaa3bda75ef0d8836e1f8cc84af62f971b1d756d740efc95c38c3e04c0bfde2 \ + --hash=sha256:e4f709815924ec325b9a890e6ab2bbb0ceec8e319a4e257328eb752cf36b2efc + # via vllm +openapi-pydantic==0.5.1 \ + --hash=sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146 \ + --hash=sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d + # via fastmcp-slim +opencv-python-headless==5.0.0.93 \ + --hash=sha256:030ca5e0837a2963ab36ef896baa9767eb8d2b83353fb28af5a521e40dd8756f \ + --hash=sha256:09a872a157c1376ab922a69bbf22f9a95bcc7b658a9d8b436a60212b02b2eeb4 \ + --hash=sha256:10818d91510e05c04568ae12b5cd120779c70c01bf897b001a6221fe430df80f \ + --hash=sha256:1e55af3abfb462eeeabe5c775f12bdb36216d8a93a3583d69e6bd6e1d6ba7d00 \ + --hash=sha256:829717b6a95554f273e49e357cee3b3a2a26b6f4842fbc1bed2b45bdd8f87e0e \ + --hash=sha256:840bd717c21e5c11cadadc022a823315ea417f961213d06b4df010e019eb16f4 \ + --hash=sha256:b82f9831daab90b725c7c1ee1b36cb5732c367096ac76d119e64e14eb70d5f3c \ + --hash=sha256:c6bcd96b185975ea240d22cfdb15a1f6d080cc95264cfbe2621f21bb144d89b9 \ + --hash=sha256:ed709fdf9aa0bd1f2ed8549e71d19449b03a675bb581eb292285f6861953be37 + # via + # mistral-common + # vllm +opentelemetry-api==1.44.0 \ + --hash=sha256:67647e5e9566edcf421166fdf022b3537f818635daa852b289e34604dc6fb33a \ + --hash=sha256:94b98c893a91b88657eaac1e3ba89618cdb85be6918196705354f34728b2cdef + # via + # daytona + # fastmcp-slim + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http + # opentelemetry-instrumentation + # opentelemetry-instrumentation-aiohttp-client + # opentelemetry-sdk + # opentelemetry-semantic-conventions + # vllm +opentelemetry-exporter-otlp==1.44.0 \ + --hash=sha256:4a498fa8d8fd8be9e8e2d175fe5524a3fe581ccffadd8509db86526a5fb97051 \ + --hash=sha256:af1cde7c33ea8ed624bf04ac49a885730fe44c1f1ad698656e592c38f70ce106 + # via vllm +opentelemetry-exporter-otlp-proto-common==1.44.0 \ + --hash=sha256:9a9fe61bba73d802904bc989f1d6b4a7b1ee40f06c40e98d6f85af65aaebb694 \ + --hash=sha256:dc87a5a5bc58f149a56d1547e4691588fa12994cdc3bc039a694ccb3375862ac + # via + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +opentelemetry-exporter-otlp-proto-grpc==1.44.0 \ + --hash=sha256:40d1ae9e03fcc36de3cbac610cc99f35894938bff9cfd90fc4ec68bd85448463 \ + --hash=sha256:6a1a645ea182a2f59440c51fa8301d309f3324a8f9d65f8395584b064b67ee4e + # via opentelemetry-exporter-otlp +opentelemetry-exporter-otlp-proto-http==1.44.0 \ + --hash=sha256:838592fce774c1c8bb7b9a0a7facbfa82e17be5a8a4e94cef10cb84ae026bae3 \ + --hash=sha256:c633d7270ad6b57cd4cfbe8b0007a9e2e7c0cb50bd6c50fe2a7b245f721a09d8 + # via + # daytona + # opentelemetry-exporter-otlp +opentelemetry-instrumentation==0.65b0 \ + --hash=sha256:071d9d9eced9bd6460444ec3b0c77229870ed05a881c22c84fdede58e4eed09b \ + --hash=sha256:ea967a72b9939b5fcfdad572753b4306c59dcb99e3f382d95dae04286805e137 + # via opentelemetry-instrumentation-aiohttp-client +opentelemetry-instrumentation-aiohttp-client==0.65b0 \ + --hash=sha256:3a060efa53fa44d02ba7372a7ed2b42cdfa6be6df81b089845067ad840e25729 \ + --hash=sha256:85906a2806ee5641756b5c33274e9aa75c3cc2441e3b830aa5804cf0e1fa9dd1 + # via daytona +opentelemetry-proto==1.44.0 \ + --hash=sha256:898b155a0e1557afd867478fb6158e8122a46329ca0bb8dc53cc55e98f017f56 \ + --hash=sha256:c547a79c2f8c0c515d31509154682e5921c7cfd5ca67b70e1f9266e2c3e103f3 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http +opentelemetry-sdk==1.44.0 \ + --hash=sha256:cebe7f65dc12f26ead75c6064de12fd2a9052e5060c0272d402cfa203aae123b \ + --hash=sha256:df081c4c6bcfdb1211e3e86140376792643128a25f8d72d1d27675936e7e96ad + # via + # daytona + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http + # opentelemetry-semantic-conventions-ai + # vllm +opentelemetry-semantic-conventions==0.65b0 \ + --hash=sha256:1cacde7b0ad306f84c5ef08c3dbe1bbaf20165bba6f8bff43b670e555a086bcb \ + --hash=sha256:f9b2b81e9d5b64f11bc952075e7e9c7fb0aab075c7fd1c46d597f1b919852d60 + # via + # opentelemetry-instrumentation + # opentelemetry-instrumentation-aiohttp-client + # opentelemetry-sdk + # opentelemetry-semantic-conventions-ai +opentelemetry-semantic-conventions-ai==0.5.1 \ + --hash=sha256:153906200d8c1d2f8e09bd78dbef526916023de85ac3dab35912bfafb69ff04c \ + --hash=sha256:25aeb22bd261543b4898a73824026d96770e5351209c7d07a0b1314762b1f6e4 + # via vllm +opentelemetry-util-http==0.65b0 \ + --hash=sha256:7553b606f963097cb190536dc30556cce85090692e471a422fff30ca29b04348 \ + --hash=sha256:84f82d826978bba416ab453460ff6a7391cdc3534c93a786595e4068680016b7 + # via opentelemetry-instrumentation-aiohttp-client +orjson==3.12.0 \ + --hash=sha256:010811c1b69773450a01cef97727a67b223242f350b77d4ca000e59a9ef2155a \ + --hash=sha256:01efac2074fffb4cb1ea3fab7861e9d0f2a26913854a972f5ac760525dbdaf6e \ + --hash=sha256:03091c8a64db4be38746597ceea68f33c238e27acd9bfe99fb59420224ae7a55 \ + --hash=sha256:08231552159be266a7269555bd9f7c016aee7d9ad6dab06eb58796c5ccb7101c \ + --hash=sha256:0b1ac5bf6609b2716c7954011c5fef6254922df029f45d032ee4ebf5d363cbed \ + --hash=sha256:103b5db66aa53c1f9e88c2524be4f383e831ba7dfd5f9f5af6336a177c622f11 \ + --hash=sha256:1192a7021b6d071aaf909864f6e924d6a2675ca360485b972b8401749311750b \ + --hash=sha256:11edb4660a6680abee9788a3a9072208a2c96538cc1322bd79542065229d8e54 \ + --hash=sha256:18a87929f31d94a77f7dc93cf527e91f39ce7fe7813d588a4de2507efd32a387 \ + --hash=sha256:1c680706fc8396d95e7c4c1f9482563f552137aef91b57237a3ad5aaf64629df \ + --hash=sha256:2b7bcefb9f40fa242fa6b06377232c048e655747790829609168c01162f60578 \ + --hash=sha256:2bb3ce43203936072dd8b4917b01d3aecfc02329bfb42510cb7cfb24708adc9c \ + --hash=sha256:2d3a9da945a4d96ae758fdaaca56742e6b73b6fd554c5d8876f252a6dad70b83 \ + --hash=sha256:2eb5c56e534127b2b8fa38d2363c8b1b8190367ee0d1d16c041517d880843b94 \ + --hash=sha256:31ed278a36304390adc3eec5d7f6fd593a7c3e99e5a06cd07866396c4b1b4710 \ + --hash=sha256:33efefcf5d88eaf400b47e2eba02f91f319bb9951be61ca500b7d536d3f2079d \ + --hash=sha256:3bb17a06f9bd15237b3216c044209fe92597379124018cfc196fbb846cde64df \ + --hash=sha256:3dbce9b6b3074b31a5d5dd322a9c4e5b16f206091ece4194c2e36952847a105e \ + --hash=sha256:40f92192227505acca4e2533ce565f8e6b9535f7d0d09b0968452f18b7376b38 \ + --hash=sha256:477ecaf6b9f88f873341b91fcc736119ca81b5e002a9f7f308ff5b4f2ce2a70e \ + --hash=sha256:50fae885cb073eac7556353ff3df93312b0d5137b0a5056b2bb63f97ed9a93c7 \ + --hash=sha256:532ff8cd4bd59a327a953a7dcde922c7fc25b85e29721bb8633265430d3a3873 \ + --hash=sha256:53c0c474a9d9aff9aebfc0c88de1f28f843d940e6e3a80729abdf6a20274356f \ + --hash=sha256:58c58e1de0006ffb580368d6793c36c7b0b021db066479cf281bf5061e732328 \ + --hash=sha256:5a0fdbc216388f653d3752ff310e710f59253bd4ed6a2bfb3f4f06b84714bbd8 \ + --hash=sha256:61318b6de893c7a9d9f3e5ecbadccbfc26a7eb417ccc7bbf0771de3b4d72f868 \ + --hash=sha256:644d005bc82f917337a95ce270c9f6f92f9834c2bed7b1477572f8db00784222 \ + --hash=sha256:6a2a79c89984dc719817d388c8709e0efc2a2795a934eaa746b4882eb6045adc \ + --hash=sha256:6a31348d7dfa64cd9c78bd1f510ff44c48fe64d71094e6b90e364dba3b55949e \ + --hash=sha256:747843254519dd43b93eee3153a19e5a509334320c4d2f823ec879232db5c796 \ + --hash=sha256:784106539f4b9d4b930e0b4eb8d45168507dae001945e71b4675a367f1e5e806 \ + --hash=sha256:7c2ad193c8004254f34b499f3bd2c80f043d10754aff2b38f93da574f4883f98 \ + --hash=sha256:83445adc40cba26d6d621185a45128ce455b766af368cad2ab64b970603a7978 \ + --hash=sha256:859fc4196855890150bb08e649b30d2c93b249b3e3edd0d3bb2231abf8aa8adc \ + --hash=sha256:8c3bb86dd10f39b3fbf434b7d5dc7cac77d6fc8ac572ae30a10731ede2c4b647 \ + --hash=sha256:8e29957429c35bbb5a185a119c523aa2428b7bbf1a293724c7b9375ed8f892a3 \ + --hash=sha256:8e386b0bc0ddd7cd2056f884b5a0af33592bd01ac66a7ca4b42a65a7e7774a13 \ + --hash=sha256:92ffc09e07233a6ab6d4e067f7841edcbcc134cb4812155cf171ea5255a421d7 \ + --hash=sha256:9a36ec60f1796f9a3f13e3b98390295e17a1c7c10155b448d264098bf9ee5900 \ + --hash=sha256:9caf3d09f47c3c70c4451ada20ef9bc4a4cdffa26f49862cf0a253b329aae2d5 \ + --hash=sha256:9e6fee342a48760e854d743e7a81534d8e2925a6f46e09f750cf56b50fd1de5d \ + --hash=sha256:a15f9a891bce5f5cc5d210e3ad8614d4d1b489a56448c099d6d2a7168b2d954a \ + --hash=sha256:a696529ec96a90d9a5f9570207efe403c8b08f8e4aa2783ee3403511e2fdfa10 \ + --hash=sha256:a6cf4b18e7de173f209f2084ffbd736dd72389a396326ee80a7022168be232e5 \ + --hash=sha256:a791f793b287bbc135b8e87c34e35c8bfc693e2a8a620fab1ae682b925f9a32e \ + --hash=sha256:a94f0f0c6fcbb2b5bd9734c57a489c7584a732bbdf04a39e8c83b861e9d03e92 \ + --hash=sha256:aa3e43a6846e91d7bde3d5a9c66090fcd8744f569a9b6cffc5e1ca38f6a461c0 \ + --hash=sha256:ad0422b92d5195443a39f80c3bcf731cc2e00f153bd32063a47b73b057bd0f03 \ + --hash=sha256:ad29eece0c601737f2a60edc2752a84e7a0785df3efb62e3012834700a5afe0d \ + --hash=sha256:b85931be5b6763c31283805c9bdaae1ca03ad9f6f12a15f1cbf6745b907932c2 \ + --hash=sha256:b9dca132b1fda5565088e65a6b6e742285e0aeceb6fae549fa8863e16c7d3998 \ + --hash=sha256:bc7a872f03522d90e0429e6c0c5cd23084f767bedcb4c58048eec19294613344 \ + --hash=sha256:bd57d79aefa3f84eec851d6de7a366795b9345cfaf17f82b4820430a7a5fa241 \ + --hash=sha256:bf44e374aadde77b1f6109f1030be51433eb61984379852766b6f4e187db7b1e \ + --hash=sha256:c6b11be792c3d2c6a4be2af4ebf97a68d0bf5f580aca6e86a418a354f6cc846a \ + --hash=sha256:d14203fb1aae2ad9b3d52f8a0e82aeb10197ef1c9bc61da7f358bd70b00123d5 \ + --hash=sha256:d39f3f5c3927e2dc0913fe5bbc1a2f6b1b9d1bba1de6358340d0ad0d0c00ca92 \ + --hash=sha256:d8e78d3d93705e3d27cc17cdb209e44d7a8ea203010cac6ce9c7ffc1ae1996f1 \ + --hash=sha256:dce0166feb0a737ab84f598c9a338cbc0b764a036617aa686194f53c7eba0c3e \ + --hash=sha256:e4ac5059baab4b3acbd99485de019ff8cda0fdf34b61fa74f7197a53db78bfe8 \ + --hash=sha256:e9683ee9ea0659da64f36574ef675b8a86330c34c19ea75db1fb93c3ff99e0ef \ + --hash=sha256:ed4ca42bd55955aa34deedcfdfd0e0c31abf51143aae158ae2bc3520b626e517 \ + --hash=sha256:f06dd838d1e07d9b1de0932ec0485ec92c4d5f5d1ad4817a656268c3e88be1e1 \ + --hash=sha256:f3c0683136acdc29afdf88a5bc2f7d3d0e34087788d1d63c0144b805a87a196f \ + --hash=sha256:fb2539159dfe8d371914f354360fa50e4a577cc89222a3828b9650a5e5040252 + # via + # gradio + # trackio +outlines-core==0.2.14 \ + --hash=sha256:056f656ea6e4807338963377afb50b9d936593ba3545a819f1aba56fd6e14920 \ + --hash=sha256:060a0174a6262bfd378763f210e374e52011776849a3a767df9863fb6839c142 \ + --hash=sha256:0cd8ce3ce61df44fd9c5450d9744e2280586c2a6e6e3dfefa0dab1944764b424 \ + --hash=sha256:0e4b5b7c8e50489bea444b095692ddb5d8fb92ea6b949c4a6a3381eca9b691a7 \ + --hash=sha256:0e4c69f0a8565edb56464c4c9b6c291a10805f3a96dff84182980e90ae1a5e2f \ + --hash=sha256:1de34681c7e0e7e1551fc9036e4fa3c57986336c905a10536591ceb6d869c258 \ + --hash=sha256:3bb2060c240c4507f334965a8948dbeeb22007560d797f6debd92346c0b620cb \ + --hash=sha256:3e67fc23b1a3ac9562488fb50f409c171538b76f64aa5f7e25d9b0bf14770204 \ + --hash=sha256:4daa22d677dc6a74c44f9266ec9e3151332dcea4250dd019ea0c75b98ae32938 \ + --hash=sha256:5f8893cf24e4f3e5a7b246b578079ff0dff3228aa9c731bd7fb1f3f55ebee19e \ + --hash=sha256:615566bf8257d2bba8ac192cdfc29d1c4357f57b53672fbd622e821215e4f1bd \ + --hash=sha256:63f53cfd9614e754499ae86dd699f3abcecf42d6a4e58d80fd80347881d85960 \ + --hash=sha256:6453e23f01d98ec48e3a4141d7112792ce77001dfb28d91d6fd89f47009f91ef \ + --hash=sha256:64808deed1591ca3029ff64346ceb974cd5d780c916ea82504951fe83523039e \ + --hash=sha256:66e695b375b180725fb534d9adf298531c152ec3d881e3b9e01c82b5dd269f52 \ + --hash=sha256:69410e5b55bcbaad8c865d94bd01e7bff8a57996dcd2251b7d50dec70d7d9a63 \ + --hash=sha256:6b149452f7387501252f46f2c81847067e0820d7bff8b2b4201084f559d2300d \ + --hash=sha256:6bd166d3b07acef2f60d4ede44592a26d3f7d8712876bfc8e22150045def5857 \ + --hash=sha256:7770b5e0497e6f4548a8923299d4438d7dd61dc17c2f58acfd5df4d3101bb991 \ + --hash=sha256:7a52a56b2b627cec2d824af063a5020bf6ab9090cf9c9b4f20dcf1407eee80fc \ + --hash=sha256:7d14c48649d5df5c488b16c67493385cf8b3fe71da14466843e294c562417f21 \ + --hash=sha256:7deef6df74cb247f2a3a62f03438ba967456504b0555ec7029f8db834e054448 \ + --hash=sha256:7eba2b41dac03d6e6e8d5ea0aecbbc03dacb4c57de3b1fc944d0bafb022941f7 \ + --hash=sha256:813b28813b22025c3d079b3b8a20cf5a28c6d5ba29ec21c5b1093442aa5d4e91 \ + --hash=sha256:81d01cfae29de5671bc5013fd6b2008621157bec3d8be284da7da2dc0672745c \ + --hash=sha256:870e8e038853818cb202ccc8cde92251f300f96805bfcc3be1c883adda7b5297 \ + --hash=sha256:87b42440478764cce1353a87d8560ef82f3b39b9d753bfe93195ea3584f369e3 \ + --hash=sha256:8a5e5f34961fe4d04c389d00f92d624c6318ab3ff00467fbf7c93324458886d9 \ + --hash=sha256:8b3e8d668188282a1f7666732bb8a01958ab134db35bb792e7442a40e55ff1e7 \ + --hash=sha256:95e6476d9702d2fcc4e85370dbbfb6933a46c816e9c90107f6ce36eb68b5d64a \ + --hash=sha256:98d1929a7a0ff43332448c176b92041c2755c4843a22223d341c3b96165c2a37 \ + --hash=sha256:9d45462d7548aa0e17176a691ae73447f3e6bed9658a0cd96fe72eadf7474475 \ + --hash=sha256:a2795dc2047821b229457f941a303639e0c14e4c3c5718797540a27b529a062e \ + --hash=sha256:a646a43a482aa0b4a2b286735efa476b31cecd0032024cba4bb9a5d62623ef45 \ + --hash=sha256:adf96395759d7fdf6efeb8a67d3f36f520c1546bfd4df0752306db8c7cb7d6c5 \ + --hash=sha256:b02bb0fc21c5e23e2ff9b2d1459db2c1c3e813a7646c9d5db091c6931edb9c85 \ + --hash=sha256:b582b5d2f773cff966f37d7a5680d97506792647c93fb2e522283e8a14726e9d \ + --hash=sha256:babf97a54662330c55a79fdcab8994f96faa6dcb71b458d4b18c4fb538f5d461 \ + --hash=sha256:bb008c7ecc034bcfda0ddc10a4d1f2181a4b61ec1643ee56183dd6fa64139c9d \ + --hash=sha256:bb3245cbd7a2e9800f257b45cb5a8d690abd39dbaa8371ea40132f6f1eb2e3d2 \ + --hash=sha256:c76f28feb6ea71b1ff4b0ba5901dc383273a32b156213dc1bc753fc634645a1e \ + --hash=sha256:d1776ae984574461f249fe590314a439992eb9b883f4091b8fa7fc56f29f3717 \ + --hash=sha256:e604925d6525f669253160568397df6d6c8124b2e01f1fde553e3b9f28ce9e21 \ + --hash=sha256:e75395b1cccecdf85d8d8265aba28841ddeb1e8da406f4b1e0135df5a6e9960f \ + --hash=sha256:eb27e92204b296a063ac58f361153be4e78c8103a96e0b1c085b22d4fc3534cf \ + --hash=sha256:f04731a5e29a190e2cc9f692a1f3fb2414a645355ca7d01b83df43439c38bea8 \ + --hash=sha256:f0e5037153b5b3abfb617f6dfdc3ff28b6fab50f0de5936ea6995f5675d23e0b \ + --hash=sha256:f753edd430ac27e6dcde5a614665888db72b78c666aa160c478afd1eb986fb8b \ + --hash=sha256:fd7d432817978ea474de428ed6ca5ed3195812f8cfb787b85fb5ec0a51a440d2 + # via vllm +packaging==26.2 \ + --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ + --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # accelerate + # bitsandbytes + # datasets + # deprecation + # fastmcp-slim + # flashinfer-python + # gradio + # gradio-client + # harbor + # huggingface-hub + # kernels + # lm-format-enforcer + # opentelemetry-instrumentation + # peft + # transformers +pandas==3.0.5 \ + --hash=sha256:08d24fe11a17dc33bd6e937dc9c665f9cba08fbdc9f657f405713515febe300d \ + --hash=sha256:0d298e951f23016ce4699951d044ae6418dbc91bf68cefca0f77666fcbb4e5c6 \ + --hash=sha256:0fac0010c75e4efb6b99e249c183a8993ce0dc95c240f9b120a5e67c727b7928 \ + --hash=sha256:1c10461f6eeb35d8f05b6184c65c8b9991663b66c46b1d559b682cb34ae7c6ea \ + --hash=sha256:25ff585b972a18ef1fe9ffa3ac6544d9950508aa76832e5147640b6022821e49 \ + --hash=sha256:2946e77e4a53cd248cbde631a12f0e51c8324ce354c3eba4d20147c1ad6f4282 \ + --hash=sha256:2a29c53d85ea98c5e792c59ef82ee9fbe6ca902c0d0adb6b23f45ef894cd7bf6 \ + --hash=sha256:2c0cf1dd9b55a22d105fc46c1b489af3bd42264fcba7c66297bf47a9a1d9c78a \ + --hash=sha256:2f264fc46911cc8131a7322a16199bbf8e353d27c10bb211f5bd0c814324dc36 \ + --hash=sha256:303da736987d481074ca720ada325f8bd80c64ebc2d45ed79b29df3aaa4a26ca \ + --hash=sha256:3b2801bbb049d0136f6c213eae02b5fca969384fc2064dd728d8620552aa49da \ + --hash=sha256:3c5015fd1730fbf883647e88068176c839c102cea883ba1769a6f4593bfc1f8c \ + --hash=sha256:3c5ed2e7c06e91d340dfd091d7934f9bc82e4a36b95f647f090b9d1c9ac649da \ + --hash=sha256:4b11c36e218331d0387cbe3a0a5f75162357a1d92d57b2b08a336ff94b19b2be \ + --hash=sha256:5183427f5a8156d480f30333777bc978be93650a49a7c01db26adffe95b31e85 \ + --hash=sha256:53730687fcd161883b24e10411c06d6a4c0f2275d2faf3bb2bc25deb4ba8007c \ + --hash=sha256:66266d3442a5e8b3c90274c2b8b230bee42dd1c286bc822cc2f9f2c7e12b883e \ + --hash=sha256:679f4e85b30ddb1515458ab1e788d3e260eae369b1f78da7a3aa4cac8ebf4a2a \ + --hash=sha256:71ecc8fb7ed1a7aa4392316b5309a6347e8e7f832f38fd897846b3a1457a9298 \ + --hash=sha256:73fa87b08a7ef706f8aafda39ddaccf2a99047bea62d8c88a0361bcafb2237bc \ + --hash=sha256:80a611068e8a3ac23f7398c6c14eb46dc974e5cc9997f653e2dcfd1da74edd41 \ + --hash=sha256:960d3ebcf249f75206899fcd2c6de53f736b7265759ced0d3e559df0b8b709b0 \ + --hash=sha256:9e94c2c5ca43bd3ca32bf64d32308887b65e5f9bfd8023ea52755107a999f93b \ + --hash=sha256:a5ad3b02ed6bc7d7ae9b70804b2c6aa31827489d150f8e623ce82491b82085d7 \ + --hash=sha256:b1261758dfb6cf12c3cff8300e21cefad30e7ec709abb4c24ac7318e6a52462a \ + --hash=sha256:b173f5951ff6b8b0ec7675e20dff3c97b7e7a57dfcce387c2d7c5afe87cb7899 \ + --hash=sha256:b2acb4650527eec6822c3dadb2b771277b65e7dae7a267d4bccf65fd1bb3fbce \ + --hash=sha256:b58b1b39d46a5862e3fb18f50d1a201398619d16a0f9f73f57eea5583cf0e63c \ + --hash=sha256:b86765f268b56f7e665b93bce9d5df69dee7f99e595cf8fb839483ab315942a3 \ + --hash=sha256:c1c05a767fe8e5b4fe9e1c29806829c582052eaedb9120a3da83ba3f69e24a5b \ + --hash=sha256:c2e26bb46934b8a2ca0c3de1d3d606fc5f6746584791b2db264d58cf370e08dc \ + --hash=sha256:c597ecf5616b5c420372c1d4d4c00dbbfba7398bea857dcc984347e1ea48417b \ + --hash=sha256:cce3a9d11d2b1f82c69a27ec1f4948a170e2c403c4bbfa8cca62e3fdebe2ef3a \ + --hash=sha256:cd8f7c6dc98527058ee6264219343f5392240a6f1bfa654fc5d79023020d0c92 \ + --hash=sha256:cf52e1f61d229496da17dc7ab54acdee627357e7008fd4fecba3d0ba2937fa58 \ + --hash=sha256:d373ce03ffd84010ed9839fa73672a9c8256990532e158440c0085db7d914b34 \ + --hash=sha256:db172144bb56422bd157812f3b021eacc255451470b31e2c633c349490a1cfee \ + --hash=sha256:dca3734d6ab7c906e6730f0788b0a1dbb9f2467731f9711f77995c8e9d62d712 \ + --hash=sha256:e2759e890db96dfcffdbd9b86c3c2cb6afaf58def482820317e06163ec1066cd \ + --hash=sha256:e819dd5f62966b481a8cb649d3299ebd886a1ea91ed5a99bf7ce77c98d18ab94 \ + --hash=sha256:ef01af4d8dc6cd2c8d6c7736f149574ef93fe043811eeb5e445f2647154b5040 \ + --hash=sha256:fa290c16964d4963fbfbc358928239cf3bd755b20e988ce944877def2f44471d + # via + # datasets + # gradio +partial-json-parser==0.2.1.1.post7 \ + --hash=sha256:145119e5eabcf80cbb13844a6b50a85c68bf99d376f8ed771e2a3c3b03e653ae \ + --hash=sha256:86590e1ba6bcb6739a2dfc17d2323f028cb5884f4c6ce23db376999132c9a922 + # via vllm +pathable==0.6.0 \ + --hash=sha256:6404b8b82aef5ff0fd478934137128b99b12212ba35afdde5525ca4f8388ea58 \ + --hash=sha256:82c4ca6c98c502ad12e0d4e9779b6210afee93c38990988c8c5d1b49bdcdf566 + # via jsonschema-path +pathspec==1.1.1 \ + --hash=sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a \ + --hash=sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189 + # via + # harbor + # scantree +peft==0.19.1 \ + --hash=sha256:0d97542fe96dcdaa20d3b81c06f26f988618f416a73544ab23c3618ccb674a40 \ + --hash=sha256:2113f72a81621b5913ef28f9022204c742df111890c5f49d812716a4a301e356 + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +pillow==12.3.0 \ + --hash=sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756 \ + --hash=sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a \ + --hash=sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59 \ + --hash=sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45 \ + --hash=sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3 \ + --hash=sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df \ + --hash=sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139 \ + --hash=sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b \ + --hash=sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39 \ + --hash=sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e \ + --hash=sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8 \ + --hash=sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1 \ + --hash=sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8 \ + --hash=sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89 \ + --hash=sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5 \ + --hash=sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130 \ + --hash=sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd \ + --hash=sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d \ + --hash=sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b \ + --hash=sha256:25b9b82bb22e6e2b3cd07b39c68b7b862001226cb3dff7130d1cb914121b39ed \ + --hash=sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace \ + --hash=sha256:300557495eb45ebb8aec96c2da9c4be642fbf7cd937278b4013ba894ea8eb0eb \ + --hash=sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931 \ + --hash=sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510 \ + --hash=sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6 \ + --hash=sha256:37dc8f7bbb66efe481bb60defacef820c950c24713fb44962ed6aa2a50966de1 \ + --hash=sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce \ + --hash=sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385 \ + --hash=sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e \ + --hash=sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c \ + --hash=sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7 \ + --hash=sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace \ + --hash=sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c \ + --hash=sha256:514435a37670e3e5e08f3945b68718b6ed329bb84367777e16f9f4dfe1e61a0f \ + --hash=sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64 \ + --hash=sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f \ + --hash=sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a \ + --hash=sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827 \ + --hash=sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17 \ + --hash=sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4 \ + --hash=sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a \ + --hash=sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701 \ + --hash=sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e \ + --hash=sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91 \ + --hash=sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66 \ + --hash=sha256:85f998ea1848bc6757289e739cfbdda3a04adfd58b02fc018ce54d754a5ce468 \ + --hash=sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217 \ + --hash=sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658 \ + --hash=sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418 \ + --hash=sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a \ + --hash=sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c \ + --hash=sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330 \ + --hash=sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402 \ + --hash=sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09 \ + --hash=sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930 \ + --hash=sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f \ + --hash=sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec \ + --hash=sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a \ + --hash=sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94 \ + --hash=sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468 \ + --hash=sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b \ + --hash=sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965 \ + --hash=sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8 \ + --hash=sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd \ + --hash=sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7 \ + --hash=sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c \ + --hash=sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777 \ + --hash=sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35 \ + --hash=sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9 \ + --hash=sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f \ + --hash=sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f \ + --hash=sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0 \ + --hash=sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c \ + --hash=sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71 \ + --hash=sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3 \ + --hash=sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838 \ + --hash=sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf \ + --hash=sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321 \ + --hash=sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26 \ + --hash=sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec \ + --hash=sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9 \ + --hash=sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65 \ + --hash=sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5 \ + --hash=sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e \ + --hash=sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d \ + --hash=sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198 \ + --hash=sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7 + # via + # gradio + # mistral-common + # torchvision + # trackio + # vllm +platformdirs==4.11.8 \ + --hash=sha256:52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081 \ + --hash=sha256:f23abafea7dd4276d1f29104b83598d7dcc567cafd07c9c951e66665645437fc + # via + # fastmcp-slim + # harbor +postgrest==2.31.0 \ + --hash=sha256:2f395d84b2ee34fc57622ff2f711df603e2ede625f98e5015240741888f7bd0c \ + --hash=sha256:c2fd47c94e13ee8335111c4f03c9a24ea9766ce9d35fc3cd7330057c9e7ea0c3 + # via supabase +prometheus-client==0.26.0 \ + --hash=sha256:04a91bcf94e2cf74a44a1a874d651a2e853ed354b6e822f3b7487751465d5c2b \ + --hash=sha256:fa93d06737aa02bacd05794768508bb97d2fbee28cb3bca04eaae92f0ca953d6 + # via + # prometheus-fastapi-instrumentator + # vllm +prometheus-fastapi-instrumentator==8.1.0 \ + --hash=sha256:b77f3043665e8d28e2bbd21017506195a43d9adf1d402d01bf95b494b7e560e1 \ + --hash=sha256:b9f40b2cff3f7891ca0610b3ae4fc6ec723fd326b04bb659819aaeb821a0fc7d + # via vllm +propcache==0.5.2 \ + --hash=sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427 \ + --hash=sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5 \ + --hash=sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa \ + --hash=sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7 \ + --hash=sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a \ + --hash=sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0 \ + --hash=sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660 \ + --hash=sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94 \ + --hash=sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917 \ + --hash=sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42 \ + --hash=sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3 \ + --hash=sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa \ + --hash=sha256:1ca071adabaab6e9219924bbe00af821f1ee7de113a9eca1cdc292de3d120f4d \ + --hash=sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33 \ + --hash=sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a \ + --hash=sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511 \ + --hash=sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0 \ + --hash=sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84 \ + --hash=sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c \ + --hash=sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66 \ + --hash=sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821 \ + --hash=sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb \ + --hash=sha256:2f8ea531c794b9d6274acd4e8d2c2ebcac590a4361d27482edd3010b79f1325e \ + --hash=sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853 \ + --hash=sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56 \ + --hash=sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55 \ + --hash=sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6 \ + --hash=sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704 \ + --hash=sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82 \ + --hash=sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f \ + --hash=sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64 \ + --hash=sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999 \ + --hash=sha256:4621064bbf28fa77ff64dd5d94367c04684c67d3a5bf1dff25f0cd0d98a38f3b \ + --hash=sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb \ + --hash=sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d \ + --hash=sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4 \ + --hash=sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab \ + --hash=sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f \ + --hash=sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03 \ + --hash=sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5 \ + --hash=sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba \ + --hash=sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979 \ + --hash=sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b \ + --hash=sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144 \ + --hash=sha256:5fcb98e7598b1ee0addab320d90f65b530297a867dbfe9de52ea838077e16e3d \ + --hash=sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e \ + --hash=sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67 \ + --hash=sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117 \ + --hash=sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa \ + --hash=sha256:6bf3be92233808fcd338eba0fb4d0b59ec5772af4f4ecfcec450d1bfc0f8b5eb \ + --hash=sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96 \ + --hash=sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5 \ + --hash=sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476 \ + --hash=sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191 \ + --hash=sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78 \ + --hash=sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078 \ + --hash=sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837 \ + --hash=sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a \ + --hash=sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba \ + --hash=sha256:8114f28879e0904748e831c3a7774261bd9e75f49be089f389a76f959dcd13fe \ + --hash=sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c \ + --hash=sha256:823581fd5cb08b12a48bfa11fe962a7916766b6170c17b028fbdf762b85eb9bf \ + --hash=sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c \ + --hash=sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9 \ + --hash=sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8 \ + --hash=sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe \ + --hash=sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031 \ + --hash=sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913 \ + --hash=sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d \ + --hash=sha256:949c91d1a990cf3b2e8188dfcfb25005e0b834a06c63fa4ef9f360878ce21ecf \ + --hash=sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f \ + --hash=sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539 \ + --hash=sha256:a0e399a2eccb91ed18721f86aa85757727400b6865c89e88934781deb9c8498b \ + --hash=sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285 \ + --hash=sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959 \ + --hash=sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d \ + --hash=sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4 \ + --hash=sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f \ + --hash=sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836 \ + --hash=sha256:b05d643f944a8c3c4bd86d65ffd87bf3264b617f87791940302bc474d2ff5274 \ + --hash=sha256:b96db7141a592cbc968daf1feea83a118e6ab378af4abbc72b248c895414c22d \ + --hash=sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f \ + --hash=sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e \ + --hash=sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe \ + --hash=sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1 \ + --hash=sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a \ + --hash=sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39 \ + --hash=sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7 \ + --hash=sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a \ + --hash=sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164 \ + --hash=sha256:cc1177027eda740fdb152706bd215a3f124e3eea15afc39f2cb9fe351b50619e \ + --hash=sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2 \ + --hash=sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0 \ + --hash=sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0 \ + --hash=sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335 \ + --hash=sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568 \ + --hash=sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4 \ + --hash=sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80 \ + --hash=sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2 \ + --hash=sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370 \ + --hash=sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4 \ + --hash=sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b \ + --hash=sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42 \ + --hash=sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a \ + --hash=sha256:decfca4c79dd53ebab484b00cc4b6717d8c369f86e74aa4ca395a64ac651495e \ + --hash=sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757 \ + --hash=sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825 \ + --hash=sha256:e4294d04a94dcab1b3bccd8b66d962dcad411a1d19414b2a41d1445f1de32ad0 \ + --hash=sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27 \ + --hash=sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf \ + --hash=sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f \ + --hash=sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d \ + --hash=sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366 \ + --hash=sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc \ + --hash=sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c \ + --hash=sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7 \ + --hash=sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702 \ + --hash=sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098 \ + --hash=sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751 \ + --hash=sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e \ + --hash=sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6 + # via + # aiohttp + # yarl +protobuf==7.36.1 \ + --hash=sha256:0b53ce95272aad50ad25d7ff03373743209822e8ba42ea7fad27d2bee1547d00 \ + --hash=sha256:39c518c05586c016d7874ff6079ee115bcec1ea5fbb1d177fbf7867ef4c67e44 \ + --hash=sha256:3cf2ee25d006cee57294a1196ea43b37feb78e0dcd1e8af5c1aeddb777655aca \ + --hash=sha256:43d3d37b1eb24c113b9b7d02008cac44e423f00b611b7781ae998d7623972969 \ + --hash=sha256:51139351435d9b43d88a55eaa49fb6f737fbb478fb0cbf2cf694d1a04a9d3363 \ + --hash=sha256:7d951e46b3f963d6c264c367c437921de9d5aedd9c3f9612b9077736b4e3ad5c \ + --hash=sha256:97198b77e369a0abd8e262b8f6c7266c55ddb796a3a12c76d7b8881188ed83aa \ + --hash=sha256:d0f6470f0ce2b84e3feaea2d4b816378b37ba4d4aa08a274305373de93e2d524 + # via + # googleapis-common-protos + # opentelemetry-proto + # vllm +psutil==7.2.2 \ + --hash=sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372 \ + --hash=sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9 \ + --hash=sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841 \ + --hash=sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63 \ + --hash=sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979 \ + --hash=sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a \ + --hash=sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b \ + --hash=sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9 \ + --hash=sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee \ + --hash=sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312 \ + --hash=sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b \ + --hash=sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9 \ + --hash=sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e \ + --hash=sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc \ + --hash=sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1 \ + --hash=sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf \ + --hash=sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea \ + --hash=sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988 \ + --hash=sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486 \ + --hash=sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00 \ + --hash=sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8 + # via + # accelerate + # peft + # tilelang + # vllm +py-cpuinfo==9.0.0 \ + --hash=sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690 \ + --hash=sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5 + # via vllm +py-key-value-aio==0.4.5 \ + --hash=sha256:ab862adbcb8c72547d1c57821f22cbbb71ab86509039c96f36e914e0336c8dd7 \ + --hash=sha256:c6563a2c6abe5da5e20f4f9e875c2a9b425a2244a54fadbf46cf140a9eea45d7 + # via fastmcp-slim +pyarrow==25.0.1 \ + --hash=sha256:0b1edbb2f385a6a65e9711b62ba86ac54a7816a3f8d17bb3e8a5929d65fb2485 \ + --hash=sha256:0b726ad7e7b669be982b0c71c07fe4b037d654354130da79a7902a669e93a66b \ + --hash=sha256:0befcf816e45a1af33ac775a9970b749e4868a230c7372f0ae5e932bee27039f \ + --hash=sha256:0fe7c8b6c03969b49c8c66182e4a18e3819ab92d07cfab5d8370c531b9369ef0 \ + --hash=sha256:119297a6dc197e45d9c6d4415f7814a67ffa36c180d26f68c154c58067ae782d \ + --hash=sha256:169d3429d5be7c752125890620f75a60776d38b0035eddae939651640822332e \ + --hash=sha256:25f8720bf6387d5dc2ebd2622112de630760419e4b66134405dd24110d15f37e \ + --hash=sha256:31e49a7888fcdf3a835da33ae777f6bb9a866334e5a789282fc26dcf426f7f15 \ + --hash=sha256:35935cd5de130aa5cf4dea052a63e6bf2e17006c35c3a468194242b9b2bf5956 \ + --hash=sha256:38a9a4b4b9613380e200641891495a56c3d5a98a092db4a870af9975e220471d \ + --hash=sha256:3f89685964f46e4216103c75483aac0c0692a5f72212d7ca835adba5ede56ce3 \ + --hash=sha256:4288f27577352d608ca08553b0865e4a9b3aa14820c5d95b53337218d609835b \ + --hash=sha256:4340f0ba6c1d2e13f21658de1d7c662ca2545018568d0030a1e9afca159d87e3 \ + --hash=sha256:44a9120ce5bd81936b8ab9a88076e3fd47c2c6838e0e43630fed83626aca81d9 \ + --hash=sha256:4facd65742a024a4a366328a1d2292062d72d6e023c1b7dda8d4c37544933a25 \ + --hash=sha256:51093dd9e10325fbdb3c10a2ae7c4806e5c822d94e74ae4938b26524a3323fee \ + --hash=sha256:514ddb60285631af068875550c90eddc181db3e8e63a032b1559be189e82f056 \ + --hash=sha256:5389cdf79447ed1515c9e31620e6e1e2302249564d603f2ad727d4f6d313e4c3 \ + --hash=sha256:59a2de54c0cbd954da861eee4d1d330f8e909c45b53455baef696380f2c55033 \ + --hash=sha256:60e89d8f13861a1f7f8d950fa54aebb8023b30734d0ac51ffa80beabe2df4bba \ + --hash=sha256:6109c94d8b9f3b17a041daca16cacb2f651ad8f1ef70a4232c2c0f37a23da2a8 \ + --hash=sha256:62cd0d785b8aa6675ee355f9fc02252a340f4441257c42674937826fd7594325 \ + --hash=sha256:6943e2fe7954d29d84de45d29d34c8dc36ce96570e67d89aa9976e650a4a9138 \ + --hash=sha256:6a1fdfc6659b6b19022f2e50627fb5cf7156a66c46bf4299379955cbe742382a \ + --hash=sha256:880523be3d29efcf83d3998835d206118ccf35e3871dbd2fb60408cf6b007a80 \ + --hash=sha256:8858d7bfc22e3f51529aeaa4077225029724623e4595dc9eff8c793935c34140 \ + --hash=sha256:9150a83248bfed9813ea3c3af74c3856c1984d444aa28e58bf7733b9750ddf6a \ + --hash=sha256:9171748cdf796972d85a4b60157c279913e242992e350c90c7450182a9838b2a \ + --hash=sha256:a4d6d5e9a3d1879a97c08ded0c797579b7965eafd0f0c26c30b45ccc06db939b \ + --hash=sha256:a4dd8bf99a8fac133efc0ed6a92f5fddbe2adba0d0f6dd720e39ba9855cea85c \ + --hash=sha256:aa0559502e1cd6254d6814614085dd9c5a3dd0419362978a936a3f68a9e5c3df \ + --hash=sha256:b7a296aac7a71fa0886c08e155ddb6c636a50013f801f6178daafa0f9e726188 \ + --hash=sha256:bddd0c4f7630c2a3ddf6347c1bdaa79d97bcf6bd445f9e60c816b7d77c85a5ae \ + --hash=sha256:bf0b672390cdcb640d7288f96b826d71ff4e9abb254a86c89890baf51a29cee6 \ + --hash=sha256:c7c534ec03c358a76ea3e505e74c1b6aef290af90c444dfd092dbfe23e755b85 \ + --hash=sha256:cab40b1edfef0262e0e5251aa2c58d75630f24d06dd7794480243acc001a1d7d \ + --hash=sha256:cc4aa407fde9fc660be3939e49ea31f50f3e9fec17c0ec63159f7711edd3efc9 \ + --hash=sha256:d51592cb7561e87877c506113e7adbf1342ab579e6c21f0ef44b8ba41cb74c80 \ + --hash=sha256:dda9470024204d7bbf2042b47c6e8a0e47a3eeb8e34405882dfaea6577e0c153 \ + --hash=sha256:df961f2e7ae9cf496459259d798652c70625f6c080650d6952f8c04053c58ee9 \ + --hash=sha256:eb6203482ff3746a5632303a7279ae0b5a304c46985b49ed1378cb350ea6728d \ + --hash=sha256:f3831aaa25c67a99f99dc8b05873cb9d64560390372e2aa197ce9dd4a3f06a44 \ + --hash=sha256:f729cfdbd36fd99d543b67a914d2de044c84ebe45be8b34902b299b608c15c8f + # via datasets +pybase64==1.5.0 \ + --hash=sha256:0030a64fe91791e5e553edaff3a55d319cd07fb5e097b09c5f7f45e4905c40cb \ + --hash=sha256:0202f2e2bc65a97969f6b1076cb4714054f0c770e9a44c883280c1435c82c858 \ + --hash=sha256:0234b8f85c8816e82bbabf67a37014c3aaa2a668d3ab92fb5ef52c511318c84a \ + --hash=sha256:045fa2f3f5da6cfa86822c645b92e18cfc7c13babccb5ceec9bb64a17ac3f1bc \ + --hash=sha256:0578c54f1ae89e6175eddb742dbaf2e95a060735ec11f4b661f762b635680cbd \ + --hash=sha256:0855f67fa47c0bdf237ea875c11afce2a8cd879644b288d3f05ed9effab17953 \ + --hash=sha256:0872880c9150fc79347c658937507033b8e600570569e4494e1230987e91be04 \ + --hash=sha256:08907ffbf8381a017f6332ce02b818e672c73563ec19f38a022a34fd1c55b493 \ + --hash=sha256:0976e9b7465387038868c6b560d7cdcbb9ef5214faf55ae6036e4aa4e93ba423 \ + --hash=sha256:09ba0119df1766bb43ae9774df511b396b89bde68a797119366aca1292f83eac \ + --hash=sha256:0abc0f2312c17765bf92dd382982cca9dc1b0148bf0d708f5f88339d84bb7687 \ + --hash=sha256:0be37689b624ae293394fc826c9a048c6118520d6a962de033ffb054564bf61f \ + --hash=sha256:0d1c371e90556712ec937ded4fe1986176e01ce9568749f98c562115a427ab2f \ + --hash=sha256:0e8691ad425ab8c586ad93a2d71789c6ab86e201377619ea146ab0ed092aa2ab \ + --hash=sha256:0eb9489fe31db090f95affe81fea96c3dab51c24593ce14fef936ce92d802b63 \ + --hash=sha256:1003c3643cb785b90237c9fab9163dbb349b17a774f9421488a2147f7382c134 \ + --hash=sha256:106dc1813dff9ad1e936ab6de486bc0e19d281741c1cdcb3effe31602c571d71 \ + --hash=sha256:107129bf5591f040cd6cfe3b7ea5c1626a2f9610763e54d450778c578ca2b69a \ + --hash=sha256:11dfa286f6c5fe6795430bf08fc44b64c98e208558215b0590c9f28fd99a92e3 \ + --hash=sha256:125945f5b3cde8b79a8f942cfdb0390f4388fb9458a41f5f2a93746e1ef3c546 \ + --hash=sha256:1439d84162a4ee5598ff324b63f651d9c5adaaa9fce271764384cd55f50bcf2f \ + --hash=sha256:14e4eb6091afd1cf956a37a331566c453aa080fd692acfa76f35761a04f19fd0 \ + --hash=sha256:15b0ac4dc01be9a7d2a3e508720a8e3aea9f0dfb1a3dd62b7d5a23f35e76ee7d \ + --hash=sha256:16201c0998c80f0bac0817a792969b7e1f4169014a8a6b32019e005384734805 \ + --hash=sha256:163586e9ec367158b0f744ae12d27a28381f85dce7b90a4f9aaa901b1fa06d74 \ + --hash=sha256:19e047b417c39264062edd94d0012fc159573d57da7fa3633852f13d07f6e522 \ + --hash=sha256:1a2b9cf39b4d30f600df8c56cccbc03adfc6e1ae8c04cd6b181105a432d4a515 \ + --hash=sha256:1aaae81669bf18b5a35dcb43dbb200f52b13f847a56bed7a2e82f31cc6f9f74d \ + --hash=sha256:1bde27266ec4a56c38ef8e17998e430d30cc6310fde76332381bf5aaa81872ba \ + --hash=sha256:1c32f2078df7e3c4f7e573592cdcd8eb50c827cd51226291ee867c217f036abe \ + --hash=sha256:1e149af6b5a5af697725abc52aefef7e3ab036f21f5c229848b0f8bc8f26edee \ + --hash=sha256:1e3f5f726bedde8d7006c4f8d61f0f053de65b806af24110278c530445b6da50 \ + --hash=sha256:1e571d2db1c515641e9918cf04f23be58818ba6d56f266fab31dfc6d5f6e01d9 \ + --hash=sha256:1f315f07b269f074995c445b65dfde62d12c0e889e9c3b0534befdb05866e880 \ + --hash=sha256:1f7ddf3a7f1c85061f246a481c63a70d7aadd0a49add8e6c109b65360fbf923e \ + --hash=sha256:20e4c838a84fad3491027f0bd364f6fe21eedecab51860078b23cdb22bcb016d \ + --hash=sha256:20f18edb511ccfb652e114d985a61a4201f9d60bf5a3b3f9e6e95caf3a2f7859 \ + --hash=sha256:216a160461168c12c5ec0d6384a0dcb73e7b3c392df3e30c1fa11cff1cc8be82 \ + --hash=sha256:216b78caa73ae9b82f3f006e9694ee5a1bde89e50f4552fd1679b56b080cfb7e \ + --hash=sha256:220d8ab003d44144d80f8b776019adedc23fdc7bcb270396744b9805a8186d0e \ + --hash=sha256:2615d10e4cad323925d2f7d904ae38c6ae439b33069a0d56cc4ce64ea4c9b339 \ + --hash=sha256:26422429a0bb2f15773dacc0fcb1bcddfce68c6b2d41fc14bc7fc17f8c529542 \ + --hash=sha256:282bd86c49ddd905bc9b8f166433b4e2e07f6130a273a5ca61c55f44005a263b \ + --hash=sha256:283d2fabf23e356e72b4fb8a59f5e319202c0328c748f6596f14459b0650bfbb \ + --hash=sha256:28d5db510433bb1544dc128c4e7ebd85ae57cec2a4608edd1f7ca4fed3e53b3d \ + --hash=sha256:2b5563aca0b7b74751dafe6cc3e1850a3401414c05342f1bbeb26549b5c3bda0 \ + --hash=sha256:2d240554e1a63ad9b7cb128acf94d4bc7d8400c78dfb76521775e767d4aa0b22 \ + --hash=sha256:2e79853f8e52ab0afa7b3ae445de23767b033fa0e58ad11099d3c6b79d012c7d \ + --hash=sha256:305ae0210e974f5d0dad3f0052559a83297433412e6ba0f8a6aed93bb4083ddb \ + --hash=sha256:30b0bc5add7b5ffbf9e8f84ad8cbbeeac420da70666d32bedecdbf2051e15592 \ + --hash=sha256:32db63c2b2ebbd1152538e0c405bcb38bbaed1adba0efea04bd3d4b33e9cec70 \ + --hash=sha256:3344ce336d9d8292125369c1475d1663e7e1a06894e8e5150307e11f782c6afd \ + --hash=sha256:3398eb35a82a94d61756f7a4ad6a1c5a3e735c6abb97167398a22389a9b8ca7a \ + --hash=sha256:352860c3c88a6ff74ed877755e20084e7645cbd5ed973448ca38f83c0aebc2ec \ + --hash=sha256:356e7bd1453551c06231df8411bfbaed9998fbcba2da723d84fb270ff1f977a7 \ + --hash=sha256:37daeed30664d0d59dc0c99707a3a3fb723b8dffdf62266078308b9b26c7a18f \ + --hash=sha256:37e0adc6147a9d3bbf22f77f25fd8706c4179e861d4605173841ae4095a205a0 \ + --hash=sha256:3c6e18fb581e9ec9e65c60ba7c752ef409b2472319ebdfa53cce0679669a4bb9 \ + --hash=sha256:40399e568324635235697b00410634e0fb027432e9b9fef92886eb3407a5c211 \ + --hash=sha256:40fd8e16bfde1e9d80700bbdb51a830c0f7e384c2130c4a8ed5f0912fb269cce \ + --hash=sha256:435064ff2fc778a02d1234289a22050a4d3b29752062b5ecaf45eae62273ec47 \ + --hash=sha256:43885294c9e7c79c4a43c42fe759a82e92d8822fe3e7f2f8b23af90e5dbc4269 \ + --hash=sha256:43df778a20db59f231b02c6dd70958e1fad532fc8a4f6bebb0555e74abe01898 \ + --hash=sha256:44e8a5ac6a2f69894f1bc980d1397312c77f8d313a5e721b6409959b52c18330 \ + --hash=sha256:49c62921f55d9d7713faeb855bd9aad1edfb8e09e2c8133b7058d4c447bdaa6e \ + --hash=sha256:4bb9dd97bdab9b6ba0e80f9d83e140e8263567d28878fcc52f8f0f41990926a6 \ + --hash=sha256:4c94d6b104411d33df813b1defa8a1194a884e9393839fefa3f7ea7377e1efeb \ + --hash=sha256:4e5ffb327f6df95bbe49e517eb3f4ce5127cc6bf9a7065f32e457ed51fac7c97 \ + --hash=sha256:4e8b163c8d2d2a5f414f2c31cdd91024e0c91c72e735a9a564a62460ac838acb \ + --hash=sha256:50eea4c9a05308fbae30ee976150e7416baade27970ae8e229174ce92b5e07bc \ + --hash=sha256:545ab2a433769e3b8e1ce2b4f7b07218bbde202f4954fbfe52948b2522120727 \ + --hash=sha256:5d02948944dad3e99ebe70a3049d7df66f5faba97ed03b411349b034558ed936 \ + --hash=sha256:5d09d63b219adfb1b40e104036dc2462234d2f06c05e436918e08f31a09a973b \ + --hash=sha256:6260074fae5bc47838af0fee1a6f48530d1ac7b5f49c80868144ba2f69f43145 \ + --hash=sha256:678cf90273ee5fa7cedb35334c765ced4dad38608c0258445da009c1da9dd174 \ + --hash=sha256:69a2c6eaaa3b7e157ddd1c3803d09e5fa80d9aeb5191b81ad60e182662c2a324 \ + --hash=sha256:6a005b8dcc724f0dae96d0504f93d16a283d9a79bdaeee57648335ff0b483470 \ + --hash=sha256:6ab1a34d824efc0bf235c0abf9415256bbd74288cdfc47f6646ec9fce04076f9 \ + --hash=sha256:6ae263c1244bf375420fcdfd5ab32d463496f3814177edc8f0f3a8b56d7fe643 \ + --hash=sha256:6b5bc65829f567e11e83de59e4bfcccd8827a0da01cd525d1872f9cc3a8cd520 \ + --hash=sha256:6be40c3311eabe8a816e00041844f9b249828015dc98be8a48a7c3275954ee76 \ + --hash=sha256:6fa782fc5d7d53bb4c1b01e34909287f301c4c81251f8130e55848ab5d2f23e1 \ + --hash=sha256:716aed288780c9c2081943a3a7b5be6993cdad56b0cdcb4ef4b562ef56c5a1ae \ + --hash=sha256:741f944bef8dd709e9ca9e991f5f6a91a8d49b6e2725fdb4070027f0ec06faa2 \ + --hash=sha256:75d21d0a2cae0bb071c68686d77e5100be611ec4e80e0d97f8736c27da0ab197 \ + --hash=sha256:7661246f93c902bf147d5f7d72874902ef3e49a63ca3f0de333cb8e85765d2fd \ + --hash=sha256:76ca8ca7ff922c5d51395fb72b1ee2f8e9ec896c293c3c5b3bca81f71653fed9 \ + --hash=sha256:774fe1a69e99c60ef7f5fb3d688e85db707e232355b4c93bbb96b4d17c5503cc \ + --hash=sha256:7b0698cb1eb0c13b1979c9538603fd8e07c87bd8bb76cf0b707cdca68f56ad0b \ + --hash=sha256:7b809817bf0413bcc00cab69d6a055e1fb2626b22359772c2c3570ac3fef7462 \ + --hash=sha256:7dc71ba89766bef4bd2d9be8a827ce784f1c85915b8bcad2deefd7d892d6816e \ + --hash=sha256:7ec51301e1f9f1fbdbd3bb6b34e0df08f5272937e0f86f535e9616341eb452af \ + --hash=sha256:80b171f1546935be4dae1e01bfd8630d2712271e067858b7135726e7d9bc7cce \ + --hash=sha256:80eb2c568f1f09283ad7528407a97e84935f23851943ed27206b52664b8010f0 \ + --hash=sha256:816fccccaa736743c19f8fd687def788e0c0813f8168f88c4d169827b6726d65 \ + --hash=sha256:82b38c11b73d4ea37b1d76d4690131472ce6a144166a63fedf336d88a101336b \ + --hash=sha256:831c25fd670727aea65525b9d6cff00718f26ca92433f9ed039fe67af9825388 \ + --hash=sha256:83496c6800d5e1002d1e923ab5bef49bb67a07c2faac8374364497182f04af72 \ + --hash=sha256:84e619e315fdaf8b70d54cdd0be12c7895dcdcd0212a42a67576b33f7af111dd \ + --hash=sha256:865b7db127a95e33640ebcdb4bb3aad165d4873ee7c1008949129f3c4f900dd8 \ + --hash=sha256:89756a61cd09a5669ce923081f518476ff4b960c5d850a5dd54f0cf4406ac684 \ + --hash=sha256:897ca382ec6c7bad041ce7b3a64b3a15f1b639dfea89ffcf29bdd235c706fac3 \ + --hash=sha256:8a5aaa4343b5ed1af3850ce351482e7385d695af15b81b244c3f823949dfe796 \ + --hash=sha256:8b08e4a065c9fa88ab9b8a2345b58073776806488b1ff5e4348957d0aa218043 \ + --hash=sha256:8b47a5b4a359e42b4b726cbd9558347c5324194aadaf12e4ad219efc89dc9812 \ + --hash=sha256:8d303baddeddaccada149bbee270b3e2eedcaec2df082834895cdd897a602674 \ + --hash=sha256:8dfe4566d653684daa21f41c75c8a64a8333b36a4377ccb12a1f16e321d7d1ca \ + --hash=sha256:8e6afda6996523b29d42b8b9dba309d2bad53fd2eaa06189d735c8c7e2885455 \ + --hash=sha256:9043a0a4d93affc7c78f78023ab155b1128f819a900d08e3c2da29987062ab4c \ + --hash=sha256:91aceea4287299ee60c1176909efd6f2de091da24c0d93d2f9861c93e3776ef7 \ + --hash=sha256:925f34f75e024abe94dd0f33da8f0cb21db35f85d534219dc18abde90c06a8d7 \ + --hash=sha256:92998479a2a4464d141ef709e52dc3e4d4d4ce7f3b9cb5052d2c56c55b405b15 \ + --hash=sha256:92dbad4599d5d081f905bba43b10690cc4d445857d04a7b18eba1a09bfa27cf6 \ + --hash=sha256:93bc9bdfaf87dc7d79ee0182b255383b7f82a3167d0166b99330d897b59f9053 \ + --hash=sha256:967ec267e9c36c50b8a7e2ab188ab3036024e8ddb9f036eb97fc32a431d201de \ + --hash=sha256:9732eba18ba7fe44c1b2827bfaadf381fed3789bd7e20c990e6c8d1ceba0179b \ + --hash=sha256:99570e43605b9c849ff1606e1691e503962250f80ec3e827249f7ad820e402d8 \ + --hash=sha256:9d16bd1cdbb63985cb2f3ec4bda4de13ba6396c1f81468941c650b4157670ee1 \ + --hash=sha256:9edbf7e7a97454904a4ccfbd007a511b75ebf13cba9d0dbdfe6c4480e154edf6 \ + --hash=sha256:a119cdd2e59b30aa570e75182b22fa149da50e921ed8b4c492eb9ed308d944c0 \ + --hash=sha256:a1529b8e08a93dd9c00d1e3b3c2b627a9600d96c2f40143dc0b3a85f48fa85e5 \ + --hash=sha256:a167f17421c237a32c93072a053ff756d9fb225e69a620c3f4818665f0520044 \ + --hash=sha256:a18c7dfab52b07453321b24e5be2d532e7875076e67b7295b5b471988616b541 \ + --hash=sha256:a1f8f1bb4158069291fe6ac2d34db942418f2804564d04b8e97722041035f843 \ + --hash=sha256:a34261348f88443d9e234f251a1f1fcb711c1cc006824fdb29b649735d8ac35f \ + --hash=sha256:a580b3b8c49fe60200291cffbe8204116ba193c9a04230f087cc20bab561ae0e \ + --hash=sha256:a6a5f053f077aa8f0ffe5d4d03dd7d3fae4b85155942228a6dd20b467c4d7d80 \ + --hash=sha256:a707d36935229ae5c3044cd601908cb7bd9f25757003d029765ccf66818301ce \ + --hash=sha256:a80226a2135de8a454e6812dd604d1c42c4e94269223b242395d689bf247824f \ + --hash=sha256:a80b502057361c8f2f5f9b75ecda9127b4ea1b1baec7b99b63d425c09e799b12 \ + --hash=sha256:a8bc9cb80cd736785aa39be5e5d934772a36f9ba30fa71b7c19dbe1da44a306f \ + --hash=sha256:a9bcbdefd0858372c2e3c657ca8c1e2cdf0af5963cb45085cc861dfac0ddd422 \ + --hash=sha256:aa56c549af248664ed7e1cc8ebc4dd7f1505b1444d8f3bf15b6a89b43dd4151f \ + --hash=sha256:aabdeccdd1be80735cd8cb815565d9528c767113358fac2e8eba21030e018a65 \ + --hash=sha256:abb4aedb092aca028e1111998a0c2a5b6e327707e61df2c22e061118b0a8ccd5 \ + --hash=sha256:ac68b88c9665cb49940e12ad5d020e9388f95b512aabef15fe0ce1ba47ad63a5 \ + --hash=sha256:ad2b01763ed71f190651fe53faa1ec5e41ed8d6c730d0f32f25da8afff07b119 \ + --hash=sha256:ade98a94cd71692baf0ab21245ecf9a2f1c275460dc4106e23ce9aca1c4c1838 \ + --hash=sha256:adfc52cee3ad56c070e824bee9feda1f13c8679601ff8d0535f03da60bdcdda6 \ + --hash=sha256:ae78cdaec57f21e7f44cc5f9866d694cc072e1b1082286f30fd74e7545fa2916 \ + --hash=sha256:aea6ab63971f72f69b2cace481e0df9cb01486317296e7809a086a71864a6013 \ + --hash=sha256:afa9be577f5cf1115f3cb597d5410607dde4167efb413ee2b3c4639913031aac \ + --hash=sha256:b0597ca31c472f3071844648ce5ab86a1732033ca230daffd8f87c6f8596a8ae \ + --hash=sha256:b059b951347a6e16d29b1488f624a7b213c7e8482869b1eac2b684e6fb1ac236 \ + --hash=sha256:b1a65d388032eeec4018de3b80e83a8dfa841f663c1d7e14beea4bc5cf4eac61 \ + --hash=sha256:b391e54bc8198387cf089ffd343d8c99d58e73f209c31aa2e5f420bf20bbb0c7 \ + --hash=sha256:b3ee57900cb5d35a79d992800103180d715b68d8b56658b445a10f97e8805982 \ + --hash=sha256:b51c308c5732bf4fe5ff6edfd4bced2a32bf41fe664cafc3088d3cff7566734b \ + --hash=sha256:b618ecec8f13b3f9dd58e257aa98fc9b017829a1bdc4f576e9146998956ec2c7 \ + --hash=sha256:b6cb9e548816e0838b10d29b061cfbbfc81b726f6c5f89d60e83bd7d703ed06b \ + --hash=sha256:b7af9ad847b351b42ec54b3c0580febe406b28408917b7fc1565c87896ed0c4d \ + --hash=sha256:b813d6eda1805d7d8acb176589ee1a51c4d0e5e3245093eddbd330d6508bf112 \ + --hash=sha256:bb1030e8b12b4ef517b142f169f610b7971784188a86da19177e78d2481de6e6 \ + --hash=sha256:bc40fdbb89caecbd3c8c261409dd9e9245d6985c85fd326b6c748909b9a21936 \ + --hash=sha256:bd4ea6385741cdcaa01b516b170d69fb27cf4cc91ea9308ce4ab6de0482bcd7a \ + --hash=sha256:be98a4e72e3821714770ed290e5e1a8a6cabe77af58520a9adf718acc43a165e \ + --hash=sha256:bf98b77c6cca5c5da30135b69b30668da07a32d41210c62121b34c84239d9d4a \ + --hash=sha256:c1a3279af228faca3c224cc8c30aa130b5f3184ba420ac477de1db2cb99be8a7 \ + --hash=sha256:c3455b23f785486a5ab3d2b8bfc7f573d1bab0a10d061fb9b7f596096e316ae2 \ + --hash=sha256:c3930278a6635dac4dff15f8f336ae643101608160f4525e67a9fc8416061daf \ + --hash=sha256:c3935b4402f257d9c7448944db07f91d6fc20453f8c3f0fa1bf26c490b534c84 \ + --hash=sha256:c6b6c15473fff013dfcb0b89cfcbc922442459b08e96d37cdcf1a8bec28e4ed4 \ + --hash=sha256:c7010b9ce91aaea5e389a7c4de0b8459a5a05a6795921124d8c82928eb13c4a9 \ + --hash=sha256:c8b5f52776f0277e72a9c7e7944f682de2b3ee4655b7972a48c53f871963741a \ + --hash=sha256:c8caba079f6d36885b67f94e92dedf3663219a5f7d76a5f744045b4b7618d77f \ + --hash=sha256:c9c391fc80f26bd809ce472208121718552483bb0ad649ed771bd705dba91a1f \ + --hash=sha256:cbc41c5376b30ba7b3d558505f7598799034c8aef30e3cee00f32bf8d26fbede \ + --hash=sha256:cc35889ddf38f5036eb98617c065f0c9245e37e9dad16209b7e55e9e30b449de \ + --hash=sha256:ccbae849677648be456ea0de769a78e432d2d24f71cbdc739741e69f8160e0d7 \ + --hash=sha256:d01e4d495c5b10e79de3449501e41d2bc2a4aa90844a3735eb962a3a01645971 \ + --hash=sha256:d0930504fbe5c003f31d67aeab4b8f155a409168a26ef8ea7df759bc50ab6729 \ + --hash=sha256:d1149b7360dd99ef1ad10618df2a4f54a00385bc8d2c1aa244c0301a548ac415 \ + --hash=sha256:d1ddf909fc9ee5f590de65a4595171aaf0d26656d81d1f50af49c4a73713725d \ + --hash=sha256:d373b682dd0a267ece21869ca9a48d40b55120a3be714661ad0e9afdce9ce27e \ + --hash=sha256:d3e26e250aa51813881d03c09995a41462e115ab9c3c2b6d5202e4286b924d00 \ + --hash=sha256:d42196f594460a083084d8e3c2f2554c958ebd8fe19bc30ef1b938197436e7d5 \ + --hash=sha256:d5a27a14899cb1b878c2924dd150d943c4e5cee02a50a409a1f62f4ad852038e \ + --hash=sha256:d691553d1a88ed87cf1837babec3663275b29de906b48433c15b298e262e5243 \ + --hash=sha256:d7c77f38e6d0b5bf8d7af9cb9c6bb9f4e62f25edc2931251d46c3ed0d89121ab \ + --hash=sha256:d83f517403ff39404b8586d07e97c019cb2a7cb6665cb070c6aebf1fc03e5487 \ + --hash=sha256:d8e05ac71573089f25cdbad4b01db8d0b8e82846cd42291ef002d265903b1e41 \ + --hash=sha256:dadbc503888b18722f8e5c988ab67f555279263998e73d0a946f8bc9eaf4f745 \ + --hash=sha256:dc5b02c33ee9dee2cb3487c5d381bf931ff22144b1711fa093727fba991347ea \ + --hash=sha256:dc719c38087e09788d40216ebaacc89504dd8e964c0457085a4c1b83695eaa5b \ + --hash=sha256:dc96f63170b2fc943ac83da1015c6333cbaf251d12174b6e506315b941dd16b5 \ + --hash=sha256:dd1ace6dacffce5cdbe68a3b2efdf22e3c890a906d887075e10dcc5f4124068b \ + --hash=sha256:dd4abc5f83ea43fe977caa7111af763e0f2ad5f4143a55abaef8bc4efe4fe30c \ + --hash=sha256:dedecea1ef347db51736836fb609168ab376cdb956a5ded576f271054fba0efc \ + --hash=sha256:df018a1067ccf82ecc40484d2f2ad495a9f06a47c310265a53162a84f417a9dd \ + --hash=sha256:e0143b3515b97bb3c4743fbdf10f53950c0bb1fe1a2db1054b422ba370594333 \ + --hash=sha256:e161a4ba46caaa9417d5cd55f23c0717d5243b4f2a96c176b0d1a07bf86e0b0c \ + --hash=sha256:e1df96c88f8e9f57cbe25f0d8f28411e2d1cc42be26e99078f6e4efa876dcb96 \ + --hash=sha256:e34137a2fe746c8784a937fe74bde983b3d790f8aedf8625f1645d2744b01966 \ + --hash=sha256:e3ed723ed56d273b0e3a45c2583c5566ccb39cc5fd4d335bdcbe235f84e1a211 \ + --hash=sha256:e675b15b7a7b81e5b1a1e747cc49f9f9e6649d3b5e8a61719b46b9a671433210 \ + --hash=sha256:e868946a538178990a43fa6bbeff1eb027e515d6269743e4d31d19f72daf00ac \ + --hash=sha256:e8d559a46759687accc1780fbb07be17f663746842853c88115cbf89c680fb4e \ + --hash=sha256:e953b14d562b7c08eae7b7c327b5162c78a6975974d8de8d7acff2b8b7c682b0 \ + --hash=sha256:eadf5e5fa8c0e2f15a3fe6f5513882f33b4a1b77d8c8cc9252c1e0dcc9e5bf6a \ + --hash=sha256:ee074ecc63f43c664a35c9aea9daa84ab9d0de24487353f53aed097012c8d43c \ + --hash=sha256:eed1b552f5979a4e3545dbaed4dd8111af9d321844232945bd0ed3a505602dd0 \ + --hash=sha256:f091c932bef000b8dff3ee00dfd8769e138021770d46d577168d802af7abd22b \ + --hash=sha256:f1626f1de1d7c109e25e20528cf1ffe17d0b614baa87c9d20f6181cb65234168 \ + --hash=sha256:f2509dc39574f1a0c60eb5f6c968e6f064b55bea88506df25d15ba6d391b1c48 \ + --hash=sha256:f4135c1e12615fa7989c9aec4720cedaa342bc4b8dbd5665f84a95790e3db5fd \ + --hash=sha256:f5b5f72a0d761849c75b0524606707b28600eb9bf75263e7f36a7ca33627fbbb \ + --hash=sha256:f5d1c9d46d6b8459f5dac87b1778950ad28e27a83d1cdba1d2c34a031dcd57e2 \ + --hash=sha256:f5d28afc34ee925f0beb376d2e3ace38267e700994481511686f2b467f11f51c \ + --hash=sha256:f8dcf39b6aabed5d3820188451e98d651a9fde2453a2e99fb386941d4bd518d9 \ + --hash=sha256:fb1734c69974acaee369726b48031c0d0117830bc050188086a69227c32d2426 \ + --hash=sha256:fb5dc922ce3cb4211caa7e29e6daee98f319e59f297a904acd74f2fdd0674356 \ + --hash=sha256:fbf8e901a9caf045062b7a1a8f7db056c492a5a76a0c612714ed7abb5ad42f7a \ + --hash=sha256:fe57aab650c771802cc7b0eb541a74b6a181cd1870f61c537294ab462fec34e8 + # via vllm +pycountry==26.2.16 \ + --hash=sha256:115c4baf7cceaa30f59a4694d79483c9167dbce7a9de4d3d571c5f3ea77c305a \ + --hash=sha256:5b6027d453fcd6060112b951dd010f01f168b51b4bf8a1f1fc8c95c8d94a0801 + # via pydantic-extra-types +pycparser==3.0 \ + --hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \ + --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 + # via cffi +pydantic==2.13.4 \ + --hash=sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba \ + --hash=sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # anthropic + # compressed-tensors + # daytona + # daytona-analytics-api-client + # daytona-analytics-api-client-async + # daytona-api-client + # daytona-api-client-async + # daytona-toolbox-api-client + # daytona-toolbox-api-client-async + # fastapi + # fastapi-cloud-cli + # fastmcp-slim + # gradio + # harbor + # litellm + # lm-format-enforcer + # mcp + # mistral-common + # model-hosting-container-standards + # openai + # openai-harmony + # openapi-pydantic + # postgrest + # pydantic-extra-types + # pydantic-settings + # realtime + # storage3 + # supabase-auth + # vllm + # xgrammar +pydantic-core==2.46.4 \ + --hash=sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0 \ + --hash=sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262 \ + --hash=sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda \ + --hash=sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0 \ + --hash=sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e \ + --hash=sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b \ + --hash=sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594 \ + --hash=sha256:10e17cbb10a330363733efc4d7c4d0dd827ac0909b8f6a6542298fed1ea62f29 \ + --hash=sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2 \ + --hash=sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c \ + --hash=sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d \ + --hash=sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398 \ + --hash=sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d \ + --hash=sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3 \ + --hash=sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f \ + --hash=sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb \ + --hash=sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7 \ + --hash=sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5 \ + --hash=sha256:228ee9bae8bef5b1e97ec58302f80357c37199e0d0a99174e138d28e6957b9d9 \ + --hash=sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462 \ + --hash=sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4 \ + --hash=sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b \ + --hash=sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d \ + --hash=sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df \ + --hash=sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2 \ + --hash=sha256:3447661d99f75a3683a4cf5c87da72f2161964611864dbbeac7fbb118bb4bfc0 \ + --hash=sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519 \ + --hash=sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd \ + --hash=sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7 \ + --hash=sha256:3be77f45df024d789a672ae34f8b06fb346c4f9f46ea714956660ea4862e89ac \ + --hash=sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6 \ + --hash=sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565 \ + --hash=sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898 \ + --hash=sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb \ + --hash=sha256:432c179df7874eeb73307aad2df0755e1ae0efa61ff0ea89b93e194411ae3928 \ + --hash=sha256:4a05d69cba51d852c5c3e92758653245a50c0b646ced0cf05bd793ed592839d6 \ + --hash=sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3 \ + --hash=sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a \ + --hash=sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596 \ + --hash=sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987 \ + --hash=sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e \ + --hash=sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d \ + --hash=sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712 \ + --hash=sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008 \ + --hash=sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd \ + --hash=sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1 \ + --hash=sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be \ + --hash=sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea \ + --hash=sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292 \ + --hash=sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33 \ + --hash=sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3 \ + --hash=sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4 \ + --hash=sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b \ + --hash=sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826 \ + --hash=sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac \ + --hash=sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7 \ + --hash=sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d \ + --hash=sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf \ + --hash=sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4 \ + --hash=sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc \ + --hash=sha256:8b9bab013d1c7a79d3501ff86d0bc9c31bf587db4551677b96bec07df78c6b15 \ + --hash=sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3 \ + --hash=sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b \ + --hash=sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914 \ + --hash=sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04 \ + --hash=sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c \ + --hash=sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b \ + --hash=sha256:91a06d2e259ecfbd8c901d70c3c507900458498142b3026a296b7de4d1322cc9 \ + --hash=sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce \ + --hash=sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4 \ + --hash=sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a \ + --hash=sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f \ + --hash=sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424 \ + --hash=sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894 \ + --hash=sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9 \ + --hash=sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76 \ + --hash=sha256:9f444c499b3eefd3a92e348059471ea0c3a6e303d9c1cec09fa748fd9f895201 \ + --hash=sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb \ + --hash=sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109 \ + --hash=sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4 \ + --hash=sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848 \ + --hash=sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526 \ + --hash=sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0 \ + --hash=sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01 \ + --hash=sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458 \ + --hash=sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e \ + --hash=sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba \ + --hash=sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a \ + --hash=sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39 \ + --hash=sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c \ + --hash=sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000 \ + --hash=sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b \ + --hash=sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf \ + --hash=sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4 \ + --hash=sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd \ + --hash=sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28 \ + --hash=sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9 \ + --hash=sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30 \ + --hash=sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983 \ + --hash=sha256:d80ee3d731373b24cebbc10d689ca4ee1875caf0d5703a245db18efd4dd37fc1 \ + --hash=sha256:d995260fdf4e1db774581b4900e0f832abe3c7c84996726bbc161b19c8f29e76 \ + --hash=sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5 \ + --hash=sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4 \ + --hash=sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7 \ + --hash=sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c \ + --hash=sha256:e68b7a074f65a2fd746c52a7ce6142ab7006074ac269ace0c25cd8ba171f8066 \ + --hash=sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3 \ + --hash=sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02 \ + --hash=sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89 \ + --hash=sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50 \ + --hash=sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76 \ + --hash=sha256:f13a646d65d09fbf1bc6b3a9635d30095c8e7e5cc419ff35ecc563c5fd04cd49 \ + --hash=sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b \ + --hash=sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d \ + --hash=sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7 \ + --hash=sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4 \ + --hash=sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c \ + --hash=sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e \ + --hash=sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff \ + --hash=sha256:fd8b3d9fd264be37976686c7f65cd52a83f5e84f4bfd2adf9c1d469676bbb6ae + # via pydantic +pydantic-extra-types==2.11.1 \ + --hash=sha256:1722ea2bddae5628ace25f2aa685b69978ef533123e5638cfbddb999e0100ec1 \ + --hash=sha256:46792d2307383859e923d8fcefa82108b1a141f8a9c0198982b3832ab5ef1049 + # via + # fastapi + # mistral-common +pydantic-settings==2.15.0 \ + --hash=sha256:0ba092c291c94baceb5eff768aa0d56400a457585bc0175925a5a5510303da42 \ + --hash=sha256:694b793e84f766ba76a90ebdefc01d0a9a045dab0382bee70393da93712ad117 + # via + # fastapi + # fastmcp-slim + # litellm + # mcp +pydub==0.25.1 \ + --hash=sha256:65617e33033874b59d87db603aa1ed450633288aefead953b30bded59cb599a6 \ + --hash=sha256:980a33ce9949cab2a569606b65674d748ecbca4f0796887fd6f46173a7b0d30f + # via gradio +pyelftools==0.33 \ + --hash=sha256:660d82dcbeb8e83d1702bd97f223f761625da06111c0cc988eac6b8ab0c1b61f \ + --hash=sha256:f215ad5f47d3f1373a21496a6c9e0707c622840d0622f23ff7ce08678b020036 + # via humming-kernels +pygments==2.21.0 \ + --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 \ + --hash=sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c + # via + # rich + # rich-rst +pyjwt==2.14.0 \ + --hash=sha256:77283c83fb56ecf566a886c757a714bc83668e38156de2cce8263302f42e0b86 \ + --hash=sha256:ad0cef71c756a56e74863c2919cf0985f72decbcfcb550ee2f422e7c62b5eedc + # via + # harbor + # mcp + # supabase-auth +pynvvideocodec==2.0.4 \ + --hash=sha256:46e2adb82dc6ac333d3535cc76e4e25c7e8d80dd272b1aba0c28702b861d5261 \ + --hash=sha256:4dba42331f6d319087d05359787c7c542483fab22107c07b983cad928c1e3cfe \ + --hash=sha256:51724c6a0e3623c092cccdf93c8b09cced6881f3d0c76653f6ffbec0371f29cd \ + --hash=sha256:7af1d9ab37f7415168c23be00539094e7b21774661af2a0a2b9bb574670bc75e \ + --hash=sha256:834fdbef7f3fc79285b5c2a88d1f1f7cd13543a9a7f2a2786141b181d1daaac9 \ + --hash=sha256:8d6d3d63317c452bdd18167f622abb392bd982ae65293673a169e3f253c1d1ed \ + --hash=sha256:8e704a2b553a35cc2de10543a232e7feddc473f3e5478996595236e3194efc5d \ + --hash=sha256:a0daa28b09705806c8c6b26326df217c45e60c0a12a673ea3ea6ee5e2e7193b0 \ + --hash=sha256:b59cec7a1a3f78fad13fead78cad8b6d9686827f9ff4477080245457675a01d0 \ + --hash=sha256:bad9e25f494abdcfa8f9dffa33a840509eda3ffcdf6e7cf6465d73be307c0c82 \ + --hash=sha256:bd3779ff73ad703393c0a19c3650f269ca25e71902c24efa0719ed4a58cd9390 \ + --hash=sha256:c7a133a8088ff9152f7953ff63c5cbc27801f0313d1e577c5f3e65da67abf570 \ + --hash=sha256:d9ec06f47bca7b20a6e8234afaf596699c57a477be292613d676802e7e808ed2 \ + --hash=sha256:f678e5a6903498f291cffaba5d47b023c67bf18172049186044c4466492ad443 \ + --hash=sha256:f809fb18929ac2af042835f10c7679b1c86db9817776f22bc7467907c5c3d918 \ + --hash=sha256:fbe730967d5402ffca520b12fa21725cbd22a6d2c9fae882ea1d95085a400fd9 \ + --hash=sha256:fc299a14e61832850be91f8441669ada6fc270903ad1c50acde8e9353590fd3a \ + --hash=sha256:fcb06ef6ef24ee33b8f34950696a4e01636f2d8cdb96c407cd692931d049ac3d + # via vllm +pyperclip==1.11.0 \ + --hash=sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6 \ + --hash=sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273 + # via fastmcp-slim +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 + # via + # botocore + # daytona-analytics-api-client + # daytona-analytics-api-client-async + # daytona-api-client + # daytona-api-client-async + # daytona-toolbox-api-client + # daytona-toolbox-api-client-async + # pandas +python-dotenv==1.2.2 \ + --hash=sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a \ + --hash=sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # daytona + # fastmcp-slim + # harbor + # litellm + # pydantic-settings + # uvicorn +python-engineio==4.14.0 \ + --hash=sha256:9f0fe275fb7d67bfc1a632421adf22949fd4843bd9c458c004b0a89cede302a2 \ + --hash=sha256:eaa1e386baf9c2c7959eef7f9d9165c5ea910c5b392f5316e78d29ed073cb43d + # via python-socketio +python-json-logger==4.2.0 \ + --hash=sha256:158a52126fcd6869e09574d2b66272666f3dc8f468c62637ef9a1fa883719cb9 \ + --hash=sha256:e371ebe22ec01e289850102091a2b1f6fc9e655c7f1f5f29073936756c290afa + # via vllm +python-multipart==0.0.32 \ + --hash=sha256:be54b7f3fa167bb83e4fcd936b887b708f4e57fe75911c02aebf53efaf8d938e \ + --hash=sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23 + # via + # daytona + # fastapi + # fastmcp-slim + # gradio + # mcp + # trackio +python-socketio==5.17.0 \ + --hash=sha256:b5826fd2f8aa02e11347816349b74ac6b53e8a4f4e4b1cf1388e1aff19b7f3f4 \ + --hash=sha256:c3bbfc4937dcfea7c4d1b182afa94d4a30335d153987e8f2078b344beacf95a0 + # via daytona +pytz==2026.3.post1 \ + --hash=sha256:2211d3fcf9a797d3405cac96ac7f61d80e6a644f72a3309607282fe8a2010c5d \ + --hash=sha256:dd95840dd199baea12d9cc096a1d452caa6596a1c1e4b5f3dbd1541855d5e815 + # via gradio +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via + # accelerate + # datasets + # fastmcp-slim + # gradio + # harbor + # huggingface-hub + # jsonschema-path + # kernels + # lm-format-enforcer + # peft + # transformers + # uvicorn + # vllm +pyzmq==27.2.0 \ + --hash=sha256:00e73942ef12cecbc7951c4a9104bb8ffaed742abb13af2da6833d90dd368cef \ + --hash=sha256:010db74a1dd67c7cd8b8b30916355735db7d633a070510bb34e41ab679ab2c0e \ + --hash=sha256:0e1af01858d6dc0c09cea57f9cb1ddf4601f04897b6bb1efc3a2038123c87d79 \ + --hash=sha256:0f4bd6743e8bf854c3bfce892dd6578a514aabf128e37a4b2eafcf01856f7e44 \ + --hash=sha256:1132805970045adb9f5f05dd57040978286a8e21a5475f2c2ddf1bc983b9a2c7 \ + --hash=sha256:1ecbdd131b9669f62d3a45afee5527c7ae9f141e4301267f21714c90bd21725f \ + --hash=sha256:1f8079d0521fe94bbb401fe9407578b28f3701627c8be2c9f7e0c5b77dcb0109 \ + --hash=sha256:211350c3ccd4746bc5a85e8fe961bad1f7f2f274f67cf1f785fad7f96f562eea \ + --hash=sha256:288cc790da0e3064a14a38ddc56ba169dada8c8af4cb86518db2bcbd380eedbb \ + --hash=sha256:2c218c6ab8bc447ba62054b581fd30209689d199c6ecb253f79615ca74a38e12 \ + --hash=sha256:3146385b94a760236c5eceff468a66a296a716ca98a2e0f9217b1518118466b1 \ + --hash=sha256:348d6fd3e4b81ae4580622ea8c2ea60224e84b2ac1b3be4482e6edc7de06e7a3 \ + --hash=sha256:376981d106598beb70be384f44d8f589832fd0051d184d38d10043da3cc3b080 \ + --hash=sha256:39755dc4a923021bd0677990ffdbc21cff0e1ee1cf07fe3817acea153ef4cb67 \ + --hash=sha256:3ab6eb88590e510ab16715c32dbba12000da9bee989fdadd9ee19a234c492eb7 \ + --hash=sha256:3d45189c0c3c99f817b7fefff0d32eeef684cf33e1e3c0fc4281515357c54702 \ + --hash=sha256:3ee556ed1cf836f96de9d5e545563116426d4a94f21b8041fdc79408eff18ebb \ + --hash=sha256:3ee8dd7031d5e23f632e0e7eee67183ca7d2536e0de35dc1e5d69f3471a791e8 \ + --hash=sha256:40124779c3a56ad5d91902df1ff89159cb414b6c1a0ee697abcc66cf5e6db62d \ + --hash=sha256:40d96cb7a8f6a43aa9617c00215c2b73e1b5e4a1d6cbc9f5860ed7ac682599f0 \ + --hash=sha256:44f261eca7dfb9904ea2b56428f59ab693bbe2715c0413a701f17b067ebf877c \ + --hash=sha256:468139ddb2e494d06e586bd3a6835077e8b3764560c8db552fe685c5867fc24e \ + --hash=sha256:480dba27b145373b5e103890f17969d891bc9e86746d6b8b29dd70b0d4addc62 \ + --hash=sha256:4ebc7889b31bc11c72e9f17ba3ebb0a8b0911cce413f41b498e55383a94819a3 \ + --hash=sha256:507c0b33f95502723d325487e8e50c2cdd3b37444143f05423a3861327f69bf7 \ + --hash=sha256:54d4259d1bfae24ecdb5ca79f7acc2eac6c286a02d6a0ae617797cb45f0726d3 \ + --hash=sha256:56b48fa9d478a3af7254f397697a62f5ad3e1bb677e200b2701f0c290d97e5af \ + --hash=sha256:591c8de5851c5ea372194469fe97587b97c3b641e9a70f31bb3474acbfde0241 \ + --hash=sha256:650c6cd7cb39a069e7048261efe66fce8bf2e0052c831a7a099b7a0f2ea860d7 \ + --hash=sha256:679b5b1dde326a921ea2c9ec1f9ea3115bfe1b4735779bbc6eb0473a0ed93f71 \ + --hash=sha256:6eb63cc61ab93b01b9afc887a160255e2fbe703fdbacfe5feaef87214f51bd6c \ + --hash=sha256:714f8cbd66c7e405338d668f79d2fe83fe923defe348e843be998603cf92eeff \ + --hash=sha256:722f0a6940be1a483c81029a271d950e04dc2ff113a42e21b3d2b7a0d8e59638 \ + --hash=sha256:76afba06ae698f2b8fe4fb34b32c760a650f168c2e622f370f2c528035b7f650 \ + --hash=sha256:770a37f28ddfbe1d2c40a2e3ce37e5fd10831daa6ae9634105aa8a5d23507b00 \ + --hash=sha256:7e2579c5de82ddf4544d723c1bc8b44c3b806d157acc9fb2a2d18e10ef28e202 \ + --hash=sha256:82a09aa67871d4f2fcafd47bf670fb93210b232a7c2d4b8a54676314edf04033 \ + --hash=sha256:88c0fac061bac269076edeb3a209acefc96cd6167c239daf1c2b404ac48d7012 \ + --hash=sha256:8a5c04ad2e368142aea52d1abdf6631cb2534864e3c16ab78268ab957060b2a6 \ + --hash=sha256:8b86e04f55af0f4d8cd8ecf14c0b8b81ebc8fd66fa20126b753514628ecadc7e \ + --hash=sha256:917d601e9540098f580d2723d0ce6402cdb6f02bc8dc2de74e0dca6e13bffd1b \ + --hash=sha256:9216132843d139a123f243c07fe70f7487dce5041093dd77040f9adb5dc91872 \ + --hash=sha256:94242bd4de6af7e74665e14a88630bccd615057f6acfaf08a3a432551d604645 \ + --hash=sha256:95369ed6626afcfe2ac89832fb1b917c077fbeb905fbbe5d918349ce0222b89b \ + --hash=sha256:95f52b877149b06bbdeec2e8ea6230aad14950bbfbcfa16e7eb88951f07d6b28 \ + --hash=sha256:97d4c6622f129b514a4f5939af1b5f434c97f47085d9311b5f7f36e24b3bd447 \ + --hash=sha256:9846e881620dd62566ca76a53e384c3f37490faf4b9240aebc7498810dfca853 \ + --hash=sha256:9ab72ee77b313d0658447204c8201f9b315146e923b48c56ea7dbd005d464a91 \ + --hash=sha256:a070a9cdad1f8f8a85ea153afcc4654f11b10895d14c0acabe10f1df0e0892ea \ + --hash=sha256:a0ee3c49be2aa15abd12cbbd14d4ea2892f872c688e1e487af39ec1972ed549d \ + --hash=sha256:a7c1144dc61777938e932a2c9011b980b89fd8ff3733033b34c44c299187a6e1 \ + --hash=sha256:a843094b4d3d633bc3623e47a2ff50742d6af02bc1f7606aa2e67e971e21878d \ + --hash=sha256:ac126d48cf18aa955daabef43bf0009ff76ad4deee437d09ecf15388214b5beb \ + --hash=sha256:ae6ebbc0bfe5a21ce21e32ba567bf73df2d93888109c65acbd42506cf9395759 \ + --hash=sha256:b26f2d0493b79ce3c3112c8a12649418915582ba4707b8ed9f44febf2be71f42 \ + --hash=sha256:b398c5fe102b41e1559f7ffdae760aabd5f432d73b047b4ae0eac4e01cb594d2 \ + --hash=sha256:b8d5f66e4a8246cf77f7b8f7902af64f00553368fa0373c89d99b78f0ad79394 \ + --hash=sha256:baa2ce3485145653194d6c8c5beedd1e9f0bf46a0919c9fa2fe2204fc35b74d9 \ + --hash=sha256:bad4813f270592cedf56977e31ac1fc374fb0f6f67ea5134a5e37c19cb429a8e \ + --hash=sha256:bf0b6e4ce1bb089751c504c5493d6b0557eabd02dd21b76e9086cf964234b103 \ + --hash=sha256:c218b816220d05acf6ab1bafca58926d95cbcc5fec5024724666030466308f0c \ + --hash=sha256:c5129a8fe43ecc49b99eb75616603d483a3c2fcaef504988fafe8ea392aea98b \ + --hash=sha256:c551b9e2f86dc625fcb1a032c0d68042678caf96a8dd7c28796766b673bd5b52 \ + --hash=sha256:c7cfb75caa83f5153c687e9d2107f64b5ef0ef0d6edd260d3ff920baaaa69101 \ + --hash=sha256:c9322f9c87b0935870516c2876e1e29497fdc50439c785ece63e3fbbab06c821 \ + --hash=sha256:d1526b42a2e725b84ed226f37becedc250c6347594e5ed304e4e9aff68c9aec3 \ + --hash=sha256:d1bc1d380a91d954ed5fc9f12915dba014eed0978d2de05ee7ca688bdaac144a \ + --hash=sha256:d41ebb260b69329b7d4a2936d44c872c86dd785355b51366c8b14e07ed7e9373 \ + --hash=sha256:d61910b52be5b2cd8b248dbcbe3a1b0275556a7d99fb613fc43323b546e273b8 \ + --hash=sha256:d61a0169ba05ab7ebc48dc793f092df12f789bf378dac8321ccd966fd93d94e8 \ + --hash=sha256:d64da42cae09e6b0c61368b4cc8ca80f23ce3af17584d08053f3dc957433d5ed \ + --hash=sha256:d9527e3dbaef1edaeeb2446fa7379446814a43ade8adc7c4a5ebe69437815ddd \ + --hash=sha256:dcc99ca132b667a4ed750afd42db4ea73288f18425a9b2e3c0af095665c491f5 \ + --hash=sha256:dde5e291548ca0f397623b5e523db5c90172b32aa4fd3ba464a79ea31a580b43 \ + --hash=sha256:dea74fd65f1fc5f7fe167916a473ebe6ed6174e5e5d9de11ea6583661be6cf43 \ + --hash=sha256:dfcd024eade5870b25f890c4df0ba9421ed8167d8d3d82334237512c1158dada \ + --hash=sha256:e0fa0bc6b1a184aee59b32efcd1b7f0e6d5b8f9387799e4c16a4cb66a86747d6 \ + --hash=sha256:e1ed46048d1920cabc96d952a0d5cfe4127ad8db572c335aae4e3c57b9278d7f \ + --hash=sha256:ec8a318dfc27c7d946651b3d9e8025d5734f30c168a822195601827207bac09b \ + --hash=sha256:edce90a1e588ec63adbf612cc0ad582de4169cd216c7ae53c15f42a2ee902f35 \ + --hash=sha256:f52f08101907609cc08db6a1f9f2a7a9afd54e9b2ca16178c9c38e99fb593cef \ + --hash=sha256:f5c6d8744d10b5e1eadd90a7c58f8546acf6bf680ee463f7e6ada09ad6c9f802 \ + --hash=sha256:f707bcf2c1d007d14d70531d4dd7b41060881c73efa845580bf6faaf9ea24d42 \ + --hash=sha256:fba8afcf265c6e9fbe1594cb045d4765c6c9a7d607653a8196067ef23566b843 \ + --hash=sha256:fdaaa4ea3242f6ad298eb5177eb042aea5c73c30e76d20caee7b15af20d24ec2 \ + --hash=sha256:ff60f0f7ccfda0e303ac43bec7096007b7cdf2c41b3739d1ec667febe67acab3 + # via vllm +quack-kernels==0.5.0 \ + --hash=sha256:08821ebfb8e638cc20308d5c59410c6dbb3b637ccc7b07bd57c7a9261a06af74 \ + --hash=sha256:c7c7338b67243397b6ca166e648bba161076e99f3858b532e1c877dcc6eaa03d + # via vllm +realtime==2.31.0 \ + --hash=sha256:9e641cb4d77ca0fe768515f8cf9f83550c79f49ce1550a95afc2dc0e252be8c9 \ + --hash=sha256:f6e494b53d6a6e80b6efcee6711c8dd40413a52e766271de1bce8ced6c36cc1d + # via supabase +referencing==0.37.0 \ + --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 \ + --hash=sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8 + # via + # jsonschema + # jsonschema-path + # jsonschema-specifications +regex==2026.9.10 \ + --hash=sha256:030fa9e23624e39b3b94e46b90a5abd1a1678eb2f58fcdd3fd6c27526bf91c7e \ + --hash=sha256:032da15431c890d376f53547f0a6219f4f4cd19f3e4f11bdc321453b5bd207e4 \ + --hash=sha256:044bd4639b6bb409ec9e5d8b7accd57e02b4c4a4e2eafde916f8ae8006b3e40b \ + --hash=sha256:048a89ee797db10160bd2bd519286577a6b43a100279bd4b7d8456a3d69c80a0 \ + --hash=sha256:05fb018cfe7144585fc83882405906ff84994a2d154afc2509ecc7752c51f864 \ + --hash=sha256:07b45ba5c94b8fcb30cb6c56a11f715c57533a3017964504322ea52690a27b72 \ + --hash=sha256:0aa7589394230e0f0a422ab6b90841ff12c87e855e7aaf75d192a54a5f124548 \ + --hash=sha256:0acee94b480dd853e39434aa9a575f95385b1b4b8fa3feae56db363ca5cad782 \ + --hash=sha256:0b9ba3b2765cdfe18f0f561a69f78a69701f2896654a81c711108d35d14e5099 \ + --hash=sha256:0c32480f3371b75068decaf9e5da72c224e953830dd71e36e06cf80e30ea39d8 \ + --hash=sha256:1270cdec69248592bbe38a0b263ed58d907b891bd2b93703e225c317e421bda1 \ + --hash=sha256:13c52fc377792675f604a207a2ae5958c080f6854f7698d40d9ff034d95b1e76 \ + --hash=sha256:14caa05ce39ec70437af5aac8814c50ee6628f4a90353871c059692f448a164f \ + --hash=sha256:1562aabd9d4eb09bd88a62ad97ed06800094b529ac43419e43020b9cefec79b0 \ + --hash=sha256:175cf49ce7a994c88b8f15e3cb17cdb66a48ebb2d36de736b8205033db950f89 \ + --hash=sha256:1aa309ab7ba89a62d6cf70dbd38d4176440bce3c7001ab86256704cf4c18c6eb \ + --hash=sha256:1ad10a135fa0b4e4a462a61d07c6654d7518cfdb5cb8da08f9ff7d61384af1fe \ + --hash=sha256:1b891f77554bff991804cee24b78b40789f7d5993a24c7907bc7025fd2a70c8d \ + --hash=sha256:1e321e2c84f0e52c457f5ea5944f796d6e8e09cb99738ea98dcc1bfe402a128d \ + --hash=sha256:1e954e246466d5a1a78f563ce8364b5d7cb19e7adb0ccdec8f9c9610083187bc \ + --hash=sha256:1f0a8b4928823bc8b217a1ab7bf3d90598909dec9a70fbbfe9a52cc4eca55990 \ + --hash=sha256:1fbc8314436353e097c050e11b01a6c11433579437ed0579730157676ef59e2f \ + --hash=sha256:20e8bfb07ad79a282f8b95b56fe67f9750b1b7f775724e4ba1f23cb296115ce4 \ + --hash=sha256:217e98ba5fc8908ed8ffd4ebac04753a0c831067cbfb495b9821b94cc61eaa76 \ + --hash=sha256:239620b0e0681669367c0e218c8eb2551d9f8fe3b9fccfc8d0003377804e8348 \ + --hash=sha256:23ac9a28180f274d7dd7651fa131ad5b02d343b75df4b040737f0356223895dd \ + --hash=sha256:2479171edccced52ef02b899558f88ab2c235fe05b93180fdcae1670aacd89e1 \ + --hash=sha256:24d12a625a37c89c2b09303402a06942f55f071b95a7916a49c17034c3d47cd5 \ + --hash=sha256:2dd9286093c71afc8f55ef035c5b9d2776641fd72c6535f1febc92d0b0be9666 \ + --hash=sha256:2e67f8843f0e4b931f1fa860bf3bbe4134b714c0155cc5c7c0d7ea450230aae0 \ + --hash=sha256:31e4df2b11d48f61d511019bc1ee9b477055f17c352b68fe72db7a98b14d603c \ + --hash=sha256:3264132d576847ab5f88bb83e7debe67854bf165b3ea613bd467312b6099536a \ + --hash=sha256:3540734dbe241ebb3b87d5713781f6749a3e4d45480f506aa5fb5cbb0c37d249 \ + --hash=sha256:35ba3bab0c45079735f55ac61526774de1d84bc4a0333cc554e1a4ab74913924 \ + --hash=sha256:3a66e40a1a20de96a2fee00ed67e11012b62d85b277688258677fd19997addb7 \ + --hash=sha256:3bdeed3318a8eb2bbadc9c56347e0ff651639e934a47e168d05a3b12929fd0e7 \ + --hash=sha256:3fb4ae8cf83ef4e9addd43b2da31a9f45be816a8036fae8af59c8998b72718e2 \ + --hash=sha256:4971776b4f2bd7fd9a83eceb2cb2592cbe2924f639fe8045e6a9de5ba4bfcf25 \ + --hash=sha256:4a761ea45f2ad74c575ef5850ea514cef97302a552d3c7c9d1a1a870d4661d6c \ + --hash=sha256:4c66d54042a14a503907d81861b8a5235e6d1f03d4fbc1d8767f652eaf957ac1 \ + --hash=sha256:4db7d00c4afbfbb55b8e17b1e371da11418ea9389b030acec63c1fa4c7ad4b86 \ + --hash=sha256:4f0407474ffac8e5e89d93ca41d60891e29f0ab8423eb66ff292d850a86a0843 \ + --hash=sha256:53e182b6b04d0011909b47d51a2d72d908de07c7b1c7f16b3adda2204d723bc1 \ + --hash=sha256:5847e22bbf959764d776937d791d034cc2d19b787e361c88d97e859e8dc68502 \ + --hash=sha256:58c01f7b81079cf0817ba831ff4d9eff5d28be4a3ac76c353e6f09bd63f4c386 \ + --hash=sha256:58da726d3e766c0b3f5a3997dfaf0275898a1107b8191cdd6b0437fe45fd817d \ + --hash=sha256:5bef622850cf760154719d4e0d74b0a855962432995168e250069899ae12fe8f \ + --hash=sha256:5ccd139b2061132e7b265cfb4b4721baeb9f8928b81415304abf1ec7e3181c26 \ + --hash=sha256:5cef9f3d14796500ea834c41dbe688f1f6b23c7024dc23e8a794d7ebaf5d71d0 \ + --hash=sha256:63bb62cf62217dc38c8a6b2b61b165b0e4eb8fa93b0aba12139251c0986a8fa3 \ + --hash=sha256:681ed38664b64c6617d3c3c332018d1948c77e139c5ea667c1886efa671e426f \ + --hash=sha256:6888065672b341e5246f391ec16dc258a29218ac784172fd67c30d941544755b \ + --hash=sha256:6aebdd9a946de328b3f6f61dbf48dd064a36eb6dddf96e34ae6651d37f6e9383 \ + --hash=sha256:6afcad14310f1311d077553ed374b42a5e538f85a8c884b4e38e52de091c8077 \ + --hash=sha256:6b34a778c695d24e77c140e3b4c95da69282e34f2f6b02b55656aa4a0379f643 \ + --hash=sha256:6fd555fc9abef50c530869690b2daca054c8811a7aff632d11f9a7b2590b2742 \ + --hash=sha256:71879292c9c7ac67b1680345b16daba1be937cb027362cfa04e68f65db2dcfdd \ + --hash=sha256:75242f44a3e283106077be4ab717bc535e4701c9d54ad69e195945c22f137a1d \ + --hash=sha256:75aa39d3f4f1650eea84e46b0d8cefe77dd5478c10e3d0aaf0b0f00493475a7a \ + --hash=sha256:75f9297b16fcb588a1f8d8a55dabef3c0c20b0c7bac43c87ceaaaf1a825c12f4 \ + --hash=sha256:79e9432995e14c749d34209413de5e621ec8e67789bf4f46dbfabea9d06a2406 \ + --hash=sha256:7abb38b8c40f3a235235a44da452c64b7b5c1d650ec6351027db0e090804f2e5 \ + --hash=sha256:7dcad477c49c4c626a6c4fcd71b39a971aa217060cc40a6569fd24edcc0fa509 \ + --hash=sha256:7e6c0b5ec6ddee4032247585dc491b0fa58627745b66a705728703a3f0331231 \ + --hash=sha256:7f8f10015866608fe4c043cec2e4fe4c39a94bb50e45091de4cdf4004b9ae4b0 \ + --hash=sha256:866de9f98df0611d7b62b3a8729d3284a64c0cc6edd90bb95a533e443a4939cb \ + --hash=sha256:87f5f75c109f08f5c602d68e1af54cead8165189c727b6ac946b30b9833a3ba4 \ + --hash=sha256:880ac684c27176464c00c3fdc456116364f5ebc70da07aad0c2d4a7ba45e98db \ + --hash=sha256:88b02aa8d0ec9b6189fe933d425775882271c23700ac11fd26d1779b0f56fde3 \ + --hash=sha256:8ba1f78bd4fef2d8f84b894ec28ac3481afe6cc07aaa253ad4717ef7b3fe6bcb \ + --hash=sha256:8c07021a4faa3f092869adbd1f35cdc7a592276c807aeebc3ceb8ff1a638f0b4 \ + --hash=sha256:8d5c4518235a2ec1611e57af85fa488d529c1106aacff12adadcedf8687012cd \ + --hash=sha256:8e127d9a80cbf1c3276bb465c6d047e8705e97b58c2b8f2f0c0a69c336b44b37 \ + --hash=sha256:94c5ce3bc41d226b4eb89ca3f842b2e28c031487fb1f34eb2153d98235831325 \ + --hash=sha256:94d096369b7cd96d15343fef5257fe39eff9d0e8758b92a0e15e358b92cdb2fc \ + --hash=sha256:968c1e33edd9a104d1bf24c8d476c72de7e3839ae7f894b37e9e4f4739fdeeca \ + --hash=sha256:990797e765d89a423880052c68b61c31afe701de94a8c060f61c40605ca6c727 \ + --hash=sha256:9ce239acb15843ab03976626af810a4424b0409689ec2bbc52088ab5479ab487 \ + --hash=sha256:9d772586951d7d6a5d162d48f414065e483b1c81ab38fd8ed97c78b05883421a \ + --hash=sha256:9fbd2e5d8002dc49a6129fb321ec51c57a025e752ed525ddce0ba9223c4350a7 \ + --hash=sha256:a41693eb3fc4b92e6127d113813c6c395237f7edd3224abf67609af48c690d11 \ + --hash=sha256:abbfc1c33bf8efddcc43844aba61e036d74a918680dc3ce8ce2538b004eda0f9 \ + --hash=sha256:b298cdc33c5cc6969ff07f0fba19cc73e0fd8576373c50935feadaca2f6b4405 \ + --hash=sha256:b43456de605c8ee77eb75f07bc1ee44ba27f9cee22207deb77d495e954b7d953 \ + --hash=sha256:b71649169a9fcf30b395ee01047fa7ad6654a4c900ca75b23c04dedcce6a1f8c \ + --hash=sha256:b91c37551bf39d75116c02b146956f65b9aa0337a4a652f4ae186983789d4001 \ + --hash=sha256:b9d36b03dc362aa40ffaaec9d9bd75e87763529563ec008c43b0e07782f5be7a \ + --hash=sha256:bafa41b0dd63669e5c0f8adf3d24819efeb73c847f492eb011212eb352e69041 \ + --hash=sha256:bb7774924f8cd69f49cba0b3c2d679a6326f777e0e67d130ad5203e4df53f0d3 \ + --hash=sha256:bf29611e5376fec8f795879bb5c6153a76c3a292573d173c26784042b01eb840 \ + --hash=sha256:c014641157e9049b0603b8daa5343bd408d9b757b709aaa0f373cd3fab2d7944 \ + --hash=sha256:c103b3b14e011774af4fb7e4617ad4d72b9171905cd3b231a70a4efd76e477d7 \ + --hash=sha256:c22df8dd6373bbe3898e77429ffc85594300e39d752fd0e68a31e59d37899376 \ + --hash=sha256:c25a754bb81a2edcfc3b65eda50f017d736f818112ed43e8aafd595cb00678ae \ + --hash=sha256:c32818b28bcd153b25b63038348a9fe9b9fbcddb60df43f204c3ab55eeb57f77 \ + --hash=sha256:c37fa93bf18bf4f90b01c0fa9f11ea567ee4b7dd8bf96e63663e5edc37aa38cf \ + --hash=sha256:c3d95d7d9538b5b726dd6fcd7b6117a71e6565202f6d64f5845fb4d8f203f533 \ + --hash=sha256:c8fbd9cb30c68c1686b94029b9ef845d5870d3d65baf66cb126b676849b9d72b \ + --hash=sha256:cb76a9c4e07a6a47849726af0ed14c41741a182f097f134a8cf29c1bc0f4dde8 \ + --hash=sha256:ce7c118cb102975f974585688357a717ffbf9dddd64ab0bb1bc93eb5b367cf95 \ + --hash=sha256:cf377960d2ac37d987394a9dbaa75e91338c41a46d41e1d25e90125e7b3ee2dc \ + --hash=sha256:d278ad30ec83b6b9202685b0f80b741a51ea3ca7f0595ebda96e7628b6398876 \ + --hash=sha256:d2d377fd1cad611b806cdd732d86b65f536c768209890cb442556548daa65a23 \ + --hash=sha256:d414c411c06fe0009eac33488fb1591c66b5c2673e342e452e7bb2fe63da8194 \ + --hash=sha256:d8c668af8f7bdb1d18739c27d30cd9f4b371495a883f75a002fb7a39d740fecd \ + --hash=sha256:dce932f8e3ba936475ea3d0d8b59f7b050a9e206e994f53f8fd80299871e87da \ + --hash=sha256:debc629e98b95abaea1cf3057ca296151f348c697c9b8a59d18013adb302c0dd \ + --hash=sha256:e0dc78251154b66dc60211563fc115345da332eaa881e4e2523fb1edae3772f4 \ + --hash=sha256:e5e4a6e0734a685d13b9685622bb503bdbb2927f8b0df025a5085f0ea067475b \ + --hash=sha256:e6b99181d184d0f5c7b36b8d12b94d1e9499cce6246594331f9edc5d2ea9fceb \ + --hash=sha256:e7327795089ddb44912dce1434e1d7244be2e9fb48fcc2d6782936af7a3062db \ + --hash=sha256:ebb2ba68e4641a994061f70bf44ed448fba0b9b1d18c94ffb9efc1cca805b39b \ + --hash=sha256:ec8855f08c17895a26fbf5f19ed829722e19b34a96629e49a43c92974924026b \ + --hash=sha256:ecb2e7acb18f8cc4a67f0ad986c0af291ea4dd385d0614ba9bc09d7f8bbb478c \ + --hash=sha256:ef4c0a9dfdc90581b90b1b95a8c3d1557f8ff8f5a2a53536d26314de699d1468 \ + --hash=sha256:ef4ce69ff97fbb44b46751cfea5e859ad0b66d1a50abf34954f0645f51e81671 \ + --hash=sha256:ef5a059ea1c6ee5d1c7e99a2484e628608d010921efe876c6f0e2029d2f35eca \ + --hash=sha256:f0e2e5d23448b660d60a6ed85c46cc03b4b48bd276b8f4041d4a5fe2a4a0626b \ + --hash=sha256:f2374c27deb189b282ec7e16106752c22ad39b056bbd8018960b1e4cc95d67a1 \ + --hash=sha256:f2f43bf4e47ff7ce9e585558706d698c6204d0f80bf2207766382ed817c8e9f4 \ + --hash=sha256:f5c629df03adec31ee505dda3c8988f106c9390e4cbd343600036eb8b3d6724f \ + --hash=sha256:f70b9f0e39c2dba1d9da6bf7ef7c377cad7277f8440e9a69be05ede529ff024c \ + --hash=sha256:f7d4656e17ab736e9415a6442a345bfc97bb8b7dcce47884bb74a37f70f08d0c \ + --hash=sha256:f8bdec659a8fa7af51a32b224b3b7c02bc415d54ffd35187b1d224176b17d607 \ + --hash=sha256:faa911fbbcf8ac90bda0e0657d60768e3390954ef0588211d63a22add1cb1cd1 \ + --hash=sha256:fbc4e2f3cb7ce8436154e6483079e7d35eeb321a952fa936e180300630d8b873 \ + --hash=sha256:fd6bd89b9fc06018d35851cab0240adb7dd84d51941b19f6574ac90cd54e3ae5 \ + --hash=sha256:ff4d7b14ea19e50c8d9d6d83f45bd9b45cbb624c07ac1fa54db0a019049abed7 \ + --hash=sha256:ff6b3267318661dfddf6b3628663e00e5946bd0a5c8fa678537a1401f0388f91 \ + --hash=sha256:ffc2da104e43db716ce30cef9f28049a1faa6aca385dd8771b033268d0730b07 + # via + # tiktoken + # transformers + # vllm +requests==2.34.2 \ + --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 \ + --hash=sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed + # via + # datasets + # flashinfer-python + # harbor + # mistral-common + # opentelemetry-exporter-otlp-proto-http + # python-socketio + # tiktoken + # vllm +rich==15.0.0 \ + --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \ + --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # cyclopts + # fastmcp-slim + # harbor + # rich-rst + # rich-toolkit + # typer +rich-rst==2.1.0 \ + --hash=sha256:7ecd1343ee12c879d0e7ae74c3eb6d263b023d2929c6d114212eb1fd91057255 \ + --hash=sha256:f4d117b49697f338769759fa5cacf5197da4888b347b9fda2e50aef5cd8d93bd + # via cyclopts +rich-toolkit==0.20.5 \ + --hash=sha256:0c9e1c414ffb0720be26285d472e263d1e704b71d31a7e13274b9996db4969e1 \ + --hash=sha256:e21fd616db9c0539d3c50f3433fb1173db95c19d3cbf215615e224182fad6c18 + # via + # fastapi-cli + # fastapi-cloud-cli +rignore==0.8.1 \ + --hash=sha256:0089e02bc87182e92b0f676659c748edc0378a390510ee0971e14d60b4e53f78 \ + --hash=sha256:0097a8c35106997d2b52851f0888777efe10e34772140a9fdb018b2f99238159 \ + --hash=sha256:0378cf77b8d64560e0cb433deada18438b36ef7933dd284dd65347d03c56c429 \ + --hash=sha256:0585eafcacf15f8f2764b8d03a579761296e8c51dd0558f8eb59040810ba8404 \ + --hash=sha256:05fe665768bfe8ddda77e5ef1c2332555ea2abecd33aad05fa41a19d959ba011 \ + --hash=sha256:08acabc3203c68a8bed7689c50b546a4ad7c9144ced2199c1261a149f7a3cef2 \ + --hash=sha256:092930871001f214069d03744225ccef7379a32a515e2f005f9f642a2ef34629 \ + --hash=sha256:0cbb7afa893d46ffc9a303a76d9058e0215ebf085c2e3100e31d2226f6a9bb7f \ + --hash=sha256:0dbea2e28538fa64f8409085abf50240f1f7c5981a5479bbb1aea7b84d44aa45 \ + --hash=sha256:0dc7765f94d6660574d648c89e28c9932248499d47dad49c2786f2e49231e1d9 \ + --hash=sha256:1160abcd855964a9dd69f3d603ef57be14b1ea51ebaf50b07f737a3f3a8b89a6 \ + --hash=sha256:121ab7ac93e39fd1d70098461c1ed9a6fb89d54e7bf8c60ae351b23b05cbb8c1 \ + --hash=sha256:13addb2a4805736a3c8adcd1c60871f517a4bd8b3a2b127e36d1e701924ccc0f \ + --hash=sha256:1467756e8454d3f816131bbad8b0efc52b7f6924e9b151f2962cec9e2f4af706 \ + --hash=sha256:149b7a5cd9ece252ae28b386a979fac575546af5cb667a3f93148addfd8e6686 \ + --hash=sha256:1524ca0df28b6c967fb9594a00cf1ba79e280dc513ec3e02d29345c79390e028 \ + --hash=sha256:16b17b02d3e6ff3b88583af4809b64e14c77cf4de195763f063e5e77d70cd112 \ + --hash=sha256:1867356484fdc502695f71c2c986cde702e2a14d6db76ca3837a6f066cafb93c \ + --hash=sha256:19606039e35dea7c1a8360f76fe39c5df6715d593a4f2e0c0c712b1b50985d10 \ + --hash=sha256:1a3454cdd8bc145fd055dd45f650ccf1509e7b4edfc720152eb2be594b230f03 \ + --hash=sha256:1c2a29e7f54e879fa54982887de480b2cfa6e3cb51f01d70dd4e4035f5b79670 \ + --hash=sha256:1c668a541ecd8af3d7bd06c48c62e1c3e0a755028d57c07cee697a25d9b0678d \ + --hash=sha256:1ce5c6d8f02badc55112b014d4fc9af662f0649869913d25785d7b3676ac19ed \ + --hash=sha256:20743f8ef88aa6211f5ce3618c2dda34dd0afb4553e70ed7b18ef4cc1a0d8d9e \ + --hash=sha256:2584771ef0e3e6c12f19bc50eafc1aff338e125efe0030ba0c3b29731faabf6d \ + --hash=sha256:28a3baab3d1b7ea42c38e492d4a100de3e5c67217432364c62b5719e0f04e96a \ + --hash=sha256:2a809a250f1532b93dcc52e173ee71adcafdb536a125532687666047c4537ac0 \ + --hash=sha256:2aa76742832db1fdac070fa8e694dda0daf6f2f7191832bd555d1e80f8c9c42b \ + --hash=sha256:2b6cf58501e9ff1b6a71c3fd66c8a105311e1f23237626fd4c9c00606bb3d30f \ + --hash=sha256:2ca91c91a53135889945e77b286215ecd41c8f1090398db97ca3459c5a290eb6 \ + --hash=sha256:2e68cfc4ee0a2909952af2aebd608d1cd22f7d1cdce332cb0b5ea3762939865d \ + --hash=sha256:3012fc79b19953f76a67b2e6bae5f456742e96f0ed33d3c6c9390b0586fc2da9 \ + --hash=sha256:304ddf5f807c788c08a8210f9977131e7bfa9aa94704668d127689c00279ef41 \ + --hash=sha256:3054fab93e2d2ad483cb89417f7b883cee025cb22286eb824af0073f2ffd5f7e \ + --hash=sha256:329ae0bea9598541cb818bbecb1e1f56fc1faae77828ebb3846ef2b24a050d05 \ + --hash=sha256:32b0fcd01495cc4f4d10b5307f9f15818c9db752cfbd7a9ecb00b0f129a70dc2 \ + --hash=sha256:3538084cef4a66ba3fee7c453d17db7cfb32a6653456381b62afd8d53090d6fa \ + --hash=sha256:36381a30591b410b15d342279e1b3a3515e240a734c37d119cd0a9e6f71ef596 \ + --hash=sha256:39478de64db6d233d1bdfca58fa8223a073a13f0facf992ba05a739a093fe4ae \ + --hash=sha256:39b300b82e90f7a6ad102544b777f2aa8a26e9260ebb021399ee5a0a708a7c2d \ + --hash=sha256:3a437c870f1465aba36eb4ac7108c09d5097c436123fdaec980e7a26a4595141 \ + --hash=sha256:3b1018f5ad66da4eee7f451db38a10444aae68952dda2afa52ad49207acf8490 \ + --hash=sha256:3b745c799e031dbad32aed9089d02f6c7be6024284311a5a76dc2f7d43635409 \ + --hash=sha256:3d463ce689125a3e311489d1b1b39b8471fe0d6e76b8afe9e26b40766a3fac19 \ + --hash=sha256:3d581bf107490abae4d40a9aa823a79e18c88a21658514ccaf95e42e85d278f7 \ + --hash=sha256:4069fcdff01999cd2d5a426eccda45a8b31a3e0eeee8b5a9da5a452714cbb2c7 \ + --hash=sha256:4225f2d2f0b3e3c39a815f15dbe551f39f0d7f82e2a92d16bdbdb07ff4b6718e \ + --hash=sha256:42480707cdd5f92d6b1252faada600e03ab22fbfd53b124c6f7b33c21e06b6cf \ + --hash=sha256:425b962f3d68b86ebb785409153708a38b0658542c918ee706ea433e2547c805 \ + --hash=sha256:4268e83bcb88f240ed4f43d73e7de9e4bcd8026bdfcfa836567d6c29863e5361 \ + --hash=sha256:43342bf37e7bb57d69f766678c2b19fecf2b3ec757be3f1bb2fb774d2be8c81a \ + --hash=sha256:45ec57389b855fa95b251205e3a8a1963a7185f28665c1a0b24e05d05012da89 \ + --hash=sha256:463f9734f06f2dd939c45f0e58d50c0648f799e9c41fb657e40d236054b14cfc \ + --hash=sha256:46aa37be9078fca572461a40b5832dd80aa34673d6e557d606feafe57072afe0 \ + --hash=sha256:484c0a4803a1eb9097d875df3200e60e58cbf316d4d40357f92e8e757ce529dd \ + --hash=sha256:4862e015736af87a4afa1a75023d28363ee34449928f8f5b521d1f9eabb0c826 \ + --hash=sha256:487fb09f98c1e771f52b8433880c0012c25c5868db6e6f9f740ee0d3a13d4ae1 \ + --hash=sha256:4900bd1ba8938e5a5e601306504c7b8169783a2d44a8396c8fda4a9b659eed51 \ + --hash=sha256:4cc3003a462778a75aadabde87edebc0e1d205c119c703c155bb03a4f3e8be23 \ + --hash=sha256:4d4a70c9b857657c3a542fd367e16ae106fb6e3e4448cddb30e4edc604d5a025 \ + --hash=sha256:4d891dbe52b1aa4df22a69346e731ee3df219128956ca4391722773e6baab16c \ + --hash=sha256:4f020b018577d0081a2b23da199d22c58f4cbd63935c8e991adffbb1a755b467 \ + --hash=sha256:4fb05a077bb4b90b51c3fc7ffd4110c3003a90ccf6ec1a8f8a4b2c017a3e8442 \ + --hash=sha256:5094965794d163e1c4a71dccf3d1c3b3df86802e3afbfa461b300aa923cd7ea5 \ + --hash=sha256:514000eabd84e8f6f6a589a30d00e76d7cebe0d99b6cacede1223f64dca4b742 \ + --hash=sha256:514f7be6d04a6f6be4835748b14bdb027799759d02ada03ead064a50c763b70d \ + --hash=sha256:516ff2dba78230ed5a150db4fdfc0ad435a5a9137d27366e20a311001ca5a878 \ + --hash=sha256:51e608acd3c1aa834f2b7d956ff5380a0a6b6b595df80e665f2a2889982f1855 \ + --hash=sha256:54bd28cc90dc4c89c92a9191bc899e84786087bfb7c417ad0c29aeb90c2d0333 \ + --hash=sha256:562055b620844654f0afff4df0a9c83f3679452c276cfa76cc5e49c14ddba481 \ + --hash=sha256:580e019787032b430b857335a66db53d7ae0a200586b3a6b5d3ef227648300d6 \ + --hash=sha256:58449c827c36dba68133a5ee4137b67d7d6e050234a95c3a8f5e3cefbd6970ec \ + --hash=sha256:58d7172259fd45d8ba00f3e02af5af2113f547f4a7fefab42cab567aa7c999fc \ + --hash=sha256:59f4f92ec5165619b3bc58130ab558bc618f9c5e9df06807339a85d965143fd5 \ + --hash=sha256:5c51c54dd953d18b6cefd5c6c70913aa87567efbead5c1f34005931305f9c53d \ + --hash=sha256:6012766ea3a5a635d9b79f3e8c3797d5e47ce5e5dd81993c9f92a3ea4ff68b4c \ + --hash=sha256:601ff49d8458a21d745908e35f792c02b45ecae4a37fa3cd1fc03fa065962bc0 \ + --hash=sha256:60f1ae9eaa51b50d5afe99bd25ffb2cfc10729701ca08c179489555d2614f3ae \ + --hash=sha256:62470f4d70d83f124381975614ed7c7db6f5c5fb4d777ee88f7bdb9a7d14b65c \ + --hash=sha256:6602011b6e6ecd157f3b0a0a25b1777b40c541ca21de6ebe2137af713d0efa15 \ + --hash=sha256:66623b4b19027202abef32ffdba74b17722b63f9b2024a6a2d660eb0c4f4abc8 \ + --hash=sha256:67a0ac8ceceb67fe9acb68ed16c29c30303395795c12c699c1af55e4af07410d \ + --hash=sha256:67e30c0883f9aef3bcc45e5dc6980bc41f161da6c1b1a460f0bd87064c1d6594 \ + --hash=sha256:69be37202a052d9e13affed6b774f86d8bf3b417b15d54ad737033e56da07ec0 \ + --hash=sha256:69db47c2fa51d88e93b4e4de4f44220ce7a9337e4b7526a16f5660b19974a8ca \ + --hash=sha256:6e658a26627f27e15ec54027f051c50818c86b60f940cc20991054e00f5c0152 \ + --hash=sha256:711369d017f8959e3a253dedb76f6d2f43eb669a4228b2c1b796e30a5370ca36 \ + --hash=sha256:74996d8ed1a494ff8e61d9194a0dd3637e6b19a582cbed6fe0e1a4cc60e7b266 \ + --hash=sha256:75b9782c32434196c66f475cdf7be6f2a8c15c61c8c238cbe38b64657137f21a \ + --hash=sha256:773acea1c43241d5e96db86d52133f3098246337639a1f6f474ee8d5b304b36b \ + --hash=sha256:78490b93aec14a87fa4c23e634b1d1dcb2bf5dcb90923f3b03a3428892a13d9f \ + --hash=sha256:790e14dba9e1f8a532071d64e1c2fa7529c71db75996e95519d06d3378cbbcd5 \ + --hash=sha256:7a184fa45db8cdc7a8604d2df020a107be3fc0adc2e522b4e4eb5cd5b57d5f84 \ + --hash=sha256:7c1e95cc854f8666383905582d879c1dd3275a983fe2e312e399c02c90fcd0e8 \ + --hash=sha256:7cad647c6e0542219cfa421780e854cd1890936bda7096cfb9d859fc84eeb441 \ + --hash=sha256:7d38b47249754453e435d728ec019decee5ab450d50347025605484aa7526bdf \ + --hash=sha256:7ed31657a20df59b0bd63283d278e8678749aa0154ba98fae0838a9845599695 \ + --hash=sha256:8232b4a3e5a51f45f3bb8c53be7c96ac437bb238e414dfc37a0a2a42a6c90394 \ + --hash=sha256:831c9e863acb90176a47db90e55b33f1e6305206d708d784a0161bdf87b8ae5a \ + --hash=sha256:86f98b4481c8d60635e8f2c2d33b92d0b8c02d2a87eea49e19d7b25cde87a77e \ + --hash=sha256:87d0e03fde346256577b7aaafce44d593a7454995ea88c03005982c80890d6c8 \ + --hash=sha256:8c5ce3b10ce4b716abc535bc0fdd66b0b9fa9f5b1987b36d3f5ece7d0e2a9a81 \ + --hash=sha256:8db2be0d49175d5db4cd503f8dc62cbe687b9ebc71af4cede1c8b40bab8f4f4a \ + --hash=sha256:8dbc5898945c027dc0ae451de0c983e20bd8fa8297f40ee38d0ce1a3ed924d4c \ + --hash=sha256:8dfa13e24fc32d3df33d40788cd04e07f6b607f3c5306808ee0c285d2135effe \ + --hash=sha256:90e60f0073caae0f1d59c993adedafa3a57bc6fac551669677cb25aa7fa9d9b8 \ + --hash=sha256:916f8602d4dec7fc8b492883539b06fb3fea4f7b29ba8e4c300e77b21bdb8d28 \ + --hash=sha256:96ef71291f53bf43b85055c220697053c35173418aa1a5452ae71e86ef83d3b2 \ + --hash=sha256:97fceadfe03f3b8cd77cceeed92d00fad67f3ab80a0fd4d31e8dec104b721018 \ + --hash=sha256:98028ec3e74c216fbba4a01538b7b440fdd6a908ad330dcbacbe591bcc06bb2b \ + --hash=sha256:9869b618dc28104003292f26cad26a2d54f6a3941684f16fe9ff435b5df1b0c0 \ + --hash=sha256:a1fd861fe4c1fea39541e7c9e8a409472b9cf7604bb472fa9dc09c97bf7134b0 \ + --hash=sha256:a249e142d2d313872e526949b9cdc5363041d64531f53553e702c00747225dc2 \ + --hash=sha256:a2e5df9ba53e502f676b054c4d12371273204e2b9c6ec29afad3a155b9ad3399 \ + --hash=sha256:a6c53ebb7ea2d22116d99ec155c5313c3d76220f06b4118c1e1de629de721a95 \ + --hash=sha256:a742a2c09a63bd65a52ed6047ba324593b6bf2080bfc570af199b00d617a7349 \ + --hash=sha256:a82567032d81f559ba4a9f3e96ffcb48ae149dff2f44c58c966b5890eb248895 \ + --hash=sha256:a8ddf078c4b5da0665a83f7b558dee2d746293ffcce3dfe8786015aed0aac4c2 \ + --hash=sha256:ab53a0908a1f24d2aaf4201920932134fbeb19be1d4c9619514ef7c781c9f3cf \ + --hash=sha256:ab668664e6388afc08fec50186ec21daf17c738ee71bb039d12cf5f14e964dde \ + --hash=sha256:ac0a7cadcf6154dd60b2f101425644179b34c540a88b84089995c1745e8c623e \ + --hash=sha256:b0ad0565d31aad6500a7ded6affb0165ae39560b6b056c6b179e5550ff3a887b \ + --hash=sha256:b2cf5a1498d7f1a14fb5599e59805875cefbcd8b7d989e3b73f52829200ffc60 \ + --hash=sha256:b4316b266e88c25ac7b4a0c765aa93c436dfa689a29429bd276878db45e17153 \ + --hash=sha256:b493c787dd2cc31b1e1c678e626bde593d83f9df420096f91dee6841ca317bfd \ + --hash=sha256:b56052bff3aa1e6dacb42192be701efdbcc546c52f6302d4146ed93a31df4593 \ + --hash=sha256:b5acf12993258f0eac4db81c30bed5469ebadd1b5bb2863c985fe0d802d94e0f \ + --hash=sha256:b60f3751f681a12798927d51d1d6a46b81fe7f9923a513dd8fcc7947d785e12f \ + --hash=sha256:b9e7ba47a5bb25ad45d39983047ecfbcba01ecda0145458c548cd3f390b73bb7 \ + --hash=sha256:baf3188d642019c0b9e93230cdca140d2a46dd846544d10c19a7c857dd953eea \ + --hash=sha256:bb58e93997d546fad4cfcd730a54d7c12cd7a61fe7a9b31bfc75402403dc559f \ + --hash=sha256:be994859b2cbbc69338351bc9908dd7d049de232e6eab5e998ec1feac3faf785 \ + --hash=sha256:befb772556c8463c640b290f632b57440182edd39996708a33c50bcf437796f9 \ + --hash=sha256:c0a66b10f89af390b014e39db3f20489336eafa2521872b0c05017459721a420 \ + --hash=sha256:c199fa2d4a898e9b686d846371ead3c8e08d3e29f78e2ecabf4c580820a8f764 \ + --hash=sha256:c49ea7bac11f3bed7af4a21fb76dd7820341103dd3d68676a7177d797262b84c \ + --hash=sha256:c676080bb70cbd5429052bb5a3827b35543876a42c792c6d713d9e162bdaa00d \ + --hash=sha256:c735c5586f4306d9a62ad374093d6127b24ddadeaac322684e3e4c923eeb6a58 \ + --hash=sha256:c75ac1952ca8892422de328a925f4804120b149543042313bb3af7cbfdd65d64 \ + --hash=sha256:c76ef4db664b9bde5da57f1cc58ed8545f9dfee864fd51eca21c60ebe9206dae \ + --hash=sha256:c96c3a434b50a7663e011cf6060b0f9ba44fd956b310b230bbca9dd665bb1f53 \ + --hash=sha256:c978f26c25e66c118da1e6218f34cc5ce2bf9c5cc3969011cde47bacbfe4a072 \ + --hash=sha256:cf28097a83e1237f87e11e33fb12def022e103cab2a20340a19753e8e222aebf \ + --hash=sha256:d398c9e0d2a51ad16c296ea287a9e70d75715ff5dea124a7215228228cec8f22 \ + --hash=sha256:d59e4ee62d89136eb3f0f04db023c17f9294cde905cae2a0fc8d89a4ab86c57b \ + --hash=sha256:d63559131efa9cbba82494ec8698e38bea403f214969066c661b3eb4da4ad4f9 \ + --hash=sha256:d7d56bf138418e31991ce17b738c491fa1ac098bfcc8c3ba67fc0faeecfed357 \ + --hash=sha256:d886d6bbdd0a1a3ef73bd38d6768cabc9b30bfd1be7b157b27a4ac7ec6c5244b \ + --hash=sha256:d9a9812dc25dcfb9bf2e546247f6e38b0c29a0d39864eb62de2a48b2ba41b961 \ + --hash=sha256:dd43540d294fbcb66daf66836b6c57043c0d3af672722099cd4a0c91448d948e \ + --hash=sha256:df64fa4f0b451cf198bcb1596bdce1d976396e579188141276d4ee8288ee2486 \ + --hash=sha256:e0ac8e46f89a824326626f4023253cdb8dd09c55c65da51b9824200d261697a6 \ + --hash=sha256:e250726b08957aabcf4e28aef4ea18bcccbd95f240cebb75a74b424f067dce4b \ + --hash=sha256:e425a42f6601bfed266f76f3294bca48efcfdb10c3c0c279fb2f977b1e2cc2bc \ + --hash=sha256:e4bc5ad282f7f82d9dcbb51b2e079f9a09c189e8c36017833e34003d253e0117 \ + --hash=sha256:e68a572efc126aa45195f1581a5ea97c4e36eeba6874a6635ae44daa4fbec7a4 \ + --hash=sha256:e8315151a83b982fe972420372e80c550616095181d1549faaa1ec31f363f5ae \ + --hash=sha256:e8625d98b9d83ef55f11c06155bf31cee21bab9c1fc001459a7cca878bf1e8c1 \ + --hash=sha256:e8c4905de9800dd404084e2af0b97fddbf002e283663809c37d9b10dbea1c024 \ + --hash=sha256:ec800546b960d5044d2468d22dd0689210e846350c42cc9f02519c2a2528b3ef \ + --hash=sha256:f109c7f0c579a8fe795300d830eea53e5e28a96c6f819686c33f7d4afaa9c9b5 \ + --hash=sha256:f2f0ea489c0e3439f1e94f2323e55218321b0a5f098dda4f1537e1caee12b035 \ + --hash=sha256:f52098f3245c7557229d8253ea3a53a03de03704685aba8a1ccd24a5f004db70 \ + --hash=sha256:f7136b7ff29c37c8ec8effa3c59e27839482ba542af94fdc2581a59405e99037 \ + --hash=sha256:f7b929db3f8f3fa240b0f328453de0b1b747e51b7992a9198c8412d16f4f0027 \ + --hash=sha256:f8a6b6664b3e9b403af1abcf81475782120b52e1f4063250638dce18a1856fb8 \ + --hash=sha256:f922dcf01e6a7ad26adfcb7f42635ae63066b883d409876129db62297061327e \ + --hash=sha256:fc042ee2f3e3a38f6f44bd9f097c81ee94686897d3931f5e43d21789d34a81a0 \ + --hash=sha256:fc48dde27bf241e3982756edad000ca17da2e42bdf163919932f1e37d588f404 \ + --hash=sha256:fe7a7dcd84affbf312336991202c11922c13aff323e2857c5baf10afc0dd7f2d \ + --hash=sha256:ff09db59f672d929bca88ee7089d3697256967df76a2bab8b187208f2b517bc0 + # via fastapi-cloud-cli +rpds-py==2026.6.3 \ + --hash=sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5 \ + --hash=sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680 \ + --hash=sha256:127e08c0642d880cf32ca47ec2a4a77b901f7e2dd1ad9762adb13955d72ffcc9 \ + --hash=sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538 \ + --hash=sha256:168c733a7112e071bb7a66460e667edfcff06c017a3c523f7a8a8e08d0140804 \ + --hash=sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf \ + --hash=sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4 \ + --hash=sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97 \ + --hash=sha256:1e5822dfc2f0d4ab7e745eaa6d85945069329beeccef965af3f3bb26058fcab6 \ + --hash=sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96 \ + --hash=sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a \ + --hash=sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187 \ + --hash=sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975 \ + --hash=sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f \ + --hash=sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703 \ + --hash=sha256:2bfd04c19ddbd6640de0b51894d764bd2758854d5b75bd102d2ef10cb9c293a9 \ + --hash=sha256:2c54a076ca4d370980ab57bc0e31df57bbe8d41340436a90ef8b1219a3cbb127 \ + --hash=sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f \ + --hash=sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa \ + --hash=sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05 \ + --hash=sha256:30c6dc199b24a5e3e81d50da0f00858c5bbdb2617a750395687f4339c5818171 \ + --hash=sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba \ + --hash=sha256:3a83ae6c67b7676b9878378547ca8e93ed77a580037bcbcd1d32f739e1e6089c \ + --hash=sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223 \ + --hash=sha256:421aba32367055614287a4292b6a17f1939c9452299f7a0209c117e990b646d4 \ + --hash=sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885 \ + --hash=sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698 \ + --hash=sha256:4cf2d36a2357e4d07bb5a4f98801265327b48256867816cfd2ceb001e9754a8f \ + --hash=sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7 \ + --hash=sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed \ + --hash=sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f \ + --hash=sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf \ + --hash=sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e \ + --hash=sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f \ + --hash=sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24 \ + --hash=sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a \ + --hash=sha256:5e8d07bddee435a2ff6f1920e18feff28d0bc4533e42f4bf6927fbd073312c41 \ + --hash=sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc \ + --hash=sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d \ + --hash=sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146 \ + --hash=sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e \ + --hash=sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e \ + --hash=sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4 \ + --hash=sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12 \ + --hash=sha256:7b689145a1485c335569bd056464f3243a29af7ed3871c7be31ad624ba239bc7 \ + --hash=sha256:7f88d653e7b3b779d71ae7454e20dcc9b6bae903f33c269db9f2be41bda3f261 \ + --hash=sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6 \ + --hash=sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5 \ + --hash=sha256:83e35b57523816c8613fd0776b40cd8bb9f596b37ddd2692eb4a6bb5ab2f8c93 \ + --hash=sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7 \ + --hash=sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda \ + --hash=sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8 \ + --hash=sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342 \ + --hash=sha256:8bb68f03f395eb793220b45c097bd4d8c32944393da0fad8b999efac0868fc8c \ + --hash=sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb \ + --hash=sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0 \ + --hash=sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77 \ + --hash=sha256:8d2294a31386bfa251d8c8a39472beee17db67d4f1a6eabea665d35c9a4461c3 \ + --hash=sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885 \ + --hash=sha256:8e65860d238379ed982fd9ba690579b5e95af2f4840f99c772816dbe573cb826 \ + --hash=sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617 \ + --hash=sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb \ + --hash=sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577 \ + --hash=sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80 \ + --hash=sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e \ + --hash=sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945 \ + --hash=sha256:9891e594296ab9dada6551c8e7b387b2721f27a67eecd528412e8906247a7b90 \ + --hash=sha256:9c1255b302953c86a486b81d330d5ee1d5bd937691ce271b6be0ef0e299eaab7 \ + --hash=sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0 \ + --hash=sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140 \ + --hash=sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822 \ + --hash=sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba \ + --hash=sha256:a3450b693fde92133e9f51060568a4c31fcca76d5e53bbd611e689ca446517e9 \ + --hash=sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4 \ + --hash=sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a \ + --hash=sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8 \ + --hash=sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf \ + --hash=sha256:acc992ab27b15f852c76755eb2ab7dce86585ddadba6fa5946e58556088845b4 \ + --hash=sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324 \ + --hash=sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53 \ + --hash=sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b \ + --hash=sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41 \ + --hash=sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9 \ + --hash=sha256:b5c2dc92304aa48a4a60443b548bb12f12e119d4b72f314015e67b9e1be97fca \ + --hash=sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1 \ + --hash=sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d \ + --hash=sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690 \ + --hash=sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107 \ + --hash=sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2 \ + --hash=sha256:ca6546b66be9dc4738b1b043d5ebd5488c66c578c5ff0fd0e8065313fe3afb76 \ + --hash=sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d \ + --hash=sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af \ + --hash=sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6 \ + --hash=sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db \ + --hash=sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369 \ + --hash=sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd \ + --hash=sha256:db08f45aecde626498fb3df07bcf6d2ec040af42e859a4f5040d79c200342911 \ + --hash=sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504 \ + --hash=sha256:de3eceba0b683bcbb1ab93da016d0270df1f9ae7be716b40214c5dafac6ea45a \ + --hash=sha256:dfcc8b909769d19db55c7cc9541eb64b9b774b1057ffffb4f1048070475bb9f9 \ + --hash=sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13 \ + --hash=sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc \ + --hash=sha256:e52655eaf81e32593abedaa4bfe33170c8cfedf3365ed9be6e11e07f148f0278 \ + --hash=sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868 \ + --hash=sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2 \ + --hash=sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd \ + --hash=sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4 \ + --hash=sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6 \ + --hash=sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9 \ + --hash=sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00 \ + --hash=sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f \ + --hash=sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e \ + --hash=sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442 \ + --hash=sha256:f8f23ead891a3b762f35ab3b04623da7056545b48aa60d59957e6789914545da \ + --hash=sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90 \ + --hash=sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef + # via + # jsonschema + # referencing +s3transfer==0.19.2 \ + --hash=sha256:ba0309fd86be3c27dbf78cdd813c13c5e1df16e5874b99d2535ebbdfb9892993 \ + --hash=sha256:d8168eccca828cbb2cd573675333f3bddd254313a9c42494b84c76b539e8ba25 + # via boto3 +safehttpx==0.1.7 \ + --hash=sha256:c4f4a162db6993464d7ca3d7cc4af0ffc6515a606dfd220b9f82c6945d869cde \ + --hash=sha256:db201c0978c41eddb8bb480f3eee59dd67304fdd91646035e9d9a720049a9d23 + # via gradio +safetensors==0.8.0 \ + --hash=sha256:040070828e36dc8e122178bbbd5830ff9e97920affb84cbe0f46442497bed358 \ + --hash=sha256:096ec1a98435df7beb08853bb5aa9081a84f23d0adc67ed1a0a10550f608373f \ + --hash=sha256:2ddf52eac562eda224f99acfa7889d02968c1fd59a5b011ae7d8137c37e9c02d \ + --hash=sha256:3ae091f16662658bdc019a4ff6cb4c085bb7d725eb5978b183ffd265863b6d2d \ + --hash=sha256:4124502b78f03534117c848f87a39b8f31e577b15eff423bf8bfb95f2a8c30d0 \ + --hash=sha256:4a95ae2b05d7726d751da4ebf626a2ca782b706e101bd894c95bc2450b1cffcc \ + --hash=sha256:7a46e5ff292c356d6991e60942ba7f79817682d3a2cef0702136448cb9c4d235 \ + --hash=sha256:7bc0a787ba8a35be368ee3574edfa2b1ad389eebd0a72e482ae275490e3f6c98 \ + --hash=sha256:87eec7ffed2b809f05a398a8becb7d013f19f7837cd15d9748580d6cf30dbaf4 \ + --hash=sha256:8e080062fcde23be189565e1c3305d16751a218ecf9412c8601e64204eb6f846 \ + --hash=sha256:8e9f537aa183a38ace122d27303dcd986b26bd2a7591f9181d7f0c396f4677ca \ + --hash=sha256:c554f85858e05226d3c2828e32395e677434685d6d94594a41643361c5e837f0 \ + --hash=sha256:c80201d22cbf405b80647a60ada77bba06c8fba2da2743ba1e89cdcc39a81f25 \ + --hash=sha256:f7838e5135a406ad3e02efdcb8cf2e5397d368b0154537c4fec682dbc544d452 \ + --hash=sha256:fabaf3e0f18a6618d9b36560682562157f77c2b71fcffc7b432be2baed9d753d \ + --hash=sha256:fcdd41ec4628fee5799f807c73c353629130fbd942aa23d83c623dd6c9d52d78 \ + --hash=sha256:fd6f3f93c9a0a7cc2788ee63fb763353d4bd2e89b0751bc78fcf7dda00bea774 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # accelerate + # humming-kernels + # peft + # transformers + # vllm +scantree==0.0.4 \ + --hash=sha256:15bd5cb24483b04db2c70653604e8ea3522e98087db7e38ab8482f053984c0ac \ + --hash=sha256:7616ab65aa6b7f16fcf8e6fa1d9afaa99a27ab72bba05c61b691853b96763174 + # via dirhash +secretstorage==3.5.0 \ + --hash=sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137 \ + --hash=sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be + # via keyring +semantic-version==2.10.0 \ + --hash=sha256:bdabb6d336998cbb378d4b9db3a4b56a1e3235701dc05ea2690d9a997ed5041c \ + --hash=sha256:de78a3b8e0feda74cabc54aab2da702113e33ac9d9eb9d2389bcf1f58b7d9177 + # via gradio +sentencepiece==0.2.2 \ + --hash=sha256:046b15ea22d8042e2e173561d464ec3b64a9c2081324df70ebce7bf7ebb3e497 \ + --hash=sha256:0e2aae42960392d6dcb9a72d8e1e65a97294c965071b43c7b3429a42f350250e \ + --hash=sha256:1120e0791540615e650b2e9bea835bf38a7362455d8ab62dee7968219c2d79a0 \ + --hash=sha256:1402d8ee36f0d851cea8eee4dbb85fea14643b7503cf4d00d102eec0fe3ca719 \ + --hash=sha256:1416b92f2f010333786fe6306ed2631121d5ea492219b0841e967b6765e64107 \ + --hash=sha256:16c84ddef8d3084a8af37208acd365b08092ca089080f1a71fbfdd911adda9b3 \ + --hash=sha256:1edb10e520e4bddf74d85b0f5ae74cc2d60c2b448885080bfb618bc2b3a49f6b \ + --hash=sha256:201a8e0f55501a76e08dbf2c54bc45f4642b379271e89c667d517bfbc2191f2a \ + --hash=sha256:252908153eeec06c3ca3a32077e64a49d572e3d89881475b4e0f02d99d9fcc7c \ + --hash=sha256:38111ed1f79268f399c505028023d5eaaf0ab4e5eafceb709468b0d3323e7838 \ + --hash=sha256:3ab3f1ae98970b5590e2209341522718900ba19bcc2c207ffaa6bd417ad960c5 \ + --hash=sha256:3d2b5e824b5622038dc7b490897efe05ebbbb9e7350fc142f3ecc8789ef9bdf6 \ + --hash=sha256:3ec27c152a1f1b24bc9168b55a5880f3c16e2334e697da6f55a1046a22405a3d \ + --hash=sha256:3f5851441ab1ef8634963a5100b733a8bbeefe623e0c5c005b1f1f3880e574cf \ + --hash=sha256:3fd9ce2ab4460c713cfdeb4aca693ca6732a11538e05fb332d5af42e3d7fde25 \ + --hash=sha256:44284adc6fbe9d5bdd480541431a3d93f674fa44736714d3ad4bcee8283ace7d \ + --hash=sha256:443ac618c7a2a1377cf5c82581fbb849591d14e656d5e5a3e4682d4e36a34e4e \ + --hash=sha256:46ba07b543add034de0ff47ac5f907e9a06682f91d85121a972764628933be6b \ + --hash=sha256:4f0603267cd15b92b68c2c0e852a441507614b70dc7773659baa6b8c214a91fd \ + --hash=sha256:524e2a85c028a0d2f9935191fa751e5ef9d9bcc39616f70ab14b28d0369c9936 \ + --hash=sha256:54a83df9260a89c1734256e620fe1f1a6bfedd7547139d4dc1384efac11a3a85 \ + --hash=sha256:59d6588712101ccfcae9b03692be3aaae1514c2078666d7b05f15ba3a702e41b \ + --hash=sha256:63250cfab8b80a1ef82a614eb2b3cadfec2c405f870cedc139d08e2f063eb708 \ + --hash=sha256:64b656f025355cf8c51abe9fbe3848540756c6d7ca5e6791b1afa664bc24c7cb \ + --hash=sha256:65d84ec36888de4a848eee5f910e67fbc79b064685ef1e10a502e14520ead9c9 \ + --hash=sha256:69e9dc8078e128286ed3b975e37c837ba96e215a50c3ef9f3f8b7ab9e5a832a0 \ + --hash=sha256:6dd76f3e5c8b2eb8a3a3efee787bbf5b9a66e52a048fe09cab85eca33fec6790 \ + --hash=sha256:70d4ca6f4d06df7f0ccab6fe4f49c8a712c8c8b6847b4f0af9a0e1dbb0e0337e \ + --hash=sha256:72b7825b331b1b7e7c45be2e674b3e3c65af608fa376bad2d851b20aaf0cdc78 \ + --hash=sha256:741b4b367140e9b5c36b5a14c72179f2c946d991ea9a7c031a2a1ee6ad097b99 \ + --hash=sha256:74f0ee601047c0c12a783088b51be4e6214a62ecd9e02278c477433cd16e0ed9 \ + --hash=sha256:76ff5814db72e7462dece042d7593cdf102b8ec82c2b1cc201a2add34ee3050d \ + --hash=sha256:77c3ce990b23441e5ecfa5bce181fd6f408b564aeb6d7e1d1e7de9c5612501c8 \ + --hash=sha256:79bac5a251f23a7341e28fda9ce0d5319edf45328239ce037c0682936f137906 \ + --hash=sha256:7c6e7bf684dc12145bfa685d3060beaea55139134ba848289bee514ed42e7383 \ + --hash=sha256:7fc14c1585139fa6b68775e616a6b90cf622ebf219f9558c0aeaf5d253ee6c9b \ + --hash=sha256:89625fb43765cccaa1443b9adb61f283e5fe4cb1536728205d06bada730caa53 \ + --hash=sha256:8b2db2056c97224e122054fd794543cde5d24b7cae28424f6e3eb79bbe08e42b \ + --hash=sha256:8d44b20234905ff022b7d535f79d1f823ad7670c9851cc4f03cdc34787cdb3ab \ + --hash=sha256:8eed98514bffe5ecac37f493f91869c351fbb05629328bfdbc08502c6c094dc0 \ + --hash=sha256:8f1f61592e7cabd45d49ce8cc0ef42ca655c091e037153754fb3fa59725b5914 \ + --hash=sha256:b23fe17779834d3c27aaf2edac9486d04cca1a7deb8f5facda35150ac6263a91 \ + --hash=sha256:bc7b0b1da20f856bfac5f84b2673fe534b167e41980b27442ca8f78c2b7eb77e \ + --hash=sha256:c62bd361cec1f5b556eb8210264ecfff37486cd990c3386cc00310f26c54090a \ + --hash=sha256:c76c9b3324efd79029eeb0fd2ced1964bdbeca7d45e030b46fa3ef3cf74f8032 \ + --hash=sha256:c798f0b327bac10dc95cdac77b9a197ab2bd7dd1e60ebd7586a12d918d4be711 \ + --hash=sha256:c8a168b040bc61681293f79a949b5d911c8e25086f4260285b8d97ab5f1195da \ + --hash=sha256:caad9566e2ef0e5640d36032c69b0edc7ac6028277b93d93815898804fac450c \ + --hash=sha256:cbce24284f51f71d10a42b7b9c964dcb9048b28f1c8e5db40bcbcb6f428cba6a \ + --hash=sha256:cd810878180a52950e5a61f25ada5248a453bbdbafe474f89514135fbc1f633d \ + --hash=sha256:d254c98ca6387655400b3959c33c83efd807f5edeb608e3aca45800ceaa77151 \ + --hash=sha256:d795c4ac689a57f9d4ba2288126ec7901d389ad5827d2f8b8533c883974fe563 \ + --hash=sha256:df88b0c34f2fa909d322f7b06b1398e1e81af4b2f42a7b8e3556f928b25d1811 \ + --hash=sha256:eb8da9d9a9b418422c21a07fd19b9d9228692b7a7468a45eec6b11642d3c808b \ + --hash=sha256:f7c06c751c19d923435a54bff4f7e66e728fad160e8da28254f133abc9725820 \ + --hash=sha256:fa9f5ef0e2a82233dd0b8b32ea3f5710e0c44afbc07ed3620219f32601e56090 \ + --hash=sha256:fd523c4992041faa5c2b3cde62253d11a96c30d73a34afe48a486e8e2254cd1c + # via vllm +sentry-sdk==2.69.1 \ + --hash=sha256:2d2556d9a14db548982b914cbed2a2dc07b6e55d941a620752f89a2afccfd78d \ + --hash=sha256:f9284b417540b0784b994fa021eb6f1e30ae1cce593d83541274d03c93966eff + # via fastapi-cloud-cli +setproctitle==1.3.7 \ + --hash=sha256:00afa6fc507967d8c9d592a887cdc6c1f5742ceac6a4354d111ca0214847732c \ + --hash=sha256:01f27b5b72505b304152cb0bd7ff410cc4f2d69ac70c21a7fdfa64400a68642d \ + --hash=sha256:02432f26f5d1329ab22279ff863c83589894977063f59e6c4b4845804a08f8c2 \ + --hash=sha256:047138279f9463f06b858e579cc79580fbf7a04554d24e6bddf8fe5dddbe3d4c \ + --hash=sha256:0564a936ea687cd24dffcea35903e2a20962aa6ac20e61dd3a207652401492dd \ + --hash=sha256:0b5074649797fd07c72ca1f6bff0406f4a42e1194faac03ecaab765ce605866f \ + --hash=sha256:10e92915c4b3086b1586933a36faf4f92f903c5554f3c34102d18c7d3f5378e9 \ + --hash=sha256:134e7f66703a1d92c0a9a0a417c580f2cc04b93d31d3fc0dd43c3aa194b706e1 \ + --hash=sha256:13fe37951dda1a45c35d77d06e3da5d90e4f875c4918a7312b3b4556cfa7ff64 \ + --hash=sha256:1403d2abfd32790b6369916e2313dffbe87d6b11dca5bbd898981bcde48e7a2b \ + --hash=sha256:14c7eba8d90c93b0e79c01f0bd92a37b61983c27d6d7d5a3b5defd599113d60e \ + --hash=sha256:1607b963e7b53e24ec8a2cb4e0ab3ae591d7c6bf0a160feef0551da63452b37f \ + --hash=sha256:1fae595d032b30dab4d659bece20debd202229fce12b55abab978b7f30783d73 \ + --hash=sha256:23e520776c445478a67ee71b2a3c1ffdafbe1f9f677239e03d7e2cc635954e18 \ + --hash=sha256:2906b6c7959cdb75f46159bf0acd8cc9906cf1361c9e1ded0d065fe8f9039629 \ + --hash=sha256:2a4e03bd9aa5d10b8702f00ec1b740691da96b5003432f3000d60c56f1c2b4d3 \ + --hash=sha256:2dc99aec591ab6126e636b11035a70991bc1ab7a261da428491a40b84376654e \ + --hash=sha256:316664d8b24a5c91ee244460bdaf7a74a707adaa9e14fbe0dc0a53168bb9aba1 \ + --hash=sha256:318ddcf88dafddf33039ad41bc933e1c49b4cb196fe1731a209b753909591680 \ + --hash=sha256:3384e682b158d569e85a51cfbde2afd1ab57ecf93ea6651fe198d0ba451196ee \ + --hash=sha256:35a2cabcfdea4643d7811cfe9f3d92366d282b38ef5e7e93e25dafb6f97b0a59 \ + --hash=sha256:376761125ab5dab822d40eaa7d9b7e876627ecd41de8fa5336713b611b47ccef \ + --hash=sha256:3a57b9a00de8cae7e2a1f7b9f0c2ac7b69372159e16a7708aa2f38f9e5cc987a \ + --hash=sha256:45e3ef48350abb49cf937d0a8ba15e42cee1e5ae13ca41a77c66d1abc27a5070 \ + --hash=sha256:46178672599b940368d769474fe13ecef1b587d58bb438ea72b9987f74c56ea5 \ + --hash=sha256:47d36e418ab86b3bc7946e27155e281a743274d02cd7e545f5d628a2875d32f9 \ + --hash=sha256:4a5e212bf438a4dbeece763f4962ad472c6008ff6702e230b4f16a037e2f6f29 \ + --hash=sha256:502b902a0e4c69031b87870ff4986c290ebbb12d6038a70639f09c331b18efb2 \ + --hash=sha256:52b054a61c99d1b72fba58b7f5486e04b20fefc6961cd76722b424c187f362ed \ + --hash=sha256:5818e4080ac04da1851b3ec71e8a0f64e3748bf9849045180566d8b736702416 \ + --hash=sha256:585edf25e54e21a94ccb0fe81ad32b9196b69ebc4fc25f81da81fb8a50cca9e4 \ + --hash=sha256:5ce2613e1361959bff81317dc30a60adb29d8132b6159608a783878fc4bc4bbc \ + --hash=sha256:5fa1953126a3b9bd47049d58c51b9dac72e78ed120459bd3aceb1bacee72357c \ + --hash=sha256:6134c63853d87a4897ba7d5cc0e16abfa687f6c66fc09f262bb70d67718f2309 \ + --hash=sha256:61e96febced3f61b766115381d97a21a6265a0f29188a791f6df7ed777aef698 \ + --hash=sha256:690b4776f9c15aaf1023bb07d7c5b797681a17af98a4a69e76a1d504e41108b7 \ + --hash=sha256:6915964a6dda07920a1159321dcd6d94fc7fc526f815ca08a8063aeca3c204f1 \ + --hash=sha256:6da835e76ae18574859224a75db6e15c4c2aaa66d300a57efeaa4c97ca4c7381 \ + --hash=sha256:6f1be447456fe1e16c92f5fb479404a850d8f4f4ff47192fde14a59b0bae6a0a \ + --hash=sha256:6fc87caf9e323ac426910306c3e5d3205cd9f8dcac06d233fcafe9337f0928a3 \ + --hash=sha256:7081f193dab22df2c36f9fc6d113f3793f83c27891af8fe30c64d89d9a37e152 \ + --hash=sha256:7f47accafac7fe6535ba8ba9efd59df9d84a6214565108d0ebb1199119c9cbbd \ + --hash=sha256:7f9e9e3ff135cbcc3edd2f4cf29b139f4aca040d931573102742db70ff428c17 \ + --hash=sha256:80b6a562cbc92b289c28f34ce709a16b26b1696e9b9a0542a675ce3a788bdf3f \ + --hash=sha256:80c36c6a87ff72eabf621d0c79b66f3bdd0ecc79e873c1e9f0651ee8bf215c63 \ + --hash=sha256:80e177eff2d1ec172188d0d7fd9694f8e43d3aab76a6f5f929bee7bf7894e98b \ + --hash=sha256:83fcd271567d133eb9532d3b067c8a75be175b2b3b271e2812921a05303a693f \ + --hash=sha256:8ce2e39a40fca82744883834683d833e0eb28623752cc1c21c2ec8f06a890b39 \ + --hash=sha256:96c38cdeef9036eb2724c2210e8d0b93224e709af68c435d46a4733a3675fee1 \ + --hash=sha256:9796732a040f617fc933f9531c9a84bb73c5c27b8074abbe52907076e804b2b7 \ + --hash=sha256:97a090fed480471bb175689859532709e28c085087e344bca45cf318034f70c4 \ + --hash=sha256:9888ceb4faea3116cf02a920ff00bfbc8cc899743e4b4ac914b03625bdc3c300 \ + --hash=sha256:9cc9b901ce129350637426a89cfd650066a4adc6899e47822e2478a74023ff7c \ + --hash=sha256:9e02667f6b9fc1238ba753c0f4b0a37ae184ce8f3bbbc38e115d99646b3f4cd3 \ + --hash=sha256:9e64e98077fb30b6cf98073d6c439cd91deb8ebbf8fc62d9dbf52bd38b0c6ac0 \ + --hash=sha256:9e803d1b1e20240a93bac0bc1025363f7f80cb7eab67dfe21efc0686cc59ad7c \ + --hash=sha256:a05509cfb2059e5d2ddff701d38e474169e9ce2a298cf1b6fd5f3a213a553fe5 \ + --hash=sha256:a20fb1a3974e2dab857870cf874b325b8705605cb7e7e8bcbb915bca896f52a9 \ + --hash=sha256:a5d1cb3f81531f0eb40e13246b679a1bdb58762b170303463cb06ecc296f26d0 \ + --hash=sha256:a600eeb4145fb0ee6c287cb82a2884bd4ec5bbb076921e287039dcc7b7cc6dd0 \ + --hash=sha256:a74714ce836914063c36c8a26ae11383cf8a379698c989fe46883e38a8faa5be \ + --hash=sha256:a7d159e7345f343b44330cbba9194169b8590cb13dae940da47aa36a72aa9929 \ + --hash=sha256:a93e4770ac22794cfa651ee53f092d7de7105c76b9fc088bb81ca0dcf698f704 \ + --hash=sha256:a97200acc6b64ec4cada52c2ecaf1fba1ef9429ce9c542f8a7db5bcaa9dcbd95 \ + --hash=sha256:acb9097213a8dd3410ed9f0dc147840e45ca9797785272928d4be3f0e69e3be4 \ + --hash=sha256:b0304f905efc845829ac2bc791ddebb976db2885f6171f4a3de678d7ee3f7c9f \ + --hash=sha256:b08b61976ffa548bd5349ce54404bf6b2d51bd74d4f1b241ed1b0f25bce09c3a \ + --hash=sha256:b1cac6a4b0252b8811d60b6d8d0f157c0fdfed379ac89c25a914e6346cf355a1 \ + --hash=sha256:b53602371a52b91c80aaf578b5ada29d311d12b8a69c0c17fbc35b76a1fd4f2e \ + --hash=sha256:b74774ca471c86c09b9d5037c8451fff06bb82cd320d26ae5a01c758088c0d5d \ + --hash=sha256:b7cb05bd446687ff816a3aaaf831047fc4c364feff7ada94a66024f1367b448c \ + --hash=sha256:b91387cc0f02a00ac95dcd93f066242d3cca10ff9e6153de7ee07069c6f0f7c8 \ + --hash=sha256:bc2bc917691c1537d5b9bca1468437176809c7e11e5694ca79a9ca12345dcb9e \ + --hash=sha256:be7e01f3ad8d0e43954bebdb3088cb466633c2f4acdd88647e7fbfcfe9b9729f \ + --hash=sha256:c18246d88e227a5b16248687514f95642505000442165f4b7db354d39d0e4c29 \ + --hash=sha256:c3736b2a423146b5e62230502e47e08e68282ff3b69bcfe08a322bee73407922 \ + --hash=sha256:c4fb90174d176473122e7eef7c6492d53761826f34ff61c81a1c1d66905025d3 \ + --hash=sha256:c77b3f58a35f20363f6e0a1219b367fbf7e2d2efe3d2c32e1f796447e6061c10 \ + --hash=sha256:cbc388e3d86da1f766d8fc2e12682e446064c01cea9f88a88647cfe7c011de6a \ + --hash=sha256:cdd8aa571b7aa39840fdbea620e308a19691ff595c3a10231e9ee830339dd798 \ + --hash=sha256:cf2727b733e90b4f874bac53e3092aa0413fe1ea6d4f153f01207e6ce65034d9 \ + --hash=sha256:cf555b6299f10a6eb44e4f96d2f5a3884c70ce25dc5c8796aaa2f7b40e72cb1b \ + --hash=sha256:cff72899861c765bd4021d1ff1c68d60edc129711a2fdba77f9cb69ef726a8b6 \ + --hash=sha256:d8828b356114f6b308b04afe398ed93803d7fca4a955dd3abe84430e28d33739 \ + --hash=sha256:db0fd964fbd3a9f8999b502f65bd2e20883fdb5b1fae3a424e66db9a793ed307 \ + --hash=sha256:db116850fcf7cca19492030f8d3b4b6e231278e8fe097a043957d22ce1bdf3ee \ + --hash=sha256:de879e9c2eab637f34b1a14c4da1e030c12658cdc69ee1b3e5be81b380163ce5 \ + --hash=sha256:deda9d79d1eb37b688729cac2dba0c137e992ebea960eadb7c2c255524c869e0 \ + --hash=sha256:e7c5bfe4228ea22373e3025965d1a4116097e555ee3436044f5c954a5e63ac45 \ + --hash=sha256:eb440c5644a448e6203935ed60466ec8d0df7278cd22dc6cf782d07911bcbea6 \ + --hash=sha256:f1704c9e041f2b1dc38f5be4552e141e1432fba3dd52c72eeffd5bc2db04dc65 \ + --hash=sha256:f2ae6c3f042fc866cc0fa2bc35ae00d334a9fa56c9d28dfc47d1b4f5ed23e375 \ + --hash=sha256:f6f268caeabb37ccd824d749e7ce0ec6337c4ed954adba33ec0d90cc46b0ab78 \ + --hash=sha256:f8d961bba676e07d77665204f36cffaa260f526e7b32d07ab3df6a2c1dfb44ba \ + --hash=sha256:fcb966a6c57cf07cc9448321a08f3be6b11b7635be502669bc1d8745115d7e7f \ + --hash=sha256:fe5ca35aeec6dc50cabab9bf2d12fbc9067eede7ff4fe92b8f5b99d92e21263f \ + --hash=sha256:ff3c1c32382fb71a200db8bab3df22f32e6ac7ec3170e92fa5b542cf42eed9a2 + # via vllm +setuptools==80.10.2 \ + --hash=sha256:8b0e9d10c784bf7d262c4e5ec5d4ec94127ce206e8738f29a437945fbc219b70 \ + --hash=sha256:95b30ddfb717250edb492926c92b5221f7ef3fbcc2b07579bcd4a27da21d0173 + # via + # model-hosting-container-standards + # torch + # vllm +shellingham==1.5.4 \ + --hash=sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686 \ + --hash=sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de + # via typer +shortuuid==1.0.13 \ + --hash=sha256:3bb9cf07f606260584b1df46399c0b87dd84773e7b25912b7e391e30797c5e72 \ + --hash=sha256:a482a497300b49b4953e15108a7913244e1bb0d41f9d332f5e9925dba33a3c5a + # via harbor +simple-websocket==1.1.0 \ + --hash=sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c \ + --hash=sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4 + # via python-engineio +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via + # python-dateutil + # vllm +sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via + # anthropic + # openai +sse-starlette==3.4.11 \ + --hash=sha256:1bae716c02f3e6f294be41ff333220692dae7c3cbab077c900f159676719dade \ + --hash=sha256:c7b2244bdff016fe7f64e10075e89a3e6bbf899649cc89b0fe884b5545042453 + # via mcp +starlette==1.6.0 \ + --hash=sha256:a86dd39d14bb45f85a3d18525215a9ef0cfd1f192ac793220e72598c90335f0c \ + --hash=sha256:d4e3ac5e546444960c710297a3c9fc3f7ebae1b7e963f3d36173b49da535be9b + # via + # fastapi + # fastmcp-slim + # gradio + # mcp + # model-hosting-container-standards + # prometheus-fastapi-instrumentator + # sse-starlette + # trackio + # vllm +storage3==2.31.0 \ + --hash=sha256:4bf46e8bea320743179a6beafdc7531c5242495e00e0cc22af7c7a9d69d4ed84 \ + --hash=sha256:d2161e2ea650dc115a1787c30e09b118365589ac772f4dd8643e3a503ecfc667 + # via supabase +strenum==0.4.15 \ + --hash=sha256:878fb5ab705442070e4dd1929bb5e2249511c0bcf2b0eeacf3bcd80875c82eff \ + --hash=sha256:a30cda4af7cc6b5bf52c8055bc4bf4b2b6b14a93b574626da33df53cf7740659 + # via supabase-functions +supabase==2.31.0 \ + --hash=sha256:25f2a99207a75f2d9377e2332783b4389cf56b02cbebdaf0c1743112dcbb704e \ + --hash=sha256:3467b09d00482b9a0138235bdbde7a350426f93cf2a1342372eaddfc669f1206 + # via harbor +supabase-auth==2.31.0 \ + --hash=sha256:0945b33fa96239c76dc8eaf96d7d2c94991950d24b4cfe4a5c2da9aa5e909663 \ + --hash=sha256:5e9c8b4ecdee6af04dbcb06455ce78cb15674806fcb6b425170455307d70b0ee + # via supabase +supabase-functions==2.31.0 \ + --hash=sha256:3fdc4c4766152bfda63bdd0e286fc8a06f50e1280711fae4a1dfc9b7e9ebabc6 \ + --hash=sha256:4ad027b3ae3bd28b31233339f4db1da6965affd3546f655b421baf40cee2690f + # via supabase +supervisor==4.3.0 \ + --hash=sha256:0bcb763fddafba410f35cbde226aa7f8514b9fb82eb05a0c85f6588d1c13f8db \ + --hash=sha256:4a2bf149adf42997e1bb44b70c43b613275ec9852c3edacca86a9166b27e945e + # via model-hosting-container-standards +sympy==1.14.0 \ + --hash=sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517 \ + --hash=sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5 + # via torch +tabulate==0.10.0 \ + --hash=sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d \ + --hash=sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3 + # via + # flashinfer-python + # humming-kernels +tenacity==9.1.4 \ + --hash=sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55 \ + --hash=sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a + # via harbor +tiktoken==0.14.0 \ + --hash=sha256:087538c080e5ff421abd3a0785ed63c5111d06af98e6cd0d374dbe5969147ca3 \ + --hash=sha256:10f31e63e40313f2e518d87f7086cfa44e45f64cc14d8ae14103b41220c30a14 \ + --hash=sha256:11d8211b290855d2721334ff17dd9b3a17bfb26872be01f25d73612ef7ece890 \ + --hash=sha256:144a3fc369f92b7d548995217c5d6e84038d3572157a0f6f34080d65291d0f78 \ + --hash=sha256:149d97453c4c98c04b081d64a85e635921269b532710d6faf81e9e82b790e7d3 \ + --hash=sha256:14b47e3674f2624803a8acc8fb367b7e24fc53055f9df3296482fe9a3a34a232 \ + --hash=sha256:151d37a150c8f3dfc5f4345597b10e101876bd1bd13494e0185af6b508758d2e \ + --hash=sha256:18a1b651c4b032004bf7b4f1713391a54b2a341a52c6e8a2b59acae9d16e13c7 \ + --hash=sha256:19d643d701fdaa70e5b9c7f8f96abcaffe77ca5e482a3a1a7dde46feb4284695 \ + --hash=sha256:1b6e4adcfd285c44502aed51df98aaaca4f0fea028165dbf8a9e857b9f98d8ea \ + --hash=sha256:1f83081065ee5833d35b49e9180f3d8d15622a603dd1c435da0da6cc12b3662f \ + --hash=sha256:2157f52e4b4d7ac5ecc7457b3716834706e7ef9a46f5144029bfeb7cf71f4e06 \ + --hash=sha256:231dec90efcdccf1b565a1416107736f1e09b1a08fe736ef9d6363e626d03874 \ + --hash=sha256:26cc4b4840fa0e9f4b72ed489883e12f57e00d1021ca794720e3c29a12f0edef \ + --hash=sha256:26e60f6a956ee171ab728b37b8439905d7ea1db435c30f9822f291e9861c861d \ + --hash=sha256:2cc19ac87b41c9493c9778ff5847f0c8bbcf5bd0ec6b87ce06c1c802adc8a771 \ + --hash=sha256:2ea70afba6b9eddbf22c165142e5f0a2ad7aa36a452873c48b57bb2aeb8492ae \ + --hash=sha256:2ec16eb585332c55d022d86354e209ddf27326b1ea3477585ab248e7776d3b1f \ + --hash=sha256:2fc834fbe3f6a0736905c36ab709537e6840dbd63b982dc9e0216ae7d305ba1a \ + --hash=sha256:380873f330b741c4435574f37edb20813d04603ace2d53e0a63560e1fec83010 \ + --hash=sha256:3b12e54f8bec91433e41aff65d8d1f209a4f678081163747079806e5361f6c91 \ + --hash=sha256:3c5349c9f916283bba32bec8af69b763e4faa304dc004d0eaaea66a3cf004c1f \ + --hash=sha256:3de75343041a1c57333b1e707ac8a9769738241d7d6a55d39e12cf84548337c6 \ + --hash=sha256:3fd7c14b1cb45b486c39fc9b3443bb341f3e2fc7e6f31247f3435a5836651632 \ + --hash=sha256:447ada49af4898b5e992f0b5799d2f3af385921102c211947ce3fe960dd919da \ + --hash=sha256:4d8d91d68353bd167fdf26467e5ff9e56aaa5f87d6410c0238608629e4dc0d33 \ + --hash=sha256:50a7e5646cbac2a8f7c3e8c0934ffda1a4357ee9c44b652434b23c3ed54d0900 \ + --hash=sha256:561e7580f84a79859af1ef6f676968e9030fcc3fe195700b15235bca64f009c9 \ + --hash=sha256:60c47ca69ddda0dea8256fffd12e1b86f4b59734a20e4a70c61f63cc5f021df4 \ + --hash=sha256:6eb94895c45f26bb8f5546e5fd8a069efcf6e3f108ea9d5cbe3bf6f7f3983438 \ + --hash=sha256:728303a072163130c5b477b1f20d6211895569c1d5302c24ffc93a3009160871 \ + --hash=sha256:78571efc311c30b73f31eb949a921d6dac39a5d9dc42d1cfa8f8db157b3447b1 \ + --hash=sha256:7896eea257fe497a2b7134474d909156c6744ce8da35bce88011a960e008aa0d \ + --hash=sha256:7aab286a020660a039097912a088236b985d18a3090d73f136c4413d29d37ca0 \ + --hash=sha256:7b7acbb7a4b8383707bce22ad3c162006478c27b56368acd3e1fcb1658a80425 \ + --hash=sha256:7db45b98e94adf4173a5cd7422b150999a7ee11ff847783a14f6e1b80cc38cb6 \ + --hash=sha256:86951a971c53979ec857bd8c4a32dc227ab0fd33f6c12a3bd62d3fbf5f0bfcaa \ + --hash=sha256:86f66c85e796f5d05d5c4a60ec1d40cbfebc47a32464053528c797163fa9ab89 \ + --hash=sha256:8e947aefe98ef74cce94923f90e48c98fe34eb1ec0a6bfdfadfc5a96359bfc36 \ + --hash=sha256:90a762670c7f968184723769a06ed51f5cf5ce5dcd1e30164f25c72d85c2d1f1 \ + --hash=sha256:94f77b60a8ab23580db19ae822744c9716c1720020d2179ca5605112d12326f1 \ + --hash=sha256:979c1524f753b662b0f3cd261b135afe6659cce33caaa7a5ea00dd1756b3055c \ + --hash=sha256:a140e83317fef02faeeb78d9a8efac623887f2feaf0055c55dcdb2b17f0226ad \ + --hash=sha256:aa428a559d5fd02ae619aacaace86c7474a1f2702d2c01fc828908dd60f20f7a \ + --hash=sha256:b950248272f1b303dc32986396e2dccfa10cf6d1e83ec8f0bba1776660305482 \ + --hash=sha256:c2edf09b381fafbc014ae8e018ed25087abb9a3dafa8465a0ea63c6558c47a79 \ + --hash=sha256:c3093001ddce822b4587e6e94bf6de36a5f97b3f31de1c9fc8d4fda144c59ff4 \ + --hash=sha256:c6cb9896a82b9ee44e15ba0b5c8044072f2e4d48acaa704c8d3feeef5ad9487c \ + --hash=sha256:c77d4a3e1deb2707819df92046b89aad1ac81d27e07616b797cbff3f62c037da \ + --hash=sha256:ca4db6ff5c5bf600f9b7761a0070ed44dfe5797a76bd432fb978bc480ef40c58 \ + --hash=sha256:cbe2cc3bba939bcdaf103e03df9d5039d33887080b315624be28ec69059e5f94 \ + --hash=sha256:cd8ca1305c1c902fe42c486165f2e4808d9997625c98ffb05b9e0366d99d3948 \ + --hash=sha256:d0781223705199b289faa59601bb9c2441712d4c600dd13c43d8fd6a33d22cd5 \ + --hash=sha256:d6cebe67765569df3dafac8474e4eccf5c19d24140492567a5e58a11445732a4 \ + --hash=sha256:e067f4cbcc5d036e8aff7fe7a6b530a8f4de2e4616ad9005a24a1879e24e6450 \ + --hash=sha256:e2eca764c53490f8930dbce329e0769f11108d87d908282a80c5c130e26e7037 \ + --hash=sha256:e3442bbb2f0c588cec876061e37ae67b455b9df9978b003c8fe30e45f2ef5b42 \ + --hash=sha256:e4ddf863b59347deaa92302dcd90e5eb003cdc9be06ec2b692c38d1bdd9efd49 \ + --hash=sha256:e9c5fe393aab56469f04e432ff851216d3def3436cf5f07e442a240164bf500f \ + --hash=sha256:eceeff0c62419bc78d4b6e70a4762a4d25df3ae8f2d5946e3853ce93e7a57098 \ + --hash=sha256:f2af4a336ea56d6c14f27741a0e1d8294a35dd0b038bcf990d232ebb54eb994b \ + --hash=sha256:f3d6cf93fbe2e7117eb7bedca684216fbe328a41f0843ce34245451d8eb2df1c \ + --hash=sha256:f5e7665f6624e052e5e7f6a36919ab69279decdc976d7b16b4fa15e1897d0513 \ + --hash=sha256:f702e0aeeb6506e57687e881c59e844ebe8f0a6a097ddafe20e3ab25f387be4e + # via + # litellm + # mistral-common + # vllm +tilelang==0.1.9 \ + --hash=sha256:00ed594fdeb229c5505b9ffa895c3c5daeb28641c78f783fa1f724cf1e08cecd \ + --hash=sha256:287f727c913bb648fcf6c1968809ba3390e55eeed257a5c6bb9a80bc05966af4 \ + --hash=sha256:4bbccfe9035aed775ffafb6dc25a5994504b24e2c5d95d0f39643edfafa7bf12 \ + --hash=sha256:77ab0ee2f40f66ea015b6b21426d482751e28cbc635ef9d1198cbd6502454a7c + # via vllm +tokenizers==0.22.2 \ + --hash=sha256:143b999bdc46d10febb15cbffb4207ddd1f410e2c755857b5a0797961bbdc113 \ + --hash=sha256:1a62ba2c5faa2dd175aaeed7b15abf18d20266189fb3406c5d0550dd34dd5f37 \ + --hash=sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e \ + --hash=sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001 \ + --hash=sha256:1e50f8554d504f617d9e9d6e4c2c2884a12b388a97c5c77f0bc6cf4cd032feee \ + --hash=sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7 \ + --hash=sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd \ + --hash=sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4 \ + --hash=sha256:319f659ee992222f04e58f84cbf407cfa66a65fe3a8de44e8ad2bc53e7d99012 \ + --hash=sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67 \ + --hash=sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a \ + --hash=sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5 \ + --hash=sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917 \ + --hash=sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c \ + --hash=sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195 \ + --hash=sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4 \ + --hash=sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a \ + --hash=sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc \ + --hash=sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92 \ + --hash=sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5 \ + --hash=sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48 \ + --hash=sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b \ + --hash=sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c \ + --hash=sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5 + # via + # litellm + # transformers + # vllm +tokenspeed-mla==0.1.2 \ + --hash=sha256:592590f36d85e624ecdc5e357ff35e29e761e6d879900dce8b67a6785c8ce75c \ + --hash=sha256:c9466a351fe039792e56cf49f3e79744c1dc28c7af10306a02e62b8e92fa5985 + # via vllm +tokenspeed-triton==3.8.10.post20260906 \ + --hash=sha256:2c674d5ab2706e871440a3eb148cd1929990b5009aa58215c3ad7c0c0de0e12f \ + --hash=sha256:6c1d16d7801e0f186e4dae14dc5216ff36f902cf48f64decb26b4688608dbe7d \ + --hash=sha256:949e1651e5c739c6eccd76360d0a004dea3ad508cceb8fdb501d76316de8a9c8 \ + --hash=sha256:c71e92a609b51c028de78718b5530cab3be4e7cbd7bbda1c3dc0829a86e50409 \ + --hash=sha256:e4272f70e7f95ca2576067d99d4232ad5f4e88b22db02163aa9cd8e7c3732afd \ + --hash=sha256:f58359d2c08bfea6a02239af65ffe964fabae9a016dd7b8ab0f8effd8ecc106c + # via tokenspeed-mla +toml==0.10.2 \ + --hash=sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b \ + --hash=sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f + # via + # daytona + # harbor +tomli==2.4.1 \ + --hash=sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853 \ + --hash=sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe \ + --hash=sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5 \ + --hash=sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d \ + --hash=sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd \ + --hash=sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26 \ + --hash=sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54 \ + --hash=sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6 \ + --hash=sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c \ + --hash=sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a \ + --hash=sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd \ + --hash=sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f \ + --hash=sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5 \ + --hash=sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9 \ + --hash=sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662 \ + --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 \ + --hash=sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1 \ + --hash=sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585 \ + --hash=sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e \ + --hash=sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c \ + --hash=sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41 \ + --hash=sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f \ + --hash=sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085 \ + --hash=sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15 \ + --hash=sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7 \ + --hash=sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c \ + --hash=sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36 \ + --hash=sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076 \ + --hash=sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac \ + --hash=sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8 \ + --hash=sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232 \ + --hash=sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece \ + --hash=sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a \ + --hash=sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897 \ + --hash=sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d \ + --hash=sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4 \ + --hash=sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917 \ + --hash=sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396 \ + --hash=sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a \ + --hash=sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc \ + --hash=sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba \ + --hash=sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f \ + --hash=sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257 \ + --hash=sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30 \ + --hash=sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf \ + --hash=sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9 \ + --hash=sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049 + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +tomli-w==1.2.0 \ + --hash=sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90 \ + --hash=sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021 + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +tomlkit==0.14.0 \ + --hash=sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680 \ + --hash=sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064 + # via + # gradio + # kernels +torch==2.11.0 \ + --hash=sha256:01018087326984a33b64e04c8cb5c2795f9120e0d775ada1f6638840227b04d7 \ + --hash=sha256:0f68f4ac6d95d12e896c3b7a912b5871619542ec54d3649cf48cc1edd4dd2756 \ + --hash=sha256:1b32ceda909818a03b112006709b02be1877240c31750a8d9c6b7bf5f2d8a6e5 \ + --hash=sha256:1e6debd97ccd3205bbb37eb806a9d8219e1139d15419982c09e23ef7d4369d18 \ + --hash=sha256:2658f34ce7e2dabf4ec73b45e2ca68aedad7a5be87ea756ad656eaf32bf1e1ea \ + --hash=sha256:2b4e811728bd0cc58fb2b0948fe939a1ee2bf1422f6025be2fca4c7bd9d79718 \ + --hash=sha256:2bb3cc54bd0dea126b0060bb1ec9de0f9c7f7342d93d436646516b0330cd5be7 \ + --hash=sha256:2c0d7fcfbc0c4e8bb5ebc3907cbc0c6a0da1b8f82b1fc6e14e914fa0b9baf74e \ + --hash=sha256:4b5866312ee6e52ea625cd211dcb97d6a2cdc1131a5f15cc0d87eec948f6dd34 \ + --hash=sha256:4cf8687f4aec3900f748d553483ef40e0ac38411c3c48d0a86a438f6d7a99b18 \ + --hash=sha256:4dc8b3809469b6c30b411bb8c4cad3828efd26236153d9beb6a3ec500f211a60 \ + --hash=sha256:4dda3b3f52d121063a731ddb835f010dc137b920d7fec2778e52f60d8e4bf0cd \ + --hash=sha256:563ed3d25542d7e7bbc5b235ccfacfeb97fb470c7fee257eae599adb8005c8a2 \ + --hash=sha256:63a68fa59de8f87acc7e85a5478bb2dddbb3392b7593ec3e78827c793c4b73fd \ + --hash=sha256:73e24aaf8f36ab90d95cd1761208b2eb70841c2a9ca1a3f9061b39fc5331b708 \ + --hash=sha256:7aa2f9bbc6d4595ba72138026b2074be1233186150e9292865e04b7a63b8c67a \ + --hash=sha256:7b6a60d48062809f58595509c524b88e6ddec3ebe25833d6462eeab81e5f2ce4 \ + --hash=sha256:8245477871c3700d4370352ffec94b103cfcb737229445cf9946cddb7b2ca7cd \ + --hash=sha256:8b394322f49af4362d4f80e424bcaca7efcd049619af03a4cf4501520bdf0fb4 \ + --hash=sha256:98bb213c3084cfe176302949bdc360074b18a9da7ab59ef2edc9d9f742504778 \ + --hash=sha256:a97b94bbf62992949b4730c6cd2cc9aee7b335921ee8dc207d930f2ed09ae2db \ + --hash=sha256:ab9a8482f475f9ba20e12db84b0e55e2f58784bdca43a854a6ccd3fd4b9f75e6 \ + --hash=sha256:b2a43985ff5ef6ddd923bbcf99943e5f58059805787c5c9a2622bf05ca2965b0 \ + --hash=sha256:b3c712ae6fb8e7a949051a953fc412fe0a6940337336c3b6f905e905dac5157f \ + --hash=sha256:cc89b9b173d9adfab59fd227f0ab5e5516d9a52b658ae41d64e59d2e55a418db \ + --hash=sha256:d91aac77f24082809d2c5a93f52a5f085032740a1ebc9252a7b052ef5a4fddc6 \ + --hash=sha256:f99924682ef0aa6a4ab3b1b76f40dc6e273fca09f367d15a524266db100a723f \ + --hash=sha256:fbf39280699d1b869f55eac536deceaa1b60bd6788ba74f399cc67e60a5fab10 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # accelerate + # bitsandbytes + # compressed-tensors + # flashinfer-python + # humming-kernels + # peft + # quack-kernels + # tilelang + # tokenspeed-mla + # torch-c-dlpack-ext + # torchvision + # vllm + # xgrammar +torch-c-dlpack-ext==0.1.5 \ + --hash=sha256:30e3eab616dbc81dfdb7492aca557be551a9163ba9b585f97394a42b336b113a \ + --hash=sha256:3448c4f0d64104d0b2e58080a7efa72304a04960c18f338024b80b13cd3eca26 \ + --hash=sha256:3562ee411258676f9c38b8ad39306d1c8d027b6a86f6a87c920d2d009a9d1510 \ + --hash=sha256:3a58fdf45fb0bda7bc459632cec891570f31c11636d5851c825cf308ec8b73c2 \ + --hash=sha256:3e43083f8f889cdde2b93d7458795aecc95c7268eab40a640d69f71bff7fbcac \ + --hash=sha256:49f1e99d13c64e22dac0a34a1560e9e5a398a49a9fa81df83053e04fde6ec5bd \ + --hash=sha256:4a8680c42ead771773657dbdb88f6ea07d662640abf5f1a405f1da753b5a0dbc \ + --hash=sha256:4b272c1221c3f4988079ea084456f535a25656aa2da5781def43f5bd90afdfd7 \ + --hash=sha256:56bd25a2af19280bf8a06aa62cff5510106f43235b9327d8561b3e9a659c4d84 \ + --hash=sha256:670fbbab70123cc228bed41693a3720757af57a0ad22669063c9db25321e8f55 \ + --hash=sha256:74676474e0afa9a4216c4755ea7cf05e8158be1d168f6bda669ba91097c263f2 \ + --hash=sha256:74acea2ed395cadda63342845b9e9ee7cd4537846223dacfb4431b4610109265 \ + --hash=sha256:78dd4904bd26170a2dd7c0eab56367756ee0a15672ce9b84146169e68f0c6ddc \ + --hash=sha256:7b985a324c68241cf83a9474b28015524b66775b12a91930dd4c0760aa628d01 \ + --hash=sha256:8244a2a6637f148627fdaec58504633a9751f9e00befafb2229c40b451735589 \ + --hash=sha256:ba3d88f0f7d5e1d9c3d4a3179037fc8e261c3b77ac1fad23edc0d3a9214ef193 \ + --hash=sha256:c0f2bd51fcd99c0e5b50314e1985f2728c4941bfa821f065e6c30951d1f995ca \ + --hash=sha256:c7468df84ec152d930fbc3acf460c44a60b3462b95af3d3a676d133629c7e176 \ + --hash=sha256:cac94a4905d391889e679a8da31e46dc325af5d55d13b7c70c0ce3d71d1ced6d \ + --hash=sha256:d06f0357d575d22a168cc77acb9020fc4bae30968ceb6718a055dcbe92bacabe \ + --hash=sha256:d794e19fa3f330ab7a29987c07e031fc08e4953aec516d35701d0827863e356b \ + --hash=sha256:debe62e5ef93e631065d6b9f6e60d3d39bae6b89fa1b25d9523f40b3efbf8aba \ + --hash=sha256:e0f6c197d5293884898b9ebf13d07501de39cb94799b374ed43f91731087d557 \ + --hash=sha256:e6f9da4bb9af70e27facc777458be62e10dbbbddda7672d16138db0553c5a524 \ + --hash=sha256:fba674110e1fab0b176bb5a28223e157db65c90767d4ba74abdbee9f537b0e9d + # via + # quack-kernels + # tilelang +torchaudio==2.11.0 \ + --hash=sha256:00e9f71ab9c656f0abdb40c515bd65d4658ab0ad380dee27a2efd7d51dabd3d6 \ + --hash=sha256:13cff988697ccbad539987599f9dc672f40c417bed67570b365e4e5002bbd096 \ + --hash=sha256:1424638adb8bb40087bc7b6eb103e8e4fe398210f09076f33b7b5e61501b5d66 \ + --hash=sha256:1a07ec72fd6f26a588c39b5f029e0130d16bb40bc4221635580bf8fb18fcbc80 \ + --hash=sha256:1be3767064364ae82705bdf2b15c1e8b41fea82c4cd04d47428a8684b634b6ed \ + --hash=sha256:1c1101c1243ef0e4063ec63298977e2d3655c15cf88d9eb0a1bd4fe2db9f47ea \ + --hash=sha256:478110f981e5d40a8d82221732c57a56c85a1d5895fb8fe646e86ee15eded3bd \ + --hash=sha256:492dd64645e9d0bb843e94f1d9a4d1e31426262ffc594fafecc1697df9df5eb9 \ + --hash=sha256:5847fe2022b17c6580aeb39c8797a443411cc09edfd9183cd50ac1a3b8ccf97c \ + --hash=sha256:6503c0bdb29daf2e6281bb70ea2dfe2c3553b782b619eb5d73bdadd8a3f7cecf \ + --hash=sha256:67f6edac29ed004652c11db5c19d9debb5d835695930574f564efc8bdd061bba \ + --hash=sha256:6ebb59c694909eccb5d61b7cc199d297692012c43286e36d92983aa7bad7586d \ + --hash=sha256:73dab4841f94d888bc7c2aed7b5547c643edc974306919fe1adfb65d57cccf4b \ + --hash=sha256:79fb3cb99169fd41bd9719647261402a164da0d105a4d81f42a3260844ec5e79 \ + --hash=sha256:7e2da1df4f6fe885c46db350a0dc90a0dff4b54541dff8846faa904d255e2bfe \ + --hash=sha256:88fb5e29f670a33d9bac6aabb1d2734460cf6e461bde5cdc352826035851b16d \ + --hash=sha256:986f4df5ed17b003dc52489468601720090e65f964f8bebccf90eb45bba75744 \ + --hash=sha256:9fe3083c62e035646483a14e180d33561bdc2eed436c9ab1259c137fb7120b4a \ + --hash=sha256:a1cf1acc883bee9cb906a933572fed6a8a933f86ef34e9ea7d803f72317e8c1b \ + --hash=sha256:b034d7672f1c415434f48ef17807f2cce47f29e8795338c751d4e596c9fbe8b5 \ + --hash=sha256:bb59ba4452bbbe95d75ad3ef18df9824955625f36698ce9a5998a4a9f3c1ba1d \ + --hash=sha256:bc653defca1c16154398517a1adc98d0fb7f1dd08e58ced217558d213c2c6e29 \ + --hash=sha256:bda09ea630ae7207384fb0f28c35e4f8c0d82dd6eba020b6b335ad0caa9fed49 \ + --hash=sha256:be7ad472acb16d16e98c005f0219b0db06a47dfe8f7b4d177062e1638f871e3b \ + --hash=sha256:cc09cd1f6015b8549e7fe255fb1be5346b57e7fee06541d3f3dbb012d8c4715f \ + --hash=sha256:da2725e250866da42a12934c9a6552f65a18b7187fd7a6221387f0e605fb3b96 \ + --hash=sha256:e3f9696a9ef1d49acc452159b052370c636406d072e9d8f10895fda87b591ea9 \ + --hash=sha256:ed404c4399ad7f172c86a47c1b25293d322d1d58e26b10b0456a86cf67d37d84 + # via vllm +torchcodec==0.16.0 \ + --hash=sha256:0db3218a1bbab542aa3d820899895eb3df17346846a25dc2b9f28c92c47e5998 \ + --hash=sha256:1021a918c24184a1ce0b1dd8ca06408fe09ba988ecdaffc53b9dd51a42138ff0 \ + --hash=sha256:12bd2c7eff4056e74c1f57c115d455a5efc2d0f577517242ec66bf30712f5a9f \ + --hash=sha256:154c5cf6c2dd2801700dcf0939081f614c50dcbf99f5ecc7f6854e98b7959339 \ + --hash=sha256:158e1ed469a5300aeb843102f8d6c4354f97a835ce5dca087aa827c135bd4869 \ + --hash=sha256:26f4c4ee2896ddee5b38bf31ae96e721b853dba6b0b4104ac2e11391ddd7966d \ + --hash=sha256:2f2111c9c2b4a76676ea265ffed3eac6031db386ced3b71ff6910cd31b1f4f1a \ + --hash=sha256:4d2eddb85554cf1113935f706cb4f990d2cafc084aca6fbdea894a29d34d1c32 \ + --hash=sha256:5309c29e31b719f81dba75fcfa49a9ce6a38d069d7f570767466fe634783e7ba \ + --hash=sha256:602a5d8fa1c26d52d3c814e8912ab3a5538889f1384665260061addef2b5d83c \ + --hash=sha256:6b28fd96c904f157ee43c417c5d9fdc1a6f5709b3039fa85e0fa943004c9439c \ + --hash=sha256:74862359c18950be2ece0368d2cd2b619901b3bc9b72e17272d116366d6ef92c \ + --hash=sha256:9a899bf100cc40a0401fafc14f47c79979311d1df3fbe418c5d1019cde69d607 \ + --hash=sha256:9fcda10720fcbccae8744b8581080e985f7743cb8b365f7ce2bb6f045e7b84fd \ + --hash=sha256:a273d93170813d6aabb8e6a5366bd4e337ad2ea2d9b07226cca60f0165895945 \ + --hash=sha256:b37783d709cb60694fe10ac03a7c2e78afdbe4cfcde5ea3f7d55a17ca23dafcd \ + --hash=sha256:b44990348cb23d94c5455d3d9b4006bd58ba19a6e40d1e9bc02ff62ae070ae0e \ + --hash=sha256:d14e43ace5f9fe095287a5b4a39f1c4353c01493749e40ff4e17ebb791f741e8 \ + --hash=sha256:d26fe96259211115c17fb2bd071fbdf4da264cf04bb88fba006ffffb9c23c5a1 \ + --hash=sha256:d2e2742562569eb88230c27570de1116ac361026202447371d9c52dcac324ad4 \ + --hash=sha256:d9e4c83b3c02b206b24a5409746babd74ed00bfa0a6320b23714d73c61124397 \ + --hash=sha256:f867dadd3520d0a098bcdcb1a7f23ee99d3fccc4ba3a82f27d5228330e2f41d9 \ + --hash=sha256:fd732bf687e74ba1b1b273bb272038664bb9c13a0b1747a25b604a7876613a5d + # via vllm +torchvision==0.26.0 \ + --hash=sha256:0f3e572efe62ad645017ea847e0b5e4f2f638d4e39f05bc011d1eb9ac68d4806 \ + --hash=sha256:114bec0c0e98aa4ba446f63e2fe7a2cbca37b39ac933987ee4804f65de121800 \ + --hash=sha256:1c55dc8affbcc0eb2060fbabbe996ae9e5839b24bb6419777f17848945a411b1 \ + --hash=sha256:2adfbe438473236191ff077a4a9a0c767436879c89628aa97137e959b0c11a94 \ + --hash=sha256:358fc4726d0c08615b6d83b3149854f11efb2a564ed1acb6fce882e151412d23 \ + --hash=sha256:3daf9cc149cf3cdcbd4df9c59dae69ffca86c6823250442c3bbfd63fc2e26c61 \ + --hash=sha256:406557718e62fdf10f5706e88d8a5ec000f872da913bf629aab9297622585547 \ + --hash=sha256:4280c35ec8cba1fcc8294fb87e136924708726864c379e4c54494797d86bc474 \ + --hash=sha256:55bd6ad4ae77be01ba67a410b05b51f53b0d0ee45f146eb6a0dfb9007e70ab3c \ + --hash=sha256:5d63dd43162691258b1b3529b9041bac7d54caa37eae0925f997108268cbf7c4 \ + --hash=sha256:7058c5878262937e876f20c25867b33724586aa4499e2853b2d52b99a5e51953 \ + --hash=sha256:7993c01648e7c61d191b018e84d38fe0825c8fcb2720cd0f37caf7ba14404aa1 \ + --hash=sha256:8008474855623c6ba52876589dc52df0aa66e518c25eca841445348e5f79844c \ + --hash=sha256:82c3965eca27e86a316e31e4c3e5a16d353e0bcbe0ef8efa2e66502c54493c4b \ + --hash=sha256:9a904f2131cbfadab4df828088a9f66291ad33f49ff853872aed1f86848ef776 \ + --hash=sha256:a06d4772a8e13e772906ed736cc53ec6639e5e60554f8e5fa6ca165aabebc464 \ + --hash=sha256:a39c7a26538c41fda453f9a9692b5ff9b35a5437db1d94f3027f6f509c160eac \ + --hash=sha256:b6f9ad1ecc0eab52647298b379ee9426845f8903703e6127973f8f3d049a798b \ + --hash=sha256:b7d3e295624a28b3b1769228ce1345d94cf4d390dd31136766f76f2d20f718da \ + --hash=sha256:b7e6213620bbf97742e5f79832f9e9d769e6cf0f744c5b53dad80b76db633691 \ + --hash=sha256:c409e1c3fdebec7a3834465086dbda8bf7680eff79abf7fd2f10c6b59520a7a4 \ + --hash=sha256:d61a5abb6b42a0c0c311996c2ac4b83a94418a97182c83b055a2a4ae985e05aa \ + --hash=sha256:de6424b12887ad884f39a0ee446994ae3cd3b6a00a9cafe1bead85a031132af0 \ + --hash=sha256:e9d0e022c19a78552fb055d0414d47fecb4a649309b9968573daea160ba6869c \ + --hash=sha256:eb61804eb9dbe88c5a2a6c4da8dec1d80d2d0a6f18c999c524e32266cb1ebcd3 \ + --hash=sha256:ebc043cc5a4f0bf22e7680806dbba37ffb19e70f6953bbb44ed1a90aeb5c9bea \ + --hash=sha256:f13f12b3791a266de2d599cb8162925261622a037d87fc03132848343cf68f75 \ + --hash=sha256:fd10b5f994c210f4f6d6761cf686f82d748554adf486cb0979770c3252868c8f + # via vllm +tqdm==4.70.1 \ + --hash=sha256:c293e525e6fef9c20e8728fd4612df02a0aa31bb5fe91ecd93e123b1b7bffa73 \ + --hash=sha256:cefd0eca11b2a37a3aee776544d4f4ae913f02688135b5556b8788dfa474afc4 + # via + # datasets + # flashinfer-python + # huggingface-hub + # humming-kernels + # openai + # peft + # tilelang + # transformers + # vllm +trackio==0.33.0 \ + --hash=sha256:277340507ac46c02c06900c1d680129bdb528223c8110b0b6bc9326bb9f0891d + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +transformers==5.14.1 \ + --hash=sha256:60d196c27781eacf8637e2b533f517582907ad6f9ae142046d6b69431a5b2173 \ + --hash=sha256:9db974c4079ede2d1a3ea7ca5a240df33f2cc26fc2b36ba64c5f2a4f43b6e725 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # compressed-tensors + # peft + # vllm + # xgrammar +triton==3.6.0 \ + --hash=sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d \ + --hash=sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9 \ + --hash=sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6 \ + --hash=sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4 \ + --hash=sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd \ + --hash=sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7 \ + --hash=sha256:49df5ef37379c0c2b5c0012286f80174fcf0e073e5ade1ca9a86c36814553651 \ + --hash=sha256:6c723cfb12f6842a0ae94ac307dba7e7a44741d720a40cf0e270ed4a4e3be781 \ + --hash=sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca \ + --hash=sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803 \ + --hash=sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea \ + --hash=sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f \ + --hash=sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3 \ + --hash=sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43 + # via + # humming-kernels + # torch + # xgrammar +truststore==0.10.4 \ + --hash=sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301 \ + --hash=sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981 + # via + # httpcore2 + # httpx2 +typer==0.27.0 \ + --hash=sha256:629bd12ea5d13a17148125d9a264f949eb171fb3f120f9b04d85873cab054fa5 \ + --hash=sha256:6f4b27631e47f077871b7dc30e933ec0131c1390fbe0e387ea5574b5bac9ccf1 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # fastapi-cli + # fastapi-cloud-cli + # fastsafetensors + # gradio + # harbor + # hf-gradio + # transformers +typing-extensions==4.16.0 \ + --hash=sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8 \ + --hash=sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5 + # via + # aiohttp + # aiosignal + # anthropic + # anyio + # apache-tvm-ffi + # cuda-tile + # daytona + # daytona-analytics-api-client + # daytona-analytics-api-client-async + # daytona-api-client + # daytona-api-client-async + # daytona-toolbox-api-client + # daytona-toolbox-api-client-async + # exceptiongroup + # fastapi + # fastmcp-slim + # gradio + # gradio-client + # grpcio + # httpx2 + # huggingface-hub + # mcp + # mistral-common + # nvidia-cutlass-dsl-libs-base + # nvidia-cutlass-dsl-libs-cu13 + # obstore + # openai + # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-exporter-otlp-proto-http + # opentelemetry-sdk + # opentelemetry-semantic-conventions + # py-key-value-aio + # pydantic + # pydantic-core + # pydantic-extra-types + # realtime + # referencing + # rich-toolkit + # starlette + # tilelang + # torch + # typing-inspection + # vllm + # xgrammar +typing-inspection==0.4.4 \ + --hash=sha256:547274fa6b0a561ccf549cc9524b999a578e737d015d8709d021f9d0d13bea47 \ + --hash=sha256:65b8397ba37ccbce054456aaccddfc91e6e3083c92824df348d96ca832f3f147 + # via + # fastapi + # mcp + # pydantic + # pydantic-settings +uncalled-for==0.4.0 \ + --hash=sha256:16c4bb3337532e4bd5569adc192285976f3ad5305402256d34c67a12b5c968bd \ + --hash=sha256:335b95bd2422332ec210d518f314a16e4c640921c39fc8bf2ad095bd3538f4af + # via fastmcp-slim +urllib3==2.7.0 \ + --hash=sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c \ + --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 + # via + # botocore + # daytona + # daytona-analytics-api-client + # daytona-api-client + # daytona-toolbox-api-client + # requests + # sentry-sdk +uvicorn==0.51.0 \ + --hash=sha256:5d38af6cd620f2ae3849fb44fd4879e0890aa1febe8d47eb355fb45d93fe6a5b \ + --hash=sha256:f6f4b69b657c312f516dd2d268ab9ae6f254b11e4bac504f37b2ab58b24dd0b0 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # fastapi + # fastapi-cli + # fastapi-cloud-cli + # fastmcp-slim + # gradio + # harbor + # mcp + # trackio +uvloop==0.22.1 \ + --hash=sha256:017bd46f9e7b78e81606329d07141d3da446f8798c6baeec124260e22c262772 \ + --hash=sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e \ + --hash=sha256:05e4b5f86e621cf3927631789999e697e58f0d2d32675b67d9ca9eb0bca55743 \ + --hash=sha256:0ae676de143db2b2f60a9696d7eca5bb9d0dd6cc3ac3dad59a8ae7e95f9e1b54 \ + --hash=sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec \ + --hash=sha256:17d4e97258b0172dfa107b89aa1eeba3016f4b1974ce85ca3ef6a66b35cbf659 \ + --hash=sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8 \ + --hash=sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad \ + --hash=sha256:286322a90bea1f9422a470d5d2ad82d38080be0a29c4dd9b3e6384320a4d11e7 \ + --hash=sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35 \ + --hash=sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289 \ + --hash=sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142 \ + --hash=sha256:3b7f102bf3cb1995cfeaee9321105e8f5da76fdb104cdad8986f85461a1b7b77 \ + --hash=sha256:40631b049d5972c6755b06d0bfe8233b1bd9a8a6392d9d1c45c10b6f9e9b2733 \ + --hash=sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd \ + --hash=sha256:4a968a72422a097b09042d5fa2c5c590251ad484acf910a651b4b620acd7f193 \ + --hash=sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74 \ + --hash=sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0 \ + --hash=sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6 \ + --hash=sha256:535cc37b3a04f6cd2c1ef65fa1d370c9a35b6695df735fcff5427323f2cd5473 \ + --hash=sha256:53c85520781d84a4b8b230e24a5af5b0778efdb39142b424990ff1ef7c48ba21 \ + --hash=sha256:55502bc2c653ed2e9692e8c55cb95b397d33f9f2911e929dc97c4d6b26d04242 \ + --hash=sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705 \ + --hash=sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702 \ + --hash=sha256:57df59d8b48feb0e613d9b1f5e57b7532e97cbaf0d61f7aa9aa32221e84bc4b6 \ + --hash=sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f \ + --hash=sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e \ + --hash=sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d \ + --hash=sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370 \ + --hash=sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4 \ + --hash=sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792 \ + --hash=sha256:80eee091fe128e425177fbd82f8635769e2f32ec9daf6468286ec57ec0313efa \ + --hash=sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079 \ + --hash=sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2 \ + --hash=sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86 \ + --hash=sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6 \ + --hash=sha256:b45649628d816c030dba3c80f8e2689bab1c89518ed10d426036cdc47874dfc4 \ + --hash=sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3 \ + --hash=sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21 \ + --hash=sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c \ + --hash=sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e \ + --hash=sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25 \ + --hash=sha256:c3e5c6727a57cb6558592a95019e504f605d1c54eb86463ee9f7a2dbd411c820 \ + --hash=sha256:c60ebcd36f7b240b30788554b6f0782454826a0ed765d8430652621b5de674b9 \ + --hash=sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88 \ + --hash=sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2 \ + --hash=sha256:ea721dd3203b809039fcc2983f14608dae82b212288b346e0bfe46ec2fab0b7c \ + --hash=sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c \ + --hash=sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42 + # via uvicorn +vllm==0.25.1 \ + --hash=sha256:16fc7a28df1576eb6f7ca0455026551b8f9adb674c19c66059359ef3e964bd1e \ + --hash=sha256:902be760af4c5ebfad8af5b8ea07a53ae14e5a6c839c8ab56da30581abb75ad2 \ + --hash=sha256:ddbdec3f1c0f21afa70b7eb6ddf3faa29d26b1302ee4b5d5e00ec3af41b0c2e4 + # via -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in +watchfiles==1.2.0 \ + --hash=sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9 \ + --hash=sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98 \ + --hash=sha256:027ae72bfdfd254862065d8b3e2a815c6ab9b1853ce41e6648ece84afd34a551 \ + --hash=sha256:03b14855c6f35539e2d95c442ae9530a75762f1e26567152b9ed05f96534a74d \ + --hash=sha256:054dc20fd2e3132b4c3883b4a00d72fd6e1f56fdaf89fccd12e8057d74cd74d7 \ + --hash=sha256:094b9b70103d4e963499bdea001ee3c2697b144cd9ae6218a62c0f89ec9e31db \ + --hash=sha256:0a105bc2283f67e8fbec74253ec2d94925de92ed72c0393f1206bf326b7b7b69 \ + --hash=sha256:0a37faaed405c67e28e6be45a1fa4f206ef5a2860f27c237db9fa30704c38242 \ + --hash=sha256:0c4997d4e4a55f0d02b6cde327322daf3a0400e5df6c6b15948994bf72497925 \ + --hash=sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f \ + --hash=sha256:0d191c054d0715c3c95c99df9b8dbf6fd096d8c1e021e8f212e1bd8bc444ccb5 \ + --hash=sha256:0e831a271c035d89789cffc386b6aa1375f39f1cd25eb7ca0997e4970d152fc5 \ + --hash=sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427 \ + --hash=sha256:11743adfa510bfffebe97659fb280182b5c9b238708f667e866f308c3430dc19 \ + --hash=sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4 \ + --hash=sha256:204f299afcbd65918ab78dbc52626b0ae45e9d8cef403fdbf33ecf9e40eac66e \ + --hash=sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa \ + --hash=sha256:23282a321c8baf9b3a3c4afff673f9fe65eb7fdc2338d765ccad9d3d1916a5ba \ + --hash=sha256:24b2405c0a46738dd9e1cf7135aa5dbdb9d42d024628651b3b13d5117e99f8df \ + --hash=sha256:2581a94056e55d7d0a31a823ea92bf73749c489ca2285bfdc0fbe6b2bb49d50c \ + --hash=sha256:2995c176de7692b86a2e4c58d9ec718f753150a979cb4a754e2b4ffa38e70906 \ + --hash=sha256:2b37d10b5a63bd4d87e18472d80fa525bd670586fae62e5dd580452764879b65 \ + --hash=sha256:2cb93af48550faf1cea04c303107c8b75833de7013e57ce27d3b8d21d8d0f58c \ + --hash=sha256:2d95ddc1eb6914154253d239089900813f6a767e174b8e6a50e7fdacb7e4236c \ + --hash=sha256:3416ff151bb6b5a8d8d11664974fbef4d9305b9b2957839ab5a270468fd8df30 \ + --hash=sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077 \ + --hash=sha256:37a6721cdf3f65dbb13aa9503510ccb4451603ac837e44d265d7992a597e1374 \ + --hash=sha256:41bc1199f7523b3f82843c88cbb979180c949caef0342cf90968f178e5d49b01 \ + --hash=sha256:43d818978d06062d9b22c4fab2ebe44cf5213d42dc8e62bda8c2760cfa2eeb33 \ + --hash=sha256:4429f3b105524a10b72c3a819b091c495d2811d419c1e1e8df773a5a5974f831 \ + --hash=sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9 \ + --hash=sha256:4674d49eb94706dfe666c069fc0a1b646ffcf920473492e209f6d5f60d3f0cc2 \ + --hash=sha256:4c887eba18b7945ac73067a8b4a66f21cd46c2539b2bc68588f7be6c7eb6d26b \ + --hash=sha256:4e4ff8e37f99cf1da89e255e07c9c4b37c214038c4283707bdec308cb1b0ea1f \ + --hash=sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658 \ + --hash=sha256:5327989a465505f05cfe06f04fa9d0c2fd5432bb243e10e6f012b1bdca3c8579 \ + --hash=sha256:53b2290c92e0506d102cd448fbc610d87079553f86caa39d67440856a8b8bba5 \ + --hash=sha256:56d8641cf834c2836922899105bd3ce3d0dfc69291d52edf0b4d0436829b34c0 \ + --hash=sha256:57a2d9fa4fb4c2ecae57b13dfff2c7ab53e21a2ba674fe9f05506680fcdcc0d7 \ + --hash=sha256:63ac26eefbf4af1741247d6fb68b11c49a25b2f7413fbd318a83a12aaa9cf666 \ + --hash=sha256:6543cf55d170003296d185c0af981f3e1311564907e1f4e08671fc7693a890a5 \ + --hash=sha256:704fd259e332e01f9b9c178f4bce9e49027e5587cc2600eeeaf8e76e1c846201 \ + --hash=sha256:71283b39fd17e5408eb123bd37aeecfd9d54c81fc184421943208aadb879d103 \ + --hash=sha256:71cd71740ed2c15211ebb237ced4e39a1cdf6f80566e5fe95428da1626f4fde6 \ + --hash=sha256:7571e4464cb6e434958f867f7f730b8ab0b75e3f8e5eac0499168486ab3c33a8 \ + --hash=sha256:772b80df316480d894a0e3165fdd19cf77f5d17f9a787f94029465ad0e3529d1 \ + --hash=sha256:77a0feab9af4c021c581f695258c642b3d10c5fd4c676e33a0d8606425d82631 \ + --hash=sha256:7a2cffd17d27d2ecbb310c2b1d8174f222a5495b1a721894afa88ec11e25b898 \ + --hash=sha256:7a7ce236284f002a156f70add88efe5c70879cccbb658be0822c54b1306fc09d \ + --hash=sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44 \ + --hash=sha256:8520a4ab0e37f770afc34459c4f8f7019e153f9124dc101c15538365875d1ab2 \ + --hash=sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5 \ + --hash=sha256:89d8c2394a065ca86f5d2910ff263ae67c127e1376ccc4f9fc35c71db879f80a \ + --hash=sha256:8c520725602756229f045b032a1ff33d7ef0f7404189d62f6c2438cb6d8ef6a1 \ + --hash=sha256:8f200104103feb097de4cab8fe4f5dd18a2026934c7dea98c55a2f5fd6d5a33b \ + --hash=sha256:8f70d8b291ef6e88d19b1f297a6905ddb978888d9272b0d05e6f53309856bcfc \ + --hash=sha256:8fa585ede612ee9f9e91b18bebf9ba11b9ae29a4e3a0d0cf6fca3e382133f0d5 \ + --hash=sha256:922c0e019fe68b3ae392965a766b02a71ba1168c932cebc3733cd52c5fe5b377 \ + --hash=sha256:9342472aff9b093c5acd4f6d8f70ae0937964ab56542502bcf5579782da69ae8 \ + --hash=sha256:9649193aa27bd9ff2e80ff29bfaa93085496c7a3a377592823cc58b77ee88add \ + --hash=sha256:9f04b092229ad2c50126dd3c922c8822e51e605993764a33058d4a791ab42281 \ + --hash=sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9 \ + --hash=sha256:a16ffe19bf5cf9f5edaa1ad1dd830c5a816e8feec430c522302ab55483a4b994 \ + --hash=sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0 \ + --hash=sha256:a711b51aec4370d0dcda5b6c09463206f133a5759341d7744b953a7b62e1100e \ + --hash=sha256:a88fc94e647bc4eec523f1caa540258eb71d14278b9daf72fa1e2658a98df0f0 \ + --hash=sha256:ae99b14c5f21e026e0e9d96f40e07d8570ebee6cafd9d8fc318354606daa7a28 \ + --hash=sha256:b0ef001f8c25ad0fa9529f914c1600647ecd0f542d11c19b7894768c67b6acb7 \ + --hash=sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55 \ + --hash=sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb \ + --hash=sha256:b62f042afde2dde21ec1d2c1a74361e804673df86f51e418a999c9acfe671b07 \ + --hash=sha256:b718bf356bbc15e559bd8ef41782b573b8ae0e3f177ab244b440568d7ea02cfb \ + --hash=sha256:b8c8358484d5fa12ef34f05b7f4168eaf1932f408725ff6d023c33ec17bd79d4 \ + --hash=sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0 \ + --hash=sha256:b9909cc2b48468b575eefa944919e1fe8a36c5849d5c7c168f80a8c1db69398e \ + --hash=sha256:b9f732dc58b2dbe69e464ccf8fff7a03b0dd0be439da4c0720d3558527d3d6b4 \ + --hash=sha256:bb68bf4df85abebe5efddc53cf2075520f243a59868d9b3973278b23e76962a9 \ + --hash=sha256:bb7e52ecf68ba46d22df23467b87cffeb2146908aa523ebfe803019618cfda06 \ + --hash=sha256:bc13eb17538be00c874699dc0abe4ee2bc8d50bb1166a6b9e175ef3fd7eb8f26 \ + --hash=sha256:c0db965c5f79aa49fe672d297cf1febc5ad149b658594944f49a54a2b96270a7 \ + --hash=sha256:c16cb06dd17d43b9d185094268459eac92c9538356f050e55b54e82cf700e1d4 \ + --hash=sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3 \ + --hash=sha256:c5c19526f4e54a00f2666a6c0e9e40d582c09e865055ea7378bf0009aab857b3 \ + --hash=sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838 \ + --hash=sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71 \ + --hash=sha256:cee9d5efd929efdac5f7e58f72b3376f676b64050a91c5b99a7094c5b2317488 \ + --hash=sha256:d158cd89df6053823533e06fb1d73c549133bff5f0396170c0e53d9559340717 \ + --hash=sha256:d20029a60a71a052a24c4db7673bc4de39ab89adbaccbfb5d67987c5d73f424d \ + --hash=sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44 \ + --hash=sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2 \ + --hash=sha256:d516b3283a758e087841aedb8031549fb41ced08f3db10aa6d2bf32dc042525b \ + --hash=sha256:d73a585accffa5ae39c17264c36ec3166d2fad7000c780f5ef83b2722afb9dd2 \ + --hash=sha256:dbd6c97045dad81227c8d040173da044c1de08de64a5ea8b555da4aee1d5fa22 \ + --hash=sha256:e0618518f282c4ebff60f5e5b1247b6d91bb8b9f4476947563a1e74acc66f3c6 \ + --hash=sha256:e140ed30ebde76796b686e67c182cff10ea2fbab186fafd1560f74bb5a473a6e \ + --hash=sha256:e1cfd51e97e13ff3bd047c140764d277fc9b95b7cb5da59e46a47d167adab310 \ + --hash=sha256:e2ca07fa7d89195ec0865d3d285666286740bfa83d83e5cee204043a31ecc165 \ + --hash=sha256:e53a384f76b631c3ae5334ce6a52f0baa3a911eb94a4eac7f160079868b716d5 \ + --hash=sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799 \ + --hash=sha256:eb72919d93e3a16fc451d3aa3d4b1698423daca1b382d3d959c9ac51297c12a8 \ + --hash=sha256:ecb47f183a8025b2aa18b546725c3657e542112ae9c0613a2af79b4fa8d04ad7 \ + --hash=sha256:f155b3a1b2a5fc89cdc70d47ee5d54e3b75e88efa34982028a35daef9ba00379 \ + --hash=sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925 \ + --hash=sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72 \ + --hash=sha256:f88af53d6ddaf72179ef613ddc905e6f4785f712b49b80b3bef9f3525e6194b4 \ + --hash=sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08 \ + --hash=sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4 + # via + # fastmcp-slim + # uvicorn + # vllm +websocket-client==1.9.2 \ + --hash=sha256:0fcb57545848be86992e128218fd96dd87a6769ffdb1a968dff79632b85604d0 \ + --hash=sha256:e1a673830a9c7bfa47b1cd3d5e4178f4c9651d80a4eab02c9c23a1c3ec6250ce + # via python-socketio +websockets==15.0.1 \ + --hash=sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2 \ + --hash=sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9 \ + --hash=sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5 \ + --hash=sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3 \ + --hash=sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8 \ + --hash=sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e \ + --hash=sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1 \ + --hash=sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256 \ + --hash=sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85 \ + --hash=sha256:2034693ad3097d5355bfdacfffcbd3ef5694f9718ab7f29c29689a9eae841880 \ + --hash=sha256:21c1fa28a6a7e3cbdc171c694398b6df4744613ce9b36b1a498e816787e28123 \ + --hash=sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375 \ + --hash=sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065 \ + --hash=sha256:363c6f671b761efcb30608d24925a382497c12c506b51661883c3e22337265ed \ + --hash=sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41 \ + --hash=sha256:3b1ac0d3e594bf121308112697cf4b32be538fb1444468fb0a6ae4feebc83411 \ + --hash=sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597 \ + --hash=sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f \ + --hash=sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c \ + --hash=sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3 \ + --hash=sha256:47819cea040f31d670cc8d324bb6435c6f133b8c7a19ec3d61634e62f8d8f9eb \ + --hash=sha256:47b099e1f4fbc95b701b6e85768e1fcdaf1630f3cbe4765fa216596f12310e2e \ + --hash=sha256:4a9fac8e469d04ce6c25bb2610dc535235bd4aa14996b4e6dbebf5e007eba5ee \ + --hash=sha256:4b826973a4a2ae47ba357e4e82fa44a463b8f168e1ca775ac64521442b19e87f \ + --hash=sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf \ + --hash=sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf \ + --hash=sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4 \ + --hash=sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a \ + --hash=sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665 \ + --hash=sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22 \ + --hash=sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675 \ + --hash=sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4 \ + --hash=sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d \ + --hash=sha256:5f4c04ead5aed67c8a1a20491d54cdfba5884507a48dd798ecaf13c74c4489f5 \ + --hash=sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65 \ + --hash=sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792 \ + --hash=sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57 \ + --hash=sha256:67f2b6de947f8c757db2db9c71527933ad0019737ec374a8a6be9a956786aaf9 \ + --hash=sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3 \ + --hash=sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151 \ + --hash=sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d \ + --hash=sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475 \ + --hash=sha256:7f493881579c90fc262d9cdbaa05a6b54b3811c2f300766748db79f098db9940 \ + --hash=sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431 \ + --hash=sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee \ + --hash=sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413 \ + --hash=sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8 \ + --hash=sha256:a625e06551975f4b7ea7102bc43895b90742746797e2e14b70ed61c43a90f09b \ + --hash=sha256:abdc0c6c8c648b4805c5eacd131910d2a7f6455dfd3becab248ef108e89ab16a \ + --hash=sha256:ac017dd64572e5c3bd01939121e4d16cf30e5d7e110a119399cf3133b63ad054 \ + --hash=sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb \ + --hash=sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205 \ + --hash=sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04 \ + --hash=sha256:b7643a03db5c95c799b89b31c036d5f27eeb4d259c798e878d6937d71832b1e4 \ + --hash=sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa \ + --hash=sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9 \ + --hash=sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122 \ + --hash=sha256:d08eb4c2b7d6c41da6ca0600c077e93f5adcfd979cd777d747e9ee624556da4b \ + --hash=sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905 \ + --hash=sha256:d591f8de75824cbb7acad4e05d2d710484f15f29d4a915092675ad3456f11770 \ + --hash=sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe \ + --hash=sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b \ + --hash=sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562 \ + --hash=sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561 \ + --hash=sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215 \ + --hash=sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931 \ + --hash=sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9 \ + --hash=sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f \ + --hash=sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7 + # via + # -r HuggingEnvs/04-data-agent/hf/locks/requirements-train.in + # fastmcp-slim + # realtime + # uvicorn +wrapt==2.4.1 \ + --hash=sha256:03b5598edd435373278731d0d53449ce7a9626bc48d5548e4a71124ee3e526a1 \ + --hash=sha256:094a606d0bf1c4b847b3a743d22fc69cb164015b8c70cf6f20a534d30889ed47 \ + --hash=sha256:0aacc1af512e040fb0b3c4593a8bded9aaa04235025f08a54eda9d8c1103d3c6 \ + --hash=sha256:0c974c36e8205255a3947dad9c2fe000431b57dd522946e56c192174a7f92a0f \ + --hash=sha256:0cbee8d00840ac89846f6567c75506260a15fbb4dc74de180678fafe09458d47 \ + --hash=sha256:0f5990f5db090f069fcd577cc61cc8d463db73cde132abba9033b0a264fc06d0 \ + --hash=sha256:1e84ec5d89a0a07a0ef6bcd343f5c8ecdc95601d71de3058cdc63274e86c193c \ + --hash=sha256:1f1851be0e593d65e68d7c1bc9d3b971e19fefa219f617e490d75e80e467ee14 \ + --hash=sha256:1fe758b9c2d49138231ec3efabd106fec665f86fec50b3d02d7edd75f08a69ca \ + --hash=sha256:20327e162ef7953fae56b31a43ca457f94ed1fc7a206d01e3d5c8412d1b91572 \ + --hash=sha256:2286e8e4a937966706463d1bcea30dfefee529e6e73e097a18e9e066a07ae6c2 \ + --hash=sha256:24389748f0b9d5b67e478fad4fc8b3f1108422ef80716e48eead6cebcebbff08 \ + --hash=sha256:28cb1c2713b4377bf03ddcb3e76d8216e4bb334199066c6122be7eed2f72de8c \ + --hash=sha256:2945f4bce1518e09eb25458dbd09800f7aa3fee81a5a5cefe66c4e82fe8cb69c \ + --hash=sha256:2b21924949dedc3ac63725e09b9b0a130e771975f03432789344ed3422c46008 \ + --hash=sha256:2cedb743dbdfb9b6d4f11acd8cb6329460264429777e81ea2e86b1b72ea503af \ + --hash=sha256:2e2e694057bcfd46f43e28f7b50fad8f5fec7113cc6445fdf251219539a08f28 \ + --hash=sha256:2f725af353bb3319c528ee69dbff838599426974288b281a3258d5397b18cb63 \ + --hash=sha256:2f86e328c482bc5383b4eda5094be0bed3617fc3076aa9225ff1a9eb6372de9b \ + --hash=sha256:30d11c289b013bf384ff1a1a6553f150d0b855901708a9bef667a5680f8247c9 \ + --hash=sha256:3152b2e94d733a9bd70dbd1c95f148266f079a70e9ffca2a9c5dc421ade1b4e6 \ + --hash=sha256:355e766371657ceca791041b82a7943aa3c8a0d879359c112e63bf3c1c8cfea7 \ + --hash=sha256:3593b43fabab6b59fe77e38f7e40974aae120c30cea3fd1ceaea6621ee96be00 \ + --hash=sha256:37ff91b390400463ecd4d080ea823314510b6843ac53e6e35cc09bba1805d466 \ + --hash=sha256:38819761401baa2d11916d7265b82f23265f8fe5a31c431dd7c24a8863c65f88 \ + --hash=sha256:3bc7ab495e564449f39db2f54c1033d7190c944736d525f3bd06cefaf0134df0 \ + --hash=sha256:3bfc6907ebed560d2d3f677c3b17bf6199679163b6c6e475035c9dca497d1697 \ + --hash=sha256:3d5c8f3b4eae814213d4097f134be03aeb23816e6358213454ec5e62e8449fb6 \ + --hash=sha256:42d01574bd4bcafc3476e77a95c6c0dd6167101991401325fc5591e2db21a91d \ + --hash=sha256:432f402f9b6014403cacf9fd18a6bf089c77964330eae951a155131ab0414f5d \ + --hash=sha256:47c267617551e906de72f6e7265aa3bce84c63d44513d2d2e735e943422aa0a2 \ + --hash=sha256:4935ad7324d8637715f7cefc35ad65be80900f91d1631947b01bf70651daa328 \ + --hash=sha256:4d48f1720569b4f6e2df97783fd270554abc4c6ebd74b2e5f15e5110283aaf78 \ + --hash=sha256:4dc92697444ee380544fbb43c86612d8486529aadf917c22b5524141d4af074c \ + --hash=sha256:52e474424ea1863199e8635a3915b279164929e71b81af97f2105cab1ce78ef2 \ + --hash=sha256:53e15cd74bd6b84d7fa90b93dda7334d85f4641fae97632de8aa61d268dfd145 \ + --hash=sha256:55f36bb1461f93beaf18d818e568b5de343dd8fade7456773d201a38ee723bd3 \ + --hash=sha256:5a54744b1193505f19016194979b773ee3754a199e9ad90db18f2e9a18fffa16 \ + --hash=sha256:5c0217b8c12952bf4d137a19ff2c61cc9b626f52c09c43f0289769c9f5f808aa \ + --hash=sha256:5d83e412665aeb1e854eefbf1564d0d67872d9994b502a0bce96e6ff7f4970b7 \ + --hash=sha256:68a403adbeb4dd2654d6d108e43e69f0f90e6d34cb7588e0e6589109f6985e67 \ + --hash=sha256:69fa244ddc1f9c3e8d390e169ebbf018eee53c2e05bda61c980418df89585ccf \ + --hash=sha256:6b9df84f0a96763159cccb8e3b0ed83cc950c7c8bf82d6e45428372a805b3224 \ + --hash=sha256:6f32a45d0e883918387aabf5384952e5a93ae40be0a6fb5f1eddc8281af4c714 \ + --hash=sha256:6f5577fdc702427698c81c8c9c3b8479cbecb3dfd0f697be8c1d09c89f5838cc \ + --hash=sha256:7033c3c5eb7cd301d9bea8a01660fbe9561c9a3a069d1733e9a712917def0aa2 \ + --hash=sha256:707d2bef68deddd0fc74a81103d91b286a69ac5a9ff0f0a6dad66f8787e86697 \ + --hash=sha256:730195aa58afaa7dbb5d351e8728cec6763be2605ef82eec9f134950e3c8618e \ + --hash=sha256:78af62413095d0a57077606654ce85e273deda8e2bfa28fdb04257b962d94ef2 \ + --hash=sha256:7aaff952dd6930fc87b52d44bf72ac8426e45e90058fe0f2da446889738eff44 \ + --hash=sha256:7c89bdfe72c2b96bbe431764a90a25723570a896b605d08e08bca6fe150b8a33 \ + --hash=sha256:7cb3035b332bc9d21478600ba82c7c71e2d074ed9b4e76364297f54598792227 \ + --hash=sha256:7e86fbc2ac8a363ea04abf631fad82720e16b17a25020f32dbe9b24a2ed2b0e3 \ + --hash=sha256:7ff549316d94c404999f96bb0a6993222566231ff4bede1b6f68908d1958a08f \ + --hash=sha256:80afa3b7010e82899044a2a189c468a0cd8c89980398a59ba3b96b451a6dc5e9 \ + --hash=sha256:8342f332dada211f64b74609e332d727b13315e9a83177f7918bf68c59f815f2 \ + --hash=sha256:898513db90d55a4ed3009312d41c12932b8163edbae3535280046d47aaff774f \ + --hash=sha256:8c4b44e4be7680fc496816e824b6ed134d781c4da296e44b75399196e5c248f1 \ + --hash=sha256:8d67b916f2f777a6d31067e842598e059f1a0e29022c86454564fc9efb9a0c19 \ + --hash=sha256:8e82a1669d63b79a2b53041bbb6ea096b5763cab3ca698ed7ac244b3acb7cd51 \ + --hash=sha256:8ec7aeb92e956810eedc268fe75f8c11f05d6c3d864d0995a53eb6623b3b6a51 \ + --hash=sha256:9356dbb59199a0e4709de35fa4a1ac1a88ef6da99711a397f5b009233faff326 \ + --hash=sha256:96a5023fa63ca2f095f7776c8bfaa1694547577f762646c375abafd8f8ae649b \ + --hash=sha256:9ecdeb8a1ec13397421e6f925412186bd87ab86d63517e6825b6d7bccf781f26 \ + --hash=sha256:a0c3b217332cf0c4df085fec41c126bf7507d6c1ca0efb3ba8d2b3fd234d4e73 \ + --hash=sha256:a1e4870d3368c6c918f38e308d5dcea970ea5b908ce889c21412f3a517ebf0c3 \ + --hash=sha256:a23b89621cfeb3329b1a290596bf402e61d7d5a647a65ca8ea735e48771b71d4 \ + --hash=sha256:a524ca32f0bcdde2b728d9f81f9527d4dd24b13f15d180f05d08cdc01d1cebe0 \ + --hash=sha256:a7ce62ee24eddb76c317ec790c44f4fbe9f34ee0a5dae37b30c65a2b8f3e3604 \ + --hash=sha256:ac1939ccf3e1c33f463706fbf52db5075f5eefb6042bcc1f9cf48c2c20ef478c \ + --hash=sha256:b0d38d9cc23e9781e6584f303e5c5a9f0d45b85de92ad45678c4dde8d49d9535 \ + --hash=sha256:b0ee076be124406a7f97ca663c4a3ba32b6bcdd9102ca82497feaca79e9cb33c \ + --hash=sha256:b19e71c914c435d2c5652caea386c9bf2807979e957183f5bb06d5ed5fa8b55e \ + --hash=sha256:b4e7efdd476ac631a0181551fd9aace844765ea3ce2b5133b194fae4421e8ad0 \ + --hash=sha256:b6ea396edcfb305698f44ef0a47b9adaca15d30386ce093ca606844f776d7236 \ + --hash=sha256:b78dd8d058468156003ee8c212dab334b6eea18cd9c5db3b81f6c90a9408afcc \ + --hash=sha256:b86bfe741840cec6d8d5858b1eb53a325f564f08d7e6fb5694981961edcec230 \ + --hash=sha256:ba66eccadd4b857a845e309c08457bb71fa8300000e508e3a86cd695bf9c7515 \ + --hash=sha256:bc67d4872af5ab2dc1b88904097b92ac00e7658fdf010dee36807807fe882ac4 \ + --hash=sha256:be6cdd7121adc89a6f52e3c2f4e26a2d4dcdc1c0fde47e3156234db8939e4cdc \ + --hash=sha256:c15c4ede3fde08723cabab0a892d4b75d35b5a6f0a51c84e6542ac0bdc0507aa \ + --hash=sha256:c2ab24ed5d3a19f31fb815419a61b27904b5b0222eaacac5e1853d5361035df8 \ + --hash=sha256:c57abea6aa7de8c584b32958d8f606a08d659d3a5a1296ba2038ea9c185a417e \ + --hash=sha256:c6c35541cc729964c65c2b9b1f9cf317811039abc2da13b4557f20f21cdc292b \ + --hash=sha256:d2d6f9abaa52de05090a2b4a4c1d0e858a268c4de69eec277506af9fbcccff91 \ + --hash=sha256:d60702ebc914d0bb01aa48f5c1785ceaaaa505e0a841b444a69d6ceb5de4097e \ + --hash=sha256:de5d2d7f12557c9e994b4037330efa5700fccb9f2b9df0bd4ccf384eaf73c254 \ + --hash=sha256:e0a518cac3e789443af54fc77f23e66f17d80192c281b160b42058f11fabccc5 \ + --hash=sha256:e32c5951c36fed88b6c603dc0bab209a62dc3217bd8762413634725fe28f2f3c \ + --hash=sha256:e59f1d5db7bba65f57a2b121eea839214465e878d957d9ab29ad039279ec2a66 \ + --hash=sha256:e5b347c2184906daedae9a2f9fac67cf02c61f7b6498f09cafdcdd6b404ea9be \ + --hash=sha256:e78c0f6d66a564bab245faf170d55595df6fc237117b99d97fd838678f0906f0 \ + --hash=sha256:eb7c0f8bdd21e954bad89d554cfb7431c2f8179842853f199841ab90cbebe914 \ + --hash=sha256:ebf3b703752b53366fd02b7fbd8c447428e0349c9af3fc17c7a05076dbdd749a \ + --hash=sha256:edd03758a7578526642508b8833d43496fdfba0f64e0025dfca153a7c1777735 \ + --hash=sha256:ee437bd7fd050823ef731aad20e968ca8fe670b95e1841f5d201bfdbad4a4e96 \ + --hash=sha256:ef19a2590b195ac294deff8ee350a027a479afdd2ef2170ce3900af92406e110 \ + --hash=sha256:f1556a96b20d5bfdc9cb5dcd0a3ec65cbbac8a4eebcd3cd34efdc91c9519f660 \ + --hash=sha256:fb5b3f94258bcf71db902795f4a71151c7a74d9fe21fffa00752d2bd286866f8 \ + --hash=sha256:fd6390aab9e8aa40c52eff3c180f098e8d9f5894b1fd4c4fd2c207067b33ed16 \ + --hash=sha256:fdc997819a6df4c65bdb0a1c5601c98b479ee34cc2f94ffa98a767034ad6366d + # via + # deprecated + # opentelemetry-instrumentation + # opentelemetry-instrumentation-aiohttp-client +wsproto==1.3.2 \ + --hash=sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584 \ + --hash=sha256:b86885dcf294e15204919950f666e06ffc6c7c114ca900b060d6e16293528294 + # via + # daytona + # httpx-ws + # simple-websocket +xgrammar==0.2.3 \ + --hash=sha256:00f6ba916fe84552f303b1b576061296ff8bea1e24d065efec49543489dd5217 \ + --hash=sha256:07d4c238c4545741573653fbfa66072ae208a85926c3a3f4d57c99fd67fde886 \ + --hash=sha256:11108010c54c8f12f0b14c239ef5bca217cf107fa3d0bf0db008bf594ab0a1ff \ + --hash=sha256:11255f184971489fc72b948b096e2917f482ba2dca975177f5411562cedb9c6d \ + --hash=sha256:11d5cd45891f311d7c487635fdee637c5ae375d8292601ad55483c89653749e6 \ + --hash=sha256:173e167d43a5cf4171eee2be86097decff8803b0a0853d7baaf446c732a7d3a9 \ + --hash=sha256:1ba69a13ae4cc94b7a3a0b5ac67865ce372e0eac7b6486f7e8ca0c0cbbbc3097 \ + --hash=sha256:29d0fbd4709733b224e6e115d1001fb07124c71fe114a9087a2e3e53ce871517 \ + --hash=sha256:355f21445ba361fa258d34106fa31852be9222f0fd3f48fa3fe089d5201679e7 \ + --hash=sha256:3a718fa2c1bfc06951e1c480a656c28bde6d5f828199308fd577bd264e05b923 \ + --hash=sha256:3fdcee5e375c8cfe83e41c4a396a861cafe7cda381c566c24d26628a970a9ec4 \ + --hash=sha256:48d2c9d2bab9b60653204bf334663e06f6044d8f5c104aca68ee23526afb3161 \ + --hash=sha256:5eec3987abb915b7182587cf8063ae16b9fcb51e8b5f70558d8d2f0bfb8e04c4 \ + --hash=sha256:66501b2f02d5620971a13a085566f4fadd5ce86c6de41dbe3b47b5062644881a \ + --hash=sha256:838f88cd74c00670e4b0797ffd7bec8399b67f90ca0f199c96a68333f70553b4 \ + --hash=sha256:8c262af85340825e6bda407293713582f95169fb098cb107751b55c0b3c309fb \ + --hash=sha256:a1b6dc2657d9a1efbb770e0982ec0f2718c1de198cf24ccc8db2372eba01cb64 \ + --hash=sha256:aa35f24835a59c822e249ecc80912eea4de03fc8b04afb2f82c8b950a56be6ef \ + --hash=sha256:b257973b2878bcb2c02f24057d174c0d9715cdc02e8ce29149a350184e619f86 \ + --hash=sha256:b385f2ce3dac24273325ff0c41e3098a07fba061c265ab68cfc9f7fafa71e5b3 \ + --hash=sha256:ba7051a0cc67f9a2ebb9c564f3cf286ccad532534b3faff8b261fe0909dd18c8 \ + --hash=sha256:bec963362548e48b9a763de8f801d2b6c3f6dfb050cc88de2a83fe5e90dec357 \ + --hash=sha256:c4039c128c1636a6ba4def3c8dc31f1d3942133195a0caccf4efc7fef50f2667 \ + --hash=sha256:d38fb3ad3118b8f08b1da53fb6feb81a206e6eb9df6abb16bda47e2cb272deef \ + --hash=sha256:d5c03cdd7847e1281ef2189e600fe7b4fe39058e1f7b6946e58c7f0308dc11cf \ + --hash=sha256:d7e0fa6f30ae206fca972a8ef5f607be543f32a9155523f445730a427a264679 \ + --hash=sha256:e7787dd8321a04f86116b756aa3dadd622e3607a3559b1e986cc5f77da00d68e \ + --hash=sha256:e983c6521ebac727e8513acd3f3eb76b9bfe9fedb3c528ddb01845c08f098652 \ + --hash=sha256:ea79d53314d614a7beab09570a659ec72e4c79fe61b0a9ad153da95b4ee8fcd7 \ + --hash=sha256:eeb5e46bd7d3230e5d8e6385793c48ec872a4fb377c19d341dbcaedc41f495e9 \ + --hash=sha256:f1270736d74ca3276cfba593457c23b9ca37032bd8051614533c81ed892d2727 \ + --hash=sha256:f26c8bb1845119856b09658bcf2ee525957dc618d954684e5c393d16bcc1f1da \ + --hash=sha256:f76423630ae3ac4e090cb38ce1e30e7bcc69b3dee4d22d94353944386a4c6f18 \ + --hash=sha256:fdf081fab29694302d41d61dcf52fad7d253879a718bc6afc68db0a0dabd7f19 \ + --hash=sha256:fe71c621a18ec1fd0a74e755e72c5e521c4854b75d14ad52f1fc8c325d387124 + # via vllm +xxhash==4.0.1 \ + --hash=sha256:0163b5d259de23ae9e07b7eabf435ce4704f6f205589a2b154e6af4be985ce1b \ + --hash=sha256:03600a8987849b2bef7be795a60a6052b635c63fa98b718b08ca5ee823691cfc \ + --hash=sha256:04f9a24de11a6647666d5302fd73d6a5224ce50ddc965fb0bb44cee736e6bd7c \ + --hash=sha256:06713a5aaf1d0905c5579416c020c02e42b3ceb931e86c7d3b7fb85403dee3f3 \ + --hash=sha256:06d7fbd609503c3be5e65cdb6bb2f040d6a98574404e2e1d5c60815c97fff4aa \ + --hash=sha256:0718ad66f4ded2411f8e62bdba549ee71e313a2d26ef5060ca3fdbf29897dd3c \ + --hash=sha256:08ed8da18cd4fd0a6a5d6a444852d8fbd0e565388a74a4937085451b5f1a312a \ + --hash=sha256:09f9feb118966cc6650e1806205d577eae7ca394aa6acf349a0b62a94bbeb329 \ + --hash=sha256:0ab851b45c70d4992be7cdeeee16f97a0b677408c758c4b1efb1cfe8030bfd37 \ + --hash=sha256:0b1082fd0f089ce9098ed77aad8b777b5d156f8ac601c69cab73811822b8ef07 \ + --hash=sha256:0b20a06454b34f1531fc677c54efe2ecdec691ef9224f7fa919bf2c1363f7ff1 \ + --hash=sha256:0b42a5a26607e4b2409fea174773a66f2dff9dfdbf2c1a851bb7b804e2c97535 \ + --hash=sha256:101aa300de6ceef3d9c77569706330d8921fc45dd82bceed2084f1e9f2557a24 \ + --hash=sha256:1216f7ba5683f17a89eb7dcb4bc50a0b743dfe1902278d7b3d0786f538118433 \ + --hash=sha256:1642907941ee4b75aacc3db688af52ea02ca2305ab22af7ee686ed726b332684 \ + --hash=sha256:168dd6b51725a222abc722832e56624d15a63fc2e8249021509c93f1063913f6 \ + --hash=sha256:1749f0688020209fe0d357ce1e1cd9ec9c6161ed0405ea949d24581c4c43fa91 \ + --hash=sha256:1b3cccf75eeb5b01639b2feadb042a8e07889293b7ca72fa2985e7dcb64763cf \ + --hash=sha256:1b50223d92df94d54e1a31469335a2c74b16692e6c1cb726f1e6949514458706 \ + --hash=sha256:1bc591533fc975614f7e13594daee76af96b8e1fbcf8de76c8773858fa9e7cea \ + --hash=sha256:1c2200b98a805351cb3142ae4e1fdcc9e91b5e20f5d30d4862b0b96f92558f4e \ + --hash=sha256:1c7c642a0f79c3e3cf2965475507574d3d1a50ec71060039d60cb87358667cb2 \ + --hash=sha256:1ee523f51718e41753f04f7102bb4dc55a18d2ea5cbaceef8ec7ca08571bd428 \ + --hash=sha256:1f3346c5c287ac3c7f38b20380f55e8768230e7252af59fabcf3b87ab21e4256 \ + --hash=sha256:2194bf96d5f3d4e0cb65deba370ec83dda3edfba42155f9384190ed5e51ea5e2 \ + --hash=sha256:237b8f63a2a0fcfb1ffc06e21dad23add44e6d354b2b014364a1d41e419a4dee \ + --hash=sha256:23a4376b4a3183cb50d4d2a3179f887a7773cc695eb2c908e551bec3221b8c60 \ + --hash=sha256:247ece770647c0aef080561fa996f9774b4dadce2d0c42eeb98229db7dcf820d \ + --hash=sha256:2696bbac613f6880fed60316c298bf3091d4f8eee3ae2e9466f70bb76204fb0c \ + --hash=sha256:26fe6238c2d5b11ed5063b9bf4eb290624b004fd074688da6bb079bd564f10d7 \ + --hash=sha256:2d52dc7c33c1b83082b707f6b7814dc76d2faaa2ea62bd9c5fab4b36f83c087f \ + --hash=sha256:2df3ca8757dc381e75e90a4d7995a6324f58a923c7145220a7b2c0231f66fddc \ + --hash=sha256:303121aab4b7f898058582d7962ea79d9e26e2379d7b6d8743f70f2671674481 \ + --hash=sha256:3088dadbffa33c29e0518578430a7dff2e901a212e487aefa5faaa0dc06dad34 \ + --hash=sha256:31d86f9e81f3e84e00131ac7c54caf5119ae4ddd82c09c31cff597c813ce1ee2 \ + --hash=sha256:3358097d333d40657569ec1121e21043dd7d0efa10aead1b50e8b4fa83077d7b \ + --hash=sha256:33e270d302c95ec426dfa0f5a4e16bff2ab8d7b8a46faa4746affb05e684ac77 \ + --hash=sha256:33fd538191f47071deef6b1f676535e2aa770f1fd150ae4cc75a34c9e930be3d \ + --hash=sha256:348c8f288dc961d6bbd1985c8152a3ed7a85c95df00e82320f0c5215d922a399 \ + --hash=sha256:349775ac30372b344d2338b2a168c0a1312a644194da25b8bec476d55761a128 \ + --hash=sha256:34ed93e20bfd98d722b902121643791eeb4b1641871e2dc63d0d4c2d93f187df \ + --hash=sha256:37f667dee0f867c42894b34e2a6fe26bf195c0ea4683d9d2b713db023f242c3a \ + --hash=sha256:3891efe3d7a531ce6da0a4a50a99dd41c75b8fd4ca19d73c86431b4db5c305f0 \ + --hash=sha256:38c3d22129a6958846a3098d68bc8e661704461c0be4793ae28836e4690c8478 \ + --hash=sha256:3c2445edafc300cc40feb6a25a8356a971c30cd0bf47b5349c2ad74c508343b1 \ + --hash=sha256:3f68fe400ceec235f3e4a4b02a28c2fd2d283584a193223c921dd4c48f1d0754 \ + --hash=sha256:3fb1d30d4b6d6e2c4a08e5ac6fffdb2b572d2cfcca15a5509cf4e7a1350f955c \ + --hash=sha256:41e579025a6e13a99e6d71e39c9cfc621a0dcdbbf19106325e145fa858f2d794 \ + --hash=sha256:421b94f3ba7067958d02e38960d987756347aa150df06df11aa68ae1af78c619 \ + --hash=sha256:427b62d62d4f967fbb10b82a3813e4875c2a6e7e7634739f17265b650c7f65a6 \ + --hash=sha256:436e11b4dd966afe5f7f665e4cc4c5485ffe3ceb42f25a22e1701d236abf1853 \ + --hash=sha256:43bcf2a871f28f16135545415cab3ec43904d4c80425a64598a9e6cebfb2b5ba \ + --hash=sha256:43e5f9169e73d0f0db33b5f6b8554bcce69ac278c966daf83d5eb4eb2f13829f \ + --hash=sha256:440c401e146ce64bdb3beb8ff0c84677b6f21307c28a34779071cecee5d4d70c \ + --hash=sha256:44ab12e8cd17d4f001769f00ad465208b4bcb897ed29e65f058f74466b57a98f \ + --hash=sha256:4528cf80ebbbf57d40edfb31521ae265daa6dd636d615b1cf0ac86209579e59d \ + --hash=sha256:45e88111ebe331de478ef8d4293efbe88f3cf8b863386c9a2357136b838e1af0 \ + --hash=sha256:4741d42d59e4e5fa1a86c17ab9c27dc8ea459c700d91b6742fdb9138d9a516cb \ + --hash=sha256:4751f1d7eecae6b2d2a773630f1a7248f125c9a92a456694d03c15bceffc9d68 \ + --hash=sha256:488ca5c5e28ef56ec4bbb12f835b3f1cbecc5f3510062e70117bc6594851932a \ + --hash=sha256:4972332c079d6aad69c4620a68d015a4ecb33141583f70d642cf9edf6a713763 \ + --hash=sha256:4a252fb862b0ae2590587e625f47a0e03da05cf0205e8830b67b6596c06038b1 \ + --hash=sha256:4a76345f5aceb4ec404918edf9c7f2b5507db864dc0d7455982009ac0890b57b \ + --hash=sha256:4af350bc3f329970c0e3a59af84a8a30998bf8a9167eb50cd48e59baaa1d7bec \ + --hash=sha256:4bbf3ff651e0f1a19beb5d0f48e0874a9bad2482a588c9d214c96ef1fff1cd9c \ + --hash=sha256:4e5141543c7f7fe3087500bbb4ac2845cb528a980aa91f8f1e661e2292ff4a5d \ + --hash=sha256:4f5e5c6df4b703afcbe9352d238a51efd97c3b91fdc3a2052e40fdacb1e7505f \ + --hash=sha256:515a822c73abbf6a0b7c70976d9662be342835c9d78b8dc7c023411f39c35dbc \ + --hash=sha256:554f87034635bcec47c5d72447bf3db7e02da1bf493a0ada010db28a76f891c6 \ + --hash=sha256:567cbc630302a46a8ecfd943b309ccf5372bb3718f1f3762d452df30f033bcf0 \ + --hash=sha256:57d7fa8f23908d173001c21a9e82bfc6ad997d1b6c270fb121812b7ed158891c \ + --hash=sha256:5adf927dca8c47fde7e683fe69efdd81bc865c4db1fb6bb00b391e2b6185207b \ + --hash=sha256:5b7875ac1a2edcb691f27642b8b94b904baa6bcecb7d79c72df2228ba8cb5c51 \ + --hash=sha256:5b7979f71d06ae45a769de0699900a246d8cb632db1e8bfdc79ec019063a503c \ + --hash=sha256:5c2d525a3afabcd8e3549d85fc7e111fde6bc302d06a1893fe73adb79823415e \ + --hash=sha256:5dc434c946012e6d8a72b10f970ea30755b718251dd7591dbfdabafd3bcb21bc \ + --hash=sha256:5f1ea31d61bcd2cd2f3ec4ca80a64187bbd7948f490b63cf0dcbc6e717b4c1e9 \ + --hash=sha256:62198213fc3e0c56e567894b318ba45834e007d065f84ba6dc9165d21546fc56 \ + --hash=sha256:63aa52659bc32bb9bd7cb5caf523b4d14429a477762cfac886132d687c1f80fc \ + --hash=sha256:649f2682c090cca1ac4037866381f3652eaacbd56e5178030f4ce1325b8f945b \ + --hash=sha256:67e57b834e07ed973cee7b6da1548ff28a56458d77696fd2a5f397f340694848 \ + --hash=sha256:684160b3c0a9b62c6f0de90f44e11dc5d8643dcfa18a5856b45fb1c47478bb71 \ + --hash=sha256:6a8c5ce76b94ba49f3be8a8f2611abc6564210702c72ac9e237ca2bebfd17794 \ + --hash=sha256:6a9f98af872355e0c02439e48583958eee00e60b928bb20476460d9d40cb7b4e \ + --hash=sha256:6c45258a37fc22721395c09927cb982d3e7a83607cab15be7e2416501bd3a330 \ + --hash=sha256:6cbf4e21ef0890804b5bb9ad25c48f9c127758d7f6c66bef374efcacc63c738a \ + --hash=sha256:6cf633df84d80a1668fcf61e330791dae46825e395549e7d34f376411e75088a \ + --hash=sha256:6efb8f21cc136c79b3e5bb747c8682d37916fb202cdbbc32182de5c4e47f821f \ + --hash=sha256:70129ebb8f20e1ac1da58b78ed381624bd689a43a9a7366560bd8fabea145105 \ + --hash=sha256:704381264b36a18b9c62ecbabe2e71d0fc58c77c129c15355c989b10bf05b6b0 \ + --hash=sha256:7236be540d6be9ce448d98b940dd26ddf70ca41012e8a14a53fd9354cefe4e8d \ + --hash=sha256:72f34834518157a75e7090f328ee7a16c70c804cfc7c694fa069cc888e9fc03e \ + --hash=sha256:74379a577a9f3b6afbdedf1b90e5c7764467051977f18a326d7d607336d743bd \ + --hash=sha256:74a164e8b63f1e9cf35c9a7809d082b033d1a00e7375d5d814415436e7867e57 \ + --hash=sha256:760de77279e9cf9c81d012ce0705cba13afccee9b09c480f17d778c8c5cefae8 \ + --hash=sha256:764b32d52d15b8b95ac8160e540772fa1adeb611fe40bffaeb42e7bf98279e44 \ + --hash=sha256:79a3203aadf39637869dfea1185227d8452844d78b837e54fb1117b4d34ba5c3 \ + --hash=sha256:7c343ee174d417a44d0c3355602c0cbbfa52a04d1bbbf1723378c7d2c8f60626 \ + --hash=sha256:7e27dbed5c4ba033919e4b4ed8dc14e029e91d14a93cd9f920d25277c7df6781 \ + --hash=sha256:81507a68ba84c55241fb61cce1469f473a5da4205fc8ef6f698e5948eea8dd88 \ + --hash=sha256:81664268dba92e037b740ecf37fa02f1cab4a391f93f28e35792b3341c60648f \ + --hash=sha256:839f58c5bd9989875be0fd28446dbf32cace2c2cd8bf2f6762acdc38a95cd1aa \ + --hash=sha256:83b8c2013edb5dc1f9e7268b6496130705bc48d79c86bb8817b3d210b81a5513 \ + --hash=sha256:84df5f8da574caadbc0cb1b8866ecc2368cc941f0cd05f677756c802f370dafa \ + --hash=sha256:8580aab306888224074c7edeec734de0c3c5ccde65b2da4e6c9a5e28f7c0a1bd \ + --hash=sha256:85bdd40cb505a11e0ca04191711266c5fd696ed786ae83849955e457774edc96 \ + --hash=sha256:85e402dab0f9acd3604539747c6fcc57dc188a18af6ab07eb8189351cd32466c \ + --hash=sha256:863f3d3b44110f7243e86cf994aa5c5d88f2348b6e84ab4402fadadfbf9f7da7 \ + --hash=sha256:86b2b12bec60c678ed8f5cca0258ad93a8928ebddb6ca7732f0875afe1451d1a \ + --hash=sha256:87aa309a93bd5ec13f14309a305ff4e9bf74c5363fc46c264c0a22edfd5b0670 \ + --hash=sha256:87cbdec1a7dd930079671a60b249f3ca4e773e6fbd0676e21e36fdc9dd0f3b00 \ + --hash=sha256:87da13df72c5612771cd905a8b121e0bfea62d7659b1c92198736eb722220e83 \ + --hash=sha256:88d87719fe6bddf117238b341c5db851f8e96ba68ad9832b450e4a43dc60b37f \ + --hash=sha256:8b4477edc03091f51f5309406d230851c23cf4822029e3bf40b8df53093fff1c \ + --hash=sha256:8b99ebaf9e816ac5069423b1367ee7e8078fbcebcf62545506bb0608d2f4f468 \ + --hash=sha256:8ba782ca3bf1e81492611152b9a0d5264971339e95e34d69de0ac2c926be496d \ + --hash=sha256:8bcba9456242ebf180a04d9443812fd85ffe6bd12bda464dd116fcece8886ff3 \ + --hash=sha256:8c9fe122444e129881afd1d4d1c7ac0d3ce2d91b68c2b40173b6025ff1c31f9a \ + --hash=sha256:8ec4777d92fd61a5c8fdeddab894fd65bea301a8092fb5419ec6472aa4d458d7 \ + --hash=sha256:90cb2a1c9cc503a054a19612b48ff6e8e47805f618bdb3224a07568aad03a37e \ + --hash=sha256:9283d9dd6b44acad35118e2976fc763a065509e4118debdb61916ec322ed17b9 \ + --hash=sha256:94ac8a6b8c47951173f0b67bf862bcb971bf24e493b9fbbdb0e010cbbc7d9f54 \ + --hash=sha256:96d8de55029d42251945531f6aa7590c32b48163c66a43bf29d8657d7446a377 \ + --hash=sha256:96dedccfb09a73a25751053a183159b88f4ee75f388df8166040c152ac0531c6 \ + --hash=sha256:9761ff4a0ffa583fe850731ad24fe82c88cccb7a2294727db0955f3279a4cb3f \ + --hash=sha256:97b455de3e8b1b0b1e4594cb61a468992563f03ca264062fbb0a66b393c01d90 \ + --hash=sha256:97b94fb29abf21f5f0bde15f7dbdd3a4aa2dc59f37026adc7b4bee8563b84375 \ + --hash=sha256:99054b838b74d8d3995ea0d410976ae967c46207ae22d6ddfc535e809197dab9 \ + --hash=sha256:99166cc98637e8bf550cda2aab07f4f1d5f899c45fbd721801aeabcc9d404824 \ + --hash=sha256:9a51b061d54cda8b83e62c44458bfbf0dabbef9b975dd9649952ba5076b9f349 \ + --hash=sha256:9b1dddc257279417d93c9e59420d49ef90aece90d7a01996db3aade74b0281b1 \ + --hash=sha256:9c3c4b9aa9a27196b921197f7daf9e6c1412739df06a99cfa6e923879362eff6 \ + --hash=sha256:a14578102a6081465aec9cf73c76c3cd3f79f0709bdb3b8ae7ab0b54c9d8b089 \ + --hash=sha256:a16a3fa6936e36bb1414d16a6bd012c9033e5161b68b426805b61d895392437d \ + --hash=sha256:a33de7633c948ab2dc144af370a66e7e7af29b425dcd0f7e4f59689fb9391b53 \ + --hash=sha256:a43418e1a90b4809a9caf64aeb8b0696e3e1f300a323acc1e6ee2f93ae319fcf \ + --hash=sha256:a4553d36cc0b7fce1f35ba8a94dfd775aa3ed12f5eab2dc3b46ac75a0706b0bb \ + --hash=sha256:a5b21b42a01a343096a1c018d35e9b7aec9c7065dda53ae8da071e37478b2cea \ + --hash=sha256:a65785e653573fcd1e33062760ab4c3c3440e8e910765018e4b6ed4ad07b54a0 \ + --hash=sha256:a6671a8f6ea4f2101ce11fab5023a2e59391cff249fc3928cecb69d971525fd5 \ + --hash=sha256:a69e8946e4902ea11fc1c557740cdbfe7d75c78fcc5e4324ff89a696a634357d \ + --hash=sha256:a6e3653df1a70b8ac4191216324242e4be2bca18c9a7c10934e1bd56dc7ca15e \ + --hash=sha256:a865d2d470220e659220fdb59d5b6c4422802d8d6098e1324bc4d12444798914 \ + --hash=sha256:a949b072ea59c6eca0811ccd9e95133cc50d2afda8d464b5b077c78f78efa269 \ + --hash=sha256:aa6ccc7f31018484d652cf52db020003433f3c9fa83189c028bd807d2adde503 \ + --hash=sha256:ac0f291ab6485bd71f33941f9b92771318332a05d505460b41e893a549caadc0 \ + --hash=sha256:acb31ecdd1a97fab5cd39a84ee9f515e727d319f796fec48703b8339b9998360 \ + --hash=sha256:acf52474b2494ef66dc7e0fb6d5e2b50c18313039ad4d275fbf9f9907c804bc5 \ + --hash=sha256:ad889d58361a26ba75f5d6a1a0da08ed4950ec4ac8a6da86e1c5ce1b95ccb43f \ + --hash=sha256:adbd48b30e3f82c89fb2b3e6a87cdd28d113b190a5ed0ee2dee286323ee9a621 \ + --hash=sha256:af05a3f650220a6c59fa0ad2410249f2d2470a05225807c378fb67458693f8df \ + --hash=sha256:b3662719007e059abde7eddacf8517142ba076ddc7b30c807260e57d28c3c191 \ + --hash=sha256:b3bece52127ac20044311ee73567f9f0893b5de64f9028aecc90cc740cfd525a \ + --hash=sha256:b4c8842fb19d78b5e8c2a52baf4c8357658cc56c62bc822b86ce0f942f28e286 \ + --hash=sha256:b659fad79c99b0238c7ad7e9d7dbf4eebfea9097c2dba65fa0a4d18a25b29a2f \ + --hash=sha256:b6c1f9c59bbe593f88a0aad30be4150f15bd57bd64efb95feeabcb8e563f1ecd \ + --hash=sha256:bdd16718b63aa3ebd68aabb79021a40e47c81374852d41a306b9453141bbcbee \ + --hash=sha256:bf430c587f447a554c53768ad76b9846fe7c5632180ef6f69c4fce8b0552fbd0 \ + --hash=sha256:bfed61996d618eb90d6eaae0178002e3466a28b06bfc557a7a3a7266378d8c5a \ + --hash=sha256:c09ada495567c9c9a8156c5ebcfb93be7fece0755062d738c972dcbecd0d84b5 \ + --hash=sha256:c0e6ccc2b19ec8a726b2e26062ac71ea63e15500d6bf85910e42481844fdffc1 \ + --hash=sha256:c101180495cb4ba3617b279a944345c53a5e73b0c150053d1fa8d8af32de9579 \ + --hash=sha256:c10b9206753b64aa791b35b201485477525b26fdec5bf86e8364c388a03e2592 \ + --hash=sha256:c3074db513c81f764053e3da079312ecf85a50d8350c71f4cc0105d9662a9e6c \ + --hash=sha256:c30dd1af66a820820398b26e0d74e7a9aa43cae705924f23ed828cd8e5c26c3d \ + --hash=sha256:c57963970d359a72262f7fe6be88f945e2334d4bc41462b7f08c37b0abf35ca6 \ + --hash=sha256:c6301d92545c591ad31c3e050aa40a5f8a4c16413f1f9e6f9322c6f0f9d2b736 \ + --hash=sha256:c6370189e8e66b7e608f533b939a9de092ddca6cce084ca0d3d414d2ed5b5d59 \ + --hash=sha256:c6fc415b5568bd9accc7187f1729a99707330c0a67a8b9f93c1149ed573ed75d \ + --hash=sha256:c7484fea54964edd417cc3a104d5180562514aa7c4e2a2bc26d776ef0c4cb4a1 \ + --hash=sha256:cba763d84b06bda2c38d5185dee76f1b9dfdc0789e96e476d9e10005526d0788 \ + --hash=sha256:cd878d32f5c6cbce9783f8d6897561fb772211edba9dde49d85672b88ed45276 \ + --hash=sha256:ce6d5cc94a50291d080259a126cbf1e9ba4ac861e6429d2f3cdbb1474f51945d \ + --hash=sha256:d0d24a4f3fb63852cd09af46ae4b7a4d00cc8b8615a046dca543786e728d1056 \ + --hash=sha256:d1e0d1ea6e44f51808a9e8469c8afdebcdf6fa23d1ea524a0303d57d23919712 \ + --hash=sha256:d54b8ae068af532c8cdf56abb9e09a60fbe7b10792444c9c27987bb6d3b450fa \ + --hash=sha256:d55bf4ef10eb09b8b6866790e083d26d087d84caa3cc0946ba87c3ca7ecaf7b7 \ + --hash=sha256:d9f3848ffaf010bdbabdbf4c25641fa258b6227ff27bc74a4d06edef521a4873 \ + --hash=sha256:da0264844a09b538c894e5eff25313d941deb4dedec2131b98418a71a3c9944e \ + --hash=sha256:da544672efd9ad76077928a3e6c5d894e52ce82d3bf14002db4a1bf17d1a36a2 \ + --hash=sha256:daade8936c4deaaf7b01561324ce438ba4f885d717e9adc62b4d67212ad7d7bd \ + --hash=sha256:dd649663ddeafbfd4734eb8abae921dd5baa1242f20bda54e8bc927369ccded4 \ + --hash=sha256:deca2a30d983d240b8375ec2ee0a4288e72042827fc61df2f7671f8467e4cb2f \ + --hash=sha256:e259bb7e1e2d8de6b35f430f5c7220b1c0ebf3962d1ba7ec7545980d5931edb8 \ + --hash=sha256:e3996ff9b6f99180357024336bf5749a8ad6476a9a2523e535c5212b995b12a2 \ + --hash=sha256:e3eba72f9bb84fe696516f4cbca68d3d74a376157e68bacddbb7f2516af61523 \ + --hash=sha256:e4296fcc790876a8b0f297edc83d3b088457b774d8f67b4636807f8a2ec69a79 \ + --hash=sha256:e53926e76131a74e79cc0b39fa712c227875f180afc68646bd1e1d8a17e60313 \ + --hash=sha256:e681a6fc7e4f715252b9b5acfb30536ec7dd1f75033a32dc617e6fa95af1a3fd \ + --hash=sha256:e71b34978e77868cbf2d18c5206a4603f9c644dd7181bec5643bd40141d3b8c5 \ + --hash=sha256:e8cda075b10bb3917b002c74a04f9e02b7d13b5bf732571404d51c52b11c7329 \ + --hash=sha256:e90b4bcf1d9eb1010fdaee7c9209fb667e74c0684f3ba17f9032bd7319da90c9 \ + --hash=sha256:e961093277ff9d42addb9dad5614dfb7800ccba07c245c39c8e9b4daa35d160c \ + --hash=sha256:e9701c073bd062fb6bf6be51b47186ad15f1e87feedf4ea07198e0333ec068dc \ + --hash=sha256:e998cb3685b92101ec5de0fb4d9485cf01e50bc418211955c55d98064664cf4c \ + --hash=sha256:ea5ecf800b45bdb34afe05a1d0dae1f8ea02a290e50636dccd399063f6b180f8 \ + --hash=sha256:ec1a470c6db94ac4589c203921e89ac1bc13e796a8b1784d8135e1893559cd3b \ + --hash=sha256:edccc2ec58435a580f96a48a3ccae8cd0a480824119165dd90108718ad81ae6e \ + --hash=sha256:f00330ac7e24769e2032203f2b01794d670916b0c1799fd261340f1af9499875 \ + --hash=sha256:f09ee747e2a5f876cc5ad56947734811828335e13b403dd8ea1e06d77a9dd48d \ + --hash=sha256:f18732adcc271741bd651c3e56fa519d8a237d2cccda01fe3afb226bf87f783b \ + --hash=sha256:f1b603d0686c99fa0879f104a74e7db58367634c6e50ba827bee9aa095e23205 \ + --hash=sha256:f33cf0baa91eccd2cb7b62bf00f10c2264ef578b71dd33a12962e71a36eb4d32 \ + --hash=sha256:f3e1a44af01b6692de0ec6caba5f0bf93ceb36896e02b7fc00952c6ea7ef39e1 \ + --hash=sha256:f484ed57bb3e4142f9d6439568658c38be5f94b702ba00a1ff32c69783b6c66d \ + --hash=sha256:f5d031f35962e5483a613214e61f09fe24ab523062c3646d592dc16c4a217451 \ + --hash=sha256:f6247f5e23ee94f2557ac9dab738a336f607c6ff476fcf66ca70c3aef5eee15a \ + --hash=sha256:f7db035447a0ac8959aa230c5d36545ecf9f547413eb1711c0ca6f0ba1418925 \ + --hash=sha256:f83295394d34e1287e5b30fcc496c13b92cf886a131f3dae5444e38da8757efb \ + --hash=sha256:fac4832b638000106207bc44e44b9616a6a416aaee56c62b01d61f3705e49f58 \ + --hash=sha256:fb59a0dd61fb2ad481c03fda399d78ce57dab6bb62c2c8fdb446a7ba4754b89a \ + --hash=sha256:fc737c05ca2d48e5dcdbbb249314df3fc6c2a0be6da8b0aa28e13d72afaad7cd \ + --hash=sha256:ff48915bf1871a1f19f74c11834c6329443d306cedc0c05fe7fe617810422a80 \ + --hash=sha256:ffa44b4c7c5d0ffa31356b4428659516c0e47647825c74079a296b3857b6d99d + # via datasets +yarl==1.24.5 \ + --hash=sha256:0055afc45e864b92729ac7600e2d102c17bef060647e74bca75fa84d66b9ff36 \ + --hash=sha256:0465ec8cedc2349b97a6b595ace64084a50c6e839eca40aa0626f38b8350e331 \ + --hash=sha256:0ebfaffe1a16cb72141c8e09f18cc76856dbe58639f393a4f2b26e474b96b871 \ + --hash=sha256:16a2f5010280020e90f5330257e6944bc33e73593b136cc5a241e6c1dc292498 \ + --hash=sha256:17f57620f5475b3c69109376cc87e42a7af5db13c9398e4292772a706ff10780 \ + --hash=sha256:2120b96872df4a117cde97d270bac96aea7cc52205d305cf4611df694a487027 \ + --hash=sha256:240cbec09667c1fed4c6cd0060b9ec57332427d7441289a2ed8875dc9fb2b224 \ + --hash=sha256:24e861e9630e0daddcb9191fb187f60f034e17a4426f8101279f0c475cd74144 \ + --hash=sha256:2729fcfc4f6a596fb0c50f32090400aa9367774ac296a00387e65098c0befa76 \ + --hash=sha256:2c1fe720934a16ea8e7146175cba2126f87f54912c8c5435e7f7c7a51ef808d3 \ + --hash=sha256:2cabe6546e41dabe439999a23fcb5246e0c3b595b4315b96ef755252be90caeb \ + --hash=sha256:2dbe06fc16bc91502bca713704022182e5729861ae00277c3a23354b40929740 \ + --hash=sha256:3363fcc96e665878946ad7a106b9a13eac0541766a690ef287c0232ac768b6ec \ + --hash=sha256:377fe3732edbaf78ee74efdf2c9f49f6e99f20e7f9d2649fda3eb4badd77d76e \ + --hash=sha256:3ac6aff147deb9c09461b2d4bbdf6256831198f5d8a23f5d37138213090b6d8a \ + --hash=sha256:3f45789ce415a7ec0820dc4f82925f9b5f7732070be1dec1f5f23ec381435a24 \ + --hash=sha256:4103b77b8a8225e413107d2349b65eb3c1c52627b5cc5c3c4c1c6a798b218950 \ + --hash=sha256:4377407001ca3c057773f44d8ddd6358fa5f691407c1ba92210bd3cf8d9e4c95 \ + --hash=sha256:46c2f213e23a04b93a392942d782eb9e413e6ef6bf7c8c53884e599a5c174dcb \ + --hash=sha256:47e98aab9d8d82ff682e7b0b5dded33bf138a32b817fcf7fa3b27b2d7c412928 \ + --hash=sha256:4a36f9becdd4c5c52a20c3e9484128b070b1dcfc8944c006f3a528295a359a9c \ + --hash=sha256:4af7b7e1be0a69bee8210735fe6dcfc38879adfac6d62e789d53ba432d1ffa41 \ + --hash=sha256:4d97a951a81039050e45f04e96689b58b8243fa5e62aa14fe67cb6075300885e \ + --hash=sha256:4db9aecb141cb7a5447171b57aa1ed3a8fee06af40b992ffc31206c0b0121550 \ + --hash=sha256:53e549287ef628fecba270045c9701b0c564563a9b0577d24a4ec75b8ab8040f \ + --hash=sha256:56b149b22de33b23b0c6077ab9518c6dcb538ad462e1830e68d06591ccf6e38b \ + --hash=sha256:570fec8fbd22b032733625f03f10b7ff023bc399213db15e72a7acaef28c2f4e \ + --hash=sha256:5b8ee53be440a0cffc991a27be3057e0530122548dbe7c0892df08822fce5ede \ + --hash=sha256:5ba4f78df2bcc19f764a4b26a8a4f5049c110090ad5825993aacb052bf8003ad \ + --hash=sha256:5c55256dee8f4b27bfbf636c8363383c7c8db7890c7cba5217d7bd5f5f21dab6 \ + --hash=sha256:5c88e5815a49d289e599f3513aa7fde0bc2092ff188f99c940f007f90f53d104 \ + --hash=sha256:5fede79c6f73ff2c3ef822864cb1ada23196e62756df53bc6231d351a49516a2 \ + --hash=sha256:65be18ec59496c13908f02a2472751d9ef840b4f3fb5726f129306bf6a2a7bba \ + --hash=sha256:66410eb6345d467151934b49bfa70fb32f5b35a6140baa40ad97d6436abea2e9 \ + --hash=sha256:665b0a2c463cc9423dd647e0bfd9f4ccc9b50f768c55304d5e9f80b177c1de12 \ + --hash=sha256:6b8536851f9f65e7f00c7a1d49ba7f2be0ffe2c11555367fc9f50d9f842410a1 \ + --hash=sha256:6c95b17fe34ed802f17e205112e6e10db92275c34fee290aa9bdc55a9c724027 \ + --hash=sha256:6e73e7fe93f17a7b191f52ec9da9dd8c06a8fe735a1ecbd13b97d1c723bff385 \ + --hash=sha256:6efbccc3d7f75d5b03105172a8dc86d82ba4da86817952529dd93185f4a88be2 \ + --hash=sha256:709f1efed56c4a145793c046cd4939f9959bcd818979a787b77d8e09c57a0840 \ + --hash=sha256:79af890482fc94648e8cde4c68620378f7fef60932710fa17a66abc039244da2 \ + --hash=sha256:7bcbe0fcf850eae67b6b01749815a4f7161c560a844c769ad7b48fcd99f791c4 \ + --hash=sha256:7c0494a31a1ac5461a226e7947a9c9b78c44e1dc7185164fa7e9651557a5d9bc \ + --hash=sha256:7ce27823052e2013b597e0c738b13e7e36b8ccb9400df8959417b052ab0fd92c \ + --hash=sha256:7f72c74aa99359e27a2ee8d6613fefa28b5f76a983c083074dfc2aaa4ab46213 \ + --hash=sha256:7fa5e51397466ea7e98de493fa2ff1b8193cfef8a7b0f9b4842f92d342df0dba \ + --hash=sha256:82632daed195dcc8ea664e8556dc9bdbd671960fb3776bd92806ce05792c2448 \ + --hash=sha256:82f75e05912e84b7a0fe57075d9c59de3cb352b928330f2eb69b2e1f54c3e1f0 \ + --hash=sha256:841f0852f48fefea3b12c9dfec00704dfa3aef5215d0e3ce564bb3d7cd8d57c6 \ + --hash=sha256:874019bd513008b009f58657134e5d0c5e030b3559bd0553976837adf52fe966 \ + --hash=sha256:88f50c94e21a0a7f14042c015b0eba1881af78562e7bf007e0033e624da59750 \ + --hash=sha256:89a1bbb58e0e3f7a283653d854b1e95d65e5cfd4af224dac5f02629ec1a3e621 \ + --hash=sha256:8a6987eaad834cb32dd57d9d582225f0054a5d1af706ccfbbdba735af4927e13 \ + --hash=sha256:8ac73abdc7ab75610f95a8fd994c6457e87752b02a63987e188f937a1fc180f0 \ + --hash=sha256:8ccf9aca873b767977c73df497a85dbedee4ee086ae9ae49dc461333b9b79f58 \ + --hash=sha256:90333fd89b43c0d08ac85f3f1447593fc2c66de18c3d6378d7125ea118dc7a54 \ + --hash=sha256:92ab3e11448f2ff7bf53c5a26eff0edc086898ec8b21fb154b85839ce1d88075 \ + --hash=sha256:9335a099ad87287c37fe5d1a982ff392fa5efe5d14b40a730b1ec1d6a41382b4 \ + --hash=sha256:96d30286dd02679e32a39aa8f0b7498fc847fcda46cfc09df5513e82ce252440 \ + --hash=sha256:9baafc71b04f8f4bb0703b21d6fc9f0c30b346c636a532ff16ec8491a5ea4b1f \ + --hash=sha256:9d1216a7f6f77836617dba35687c5b78a4170afc3c3f18fc788f785ba26565c4 \ + --hash=sha256:9d399bdcfb4a0f659b9b3788bbc89babe63d9a6a65aacdf4d4e7065ff2e6316c \ + --hash=sha256:9e4e16c73d717c5cf27626c524d0a2e261ad20e46932b2670f64ad5dde23e26f \ + --hash=sha256:9f4d8cf085a4c6a40fb97ea0f46938a8df43c85d31f9d45e2a8867ea9293790d \ + --hash=sha256:a33700d13d9b7d84fd10947b09ff69fb9a792e519c8cb9764a3ca70baa6c23a7 \ + --hash=sha256:a3732e66413163e72508da9eff9ce9d2846fde51fae45d3605393d3e6cd303e9 \ + --hash=sha256:a4582acf7ef76482f6f511ebaf1946dae7f2e85ec4728b81a678c01df63bd723 \ + --hash=sha256:a61834fb15d81322d872eaafd333838ae7c9cea84067f232656f75965933d047 \ + --hash=sha256:a7cff474ab7cd149765bb784cf6d78b32e18e20473fb7bda860bce98ab58e9da \ + --hash=sha256:a8fe66b8f300da93798025a785a5b90b42f3810dc2b72283ff84a41aaaebc293 \ + --hash=sha256:a929d878fec099030c292803b31e5d5540a7b6a31e6a3cc76cb4685fc2a2f51b \ + --hash=sha256:ad5d8201d310b031e6cd839d9bac2d4e5a01533ce5d3d5b50b7de1ef3af1de61 \ + --hash=sha256:af3aefa655adb5869491fa907e652290386800ae99cc50095cba71e2c6aefdca \ + --hash=sha256:c0ebc836c47a6477e182169c6a476fc691d12b518894bf7dd2572f0d59f1c7ed \ + --hash=sha256:c687ed078e145f5fd53a14854beff320e1d2ab76df03e2009c98f39a0f68f39a \ + --hash=sha256:cbb833ccacdb5519eff9b8b71ee618cc2801c878e77e288775d77c3a2ced858a \ + --hash=sha256:cf139c02f5f23ef6532040a30ff662c00a318c952334f211046b8e60b7f17688 \ + --hash=sha256:d46b86567dd4e248c6c159fcbcdcce01e0a5c8a7cd2334a0fff759d0fa075b16 \ + --hash=sha256:d693396e5aea78db03decd60aec9ece16c9b40ba00a587f089615ff4e718a81d \ + --hash=sha256:d897129df1a22b12aeed2c2c98df0785a2e8e6e0bde87b389491d0025c187077 \ + --hash=sha256:daba5e594f06114e37db186efd2dd916609071e59daca901a0a2e71f02b142ce \ + --hash=sha256:dd625535328fd9882374356269227670189adfcc6a2d90284f323c05862eecbd \ + --hash=sha256:e006d3a974c4ee19512e5f058abedb6eef36a5e553c14812bdeba1758d812e6d \ + --hash=sha256:e1ae548a9d901adca07899a4147a7c826bbcc06239d3ce9a59f57886a28a4c88 \ + --hash=sha256:e2935f8c39e3b03e83519292d78f075189978f3f4adc15a78144c7c8e2a1cba5 \ + --hash=sha256:e42d75862735da90e7fc5a7b23db0c976f737113a54b3c9777a9b665e9cbff75 \ + --hash=sha256:e7d42c531243450ef0d4d9c172e7ed6ef052640f195629065041b5add4e058d1 \ + --hash=sha256:e81b83143bee16329c23db3c1b2d82b29892fcbcb849186d2f6e98a5abe9a57f \ + --hash=sha256:e8ffa78582120024f476a611d7befc123cee59e47e8309d470cf667d806e613b \ + --hash=sha256:ebb0ec7f17803063d5aeb982f3b1bd2b2f4e4fae6751226cbd6ba1fcfe9e63ff \ + --hash=sha256:f08c7513ecef5aad65687bfdf6bc601ae9fccd04a42904501f8f7141abad9eb9 \ + --hash=sha256:f0a658a6d3fafee5c6f63c58f3e785c8c43c93fbc02bf9f2b6663f8185e0971f \ + --hash=sha256:f0e466ed7511fe9d459a819edbc6c2585c0b6eabde9fa8a8947552468a7a6ef0 \ + --hash=sha256:f141474e85b7e54998ec5180530a7cda99ab29e282fa50e0756d89981a9b43c5 \ + --hash=sha256:f4239bbec5a3577ddb49e4b50aeb32d8e5792098262ae2f63723f916a29b1a25 \ + --hash=sha256:f540c013589084679a6c7fac07096b10159737918174f5dfc5e11bf5bca4dfe6 \ + --hash=sha256:f9f3e9c8a9ecffa57bef8fb4fa19e5fa4d2d8307cf6bac5b1fca5e5860f4ba00 \ + --hash=sha256:fa139875ff98ab97da323cfadfaff08900d1ad42f1b5087b0b812a55c5a06373 \ + --hash=sha256:fcd3b77e2f17bbe4ca56ec7bcb07992647d19d0b9c05d84886dcd6f9eb810afd \ + --hash=sha256:fd8c81f346b58f45818d09ea11db69a8d5fd34a224b79871f6d44f12cd7977b1 \ + --hash=sha256:fe7b7bb170daccbba19ad33012d2b15f1e7942296fd4d45fc1b79013da8cc0f2 \ + --hash=sha256:ff330d3c30db4eb6b01d79e29d2d0b407a7ecad39cfd9ec993ece57396a2ec0d \ + --hash=sha256:ff405d91509d88e8d44129cd87b18d70acd1f0c1aeabd7bc3c46792b1fe2acba \ + --hash=sha256:ffcd54362564dc1a30fb74d8b8a6e5a6b11ebd5e27266adc3b7427a21a6c9104 + # via + # aiohttp + # postgrest + # storage3 + # supabase + # supabase-functions +z3-solver==4.15.4.0 \ + --hash=sha256:00bd10c5a6a5f6112d3a9a810d0799227e52f76caa860dafa5e00966bb47eb13 \ + --hash=sha256:00e35b02632ed085ea8199fb230f6015e6fc40554a6680c097bd5f060e827431 \ + --hash=sha256:407e825cc9211f95ef46bdc8d151bf630e7ab2d62a21d24cd74c09cc5b73f3aa \ + --hash=sha256:62c7e9cbdd711932301f29919ad9158de9b2f58b4d281dd259bbcd0a2f408ba1 \ + --hash=sha256:7e103a6f203f505b8b8b8e5c931cc407c95b61556512d4921c1ddc0b3f41b08e \ + --hash=sha256:928c29b58c4eb62106da51c1914f6a4a55d0441f8f48a81b9da07950434a8946 \ + --hash=sha256:be3bc916545c96ffbf89e00d07104ff14f78336e55db069177a1bfbcc01b269d + # via tilelang +zipp==4.1.0 \ + --hash=sha256:25ad4e16390cd314347dd8f1de67a2ac538ae658ed4ab9db16029c07c188e97f \ + --hash=sha256:4cb57381f544315db7688e976e922a2b18cdb513d21cc194eb42232ba2a3e602 + # via importlib-metadata diff --git a/04-data-agent/hf/monitor_three_runs.py b/04-data-agent/hf/monitor_three_runs.py new file mode 100644 index 0000000..2b3e7d0 --- /dev/null +++ b/04-data-agent/hf/monitor_three_runs.py @@ -0,0 +1,425 @@ +"""Persistent CPU observer for the three data-agent training runs. + +Samples artifacts every 600 seconds. No inference requests or model transfers. +Known transient controller failures can restart twice using their frozen script; +model, token, provenance and numerical failures require investigation. +""" +import argparse +from datetime import datetime, timezone +import fcntl +import json +import math +import os +from pathlib import Path +from statistics import mean +import subprocess +import sys +import time + +TERMINAL = {'COMPLETED', 'FAILED', 'TIMEOUT', 'NODE_FAIL', 'BOOT_FAIL', 'OUT_OF_MEMORY', + 'CANCELLED', 'CANCELED', 'PREEMPTED', 'ERROR', 'DELETED'} +WORKSPACE = Path(os.environ.get('TRAINING_WORKSPACE', Path(__file__).resolve().parents[3])) +LOGS = WORKSPACE / 'experiments/async_grpo_harbor_data_agent/logs' +COMPARISON = WORKSPACE / 'experiments/daytona_harness_comparison/logs/hf-20260915' + + +def read(path, default=None): + return json.loads(Path(path).read_text()) if Path(path).exists() else ({} if default is None else default) + + +def write(path, data): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + temporary = path.with_suffix('.tmp') + temporary.write_text(json.dumps(data, indent=2) + '\n') + temporary.replace(path) + + +def records(path): + if not Path(path).exists(): + return [] + return [json.loads(line) for line in Path(path).read_text().splitlines(keepends=True) + if line.endswith('\n') and line.strip()] + + +def states(jobs): + jobs = {str(j) for j in jobs if str(j).isdigit()} + if not jobs: + return {} + text = subprocess.check_output(['sacct', '-X', '-n', '-P', '-j', ','.join(sorted(jobs)), + '--format=JobIDRaw,State'], text=True, timeout=20) + return {p[0]: p[1].split()[0].rstrip('+') for line in text.splitlines() + if len(p := line.split('|')) > 1 and p[0] in jobs} + + +def training_metrics(rows): + # A completed run also emits a summary with the same step. Only optimizer + # receipts belong in the reward curve; preserve its first receipt per step. + updates = {} + for row in rows: + if 'grad_norm' in row: + updates.setdefault(int(row['step']), row) + rows = [updates[k] for k in sorted(updates)] + recent, previous = rows[-20:], rows[-40:-20] + rewards = lambda values: mean(float(r['reward']) for r in values) if values else None + speeds = [float(r.get('perf/step_s', r.get('step_time', 0))) for r in recent] + return {'step': rows[-1]['step'] if rows else 0, 'updates': len(rows), + 'reward_last20': rewards(recent), 'reward_previous20': rewards(previous), + 'reward_window_size': len(recent), 'previous_window_size': len(previous), + 'nonzero_gradients_last20': sum(float(r['grad_norm']) > 0 for r in recent), + 'nonfinite_updates': sum(any(not math.isfinite(float(r[k])) for k in + ['loss', 'grad_norm', 'reward', 'kl', 'ratio', 'entropy'] if k in r) for r in rows), + 'max_staleness': max((float(r.get('sample/staleness_max', 0)) for r in rows), default=0), + 'mean_step_seconds_last20': mean(speeds) if speeds and all(speeds) else None} + + +def summarize_artifacts(output, arm): + output = Path(output) + metric_file = output / ('run/metrics.jsonl' if arm == 'whitebox' else 'audit/metrics.jsonl') + summary = training_metrics(records(metric_file)) + summary['metric_fingerprint'] = str(summary['step']) + status = read(output / 'status.json') + summary.update(runtime_status=status, trackio=read(output / 'trackio_verified.json'), + uploads=read(output / 'upload_status.json'), + checkpoints=sorted(int(p.name.split('-')[-1]) for p in (output / 'run').glob('checkpoint-*') + if p.is_dir() and p.name.split('-')[-1].isdigit() and + any((p / f).exists() for f in ('checkpoint.saved.json', 'checkpoint.ready.json', 'checkpoint.hf.ready.json')))) + if arm == 'whitebox': + audits = [r for event in records(output / 'run/token_audit.jsonl') for r in event.get('rows', [])] + summary['tito'] = {'rows': len(audits), 'passed': sum(bool(r.get('tito_pass')) for r in audits), + 'supervised_tokens': sum(r.get('supervised', 0) for r in audits)} + else: + summary['tito'] = read(output / 'audit/tito_summary.json') + return summary + + +def original(): + root = LOGS / 'multi4-long-prod-20260915' + continuation = LOGS / 'multi4-long-prod-cont-20260915' + if (continuation / 'submission.json').exists(): + root = continuation + submission = read(root / 'submission.json') + job = str(submission['training']) + summary = summarize_artifacts(root / ('job-' + job), 'multi4') + # A fresh allocation has no new metrics yet. Preserve the actual inherited + # optimizer history instead of displaying step zero during restore/startup. + history, inherited_saves = original_history(root) + summary.update(training_metrics(history)) + resume = read(root / 'run_config.json').get('resume_state') or {} + summary['step'] = max(summary['step'], resume.get('step', 0)) + summary['resumed_from_step'] = resume.get('step', 0) + summary['resume_schedule_cursor'] = resume.get('group_offset') + summary['checkpoints'] = inherited_saves + monitor = read(root / 'monitor/status.json') + summary.update(arm='multi4', provider='slurm', job=job, source=str(root), + stage=states([job]).get(job, 'UNKNOWN'), controller_alerts=monitor.get('alerts', []), + support_jobs={k: submission[k] for k in ('eval_watcher', 'monitor', 'logging') if k in submission}, + trackio=read(root / 'trackio/status.json'), + allocation_continuation=read((LOGS / 'multi4-long-prod-20260915') / 'operations/allocation-continuation/status.json')) + summary['support_states'] = states(summary['support_jobs'].values()) + summary['evaluations'] = original_evaluations(root) + return summary + + +def original_evaluations(root): + """Keep parent checkpoint evaluations visible after an allocation handoff.""" + selected, seen, upper = {}, set(), float('inf') + while root not in seen: + seen.add(root) + for directory in (root / 'checkpoint-evals').glob('step-*'): + step = int(directory.name.split('-')[-1]) + if step > upper: + continue + if step in selected and selected[step] != directory: + raise ValueError('Conflicting checkpoint evaluation ancestry') + selected[step] = directory + resume = read(root / 'run_config.json').get('resume_state') or {} + if not resume.get('checkpoint'): + break + upper, root = resume['step'], Path(resume['checkpoint']).parents[2] + evaluations = [] + for step, directory in sorted(selected.items()): + score = read(directory / 'scores.json') + job = read(directory / 'submission.json').get('job_id') + evaluations.append({'job': job, 'checkpoint': 'checkpoint-' + str(step), + 'source': str(directory), 'complete': score.get('comparison_ready', False), + 'graded_cells': score.get('graded_cells'), 'pass_at_1': score.get('average_pass_at_1')}) + observed = states(e['job'] for e in evaluations) + for evaluation in evaluations: + evaluation['stage'] = observed.get(str(evaluation['job']), 'UNKNOWN') + return evaluations + + +def original_history(root): + rows, saved, seen = {}, set(), set() + upper = float('inf') + while root not in seen: + seen.add(root) + job = str(read(root / 'submission.json')['training']) + resume = read(root / 'run_config.json').get('resume_state') or {} + lower = resume.get('step', 0) + for row in records(root / ('job-' + job) / 'audit/metrics.jsonl'): + if 'grad_norm' in row and lower < row['step'] <= upper: + if row['step'] in rows: + raise ValueError('Conflicting optimizer ancestry') + rows[row['step']] = row + for p in (root / ('job-' + job) / 'run').glob('checkpoint-*'): + if p.name.split('-')[-1].isdigit() and int(p.name.split('-')[-1]) <= upper and (p / 'checkpoint.saved.json').exists(): + saved.add(int(p.name.split('-')[-1])) + if not resume.get('checkpoint'): + break + upper, root = lower, Path(resume['checkpoint']).parents[2] + return [rows[k] for k in sorted(rows)], sorted(saved) + + +def fetch_whitebox(args): + from huggingface_hub import HfApi + from dotenv import dotenv_values + api = HfApi(token=dotenv_values(args.env_file)['HF_API_KEY']) + job = api.inspect_job(job_id=args.job, namespace='HuggingEnvs') + prefix = job.environment['RUN_ID'] + '/jobs/' + job.environment['RUN_OWNER'] + destination = args.out + destination.mkdir(parents=True, exist_ok=True) + names = ['status.json', 'run/metrics.jsonl', 'run/token_audit.jsonl', + 'trackio_verified.json', 'upload_status.json'] + api.download_bucket_files(job.environment['ARTIFACT_BUCKET'], + [(prefix + '/' + n, destination / n) for n in names]) + summary = summarize_artifacts(destination, 'whitebox') + summary.update(arm='whitebox', provider='hf', job=job.id, stage=job.status.stage, + owner=job.environment['RUN_OWNER'], artifact_prefix=prefix, fetched_at=time.time()) + write(destination / 'snapshot.json', summary) + + +def comparison(arm, out, env_file): + launch = COMPARISON / 'long-launches' / arm + state = read(launch / 'state.json') + if not state.get('training_job'): + receipt = read(launch / 'submission.json') + job = receipt.get('launcher_job') + return {'arm': arm, 'stage': 'AWAITING_QUALIFICATION', 'step': 0, 'job': None, + 'launcher_job': job, 'launcher_state': states([job]).get(str(job), 'UNKNOWN'), + 'qualification_dependency': receipt.get('afterok'), 'launch_state': state} + job = str(state['training_job']) + if arm == 'opencode': + summary = summarize_artifacts(state['training_output'], arm) + summary.update(arm=arm, provider='slurm', job=job, stage=states([job]).get(job, 'UNKNOWN')) + plan = read(state['controller_plan']) + audit_out = out / ('native-tito-' + job) + if summary['step']: + with (out / 'native-tito.log').open('a') as stream: + result = subprocess.run([str(Path(plan['root']) / '.venv312/bin/python'), + str(Path(__file__).with_name('audit_native_progress.py')), '--root', plan['root'], + '--training', state['training_output'], '--out', str(audit_out)], + stdout=stream, stderr=subprocess.STDOUT, timeout=240) + if result.returncode: + summary['audit_error'] = 'native_tito_audit_failed' + else: + audit = read(audit_out / 'summary.json') + summary['tito'] = {'opencode': audit['opencode']} + summary['tito_checked_at'] = audit['checked_at'] + else: + cache = out / 'whitebox-cache' + result = subprocess.run([sys.executable, str(Path(__file__)), 'fetch-whitebox', '--job', job, + '--env-file', str(env_file), '--out', str(cache)], stdout=subprocess.DEVNULL, + stderr=subprocess.PIPE, text=True, timeout=90) + if result.returncode: + raise RuntimeError('HF artifact observation failed; training state is unknown') + summary = read(cache / 'snapshot.json') + controller_plan = Path(state['controller_plan']) + controller = controller_plan.parent + summary['controller_job'] = state['controller_job'] + summary['controller_state'] = states([state['controller_job']]).get(str(state['controller_job']), 'UNKNOWN') + summary['controller_plan'] = str(controller_plan) + if arm == 'whitebox': + status = read(controller / 'output/decisions/monitor.json') + summary['evaluations'] = read(controller / 'output/decisions/state.json') + summary['controller_alerts'] = status.get('alerts', []) + summary['pending_evaluations'] = status.get('pending_steps', []) + summary['published_checkpoints'] = sorted(int(p.parent.name.split('-')[-1]) + for p in (controller / 'output/manifests').glob('checkpoint-*/checkpoint.hf.ready.json')) + else: + summary['evaluations'] = read(controller / 'checkpoint-evals/state.json') + summary['controller_alerts'] = read(controller / 'checkpoint-evals/monitor.json').get('alerts', []) + return summary + + +def diagnose(current, prior, now): + alerts = [] + if current.get('audit_error'): + alerts.append(current['audit_error']) + same = current.get('job') == prior.get('job') and current.get('step') == prior.get('step') + last = prior.get('last_optimizer_progress_at', now) if same else now + current['last_optimizer_progress_at'] = last + age = now - last + if current.get('nonfinite_updates', 0): + alerts.append('nonfinite_optimizer_metrics') + if current.get('max_staleness', 0) > 4: + alerts.append('staleness_exceeds_four') + if current.get('stage') == 'RUNNING' and age > max(1800, 3 * (current.get('mean_step_seconds_last20') or 0)): + alerts.append('no_optimizer_progress_over_30_minutes') + if current.get('stage') in TERMINAL - {'COMPLETED'}: + alerts.append('trainer_ended_' + current['stage']) + if current.get('launcher_state') in TERMINAL - {'COMPLETED'}: + alerts.append('launch_dependency_or_launcher_failed') + if current.get('controller_state') in TERMINAL - {'COMPLETED'}: + alerts.append('checkpoint_controller_ended_' + current['controller_state']) + if current.get('step', 0) >= 60: + saved = current.get('published_checkpoints', current.get('checkpoints', [])) + if max(saved, default=0) < ((current['step'] - 10) // 50) * 50: + alerts.append('checkpoint_save_or_publication_lag') + if current.get('trackio', {}).get('passed') is False: + alerts.append('trackio_verification_failed') + logging = current.get('trackio', {}) + if logging.get('local_ok') is False: + alerts.append('trackio_local_logging_failed') + if logging.get('online') and logging.get('sync', {}).get('ok') is False: + alerts.append('trackio_online_sync_failed') + for role, job in current.get('support_jobs', {}).items(): + state = current.get('support_states', {}).get(str(job), 'UNKNOWN') + if state in TERMINAL - {'COMPLETED'}: + alerts.append(role + '_support_ended_' + state) + evaluations = current.get('evaluations', []) + if isinstance(evaluations, list): + for evaluation in evaluations: + if evaluation.get('stage') in TERMINAL - {'COMPLETED'} and not evaluation.get('complete'): + alerts.append('evaluation_failed_' + str(evaluation.get('checkpoint'))) + alerts.extend('controller:' + str(a) for a in current.get('controller_alerts', [])) + tito = current.get('tito', {}) + if 'rows' in tito: + if tito['passed'] != tito['rows']: + alerts.append('tito_failed') + elif any(v.get('tito_pass', 0) != v.get('completed_results', 0) or + v.get('eligible_tokens', 0) != v.get('retained_tokens', 0) for v in tito.values() if isinstance(v, dict)): + alerts.append('tito_failed') + if current.get('reward_window_size') == current.get('previous_window_size') == 20: + if current['reward_last20'] < current['reward_previous20'] - .15: + alerts.append('reward_window_decline_investigate_task_mix') + return alerts + + +def repair_controller(summary, out): + """Only retry a dead CPU observer after an identifiable transient failure.""" + if summary.get('arm') not in {'opencode', 'whitebox'} or summary.get('controller_state') not in TERMINAL - {'COMPLETED'}: + return None + if summary.get('controller_alerts'): + return None # failed eval/provenance needs a specific recovery, not a restart loop + job = str(summary['controller_job']) + directory = Path(summary['controller_plan']).parent + error = directory / ('controller-' + job + '.err') + message = error.read_text()[-12000:] if error.exists() else '' + transient = ('ReadTimeout', 'ConnectTimeout', 'ConnectError', 'RemoteProtocolError', 'Temporary failure in name resolution') + if summary['controller_state'] not in {'NODE_FAIL', 'BOOT_FAIL', 'PREEMPTED'} and not any(t in message for t in transient): + return None + path = out / ('controller-repairs-' + summary['arm'] + '.json') + repairs = read(path, []) + if len(repairs) >= 2 or any(r['previous_job'] == job for r in repairs): + return None + # Persist an intent before sbatch. Unknown responses require reconciliation. + intent = {'previous_job': job, 'state': 'submitting', 'at': time.time()} + repairs.append(intent) + write(path, repairs) + new = subprocess.check_output(['sbatch', '--parsable', '--partition=hopper-cpu', '--cpus-per-task=2', + '--mem=8G', '--time=36:00:00', '--job-name=cmp-controller-recovery', + '--output=' + str(directory / 'controller-%j.out'), '--error=' + str(directory / 'controller-%j.err'), + str(directory / 'controller.slurm')], text=True, timeout=30).strip().split(';')[0] + if not new.isdigit(): + raise RuntimeError('Ambiguous controller restart') + intent.update(state='submitted', new_job=new) + write(path, repairs) + launch_state = COMPARISON / 'long-launches' / summary['arm'] / 'state.json' + write(launch_state, {**read(launch_state), 'controller_job': new}) + return intent + + +def markdown(snapshot): + lines = ['# Three-run training progress', '', 'Checked: ' + snapshot['checked_utc'], '', + 'Automatic checks run every 10 minutes on CPU. Reward windows describe optimizer updates; ' + 'fixed-test pass@1 measures checkpoint quality. Startup and changing task difficulty can change training reward.', '', + '| Run | Job | State | Step | Reward last 20 | Previous 20 | Alerts |', + '| --- | --- | --- | ---: | ---: | ---: | --- |'] + for arm, r in snapshot['runs'].items(): + def value(k): + v = r.get(k) + return f'{v:.3f}' if isinstance(v, (int, float)) else 'pending' + lines.append(f"| {arm} | {r.get('job') or r.get('launcher_job', 'pending')} | {r.get('stage', 'UNKNOWN')} | " + f"{r.get('step', 0)} | {value('reward_last20')} | {value('reward_previous20')} | " + + ('; '.join(r.get('alerts', [])) or 'none') + ' |') + evaluations = snapshot['runs'].get('multi4', {}).get('evaluations', []) + if evaluations: + lines += ['', '| Multi-harness checkpoint | Eval job | State | Audited pass@1 |', + '| --- | --- | --- | ---: |'] + for evaluation in evaluations: + score = evaluation.get('pass_at_1') + value = f'{score:.1%}' if evaluation.get('complete') and score is not None else 'pending full audit' + lines.append(f"| {evaluation['checkpoint']} | {evaluation.get('job')} | {evaluation['stage']} | {value} |") + lines += ['', 'Independent trainer/inference GPU pairs; independent eval GPUs. The Daytona ' + 'checkpoint controllers reserve training capacity and admit one comparison eval at a time. ' + 'The original multi-harness run uses E2B.', '', + 'JSON snapshots retain checkpoint/eval state, TiTO evidence, logging health, numerical checks and recovery actions. ' + 'Transient dead CPU controllers can be restarted twice. Training, TiTO or provenance failures are recorded for ' + 'diagnosis; the monitor never resets weights in response to a reward dip.', ''] + return '\n'.join(lines) + + +def watch(args): + args.out.mkdir(parents=True, exist_ok=True) + lock = (args.out / 'monitor.lock').open('a') + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + previous = read(args.out / 'status.json').get('runs', {}) + while not (args.out / 'STOP').exists() and not (args.out / 'completed.json').exists(): + began = time.monotonic() + now = time.time() + snapshot = {'checked_at': now, 'checked_utc': datetime.now(timezone.utc).isoformat(), + 'interval_seconds': 600, 'monitor_job': os.environ.get('SLURM_JOB_ID'), 'runs': {}, 'actions': []} + for arm in ['multi4', 'opencode', 'whitebox']: + try: + row = original() if arm == 'multi4' else comparison(arm, args.out, args.env_file) + row['alerts'] = diagnose(row, previous.get(arm, {}), now) + if args.repair_controllers: + action = repair_controller(row, args.out) + if action: + snapshot['actions'].append({'arm': arm, **action}) + except Exception as exc: + row = {**previous.get(arm, {}), 'observation_error': type(exc).__name__, + 'alerts': ['observation_failed_' + type(exc).__name__]} + snapshot['runs'][arm] = row + write(args.out / 'status.json', snapshot) + with (args.out / 'history.jsonl').open('a') as stream: + stream.write(json.dumps(snapshot) + '\n') + rendered = markdown(snapshot) + args.markdown.write_text(rendered) + with (args.out / 'HISTORY.md').open('a') as stream: + stream.write(rendered + '\n---\n\n') + changes = {arm: r['alerts'] for arm, r in snapshot['runs'].items() + if r['alerts'] != previous.get(arm, {}).get('alerts', [])} + if changes or snapshot['actions']: + with (args.out / 'events.jsonl').open('a') as stream: + stream.write(json.dumps({'checked_at': now, 'alert_changes': changes, 'actions': snapshot['actions']}) + '\n') + print(json.dumps({'at': snapshot['checked_utc'], 'runs': {k: {f: r.get(f) for f in + ['job', 'stage', 'step', 'alerts']} for k, r in snapshot['runs'].items()}}), flush=True) + previous = snapshot['runs'] + if (all(r.get('stage') == 'COMPLETED' and not r.get('observation_error') for r in previous.values()) + and previous['multi4'].get('step', 0) >= 1000 + and all(previous[a].get('controller_state') == 'COMPLETED' for a in ['opencode', 'whitebox'])): + write(args.out / 'completed.json', {'at': now, 'reason': 'all three runs and comparison eval controllers completed'}) + break + if args.once: + break + time.sleep(max(1, 600 - (time.monotonic() - began))) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest='command', required=True) + watcher = commands.add_parser('watch') + watcher.add_argument('--out', type=Path, required=True) + watcher.add_argument('--env-file', type=Path, required=True) + watcher.add_argument('--markdown', type=Path, required=True) + watcher.add_argument('--once', action='store_true') + watcher.add_argument('--repair-controllers', action='store_true') + fetch = commands.add_parser('fetch-whitebox') + fetch.add_argument('--job', required=True) + fetch.add_argument('--out', type=Path, required=True) + fetch.add_argument('--env-file', type=Path, required=True) + args = parser.parse_args() + (fetch_whitebox if args.command == 'fetch-whitebox' else watch)(args) diff --git a/04-data-agent/hf/probe_capture_keepalive.py b/04-data-agent/hf/probe_capture_keepalive.py new file mode 100644 index 0000000..38e80c7 --- /dev/null +++ b/04-data-agent/hf/probe_capture_keepalive.py @@ -0,0 +1,105 @@ +"""Run delayed fixture completions through a real relay and exact-token capture.""" +import argparse +import asyncio +from concurrent.futures import ThreadPoolExecutor +import json +from pathlib import Path +import socket +import time + +import httpx + +from openenv.core.harness.capture.export import export_session +from openenv.core.harness.capture.forwarding import GradioForwarder +from openenv.core.harness.capture.runner import CaptureServer + + +class FixtureEngine: + served_model = 'fixture-model' + param_fixes = {} + api_key = None + calls = 0 + + async def completion(self, request): + self.calls += 1 + assert request['stream'] is False + slow = 'second' in json.dumps(request['messages']) + await asyncio.sleep(75 if slow else .01) + ids = [6, 7] if slow else [3, 4] + return {'id': 'fixture-second' if slow else 'fixture-first', 'object': 'chat.completion', + 'model': self.served_model, 'prompt_token_ids': [1, 2, 3, 4, 5] if slow else [1, 2], + 'choices': [{'index': 0, 'message': {'role': 'assistant', 'content': 'fixture answer'}, + 'finish_reason': 'stop', 'token_ids': ids, + 'logprobs': {'content': [{'token': str(i), 'logprob': -.25} for i in ids]}}], + 'usage': {'prompt_tokens': 5 if slow else 2, 'completion_tokens': 2}} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--out', type=Path, required=True) + args = parser.parse_args() + args.out.mkdir(parents=True, exist_ok=True) + with socket.socket() as sock: + sock.bind(('127.0.0.1', 0)) + port = sock.getsockname()[1] + server = CaptureServer(llm_url='http://127.0.0.1:9/v1', model='fixture-model', port=port, + capture_level='tokens') + engine = FixtureEngine() + server.app.state.inference = engine + server.app.state.upstreams._default = (engine, 'tokens') + forwarder = GradioForwarder() + try: + server.start() + url = forwarder.start(port) + def probe(dialect): + session = server.app.state.registry.create(max_model_calls=2) + receipts = [] + for prompt in ['first', 'second']: + body = {'model': 'fixture-model', 'stream': True, + 'messages': [{'role': 'user', 'content': prompt}]} + path = '/v1/chat/completions' + if dialect == 'anthropic': + path = '/v1/messages'; body['max_tokens'] = 10 + elif dialect == 'responses': + path = '/v1/responses'; body.pop('messages'); body['input'] = prompt + elif dialect == 'google': + path = '/v1beta/models/fixture-model:streamGenerateContent?alt=sse' + body = {'contents': [{'role': 'user', 'parts': [{'text': prompt}]}]} + began = time.monotonic() + first_byte, heartbeats, data = None, 0, [] + with httpx.stream('POST', url + path, json=body, timeout=100, + headers={'Authorization': 'Bearer ' + session.session_id}) as response: + assert response.status_code == 200, response.status_code + for line in response.iter_lines(): + if first_byte is None: + first_byte = time.monotonic() - began + heartbeats += line.startswith(': openenv keepalive') + if line.startswith('data: ') and line != 'data: [DONE]': + data.append(json.loads(line[6:])) + assert 'fixture answer' in json.dumps(data), dialect + receipts.append({'request': prompt, 'seconds': time.monotonic() - began, + 'first_byte_seconds': first_byte, 'heartbeats': heartbeats}) + document = export_session(session, capture_level='tokens') + assert len(document['turns']) == session.model_calls == 2 + assert len(document['sequences']) == 1 + sequence = document['sequences'][0] + assert sequence['input_ids'] == [1, 2, 3, 4, 5, 6, 7] + assert sequence['loss_mask'] == [0, 0, 1, 1, 0, 1, 1] + assert sequence['logprobs'] == [0, 0, -.25, -.25, 0, -.25, -.25] + assert receipts[1]['heartbeats'] >= 6 and receipts[1]['first_byte_seconds'] < 20 + return {'dialect': dialect, 'passed': True, 'receipts': receipts, 'captured_turns': 2, + 'supervised_tokens': 4} + with ThreadPoolExecutor(max_workers=4) as pool: + results = list(pool.map(probe, ['chat', 'anthropic', 'responses', 'google'])) + assert engine.calls == 8 + report = {'passed': True, 'fixture_only_no_model_or_sandbox': True, + 'engine_calls': engine.calls, 'results': results} + (args.out / 'result.json').write_text(json.dumps(report, indent=2) + '\n') + print(json.dumps(report), flush=True) + finally: + forwarder.stop() + server.stop() + + +if __name__ == '__main__': + main() diff --git a/04-data-agent/hf/probe_relay_deadline.py b/04-data-agent/hf/probe_relay_deadline.py new file mode 100644 index 0000000..38087b9 --- /dev/null +++ b/04-data-agent/hf/probe_relay_deadline.py @@ -0,0 +1,110 @@ +"""Measure a share relay's delayed-header/body behavior without model requests.""" +import argparse +from concurrent.futures import ThreadPoolExecutor +import hashlib +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +import importlib.util +import json +from pathlib import Path +import threading +import time +import uuid + +import httpx + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--forwarding-source', type=Path, required=True) + parser.add_argument('--out', type=Path, required=True) + parser.add_argument('--delay', type=float, default=75) + args = parser.parse_args() + assert 1 <= args.delay <= 120 + args.out.mkdir(parents=True, exist_ok=True) + instance = uuid.uuid4().hex + events, lock = [], threading.Lock() + + class Handler(BaseHTTPRequestHandler): + def log_message(self, *unused): + pass + + def do_GET(self): + data = json.dumps({'instance': instance}).encode() + self.send_response(200) + self.send_header('Content-Type', 'application/json') + self.send_header('Content-Length', str(len(data))) + self.end_headers() + self.wfile.write(data) + + def do_POST(self): + path = self.path.removeprefix('/' + instance) + if path not in ('/delayed', '/headers', '/heartbeats') or not self.path.startswith('/' + instance): + self.send_error(404) + return + began = time.monotonic() + try: + if path == '/delayed': + time.sleep(args.delay) + self.send_response(200) + self.send_header('Content-Type', 'application/json') + self.send_header('Connection', 'close') + self.send_header('X-Accel-Buffering', 'no') + self.end_headers() + self.wfile.flush() + if path == '/headers': + time.sleep(args.delay) + if path == '/heartbeats': + while time.monotonic() - began < args.delay: + self.wfile.write(b'\n') + self.wfile.flush() + time.sleep(min(5, args.delay - (time.monotonic() - began))) + self.wfile.write(json.dumps({'instance': instance, 'mode': path}).encode()) + self.wfile.flush() + error = None + except (BrokenPipeError, ConnectionResetError) as exc: + error = type(exc).__name__ + finally: + self.close_connection = True + with lock: + events.append({'mode': path, 'elapsed': time.monotonic() - began, 'error': error}) + + server = ThreadingHTTPServer(('127.0.0.1', 0), Handler) + threading.Thread(target=server.serve_forever, daemon=True).start() + spec = importlib.util.spec_from_file_location('probe_forwarder', args.forwarding_source) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + forwarder = module.GradioForwarder() + try: + url = forwarder.start(server.server_port) + def health(): + response = httpx.get(url + '/health', timeout=15) + return {'status': response.status_code, 'same_instance': response.status_code == 200 and response.json().get('instance') == instance} + before = health() + def request(mode): + start = time.monotonic() + try: + response = httpx.post(url + '/' + instance + '/' + mode, content=b'', timeout=args.delay + 30) + try: + payload = response.json() + except ValueError: + payload = {} + return {'mode': mode, 'seconds': time.monotonic() - start, 'status': response.status_code, + 'same_instance': payload.get('instance') == instance, + 'no_interface_html': 'No interface is running' in response.text, + 'response_bytes': len(response.content)} + except httpx.RequestError as exc: + return {'mode': mode, 'seconds': time.monotonic() - start, 'error': type(exc).__name__} + with ThreadPoolExecutor(max_workers=3) as pool: + results = list(pool.map(request, ['delayed', 'headers', 'heartbeats'])) + report = {'delay_seconds': args.delay, 'forwarding_sha256': hashlib.sha256(args.forwarding_source.read_bytes()).hexdigest(), + 'health_before': before, 'results': results, 'health_after': health(), 'server_events': events} + (args.out / 'result.json').write_text(json.dumps(report, indent=2) + '\n') + print(json.dumps(report), flush=True) + finally: + forwarder.stop() + server.shutdown() + server.server_close() + + +if __name__ == '__main__': + main() diff --git a/04-data-agent/hf/runtime/artifacts.py b/04-data-agent/hf/runtime/artifacts.py new file mode 100644 index 0000000..5c1bf7d --- /dev/null +++ b/04-data-agent/hf/runtime/artifacts.py @@ -0,0 +1,59 @@ +"""Publish logs asynchronously and publish completed checkpoint manifests last.""" +import os +import threading +import time +from pathlib import Path + +from common import write_json +from bucket_io import sync_with_retry + + +class Publisher: + def __init__(self, output): + self.output = Path(output) + self.dest = ("hf://buckets/" + os.environ["ARTIFACT_BUCKET"] + "/" + os.environ["RUN_ID"] + + "/jobs/" + os.environ["RUN_OWNER"]) + self.stop_event = threading.Event() + self.published = set() + self.lock = threading.Lock() + self.thread = threading.Thread(target=self.loop, daemon=True, name="artifact-publisher") + + def sync(self): + from huggingface_hub import HfApi + api = HfApi() + with self.lock: + sync_with_retry(api, self.output, self.dest, exclude=["**/*.tmp", "run/checkpoint-*/**", "remote-resume/checkpoint-*/**", "inference-model/**", "trackio/**"]) + for checkpoint in sorted((self.output / "run").glob("checkpoint-*")): + if checkpoint.name in self.published or not (checkpoint / "checkpoint.saved.json").is_file(): + continue + from checkpoint_store import READY, seal + seal(checkpoint, arm=os.environ['COMPARISON_ARM'], bundle_sha256=os.environ['BUNDLE_SHA256']) + target = self.dest + "/run/" + checkpoint.name + print(f"Publishing full checkpoint: {checkpoint.name}", flush=True) + sync_with_retry(api, checkpoint, target, exclude=[READY]) + # sync_bucket performs content checks for transfer; the consumer verifies native file hashes. + sync_with_retry(api, checkpoint, target, include=[READY]) + self.published.add(checkpoint.name) + print(f"Published full checkpoint: {checkpoint.name}", flush=True) + write_json(self.output / "upload_status.json", {"last_success": time.time(), "destination": self.dest, + "published_checkpoints": sorted(self.published)}) + + def loop(self): + while not self.stop_event.is_set(): + try: + self.sync() + except Exception as exc: + write_json(self.output / "upload_error.json", {"time": time.time(), "type": type(exc).__name__}) + self.stop_event.wait(60) + + def start(self): + self.thread.start() + + def finish(self): + self.stop_event.set() + # A full optimizer checkpoint can take longer than two minutes to + # upload. Allow the same hour of grace reserved for checkpoint work. + self.thread.join(timeout=3600) + if self.thread.is_alive(): + raise TimeoutError("Artifact upload did not finish before shutdown") + self.sync() diff --git a/04-data-agent/hf/runtime/auth_bridge.py b/04-data-agent/hf/runtime/auth_bridge.py new file mode 100644 index 0000000..be1fa9e --- /dev/null +++ b/04-data-agent/hf/runtime/auth_bridge.py @@ -0,0 +1,99 @@ +"""Local HTTP/SSE/WebSocket bridge to an authenticated, fixed HF Space origin. + +It keeps HF access credentials outside the native tool payload and sandbox. Bind on loopback only. +""" +import asyncio +import os +from contextlib import asynccontextmanager + +import httpx +from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect +from starlette.responses import StreamingResponse +from starlette.background import BackgroundTask +from websockets.asyncio.client import connect +from websockets.exceptions import ConnectionClosed + +HOP_HEADERS = {"host", "connection", "transfer-encoding", "keep-alive", "proxy-authenticate", + "proxy-authorization", "te", "trailer", "upgrade", "content-length"} + + +def make_app(origin, token, *, ping_interval=20): + origin = origin.rstrip("/") + if not origin.startswith("https://") and not origin.startswith("http://127.0.0.1:"): + raise ValueError("Bridge requires HTTPS or a loopback test origin") + + @asynccontextmanager + async def lifespan(app): + async with httpx.AsyncClient(timeout=httpx.Timeout(1800, connect=60), + limits=httpx.Limits(max_connections=256, max_keepalive_connections=128), + follow_redirects=False) as client: + app.state.client = client + yield + + app = FastAPI(lifespan=lifespan) + + @app.api_route("/{path:path}", methods=["GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD"]) + async def http(request: Request, path: str): + headers = {k: v for k, v in request.headers.items() if k.lower() not in HOP_HEADERS} + headers["authorization"] = "Bearer " + token + url = origin + "/" + path + if request.url.query: + url += "?" + request.url.query + upstream = await app.state.client.send(app.state.client.build_request( + request.method, url, headers=headers, content=request.stream()), stream=True) + headers = {k: v for k, v in upstream.headers.items() if k.lower() not in HOP_HEADERS} + return StreamingResponse(upstream.aiter_raw(), status_code=upstream.status_code, headers=headers, + background=BackgroundTask(upstream.aclose)) + + @app.websocket("/{path:path}") + async def websocket(socket: WebSocket, path: str): + url = origin.replace("https://", "wss://").replace("http://", "ws://") + "/" + path + if socket.url.query: + url += "?" + socket.url.query + try: + async with connect(url, additional_headers={"Authorization": "Bearer " + token}, + max_size=None, ping_interval=ping_interval, ping_timeout=60, open_timeout=60) as upstream: + await socket.accept() + + async def outbound(): + while True: + event = await socket.receive() + if event["type"] == "websocket.disconnect": + return + await upstream.send(event.get("text") if event.get("text") is not None else event["bytes"]) + + async def inbound(): + async for value in upstream: + if isinstance(value, str): + await socket.send_text(value) + else: + await socket.send_bytes(value) + + tasks = [asyncio.create_task(outbound()), asyncio.create_task(inbound())] + try: + done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) + for task in done: + task.result() + finally: + for task in tasks: + task.cancel() + await asyncio.gather(*tasks, return_exceptions=True) + except ConnectionClosed: + # Surface a broken upstream promptly to the native retry policy. + try: + await socket.close(code=1011, reason="Environment connection closed") + except (RuntimeError, WebSocketDisconnect): + pass + finally: + try: + await socket.close() + except (RuntimeError, WebSocketDisconnect): + pass + + return app + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(make_app(os.environ["SPACE_URL"], os.environ["HF_TOKEN"]), + host="127.0.0.1", port=int(os.environ.get("BRIDGE_PORT", "8100")), log_level="warning") diff --git a/04-data-agent/hf/runtime/bootstrap.py b/04-data-agent/hf/runtime/bootstrap.py new file mode 100644 index 0000000..16cc272 --- /dev/null +++ b/04-data-agent/hf/runtime/bootstrap.py @@ -0,0 +1,50 @@ +"""HF Job entry point; only Python stdlib is needed before creating the locked venvs.""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import subprocess +import sys +import tarfile + + +def main(): + p = argparse.ArgumentParser() + p.add_argument("--bundle-dir", default="/bundle") + p.add_argument("--role", required=True, choices=["preflight", "eval", "train", "coordinator"]) + args, rest = p.parse_known_args() + bundle = Path(args.bundle_dir) + archive = bundle / "bundle.tar.gz" + info = json.loads((bundle / "bundle.json").read_text()) + assert hashlib.sha256(archive.read_bytes()).hexdigest() == info["sha256"] + root = Path("/workspace/repro") + root.mkdir(parents=True, exist_ok=True) + with tarfile.open(archive) as tar: + tar.extractall(root, filter="data") + os.environ["REPRO_ROOT"] = str(root) + os.environ["BUNDLE_SHA256"] = info["sha256"] + os.environ.setdefault("HF_HOME", "/workspace/hf-cache") + os.environ.setdefault("UV_CACHE_DIR", "/workspace/uv-cache") + for name, target in [("env", root / "OpenEnv/.venv"), ("train", root / ".venv312")]: + if args.role == "coordinator" and name == "train": + continue + subprocess.run(["uv", "venv", "--python", "3.12", str(target)], check=True) + subprocess.run(["uv", "pip", "sync", "--python", str(target / "bin/python"), + "--require-hashes", str(root / f"hf/locks/requirements-{name}.lock")], check=True) + if args.role != "coordinator": + # Register the vendored TRL project itself, without resolving or changing + # any locked dependencies. Merely adding its source to PYTHONPATH leaves + # importlib.metadata empty and fails at the first checkpoint model card. + project = root / "experiments/daytona_harness_comparison/logs/20260915/source/trl" + subprocess.run(["uv", "pip", "install", "--python", str(root / ".venv312/bin/python"), + "--no-deps", "--no-build-isolation", "--editable", str(project)], check=True) + script = "preflight.py" if args.role == "preflight" else "job.py" + python = root / ".venv312/bin/python" + if args.role == "coordinator": + python = root / "OpenEnv/.venv/bin/python" + os.execv(str(python), [str(python), "-u", str(root / "hf/runtime" / script), "--role", args.role, *rest]) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/hf/runtime/bucket_io.py b/04-data-agent/hf/runtime/bucket_io.py new file mode 100644 index 0000000..60a7496 --- /dev/null +++ b/04-data-agent/hf/runtime/bucket_io.py @@ -0,0 +1,24 @@ +"""Bounded, idempotent retries for transient HF Bucket transfer failures.""" +import json +import time + + +def sync_with_retry(api, source, destination, **options): + import httpx + from huggingface_hub.errors import HfHubHTTPError + + for attempt in range(3): + try: + return api.sync_bucket(str(source), destination, quiet=True, **options) + except (TimeoutError, ConnectionError, httpx.TransportError, HfHubHTTPError) as exc: + if isinstance(exc, HfHubHTTPError): + code = getattr(exc.response, "status_code", None) + if code not in {429, 500, 502, 503, 504}: + raise + if attempt == 2: + raise + # Xet can commit data before its response times out. Sync compares + # existing content, so repeating the same transfer is safe. + print(json.dumps({"bucket_retry": attempt + 1, "error_type": type(exc).__name__, + "destination": destination}), flush=True) + time.sleep(30 * (attempt + 1)) diff --git a/04-data-agent/hf/runtime/check_task_schedule.py b/04-data-agent/hf/runtime/check_task_schedule.py new file mode 100644 index 0000000..1fb9918 --- /dev/null +++ b/04-data-agent/hf/runtime/check_task_schedule.py @@ -0,0 +1,33 @@ +"""Check real task identities across both native catalogs and frozen schedules.""" +import json +from common import RUN, configure + + +def check(): + configure() + from daytona_whitebox_backend import load_frozen_tasks + from openenv.harbor.tasks import resolve_task_dirs + counts = {} + for split, expected in [("train", 1000), ("test", 250)]: + blackbox = resolve_task_dirs(str(RUN / "datasets" / split)) + whitebox = load_frozen_tasks(split) + assert len(blackbox) == len(whitebox) == expected + for index, (directory, task) in enumerate(zip(blackbox, whitebox)): + assert task.metadata["task_index"] == index + assert directory.name == task.metadata["source_name"], (split, index) + assert task.instruction == (directory / "instruction.md").read_text() + counts[split] = expected + if split == "train": + for filename in ["reference_schedule.json", "opencode_schedule.json"]: + schedule = json.loads((RUN / filename).read_text()) + for row in schedule["tasks"]: + index = row["task_index"] + assert blackbox[index].name == row["name"] + assert whitebox[index].difficulty == row["difficulty"] + for group in schedule["groups"]: + assert blackbox[group["task_index"]].name == group["task_name"] + return {"passed": True, "catalog_tasks": counts, "both_schedules_match": True} + + +if __name__ == "__main__": + print(json.dumps(check()), flush=True) diff --git a/04-data-agent/hf/runtime/checkpoint_store.py b/04-data-agent/hf/runtime/checkpoint_store.py new file mode 100644 index 0000000..740809c --- /dev/null +++ b/04-data-agent/hf/runtime/checkpoint_store.py @@ -0,0 +1,138 @@ +"""Portable full-checkpoint integrity, including optimizer, RNG and rollout cursor. + +Native completion markers bind filesystem paths/mtimes. A remote restore first +verifies every file by content, then explicitly rebinds those native markers. +""" +import hashlib +import json +from pathlib import Path + +from common import write_json + +READY = "checkpoint.hf.ready.json" + + +def bucket_location(uri): + prefix = "hf://buckets/" + if not uri.startswith(prefix): + raise ValueError("Expected an HF Bucket URI") + parts = uri[len(prefix):].strip("/").split("/") + if len(parts) < 3 or any(p in {"", ".", ".."} for p in parts): + raise ValueError("Invalid artifact URI") + return "/".join(parts[:2]), "/".join(parts[2:]) + + +def download_json(source, name, target, api=None): + from huggingface_hub import HfApi + if Path(name).name != name: + raise ValueError("Expected a single artifact filename") + bucket, prefix = bucket_location(source) + target = Path(target) + target.parent.mkdir(parents=True, exist_ok=True) + (api or HfApi()).download_bucket_files(bucket, + files=[(prefix + "/" + name, str(target))], raise_on_missing_files=True) + return json.loads(target.read_text()) + + +def restore_model(source, target, *, arm, bundle_sha256, manifest_sha256): + """Download hash-verified inference files without transferring optimizer state.""" + from huggingface_hub import HfApi + from checkpoint_artifacts import METADATA, model_files + from common import MODEL, REVISION + target = Path(target) + target.mkdir(parents=True, exist_ok=False) + api = HfApi() + manifest = download_json(source, READY, target / READY, api) + if digest(target / READY) != manifest_sha256: + raise ValueError("Checkpoint manifest changed after evaluation was queued") + if (manifest["arm"] != arm or manifest["bundle_sha256"] != bundle_sha256 or + manifest["base_model"] != MODEL or manifest["base_revision"] != REVISION): + raise ValueError("Checkpoint evaluation provenance mismatch") + names = [name for name in manifest["files"] if name in METADATA or name.endswith(".safetensors")] + if any(Path(name).name != name for name in names): + raise ValueError("Invalid checkpoint member") + if not {"config.json", "tokenizer.json", "tokenizer_config.json"}.issubset(names): + raise ValueError("Checkpoint lacks model or tokenizer metadata") + bucket, prefix = bucket_location(source) + api.download_bucket_files(bucket, files=[(prefix + "/" + n, str(target / n)) for n in names], + raise_on_missing_files=True) + for name in names: + if digest(target / name) != manifest["files"][name]: + raise ValueError(f"Checkpoint model hash mismatch: {name}") + if not model_files(target): + raise ValueError("No model weights found") + write_json(target / "evaluation_source.json", {"source": source, "manifest_sha256": manifest_sha256, + "arm": arm, "step": manifest["step"], "bundle_sha256": bundle_sha256, + "files": {name: manifest["files"][name] for name in names}}) + return manifest + + +def digest(path): + value = hashlib.sha256() + with Path(path).open("rb") as stream: + for block in iter(lambda: stream.read(8 * 1024 * 1024), b""): + value.update(block) + return value.hexdigest() + + +def seal(checkpoint, *, arm, bundle_sha256): + from checkpoint_artifacts import REQUIRED, finalize_saved + checkpoint = Path(checkpoint) + native = finalize_saved(checkpoint) + required = set(REQUIRED) + if arm in {"blackbox", "opencode"}: + required.add("rollout_state.json") + for name in required: + if not (checkpoint / name).is_file(): + raise ValueError(f"Incomplete full checkpoint: {name}") + paths = sorted(p for p in checkpoint.iterdir() if p.is_file() and p.name != READY) + if any(p.is_symlink() for p in paths): + raise ValueError("Checkpoint must contain actual files") + before = {p.name: (p.stat().st_size, p.stat().st_mtime_ns) for p in paths} + files = {p.name: digest(p) for p in paths} + after = {p.name: (p.stat().st_size, p.stat().st_mtime_ns) for p in paths} + if before != after: + raise ValueError("Checkpoint changed while hashing") + manifest = {"schema": 1, "arm": arm, "step": native["step"], + "base_model": native["base_model"], "base_revision": native["base_revision"], + "bundle_sha256": bundle_sha256, "files": files, + "required_training_state": sorted(required), "final": native.get("final", False)} + write_json(checkpoint / READY, manifest) + return manifest + + +def verify(checkpoint): + checkpoint = Path(checkpoint) + manifest = json.loads((checkpoint / READY).read_text()) + for name, expected in manifest["files"].items(): + if Path(name).name != name or (checkpoint / name).is_symlink(): + raise ValueError("Invalid checkpoint member") + if digest(checkpoint / name) != expected: + raise ValueError(f"Remote checkpoint content mismatch: {name}") + if not set(manifest["required_training_state"]).issubset(manifest["files"]): + raise ValueError("Checkpoint lacks complete training-state hashes") + state = json.loads((checkpoint / "trainer_state.json").read_text()) + if state["global_step"] != manifest["step"]: + raise ValueError("Checkpoint optimizer step mismatch") + return manifest + + +def restore(source, target, *, arm, bundle_sha256): + from huggingface_hub import HfApi + from checkpoint_artifacts import mark_saved, finalize_saved + target = Path(target) + if target.exists(): + raise ValueError("Restore target must be new") + target.mkdir(parents=True) + HfApi().sync_bucket(source, str(target), quiet=True) + manifest = verify(target) + if manifest["arm"] != arm or manifest["bundle_sha256"] != bundle_sha256: + raise ValueError("Remote checkpoint provenance mismatch") + # Preserve the verified source manifest separately before rebinding native paths. + write_json(target.parent / (target.name + ".remote-origin.json"), manifest) + mark_saved(target, manifest["step"], manifest["base_model"], manifest["base_revision"], + final=manifest.get("final", False)) + finalize_saved(target) + # Re-seal the rebound copy so its complete integrity check also remains usable. + seal(target, arm=arm, bundle_sha256=bundle_sha256) + return manifest diff --git a/04-data-agent/hf/runtime/checkpoint_transport_smoke.py b/04-data-agent/hf/runtime/checkpoint_transport_smoke.py new file mode 100644 index 0000000..63c0607 --- /dev/null +++ b/04-data-agent/hf/runtime/checkpoint_transport_smoke.py @@ -0,0 +1,54 @@ +"""Tiny labeled checkpoint fixture: real Bucket upload, restore and tamper detection.""" +import json +import os +from pathlib import Path + +from common import ROOT, configure, write_json + + +def check(): + configure() + import torch + from safetensors.torch import save_file + from huggingface_hub import HfApi + from checkpoint_artifacts import REQUIRED, mark_saved, resume_info + from checkpoint_store import seal, restore, verify + root = ROOT / "outputs" / os.environ["RUN_OWNER"] / "checkpoint-transport-fixture" + source = root / "source" + source.mkdir(parents=True, exist_ok=False) + for name in REQUIRED: + (source / name).write_text("{}") + write_json(source / "trainer_state.json", {"global_step": 2}) + write_json(source / "rollout_state.json", {"prompt_index": 3, "model_version": 2}) + torch.save({"state": {0: {"step": torch.tensor(2), "exp_avg": torch.tensor([0.1])}}, + "param_groups": [{"params": [0]}]}, source / "optimizer.pt") + save_file({"integration_fixture.weight": torch.tensor([1.0, 2.0])}, source / "model.safetensors") + mark_saved(source, 2, "integration-fixture-only", "fixture-v1") + seal(source, arm="blackbox", bundle_sha256=os.environ["BUNDLE_SHA256"]) + destination = ("hf://buckets/" + os.environ["ARTIFACT_BUCKET"] + "/" + os.environ["RUN_ID"] + + "/preflight/" + os.environ["RUN_OWNER"] + "/checkpoint-transport-fixture") + HfApi().sync_bucket(str(source), destination, quiet=True) + target = root / "restored" + restore(destination, target, arm="blackbox", bundle_sha256=os.environ["BUNDLE_SHA256"]) + resume = resume_info(target, "integration-fixture-only", "fixture-v1") + assert resume["step"] == 2 and resume["group_offset"] == 3 + optimizer = torch.load(target / "optimizer.pt", weights_only=True) + assert optimizer["state"][0]["step"].item() == 2 + original = (target / "optimizer.pt").read_bytes() + (target / "optimizer.pt").write_bytes(original + b"tampered") + try: + verify(target) + except ValueError as exc: + assert "optimizer.pt" in str(exc) + else: + raise AssertionError("Corrupted optimizer checkpoint was accepted") + (target / "optimizer.pt").write_bytes(original) + verify(target) + result = {"passed": True, "fixture_only": True, "real_bucket_roundtrip": True, + "optimizer_tamper_rejected": True, "native_cursor_restored": True} + write_json(root / "result.json", result) + return result + + +if __name__ == "__main__": + print(json.dumps(check()), flush=True) diff --git a/04-data-agent/hf/runtime/cleanup_local.py b/04-data-agent/hf/runtime/cleanup_local.py new file mode 100644 index 0000000..08afe35 --- /dev/null +++ b/04-data-agent/hf/runtime/cleanup_local.py @@ -0,0 +1,37 @@ +"""Release only sandboxes labelled with this unique local Slurm run owner.""" +import concurrent.futures +import json +import os +from pathlib import Path +import time + +from daytona import Daytona, ListSandboxesQuery + +owner = os.environ['RUN_OWNER'] +if not owner.startswith('local-') or not owner.endswith('-' + os.environ.get('SLURM_JOB_ID', owner.rsplit('-', 1)[-1])): + raise ValueError('Expected the unique owner of this local Slurm run') +arm = os.environ['COMPARISON_ARM'] +labels = ({'openenv_component': 'blackbox-opencode', 'openenv_owner': owner} if arm == 'opencode' + else {'experiment': 'daytona-harness-comparison', 'run': '20260915', 'arm': arm, 'owner': owner}) +api = Daytona() +selected = list(api.list(ListSandboxesQuery(labels=labels), request_timeout=30)) +def delete(sandbox): + try: + api.delete(sandbox, timeout=60, wait=True) + return {'id': sandbox.id, 'deleted': True} + except Exception as exc: + return {'id': sandbox.id, 'deleted': False, 'error_type': type(exc).__name__} +with concurrent.futures.ThreadPoolExecutor(max_workers=8) as pool: + results = list(pool.map(delete, selected)) +deadline = time.monotonic() + 30 +while True: + remaining = list(api.list(ListSandboxesQuery(labels=labels), request_timeout=15)) + if not remaining or time.monotonic() >= deadline: + break + time.sleep(3) +report = {'owner': owner, 'labels': labels, 'results': results, 'remaining': len(remaining)} +path = Path(os.environ['REPRO_ROOT']) / 'outputs' / owner / 'cleanup.json' +path.write_text(json.dumps(report, indent=2) + '\n') +print(json.dumps({'owner': owner, 'selected': len(selected), 'remaining': len(remaining)}), flush=True) +if remaining: + raise RuntimeError('Local owned sandbox cleanup did not complete') diff --git a/04-data-agent/hf/runtime/common.py b/04-data-agent/hf/runtime/common.py new file mode 100644 index 0000000..97ba0d1 --- /dev/null +++ b/04-data-agent/hf/runtime/common.py @@ -0,0 +1,76 @@ +"""Portable paths and small process helpers shared by Spaces and Jobs.""" +from __future__ import annotations + +import hashlib +import json +import os +import subprocess +import sys +import time +from pathlib import Path + +ROOT = Path(os.environ.get("REPRO_ROOT", "/workspace/repro")) +RUN = ROOT / "experiments/daytona_harness_comparison/logs/20260915" +TOOLS = ROOT / "experiments/daytona_harness_comparison/tools" +TRAIN_PY = ROOT / ".venv312/bin/python" +ENV_PY = ROOT / "OpenEnv/.venv/bin/python" +MODEL = "Qwen/Qwen3.5-2B" +REVISION = "15852e8c16360a2fea060d615a32b45270f8a8fc" + + +def configure(): + paths = [ROOT / "hf/runtime", TOOLS, RUN / "source/tools", RUN / "source/packages", + RUN / "source/HuggingEnvs/04-data-agent/train", RUN / "source/OpenEnv/src", + RUN / "source/OpenEnv/envs", RUN / "source/trl", RUN / "eval-source"] + os.environ["PYTHONPATH"] = os.pathsep.join(map(str, paths)) + for path in reversed(paths): + sys.path.insert(0, str(path)) + os.environ["DAYTONA_COMPARISON_RUN"] = str(RUN) + os.environ.setdefault("PYTHONUNBUFFERED", "1") + os.environ.setdefault("OMP_NUM_THREADS", "1") + os.environ.setdefault("TOKENIZERS_PARALLELISM", "false") + os.environ.setdefault("TRL_EXPERIMENTAL_SILENCE", "1") + + +def write_json(path, value): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_suffix(path.suffix + ".tmp") + tmp.write_text(json.dumps(value, indent=2, default=str) + "\n") + tmp.replace(path) + + +def verify_bundle(): + manifest = json.loads((ROOT / "bundle_manifest.json").read_text()) + for name, digest in manifest["files"].items(): + path = ROOT / name + actual = hashlib.sha256(path.read_bytes()).hexdigest() + if actual != digest: + raise ValueError(f"Bundle file changed: {name}") + return len(manifest["files"]) + + +def start(command, log, env=None): + log = Path(log) + log.parent.mkdir(parents=True, exist_ok=True) + stream = log.open("a") + process = subprocess.Popen(list(map(str, command)), stdout=stream, stderr=subprocess.STDOUT, + env=env, start_new_session=True, cwd=ROOT) + stream.close() + return process + + +def ready(url, process=None, headers=None, seconds=1200): + import httpx + deadline = time.monotonic() + seconds + with httpx.Client(timeout=10, headers=headers, follow_redirects=True) as client: + while time.monotonic() < deadline: + if process and process.poll() is not None: + raise RuntimeError(f"Service exited before readiness: returncode={process.returncode}") + try: + if client.get(url).status_code == 200: + return + except httpx.HTTPError: + pass + time.sleep(2) + raise TimeoutError(f"Readiness deadline exceeded: {url}") diff --git a/04-data-agent/hf/runtime/coordinator.py b/04-data-agent/hf/runtime/coordinator.py new file mode 100644 index 0000000..a49419a --- /dev/null +++ b/04-data-agent/hf/runtime/coordinator.py @@ -0,0 +1,233 @@ +"""Queue independent HF checkpoint evaluations after durable checkpoint publication.""" +import hashlib +from contextlib import nullcontext +import json +import os +from pathlib import Path +import time + +from common import ROOT, write_json +from checkpoint_store import READY, bucket_location, digest, download_json + +TERMINAL = {"COMPLETED", "ERROR", "CANCELED", "CANCELLED", "DELETED"} + + +def eligible(manifest, interval, include_final): + step = manifest["step"] + return type(step) is int and step > 0 and (step % interval == 0 or include_final and manifest.get("final", False)) + + +def evaluation_key(training_job, manifest_sha, protocol): + encoded = json.dumps([training_job, manifest_sha, protocol], sort_keys=True).encode() + return hashlib.sha256(encoded).hexdigest() + + +def verified_terminal_result(stage, score, evidence, manifest, sha, source, status=None): + """Retain a fully published result even if the final artifact flush failed.""" + if not score.get("comparison_ready") or evidence.get("manifest_sha256") != sha: + raise ValueError("Checkpoint evaluation did not pass its evidence gate") + if stage == "COMPLETED": + return + if stage != "ERROR" or not status or not ( + status.get("passed") is True and status.get("finished_at") + and status.get("arm") == manifest["arm"] and status.get("phase") == "checkpoint" + and score.get("complete") is True and score.get("tito_pass") is True + and score.get("graded_cells", 0) == score.get("expected_cells", -1) > 0 + and evidence.get("step") == manifest["step"] + and evidence.get("bundle_sha256") == manifest["bundle_sha256"] + and evidence.get("source") == source + ): + raise ValueError("Failed job has no complete, provenance-verified evaluation") + + +def sync_decisions(api, decisions, destination): + """Retry transient bucket failures without losing submission intent.""" + from bucket_io import sync_with_retry + sync_with_retry(api, decisions, destination) + + +def submit_once(state, key, known, persist, launch): + """Persist intent before submission; an ambiguous response must never double-submit.""" + matches = [j for j in known if (j.labels or {}).get("evaluation_key") == key] + if len(matches) > 1: + raise RuntimeError("Duplicate checkpoint evaluation jobs require reconciliation") + if matches: + state[key] = {**state.get(key, {}), "job_id": matches[0].id, "status": "submitted"} + persist() + return matches[0] + if key in state: + raise RuntimeError("Unresolved submission intent; inspect HF Jobs before retrying") + state[key] = {"status": "submitting", "created_at": time.time()} + persist() + job = launch() + state[key].update(job_id=job.id, status="submitted") + persist() + return job + + +def validate_requested_final(request, training_id, source, manifest, manifest_sha): + """Bind an intentional stop to one fully verified, durable checkpoint.""" + if not request: + return False + expected = {"training_job": training_id, "checkpoint": source, + "step": manifest["step"], "manifest_sha256": manifest_sha} + if (request.get("full_checkpoint_verified") is not True + or request.get("user_requested_stop") is not True + or type(request.get("step")) is not int or request["step"] <= 0 + or any(request.get(k) != v for k, v in expected.items())): + raise ValueError("Requested final evaluation does not match the verified stopped checkpoint") + return True + + +def run(output, arm, admission=None, final_checkpoint=None): + from huggingface_hub import HfApi, Volume + from huggingface_hub.errors import EntryNotFoundError + api = HfApi() + config = json.loads((ROOT / "hf/configs/deployment.json").read_text()) + namespace = config["namespace"] + training_id = os.environ["TRAINING_JOB"] + training = api.inspect_job(job_id=training_id, namespace=namespace) + if training.labels.get("role") != "train" or training.labels.get("arm") != arm: + raise ValueError("Coordinator must follow the requested arm's training job") + if training.environment["BUNDLE_SHA256"] != os.environ["BUNDLE_SHA256"]: + raise ValueError("Coordinator and training bundle differ") + owner = training.environment["RUN_OWNER"] + bucket = os.environ["ARTIFACT_BUCKET"] + base = "hf://buckets/" + bucket + "/" + os.environ["RUN_ID"] + training_root = base + "/jobs/" + owner + destination = base + "/coordination/" + owner + decisions = output / "decisions" + decisions.mkdir(parents=True, exist_ok=True) + try: + state = download_json(destination, "state.json", decisions / "state.json", api) + except EntryNotFoundError: + state = {} + + def persist(): + write_json(decisions / "state.json", state) + sync_decisions(api, decisions, destination) + + protocol = {"evaluation": config["evaluation"], "data": config["data"], "pins": config["harness_pins"]} + evaluation = config["evaluation"] + terminal_seen = None + while True: + # A second coordinator exits before it can submit work. + peers = list(api.list_jobs(namespace=namespace, labels={"role": "coordinator", "training_job": training_id})) + active_peers = sorted((j for j in peers if j.status.stage not in TERMINAL), key=lambda j: j.id) + if active_peers and active_peers[0].environment.get("RUN_OWNER") != os.environ["RUN_OWNER"]: + raise RuntimeError("Another coordinator already owns this training job") + training = api.inspect_job(job_id=training_id, namespace=namespace) + if training.status.stage in TERMINAL: + terminal_seen = terminal_seen or time.monotonic() + else: + terminal_seen = None + jobs = list(api.list_jobs(namespace=namespace, labels={"role": "eval", "training_job": training_id})) + active = [j for j in jobs if j.status.stage not in TERMINAL] + _, prefix = bucket_location(training_root + "/run") + try: + folders = [item.path for item in api.list_bucket_tree(bucket, prefix=prefix, recursive=False) + if Path(item.path).name.startswith("checkpoint-")] + except EntryNotFoundError: + folders = [] + pending = [] + alerts = [] + checkpoints = [] + for folder in folders: + source = "hf://buckets/" + bucket + "/" + folder + target = output / "manifests" / Path(folder).name / READY + try: + manifest = download_json(source, READY, target, api) + except EntryNotFoundError: + continue # Files are still being uploaded; only the final marker admits eval. + if manifest["arm"] != arm or manifest["bundle_sha256"] != os.environ["BUNDLE_SHA256"]: + raise ValueError("Published checkpoint provenance differs from training") + checkpoints.append((source, manifest, digest(target))) + final_step = max((m["step"] for _, m, _ in checkpoints), default=0) if terminal_seen is not None else 0 + if final_checkpoint and not any(m["step"] == final_checkpoint["step"] for _, m, _ in checkpoints): + raise ValueError("Requested final checkpoint is not durably published") + for source, manifest, sha in checkpoints: + if final_checkpoint and manifest["step"] != final_checkpoint["step"]: + continue + requested_final = validate_requested_final(final_checkpoint, training_id, source, manifest, sha) + if os.environ.get("QUALIFY_CHECKPOINT_STEP") and manifest["step"] != int(os.environ["QUALIFY_CHECKPOINT_STEP"]): + continue + is_final = requested_final or (evaluation["also_final"] and manifest["step"] == final_step and final_step > 0) + if not is_final and not eligible(manifest, evaluation["interval_steps"], evaluation["also_final"]): + continue + key = evaluation_key(training_id, sha, protocol) + match = [j for j in jobs if j.labels.get("evaluation_key") == key] + if len(match) > 1: + raise RuntimeError("Duplicate evaluation identity") + if match: + j = match[0] + state[key] = {"step": manifest["step"], "job_id": j.id, "stage": j.status.stage, + "checkpoint": source, "manifest_sha256": sha} + score_path = decisions / "scores" / f"step-{manifest['step']:06d}.json" + if j.status.stage in {"COMPLETED", "ERROR"}: + eval_source = base + "/jobs/" + j.environment["RUN_OWNER"] + if not score_path.exists(): + score = download_json(eval_source, "canonical_scores.json", output / "score-cache" / (key + ".json"), api) + evidence = download_json(eval_source, "checkpoint_evaluation.json", output / "source-cache" / (key + ".json"), api) + status = (download_json(eval_source, "status.json", output / "status-cache" / (key + ".json"), api) + if j.status.stage == "ERROR" else None) + verified_terminal_result(j.status.stage, score, evidence, manifest, sha, source, status) + write_json(score_path, {"step": manifest["step"], "job_id": j.id, + "source": evidence, "scores": score, "provider_stage": j.status.stage, + "evaluation_status": status}) + cached = json.loads(score_path.read_text()) + if cached.get("job_id") != j.id or cached.get("step") != manifest["step"]: + raise ValueError("Cached score belongs to a different evaluation") + verified_terminal_result(j.status.stage, cached["scores"], cached["source"], + manifest, sha, source, cached.get("evaluation_status")) + state[key]["result_verified"] = True + elif j.status.stage in TERMINAL and j.status.stage != "COMPLETED": + alerts.append({"step": manifest["step"], "job_id": j.id, "stage": j.status.stage}) + elif key in state: + alerts.append({"step": manifest["step"], "reason": "ambiguous submission; reconciliation required"}) + else: + pending.append((manifest["step"], key, source, sha)) + capacity = admission() if admission is not None else nullcontext(True) + with capacity as admitted: + can_dispatch = training.status.stage not in {"CANCELED", "CANCELLED", "DELETED"} + # A normal cancellation must still suppress new GPU work. An explicit + # user stop permits exactly the checkpoint bound and verified above. + can_dispatch = can_dispatch or (final_checkpoint is not None and training.status.stage in {"CANCELED", "CANCELLED"}) + if admitted and pending and not active and not alerts and can_dispatch: + step, key, source, sha = sorted(pending)[0] + eval_owner = f"eval-{arm}-step{step}-{key[:12]}" + env = {"ARTIFACT_BUCKET": bucket, "RUN_ID": os.environ["RUN_ID"], "RUN_OWNER": eval_owner, + "COMPARISON_ARM": arm, "BUNDLE_SHA256": os.environ["BUNDLE_SHA256"], + "SPACE_URL": training.environment["SPACE_URL"], "JOB_FLAVOR": config["compute"]["eval_flavor"], + "SPACE_BUNDLE_SHA256": training.environment.get("SPACE_BUNDLE_SHA256", training.environment["BUNDLE_SHA256"]), + "EVAL_CONCURRENCY": str(evaluation["concurrency_per_arm"][arm]), + "CHECKPOINT_PREFIX": source, "CHECKPOINT_MANIFEST_SHA": sha, "CHECKPOINT_STEP": str(step), + "TRAINING_JOB": training_id, "PYTHONUNBUFFERED": "1", "TRACKIO_MODE": "offline"} + env["HF_JOB_NAMESPACE"] = namespace + if arm == "opencode": + # Checkpoint provenance remains native OpenCode, while the + # comparison evaluates the same weights through four Harbor agents. + env.update(EVAL_SUITE="harbor", + SPACE_URL=training.environment["CHECKPOINT_EVAL_SPACE_URL"], + SPACE_BUNDLE_SHA256=training.environment["CHECKPOINT_EVAL_SPACE_SHA256"]) + secrets = {k: os.environ[k] for k in ["HF_TOKEN", "DAYTONA_API_KEY", "DAYTONA_API_URL", "DAYTONA_TARGET"] if os.environ.get(k)} + def launch(): + return api.run_job(namespace=namespace, image=config["compute"]["bootstrap_image"], + command=["python", "/bundle/bootstrap.py", "--role", "eval", "--arm", arm, + "--phase", "checkpoint", "--dp", str(config["compute"]["eval_dp"])], + flavor=config["compute"]["eval_flavor"], timeout="4h", env=env, secrets=secrets, + name=eval_owner, expose=[8000], labels={"experiment": config["experiment"], "role": "eval", + "arm": arm, "phase": "checkpoint", "training_job": training_id, "evaluation_key": key}, + volumes=[Volume(type="dataset", source=os.environ["BUNDLE_REPO"], + revision=os.environ["BUNDLE_REVISION"], mount_path="/bundle", read_only=True)]) + j = submit_once(state, key, jobs, persist, launch) + active.append(j) + pending.pop(pending.index((step, key, source, sha))) + write_json(decisions / "monitor.json", {"checked_at": time.time(), "training_job": training_id, + "training_stage": training.status.stage, "active_eval_jobs": [j.id for j in active], + "pending_steps": sorted(p[0] for p in pending), "alerts": alerts}) + persist() + if alerts: + raise RuntimeError("Checkpoint evaluation failed or has an ambiguous submission; inspect monitor.json") + if terminal_seen is not None and time.monotonic() - terminal_seen >= 120 and not active and (not pending or not can_dispatch): + return + time.sleep(60) diff --git a/04-data-agent/hf/runtime/daytona_whitebox_backend.py b/04-data-agent/hf/runtime/daytona_whitebox_backend.py new file mode 100644 index 0000000..61d1896 --- /dev/null +++ b/04-data-agent/hf/runtime/daytona_whitebox_backend.py @@ -0,0 +1,219 @@ +"""Portable Daytona adapter, derived from the frozen 20260915 comparison backend. + +Catalog indices match Harbor's sorted task directories. The manifest retains +curriculum order, which is separate from the catalog's task_index namespace. +""" +from __future__ import annotations + +import asyncio +import concurrent.futures +import json +import math +import os +import tempfile +import threading +import uuid +from pathlib import Path +from service_policy import admission, workload + +from harbor.environments.daytona.environment import DaytonaClientManager, DaytonaEnvironment +from harbor.models.task.task import Task as HarborTask +from harbor.models.trial.paths import TrialPaths +from harbor.verifier.verifier import Verifier +from whitebox_bash.server.sandbox import ExecResult + + +class LoopRunner: + """All Daytona sessions in this server share one long-lived event loop.""" + + def __init__(self): + self.loop = asyncio.new_event_loop() + self.thread = threading.Thread(target=self._run, daemon=True, name='daytona-whitebox-io') + self.thread.start() + + def _run(self): + asyncio.set_event_loop(self.loop) + self.loop.run_forever() + + def call(self, coroutine, timeout=600): + future = asyncio.run_coroutine_threadsafe(coroutine, self.loop) + try: + return future.result(timeout) + except concurrent.futures.TimeoutError: + future.cancel() + raise TimeoutError('Daytona operation exceeded its deadline') from None + + +_RUNNER = None +_LOCK = threading.Lock() + + +def runner(): + global _RUNNER + with _LOCK: + if _RUNNER is None: + _RUNNER = LoopRunner() + return _RUNNER + + +def load_frozen_tasks(split): + from whitebox_bash.tasks import Task + + if split not in {'train', 'test'}: + raise KeyError('Frozen comparison supports only train and test; indices never shift by difficulty.') + root = Path(os.environ['DAYTONA_COMPARISON_RUN']).resolve() + manifest = json.loads((root / f'{split}_manifest.json').read_text()) + result = [] + for index, row in enumerate(sorted(manifest['tasks'], key=lambda row: row['name'])): + relative = next(k for k in row['file_hashes'] if k.endswith('/task.toml')) + task_dir = (root / 'datasets' / split / relative).parent + instruction = (task_dir / 'instruction.md').read_text() + result.append(Task( + instruction=instruction, answer='', difficulty=row['difficulty'], + metadata={'source': 'harbor-frozen', 'native_task_dir': str(task_dir), + 'task_index': index, 'split': split, 'source_name': row['name']}, + )) + return tuple(result) + + +class DaytonaSandbox: + """Existing bash/SETA methods backed by one native Harbor Daytona environment.""" + + def __init__(self, env, task, paths): + self.handle = env + self.task = task + self.paths = paths + self.cwd = '/workdir' + self.calls = [] + self.submitted = None + self._closed = False + self._io_failed = False + + @classmethod + def start(cls, *, task, timeout_s=900, envs=None): + role = workload(task.metadata.get('split', 'test')) + admission.acquire(role) + try: + sb = cls._start_reserved(task=task, timeout_s=timeout_s, envs=envs) + sb._role = role + return sb + except BaseException: + admission.release(role) + raise + + @classmethod + def _start_reserved(cls, *, task, timeout_s=900, envs=None): + if task.metadata.get('source') != 'harbor-frozen': + raise ValueError('Daytona comparison requires the frozen Harbor task source') + native = HarborTask(task.metadata['native_task_dir']) + name = 'wb-' + uuid.uuid4().hex + root = Path(os.environ['DAYTONA_WHITEBOX_TRIALS']).resolve() + paths = TrialPaths(root / name) + paths.mkdir() + env = DaytonaEnvironment( + environment_dir=native.paths.environment_dir, + environment_name='daytona-comparison-20260915', session_id=name, + trial_paths=paths, task_env_config=native.config.environment, + override_cpus=1, override_memory_mb=4096, auto_snapshot=True, + labels={'experiment': 'daytona-harness-comparison', 'run': '20260915', 'arm': 'whitebox', + 'owner': os.environ.get('RUN_OWNER', 'local')}, + ) + sb = cls(env, native, paths) + + async def create(): + try: + await asyncio.wait_for(env.start(force_build=False), min(timeout_s, 900)) + await env.ensure_dirs(['/logs/agent', '/logs/verifier', '/artifacts']) + await asyncio.wait_for(env.run_healthcheck(), 300) + except BaseException: + await asyncio.shield(env.stop(delete=True)) + raise + + runner().call(create(), timeout_s + 310) + (paths.trial_dir / 'session.json').write_text(json.dumps({ + 'sandbox_id': sb.sandbox_id, 'task_index': task.metadata['task_index'], + 'split': task.metadata['split'], 'source_name': task.metadata['source_name'], + 'provider': 'daytona', 'state': 'active', + }, indent=2) + '\n') + return sb + + @property + def sandbox_id(self): + return self.handle._sandbox.id if self.handle._sandbox is not None else '' + + def kill(self): + if self._closed: + return + runner().call(self.handle.stop(delete=True), 120) + self._closed = True + admission.release(self._role) + (self.paths.trial_dir / 'cleanup.json').write_text('{"deleted": true}\n') + + def bash(self, command, timeout_s=120): + try: + result = runner().call(self.handle.exec(command=command, cwd=self.cwd, timeout_sec=timeout_s), timeout_s + 15) + return ExecResult(stdout=result.stdout or '', stderr=result.stderr or '', exit_code=result.return_code) + except Exception as exc: + self._io_failed = True + return ExecResult(error=f'{type(exc).__name__}: sandbox command transport failed', exit_code=1) + + def _abs(self, path): + return path if path.startswith('/') else self.cwd + '/' + path + + def read_file(self, path): + # Missing files and permissions are agent-visible command outcomes, not transport failures. + import shlex + return self.bash('cat -- ' + shlex.quote(self._abs(path))) + + def write_file(self, path, content): + import shlex + remote = self._abs(path) + parent = str(Path(remote).parent) + result = self.bash('mkdir -p -- ' + shlex.quote(parent)) + if not result.ok: + return result + try: + with tempfile.TemporaryDirectory(prefix='daytona-whitebox-') as temp: + local = Path(temp) / 'upload' + local.write_text(content) + runner().call(self.handle.upload_file(local, remote), 120) + return ExecResult(stdout=f'wrote {len(content.encode())} bytes to {path}') + except Exception as exc: + self._io_failed = True + return ExecResult(error=f'{type(exc).__name__}: sandbox file transport failed', exit_code=1) + + def grade(self, submitted): + if self._io_failed: + verdict = {'reward': None, 'note': 'sandbox transport failure; ungraded'} + else: + # Preserve the original answer.txt instructions. The SETA terminator is an + # equivalent submission method; writing answer.txt directly also remains valid. + if submitted is not None: + wrote = self.write_file('/workdir/answer.txt', submitted) + if not wrote.ok: + verdict = {'reward': None, 'note': 'submission transport failure; ungraded'} + (self.paths.trial_dir / 'grade.json').write_text(json.dumps(verdict) + '\n') + return verdict + try: + verifier = Verifier(task=self.task, trial_paths=self.paths, environment=self.handle) + result = runner().call(verifier.verify(), 180) + reward = result.rewards.get('correctness', result.rewards.get('reward')) + if not isinstance(reward, (int, float)) or not math.isfinite(reward): + raise ValueError('Verifier did not return a finite correctness reward') + if reward not in (0, 1): + raise ValueError('Comparison requires binary correctness rewards') + verdict = {'reward': float(reward), 'correct': bool(reward), 'note': 'frozen Harbor verifier', 'rewards': result.rewards} + except Exception as exc: + verdict = {'reward': None, 'note': f'{type(exc).__name__}: verifier failed; ungraded'} + (self.paths.trial_dir / 'grade.json').write_text(json.dumps(verdict, indent=2) + '\n') + return verdict + + +def shutdown(): + if _RUNNER is not None: + async def close(): + manager = await DaytonaClientManager.get_instance() + await manager._cleanup() + _RUNNER.call(close(), 60) + _RUNNER.loop.call_soon_threadsafe(_RUNNER.loop.stop) + _RUNNER.thread.join(timeout=5) diff --git a/04-data-agent/hf/runtime/environment_ui.py b/04-data-agent/hf/runtime/environment_ui.py new file mode 100644 index 0000000..f188af3 --- /dev/null +++ b/04-data-agent/hf/runtime/environment_ui.py @@ -0,0 +1,323 @@ +"""Interactive task clients, mounted after the native OpenEnv API as in environment-101.""" +from dataclasses import dataclass, field +import json +import os +import secrets +import threading +import time + +import gradio as gr +import httpx +from common import RUN + +LOCAL = "http://127.0.0.1:7860" +TITLES = {"whitebox": "Data Agent SETA Whitebox Env", + "blackbox": "Data Agent Blackbox Harbor Env"} + +UI_CSS = """ +.gradio-container {max-width: 1440px !important; margin: auto !important;} +#agent-hero {padding: 30px 32px; border: 1px solid var(--border-color-primary); + border-radius: 20px; margin-bottom: 18px; + background: linear-gradient(120deg, rgba(16,185,129,.10), rgba(59,130,246,.05));} +#agent-hero h1 {font-size: clamp(26px, 3vw, 38px); line-height: 1.2; margin: 12px 0;} +#agent-hero p {max-width: 820px; line-height: 1.6; opacity: .85; margin: 0;} +.agent-eyebrow {font-size: 12px; letter-spacing: .13em; font-weight: 700;} +.agent-chips {display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px;} +.agent-chip {font-size: 12px; border: 1px solid var(--border-color-primary); + border-radius: 999px; padding: 5px 11px; background: var(--background-fill-primary);} +#task-panel {border: 1px solid var(--border-color-primary); border-radius: 16px; padding: 20px;} +#workspace-panel {border: 1px solid var(--border-color-primary); border-radius: 16px; padding: 20px;} +#task-instructions textarea {font-size: 14px; line-height: 1.65;} +#task-badge {font-size: 13px; opacity: .8;} +#tool-console {min-height: 290px;} +.agent-footnote {font-size: 12px; opacity: .75;} +""" + + +def task_badge(metadata): + difficulty = str(metadata.get("difficulty", "Task ready")).capitalize() + name = metadata.get("task_name", metadata.get("task_id", "")) + return f"**{difficulty}** · {name}" if name else f"**{difficulty}**" + + +def tool_inputs(tool): + return (gr.update(visible=tool in {"bash", "grep"}, + label="Search pattern" if tool == "grep" else "Shell command"), + gr.update(visible=tool != "bash"), gr.update(visible=tool in {"write", "edit"}), + gr.update(visible=tool == "edit")) + + +def split_spec(arm, split): + if split not in ("train", "test"): + raise ValueError("Choose train or test") + return str(RUN / "datasets" / split) if arm == "blackbox" else split + + +def preview(arm, split, index): + index = validate_index(split, index) + name = "harbor_env" if arm == "blackbox" else "white_box_bash" + response = httpx.post(LOCAL + f"/{name}/task", json={"split": split_spec(arm, split), + "index": int(index)}, timeout=30) + response.raise_for_status() + task = response.json()["task"] + instruction = task.get("instruction", task.get("prompt", "")) + return instruction, {key: task[key] for key in ("task_name", "task_id", "difficulty") if key in task} + + +def validate_index(split, index): + limit = {"train": 1000, "test": 250}.get(split) + if limit is None or index is None or int(index) != index or not 0 <= index < limit: + raise gr.Error(f"Choose a whole-number task index from 0 to {(limit or 1) - 1}.") + return int(index) + + +@dataclass +class BrowserSession: + lock: threading.Lock = field(default_factory=threading.Lock) + env: object = None + used_at: float = field(default_factory=time.monotonic) + + +class WhiteboxUI: + """One isolated native MCP client/sandbox per browser; explicit and idle cleanup.""" + def __init__(self): + self.sessions = {} + self.lock = threading.Lock() + self.stop = threading.Event() + threading.Thread(target=self.reap, daemon=True, name="ui-sandbox-cleanup").start() + + def session(self, request): + if not request.session_hash: + raise gr.Error("A browser session is required") + with self.lock: + return self.sessions.setdefault(request.session_hash, BrowserSession()) + + def dispose(self, s): + if s.env is not None: + env = s.env + if env._session and env._reward is None: + env._mcp.call("close_episode", session_id=env._session) + env._mcp.close() + s.env = None + + def reap(self): + while not self.stop.wait(30): + with self.lock: + items = list(self.sessions.items()) + for key, s in items: + if time.monotonic() - s.used_at > 1200 and s.lock.acquire(blocking=False): + try: + # Check again after locking: an active tool may have refreshed it. + if time.monotonic() - s.used_at <= 1200: + continue + self.dispose(s) + with self.lock: + self.sessions.pop(key, None) + except Exception: + pass # Retry on the next sweep; no user inputs or credentials in logs. + finally: + s.lock.release() + + def start(self, split, index, request: gr.Request): + index = validate_index(split, index) + from whitebox_bash import white_box_bash_env + s = self.session(request) + with s.lock: + self.dispose(s) + with self.lock: + active = sum(x.env is not None for x in self.sessions.values()) + if active >= 8: + raise gr.Error("All interactive workspaces are in use. Try again after a session finishes.") + env = white_box_bash_env(LOCAL, toolsets="bash,seta", step_limit=100)() + s.env = env + try: + instruction = env.reset(split=split, index=int(index)) + s.used_at = time.monotonic() + return instruction, "Sandbox ready. Working directory: /workdir", "Active" + except Exception as exc: + self.dispose(s) + raise gr.Error(f"Could not start the task ({type(exc).__name__}).") from None + + def run(self, tool, command, path, content, old, request: gr.Request): + s = self.session(request) + with s.lock: + if s.env is None: + raise gr.Error("Start a task first") + e = s.env + args = {"bash": {"command": command}, "read": {"path": path}, + "write": {"path": path, "content": content}, + "edit": {"path": path, "old": old, "new": content}, + "grep": {"pattern": command, "path": path}, + "glob": {"pattern": path}, "ls": {"path": path or "."}} + if tool not in args: + raise gr.Error("Unknown tool") + try: + return getattr(e, tool)(**args[tool]) + finally: + s.used_at = time.monotonic() + + def grade(self, answer, request: gr.Request): + s = self.session(request) + with s.lock: + if s.env is None: + raise gr.Error("Start a task first") + try: + if answer.strip(): + s.env.submit_solution(answer=answer) + reward = s.env.get_reward() + return f"Reward: {reward}", "Finished" + finally: + self.dispose(s) + s.used_at = time.monotonic() + + def close(self, request: gr.Request): + s = self.session(request) + with s.lock: + self.dispose(s) + s.used_at = time.monotonic() + return "Sandbox closed", "Closed" + + +def blackbox_run(split, index, harness, url, model, key): + from openenv.harbor.client import HarborEnv + index = validate_index(split, index) + if not url.strip(): + raise gr.Error("Enter the OpenAI-compatible inference endpoint to use") + yield "Starting the agent in an isolated workspace…", {} + try: + with HarborEnv(base_url=LOCAL, websocket_ping_interval_s=None, + websocket_ping_timeout_s=None) as client: + result = client.run_rollout(split=split_spec("blackbox", split), task_index=int(index), + harness=harness, sandbox="daytona", llm_url=url.strip(), model=model.strip(), + api_key=key, agent_timeout_sec=600, agent_step_limit=17) + summary = {"reward": result.reward, "ok": result.ok, "seconds": result.wall_s, + "model_calls": result.n_turns, "captured_training_tokens": result.n_trainable_tokens, + "capture_level": result.capture_level, "trial": result.trial_name} + # Human-readable captured messages only. Credentials and raw prompt-token arrays stay out of UI. + turns = result.model_dump().get("conversations", []) + yield json.dumps(turns, ensure_ascii=False, indent=2)[-120000:] or "Rollout finished", summary + except Exception as exc: + raise gr.Error(f"Rollout failed ({type(exc).__name__}); retry after checking the endpoint.") from None + + +def mount_ui(app, arm): + from openenv.core.env_server.gradio_theme import OPENENV_GRADIO_CSS, OPENENV_GRADIO_THEME + import provider_demo + use_hf = provider_demo.enabled() + if use_hf: + from inference_providers import mount_provider_relay + app = mount_provider_relay(app) + title = TITLES[arm] + description = ("Explore the data, run tools, and submit your answer in an isolated workspace." + if arm == "whitebox" else + "Connect a model, choose an agent harness, and watch it solve a data task.") + capability = "Bash + SETA tools" if arm == "whitebox" else "4 agent harnesses" + with gr.Blocks(title=title) as demo: + gr.HTML(f'
HUGGINGENVS / DATA AGENT' + f'

{title.removeprefix("Data Agent ").removesuffix(" Env")}

{description} ' + 'The same environment powers training and evaluation.

' + '1,000 training tasks250 test tasks' + f'{capability}Isolated workspaces' + '
') + with gr.Tab("Explore the environment"): + with gr.Row(equal_height=False): + with gr.Column(scale=4, min_width=310, elem_id="task-panel"): + gr.Markdown("### 1. Choose a task") + with gr.Row(): + split = gr.Dropdown([("Test · 250 tasks", "test"), ("Train · 1,000 tasks", "train")], + value="test", label="Dataset") + index = gr.Number(value=0, minimum=0, maximum=999, precision=0, label="Task index", info="0–249") + with gr.Row(): + inspect = gr.Button("Load task", variant="primary") + random = gr.Button("Random task") + badge = gr.Markdown("Load a task to see its difficulty.", elem_id="task-badge") + instruction = gr.Textbox(label="Your task", lines=18, max_lines=30, + interactive=False, elem_id="task-instructions") + with gr.Accordion("Task details", open=False): + metadata = gr.JSON(label="Task metadata") + with gr.Column(scale=6, min_width=400, elem_id="workspace-panel"): + if arm == "whitebox": + ui = WhiteboxUI() + gr.Markdown("### 2. Work with the data\nStart a workspace, inspect the files, and use the tools below.") + with gr.Row(): + begin = gr.Button("Start workspace", variant="primary") + close = gr.Button("Close workspace") + state = gr.Textbox(label="Workspace status", value="Not started", interactive=False) + tool = gr.Dropdown(["bash", "read", "write", "edit", "grep", "glob", "ls"], + value="bash", label="Tool") + command = gr.Textbox(value="ls -la /workdir", label="Shell command", lines=3) + path = gr.Textbox(value="/workdir", label="File or directory path", visible=False) + content = gr.Textbox(label="File content / replacement text", lines=5, visible=False) + old = gr.Textbox(label="Text to replace", visible=False) + execute = gr.Button("Run tool", variant="primary") + output = gr.Code(label="Output", language=None, interactive=False, lines=16, elem_id="tool-console") + gr.Markdown("### 3. Submit your answer") + answer = gr.Textbox(label="Final answer", placeholder="Enter your answer, or leave blank to grade answer.txt.") + submit = gr.Button("Submit and grade", variant="primary") + gr.Markdown("Your workspace is isolated from other users. Close it when finished; idle sessions expire after 20 minutes.", + elem_classes="agent-footnote") + tool.change(tool_inputs, tool, [command, path, content, old], api_visibility="private") + begin.click(ui.start, [split, index], [instruction, output, state], api_name="start_task") + execute.click(ui.run, [tool, command, path, content, old], output, api_name="run_tool") + submit.click(ui.grade, answer, [output, state], api_name="grade_task") + close.click(ui.close, outputs=[output, state], api_name="close_task") + def unload(request: gr.Request): + ui.close(request) + demo.unload(unload) + if use_hf: + with gr.Accordion("Let a model solve this task", open=False): + hf_provider, hf_model = provider_demo.controls() + hf_run = gr.Button("Run model on this task", variant="primary") + hf_summary = gr.JSON(label="Agent result") + hf_transcript = gr.Code(label="Agent conversation", language="json", lines=14) + def run_hf_whitebox(s, i, p, m, oauth_token: gr.OAuthToken, request: gr.Request): + yield from provider_demo.whitebox(ui, s, i, p, m, oauth_token, request) + hf_run.click(run_hf_whitebox, [split, index, hf_provider, hf_model], + [hf_transcript, hf_summary], concurrency_limit=4, + concurrency_id="interactive-agents", api_name="run_hf_agent") + else: + gr.Markdown("### 2. Connect your agent\nUse an OpenAI-compatible model endpoint to run a task.") + with gr.Row(): + harness = gr.Dropdown([("OpenCode", "opencode"), ("Claude Code", "claude-code"), + ("Codex", "codex"), ("Mini-SWE-Agent", "mini-swe-agent")], + value="opencode", label="Agent harness") + model = gr.Textbox(value="Qwen/Qwen3.5-2B", label="Model name") + url = gr.Textbox(label="Inference endpoint", placeholder="https://your-endpoint/v1") + key = gr.Textbox(label="Inference API key", type="password", placeholder="Only if your endpoint requires authentication") + run = gr.Button("Run agent on this task", variant="primary") + gr.Markdown("### 3. Inspect the result") + summary = gr.JSON(label="Score and run statistics") + with gr.Accordion("Agent conversation", open=True): + transcript = gr.Code(label="Conversation", language="json", interactive=False, lines=20) + run.click(blackbox_run, [split, index, harness, url, model, key], [transcript, summary], + concurrency_limit=4, api_name="run_agent") + if use_hf: + with gr.Accordion("Use your Hugging Face account", open=True): + hf_provider, hf_model = provider_demo.controls() + hf_run = gr.Button("Run with HF Inference Providers", variant="primary") + hf_run.click(provider_demo.blackbox, + [split, index, harness, hf_provider, hf_model], [transcript, summary], + concurrency_limit=4, concurrency_id="interactive-agents", api_name="run_hf_agent") + def load(s, i): + return preview(arm, s, i) + inspect.click(load, [split, index], [instruction, metadata], api_name="preview_task").then( + task_badge, metadata, badge, api_visibility="private") + split.change(lambda s: gr.update(value=0, info="0–999" if s == "train" else "0–249"), + split, index, api_visibility="private").then(load, [split, index], [instruction, metadata], + api_visibility="private").then(task_badge, metadata, badge, api_visibility="private") + random.click(lambda s: secrets.randbelow(1000 if s == "train" else 250), split, index, + api_visibility="private").then(load, [split, index], [instruction, metadata], api_visibility="private").then( + task_badge, metadata, badge, api_visibility="private") + demo.load(lambda: preview(arm, "test", 0), outputs=[instruction, metadata], api_visibility="private").then( + task_badge, metadata, badge, api_visibility="private") + with gr.Tab("Training & evaluation API"): + name = "harbor_env" if arm == "blackbox" else "white_box_bash" + gr.Markdown(f"### One environment, both datasets\nUse this Space's endpoint for training and evaluation. " + "Select the task split per request; run model inference on your own endpoint.\n\n" + f"| Task operation | Route |\n| --- | --- |\n| Available datasets | `GET /{name}/splits` |\n" + f"| Number of tasks | `POST /{name}/num_tasks` |\n| Task instructions | `POST /{name}/task` |\n" + f"| Task range | `POST /{name}/task_range` |\n\n" + "The native OpenEnv MCP client executes environment actions. Training and evaluation " + "share the task catalog while using separate model endpoints and sandbox sessions.") + demo.queue(max_size=1024, default_concurrency_limit=8) + return gr.mount_gradio_app(app, demo, path="/", theme=OPENENV_GRADIO_THEME, css=OPENENV_GRADIO_CSS + UI_CSS) diff --git a/04-data-agent/hf/runtime/eval_evidence.py b/04-data-agent/hf/runtime/eval_evidence.py new file mode 100644 index 0000000..b7d59b6 --- /dev/null +++ b/04-data-agent/hf/runtime/eval_evidence.py @@ -0,0 +1,62 @@ +"""Preserve eval evidence across Job and Space restarts.""" +import hashlib +import json +import os +from pathlib import Path + +from common import MODEL, REVISION, write_json + + +def persist_trial(args, result): + """Save harness-version evidence alongside each graded capture, before its trace.""" + import httpx + trial = getattr(result, "trial_name", "") + if not trial or Path(trial).name != trial: + return + output = Path(args.capture_dir).parent + try: + response = httpx.get(args.server.rstrip("/") + "/trial/" + trial + "/result", timeout=30) + response.raise_for_status() + write_json(output / "trials" / trial / "result.json", response.json()) + except Exception as exc: + # Preserve the graded result even if metadata retrieval fails. The final + # audit retries retrieval and refuses to publish an unverified baseline. + write_json(output / "trial-evidence-errors" / (trial + ".json"), + {"trial": trial, "error_type": type(exc).__name__}) + + +def restore_whitebox(output, prefix): + from huggingface_hub import HfApi + origin = output / "resume-origin" + origin.mkdir() + HfApi().sync_bucket(prefix, str(origin), include=["attempts.jsonl", "eval_config.json", "captures/**"], quiet=True) + config = json.loads((origin / "eval_config.json").read_text()) + expected = {"model": MODEL, "revision": REVISION, "pass_k": 1, + "temperature": 0.8, "top_p": 1.0, "max_output_tokens_per_call": 4096, + "max_episode_completion_tokens": 16384, "max_model_calls": 17, + "toolsets": ["bash", "seta"]} + if any(config.get(k) != v for k, v in expected.items()): + raise ValueError("Saved baseline protocol differs from the requested evaluation") + rows = [] + graded = set() + for line in (origin / "attempts.jsonl").read_text().splitlines(): + row = json.loads(line) + if row.get("capture_file"): + relative = Path("captures") / Path(row["capture_file"]).name + saved = origin / relative + if not saved.is_file(): + raise ValueError("Restored attempt is missing its captured tokens") + target = output / relative + target.parent.mkdir(exist_ok=True) + target.write_bytes(saved.read_bytes()) + row["capture_file"] = str(target) + if row.get("reward") in (0, 1) and row.get("tito_pass"): + graded.add((row["harness"], row["index"])) + rows.append(row) + (output / "attempts.jsonl").write_text("".join(json.dumps(row) + "\n" for row in rows)) + write_json(output / "eval_config.json", config) + write_json(output / "eval_resume.json", {"origin": prefix, "restored_graded": len(graded), + "original_attempts_sha256": hashlib.sha256((origin / "attempts.jsonl").read_bytes()).hexdigest(), + "selection": "First graded result, including zeros; only capture paths rebound"}) + from score_comparison import summarize + summarize("whitebox", output) diff --git a/04-data-agent/hf/runtime/eval_opencode.py b/04-data-agent/hf/runtime/eval_opencode.py new file mode 100644 index 0000000..01a5fec --- /dev/null +++ b/04-data-agent/hf/runtime/eval_opencode.py @@ -0,0 +1,150 @@ +"""Pass@1 for the native standalone OpenCode client, with a separate ledger per backend.""" +from __future__ import annotations +import argparse +import concurrent.futures +import hashlib +import json +import math +import os +from pathlib import Path +import time +from common import RUN, MODEL, configure, write_json +configure() +from data_agent_env import DataAgentEnv, opencode_agent_turns, to_trace_entries +from openenv.core.harness.capture.validate import validate_training_turn + + +def audit(result): + entries = opencode_agent_turns(to_trace_entries(result)) + if result.rollout_type != "train" or not entries: + raise ValueError("No TiTO training turns") + for entry in entries: + validate_training_turn(entry['prompt_token_ids'], entry['completion_token_ids'], + entry['per_token_logps'], entry['loss_mask']) + logps = [p for e in entries for p in e['per_token_logps']] + if not any(abs(p) > 1e-8 for p in logps): + raise ValueError("All log probabilities are zero") + return {"tito_pass": True, "agent_turns": len(entries), + "supervised_tokens": sum(sum(e['loss_mask']) for e in entries), + "forwarded_tokens": sum(len(e['loss_mask']) for e in entries)} + + +def summarize(ledger, expected, manifest, ungraded): + selected = {} + if ledger.exists(): + for line in ledger.read_text().splitlines(): + row=json.loads(line) + if row.get('correctness') is not None: selected.setdefault(row['index'],row) + difficulty={} + for i,row in selected.items(): + tier=manifest[i]['difficulty']; d=difficulty.setdefault(tier,{'graded':0,'correct':0}) + d['graded']+=1;d['correct']+=int(row['correctness'] >= 1.0) + correct=sum(int(r['correctness']>=1.0) for r in selected.values()) + result={'metric':'pass@1','implementation':'standalone-opencode','graded_cells':len(selected), + 'expected_cells':expected,'complete':len(selected)==expected,'correct':correct, + 'pass_at_1':correct/len(selected) if selected else None,'difficulty':difficulty, + 'ungraded_attempts':ungraded,'tito_pass':bool(selected) and all(r.get('tito_pass') for r in selected.values())} + result['comparison_ready']=result['complete'] and result['tito_pass'] + return selected,result + + +def evaluate(args): + out=Path(args.out);out.mkdir(parents=True,exist_ok=True) + tasks=sorted(json.loads((RUN/'test_manifest.json').read_text())['tasks'],key=lambda t:t['name']) + limit=args.limit or 250 + if limit > len(tasks): raise ValueError('Limit exceeds frozen test set') + order=list(range(limit)) + # Spread the ramp over the frozen set instead of measuring only the leading easy tasks. + import random + random.Random(42).shuffle(order) + all_scores={} + for backend in args.backends.split(','): + if backend not in {'daytona','hf','e2b'}:raise ValueError('Unsupported sandbox backend') + dest=out/backend;dest.mkdir(exist_ok=True) + ledger=dest/'results.jsonl';failed=dest/'ungraded.jsonl' + ungraded=len(failed.read_text().splitlines()) if failed.exists() else 0 + selected,scores=summarize(ledger,limit,tasks,ungraded) + backend_limit = getattr(args, backend + '_concurrency', None) + concurrency_limit = args.concurrency if backend_limit is None else backend_limit + if not 1 <= concurrency_limit <= 100: + raise ValueError('Backend concurrency must be between 1 and 100') + phases=[(min(8,concurrency_limit),min(8,limit)), + (min(32 if backend == 'daytona' else 16,concurrency_limit),min(32,limit)), + (concurrency_limit,limit)] + if getattr(args, 'no_ramp', False): + phases=[(concurrency_limit,limit)] + scalability=[] + write_json(dest/'configuration.json',{'backend':backend,'concurrency_limit':concurrency_limit, + 'phases':phases,'expected_cells':limit}) + def one(index): + start=time.monotonic() + client=DataAgentEnv(args.server,message_timeout_s=1800) + try: + result=client.run_rollout(split='test',index=index,llm_url=args.vllm_url, + model=args.model,api_key=os.environ.get(args.api_key_env,''),sandbox=backend, + agent_step_limit=17,agent_timeout_s=600,require_tokens=True,timeout_s=1800) + path=dest/'captures'/f'{index}-{result.metadata["rollout_id"]}.json' + write_json(path,result.model_dump()) + if result.metadata.get('error') or result.correctness is None: + return {'index':index,'graded':False,'error':result.metadata.get('error','ungraded'), + 'capture_file':str(path),'elapsed_s':time.monotonic()-start} + if result.metadata.get('task_id') != tasks[index]['name']: + raise ValueError('Returned task identity differs from frozen manifest') + if result.metadata.get('opencode_version') != '1.18.31': + raise ValueError('Unverified OpenCode version') + # Once graded, a failed audit stops the cohort; never retry a scored zero. + proof=audit(result) + return {'index':index,'task_id':tasks[index]['name'],'backend':backend, + 'correctness':result.correctness,'reward':result.reward,'capture_file':str(path), + 'elapsed_s':time.monotonic()-start,'opencode_version':'1.18.31',**proof} + finally:client.close() + for stage,(concurrency,count) in enumerate(phases): + remaining=[i for i in order if i not in selected][:count] + if not remaining:continue + start=time.monotonic();before=len(selected);attempts=0 + write_json(dest/'progress.json',{'stage':stage,'concurrency':concurrency, + 'graded_before':before,'tasks_this_stage':len(remaining),'started_at':time.time()}) + for attempt in range(3): + pending=[i for i in remaining if i not in selected] + if not pending:break + with concurrent.futures.ThreadPoolExecutor(max_workers=concurrency) as pool: + futures={pool.submit(one,i):i for i in pending} + for future in concurrent.futures.as_completed(futures): + i=futures[future];attempts+=1 + # Transport failures carry no returned grade and can be retried. + try:row=future.result() + except (ValueError,AssertionError):raise + except Exception as e:row={'index':i,'graded':False,'error_type':type(e).__name__} + target=ledger if row.get('correctness') is not None else failed + with target.open('a') as f:f.write(json.dumps(row)+'\n') + if target==failed:ungraded+=1 + selected,scores=summarize(ledger,limit,tasks,ungraded) + write_json(dest/'scores.json',scores) + print(json.dumps({'backend':backend,'stage':stage,'graded':len(selected),'latest_index':i, + 'latest_graded':target==ledger,'ungraded_attempts':ungraded}),flush=True) + if len(selected)-before < len(remaining) and attempt<2:time.sleep(5) + elapsed=time.monotonic()-start + scalability.append({'stage':stage,'concurrency':concurrency,'new_graded':len(selected)-before, + 'attempts':attempts,'elapsed_s':elapsed,'graded_per_minute':(len(selected)-before)*60/elapsed}) + write_json(dest/'scalability.json',scalability) + if len(selected)-before < .9*len(remaining): + raise RuntimeError(f'{backend} failed the coverage gate at concurrency {concurrency}') + if limit == 250 and scores["comparison_ready"]: + from native_grading_audit import verify + verify(ledger, dest) + if backend == "daytona": + write_json(out / "verification.json", json.loads((dest / "verification.json").read_text())) + all_scores[backend]=scores + write_json(out/'canonical_scores.json',all_scores) + if not scores['comparison_ready']:raise RuntimeError(f'{backend} baseline incomplete or TiTO invalid') + return all_scores + +if __name__=='__main__': + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('--server',required=True);p.add_argument('--vllm-url',required=True) + p.add_argument('--model',default=MODEL);p.add_argument('--api-key-env',default='HF_TOKEN') + p.add_argument('--backends',default='daytona,hf');p.add_argument('--concurrency',type=int,default=32) + p.add_argument('--daytona-concurrency',type=int);p.add_argument('--hf-concurrency',type=int) + p.add_argument('--no-ramp',action='store_true') + p.add_argument('--limit',type=int,default=250);p.add_argument('--out',required=True) + print(json.dumps(evaluate(p.parse_args())),flush=True) diff --git a/04-data-agent/hf/runtime/harbor_service.py b/04-data-agent/hf/runtime/harbor_service.py new file mode 100644 index 0000000..a32cd5c --- /dev/null +++ b/04-data-agent/hf/runtime/harbor_service.py @@ -0,0 +1,36 @@ +"""Apply the example's train/eval admission policy without rewriting OpenEnv source.""" +from functools import wraps +import os + +from service_policy import admission, output_limit + + +def install(): + from openenv.harbor import rollout, serving + + if getattr(rollout.run_rollout, "_data_agent_policy", False): + return + original = rollout.run_rollout + + @wraps(original) + async def run(**kwargs): + dataset = kwargs.get("dataset", "") + observer = kwargs.get("on_session_created") + + def session_created(session_id): + session = kwargs["registry"].get(session_id) + if session is None: + raise RuntimeError("Rollout session disappeared before policy setup") + session.metadata["max_output_tokens"] = output_limit(dataset) + if observer is not None: + observer(session_id) + + async with admission.slot(dataset): + return await original(**{**kwargs, "on_session_created": session_created}) + + run._data_agent_policy = True + rollout.run_rollout = run + # A protected Space's app endpoint cannot authenticate a sandbox's model + # credential as an HF token. Publish only capture through the existing tunnel. + public_url = serving.space_public_url + serving.space_public_url = lambda: "" if os.environ.get("OPENENV_CAPTURE_TRANSPORT") == "tunnel" else public_url() diff --git a/04-data-agent/hf/runtime/inference_providers.py b/04-data-agent/hf/runtime/inference_providers.py new file mode 100644 index 0000000..a677de3 --- /dev/null +++ b/04-data-agent/hf/runtime/inference_providers.py @@ -0,0 +1,152 @@ +"""HF OAuth model selection and temporary credentials for interactive agent demos. + +The experiment's vLLM path does not import this module. Provider availability comes +from HF's live catalog; a visitor credential is never replaced by a Space secret. +""" +from contextlib import contextmanager +from dataclasses import dataclass, field +import secrets +import threading +import time + +import httpx +from fastapi import HTTPException, Request +from fastapi.responses import JSONResponse, StreamingResponse + +ROUTER = "https://router.huggingface.co/v1" + + +class Catalog: + def __init__(self, ttl=300): + self.ttl = ttl + self._rows = {} + self._updated = 0 + self._lock = threading.Lock() + + def rows(self): + with self._lock: + if time.monotonic() - self._updated < self.ttl and self._rows: + return dict(self._rows) + response = httpx.get(ROUTER + "/models", timeout=20) + response.raise_for_status() + rows = {} + for model in response.json()["data"]: + for provider in model.get("providers", []): + if provider.get("status") == "live" and provider.get("supports_tools") is True: + rows[(provider["provider"], model["id"])] = provider + if not rows: + raise ValueError("No live providers with tool calling were returned") + self._rows, self._updated = rows, time.monotonic() + return dict(rows) + + def select(self, provider, model): + if (provider, model) not in self.rows(): + raise ValueError("Choose a live provider/model pair from the catalog") + return model + ":" + provider + + +catalog = Catalog() + + +def visitor_token(oauth): + if oauth is None or not oauth.token or oauth.expires_at <= time.time(): + raise ValueError("Sign in with Hugging Face to use Inference Providers") + if "inference-api" not in oauth.scope.split(): + raise ValueError("Sign in again and allow Inference Providers access") + return oauth.token + + +@dataclass +class Lease: + model: str + token: str = field(repr=False) + expires: float + remaining: int = 32 # Includes native capture capability probes. + + +class VisitorCredentials: + """Keep OAuth credentials out of Harbor's long-lived upstream client cache.""" + def __init__(self): + self._leases = {} + self._lock = threading.Lock() + + @contextmanager + def issue(self, oauth, model): + token = visitor_token(oauth) + key = secrets.token_urlsafe(32) + with self._lock: + self._leases = {k: v for k, v in self._leases.items() if v.expires > time.time()} + self._leases[key] = Lease(model, token, min(oauth.expires_at, time.time() + 660)) + try: + yield key + finally: + with self._lock: + self._leases.pop(key, None) + + def get(self, key, consume=False): + with self._lock: + lease = self._leases.get(key) + if lease is None or lease.expires <= time.time(): + self._leases.pop(key, None) + raise HTTPException(401, "Interactive inference session expired") + if consume: + if lease.remaining <= 0: + raise HTTPException(429, "Interactive model-call limit reached") + lease.remaining -= 1 + return lease + + +credentials = VisitorCredentials() + + +def mount_provider_relay(app): + """Only an opaque, short-lived key reaches Harbor; the HF token stays here.""" + def lease_for(request, consume=False): + value = request.headers.get("authorization", "") + key = value[7:] if value.lower().startswith("bearer ") else "" + return credentials.get(key, consume) + + @app.get("/hf-inference/v1/models") + async def models(request: Request): + lease = lease_for(request) + return {"object": "list", "data": [{"id": lease.model, "object": "model"}]} + + @app.post("/hf-inference/v1/chat/completions") + async def chat(request: Request): + lease = lease_for(request) + body = await request.json() + if body.get("model") != lease.model: + raise HTTPException(400, "This inference session is bound to the selected model") + lease_for(request, consume=True) + body["max_tokens"] = min(int(body.get("max_tokens") or 4096), 4096) + if "max_completion_tokens" in body: + body["max_completion_tokens"] = min(int(body["max_completion_tokens"]), 4096) + body.pop("max_tokens") + client = httpx.AsyncClient(timeout=120) + try: + upstream = await client.send(client.build_request("POST", ROUTER + "/chat/completions", + headers={"Authorization": "Bearer " + lease.token}, json=body), stream=True) + except httpx.HTTPError: + await client.aclose() + raise HTTPException(502, "The selected inference provider could not be reached") from None + if upstream.status_code >= 400: + status = upstream.status_code + await upstream.aclose() + await client.aclose() + message = {401: "Sign in again to renew your inference access", + 402: "Your HF account needs inference credits", + 403: "Your account cannot access this provider or model", + 429: "This provider is rate limited; try again later"}.get(status, + "The provider rejected the model request; try another model") + return JSONResponse({"error": {"message": message}}, status_code=status) + + async def chunks(): + try: + async for chunk in upstream.aiter_bytes(): + yield chunk + finally: + await upstream.aclose() + await client.aclose() + return StreamingResponse(chunks(), media_type=upstream.headers.get("content-type", "application/json")) + + return app diff --git a/04-data-agent/hf/runtime/job.py b/04-data-agent/hf/runtime/job.py new file mode 100644 index 0000000..a8f3f63 --- /dev/null +++ b/04-data-agent/hf/runtime/job.py @@ -0,0 +1,397 @@ +"""HF GPU task runner using the frozen native evaluators and trainers.""" +from __future__ import annotations + +import argparse +import concurrent.futures +import json +import os +from pathlib import Path +import signal +import subprocess +import sys +import time + +from common import ROOT, RUN, TOOLS, TRAIN_PY, ENV_PY, MODEL, REVISION, configure, ready, start, write_json + + +def inference_url(): + return "http://127.0.0.1:" + os.environ.get("LOCAL_INFERENCE_PORT", "8000") + + +def job_endpoint(): + if os.environ.get("LOCAL_RUNTIME") == "1": + return os.environ.get("SLURM_JOB_ID", os.environ["RUN_OWNER"]), inference_url() + from huggingface_hub import HfApi + api = HfApi() + for job in api.list_jobs(namespace=os.environ.get("HF_JOB_NAMESPACE", "HuggingEnvs"), labels={"experiment": "data-agent-daytona"}): + if job.environment.get("RUN_OWNER") == os.environ["RUN_OWNER"]: + info = api.inspect_job(job_id=job.id, namespace=os.environ.get("HF_JOB_NAMESPACE", "HuggingEnvs")) + urls = info.status.expose_urls + if not urls or len(urls) != 1: + raise RuntimeError("Expected exactly one exposed vLLM endpoint") + return job.id, urls[0].rstrip("/") + raise RuntimeError("Could not identify this Job through its unique owner") + + +def serving(args, output, processes): + env = dict(os.environ) + env.update(MODEL=os.environ.get("CHECKPOINT_MODEL", MODEL), TRL_PROD=str(ROOT), VENV=str(ROOT / ".venv312"), + PORT=os.environ.get("LOCAL_INFERENCE_PORT", "8000"), TP_SIZE="1", DP_SIZE=str(args.dp), MAX_MODEL_LEN="131072", + GPU_MEMORY_UTILIZATION="0.85" if args.role == "train" else "0.90", + TOOL_CALL_PARSER="qwen3_xml", REASONING_PARSER="qwen3", ENABLE_THINKING="0", + ENFORCE_EAGER="0" if args.role == "train" or os.environ.get("LOCAL_RUNTIME") == "1" else "1", TUNNEL="none", SHORT_NAME="daytona-hf", + VLLM_LOG=str(output / "vllm.log"), VLLM_SERVER_DEV_MODE="1", VLLM_USE_DEEP_GEMM="0", + VLLM_DEEP_GEMM_WARMUP="skip", VLLM_USE_FLASHINFER_SAMPLER="0", READY_TIMEOUT_SEC="1200") + extra = ['--dtype bfloat16', '--generation-config vllm', '--logprobs-mode processed_logprobs', + '--return-tokens-as-token-ids', '--no-enable-prefix-caching', + '--limit-mm-per-prompt {"image":0,"video":0}', '--gdn-prefill-backend triton', + '--override-generation-config {"temperature":0.8,"top_p":1.0,"top_k":-1}', + '--served-model-name Qwen/Qwen3.5-2B'] + if env["MODEL"] == MODEL: + extra += ["--revision " + REVISION] + if args.role == "train": + extra += ['--weight-transfer-config {"backend":"nccl"}'] + env["CUDA_VISIBLE_DEVICES"] = os.environ.get("INFERENCE_GPU", "0") + if args.dp > 1: + extra += ["--data-parallel-rpc-port " + os.environ.get("VLLM_DP_RPC_PORT", "8950")] + env["EXTRA_VLLM_ARGS"] = " ".join(extra) + proc = start(["bash", RUN / "eval-source/serve_vllm_tunnel.sh"], output / "serving.log", env) + processes.append(proc) + ready(inference_url() + "/health", proc) + from openenv.core.harness.capture.validate_llm import validate_llm + report = validate_llm(inference_url() + "/v1", MODEL) + if not report.trainable: + raise RuntimeError("Inference preflight did not establish exact token capture") + job_id, public = job_endpoint() + ready(public + "/health", proc, headers={"Authorization": "Bearer " + os.environ["HF_TOKEN"]}, seconds=120) + return job_id, public + + +def bridge(output, processes): + if os.environ.get("LOCAL_RUNTIME") == "1": + server = "http://127.0.0.1:" + os.environ["LOCAL_ENV_PORT"] + proc = start([ENV_PY, ROOT / "hf/runtime/local_environment.py"], output / "environment.log") + processes.append(proc) + ready(server + "/health", proc, seconds=300) + import httpx + info = httpx.get(server + "/deployment", timeout=30).raise_for_status().json() + if info["bundle_sha256"] != os.environ["BUNDLE_SHA256"] or info["train_tasks"] != 1000 or info["test_tasks"] != 250: + raise RuntimeError("Local environment task/source identity mismatch") + write_json(output / "space_identity.json", info) + os.environ["SPACE_URL"] = server + return server + proc = start([ENV_PY, ROOT / "hf/runtime/auth_bridge.py"], output / "bridge.log") + processes.append(proc) + ready("http://127.0.0.1:8100/health", proc, seconds=300) + import httpx + info = httpx.get("http://127.0.0.1:8100/deployment", timeout=30).raise_for_status().json() + expected = os.environ.get("SPACE_BUNDLE_SHA256", os.environ["BUNDLE_SHA256"]) + if info["bundle_sha256"] != expected: + raise RuntimeError("Space and Job runtime bundle hashes differ") + if os.environ.get("COMPARISON_ARM") == "opencode" and os.environ.get("EVAL_SUITE") != "harbor": + if (info.get("implementation") != "standalone-opencode" or info.get("train_tasks") != 1000 + or info.get("test_tasks") != 250 or info.get("opencode_version") != "1.18.31" + or info.get("output_tokens") != {"train": 16384, "test": 4096}): + raise RuntimeError("Standalone training service contract differs from the comparison") + write_json(output / "space_identity.json", info) + return "http://127.0.0.1:8100" + + +def blackbox_audit(output, server): + from openenv.harbor.models import HarborRolloutResult + from smoke_multiharness_tito import audit + import httpx + selected = {} + for path in sorted((output / "traces").glob("*.jsonl")): + for line in path.read_text().splitlines(): + row = json.loads(line) + if row.get("reward") in (0, 1) and row.get("n_turns", 0) > 0: + selected.setdefault((row["harness"], row["index"]), row) + + def check(item): + (harness, index), row = item + result = HarborRolloutResult.model_validate_json(Path(row["capture_file"]).read_text()) + report, _ = audit(result, token_budget=131072) + if not report["tito_pass"]: + raise RuntimeError(f"TiTO failure: {harness}/{index}") + trial = row["trial_name"] + path = output / "trials" / trial / "result.json" + if not path.exists(): + native = httpx.get(server + "/trial/" + trial + "/result", timeout=60).raise_for_status().json() + write_json(path, native) + return {"harness": harness, "index": index, **report} + + with concurrent.futures.ThreadPoolExecutor(max_workers=16) as pool: + reports = list(pool.map(check, selected.items())) + pins = json.loads((ROOT / "hf/configs/deployment.json").read_text())["harness_pins"] + counts = {h: sum(r["harness"] == h for r in reports) for h in pins} + write_json(output / "final_tito.json", {"counts": counts, "tito_pass": bool(reports), "reports": reports}) + from score_comparison import summarize + scores = summarize("blackbox", output) + # Require actual harness versions for partial smokes too. + for harness, versions in scores["harness_versions"].items(): + if versions and set(versions) != {pins[harness]}: + raise RuntimeError(f"Unverified harness version: {harness}") + return scores + + +def evaluate(args, output, server, public): + suite = "blackbox" if os.environ.get("EVAL_SUITE") == "harbor" else args.arm + if suite == "opencode": + command = [ENV_PY, ROOT / "hf/runtime/eval_opencode.py", "--server", server, + "--vllm-url", public + "/v1", "--model", MODEL, "--out", output, + "--concurrency", os.environ.get("EVAL_CONCURRENCY", "35"), + "--limit", str(args.limit or (2 if args.phase == "smoke" else 250)), + "--backends", os.environ.get("EVAL_BACKENDS", "daytona,hf"), + "--daytona-concurrency", os.environ.get("EVAL_DAYTONA_CONCURRENCY", "35"), + "--hf-concurrency", os.environ.get("EVAL_HF_CONCURRENCY", "35")] + if os.environ.get("EVAL_NO_RAMP") == "1": + command += ["--no-ramp"] + if args.phase == "baseline" and os.environ.get("EVAL_NO_RAMP") != "1": + smoke = list(command) + smoke[smoke.index("--out")+1] = output / "smoke" + smoke[smoke.index("--limit")+1] = "2" + smoke[smoke.index("--concurrency")+1] = "2" + smoke[smoke.index("--daytona-concurrency")+1] = "2" + smoke[smoke.index("--hf-concurrency")+1] = "2" + process = start(smoke, output / "smoke-opencode.log") + if process.wait() != 0: + raise RuntimeError("Standalone backend rollout smoke failed; full baseline held") + process = start(command, output / "eval-opencode.log") + if process.wait() != 0: + raise RuntimeError("Standalone OpenCode evaluation failed") + return + ceiling = int(os.environ.get("EVAL_CONCURRENCY", "35")) + phases = [(8, 8), (min(32, ceiling), 32), (ceiling, 100)] if args.phase == "ramp" else [(8 if args.phase == "smoke" else ceiling, args.limit)] + if args.phase == "baseline": + # One immutable first-graded ledger spans the ramp and full evaluation. + # Later passes revisit only infrastructure failures, including scored zeros + # in the resume set so they can never become best-of-N samples. + phases = [(8, 8), (min(32, ceiling), 32), (ceiling, 100)] + [(ceiling, 0)] * 4 + elif args.phase == "checkpoint": + phases = [(ceiling, 0)] * 4 + if args.phase == "smoke": + phases = [(8, 8 if suite == "blackbox" else 2)] + records = [] + scores = json.loads((output / "canonical_scores.json").read_text()) if (output / "canonical_scores.json").exists() else {} + for phase_index, (concurrency, limit) in enumerate(phases): + if (output / "canonical_scores.json").exists() and json.loads((output / "canonical_scores.json").read_text()).get("comparison_ready"): + break + write_json(output / "eval_progress.json", {"stage": phase_index, "concurrency": concurrency, + "max_new_rollouts": limit, "started_at": time.time(), "phase": args.phase}) + before = json.loads((output / "canonical_scores.json").read_text()).get("graded_cells", 0) if (output / "canonical_scores.json").exists() else 0 + if suite == "blackbox": + arms = [{"name": "model", "base_url": public + "/v1", "model": MODEL, "api_key_env": "HF_TOKEN"}] + write_json(output / "arms.json", arms) + indices = "@" + str(RUN / "test_indices.txt") + if args.phase == "smoke": + indices = ",".join((RUN / "test_indices.txt").read_text().replace(",", " ").split()[:2]) + limit = 0 + cmd = [TRAIN_PY, "-u", RUN / "eval-source/eval_concurrent.py", "--server", server, + "--arms", output / "arms.json", "--harnesses", "opencode,claude-code,codex,mini-swe-agent", + "--split", str(RUN / "datasets/test"), "--indices", indices, "--repeat", "1", + "--temperature", "0.8", "--reward-key", "correctness,reward", "--sandbox", "daytona", + "--agent-timeout", "600", "--agent-step-limit", "17", "--max-retries", "3", + "--trace-dir", output / "traces", "--capture-dir", output / "captures", "--progress-every", "1", + "--concurrency", str(concurrency), "--server-concurrency", str(concurrency), + "--sandbox-concurrency", str(concurrency), "--max-new-rollouts", str(limit), "--out", output / "results.json"] + if (output / "traces/eval_config.json").exists(): + cmd += ["--resume"] + else: + cmd = [TRAIN_PY, "-u", TOOLS / "eval_whitebox_native.py", "--run", RUN, "--server", server, + "--vllm-url", inference_url() + "/v1", "--out", output, "--concurrency", str(concurrency), + "--max-new-rollouts", str(limit)] + began = time.monotonic() + proc = start(cmd, output / f"eval-stage{phase_index}-c{concurrency}.log") + code = proc.wait() + if code not in (0, 2): + raise RuntimeError(f"Native evaluator failed: {code}") + if suite == "blackbox": + scores = blackbox_audit(output, server) + scores["training_arm"] = args.arm + scores["arm"] = args.arm + scores["evaluation_suite"] = "harbor" + write_json(output / "canonical_scores.json", scores) + else: + from score_comparison import summarize + scores = summarize("whitebox", output) + elapsed = time.monotonic() - began + graded = scores["graded_cells"] - before + expected = (8 if suite == "blackbox" else 2) if args.phase == "smoke" else limit + if expected and graded < 0.9 * expected: + raise RuntimeError(f"Scale gate failed: only {graded}/{expected} graded") + hourly = {"a100-large": 2.5, "a100x4": 10, "h200x2": 10, "h200": 5}.get(os.environ["JOB_FLAVOR"]) + records.append({"stage": phase_index, "concurrency": concurrency, "new_graded": graded, "elapsed_s": elapsed, + "graded_per_minute": graded * 60 / elapsed, + "compute_usd_per_1000": hourly * elapsed / 3600 * 1000 / graded if graded and hourly else None}) + write_json(output / "scalability.json", records) + print(json.dumps(records[-1]), flush=True) + if args.phase in ("baseline", "checkpoint") and not scores["comparison_ready"]: + raise RuntimeError("Full eval coverage/TiTO/version gate did not pass") + if args.phase == "smoke" and scores["graded_cells"] != (8 if suite == "blackbox" else 2): + raise RuntimeError("Smoke did not grade every requested cell") + write_json(output / "eval_progress.json", {"finished_at": time.time(), "graded_cells": scores["graded_cells"], + "comparison_ready": scores["comparison_ready"], "phase": args.phase}) + + +def training_command(args, output, server): + save = 2 if args.phase == "smoke" else 50 + config = json.loads((ROOT / "hf/configs/deployment.json").read_text()) + harnesses = config["arms"][args.arm].get("training_harnesses", ["opencode"]) + schedule = "reference_schedule.json" if len(harnesses) > 1 else "opencode_schedule.json" + if args.arm in {"blackbox", "opencode"}: + entrypoint = "train_standalone_comparison.py" if args.arm == "opencode" else "train_harbor_multi.py" + cmd = [TRAIN_PY, "-u", RUN / "source/HuggingEnvs/04-data-agent/train" / entrypoint, + "--server", server, "--vllm-url", inference_url(), "--model", MODEL, + "--model-revision", REVISION, "--split", RUN / "datasets/train", "--harnesses", ",".join(harnesses), + "--sandbox", "daytona", "--harness-schedule", RUN / schedule, + "--task-indices", "@" + str(RUN / "train_indices.txt"), "--learning-rate", "3e-6", + "--num-generations", "8", "--max-inflight", "32", "--max-staleness", "4", "--grad-accum", "4", + "--atomic-rollouts", "--max-outstanding-rollouts", "16", "--max-row-tokens", "131072", + "--per-device-batch-size", "4", "--reward-key", "reward", "--agent-step-limit", "17", + "--agent-timeout", "600", "--token-budget", "40960", "--max-completion-length", "16384", + "--dtype", "bfloat16", "--top-p", "1.0", "--temperature", "0.8", "--audit-dir", output / "audit", + "--project", f"daytona-{args.arm}-qwen35-2b", "--save-steps", str(save), "--output-dir", output / "run"] + else: + cmd = [TRAIN_PY, "-u", TOOLS / "train_whitebox_daytona.py", "--run", RUN, "--server", server, + "--vllm-url", inference_url(), "--output-dir", output / "run", "--save-steps", str(save)] + return cmd + + +def train(args, output, server, public, publisher): + os.environ["ROLLOUT_LLM_URL"] = public + os.environ["ROLLOUT_LLM_API_KEY"] = os.environ["HF_TOKEN"] + for key in ["TRACKIO_SPACE_ID", "TRACKIO_SERVER_URL", "TRACKIO_BUCKET_ID", "TRACKIO_DATASET_ID"]: + os.environ.pop(key, None) + os.environ["TRACKIO_STORAGE_MODE"] = "jsonl" + os.environ["TRACKIO_DIR"] = str(output / "trackio") + cmd = training_command(args, output, server) + write_json(output / "training_recipe.json", {"arm": args.arm, "phase": args.phase, + "command": list(map(str, cmd)), "space_bundle_sha256": os.environ.get("SPACE_BUNDLE_SHA256", os.environ["BUNDLE_SHA256"]), + "job_bundle_sha256": os.environ["BUNDLE_SHA256"], "initialization": "pinned base; then native full-state restore for smoke"}) + env = {**os.environ, "CUDA_VISIBLE_DEVICES": os.environ.get("TRAIN_GPU", "1")} + if args.phase == "smoke": + restored = output / "remote-resume/checkpoint-2" + for steps, name, extra in [(2, "first", []), (4, "resumed", ["--resume-from-checkpoint", restored])]: + proc = start(cmd + ["--max-steps", str(steps)] + extra, output / f"train-{name}.log", env) + if proc.wait() != 0: + raise RuntimeError(f"{name} training smoke failed") + publisher.sync() + if steps == 2: + from checkpoint_store import restore + restore(publisher.dest + "/run/checkpoint-2", restored, arm=args.arm, + bundle_sha256=os.environ["BUNDLE_SHA256"]) + from training_smoke import validate + validate(output, args.arm) + else: + if not os.environ.get("VERIFIED_SMOKE_MANIFEST"): + raise RuntimeError("Long training requires a verified optimizer/save/resume manifest") + proof = json.loads(Path(os.environ["VERIFIED_SMOKE_MANIFEST"]).read_text()) + if not (proof.get("passed") and proof.get("arm") == args.arm and + proof.get("bundle_sha256") == os.environ["BUNDLE_SHA256"] and + proof.get("remote_restore_verified") and proof.get("tito_pass") and proof.get("weights_updated")): + raise RuntimeError("Training smoke provenance or optimizer evidence does not match this run") + cmd += ["--max-steps", "1000", "--max-train-seconds", "82200", "--checkpoint-max-seconds", "3600"] + if os.environ.get("RESUME_CHECKPOINT"): + cmd += ["--resume-from-checkpoint", os.environ["RESUME_CHECKPOINT"]] + proc = start(cmd, output / "train.log", env) + if proc.wait() != 0: + raise RuntimeError("Trainer exited with an error") + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--role", choices=["eval", "train", "coordinator"], required=True) + p.add_argument("--arm", choices=["blackbox", "whitebox", "opencode"], required=True) + p.add_argument("--phase", default="smoke") + p.add_argument("--dp", type=int, default=1) + p.add_argument("--limit", type=int, default=0) + args = p.parse_args() + configure() + output = ROOT / "outputs" / os.environ["RUN_OWNER"] + output.mkdir(parents=True, exist_ok=True) + if args.role == "coordinator": + if args.phase == "qualify": + from qualify_training import run + run(output) + elif args.phase == "setup": + from setup_pipeline import run + run(output) + else: + from coordinator import run + run(output, args.arm) + return + processes = [] + from artifacts import Publisher + publisher = Publisher(output) + publisher.start() + from telemetry import Telemetry + telemetry = Telemetry(output, inference_url()) + telemetry.start() + status = {"arm": args.arm, "phase": args.phase, "started_at": time.time(), "passed": False} + logger = None + logger_stop = output / "trackio-stop" + write_json(output / "status.json", status) + try: + if args.role == "eval" and args.phase == "checkpoint": + from checkpoint_store import restore_model + source = os.environ["CHECKPOINT_PREFIX"] + sha = os.environ["CHECKPOINT_MANIFEST_SHA"] + model = output / "inference-model" + manifest = restore_model(source, model, arm=args.arm, + bundle_sha256=os.environ["BUNDLE_SHA256"], manifest_sha256=sha) + if manifest["step"] != int(os.environ["CHECKPOINT_STEP"]): + raise ValueError("Checkpoint optimizer step differs from the queued evaluation") + os.environ["CHECKPOINT_MODEL"] = str(model) + write_json(output / "checkpoint_evaluation.json", {"source": source, "manifest_sha256": sha, + "step": manifest["step"], "bundle_sha256": os.environ["BUNDLE_SHA256"]}) + if args.role == "train" and args.phase != "smoke": + from checkpoint_store import download_json + proof_path = output / "verified_smoke.json" + download_json(os.environ["SMOKE_PREFIX"], "training_smoke_verified.json", proof_path) + os.environ["VERIFIED_SMOKE_MANIFEST"] = str(proof_path) + server = bridge(output, processes) + if args.role == "train" and args.arm in {"blackbox", "opencode"}: + from service_contract import check + write_json(output / "service_contract.json", check(server, "", args.arm)) + job_id, public = serving(args, output, processes) + write_json(output / "services.json", {"job_id": job_id, "public_vllm": public, "server": server, + "space": os.environ["SPACE_URL"], "tp": 1, "dp": args.dp, "flavor": os.environ["JOB_FLAVOR"]}) + if args.role == "eval": + if os.environ.get("RESUME_EVAL_PREFIX"): + if args.arm != "whitebox" or args.phase != "baseline": + raise ValueError("This saved baseline restore is for whitebox baseline evaluations") + from eval_evidence import restore_whitebox + restore_whitebox(output, os.environ["RESUME_EVAL_PREFIX"]) + evaluate(args, output, server, public) + else: + logger_env = {**os.environ, "TRACKIO_DIR": str(output / "trackio"), + "TRAINING_SMOKE": "1" if args.phase == "smoke" else "0"} + logger = start([TRAIN_PY, ROOT / "hf/runtime/logging_sync.py", "--out", output, + "--arm", args.arm, "--watch", "--stop-file", logger_stop], + output / "trackio-sync.log", logger_env) + try: + train(args, output, server, public, publisher) + finally: + logger_stop.touch() + if logger.wait(timeout=180) != 0: + raise RuntimeError("Training Trackio persistence failed") + status["passed"] = True + except Exception as exc: + status["error_type"] = type(exc).__name__ + raise + finally: + status["finished_at"] = time.time() + write_json(output / "status.json", status) + telemetry.finish() + if logger is not None and logger.poll() is None: + os.killpg(logger.pid, signal.SIGTERM) + for process in reversed(processes): + if process.poll() is None: + os.killpg(process.pid, signal.SIGTERM) + publisher.finish() + print(json.dumps(status), flush=True) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/hf/runtime/local_entry.py b/04-data-agent/hf/runtime/local_entry.py new file mode 100644 index 0000000..e6c14a3 --- /dev/null +++ b/04-data-agent/hf/runtime/local_entry.py @@ -0,0 +1,39 @@ +"""Use the same audited HF runner with local GPUs and a local native environment.""" +import json +import os +from pathlib import Path +import sys + +from dotenv import dotenv_values + +values = dotenv_values(os.environ["LOCAL_ENV_FILE"]) +os.environ["HF_TOKEN"] = values.get("HF_API_KEY") or values["HF_TOKEN"] +for key in ["DAYTONA_API_KEY", "DAYTONA_API_URL", "DAYTONA_TARGET", "E2B_API_KEY"]: + if values.get(key): + os.environ[key] = values[key] +root = Path(os.environ["REPRO_ROOT"]) +config = json.loads((root / "hf/configs/deployment.json").read_text()) +gpu_ids = os.environ["CUDA_VISIBLE_DEVICES"].split(",") +if len(gpu_ids) != 2: + raise RuntimeError("The local recipe requires two allocated GPUs") +arm = sys.argv[sys.argv.index("--arm") + 1] +role = sys.argv[sys.argv.index("--role") + 1] +job = os.environ.get("SLURM_JOB_ID", str(os.getpid())) +port_seed = int(job) % 1000 +os.environ.update(LOCAL_RUNTIME="1", RUN_OWNER=f"local-{role}-{arm}-{job}", + RUN_ID=config["run_id"], COMPARISON_ARM=arm, + BUNDLE_SHA256=json.loads((root / "local_manifest.json").read_text())["sha256"], + ARTIFACT_BUCKET=config["resources"]["artifacts_bucket"], JOB_FLAVOR="hopper-prod-2h100", + INFERENCE_GPU=gpu_ids[0], TRAIN_GPU=gpu_ids[1], + LOCAL_INFERENCE_PORT=str(12000 + port_seed), LOCAL_ENV_PORT=str(14000 + port_seed), + DATA_AGENT_CAPTURE_PORT=str(16000 + port_seed), VLLM_DP_RPC_PORT=str(26000 + port_seed), + EVAL_BACKENDS="daytona", EVAL_CONCURRENCY="50", EVAL_DAYTONA_CONCURRENCY="50", EVAL_NO_RAMP="1", + SANDBOX_CAPACITY="50" if role == "eval" else "16", TRAIN_RESERVED_SANDBOXES="8", + HF_HOME=os.environ.get("HF_HOME", str(root / "cache/huggingface"))) +from job import main +try: + main() +finally: + import subprocess + subprocess.run([str(root / 'OpenEnv/.venv/bin/python'), str(root / 'hf/runtime/cleanup_local.py')], + timeout=240, check=True) diff --git a/04-data-agent/hf/runtime/local_environment.py b/04-data-agent/hf/runtime/local_environment.py new file mode 100644 index 0000000..e21d22a --- /dev/null +++ b/04-data-agent/hf/runtime/local_environment.py @@ -0,0 +1,56 @@ +"""Bind the frozen native OpenEnv service to loopback inside its Slurm allocation.""" +import os +from common import RUN, ROOT, configure + +configure() +arm = os.environ["COMPARISON_ARM"] +os.environ.update(ENABLE_WEB_INTERFACE="false", MAX_CONCURRENT_ENVS="128") +if arm == "opencode": + os.environ.update(DATA_AGENT_SPLITS="train,test", DATA_AGENT_SANDBOX="daytona", + DATA_AGENT_FROZEN_TASKS_DIR=str(RUN / "datasets"), DATA_AGENT_CAPTURE_EXPOSE="gradio", + DATA_AGENT_MAX_CONCURRENT=os.environ.get("SANDBOX_CAPACITY", "64")) + from data_agent_env.server.app import app + from data_agent_env.tasks import rows_for + counts = {split: len(rows_for(split)) for split in ("train", "test")} +elif arm == "blackbox": + os.environ.update(OPENENV_DATASETS=",".join(str(RUN / "datasets" / split) for split in ("train", "test")), + OPENENV_CAPTURE_TRANSPORT="tunnel", OPENENV_EXPOSE="gradio", OPENENV_MAX_OUTPUT_TOKENS="16384", + OPENENV_CAPTURE_PORT=os.environ["DATA_AGENT_CAPTURE_PORT"], + OPENENV_HARBOR_TRIALS_DIR=str(ROOT / "outputs" / os.environ["RUN_OWNER"] / "trials")) + from harbor_service import install + install() + from harbor_env.server.app import app + from fastapi import HTTPException + from pathlib import Path + import json + + @app.get("/trial/{name}/result") + def trial_result(name: str): + if Path(name).name != name or name in {".", ".."}: + raise HTTPException(400) + path = Path(os.environ["OPENENV_HARBOR_TRIALS_DIR"]) / name / "result.json" + if not path.is_file(): + raise HTTPException(404) + return json.loads(path.read_text()) + counts = {"train": 1000, "test": 250} +else: + os.environ.update(WHITE_BOX_BASH_TASK_SOURCE="harbor-frozen", + DAYTONA_WHITEBOX_TRIALS=str(ROOT / "outputs" / os.environ["RUN_OWNER"] / "trials"), + WHITE_BOX_BASH_MAX_SESSIONS="16", WHITE_BOX_BASH_MAX_CONCURRENT_ENVS="128") + from whitebox_bash.server.app import app + # The same frozen manifest-backed provider used by the Space. + from whitebox_bash.tasks import num_tasks + counts = {split: num_tasks(split) for split in ("train", "test")} + + +@app.get("/deployment") +def deployment(): + return {"arm": arm, "implementation": "standalone-opencode" if arm == "opencode" else "harbor" if arm == "blackbox" else "whitebox-seta", + "bundle_sha256": os.environ["BUNDLE_SHA256"], "owner": os.environ["RUN_OWNER"], + "train_tasks": counts["train"], "test_tasks": counts["test"], "local": True} + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="127.0.0.1", port=int(os.environ["LOCAL_ENV_PORT"]), + ws_ping_interval=20, ws_ping_timeout=None, timeout_keep_alive=120) diff --git a/04-data-agent/hf/runtime/logging_sync.py b/04-data-agent/hf/runtime/logging_sync.py new file mode 100644 index 0000000..f8a6758 --- /dev/null +++ b/04-data-agent/hf/runtime/logging_sync.py @@ -0,0 +1,136 @@ +"""Durable metrics replay through native Trackio, without optimizer network calls.""" +from __future__ import annotations + +import json +import os +from pathlib import Path +import time + +from common import configure, write_json + + +def checkpoint_scores(output): + """The logging process polls score artifacts; the optimizer never waits on HF.""" + from huggingface_hub import HfApi + from huggingface_hub.errors import EntryNotFoundError + from checkpoint_store import bucket_location, download_json + dest = output / "checkpoint-scores" + dest.mkdir(exist_ok=True) + api = HfApi() + try: + baseline = os.environ.get("BASELINE_PREFIX") + if baseline and not (dest / "step-000000.json").exists(): + scores = download_json(baseline, "canonical_scores.json", output / "baseline_scores.json", api) + write_json(dest / "step-000000.json", {"step": 0, "scores": scores, "source": {"job_id": os.environ.get("BASELINE_JOB")}}) + source = os.environ.get("COORDINATION_PREFIX") + if source: + bucket, prefix = bucket_location(source + "/scores") + for item in api.list_bucket_tree(bucket, prefix=prefix, recursive=False): + name = Path(item.path).name + if name.startswith("step-") and name.endswith(".json") and not (dest / name).exists(): + api.download_bucket_files(bucket, files=[(item.path, str(dest / name))], raise_on_missing_files=True) + except EntryNotFoundError: + pass # No completed checkpoint evaluation yet. + except Exception as exc: + write_json(output / "trackio_remote_error.json", {"time": time.time(), "type": type(exc).__name__}) + return [json.loads(p.read_text()) for p in sorted(dest.glob("step-*.json"))] + + +def sync_metrics(output: Path, arm: str, step=0, smoke=False): + configure() + import trackio_multi4 as native + training_smoke = os.environ.get("TRAINING_SMOKE") == "1" + project = f"daytona-{arm}-qwen35-2b" + ("-integration" if smoke else "-smoke" if training_smoke else "") + owner = os.environ.get("RUN_OWNER", "local") + metadata = {"arm": arm, "sandbox": "daytona", "hf_run": os.environ.get("RUN_ID"), + "bundle_sha256": os.environ.get("BUNDLE_SHA256"), "flavor": os.environ.get("JOB_FLAVOR"), + "integration_test": smoke or training_smoke} + events = [] + if smoke: + events.append(native.event(project, owner, 0, {"integration/offline_online_roundtrip": 1.0}, metadata)) + else: + for path in [output / "audit/metrics.jsonl", output / "run/metrics.jsonl"]: + for row in native.read_metrics(path): + events.append(native.event(project, owner, row["step"], + native.scalars({k: v for k, v in row.items() if k != "step"}, "train/"), metadata)) + for record in checkpoint_scores(output): + score = record["scores"] + if not score.get("comparison_ready"): + continue + values = {"eval/pass_at_1": score["average_pass_at_1"], "eval/graded_cells": score["graded_cells"]} + for harness, item in score["harnesses"].items(): + values[f"eval/{harness}/pass_at_1"] = item["pass_at_1"] + for level, detail in item["difficulty"].items(): + values[f"eval/{harness}/{level}/pass_at_1"] = detail["pass_at_1"] + events.append(native.event(project, owner, record["step"], values, metadata, identity=record["source"])) + scores = native.read_json(output / "canonical_scores.json", {}) + if scores.get("comparison_ready"): + values = {"eval/pass_at_1": scores["average_pass_at_1"], "eval/graded_cells": scores["graded_cells"]} + for h, score in scores["harnesses"].items(): + values[f"eval/{h}/pass_at_1"] = score["pass_at_1"] + for level, item in score["difficulty"].items(): + values[f"eval/{h}/{level}/pass_at_1"] = item["pass_at_1"] + events.append(native.event(project, "evaluation-curve", step, values, metadata)) + if not events: + return + ledger = output / "trackio-events.jsonl" + existing = {json.loads(line)["log_id"] for line in ledger.read_text().splitlines() if line.strip()} if ledger.exists() else set() + events = [event for event in events if event["log_id"] not in existing] + if not events: + return + native.import_events(events) + native.backup_project(project, output / "trackio-backup") + with ledger.open("a") as stream: + for event in events: + stream.write(json.dumps(event) + "\n") + if not os.environ.get("TRACKIO_SPACE"): + from trackio.sqlite_storage import SQLiteStorage + write_json(output / "trackio_verified.json", {"passed": True, "project": project, + "run": owner, "local_database": str(SQLiteStorage.get_project_db_path(project)), + "mode": "offline", "remote_storage": "run artifact bucket", "native_remote_readback": False, + "updated_at": time.time(), "unique_events": len(existing) + len(events)}) + return + config = {"logging": {"project": project, "space_id": os.environ["TRACKIO_SPACE"], + "bucket_id": "HuggingEnvs/data-agent-daytona-trackio"}} + from trackio.deploy import sync_incremental + from trackio.remote_client import RemoteClient + sync_incremental(project, os.environ["TRACKIO_SPACE"], private=False, pending_only=False) + client = RemoteClient(os.environ["TRACKIO_SPACE"], hf_token=os.environ["HF_TOKEN"], + httpx_kwargs={"timeout": 60}) + configuration = native.configuration_records(project) + if configuration: + client.predict(api_name="/bulk_log", logs=configuration, hf_token=os.environ["HF_TOKEN"]) + if smoke: + from trackio.remote_client import RemoteClient + from trackio.sqlite_storage import SQLiteStorage + client = RemoteClient(os.environ["TRACKIO_SPACE"], hf_token=os.environ["HF_TOKEN"], + httpx_kwargs={"timeout": 60}) + # Native read-back, not merely an accepted upload request. + runs = client.predict(api_name="/get_runs_for_project", project=project) + assert owner in str(runs), f"Trackio read-back did not contain integration run {owner}" + logs = client.predict(api_name="/get_logs", project=project, run=owner, run_id=None, scalar_only=True) + assert logs and "integration/offline_online_roundtrip" in str(logs) + write_json(output / "trackio_verified.json", {"passed": True, "project": project, + "run": owner, "local_database": str(SQLiteStorage.get_project_db_path(project)), + "space": os.environ["TRACKIO_SPACE"], "native_remote_readback": True}) + + +if __name__ == "__main__": + import argparse + p = argparse.ArgumentParser() + p.add_argument("--out", type=Path, required=True) + p.add_argument("--arm", required=True) + p.add_argument("--step", type=int, default=0) + p.add_argument("--smoke", action="store_true") + p.add_argument("--watch", action="store_true") + p.add_argument("--stop-file", type=Path) + a = p.parse_args() + a.out.mkdir(parents=True, exist_ok=True) + if a.watch and a.stop_file is None: + p.error("--watch requires --stop-file") + while True: + sync_metrics(a.out, a.arm, a.step, a.smoke) + write_json(a.out / "trackio_collector.json", {"last_success": time.time(), "pid": os.getpid()}) + if not a.watch or a.stop_file.exists(): + break + time.sleep(30) diff --git a/04-data-agent/hf/runtime/native_grading_audit.py b/04-data-agent/hf/runtime/native_grading_audit.py new file mode 100644 index 0000000..1fd79c5 --- /dev/null +++ b/04-data-agent/hf/runtime/native_grading_audit.py @@ -0,0 +1,57 @@ +"""Verify frozen native grading parameters and rescore the unchanged first answers.""" +import hashlib +import json +from pathlib import Path + +from common import RUN, write_json + + +def verify(ledger, output): + from data_agent_env.task import DataAgentTask + from data_agent_env.tasks import _frozen_rows + from data_agent_env.verifier import grade_rollout + import data_agent_env + + selected = {} + encoded = Path(ledger).read_bytes() + for line in encoded.decode().splitlines(): + row = json.loads(line) + if row.get('correctness') is not None: + selected.setdefault(row['index'], row) + if set(selected) != set(range(250)): + raise ValueError('Native grading qualification requires all 250 fixed first answers') + tasks = {} + checked = 0 + for split in ('train', 'test'): + rows = _frozen_rows(str(RUN / 'datasets'), split) + for index, row in enumerate(rows): + task = DataAgentTask.from_row(row) + if (task.atol, task.rtol) != (row['atol'], row['rtol']): + raise ValueError('Native task parsing changed a frozen grading tolerance') + checked += 1 + if split == 'test': tasks[index] = task + if checked != 1250: + raise ValueError('Unexpected fixed training/test task count') + reports = [] + for index, row in sorted(selected.items()): + captured = Path(row['capture_file']).read_bytes() + result = json.loads(captured) + task = tasks[index] + if result['metadata']['task_id'] != task.task_id or row['task_id'] != task.task_id: + raise ValueError('Native capture/task identity mismatch') + answer, source = result.get('answer'), result.get('answer_source') + grade = grade_rollout(task, lambda _: answer if source == 'file' else None, + ('/answer',), final_message=answer if source == 'chat' else None) + if grade.correctness != result['correctness'] or grade.correctness != row['correctness']: + raise ValueError('Rescoring changed a first graded native answer') + reports.append({'index': index, 'task_id': task.task_id, 'correctness': grade.correctness, + 'capture_sha256': hashlib.sha256(captured).hexdigest()}) + if Path(ledger).read_bytes() != encoded: + raise ValueError('First-graded ledger changed during verification') + package = Path(data_agent_env.__file__).parent + proof = {'passed': True, 'parameters_verified': checked, 'original_graded_records_preserved': True, + 'ledger_sha256': hashlib.sha256(encoded).hexdigest(), 'reports': reports, + 'runtime_files': {name: hashlib.sha256((package / name).read_bytes()).hexdigest() + for name in ('task.py', 'tasks.py', 'verifier.py', 'grader.py')}} + write_json(Path(output) / 'verification.json', proof) + return proof diff --git a/04-data-agent/hf/runtime/native_tool_smoke.py b/04-data-agent/hf/runtime/native_tool_smoke.py new file mode 100644 index 0000000..20ec067 --- /dev/null +++ b/04-data-agent/hf/runtime/native_tool_smoke.py @@ -0,0 +1,60 @@ +"""Real HTTP/MCP bash/SETA contract smoke; oracle answers are never sent to a model.""" +import argparse +import concurrent.futures +import json +import time +from pathlib import Path + + +def one(server, index, correct, run): + from harbor.models.task.task import Task + from whitebox_bash import white_box_bash_env + manifest = json.loads((run / 'test_manifest.json').read_text()) + native = Task(run / 'datasets/test/tasks' / manifest['tasks'][index]['name']) + env = white_box_bash_env(server, toolsets='bash,seta', step_limit=30)() + checks = {} + start = time.monotonic() + try: + prompt = env.reset(split='test', index=index) + checks['exact_task_instruction'] = prompt == (native.paths.task_dir / 'instruction.md').read_text() + checks['write'] = '[error]' not in env.write(path='contract.txt', content='alpha\nbeta\n') + checks['read'] = 'alpha\nbeta' in env.read(path='contract.txt') + checks['edit'] = '[error]' not in env.edit(path='contract.txt', old='beta', new='gamma') + checks['bash_same_filesystem'] = 'gamma' in env.bash(command='cat contract.txt') + checks['grep'] = 'gamma' in env.grep(pattern='gamma', path='contract.txt') + checks['glob'] = 'contract.txt' in env.glob(pattern='contract.*') + checks['ls'] = 'contract.txt' in env.ls(path='.') + checks['nonzero_command_preserved'] = '7' in env.bash(command='exit 7') + answer = native.config.verifier.env['EXPECTED_ANSWER'] if correct else '__known_wrong_contract_answer__' + env.submit_solution(answer=answer) + reward = env.get_reward() + checks['frozen_verifier'] = reward == float(correct) + return {'index':index, 'expected_correct':correct, 'checks':checks, + 'passed':all(checks.values()), 'elapsed_s':time.monotonic()-start} + except Exception as exc: + return {'index':index, 'passed':False, 'checks':checks, 'error_type':type(exc).__name__} + finally: + if env._session is not None: + try: + env.get_reward() + except Exception: + pass + env._mcp.close() + + +def main(): + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('--run',type=Path,required=True) + p.add_argument('--server',required=True) + p.add_argument('--concurrency',type=int,default=4) + args=p.parse_args() + with concurrent.futures.ThreadPoolExecutor(max_workers=args.concurrency) as pool: + results=list(pool.map(lambda x: one(args.server,x//2,bool(x%2),args.run),range(8))) + report={'passed':all(r['passed'] for r in results),'expected':8,'results':results} + (args.run/'whitebox_tools_smoke.json').write_text(json.dumps(report,indent=2)+'\n') + print(json.dumps(report)) + return 0 if report['passed'] else 2 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/04-data-agent/hf/runtime/opencode_space.py b/04-data-agent/hf/runtime/opencode_space.py new file mode 100644 index 0000000..697f978 --- /dev/null +++ b/04-data-agent/hf/runtime/opencode_space.py @@ -0,0 +1,110 @@ +"""Space entry point for envs/blackbox-opencode, independent of Harbor execution.""" +import hmac +import os +from common import ROOT, RUN, configure + +configure() +os.environ.update(DATA_AGENT_SPLITS="train,test", DATA_AGENT_SANDBOX="daytona", + DATA_AGENT_FROZEN_TASKS_DIR=str(RUN / "datasets"), DATA_AGENT_CAPTURE_EXPOSE="gradio", + DATA_AGENT_MAX_CONCURRENT=os.environ.get("SANDBOX_CAPACITY", "32"), + HF_SANDBOX_NAMESPACE="HuggingEnvs", ENABLE_WEB_INTERFACE="false", + RUN_OWNER=os.environ.get("SPACE_ID", "standalone-opencode").replace("/", "-")) +from data_agent_env.server.app import app +from data_agent_env.server.environment import DataAgentEnvironment +from data_agent_env.tasks import task_at, rows_for, _public +from data_agent_env.sandbox import BACKENDS, available +from data_agent_env.server.rollout import OPENCODE_VERSION + + +@app.get("/deployment") +def deployment(): + import hashlib + return {"arm": "opencode", "implementation": "standalone-opencode", "mode": "shared", + "source": "HuggingEnvs/04-data-agent/envs/blackbox-opencode", + "bundle_sha256": os.environ.get("BUNDLE_SHA256"), + "train_tasks": len(rows_for("train")), "test_tasks": len(rows_for("test")), + "test_manifest_sha256": hashlib.sha256((RUN / "test_manifest.json").read_bytes()).hexdigest(), + "sandboxes": {"supported": list(BACKENDS), "usable": available()}, + "sandbox_capacity": int(os.environ["DATA_AGENT_MAX_CONCURRENT"]), + "opencode_version": OPENCODE_VERSION, "output_tokens": {"train": 16384, "test": 4096}, + "interactive_ui": True, "trackio": False} + + +class ServiceAuth: + """Native execution RPCs use the owner credential; the public UI supplies its own inference.""" + def __init__(self, app): self.app = app + async def __call__(self, scope, receive, send): + path = scope.get("path", "") + protected = (scope["type"] == "websocket" or path in {"/step", "/reset", "/state", "/mcp"} + or path.startswith("/mcp/")) + if protected: + headers = dict(scope.get("headers", [])) + token = os.environ.get("HF_TOKEN", "") + if not token or not hmac.compare_digest(headers.get(b"authorization", b""), ("Bearer " + token).encode()): + if scope["type"] == "websocket": + await send({"type": "websocket.close", "code": 1008}) + else: + from starlette.responses import JSONResponse + await JSONResponse({"detail": "Authentication required"}, status_code=401)(scope, receive, send) + return + await self.app(scope, receive, send) + +app.add_middleware(ServiceAuth) + +@app.on_event("startup") +async def startup(): + import anyio.to_thread + anyio.to_thread.current_default_thread_limiter().total_tokens = 256 + for split in ("train", "test"): rows_for(split) + + +def preview(split, index): + task = task_at(split, int(index)) + return task.instruction, f"**{task.difficulty_tier.capitalize()}** · {task.task_id}" + + +def rollout(split, index, backend, url, model, key): + import json + if not url.strip() or not model.strip(): + raise gr.Error("Enter an inference endpoint and model to run this task.") + result = json.loads(DataAgentEnvironment()._run_rollout(split, int(index), url.strip(), + model.strip(), backend, 17, 600, False, key)) + if result["reward"] is None: + return "The rollout could not be graded. Check the endpoint and try again.", {} + dialogue = "\n\n".join(f"### Turn {i+1}\n{t['text']}\n" + + ("```json\n" + json.dumps(t['tool_calls'], indent=2) + "\n```" if t['tool_calls'] else "") + for i,t in enumerate(result['turns'])) + return dialogue, {k:result[k] for k in ('correctness','reward','answer','answer_source','n_tool_calls','timed_out')} + +import gradio as gr +from environment_ui import UI_CSS +with gr.Blocks(title="Data Agent Blackbox OpenCode Env") as demo: + gr.HTML('''
HUGGINGENVS · DATA AGENT
+

Blackbox OpenCode

Give OpenCode a real data-analysis task. It explores the tables, + runs its own tools, and submits an answer in an isolated sandbox.

+
Daytona · HF · E2B + 1,000 training tasks250 test tasks
''') + with gr.Row(): + with gr.Column(scale=5, elem_id="task-panel"): + split = gr.Radio(["train", "test"], value="test", label="Task set") + index = gr.Number(value=2, precision=0, minimum=0, maximum=249, label="Task index") + badge = gr.Markdown() + instruction = gr.Textbox(label="Task", lines=16, interactive=False, elem_id="task-instructions") + with gr.Column(scale=4, elem_id="workspace-panel"): + backend = gr.Dropdown(["daytona", "hf", "e2b"], value="daytona", label="Sandbox") + url = gr.Textbox(label="OpenAI-compatible inference URL", placeholder="https://…/v1") + model = gr.Textbox(label="Model", value="Qwen/Qwen3.5-2B") + key = gr.Textbox(label="Inference API key", type="password") + run = gr.Button("Run OpenCode", variant="primary") + score = gr.JSON(label="Result") + transcript = gr.Markdown(label="Agent activity") + split.change(lambda s: gr.update(maximum=999 if s == "train" else 249, value=2), split, index) + for event in (split.change, index.change): event(preview, [split,index], [instruction,badge], api_name=False) + demo.load(preview, [split,index], [instruction,badge], api_name=False) + run.click(rollout, [split,index,backend,url,model,key], [transcript,score], concurrency_limit=2, api_name=False) +app = gr.mount_gradio_app(app, demo, path="/", css=UI_CSS) + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=7860, ws_ping_interval=20, ws_ping_timeout=None, + timeout_keep_alive=120) diff --git a/04-data-agent/hf/runtime/preflight.py b/04-data-agent/hf/runtime/preflight.py new file mode 100644 index 0000000..bcd145f --- /dev/null +++ b/04-data-agent/hf/runtime/preflight.py @@ -0,0 +1,34 @@ +"""Exercise the actual frozen import and CLI boundaries in a fresh HF Job.""" +from common import ROOT, RUN, TOOLS, TRAIN_PY, ENV_PY, configure, verify_bundle, write_json +import argparse +import json +import os +import subprocess +import sys +import time + + +def main(): + argparse.ArgumentParser().parse_known_args() + configure() + count = verify_bundle() + subprocess.run([str(ENV_PY), str(ROOT / "hf/runtime/ui_smoke.py"), "--help"], check=True, stdout=subprocess.DEVNULL) + scripts = [RUN / "source/HuggingEnvs/04-data-agent/train/train_harbor_multi.py", + TOOLS / "train_whitebox_daytona.py", TOOLS / "eval_whitebox_native.py", + RUN / "eval-source/eval_concurrent.py"] + for script in scripts: + subprocess.run([str(TRAIN_PY), str(script), "--help"], check=True, stdout=subprocess.DEVNULL) + subprocess.run([str(ENV_PY), "-c", "from openenv.harbor.serving import HarborService; from whitebox_bash.server.environment import WhiteBoxBashEnvironment; from daytona_whitebox_backend import load_frozen_tasks; assert len(load_frozen_tasks('train'))==1000; assert len(load_frozen_tasks('test'))==250"], check=True) + subprocess.run([str(ENV_PY), str(ROOT / "hf/runtime/check_task_schedule.py")], check=True) + result = {"passed": True, "verified_files": count, "checked_at": time.time(), + "bundle_sha256": os.environ["BUNDLE_SHA256"], "role": "preflight"} + out = ROOT / "outputs/preflight" + write_json(out / "result.json", result) + from huggingface_hub import HfApi + dest = "hf://buckets/" + os.environ["ARTIFACT_BUCKET"] + "/" + os.environ["RUN_ID"] + "/preflight" + HfApi().sync_bucket(str(out), dest, quiet=True) + print(json.dumps(result), flush=True) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/hf/runtime/provider_demo.py b/04-data-agent/hf/runtime/provider_demo.py new file mode 100644 index 0000000..c466828 --- /dev/null +++ b/04-data-agent/hf/runtime/provider_demo.py @@ -0,0 +1,158 @@ +"""Optional HF-signed-in demos; these conversations are separate from eval ledgers.""" +import inspect +import json +import os +import time +from typing import get_type_hints + +import gradio as gr + +from inference_providers import catalog, credentials, visitor_token + + +def enabled(): + # Never expose Gradio's local mocked-login flow in an unconfigured deployment. + return os.environ.get("SYSTEM") == "spaces" and all(os.environ.get(k) for k in + ("SPACE_ID", "OAUTH_CLIENT_ID", "OAUTH_CLIENT_SECRET", "OAUTH_SCOPES", "OPENID_PROVIDER_URL")) + + +def provider_choices(): + try: + providers = sorted({p for p, _ in catalog.rows()}) + return gr.update(choices=providers, value=None), gr.update(choices=[], value=None) + except Exception: + raise gr.Error("The HF model catalog is unavailable. Try loading it again.") from None + + +def model_choices(provider): + if not provider: + return gr.update(choices=[], value=None) + try: + models = sorted(m for p, m in catalog.rows() if p == provider) + return gr.update(choices=models, value=None) + except Exception: + raise gr.Error("The HF model catalog is unavailable. Try loading it again.") from None + + +def model_details(provider, model): + if not provider or not model: + return "Choose a provider and model." + try: + row = catalog.rows()[(provider, model)] + except (KeyError, ValueError): + return "This model is no longer available from that provider. Refresh the catalog." + price = row.get("pricing", {}) + text = "Tool calling supported. " + if "input" in price and "output" in price: + text += f"Per million tokens: ${price['input']:g} input · ${price['output']:g} output. " + if row.get("context_length"): + text += f"Context: {row['context_length']:,} tokens. " + return text + "Usage is charged to your HF account." + + +def selected(oauth, provider, model): + try: + visitor_token(oauth) + return catalog.select(provider, model) + except ValueError as exc: + raise gr.Error(str(exc)) from None + + +def controls(): + gr.LoginButton() + gr.Markdown("Sign in and choose a model that can use tools. Inference uses your HF account's credits; " + "a demo runs for at most 10 minutes and 17 agent turns.") + refresh = gr.Button("Load available models") + with gr.Row(): + provider = gr.Dropdown([], label="Inference provider", interactive=True) + model = gr.Dropdown([], label="Model", interactive=True) + detail = gr.Markdown("Choose a provider and model.") + refresh.click(provider_choices, outputs=[provider, model], api_visibility="private") + provider.change(model_choices, provider, model, api_visibility="private") + model.change(model_details, [provider, model], detail, api_visibility="private") + return provider, model + + +def blackbox(split, index, harness, provider, model, oauth_token: gr.OAuthToken): + from environment_ui import blackbox_run, LOCAL + target = selected(oauth_token, provider, model) + with credentials.issue(oauth_token, target) as key: + yield from blackbox_run(split, index, harness, LOCAL + "/hf-inference/v1", target, key) + + +def tool_schemas(env): + """Derive the demo schema from the environment's existing declared tool surface.""" + from whitebox_bash.tools import specs_for + from pydantic import ConfigDict, create_model + tools, methods = [], {} + for spec in specs_for("bash,seta"): + method = getattr(env, spec.name) + params = inspect.signature(method).parameters + hints = get_type_hints(method) + arguments = create_model(spec.name, __config__=ConfigDict(extra="forbid"), **{ + name: (hints[name], ... if p.default is inspect.Parameter.empty else p.default) + for name, p in params.items()}) + tools.append({"type": "function", "function": {"name": spec.name, "description": spec.summary, + "parameters": arguments.model_json_schema()}}) + methods[spec.name] = method + return tools, methods + + +def whitebox(ui, split, index, provider, model, oauth, request): + from openai import OpenAI + from environment_ui import LOCAL + target = selected(oauth, provider, model) + # Own the same browser workspace lock as manual actions for the whole episode. + prompt, _, _ = ui.start(split, index, request) + session = ui.session(request) + with session.lock: + try: + env = session.env + if env is None: + raise gr.Error("The workspace was closed. Start the agent again.") + tools, methods = tool_schemas(env) + messages = [{"role": "system", "content": "You are a data-analysis agent in an isolated workspace. " + "Use the tools to inspect the data and compute the answer. Call submit_solution with the answer itself."}, + {"role": "user", "content": prompt}] + deadline = time.monotonic() + 600 + yield "Starting the agent…", {"state": "running", "model": target} + with credentials.issue(oauth, target) as key, OpenAI(base_url=LOCAL + "/hf-inference/v1", api_key=key, + timeout=120, max_retries=0) as client: + for turn in range(17): + if time.monotonic() >= deadline: + break + response = client.chat.completions.create(model=target, messages=messages, tools=tools, + tool_choice="auto", max_tokens=4096, temperature=0.8, + timeout=min(120, deadline-time.monotonic())) + msg = response.choices[0].message + messages.append(msg.model_dump(exclude_none=True)) + for call in msg.tool_calls or []: + name = call.function.name + try: + if name not in methods: + raise ValueError("Unknown tool") + args = json.loads(call.function.arguments) + if not isinstance(args, dict): + raise ValueError("Tool arguments must be an object") + result = str(methods[name](**args)) + except (ValueError, TypeError): + result = "[error] Invalid tool name or arguments; use the provided schema." + messages.append({"role": "tool", "tool_call_id": call.id, "content": result}) + if env._reward is not None: + break + session.used_at = time.monotonic() + yield json.dumps(messages, indent=2, ensure_ascii=False)[-120000:], { + "state": "running", "model": target, "turns": turn + 1} + if not msg.tool_calls or env._reward is not None: + break + reward = env.get_reward() + yield json.dumps(messages, indent=2, ensure_ascii=False)[-120000:], { + "state": "finished", "reward": reward, "model": target, + "capture_level": "text", "training_eligible": False} + except gr.Error: + raise + except Exception as exc: + raise gr.Error(f"The demo stopped ({type(exc).__name__}). Check your HF inference credits or try another model.") from None + finally: + ui.dispose(session) + session.used_at = time.monotonic() diff --git a/04-data-agent/hf/runtime/qualify_training.py b/04-data-agent/hf/runtime/qualify_training.py new file mode 100644 index 0000000..280f867 --- /dev/null +++ b/04-data-agent/hf/runtime/qualify_training.py @@ -0,0 +1,96 @@ +"""Run both disposable optimizer smokes without restarting active eval Spaces.""" +import argparse +import json +import os +from pathlib import Path +import sys +import time + +from common import ROOT, write_json +from checkpoint_store import download_json +from coordinator import TERMINAL + + +def run(output): + from huggingface_hub import HfApi + from huggingface_hub.errors import EntryNotFoundError + sys.path.insert(0, str(ROOT / "hf")) + import deploy + api = HfApi() + config = json.loads((ROOT / "hf/configs/deployment.json").read_text()) + policy = config["training_smoke"] + output.mkdir(parents=True, exist_ok=True) + bundle = json.loads(Path("/bundle/bundle.json").read_text()) + bundle.update(repo=os.environ["BUNDLE_REPO"], revision=os.environ["BUNDLE_REVISION"]) + write_json(output / "bundle_uploaded.json", bundle) + namespace = config["namespace"] + destination = "hf://buckets/" + os.environ["ARTIFACT_BUCKET"] + "/" + os.environ["RUN_ID"] + "/jobs/" + os.environ["RUN_OWNER"] + state = {"phase": "qualifying", "bundle_sha256": bundle["sha256"], "jobs": {}, "passed": False, + "long_training_launched": False, "standalone_gate": "wait for baseline Daytona cohort to release Space capacity"} + + def persist(): + state["updated_at"] = time.time() + write_json(output / "qualification.json", state) + api.sync_bucket(str(output), destination, quiet=True) + + def launch(arm): + if arm in state["jobs"]: + return + matches = [j for j in api.list_jobs(namespace=namespace, labels={"role": "train", "arm": arm, "phase": "smoke"}) + if j.environment.get("BUNDLE_SHA256") == bundle["sha256"]] + if len(matches) > 1: + raise RuntimeError("Multiple smoke jobs for the same arm and bundle") + if matches: + state["jobs"][arm] = {"id": matches[0].id, "stage": matches[0].status.stage} + return + state["jobs"][arm] = {"stage": "submitting"} + persist() + args = argparse.Namespace(role="train", arm=arm, phase="smoke", flavor=config["compute"]["training_flavor"], timeout="2h", + dp=1, limit=0, resume_eval_owner=None, training_job=None, baseline_job=None, smoke_job=None, + baseline_job_map=None, space_bundle_sha=policy["space_bundle_pins"][arm]) + secrets = {k: os.environ[k] for k in ["HF_TOKEN", "DAYTONA_API_KEY", "DAYTONA_API_URL", "DAYTONA_TARGET"] if os.environ.get(k)} + value = deploy.submit(api, config, secrets, output, args) + state["jobs"][arm] = {"id": value["id"], "stage": value["stage"]} + + try: + peers = [j for j in api.list_jobs(namespace=namespace, labels={"role": "coordinator", "phase": "qualify"}) + if j.status.stage not in TERMINAL and j.environment.get("BUNDLE_SHA256") == bundle["sha256"]] + if peers and min(peers, key=lambda j: j.id).environment["RUN_OWNER"] != os.environ["RUN_OWNER"]: + raise RuntimeError("Another coordinator owns qualification for this bundle") + launch("whitebox") + while True: + if "opencode" not in state["jobs"]: + baseline = api.inspect_job(job_id=policy["opencode_wait_for_daytona_baseline_job"], namespace=namespace) + source = "hf://buckets/" + baseline.environment["ARTIFACT_BUCKET"] + "/" + baseline.environment["RUN_ID"] + "/jobs/" + baseline.environment["RUN_OWNER"] + "/daytona" + try: + scores = download_json(source, "scores.json", output / "daytona-baseline-scores.json", api) + except EntryNotFoundError: + scores = {} + state["daytona_baseline_graded"] = scores.get("graded_cells", 0) + if scores.get("comparison_ready") and scores.get("expected_cells") == 250: + launch("opencode") + elif baseline.status.stage in TERMINAL: + raise RuntimeError("Daytona baseline ended without complete TiTO-qualified coverage") + complete = len(state["jobs"]) == 2 + for arm, item in state["jobs"].items(): + job = api.inspect_job(job_id=item["id"], namespace=namespace) + item["stage"] = job.status.stage + if job.status.stage in TERMINAL and job.status.stage != "COMPLETED": + raise RuntimeError(f"{arm} optimizer smoke ended {job.status.stage}") + complete = complete and job.status.stage == "COMPLETED" + if job.status.stage == "COMPLETED" and "proof" not in item: + source = "hf://buckets/" + job.environment["ARTIFACT_BUCKET"] + "/" + job.environment["RUN_ID"] + "/jobs/" + job.environment["RUN_OWNER"] + proof = download_json(source, "training_smoke_verified.json", output / f"{arm}-verified.json", api) + if not proof.get("passed") or proof.get("bundle_sha256") != bundle["sha256"]: + raise RuntimeError("Smoke evidence does not match the qualification bundle") + item["proof"] = proof + persist() + if complete: + state.update(phase="complete", passed=True) + persist() + return + time.sleep(60) + except Exception as exc: + state.update(phase="needs_attention", error_type=type(exc).__name__, error=str(exc)) + persist() + raise diff --git a/04-data-agent/hf/runtime/service_contract.py b/04-data-agent/hf/runtime/service_contract.py new file mode 100644 index 0000000..5c7448f --- /dev/null +++ b/04-data-agent/hf/runtime/service_contract.py @@ -0,0 +1,32 @@ +"""Check the remote training API without launching a sandbox or inference request.""" +import asyncio +import json + + +def validate_tools(response, arm): + tools = response.get("data", {}).get("observation", {}).get("tools", []) + tool = next((item for item in tools if item.get("name") == "run_rollout"), None) + if tool is None: + raise ValueError("Environment did not advertise run_rollout") + properties = tool.get("input_schema", {}).get("properties", {}) + required = {"sampling", "llm_url", "model"} + required |= {"agent_timeout_sec", "agent_step_limit"} if arm == "blackbox" else {"require_tokens", "agent_timeout_s"} + missing = required - properties.keys() + if missing: + raise ValueError("Deployed environment lacks training arguments: " + ", ".join(sorted(missing)) + + ". Deploy the matching environment bundle before starting training.") + return {"passed": True, "arm": arm, "arguments": sorted(properties)} + + +async def _probe(url, token, arm): + from websockets.asyncio.client import connect + url = url.rstrip("/").replace("https://", "wss://", 1).replace("http://", "ws://", 1) + headers = {"Authorization": "Bearer " + token} if token else {} + async with connect(url + "/ws", additional_headers=headers, open_timeout=30) as socket: + await socket.send(json.dumps({"type": "step", "data": {"type": "list_tools"}})) + response = json.loads(await asyncio.wait_for(socket.recv(), timeout=30)) + return validate_tools(response, arm) + + +def check(url, token, arm): + return asyncio.run(_probe(url, token, arm)) diff --git a/04-data-agent/hf/runtime/service_policy.py b/04-data-agent/hf/runtime/service_policy.py new file mode 100644 index 0000000..ee67861 --- /dev/null +++ b/04-data-agent/hf/runtime/service_policy.py @@ -0,0 +1,90 @@ +"""Per-rollout budgets and shared admission for the two environment Spaces.""" +from contextlib import asynccontextmanager +import asyncio +import os +from pathlib import Path +import threading +import time + + +def workload(dataset): + return "train" if Path(str(dataset).rstrip("/")).name == "train" else "eval" + + +def output_limit(dataset): + role = workload(dataset) + return int(os.environ.get("OPENENV_" + role.upper() + "_OUTPUT_TOKENS", + "16384" if role == "train" else "4096")) + + +class Admission: + """Bound active sandboxes, keeping slots that eval cannot consume for training. + + Synchronous native whitebox tools and async Harbor rollouts use the same accounting. + Waiting async callers never occupy the shared thread pool. + """ + def __init__(self, total, train_reserve): + if not 0 < train_reserve < total: + raise ValueError("Require 0 < training reservation < sandbox capacity") + self.total, self.train_reserve = total, train_reserve + self.active = {"train": 0, "eval": 0} + self.waiting = {"train": 0, "eval": 0} + self.condition = threading.Condition() + + def _available(self, role): + return (sum(self.active.values()) < self.total and + (role == "train" or self.active["eval"] < self.total - self.train_reserve)) + + def acquire(self, role, timeout=900): + with self.condition: + self.waiting[role] += 1 + try: + if not self.condition.wait_for(lambda: self._available(role), timeout): + raise TimeoutError("Shared sandbox capacity wait expired") + self.active[role] += 1 + finally: + self.waiting[role] -= 1 + + def release(self, role): + with self.condition: + if self.active[role] <= 0: + raise RuntimeError("Sandbox reservation released twice") + self.active[role] -= 1 + self.condition.notify_all() + + @asynccontextmanager + async def slot(self, dataset, timeout=900): + role = workload(dataset) + with self.condition: + self.waiting[role] += 1 + acquired = False + waiting = True + try: + deadline = time.monotonic() + timeout + while not acquired: + with self.condition: + if self._available(role): + self.active[role] += 1 + self.waiting[role] -= 1 + waiting = False + acquired = True + if not acquired: + if time.monotonic() >= deadline: + raise TimeoutError("Shared sandbox capacity wait expired") + await asyncio.sleep(0.1) + yield + finally: + if waiting: + with self.condition: + self.waiting[role] -= 1 + if acquired: + self.release(role) + + def snapshot(self): + with self.condition: + return {"capacity": self.total, "train_reserved": self.train_reserve, + "active": dict(self.active), "waiting": dict(self.waiting)} + + +admission = Admission(int(os.environ.get("SANDBOX_CAPACITY", "128")), + int(os.environ.get("TRAIN_RESERVED_SANDBOXES", "24"))) diff --git a/04-data-agent/hf/runtime/setup_pipeline.py b/04-data-agent/hf/runtime/setup_pipeline.py new file mode 100644 index 0000000..f1eaf21 --- /dev/null +++ b/04-data-agent/hf/runtime/setup_pipeline.py @@ -0,0 +1,191 @@ +"""Finish the two HF baselines, deploy the tested runtime, smoke, then start training.""" +import argparse +import json +import math +import os +from pathlib import Path +import shutil +import sys +import time + +from common import ROOT, ENV_PY, start, write_json +from checkpoint_store import download_json +from coordinator import TERMINAL +BUNDLE = Path("/bundle") + + +def run(output): + from huggingface_hub import HfApi + import httpx + sys.path.insert(0, str(ROOT / "hf")) + import deploy + api = HfApi() + config = json.loads((ROOT / "hf/configs/deployment.json").read_text()) + namespace = config["namespace"] + bundle = json.loads((BUNDLE / "bundle.json").read_text()) + bundle.update(repo=os.environ["BUNDLE_REPO"], revision=os.environ["BUNDLE_REVISION"]) + destination = ("hf://buckets/" + os.environ["ARTIFACT_BUCKET"] + "/" + os.environ["RUN_ID"] + + "/pipelines/" + bundle["sha256"]) + output.mkdir(parents=True, exist_ok=True) + try: + state = download_json(destination, "pipeline.json", output / "pipeline.json", api) + except Exception as exc: + from huggingface_hub.errors import EntryNotFoundError + if not isinstance(exc, EntryNotFoundError): + raise + state = {"bundle_sha256": bundle["sha256"], "jobs": {}, "phase": "waiting_baselines"} + + def persist(): + state["updated_at"] = time.time() + write_json(output / "pipeline.json", state) + api.sync_bucket(str(output), destination, include=["pipeline.json", "jobs/*.json", "launch-proofs/**", "ui-smoke.json"], quiet=True) + + def wait_until(check): + while not check(): + persist() + time.sleep(60) + persist() + + secrets = {k: os.environ[k] for k in ["HF_TOKEN", "DAYTONA_API_KEY", "DAYTONA_API_URL", "DAYTONA_TARGET", "OPENAI_API_KEY"] if os.environ.get(k)} + args_base = dict(dp=1, limit=0, resume_eval_owner=None, training_job=None, baseline_job=None, smoke_job=None) + + def ensure_job(role, arm, phase, **options): + key = ":".join([role, arm, phase]) + jobs = [j for j in api.list_jobs(namespace=namespace, labels={"role": role, "arm": arm, "phase": phase, + "run": config["run_id"]}) if j.environment.get("BUNDLE_SHA256") == bundle["sha256"]] + if len(jobs) > 1: + raise RuntimeError(f"Multiple matching {key} jobs require reconciliation") + if jobs: + state["jobs"][key] = {"id": jobs[0].id, "stage": jobs[0].status.stage} + persist() + return jobs[0].id + if key in state["jobs"]: + raise RuntimeError(f"Unresolved {key} submission intent; inspect HF Jobs before retrying") + state["jobs"][key] = {"stage": "submitting"} + persist() + args = argparse.Namespace(**{**args_base, "role": role, "arm": arm, "phase": phase, **options}) + value = deploy.submit(api, config, secrets, output, args) + state["jobs"][key] = {"id": value["id"], "stage": value["stage"]} + persist() + return value["id"] + + def completed(ids): + done = True + for arm, job_id in ids.items(): + job = api.inspect_job(job_id=job_id, namespace=namespace) + state.setdefault("job_status", {})[job_id] = job.status.stage + if job.status.stage in TERMINAL and job.status.stage != "COMPLETED": + raise RuntimeError(f"{arm} job {job_id} ended as {job.status.stage}; artifacts require inspection") + done = done and job.status.stage == "COMPLETED" + return done + + try: + peers = [j for j in api.list_jobs(namespace=namespace, labels={"role": "coordinator", "phase": "setup", "run": config["run_id"]}) + if j.status.stage not in TERMINAL and j.environment.get("BUNDLE_SHA256") == bundle["sha256"]] + if peers and min(peers, key=lambda j: j.id).environment["RUN_OWNER"] != os.environ["RUN_OWNER"]: + raise RuntimeError("Another setup pipeline owns this bundle") + baselines = json.loads(os.environ["BASELINE_JOB_MAP"]) if os.environ.get("BASELINE_JOB_MAP") else config["pipeline"]["baseline_jobs"] + if set(baselines) != {"blackbox", "whitebox"}: + raise ValueError("Both baseline job IDs are required") + state["baseline_jobs"] = baselines + wait_until(lambda: completed(baselines)) + for arm, job_id in baselines.items(): + job = api.inspect_job(job_id=job_id, namespace=namespace) + source = "hf://buckets/" + os.environ["ARTIFACT_BUCKET"] + "/" + os.environ["RUN_ID"] + "/jobs/" + job.environment["RUN_OWNER"] + scores = download_json(source, "canonical_scores.json", output / f"baseline-{arm}.json", api) + if not scores["comparison_ready"] or scores["arm"] != arm: + raise ValueError("Baseline coverage, token or version gate failed") + state["phase"] = "deploying_training_runtime" + persist() + bundle_dir = output / "bundle" + bundle_dir.mkdir(exist_ok=True) + for name in ["bundle.tar.gz", "bundle.json"]: + shutil.copy2(BUNDLE / name, bundle_dir / name) + write_json(output / "bundle_uploaded.json", bundle) + + def deployments(): + result = {} + for arm, repo in config["resources"]["environment_spaces"].items(): + host = api.space_info(repo).host.rstrip("/") + if not host.startswith("https://"): + host = "https://" + host + response = httpx.get(host + "/deployment", timeout=30) + response.raise_for_status() + result[arm] = response.json() + return result + + if not state.get("deployed"): + wait_until(lambda: all(sum(d["admission"]["active"].values()) == 0 for d in deployments().values())) + current = deployments() + needed = {arm for arm, d in current.items() if d["bundle_sha256"] != bundle["sha256"]} + if needed: + deploy.spaces(api, config, secrets, output, needed) + def ready(): + try: + return all(d["bundle_sha256"] == bundle["sha256"] for d in deployments().values()) + except (httpx.HTTPError, ValueError): + return False + wait_until(ready) + state["deployed"] = True + if not state.get("ui_passed"): + proc = start([ENV_PY, ROOT / "hf/runtime/ui_smoke.py", "--out", output / "ui-smoke.json"], output / "ui-smoke.log") + if proc.wait(timeout=600) != 0: + raise RuntimeError("Deployed train/test UI isolation test failed") + state["ui_passed"] = True + state["phase"] = "optimizer_smokes" + persist() + smokes = {arm: ensure_job("train", arm, "smoke", flavor=config["compute"]["training_flavor"], timeout="2h") for arm in baselines} + wait_until(lambda: completed(smokes)) + state["phase"] = "starting_training" + persist() + trains = {arm: ensure_job("train", arm, "long", flavor=config["compute"]["training_flavor"], timeout="24h", baseline_job=baselines[arm], smoke_job=smokes[arm]) + for arm in baselines} + for arm, job_id in trains.items(): + ensure_job("coordinator", arm, "long", flavor="cpu-upgrade", timeout="36h", training_job=job_id) + state.update(phase="training_submitted", training_jobs=trains, smoke_jobs=smokes, passed=False) + persist() + while True: + finished = True + progress = {} + for arm, job_id in trains.items(): + job = api.inspect_job(job_id=job_id, namespace=namespace) + if job.status.stage in TERMINAL and job.status.stage != "COMPLETED": + raise RuntimeError(f"Training job {job_id} ended as {job.status.stage}") + finished = finished and job.status.stage == "COMPLETED" + name = "audit/metrics.jsonl" if arm == "blackbox" else "run/metrics.jsonl" + path = output / "metrics" / (arm + ".jsonl") + path.parent.mkdir(exist_ok=True) + prefix = config["run_id"] + "/jobs/" + job.environment["RUN_OWNER"] + from huggingface_hub.errors import EntryNotFoundError + try: + api.download_bucket_files(os.environ["ARTIFACT_BUCKET"], + files=[(prefix + "/" + name, str(path))], raise_on_missing_files=True) + except EntryNotFoundError: + progress[arm] = {"job_id": job_id, "stage": job.status.stage, "step": 0} + continue + rows = [] + for line in path.read_text().splitlines(): + try: + row = json.loads(line) + except json.JSONDecodeError: + continue + if "step" in row and "grad_norm" in row: + rows.append(row) + if any(not math.isfinite(float(r[k])) for r in rows for k in ["loss", "grad_norm"] if k in r): + raise RuntimeError(f"Non-finite optimizer metrics in training job {job_id}") + latest = rows[-1] if rows else {"step": 0} + rewards = [r["reward"] for r in rows[-20:] if isinstance(r.get("reward"), (float, int))] + progress[arm] = {"job_id": job_id, "stage": job.status.stage, "step": latest["step"], + "latest_reward": latest.get("reward"), "reward_mean_last20": sum(rewards)/len(rewards) if rewards else None, + "nonzero_gradient_updates": sum(r["grad_norm"] > 0 for r in rows)} + stable = len(progress) == 2 and all(p["step"] >= 10 and p.get("nonzero_gradient_updates", 0) > 0 for p in progress.values()) + state.update(phase="completed" if finished else "training_active" if stable else "training_startup", + training_progress=progress, passed=stable) + persist() + if finished: + return + time.sleep(600 if stable else 60) + except Exception as exc: + state.update(phase="needs_attention", error_type=type(exc).__name__, error=str(exc), passed=False) + persist() + raise diff --git a/04-data-agent/hf/runtime/smoke_opencode_backends.py b/04-data-agent/hf/runtime/smoke_opencode_backends.py new file mode 100644 index 0000000..e7272f9 --- /dev/null +++ b/04-data-agent/hf/runtime/smoke_opencode_backends.py @@ -0,0 +1,41 @@ +"""Exercise the standalone sandbox protocol, deleting only sandboxes created by this invocation.""" +import argparse +import concurrent.futures +import json +import time +from common import configure, write_json +configure() +from data_agent_env.sandbox import build_backend, DEFAULT_IMAGE + + +def check(name): + start=time.monotonic();sandbox=None;result={"backend":name} + try: + sandbox=build_backend(name,image=DEFAULT_IMAGE).create(timeout_s=600,metadata={"purpose":"protocol-smoke"}) + result["sandbox_id"]=sandbox.sandbox_id + content='literal `text` $(echo example)\nhello' + sandbox.write_text('/tmp/protocol/test file.txt',content) + assert sandbox.exists('/tmp/protocol/test file.txt') + assert sandbox.read_text('/tmp/protocol/test file.txt')==content + command=sandbox.exec('printf "%s" "$PROTOCOL_TRANSIENT"',envs={'PROTOCOL_TRANSIENT':'fixture-value'}) + assert command.exit_code==0 and command.stdout=='fixture-value' + assert sandbox.exec('test -z "$PROTOCOL_TRANSIENT"').exit_code==0 + assert sandbox.start_bg('sleep 1; exit 7').wait(timeout=30)==7 + result['passed']=True + except Exception as exc: + result.update(passed=False,error_type=type(exc).__name__) + finally: + if sandbox is not None: + try:sandbox.kill();result['deleted']=True + except Exception as exc:result.update(passed=False,cleanup_error_type=type(exc).__name__) + result['elapsed_s']=time.monotonic()-start + return result + +if __name__=='__main__': + parser=argparse.ArgumentParser(description=__doc__) + parser.add_argument('--backends',default='daytona,hf');parser.add_argument('--out',required=True) + args=parser.parse_args() + with concurrent.futures.ThreadPoolExecutor(max_workers=2) as pool: + records=list(pool.map(check,args.backends.split(','))) + write_json(args.out,records);print(json.dumps(records)) + if not all(r['passed'] and r.get('deleted') for r in records):raise SystemExit(1) diff --git a/04-data-agent/hf/runtime/space_app.py b/04-data-agent/hf/runtime/space_app.py new file mode 100644 index 0000000..b860b48 --- /dev/null +++ b/04-data-agent/hf/runtime/space_app.py @@ -0,0 +1,104 @@ +"""Run a private environment Space with the frozen native OpenEnv services.""" +import os +import hmac +from common import ROOT, RUN, configure + +configure() +arm = os.environ["COMPARISON_ARM"] +mode = "shared" +owner = os.environ.get("SPACE_ID", f"{arm}-{mode}").replace("/", "-") +os.environ["RUN_OWNER"] = owner +trials = ROOT / "space-trials" / owner +trials.mkdir(parents=True, exist_ok=True) +os.environ["OPENENV_HARBOR_TRIALS_DIR"] = str(trials) +os.environ["DAYTONA_WHITEBOX_TRIALS"] = str(trials) +os.environ["WHITE_BOX_BASH_TASK_SOURCE"] = "harbor-frozen" +os.environ["OPENENV_CAPTURE_TRANSPORT"] = "tunnel" +os.environ["OPENENV_DATASETS"] = ",".join(str(RUN / "datasets" / s) for s in ["train", "test"]) +os.environ.setdefault("OPENENV_MODEL", "Qwen/Qwen3.5-2B") +os.environ.setdefault("OPENENV_MAX_OUTPUT_TOKENS", "16384") +os.environ.setdefault("OPENENV_EXPOSE", "gradio") +os.environ.setdefault("MAX_CONCURRENT_ENVS", "1024") +os.environ.setdefault("WHITE_BOX_BASH_MAX_CONCURRENT_ENVS", os.environ["MAX_CONCURRENT_ENVS"]) +os.environ.setdefault("WHITE_BOX_BASH_MAX_SESSIONS", os.environ.get("SANDBOX_CAPACITY", "128")) +os.environ["ENABLE_WEB_INTERFACE"] = "false" + +if arm == "blackbox": + from harbor_service import install + install() + from harbor_env.server.app import app +elif arm == "whitebox": + from whitebox_bash.server.app import app +else: + raise ValueError("Unknown comparison arm") + + +@app.middleware("http") +async def protect_run_artifacts(request, call_next): + # A protected HF Space has a public app. Keep cross-session run artifacts + # behind the same bearer credential already sent by the training/eval bridge. + if request.url.path == "/diagnostics" or request.url.path.startswith("/trial/"): + from fastapi.responses import JSONResponse + secret = os.environ.get("HF_TOKEN", "") + provided = request.headers.get("Authorization", "") + if not secret or not hmac.compare_digest(provided, "Bearer " + secret): + return JSONResponse({"detail": "Authentication required"}, status_code=401) + return await call_next(request) + + +@app.get("/deployment") +def deployment(): + from service_policy import admission + return {"arm": arm, "mode": mode, "owner": owner, "train_tasks": 1000, "test_tasks": 250, + "sandbox": "daytona", "bundle_sha256": os.environ.get("BUNDLE_SHA256"), + "max_concurrent_envs": int(os.environ["MAX_CONCURRENT_ENVS"]), + "admission": admission.snapshot(), "output_tokens": {"train": 16384, "eval": 4096}, + "interactive_ui": True, "trackio": False} + + +@app.get("/trial/{name}/result") +def trial_result(name: str): + # Bearer authentication is enforced above. Expose only the native result metadata + # needed to verify harness versions; arbitrary filesystem access is intentionally absent. + import json + from pathlib import Path + from fastapi import HTTPException + if Path(name).name != name or name in {".", ".."}: + raise HTTPException(400) + path = trials / name / "result.json" + if not path.is_file(): + raise HTTPException(404) + return json.loads(path.read_text()) + + +@app.get("/diagnostics") +async def diagnostics(): + # No frame locals, prompts, credentials, or answer files. Useful for distinguishing + # remote execution from response delivery when a long RPC stops making progress. + import asyncio + from service_policy import admission + tasks = [] + for task in asyncio.all_tasks(): + tasks.append({"name": task.get_name(), "stack": [f"{f.f_code.co_name}:{f.f_lineno}" for f in task.get_stack(limit=4)]}) + rows = [] + for path in trials.iterdir(): + if path.is_dir(): + rows.append({"trial": path.name, "result_ready": (path / "result.json").exists(), + "cleanup_ready": (path / "cleanup.json").exists()}) + return {"admission": admission.snapshot(), "async_tasks": tasks, "trials": rows[-100:]} + + +@app.on_event("startup") +async def configure_thread_capacity(): + import anyio.to_thread + anyio.to_thread.current_default_thread_limiter().total_tokens = 512 + + +from environment_ui import mount_ui +app = mount_ui(app, arm) + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=7860, ws_ping_interval=20, ws_ping_timeout=None, + timeout_keep_alive=120, log_level="info") diff --git a/04-data-agent/hf/runtime/space_smoke.py b/04-data-agent/hf/runtime/space_smoke.py new file mode 100644 index 0000000..52f2159 --- /dev/null +++ b/04-data-agent/hf/runtime/space_smoke.py @@ -0,0 +1,57 @@ +"""Real private-Space task discovery and Daytona tool/verifier checks, without a model.""" +from common import RUN, ROOT, ENV_PY, configure, ready, start, write_json +import argparse +import concurrent.futures +import json +import os +from pathlib import Path +import signal + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--arm", choices=["blackbox", "whitebox"], required=True) + p.add_argument("--out", type=Path, required=True) + a = p.parse_args() + configure() + a.out.mkdir(parents=True, exist_ok=True) + process = start([ENV_PY, ROOT / "hf/runtime/auth_bridge.py"], a.out / "bridge.log") + try: + ready("http://127.0.0.1:8100/health", process, seconds=60) + if a.arm == "whitebox": + from native_tool_smoke import one + with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool: + results = list(pool.map(lambda i: one("http://127.0.0.1:8100", i // 2, bool(i % 2), RUN), range(8))) + report = {"passed": all(r["passed"] for r in results), "cases": results} + else: + from openenv.harbor.client import HarborEnv + from openenv.harbor.tasks import read_instruction + client = HarborEnv(base_url="http://127.0.0.1:8100") + checks = [] + try: + for split, expected in [("train", 1000), ("test", 250)]: + spec = "/workspace/repro/experiments/daytona_harness_comparison/logs/20260915/datasets/" + split + assert client.num_tasks(spec) == expected + manifest = json.loads((RUN / f"{split}_manifest.json").read_text()) + catalog = sorted(manifest["tasks"], key=lambda row: row["name"]) + for index in [0, expected - 1]: + remote = client.get_task(spec, index) + actual = remote.model_dump() + local = RUN / "datasets" / split / "tasks" / catalog[index]["name"] + assert actual["task_name"] == catalog[index]["name"] + assert actual["instruction"] == read_instruction(local) + checks.append({"split": split, "tasks": expected, "edge_instructions_match": True}) + finally: + client.close() + report = {"passed": True, "cases": checks} + write_json(a.out / "result.json", report) + print(json.dumps(report), flush=True) + if not report["passed"]: + raise SystemExit(2) + finally: + if process.poll() is None: + os.killpg(process.pid, signal.SIGTERM) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/hf/runtime/telemetry.py b/04-data-agent/hf/runtime/telemetry.py new file mode 100644 index 0000000..f18188b --- /dev/null +++ b/04-data-agent/hf/runtime/telemetry.py @@ -0,0 +1,48 @@ +"""Lightweight inference/GPU samples outside the evaluator and optimizer loops.""" +import json +import re +import subprocess +import threading +import time + +METRICS = ("num_requests_running", "num_requests_waiting", "kv_cache_usage_perc", + "prompt_tokens_total", "generation_tokens_total", "request_success_total", + "request_queue_time_seconds_sum", "request_queue_time_seconds_count", + "time_to_first_token_seconds_sum", "time_to_first_token_seconds_count") + + +class Telemetry: + def __init__(self, output, base_url="http://127.0.0.1:8000"): + self.path = output / "inference_metrics.jsonl" + self.base_url = base_url.rstrip("/") + self.stop_event = threading.Event() + self.thread = threading.Thread(target=self.run, daemon=True, name="inference-telemetry") + + def run(self): + import httpx + with httpx.Client(timeout=5) as client: + while not self.stop_event.is_set(): + row = {"time": time.time()} + try: + response = client.get(self.base_url + "/metrics").raise_for_status() + samples = {} + for line in response.text.splitlines(): + match = re.match(r"vllm:([^ {]+)(?:\{[^}]*\})? ([^ ]+)", line) + if match and match[1] in METRICS: + samples[match[1]] = samples.get(match[1], 0) + float(match[2]) + row["vllm"] = samples + gpu = subprocess.run(["nvidia-smi", "--query-gpu=index,utilization.gpu,memory.used,memory.total,power.draw", + "--format=csv,noheader,nounits"], capture_output=True, text=True, timeout=5) + row["gpu_csv"] = gpu.stdout.strip().splitlines() if gpu.returncode == 0 else [] + except Exception as exc: + row["error_type"] = type(exc).__name__ + with self.path.open("a") as stream: + stream.write(json.dumps(row) + "\n") + self.stop_event.wait(30) + + def start(self): + self.thread.start() + + def finish(self): + self.stop_event.set() + self.thread.join(timeout=15) diff --git a/04-data-agent/hf/runtime/training_capture_audit.py b/04-data-agent/hf/runtime/training_capture_audit.py new file mode 100644 index 0000000..1c523d8 --- /dev/null +++ b/04-data-agent/hf/runtime/training_capture_audit.py @@ -0,0 +1,73 @@ +"""Reconcile exact supervised positions with native TRL rows and optimizer receipts.""" +import collections +import hashlib +import json +from pathlib import Path +import struct + +from common import write_json + + +def positions(ids, masks, logprobs): + result = collections.Counter() + context = hashlib.sha256() + for token, mask, lp in zip(ids, masks, logprobs, strict=True): + if mask: + result[(context.hexdigest(), token, float(lp).hex())] += 1 + context.update(struct.pack(">q", token)) + return result + + +def audit_async(directory, arm): + from trl.experimental.async_grpo.openenv_harness import _turns_from_trace + from trl.experimental.async_grpo.async_rollout_worker import _chain_to_sequences + from openenv.core.harness.capture.validate import validate_training_turn + directory = Path(directory) + reports = {} + for path in sorted((directory / "rollouts").glob("*.json")): + record = json.loads(path.read_text()) + raw = record.get("result") + if not raw: + continue + if arm == "opencode": + from data_agent_env import opencode_agent_turns, to_trace_entries + from data_agent_env.models import DataAgentRolloutResult + result = DataAgentRolloutResult.model_validate(raw) + entries = opencode_agent_turns(to_trace_entries(result)) + else: + from openenv.harbor.models import HarborRolloutResult + from harbor_env.harness import to_trace_entries + result = HarborRolloutResult.model_validate(raw) + entries = to_trace_entries(result) + if not entries: + continue + expected = collections.Counter() + for entry in entries: + p, c, lp, mask = (entry[k] for k in ("prompt_token_ids", "completion_token_ids", "per_token_logps", "loss_mask")) + validate_training_turn(p, c, lp, mask) + expected.update(positions(p + c, mask, [0.] * len(p) + lp)) + rows, _ = _chain_to_sequences(_turns_from_trace(entries), record["episode_id"], fork_threshold=0) + retained = collections.Counter() + for row in rows: + retained.update(positions(row.input_ids, row.completion_mask, row.old_log_probs)) + passed = bool(expected) and retained == expected and any(lp < 0 for e in entries for lp in e["per_token_logps"]) + assert passed, f"Exact supervised positions were not retained: {path.name}" + reports[record["episode_id"]] = {"rows": len(rows), "eligible_tokens": sum(expected.values()), + "retained_tokens": sum(retained.values()), "rows_over_token_budget": sum(len(r.input_ids) > 131072 for r in rows), + "tito_pass": passed} + receipts = [json.loads(line) for line in (directory / "optimizer_rollouts.jsonl").read_text().splitlines() if line.strip()] + consumed = 0 + for receipt in receipts: + for row in receipt["rollouts"]: + expected = reports[row["rollout_id"]] + assert row["rows"] == expected["rows"] + assert row["supervised_tokens"] == expected["retained_tokens"] + consumed += 1 + assert consumed and reports, "No audited rollouts reached the optimizer" + summary = {arm: {"completed_results": len(reports), "tito_pass": sum(r["tito_pass"] for r in reports.values()), + "eligible_tokens": sum(r["eligible_tokens"] for r in reports.values()), + "retained_tokens": sum(r["retained_tokens"] for r in reports.values()), + "rows_over_token_budget": sum(r["rows_over_token_budget"] for r in reports.values()), + "optimizer_rollouts_verified": consumed}} + write_json(directory / "tito_summary.json", summary) + return summary diff --git a/04-data-agent/hf/runtime/training_smoke.py b/04-data-agent/hf/runtime/training_smoke.py new file mode 100644 index 0000000..6c498ef --- /dev/null +++ b/04-data-agent/hf/runtime/training_smoke.py @@ -0,0 +1,57 @@ +"""Validate real native optimizer/save/remote-resume evidence before an HF long run.""" +import json +import math +import os +from pathlib import Path + +from common import configure, write_json + + +def validate(output, arm): + configure() + from checkpoint_store import verify + from checkpoint_artifacts import resume_info + output = Path(output) + run = output / "run" + markers = {step: verify(run / f"checkpoint-{step}") for step in (2, 4)} + for step, marker in markers.items(): + assert marker["step"] == step and marker["arm"] == arm + assert marker["bundle_sha256"] == os.environ["BUNDLE_SHA256"] + origin = json.loads((output / "remote-resume/checkpoint-2.remote-origin.json").read_text()) + assert origin == markers[2], "Resume must come from the remotely verified step-2 checkpoint" + metric_file = output / "audit/metrics.jsonl" if arm in {"blackbox", "opencode"} else run / "metrics.jsonl" + rows = [json.loads(line) for line in metric_file.read_text().splitlines() if line.strip()] + updates = [row for row in rows if "grad_norm" in row] + assert {int(row["step"]) for row in updates} >= {1, 2, 3, 4} + assert all(math.isfinite(v) for row in updates for v in row.values() if isinstance(v, float)) + assert any(row["grad_norm"] > 0 for row in updates), "No learning signal observed" + if arm in {"blackbox", "opencode"}: + restored = output / "remote-resume/checkpoint-2" + info = resume_info(restored, origin["base_model"], origin["base_revision"]) + expected = f"resume checkpoint step=2, next schedule group={info['group_offset']}" + assert expected in (output / "train-resumed.log").read_text() + from training_capture_audit import audit_async + audit = audit_async(output / "audit", arm) + assert audit and all(v["tito_pass"] == v["completed_results"] and + v["retained_tokens"] == v["eligible_tokens"] and + v["rows_over_token_budget"] == 0 for v in audit.values()) + weights = [name for name in markers[2]["files"] if name.endswith(".safetensors")] + assert weights and any(markers[2]["files"][name] != markers[4]["files"][name] for name in weights), "Weights did not change after resume" + else: + initial = json.loads((run / "optimizer_evidence_from_0.json").read_text()) + resumed = json.loads((run / "optimizer_evidence_from_2.json").read_text()) + assert initial["final_step"] == 2 and resumed["final_step"] == 4 + assert initial["weights_changed"] or resumed["weights_changed"] + assert initial["final_weight_digest"] == resumed["initial_weight_digest"] + audit = [json.loads(line) for line in (run / "token_audit.jsonl").read_text().splitlines() if line.strip()] + assert {row["step"] for row in audit} >= {0, 1, 2, 3} + assert all(row["tito_pass"] and row["supervised"] > 0 for record in audit for row in record["rows"]) + import torch + optimizer = torch.load(run / "checkpoint-4/optimizer.pt", map_location="cpu", weights_only=False) + assert optimizer["state"] and optimizer["param_groups"] + report = {"arm": arm, "passed": True, "bundle_sha256": os.environ["BUNDLE_SHA256"], + "optimizer_steps": [1, 2, 3, 4], "native_optimizer_state_verified": True, + "remote_restore_verified": True, "tito_pass": True, "weights_updated": True, + "nonzero_gradient_updates": sum(row["grad_norm"] > 0 for row in updates)} + write_json(output / "training_smoke_verified.json", report) + return report diff --git a/04-data-agent/hf/runtime/ui_smoke.py b/04-data-agent/hf/runtime/ui_smoke.py new file mode 100644 index 0000000..92c53a0 --- /dev/null +++ b/04-data-agent/hf/runtime/ui_smoke.py @@ -0,0 +1,56 @@ +"""Exercise the deployed UIs and two isolated Daytona sessions using Gradio's client.""" +from concurrent.futures import ThreadPoolExecutor +import json +import os +from pathlib import Path +import time + +from gradio_client import Client + + +def main(): + import argparse + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--env-file") + p.add_argument("--out", type=Path, required=True) + a = p.parse_args() + from dotenv import dotenv_values + values = dotenv_values(a.env_file) if a.env_file else {} + token = values.get("HF_API_KEY") or os.environ["HF_TOKEN"] + repo = "HuggingEnvs/data-agent-seta-whitebox-env" + clients = [Client(repo, token=token, verbose=False) for _ in range(2)] + + def one(i): + c = clients[i] + split, index = ("train", 895) if i == 0 else ("test", 166) + instruction, _ = c.predict(split, index, api_name="/preview_task") + try: + prompt, _, state = c.predict(split, index, api_name="/start_task") + assert prompt == instruction and state == "Active" + content = f"ui-isolation-{i}" + out = c.predict("write", "", "/workdir/ui-isolation.txt", content, "", api_name="/run_tool") + assert "[error]" not in out + out = c.predict("bash", "cat /workdir/ui-isolation.txt", "/workdir", "", "", api_name="/run_tool") + assert content in out and f"ui-isolation-{1-i}" not in out + grade, state = c.predict("__known_wrong_ui_smoke__", api_name="/grade_task") + assert grade == "Reward: 0.0" and state == "Finished" + return {"split": split, "index": index, "passed": True, "isolated_tools": True, + "graded_zero": True, "closed": True} + finally: + c.predict(api_name="/close_task") + + with ThreadPoolExecutor(max_workers=2) as pool: + results = list(pool.map(one, range(2))) + for arm in ["opencode-blackbox", "seta-whitebox"]: + c = Client("HuggingEnvs/data-agent-" + arm + "-env", token=token, verbose=False) + for split, index in [("train", 895), ("test", 249)]: + instruction, _ = c.predict(split, index, api_name="/preview_task") + assert len(instruction) > 50 + results.append({"arm": arm, "split": split, "index": index, "preview_passed": True}) + a.out.parent.mkdir(parents=True, exist_ok=True) + a.out.write_text(json.dumps({"passed": True, "checked_at": time.time(), "results": results}, indent=2) + "\n") + print(a.out.read_text()) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/hf/runtime/whitebox_tito.py b/04-data-agent/hf/runtime/whitebox_tito.py new file mode 100644 index 0000000..c2aedc8 --- /dev/null +++ b/04-data-agent/hf/runtime/whitebox_tito.py @@ -0,0 +1,45 @@ +"""Match retained supervised spans to distinct engine call occurrences exactly.""" + + +def audit_rows(prompt_ids, completions, masks, logprobs, calls): + candidates, checks = [], [] + for row_index, (root, completion, mask, row_logprobs) in enumerate(zip(prompt_ids, completions, masks, logprobs, strict=True)): + assert len(completion) == len(mask) == len(row_logprobs) + assert set(mask) <= {0, 1} + full = root + completion + assert len(full) <= 131072 + offset = 0 + while offset < len(mask): + if not mask[offset]: + offset += 1 + continue + end = offset + 1 + while end < len(mask) and mask[end]: + end += 1 + keep, start = end - offset, len(root) + offset + matches = [index for index, call in enumerate(calls) + if call['prompt_ids'] == full[:start] + and len(call['completion_ids']) >= keep + and (len(call['completion_ids']) == keep or end == len(mask)) + and call['completion_ids'][:keep] == completion[offset:end] + and call['logprobs'][:keep] == row_logprobs[offset:end]] + assert matches, f'row {row_index} supervision lacks exact engine provenance at {offset}' + candidates.append(matches) + offset = end + assert all(p == 0 for p, m in zip(row_logprobs, mask, strict=True) if not m) + checks.append({'supervised': sum(mask), 'context': len(mask) - sum(mask), 'tito_pass': True}) + # A truncated span may match several otherwise distinct calls. Find a complete + # one-to-one assignment rather than consuming the first match greedily. + assignments = {} + def assign(span, visited): + for call in candidates[span]: + if call in visited: + continue + visited.add(call) + if call not in assignments or assign(assignments[call], visited): + assignments[call] = span + return True + return False + for span in range(len(candidates)): + assert assign(span, set()), f'supervised span {span} has no distinct engine call occurrence' + return checks diff --git a/04-data-agent/hf/status.py b/04-data-agent/hf/status.py new file mode 100644 index 0000000..f161439 --- /dev/null +++ b/04-data-agent/hf/status.py @@ -0,0 +1,92 @@ +"""Read HF job state and small durable progress artifacts without streaming logs.""" +import argparse +from concurrent.futures import ThreadPoolExecutor +import json +from pathlib import Path + +from deploy import credentials + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("jobs", nargs="+") + p.add_argument("--env-file", required=True) + p.add_argument("--out", type=Path, required=True) + p.add_argument("--logs", action="store_true") + a = p.parse_args() + from huggingface_hub import HfApi + secret = credentials(a.env_file) + api = HfApi(token=secret["HF_TOKEN"]) + files = ["status.json", "services.json", "eval_progress.json", "canonical_scores.json", + "scalability.json", "upload_status.json", "upload_error.json", "training_recipe.json", + "training_smoke_verified.json", "trackio_verified.json"] + if a.logs: + files += ["serving.log", "vllm.log", "bridge.log", "eval-stage0-c8.log", + "smoke-opencode.log", "eval-opencode.log", "train-first.log", "train-resumed.log", "trackio-sync.log", + "eval-stage1-c32.log", "eval-stage2-c48.log", "eval-stage2-c53.log"] + + def inspect(job_id): + j = api.inspect_job(job_id=job_id, namespace="HuggingEnvs") + owner = j.environment["RUN_OWNER"] + dest = a.out / "downloads" / owner + dest.mkdir(parents=True, exist_ok=True) + prefix = j.environment["RUN_ID"] + "/jobs/" + owner + "/" + requested = list(files) + if j.labels.get("role") == "coordinator": + if j.labels.get("phase") == "qualify": + requested = ["qualification.json"] + elif j.labels.get("phase") == "setup": + prefix = j.environment["RUN_ID"] + "/pipelines/" + j.environment["BUNDLE_SHA256"] + "/" + requested = ["pipeline.json"] + else: + parent = api.inspect_job(job_id=j.environment["TRAINING_JOB"], namespace="HuggingEnvs") + prefix = j.environment["RUN_ID"] + "/coordination/" + parent.environment["RUN_OWNER"] + "/" + requested = ["monitor.json", "state.json"] + from huggingface_hub.errors import EntryNotFoundError + try: + available = {item.path for item in api.list_bucket_tree(j.environment["ARTIFACT_BUCKET"], + prefix=prefix, recursive=False)} + except EntryNotFoundError: + available = set() + if j.labels.get("arm") == "opencode": + for backend in ("daytona", "hf"): + for sub in (backend, "smoke/" + backend): + try: + nested = {item.path for item in api.list_bucket_tree(j.environment["ARTIFACT_BUCKET"], prefix=prefix+sub+"/", recursive=False)} + except EntryNotFoundError: + nested = set() + available.update(nested) + requested += [sub + "/scores.json", sub + "/scalability.json", sub + "/configuration.json", sub + "/progress.json"] + (dest / sub).mkdir(parents=True, exist_ok=True) + if a.logs and j.labels.get("role") != "coordinator": + requested += sorted(Path(name).name for name in available + if Path(name).name.startswith("eval-stage") + and name.endswith(".log") and Path(name).name not in requested) + downloads = [(prefix + name, str(dest / name)) for name in requested if prefix + name in available] + if downloads: + api.download_bucket_files(j.environment["ARTIFACT_BUCKET"], files=downloads, raise_on_missing_files=False) + result = {"id": job_id, "owner": owner, "stage": j.status.stage} + for name in requested: + path = dest / name + if not path.exists(): + continue + if name.endswith(".json"): + value = json.loads(path.read_text()) + if name == "canonical_scores.json" and j.labels.get("arm") != "opencode": + value = {k: value.get(k) for k in ["graded_cells", "expected_cells", "complete", + "average_pass_at_1", "comparison_ready", "ungraded_attempts", "tito_pass"]} + result[name] = value + else: + content = "\n".join(path.read_text(errors="replace").splitlines()[-8:]) + for value in secret.values(): + content = content.replace(value, "[REDACTED]") + result[name] = content[-2000:] + return result + + with ThreadPoolExecutor(max_workers=min(4, len(a.jobs))) as pool: + for result in pool.map(inspect, a.jobs): + print(json.dumps(result), flush=True) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/hf/stop_hf_after_checkpoint.py b/04-data-agent/hf/stop_hf_after_checkpoint.py new file mode 100644 index 0000000..ee753d3 --- /dev/null +++ b/04-data-agent/hf/stop_hf_after_checkpoint.py @@ -0,0 +1,218 @@ +"""Stop an HF trainer only after verifying its next full checkpoint, then evaluate it. + +Uses the existing checkpoint controller and immutable GPU bundle. It never changes +the running trainer, and it preserves an explicit planned-cancellation receipt. +""" +import argparse +from datetime import datetime, timezone +import fcntl +import hashlib +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys +import time + +TERMINAL = {"COMPLETED", "ERROR", "CANCELED", "CANCELLED", "DELETED"} +READY = "checkpoint.hf.ready.json" + + +def now(): + return datetime.now(timezone.utc).isoformat() + + +def read(path): + return json.loads(Path(path).read_text()) + + +def save(path, value): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_suffix(".tmp") + tmp.write_text(json.dumps(value, indent=2) + "\n") + tmp.replace(path) + + +def sha(path): + with Path(path).open("rb") as f: + return hashlib.file_digest(f, "sha256").hexdigest() + + +def verify_checkpoint(directory, *, job, step): + manifest = read(directory / READY) + if (manifest["arm"] != "whitebox" or manifest["step"] != step + or manifest["bundle_sha256"] != job.environment["BUNDLE_SHA256"] + or manifest["base_model"] != "Qwen/Qwen3.5-2B" + or manifest["base_revision"] != "15852e8c16360a2fea060d615a32b45270f8a8fc"): + raise ValueError("Checkpoint provenance mismatch") + required = {"optimizer.pt", "scheduler.pt", "rng_state.pth", "trainer_state.json"} + if not required.issubset(manifest["files"]) or not any(n.endswith(".safetensors") for n in manifest["files"]): + raise ValueError("Full resumable training state is missing") + for name, expected in manifest["files"].items(): + if Path(name).name != name or sha(directory / name) != expected: + raise ValueError("Checkpoint content hash mismatch: " + name) + if read(directory / "trainer_state.json")["global_step"] != step: + raise ValueError("Checkpoint optimizer step mismatch") + return manifest + + +def render(out, state): + lines = ["# SETA planned stop and final evaluation", "", f"Updated: {now()}", "", + f"- Training job: `{state['training_job']}`.", + f"- Target checkpoint: **{state['target_step']}**; phase: **{state['phase']}**.", + "- User requested the next regular save, then stop and evaluate the synchronous SETA run.", + "- Training stops only after a local readback verifies every checkpoint file hash, including optimizer/RNG state.", + "- The provider records CANCELED for this intentional stop; it is not a successful 1,000-step completion.", + "- Evaluation: the unchanged 250 fixed tests through native bash/SETA, pass@1, concurrency 50, one A100.", + "- All three environment Spaces use CPU Basic after SETA stops; limits remain 1024 transport sessions and 64/100/61 sandbox slots (Harbor/native OpenCode/SETA).", + "- CPU Basic preserves configured limits; no claim of equivalent measured peak throughput is made.", ""] + if state.get("checkpoint_verified"): + lines += [f"Verified checkpoint manifest: `{state['checkpoint_verified']['manifest_sha256']}`.", ""] + if state.get("evaluation"): + result = state["evaluation"] + score = result["scores"] + lines += [f"Evaluation job: `{result['job_id']}`. Final pass@1: **{100 * score['average_pass_at_1']:.1f}%**.", + f"Complete graded coverage: {score['graded_cells']}/{score['expected_cells']}; TiTO: {score['tito_pass']}.", "", + "```json", json.dumps(score, indent=2), "```", ""] + (out / "REPORT.md").write_text("\n".join(lines)) + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--request", type=Path, required=True) + args = p.parse_args() + request = read(args.request) + out = args.request.parent + lock = (out / "operation.lock").open("a") + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + from dotenv import dotenv_values + from huggingface_hub import HfApi + from huggingface_hub.errors import EntryNotFoundError + import httpx + creds = dotenv_values(request["env_file"]) + os.environ["HF_TOKEN"] = creds.get("HF_API_KEY") or creds["HF_TOKEN"] + api = HfApi(token=os.environ["HF_TOKEN"]) + state = read(out / "status.json") if (out / "status.json").exists() else { + "training_job": request["training_job"], "target_step": request["target_step"], + "user_requested_stop": True, "started_at": now(), "phase": "waiting_for_checkpoint"} + + def progress(phase, **values): + state.update(phase=phase, checked_at=now(), **values) + save(out / "status.json", state) + render(out, state) + print(json.dumps({k: state[k] for k in ["phase", "checked_at", "target_step"]}), flush=True) + + try: + job = api.inspect_job(job_id=request["training_job"], namespace="HuggingEnvs") + assert job.labels["role"] == "train" and job.labels["arm"] == "whitebox" + bucket = job.environment["ARTIFACT_BUCKET"] + prefix = job.environment["RUN_ID"] + "/jobs/" + job.environment["RUN_OWNER"] + cp_prefix = prefix + f"/run/checkpoint-{request['target_step']}" + checkpoint = out / f"checkpoint-{request['target_step']}" + checkpoint.mkdir(exist_ok=True) + deadline = time.monotonic() + 4 * 3600 + if not state.get("checkpoint_verified"): + while True: + try: + api.download_bucket_files(bucket, [(cp_prefix + "/" + READY, checkpoint / READY)], raise_on_missing_files=True) + break + except EntryNotFoundError: + job = api.inspect_job(job_id=request["training_job"], namespace="HuggingEnvs") + if job.status.stage in TERMINAL or time.monotonic() > deadline: + raise RuntimeError("Trainer ended or checkpoint publication timed out before safe stop") + progress("waiting_for_checkpoint", training_stage=job.status.stage) + time.sleep(30) + manifest = read(checkpoint / READY) + if any(Path(n).name != n for n in manifest["files"]): + raise ValueError("Invalid checkpoint member") + progress("verifying_full_checkpoint") + api.download_bucket_files(bucket, [(cp_prefix + "/" + n, checkpoint / n) for n in manifest["files"]], raise_on_missing_files=True) + verify_checkpoint(checkpoint, job=job, step=request["target_step"]) + proof = {"training_job": job.id, "step": request["target_step"], + "checkpoint": "hf://buckets/" + bucket + "/" + cp_prefix, + "manifest_sha256": sha(checkpoint / READY), "full_checkpoint_verified": True, + "user_requested_stop": True, "verified_at": now()} + save(out / "checkpoint-verified.json", proof) + progress("checkpoint_verified", checkpoint_verified=proof) + + # Freeze and test the final-eval controller before canceling the trainer. + controller = out / "controller" + if not (controller / "plan.json").exists(): + old = Path(request["previous_controller_plan"]).parent + shutil.copytree(old, controller, dirs_exist_ok=True, + ignore=shutil.ignore_patterns("output", "*.out", "*.err", "__pycache__", "plan.json")) + plan = read(old / "plan.json") + for key in ["root", "logger_root"]: + plan[key] = str(controller / Path(plan[key]).name) + plan["final_checkpoint"] = state["checkpoint_verified"] + coordinator = Path(plan["root"]) / "hf/runtime/coordinator.py" + shutil.copy2(out / "source/coordinator.py", coordinator) + shutil.copy2(out / "source/late_hf_logging.py", controller / "late_hf_logging.py") + followup = controller / "hf_followup.py" + code = followup.read_text() + old_call = 'run(out / "output", "whitebox", admission=admission)' + assert old_call in code + followup.write_text(code.replace(old_call, 'run(out / "output", "whitebox", admission=admission, final_checkpoint=plan.get("final_checkpoint"))')) + (controller / "controller.slurm").write_text((out / "stop.slurm").read_text()) + plan["files"] = {n: sha(controller / n) for n in plan["files"]} + save(controller / "plan.json", plan) + if not state.get("training_stopped"): + # Shut down only the old CPU coordinator; its evaluated checkpoint100 persists. + subprocess.run(["scancel", str(request["previous_controller_job"])], check=True) + launch = Path(request["launch_state"]) + metadata = read(launch) + metadata.update(controller_job=os.environ["SLURM_JOB_ID"], controller_plan=str(controller / "plan.json"), + planned_stop={"target_step": request["target_step"], "request": str(args.request), "user_requested": True}) + save(launch, metadata) + job = api.inspect_job(job_id=job.id, namespace="HuggingEnvs") + if job.status.stage not in TERMINAL: + api.cancel_job(job_id=job.id, namespace="HuggingEnvs") + for _ in range(60): + job = api.inspect_job(job_id=job.id, namespace="HuggingEnvs") + if job.status.stage in TERMINAL: + break + time.sleep(5) + if job.status.stage not in TERMINAL: + raise RuntimeError("Cancellation did not become terminal") + progress("training_stopped", training_stopped=True, training_stage=job.status.stage, + stopped_at=now(), final_saved_step=request["target_step"]) + + receipt = read(out / "space-hardware.json") + for repo, row in receipt["spaces"].items(): + if not row.get("request_sent"): + api.request_space_hardware(repo, hardware="cpu-basic") + row.update(request_sent=True, requested_at=now()) + save(out / "space-hardware.json", receipt) + for _ in range(60): + runtime = api.get_space_runtime(repo) + if runtime.hardware == "cpu-basic" and runtime.stage == "RUNNING": + url = "https://" + repo.replace("/", "-").lower() + ".hf.space" + response = httpx.get(url + "/deployment", timeout=30) + if response.status_code == 200: + current = response.json() + variables = api.get_space_variables(repo) + assert all(variables[k].value == v for k, v in row["concurrency_variables"].items()) + assert current["bundle_sha256"] == row["bundle_sha256"] + row.update(verified_at=now(), hardware=runtime.hardware, stage=runtime.stage, after_deployment=current, + concurrency_unchanged=True) + save(out / "space-hardware.json", receipt) + break + time.sleep(10) + else: + raise RuntimeError("Space did not become healthy on CPU Basic: " + repo) + progress("final_evaluation_controller_running", spaces_cpu_basic=True) + subprocess.run([sys.executable, "-u", str(controller / "hf_followup.py"), "watch", "--plan", str(controller / "plan.json")], check=True) + score_path = controller / "output/decisions/scores" / f"step-{request['target_step']:06d}.json" + result = read(score_path) + if not result["scores"].get("comparison_ready"): + raise RuntimeError("Final evaluation did not pass its gates") + progress("complete", evaluation=result, completed_at=now()) + except Exception as exc: + progress("needs_attention", error_type=type(exc).__name__, error=str(exc)[:500]) + raise + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/hf/tests/browser_smoke.py b/04-data-agent/hf/tests/browser_smoke.py new file mode 100644 index 0000000..34c5598 --- /dev/null +++ b/04-data-agent/hf/tests/browser_smoke.py @@ -0,0 +1,65 @@ +"""Exercise public browser controls and verify two isolated whitebox workspaces.""" +import argparse +import asyncio +import json +from pathlib import Path +import time + +from playwright.async_api import async_playwright, expect + + +async def check(browser, out, split, index, tag): + context = await browser.new_context(viewport={"width": 1440, "height": 1080}) + page = await context.new_page() + errors = [] + page.on("pageerror", lambda error: errors.append(str(error))) + await page.goto("https://huggingenvs-data-agent-seta-whitebox-env.hf.space", + wait_until="domcontentloaded", timeout=60000) + await page.get_by_role("button", name="Load task", exact=True).wait_for(timeout=30000) + if split == "train": + await page.get_by_role("combobox", name="Dataset", exact=True).click() + await page.get_by_role("option", name="Train · 1,000 tasks", exact=True).click() + await expect(page.get_by_role("spinbutton")).to_have_attribute("max", "999") + await page.get_by_role("spinbutton").fill(str(index)) + await page.get_by_role("button", name="Load task", exact=True).click() + status = page.get_by_role("textbox", name="Workspace status", exact=True) + try: + await page.get_by_role("button", name="Start workspace", exact=True).click() + await expect(status).to_have_value("Active", timeout=180000) + # Distinct browser sessions write the same path and see only their own bytes. + await page.get_by_role("textbox", name="Shell command", exact=True).fill( + f"printf '{tag}' > /workdir/public-ui-isolation.txt; cat /workdir/public-ui-isolation.txt") + await page.get_by_role("button", name="Run tool", exact=True).click() + await expect(page.locator("#tool-console")).to_contain_text(tag, timeout=90000) + await page.get_by_role("textbox", name="Final answer", exact=True).fill("__known_wrong_ui_smoke__") + await page.get_by_role("button", name="Submit and grade", exact=True).click() + await expect(status).to_have_value("Finished", timeout=180000) + await expect(page.locator("#tool-console")).to_contain_text("Reward: 0.0") + await page.screenshot(path=str(out / f"whitebox-{split}-graded.png"), full_page=True) + assert not errors, errors + return {"split": split, "index": index, "public_browser": True, "graded_zero": True, + "tools_passed": True, "javascript_errors": errors} + finally: + await page.get_by_role("button", name="Close workspace", exact=True).click() + await expect(status).to_have_value("Closed", timeout=60000) + await context.close() + + +async def main(out): + out.mkdir(parents=True, exist_ok=True) + async with async_playwright() as p: + browser = await p.chromium.launch(headless=True, args=["--no-sandbox"]) + try: + results = await asyncio.gather(check(browser, out, "train", 895, "browser-train-895"), + check(browser, out, "test", 166, "browser-test-166")) + finally: + await browser.close() + report = {"passed": True, "checked_at": time.time(), "results": results} + (out / "browser-smoke.json").write_text(json.dumps(report, indent=2) + "\n") + print(json.dumps(report), flush=True) + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--out", type=Path, required=True) + asyncio.run(main(p.parse_args().out)) diff --git a/04-data-agent/hf/tests/test_auth_bridge.py b/04-data-agent/hf/tests/test_auth_bridge.py new file mode 100644 index 0000000..e1c315e --- /dev/null +++ b/04-data-agent/hf/tests/test_auth_bridge.py @@ -0,0 +1,152 @@ +"""Exercise real HTTP streaming and WebSocket boundaries used by private Spaces.""" +import json +import asyncio +from pathlib import Path +import socket +import sys +import threading +import time +import unittest + +import httpx +from fastapi import FastAPI, Request, WebSocket +from starlette.responses import StreamingResponse +import uvicorn +from websockets.sync.client import connect + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "runtime")) +from auth_bridge import make_app + + +def launch(app): + listener = socket.socket() + listener.bind(("127.0.0.1", 0)) + port = listener.getsockname()[1] + server = uvicorn.Server(uvicorn.Config(app, log_level="error", ws_ping_interval=None)) + thread = threading.Thread(target=server.run, kwargs={"sockets": [listener]}, daemon=True) + thread.start() + deadline = time.monotonic() + 10 + while not server.started and time.monotonic() < deadline: + time.sleep(0.02) + assert server.started + return server, thread, port + + +class BridgeTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + upstream = FastAPI() + + @upstream.post("/mcp") + async def mcp(req: Request): + assert req.headers["authorization"] == "Bearer integration-test-token" + body = await req.body() + async def chunks(): + yield b"data: " + yield body + yield b"\n\n" + return StreamingResponse(chunks(), media_type="text/event-stream", headers={"x-test": "preserved"}) + + @upstream.websocket("/ws") + async def websocket(ws: WebSocket): + assert ws.headers["authorization"] == "Bearer integration-test-token" + await ws.accept() + for _ in range(2): + value = await ws.receive() + if value.get("text") is not None: + await ws.send_text(value["text"]) + else: + await ws.send_bytes(value["bytes"]) + await ws.close() + + cls.remote, cls.remote_thread, port = launch(upstream) + cls.bridge, cls.bridge_thread, cls.port = launch(make_app(f"http://127.0.0.1:{port}", "integration-test-token")) + + @classmethod + def tearDownClass(cls): + cls.bridge.should_exit = True + cls.remote.should_exit = True + cls.bridge_thread.join(5) + cls.remote_thread.join(5) + + def test_streamed_mcp_body_and_headers_survive(self): + body = json.dumps({"jsonrpc": "2.0", "id": 1, "params": {"text": "tool output α\n"}}).encode() + with httpx.Client() as client: + response = client.post(f"http://127.0.0.1:{self.port}/mcp", content=body, + headers={"Authorization": "Bearer caller-value"}) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.content, b"data: " + body + b"\n\n") + self.assertEqual(response.headers["x-test"], "preserved") + self.assertNotIn("integration-test-token", response.text) + + def test_websocket_text_and_binary_survive(self): + with connect(f"ws://127.0.0.1:{self.port}/ws", ping_interval=None) as ws: + ws.send('{"completion_token_ids":[1,4,8]}') + self.assertEqual(ws.recv(), '{"completion_token_ids":[1,4,8]}') + ws.send(b"\x00\xff\x01") + self.assertEqual(ws.recv(), b"\x00\xff\x01") + + +class IdleProxyTest(unittest.IsolatedAsyncioTestCase): + async def test_delayed_result_survives_idle_proxy_with_keepalive(self): + from websockets.asyncio.client import connect as async_connect + from websockets.exceptions import ConnectionClosed + upstream = FastAPI() + @upstream.websocket("/delayed") + async def delayed(ws: WebSocket): + await ws.accept() + value = await ws.receive_text() + await asyncio.sleep(1.2) + try: + await ws.send_text(value) + except Exception: + pass # The no-keepalive control intentionally loses its connection. + remote, remote_thread, port = launch(upstream) + closed_idle = [] + async def proxy(reader, writer): + other_reader, other_writer = await asyncio.open_connection("127.0.0.1", port) + last = [time.monotonic()] + async def relay(src, dest): + while chunk := await src.read(65536): + last[0] = time.monotonic() + dest.write(chunk) + await dest.drain() + async def expire(): + while time.monotonic() - last[0] < 0.4: + await asyncio.sleep(0.03) + closed_idle.append(True) + tasks = [asyncio.create_task(relay(reader, other_writer)), + asyncio.create_task(relay(other_reader, writer)), asyncio.create_task(expire())] + try: + await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) + finally: + for task in tasks: task.cancel() + await asyncio.gather(*tasks, return_exceptions=True) + writer.close() + other_writer.close() + proxy_server = await asyncio.start_server(proxy, "127.0.0.1", 0) + proxy_port = proxy_server.sockets[0].getsockname()[1] + try: + for interval, expected in [(None, False), (0.1, True)]: + bridge, thread, bridge_port = launch(make_app(f"http://127.0.0.1:{proxy_port}", "test", ping_interval=interval)) + try: + async with async_connect(f"ws://127.0.0.1:{bridge_port}/delayed", ping_interval=None) as ws: + await ws.send('{"completion_token_ids":[1,2,3]}') + if expected: + self.assertEqual(await asyncio.wait_for(ws.recv(), 3), '{"completion_token_ids":[1,2,3]}') + else: + with self.assertRaises(ConnectionClosed): + await asyncio.wait_for(ws.recv(), 3) + finally: + bridge.should_exit = True + await asyncio.to_thread(thread.join, 5) + self.assertEqual(len(closed_idle), 1) + finally: + proxy_server.close() + await proxy_server.wait_closed() + remote.should_exit = True + await asyncio.to_thread(remote_thread.join, 5) + + +if __name__ == "__main__": + unittest.main() diff --git a/04-data-agent/hf/tests/test_bucket_publication.py b/04-data-agent/hf/tests/test_bucket_publication.py new file mode 100644 index 0000000..ebd2c33 --- /dev/null +++ b/04-data-agent/hf/tests/test_bucket_publication.py @@ -0,0 +1,84 @@ +"""An upload timeout must not lose a checkpoint or publish its marker early.""" +import json +from unittest.mock import Mock, patch + +import httpx +import pytest +from huggingface_hub.errors import HfHubHTTPError + +from artifacts import Publisher +from bucket_io import sync_with_retry +from checkpoint_store import READY + + +def http_error(code): + return HfHubHTTPError("failure", response=httpx.Response( + code, request=httpx.Request("POST", "https://example.test/bucket"))) + + +@pytest.mark.parametrize("error", [TimeoutError("response decoding"), + httpx.ReadError("connection interrupted"), http_error(503)]) +def test_transfer_timeout_recovers_without_changing_filter(error): + api = Mock() + api.sync_bucket.side_effect = [error, None] + with patch("bucket_io.time.sleep") as sleep: + sync_with_retry(api, "checkpoint", "destination", include=[READY]) + assert api.sync_bucket.call_args_list[0] == api.sync_bucket.call_args_list[1] + sleep.assert_called_once_with(30) + + +@pytest.mark.parametrize("error,attempts", [(TimeoutError(), 3), (http_error(403), 1), + (ValueError("invalid data"), 1)]) +def test_transfer_retries_are_bounded_and_do_not_hide_invalid_requests(error, attempts): + api = Mock() + api.sync_bucket.side_effect = error + with patch("bucket_io.time.sleep"), pytest.raises(type(error)): + sync_with_retry(api, "checkpoint", "destination") + assert api.sync_bucket.call_count == attempts + + +def test_completion_marker_follows_retried_data_upload(tmp_path, monkeypatch): + for key, value in {"ARTIFACT_BUCKET": "org/bucket", "RUN_ID": "run", "RUN_OWNER": "job", + "COMPARISON_ARM": "blackbox", "BUNDLE_SHA256": "bundle"}.items(): + monkeypatch.setenv(key, value) + checkpoint = tmp_path / "run/checkpoint-2" + checkpoint.mkdir(parents=True) + (checkpoint / "checkpoint.saved.json").write_text("{}") + publisher = Publisher(tmp_path) + api = Mock() + operations = [] + failed = False + + def transfer(source, destination, **options): + nonlocal failed + assert not publisher.published + operations.append((source, options)) + if source == str(checkpoint) and options.get("exclude") == [READY] and not failed: + failed = True + raise TimeoutError("committed data; lost response") + + api.sync_bucket.side_effect = transfer + with patch("huggingface_hub.HfApi", return_value=api), patch("checkpoint_store.seal"), \ + patch("bucket_io.time.sleep"): + publisher.sync() + assert [options.get("include") for _, options in operations] == [None, None, None, [READY]] + assert publisher.published == {"checkpoint-2"} + assert json.loads((tmp_path / "upload_status.json").read_text())["published_checkpoints"] == ["checkpoint-2"] + + +def test_failed_data_upload_never_publishes_completion_marker(tmp_path, monkeypatch): + for key, value in {"ARTIFACT_BUCKET": "org/bucket", "RUN_ID": "run", "RUN_OWNER": "job", + "COMPARISON_ARM": "blackbox", "BUNDLE_SHA256": "bundle"}.items(): + monkeypatch.setenv(key, value) + checkpoint = tmp_path / "run/checkpoint-2" + checkpoint.mkdir(parents=True) + (checkpoint / "checkpoint.saved.json").write_text("{}") + publisher = Publisher(tmp_path) + api = Mock() + api.sync_bucket.side_effect = [None, TimeoutError(), TimeoutError(), TimeoutError()] + with patch("huggingface_hub.HfApi", return_value=api), patch("checkpoint_store.seal"), \ + patch("bucket_io.time.sleep"), pytest.raises(TimeoutError): + publisher.sync() + assert not publisher.published + assert not any("include" in call.kwargs for call in api.sync_bucket.call_args_list) + assert not (tmp_path / "upload_status.json").exists() diff --git a/04-data-agent/hf/tests/test_consolidate_async_runs.py b/04-data-agent/hf/tests/test_consolidate_async_runs.py new file mode 100644 index 0000000..910cf91 --- /dev/null +++ b/04-data-agent/hf/tests/test_consolidate_async_runs.py @@ -0,0 +1,45 @@ +import copy +from pathlib import Path +import sys +import unittest + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +import consolidate_async_runs as c + + +class ComparisonTests(unittest.TestCase): + def setUp(self): + self.score = {"complete": True, "comparison_ready": True, "tito_pass": True, + "harness_versions_match_baseline": True, "graded_cells": 1000, + "average_pass_at_1": 33 / 250, "harnesses": { + h: {"graded": 250, "pass_at_1": 33 / 250, "difficulty": { + d: {"graded": n, "correct": n if d == 'easy' else 0} + for d, n in c.COUNTS.items()}} for h in c.HARNESSES}} + + def test_weighting_uses_counts_not_mean_of_difficulty_rates(self): + c.validated_score(self.score) + m = c.score_metrics(self.score, self.score) + self.assertEqual(m['eval/pass_at_1'], .132) + self.assertEqual(m['eval/difficulty/easy/pass_at_1'], 1) + self.assertEqual(m['eval/difficulty/medium/pass_at_1'], 0) + self.assertEqual(len([k for k in m if k.startswith('eval/harness_difficulty/')]), 12) + + def test_partial_or_bad_audit_cannot_be_published(self): + for field, value in [('graded_cells',999),('tito_pass',False),('comparison_ready',False)]: + s=copy.deepcopy(self.score);s[field]=value + with self.assertRaises(ValueError):c.validated_score(s) + s=copy.deepcopy(self.score);s['harnesses']['codex']['difficulty']['hard']['graded']=98 + with self.assertRaises(ValueError):c.validated_score(s) + + def test_training_and_eval_share_run_but_have_distinct_replay_ids(self): + train=c.native.event(c.PROJECT,c.ARMS[0],100,{'train/reward':.2},{},identity='training') + again=c.native.event(c.PROJECT,c.ARMS[0],100,{'train/reward':.2},{},identity='training') + evaluation=c.native.event(c.PROJECT,c.ARMS[0],100,{'eval/pass_at_1':.248},{},identity='evaluation') + other=c.native.event(c.PROJECT,c.ARMS[1],100,{'train/reward':.2},{},identity='training') + self.assertEqual(train['log_id'],again['log_id']) + self.assertNotEqual(train['log_id'],evaluation['log_id']) + self.assertEqual(train['run_id'],evaluation['run_id']) + self.assertNotEqual(train['run_id'],other['run_id']) + + +if __name__=='__main__':unittest.main() diff --git a/04-data-agent/hf/tests/test_coordinator_recovery.py b/04-data-agent/hf/tests/test_coordinator_recovery.py new file mode 100644 index 0000000..d133a0c --- /dev/null +++ b/04-data-agent/hf/tests/test_coordinator_recovery.py @@ -0,0 +1,68 @@ +import copy +import sys +import unittest +from pathlib import Path +from unittest.mock import Mock, patch + +import httpx +from huggingface_hub.errors import HfHubHTTPError + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "runtime")) +from coordinator import sync_decisions, verified_terminal_result + + +class RecoveryTests(unittest.TestCase): + def setUp(self): + self.args = { + "stage": "ERROR", + "score": {"comparison_ready": True, "complete": True, "tito_pass": True, + "graded_cells": 250, "expected_cells": 250}, + "evidence": {"manifest_sha256": "hash", "step": 100, "bundle_sha256": "bundle", "source": "checkpoint"}, + "manifest": {"arm": "whitebox", "step": 100, "bundle_sha256": "bundle"}, + "sha": "hash", "source": "checkpoint", + "status": {"passed": True, "finished_at": 123, "arm": "whitebox", "phase": "checkpoint"}, + } + + def test_uploaded_complete_result_survives_provider_failure(self): + verified_terminal_result(**self.args) + + def test_failed_partial_wrong_checkpoint_and_unvalidated_results_rejected(self): + changes = [("score", "graded_cells", 249), ("score", "tito_pass", False), + ("score", "comparison_ready", False), ("status", "passed", False), + ("status", "phase", "baseline"), ("status", "finished_at", None), + ("evidence", "manifest_sha256", "other"), ("evidence", "step", 200), + ("evidence", "source", "other"), ("evidence", "bundle_sha256", "other")] + for group, field, value in changes: + with self.subTest(field=field): + args = copy.deepcopy(self.args) + args[group][field] = value + with self.assertRaises(ValueError): + verified_terminal_result(**args) + for stage in ("RUNNING", "CANCELED", "DELETED"): + with self.assertRaises(ValueError): + verified_terminal_result(**{**self.args, "stage": stage}) + + def error(self, code): + return HfHubHTTPError("test failure", response=httpx.Response( + code, request=httpx.Request("POST", "https://example.test/bucket"))) + + @patch("coordinator.time.sleep") + def test_transient_upload_retried(self, sleep): + api = Mock() + api.sync_bucket.side_effect = [self.error(500), None] + sync_decisions(api, Path("decisions"), "destination") + self.assertEqual(api.sync_bucket.call_count, 2) + sleep.assert_called_once_with(30) + + @patch("coordinator.time.sleep") + def test_retries_bounded_and_permission_errors_fail(self, sleep): + for code, expected in ((503, 3), (403, 1)): + api = Mock() + api.sync_bucket.side_effect = self.error(code) + with self.assertRaises(HfHubHTTPError): + sync_decisions(api, Path("decisions"), "destination") + self.assertEqual(api.sync_bucket.call_count, expected) + + +if __name__ == "__main__": + unittest.main() diff --git a/04-data-agent/hf/tests/test_inference_providers.py b/04-data-agent/hf/tests/test_inference_providers.py new file mode 100644 index 0000000..74a7de9 --- /dev/null +++ b/04-data-agent/hf/tests/test_inference_providers.py @@ -0,0 +1,99 @@ +"""Visitor credentials, provider selection, and relay boundaries.""" +import json +from pathlib import Path +import sys +import time +from types import SimpleNamespace as NS +import unittest +from unittest.mock import patch + +import httpx +from fastapi import FastAPI, HTTPException + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "runtime")) +import inference_providers as ip + + +def oauth(token="visitor-a", **kwargs): + return NS(token=token, scope=kwargs.get("scope", "openid profile inference-api"), + expires_at=kwargs.get("expires_at", time.time()+3600)) + + +class ProviderTests(unittest.TestCase): + def test_only_live_tool_models_and_no_arbitrary_selection(self): + rows = {"data": [{"id": "org/model", "providers": [ + {"provider": "live", "status": "live", "supports_tools": True}, + {"provider": "offline", "status": "error", "supports_tools": True}, + {"provider": "unknown", "status": "live"}]}]} + catalog = ip.Catalog() + with patch("inference_providers.httpx.get", return_value=NS(raise_for_status=lambda: None, json=lambda: rows)) as get: + self.assertEqual(catalog.select("live", "org/model"), "org/model:live") + self.assertEqual(len(catalog.rows()), 1) + self.assertEqual(get.call_count, 1) + with self.assertRaises(ValueError): catalog.select("offline", "org/model") + with self.assertRaises(ValueError): catalog.select("live", "https://attacker/model") + + def test_requires_current_visitor_inference_permission(self): + with patch.dict("os.environ", {"HF_TOKEN": "team-secret"}): + for value in [None, oauth(expires_at=0), oauth(scope="openid profile"), oauth(token="")]: + with self.assertRaises(ValueError): ip.visitor_token(value) + self.assertEqual(ip.visitor_token(oauth()), "visitor-a") + + def test_leases_are_isolated_revoked_and_bounded(self): + registry = ip.VisitorCredentials() + with registry.issue(oauth(), "a:model") as a, registry.issue(oauth("visitor-b"), "b:model") as b: + self.assertNotEqual(a, b) + self.assertEqual(registry.get(a).token, "visitor-a") + self.assertEqual(registry.get(b).token, "visitor-b") + self.assertNotIn("visitor-a", repr(registry.get(a))) + for _ in range(32): registry.get(a, consume=True) + with self.assertRaises(HTTPException) as error: registry.get(a, consume=True) + self.assertEqual(error.exception.status_code, 429) + registry.get(b).expires = 0 + with self.assertRaises(HTTPException): registry.get(b) + with self.assertRaises(HTTPException): registry.get(a) + self.assertEqual(registry._leases, {}) + + +class RelayTests(unittest.IsolatedAsyncioTestCase): + async def test_only_selected_model_and_visitor_credential_forwarded(self): + app = ip.mount_provider_relay(FastAPI()) + outgoing = [] + def upstream(request): + outgoing.append(request) + return httpx.Response(200, json={"choices": [{"message": {"content": "ok", "role": "assistant"}}]}) + original = httpx.AsyncClient + with ip.credentials.issue(oauth(), "org/model:provider") as key: + headers = {"authorization": "Bearer " + key} + async with original(transport=httpx.ASGITransport(app), base_url="http://app") as client: + self.assertEqual((await client.get("/hf-inference/v1/models")).status_code, 401) + with patch("inference_providers.httpx.AsyncClient", side_effect=lambda **kw: original(transport=httpx.MockTransport(upstream), **kw)): + bad = await client.post("/hf-inference/v1/chat/completions", headers=headers, json={"model": "other"}) + self.assertEqual(bad.status_code, 400) + self.assertEqual(outgoing, []) + response = await client.post("/hf-inference/v1/chat/completions", headers=headers, + json={"model": "org/model:provider", "messages": [{"role": "user", "content": "hello"}], "max_tokens": 99999}) + self.assertEqual(response.status_code, 200) + self.assertEqual(outgoing[0].headers["authorization"], "Bearer visitor-a") + self.assertEqual(str(outgoing[0].url), ip.ROUTER + "/chat/completions") + self.assertEqual(json.loads(outgoing[0].content)["max_tokens"], 4096) + self.assertNotIn("visitor-a", response.text) + async with original(transport=httpx.ASGITransport(app), base_url="http://app") as client: + self.assertEqual((await client.get("/hf-inference/v1/models", headers=headers)).status_code, 401) + + async def test_provider_errors_cannot_echo_secrets(self): + app = ip.mount_provider_relay(FastAPI()) + original = httpx.AsyncClient + with ip.credentials.issue(oauth(), "org/model:provider") as key: + async with original(transport=httpx.ASGITransport(app), base_url="http://app") as client: + with patch("inference_providers.httpx.AsyncClient", side_effect=lambda **kw: original( + transport=httpx.MockTransport(lambda r: httpx.Response(402, text="visitor-a raw secret")), **kw)): + response = await client.post("/hf-inference/v1/chat/completions", headers={"authorization": "Bearer " + key}, + json={"model": "org/model:provider", "messages": []}) + self.assertEqual(response.status_code, 402) + self.assertNotIn("visitor-a", response.text) + self.assertIn("credits", response.text) + + +if __name__ == "__main__": + unittest.main() diff --git a/04-data-agent/hf/tests/test_joint_eval_admission.py b/04-data-agent/hf/tests/test_joint_eval_admission.py new file mode 100644 index 0000000..99a94e0 --- /dev/null +++ b/04-data-agent/hf/tests/test_joint_eval_admission.py @@ -0,0 +1,27 @@ +import importlib.util +from pathlib import Path +from types import SimpleNamespace +import pytest + +spec = importlib.util.spec_from_file_location('local_watch', Path(__file__).parents[1] / 'local_watch.py') +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) + + +def test_hf_eval_startup_also_reserves_capacity(): + api = SimpleNamespace(list_jobs=lambda **_: [SimpleNamespace(status=SimpleNamespace(stage='STARTING'))]) + assert not module.admit_with_hf(lambda _: pytest.fail('local GPU allocated during HF eval startup'), {}, api) + + +def test_idle_hf_still_checks_local_and_sandbox_capacity(): + api = SimpleNamespace(list_jobs=lambda **_: [SimpleNamespace(status=SimpleNamespace(stage='COMPLETED'))]) + assert not module.admit_with_hf(lambda _: False, {}, api) + assert module.admit_with_hf(lambda _: True, {}, api) + + +def test_unknown_hf_capacity_never_allocates(): + def unavailable(**kwargs): + raise TimeoutError('HF unavailable') + api = SimpleNamespace(list_jobs=unavailable) + with pytest.raises(TimeoutError): + module.admit_with_hf(lambda _: pytest.fail('unknown capacity'), {}, api) diff --git a/04-data-agent/hf/tests/test_late_hf_logging.py b/04-data-agent/hf/tests/test_late_hf_logging.py new file mode 100644 index 0000000..2c7ff64 --- /dev/null +++ b/04-data-agent/hf/tests/test_late_hf_logging.py @@ -0,0 +1,76 @@ +"""A late logger must never race a trainer or publish scores from other weights.""" +import json +from pathlib import Path +import sys +import tempfile +from types import SimpleNamespace +import unittest +from unittest.mock import Mock, patch + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from late_hf_logging import replay, planned_stop_matches + + +class LateLoggingTest(unittest.TestCase): + def setUp(self): + self.temporary = tempfile.TemporaryDirectory() + self.root = Path(self.temporary.name) + self.env = self.root / "env" + self.env.write_text("HF_API_KEY=unused-test-token\n") + self.plan = self.root / "plan.json" + self.plan.write_text(json.dumps({"files": {}, "env_file": str(self.env), + "training_job": "train", "namespace": "org", "bundle_sha256": "bundle"})) + self.api = Mock() + self.job = SimpleNamespace(status=SimpleNamespace(stage="COMPLETED"), + environment={"BUNDLE_SHA256": "bundle", "ARTIFACT_BUCKET": "org/bucket", + "RUN_ID": "run", "RUN_OWNER": "owner"}) + self.api.inspect_job.return_value = self.job + + def tearDown(self): + self.temporary.cleanup() + + def run_replay(self): + with patch("huggingface_hub.HfApi", return_value=self.api), patch.dict("os.environ"): + replay(self.plan) + + def test_active_trainer_rejected_before_any_bucket_sync(self): + self.job.status.stage = "RUNNING" + with self.assertRaisesRegex(ValueError, "completed trainer"): + self.run_replay() + self.api.sync_bucket.assert_not_called() + + def test_only_explicit_bound_stop_is_eligible_for_late_logging(self): + self.job.status.stage = "CANCELED" + plan = json.loads(self.plan.read_text()) + self.assertFalse(planned_stop_matches(self.job, plan)) + plan["final_checkpoint"] = {"training_job": "train", "step": 150, + "checkpoint": "hf://buckets/org/bucket/run/jobs/owner/run/checkpoint-150", + "manifest_sha256": "a" * 64, "user_requested_stop": True, "full_checkpoint_verified": True} + self.assertTrue(planned_stop_matches(self.job, plan)) + for key, value in [("training_job", "other"), ("step", 100), ("manifest_sha256", ""), + ("full_checkpoint_verified", False), ("user_requested_stop", False)]: + changed = {**plan, "final_checkpoint": {**plan["final_checkpoint"], key: value}} + with self.subTest(key=key): + self.assertFalse(planned_stop_matches(self.job, changed)) + self.job.status.stage = "RUNNING" + self.assertFalse(planned_stop_matches(self.job, plan)) + + def test_other_weights_cannot_be_logged_under_this_trainer(self): + logs = self.root / "late-training-logs" + logs.mkdir() + (logs / "status.json").write_text(json.dumps({"passed": True, "finished_at": 123})) + scores = self.root / "output/decisions/scores" + scores.mkdir(parents=True) + (scores / "step-000100.json").write_text(json.dumps({"step": 100, + "source": {"bundle_sha256": "bundle", "step": 100, + "source": "hf://buckets/org/bucket/run/jobs/other-trainer/run/checkpoint-100"}, + "scores": {"comparison_ready": True, "tito_pass": True, "arm": "whitebox", "graded_cells": 250}})) + with self.assertRaisesRegex(ValueError, "Unverified checkpoint"): + self.run_replay() + self.api.sync_bucket.assert_called_once() + # The only operation was a remote-to-local read, never a bucket upload. + self.assertEqual(self.api.sync_bucket.call_args.args[0], "hf://buckets/org/bucket/run/jobs/owner") + + +if __name__ == "__main__": + unittest.main() diff --git a/04-data-agent/hf/tests/test_launch_qualified.py b/04-data-agent/hf/tests/test_launch_qualified.py new file mode 100644 index 0000000..07e9ffc --- /dev/null +++ b/04-data-agent/hf/tests/test_launch_qualified.py @@ -0,0 +1,36 @@ +import importlib.util +import json +from pathlib import Path +import pytest + +spec = importlib.util.spec_from_file_location('launch_qualified', Path(__file__).parents[1] / 'launch_qualified.py') +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) + + +def test_ambiguous_hf_submission_cannot_allocate_twice(tmp_path): + class API: + calls = 0 + + def run_job(self, **kwargs): + self.calls += 1 + assert json.loads((tmp_path / 'intent.json').read_text())['owner'] == 'unique-owner' + raise TimeoutError('Response lost after allocation') + + api = API() + wrapped = module.RecordedAPI(api, tmp_path / 'intent.json') + kwargs = {'env': {'RUN_OWNER': 'unique-owner', 'BUNDLE_SHA256': 'abc'}, + 'namespace': 'test', 'labels': {'role': 'train'}} + with pytest.raises(TimeoutError): + wrapped.run_job(**kwargs) + with pytest.raises(RuntimeError, match='intent already exists'): + wrapped.run_job(**kwargs) + assert api.calls == 1 + + +def test_unqualified_native_plan_does_not_launch(tmp_path, monkeypatch): + from argparse import Namespace + (tmp_path / 'plan.json').write_text('{"checkpoint_eval_gpu_validation": "pending"}') + monkeypatch.setattr(module, 'run', lambda _: pytest.fail('unqualified allocation')) + with pytest.raises(ValueError, match='not complete'): + module.opencode(Namespace(ready=tmp_path), {}) diff --git a/04-data-agent/hf/tests/test_local_followup.py b/04-data-agent/hf/tests/test_local_followup.py new file mode 100644 index 0000000..4c3bff1 --- /dev/null +++ b/04-data-agent/hf/tests/test_local_followup.py @@ -0,0 +1,149 @@ +import importlib.util +import json +from pathlib import Path +import tempfile +import unittest + +HF = Path(__file__).resolve().parents[1] + + +def module(name): + spec = importlib.util.spec_from_file_location(name, HF / (name + ".py")) + value = importlib.util.module_from_spec(spec) + spec.loader.exec_module(value) + return value + + +followup = module("local_followup") +long = module("local_long") +gates = module("launch_gates") + + +class LocalQualificationTests(unittest.TestCase): + def test_eval_admission_preserves_fifty_slots_with_valid_reservation(self): + spec = importlib.util.spec_from_file_location("tested_service_policy", HF / "runtime/service_policy.py") + policy = importlib.util.module_from_spec(spec) + spec.loader.exec_module(policy) + admission = policy.Admission(int(followup.EVAL_ADMISSION["SANDBOX_CAPACITY"]), + int(followup.EVAL_ADMISSION["TRAIN_RESERVED_SANDBOXES"])) + for _ in range(50): + admission.acquire("eval", timeout=0) + with self.assertRaises(TimeoutError): + admission.acquire("eval", timeout=0) + admission.acquire("train", timeout=0) + for _ in range(50): + admission.release("eval") + admission.release("train") + self.assertEqual(admission.active, {"train": 0, "eval": 0}) + + def test_checkpoint_smoke_requires_each_task_harness_and_pinned_versions(self): + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + config = json.loads((HF / "configs/deployment.json").read_text()) + pins = config["harness_pins"] + (root / "hf/configs").mkdir(parents=True) + (root / "hf/configs/deployment.json").write_text(json.dumps(config)) + tasks = root / "experiments/daytona_harness_comparison/logs/20260915" + tasks.mkdir(parents=True) + (tasks / "test_indices.txt").write_text("17,42,8") + output = root / "eval" + output.mkdir() + scores = {"graded_cells": 8, "comparison_ready": False, + "harness_versions": {h: {v: 2} for h, v in pins.items()}} + (output / "canonical_scores.json").write_text(json.dumps(scores)) + reports = [{"harness": h, "index": i, "tito_pass": True} for h in pins for i in (17, 42)] + audit = output / "final_tito.json" + audit.write_text(json.dumps({"reports": reports})) + plan = {"root": str(root), "arm": "opencode", "checkpoint_eval_qualification": True, + "bundle_sha256": "runtime", "controller_sha256": "controller"} + record = {"step": 4, "manifest_sha256": "checkpoint"} + proof = followup.validate_evaluation(plan, output, record) + self.assertTrue(proof["qualification_only"]) + audit.write_text(json.dumps({"reports": reports[:-1]})) + with self.assertRaises(ValueError): + followup.validate_evaluation(plan, output, record) + audit.write_text(json.dumps({"reports": reports})) + scores["harness_versions"]["opencode"] = {"unknown": 2} + (output / "canonical_scores.json").write_text(json.dumps(scores)) + with self.assertRaises(ValueError): + followup.validate_evaluation(plan, output, record) + plan["checkpoint_eval_qualification"] = False + with self.assertRaises(ValueError): + followup.validate_evaluation(plan, output, record) + + def test_admission_changes_do_not_hide_score_protocol_changes(self): + import copy + config = json.loads((HF / "configs/deployment.json").read_text()) + changed = copy.deepcopy(config) + changed["evaluation"]["concurrency_per_arm"]["opencode"] = 50 + self.assertEqual(gates.protocol_identity(config), gates.protocol_identity(changed)) + changed["evaluation"]["max_output_tokens_per_call"] = 8192 + self.assertNotEqual(gates.protocol_identity(config), gates.protocol_identity(changed)) + + def test_only_hundred_intervals_and_final_are_evaluated(self): + manifests = [{"step": n} for n in [0, 50, 100, 150, 200, 250]] + self.assertEqual(followup.eligible_steps(manifests, False), {100, 200}) + self.assertEqual(followup.eligible_steps(manifests, True), {100, 200, 250}) + manifests[-1]["final"] = True + self.assertEqual(followup.eligible_steps(manifests, False), {100, 200, 250}) + self.assertEqual(followup.eligible_steps([{"step": 2, "final": True}], True, qualification=True), set()) + self.assertEqual(followup.eligible_steps([{"step": 2, "final": True}, {"step": 4}], True, + qualification=True), {4}) + + def test_partial_or_wrong_implementation_cannot_qualify_native_training(self): + with tempfile.TemporaryDirectory() as temporary: + path = Path(temporary) / "scores.json" + valid = {"comparison_ready": True, "graded_cells": 250, "tito_pass": True, + "implementation": "standalone-opencode"} + path.write_text(json.dumps({"daytona": valid})) + long.validate_baseline(path, "opencode") + for key, value in [("graded_cells", 249), ("tito_pass", False), + ("implementation", "harbor")]: + path.write_text(json.dumps({**valid, key: value})) + with self.assertRaises(ValueError): + long.validate_baseline(path, "opencode") + + def test_passing_proof_cannot_hide_source_changes(self): + import hashlib + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + smoke = root / "outputs/local-train-whitebox-123" + smoke.mkdir(parents=True) + source = root / "trainer.py" + source.write_text("original trainer") + encoded = json.dumps({"files": {"trainer.py": hashlib.sha256(source.read_bytes()).hexdigest()}}).encode() + sha = hashlib.sha256(encoded).hexdigest() + (root / "bundle_manifest.json").write_bytes(encoded) + (root / "local_manifest.json").write_text(json.dumps({"sha256": sha})) + proof = {"arm": "whitebox", "bundle_sha256": sha, "passed": True, + "remote_restore_verified": True, "tito_pass": True, "weights_updated": True, + "native_optimizer_state_verified": True} + (smoke / "training_smoke_verified.json").write_text(json.dumps(proof)) + self.assertEqual(long.proof_for(smoke, "whitebox")[0], root) + source.write_text("changed trainer") + with self.assertRaises(ValueError): + long.proof_for(smoke, "whitebox") + + def test_native_grading_gate_rejects_the_old_zero_tolerance_fallback(self): + import hashlib + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + package = root / "experiments/daytona_harness_comparison/logs/20260915/source/packages/data_agent_env" + package.mkdir(parents=True) + hashes = {} + for name in ("task.py", "tasks.py", "verifier.py", "grader.py"): + path = package / name + path.write_text("explicit zero tolerance preserved") + hashes[name] = hashlib.sha256(path.read_bytes()).hexdigest() + baseline = root / "canonical_scores.json" + (root / "verification.json").write_text(json.dumps({"passed": True, + "parameters_verified": 1250, "original_graded_records_preserved": True, + "reports": [{}] * 250, "runtime_files": hashes})) + long.validate_native_grading(root, baseline) + (package / "verifier.py").write_text("task.atol or 1e-3") + with self.assertRaisesRegex(ValueError, "Native grader differs"): + long.validate_native_grading(root, baseline) + + +if __name__ == "__main__": + unittest.main() diff --git a/04-data-agent/hf/tests/test_local_score_forwarding.py b/04-data-agent/hf/tests/test_local_score_forwarding.py new file mode 100644 index 0000000..784d15a --- /dev/null +++ b/04-data-agent/hf/tests/test_local_score_forwarding.py @@ -0,0 +1,70 @@ +import importlib.util +import json +from pathlib import Path +import tempfile +import unittest + +spec = importlib.util.spec_from_file_location("local_watch", Path(__file__).resolve().parents[1] / "local_watch.py") +watch = importlib.util.module_from_spec(spec) +spec.loader.exec_module(watch) + + +class ScoreForwardingTest(unittest.TestCase): + def test_matching_baseline_and_checkpoint_forward_for_every_recipe(self): + import hashlib + for arm, cells in (("blackbox", 1000), ("opencode", 1000), ("whitebox", 250)): + with self.subTest(arm=arm), tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + baseline = root / "baseline.json" + baseline.write_text(json.dumps({"graded_cells": cells, "comparison_ready": True})) + plan = root / "plan.json" + plan.write_text(json.dumps({"root": str(root), "arm": arm, "training_job": "123", + "bundle_sha256": "bundle", "baseline_score": str(baseline), + "baseline_sha256": hashlib.sha256(baseline.read_bytes()).hexdigest()})) + directory = root / "checkpoint-evals" + directory.mkdir() + (directory / "scores-000100.json").write_text(json.dumps({ + "proof": {"passed": True, "training_arm": arm, "graded_cells": cells, + "bundle_sha256": "bundle", "manifest_sha256": "model", "step": 100}, + "evaluation": {"manifest_sha256": "model", "step": 100, "job_id": "456"}, + "scores": {"comparison_ready": True, "graded_cells": cells}})) + _, training, changed = watch.forward_scores(plan) + self.assertTrue(changed) + for step in (0, 100): + record = json.loads((training / f"checkpoint-scores/step-{step:06d}.json").read_text()) + self.assertEqual(record["scores"]["graded_cells"], cells) + baseline.write_text('{}') + with self.assertRaisesRegex(ValueError, "baseline score changed"): + watch.forward_scores(plan) + + def test_only_verified_full_scores_enter_training_curve_and_conflicts_fail(self): + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + plan = root / "plan.json" + plan.write_text(json.dumps({"root": str(root), "arm": "opencode", "training_job": "123", + "bundle_sha256": "bundle"})) + (root / "checkpoint-evals").mkdir() + source = root / "checkpoint-evals/scores-000100.json" + record = {"proof": {"passed": True, "qualification_only": True, "training_arm": "opencode", + "graded_cells": 1000, "bundle_sha256": "bundle", "manifest_sha256": "model", "step": 100}, + "evaluation": {"manifest_sha256": "model", "step": 100, "job_id": "456"}, + "scores": {"comparison_ready": True, "average_pass_at_1": 0.2}} + source.write_text(json.dumps(record)) + self.assertFalse(watch.forward_scores(plan)[2]) + record["proof"]["qualification_only"] = False + source.write_text(json.dumps(record)) + self.assertTrue(watch.forward_scores(plan)[2]) + self.assertFalse(watch.forward_scores(plan)[2]) + target = root / "outputs/local-train-opencode-123/checkpoint-scores/step-000100.json" + event = json.loads(target.read_text()) + self.assertEqual(event["step"], 100) + self.assertEqual(event["source"]["job_id"], "456") + record["scores"]["average_pass_at_1"] = 0.4 + source.write_text(json.dumps(record)) + with self.assertRaisesRegex(ValueError, "Conflicting"): + watch.forward_scores(plan) + self.assertEqual(json.loads(target.read_text()), event) + + +if __name__ == "__main__": + unittest.main() diff --git a/04-data-agent/hf/tests/test_monitor_three_runs.py b/04-data-agent/hf/tests/test_monitor_three_runs.py new file mode 100644 index 0000000..cbd367a --- /dev/null +++ b/04-data-agent/hf/tests/test_monitor_three_runs.py @@ -0,0 +1,79 @@ +import importlib.util +from pathlib import Path + +spec = importlib.util.spec_from_file_location('monitor_three_runs', Path(__file__).parents[1] / 'monitor_three_runs.py') +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) + + +def test_summary_not_a_second_optimizer_update(): + rows = [{'step': 1, 'reward': .25, 'grad_norm': 2}, {'step': 1, 'train_runtime': 4}, + {'step': 2, 'reward': .5, 'grad_norm': 0}] + r = module.training_metrics(rows) + assert r['updates'] == 2 and r['reward_last20'] == .375 + assert r['nonzero_gradients_last20'] == 1 + + +def test_timeout_observation_does_not_imply_training_failure(): + prior = {'job': 'a', 'step': 12, 'last_optimizer_progress_at': 100} + current = {'job': 'a', 'step': 12, 'stage': 'UNKNOWN'} + assert not module.diagnose(current, prior, 2100) + current['stage'] = 'RUNNING' + assert 'no_optimizer_progress_over_30_minutes' in module.diagnose(current, prior, 2100) + + +def test_resume_progress_and_tito_failure(): + prior = {'job': 'a', 'step': 100, 'last_optimizer_progress_at': 100} + current = {'job': 'b', 'step': 100, 'stage': 'RUNNING', 'checkpoints': [100], + 'tito': {'rows': 8, 'passed': 7}} + alerts = module.diagnose(current, prior, 2200) + assert alerts == ['tito_failed'] + assert current['last_optimizer_progress_at'] == 2200 + + +def test_enabled_online_logging_is_not_successful_sync(): + row = {'trackio': {'online': True, 'local_ok': True, 'sync': {'ok': False}}, + 'evaluations': [{'checkpoint': 'checkpoint-600', 'stage': 'FAILED', 'complete': False}]} + assert module.diagnose(row, {}, 1) == ['trackio_online_sync_failed', 'evaluation_failed_checkpoint-600'] + row['trackio']['sync']['ok'] = True + row['evaluations'][0]['stage'] = 'RUNNING' + assert module.diagnose(row, {}, 2) == [] + + +def test_parent_evaluations_remain_visible_after_resume(tmp_path, monkeypatch): + import json + parent, child = tmp_path / 'parent', tmp_path / 'child' + child.mkdir(); parent.mkdir() + (parent / 'run_config.json').write_text('{}') + (child / 'run_config.json').write_text(json.dumps({'resume_state': { + 'step': 150, 'checkpoint': str(parent / 'job-1/run/checkpoint-150')}})) + for root, steps in [(parent, [100, 200]), (child, [300])]: + for step in steps: + d = root / f'checkpoint-evals/step-{step:06d}' + d.mkdir(parents=True) + (d / 'submission.json').write_text(json.dumps({'job_id': str(step)})) + (d / 'scores.json').write_text(json.dumps({'comparison_ready': step == 100, + 'average_pass_at_1': .25, 'graded_cells': 1000})) + monkeypatch.setattr(module, 'states', lambda jobs: {str(j): 'COMPLETED' for j in jobs}) + evaluations = module.original_evaluations(child) + assert [e['checkpoint'] for e in evaluations] == ['checkpoint-100', 'checkpoint-300'] + assert evaluations[0]['complete'] is True + + +def test_restore_startup_retains_parent_metrics_and_checkpoints(tmp_path): + import json + parent, child = tmp_path / 'parent', tmp_path / 'child' + checkpoint = parent / 'job-1/run/checkpoint-2' + checkpoint.mkdir(parents=True) + (checkpoint / 'checkpoint.saved.json').write_text('{}') + (parent / 'submission.json').write_text('{"training": "1"}') + (parent / 'run_config.json').write_text('{}') + audit = parent / 'job-1/audit' + audit.mkdir() + (audit / 'metrics.jsonl').write_text(''.join(json.dumps({'step': s, 'reward': .5, 'grad_norm': 1}) + '\n' for s in [1,2,3])) + child.mkdir() + (child / 'submission.json').write_text('{"training": "2"}') + (child / 'run_config.json').write_text(json.dumps({'resume_state': {'step': 2, 'checkpoint': str(checkpoint)}})) + rows, saves = module.original_history(child) + assert [r['step'] for r in rows] == [1,2] + assert saves == [2] diff --git a/04-data-agent/hf/tests/test_native_tolerances.py b/04-data-agent/hf/tests/test_native_tolerances.py new file mode 100644 index 0000000..8458658 --- /dev/null +++ b/04-data-agent/hf/tests/test_native_tolerances.py @@ -0,0 +1,39 @@ +"""Explicit zero tolerances must survive task parsing and actual native grading.""" +import importlib +from pathlib import Path +import sys +from types import ModuleType +import unittest + +PACKAGE = "comparison_native_tolerance" +package = ModuleType(PACKAGE) +package.__path__ = [str(Path(__file__).resolve().parents[2] / "envs/blackbox-opencode")] +sys.modules[PACKAGE] = package +Task = importlib.import_module(PACKAGE + ".task").DataAgentTask +grade_rollout = importlib.import_module(PACKAGE + ".verifier").grade_rollout + + +class NativeToleranceTest(unittest.TestCase): + def row(self, **values): + return {"instruction": "Calculate the value", "answer": "1", "reward_mode": "numeric", + "hf_bucket": "org/test", "bucket_prefix": "task", **values} + + def test_explicit_zero_is_strict_but_omitted_tolerance_defaults(self): + strict = Task.from_row(self.row(atol=0.0, rtol="0.0")) + default = Task.from_row(self.row(atol=None, rtol="")) + self.assertEqual((strict.atol, strict.rtol), (0.0, 0.0)) + self.assertEqual((default.atol, default.rtol), (1e-3, 1e-3)) + read = lambda _: "1.0005" + self.assertEqual(grade_rollout(strict, read, ("/answer",)).correctness, 0.0) + self.assertEqual(grade_rollout(default, read, ("/answer",)).correctness, 1.0) + + def test_absolute_and_relative_tolerances_are_preserved_independently(self): + narrow = Task.from_row(self.row(atol=1e-5, rtol=0)) + relative = Task.from_row(self.row(atol=0, rtol=1e-3)) + read = lambda _: "1.0005" + self.assertEqual(grade_rollout(narrow, read, ("/answer",)).correctness, 0.0) + self.assertEqual(grade_rollout(relative, read, ("/answer",)).correctness, 1.0) + + +if __name__ == "__main__": + unittest.main() diff --git a/04-data-agent/hf/tests/test_portable_reproduction.py b/04-data-agent/hf/tests/test_portable_reproduction.py new file mode 100644 index 0000000..a55ab0d --- /dev/null +++ b/04-data-agent/hf/tests/test_portable_reproduction.py @@ -0,0 +1,83 @@ +"""Portable packaging and shared-service isolation at the actual rollout boundary.""" +import asyncio +import json +from pathlib import Path +from types import SimpleNamespace +from unittest.mock import patch + +import pytest + +import build +import harbor_service +from service_policy import Admission + + +def test_harbor_policy_is_per_session_and_releases_failed_rollouts(monkeypatch): + from openenv.harbor import rollout, serving + gate = Admission(4, 1) + sessions = {} + observed = [] + class Registry: + def get(self, key): return sessions.get(key) + async def native(**kwargs): + key = kwargs['dataset'] + sessions[key] = SimpleNamespace(metadata={}) + kwargs['on_session_created'](key) + await asyncio.sleep(.01) + if key == 'test': raise RuntimeError('trial failure') + return sessions[key].metadata['max_output_tokens'] + monkeypatch.setattr(rollout, 'run_rollout', native) + monkeypatch.setattr(serving, 'space_public_url', lambda: 'https://example.hf.space') + monkeypatch.setattr(harbor_service, 'admission', gate) + harbor_service.install() + async def exercise(): + return await asyncio.gather(*(rollout.run_rollout(dataset=d, registry=Registry(), + on_session_created=observed.append) for d in ('train', 'test')), return_exceptions=True) + values = asyncio.run(exercise()) + assert values[0] == 16384 and isinstance(values[1], RuntimeError) + assert sessions['test'].metadata['max_output_tokens'] == 4096 + assert set(observed) == {'train', 'test'} + assert gate.snapshot()['active'] == {'train': 0, 'eval': 0} + + +def test_replaced_outputs_are_archived_without_data_loss(tmp_path): + old = tmp_path / 'stage' + old.mkdir() + (old / 'capture.json').write_text('original') + archive = tmp_path / 'archive' + build.preserve(old, archive) + assert not old.exists() + assert next(archive.glob('stage-*/capture.json')).read_text() == 'original' + + +def test_packaging_excludes_local_credentials_and_caches(tmp_path): + source = tmp_path / 'source' + source.mkdir() + (source / '.env').write_text('secret') + (source / 'app.py').write_text('print(1)') + (source / 'temp').mkdir() + (source / 'temp' / 'token').write_text('secret') + target = tmp_path / 'packed' + build._copy(source, target) + assert sorted(p.name for p in target.iterdir()) == ['app.py'] + + +def test_hub_eval_capacity_is_bounded_and_source_pins_are_immutable(): + root = Path(__file__).resolve().parents[1] + config = json.loads((root / 'configs/deployment.json').read_text()) + assert set(config['evaluation']['concurrency_per_arm'].values()) == {35} + sources = json.loads((root / 'configs/sources.json').read_text()) + for source in sources['repositories']: + assert len(source['revision']) == 40 + assert set(source['revision']) <= set('0123456789abcdef') + assert len(sources['task_bundle']['sha256']) == 64 + + +def test_training_preflight_rejects_legacy_server_missing_sampling(): + from service_contract import validate_tools + properties = {key: {} for key in ("llm_url", "model", "require_tokens", "agent_timeout_s")} + response = {"data": {"observation": {"tools": [{"name": "run_rollout", "input_schema": {"properties": properties}}]}}} + with pytest.raises(ValueError, match="lacks training arguments: sampling"): + validate_tools(response, "opencode") + properties["sampling"] = {} + assert validate_tools(response, "opencode")["passed"] diff --git a/04-data-agent/hf/tests/test_setup_pipeline.py b/04-data-agent/hf/tests/test_setup_pipeline.py new file mode 100644 index 0000000..ad1eca9 --- /dev/null +++ b/04-data-agent/hf/tests/test_setup_pipeline.py @@ -0,0 +1,95 @@ +"""Verify deployment/training ordering and replay without duplicate allocations.""" +import json +import os +from pathlib import Path +import sys +import tempfile +from types import SimpleNamespace as NS +import unittest +from unittest.mock import patch + +HF = Path(__file__).resolve().parents[1] +sys.path[:0] = [str(HF), str(HF / "runtime")] +import setup_pipeline + + +class PipelineTest(unittest.TestCase): + def execute(self, fail_baseline=False): + from huggingface_hub.errors import EntryNotFoundError + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + config = json.loads((HF / "configs/deployment.json").read_text()) + config["run_id"] = "test-run" + config["pipeline"]["baseline_jobs"] = {"blackbox": "bb", "whitebox": "wb"} + (root / "hf/configs").mkdir(parents=True) + (root / "hf/configs/deployment.json").write_text(json.dumps(config)) + bundle = root / "bundle" + bundle.mkdir() + (bundle / "bundle.json").write_text(json.dumps({"sha256": "new"})) + (bundle / "bundle.tar.gz").write_bytes(b"fixture bundle") + jobs = [] + baselines = {job_id: NS(id=job_id, status=NS(stage="ERROR" if fail_baseline and arm == "whitebox" else "COMPLETED"), + environment={"RUN_OWNER": "base-" + arm}) for arm, job_id in config["pipeline"]["baseline_jobs"].items()} + remote = {f"test-run/jobs/base-{arm}/canonical_scores.json": json.dumps({"arm": arm, "comparison_ready": True}) + for arm in ["blackbox", "whitebox"]} + events = [] + deployed = [False] + class API: + def inspect_job(self, *, job_id, **kwargs): + return baselines.get(job_id) or next(j for j in jobs if j.id == job_id) + def list_jobs(self, *, labels, **kwargs): + return [j for j in jobs if all(j.labels.get(k) == v for k, v in labels.items())] + def download_bucket_files(self, bucket, *, files, **kwargs): + for name, dest in files: + if name not in remote: raise EntryNotFoundError(name) + Path(dest).write_text(remote[name]) + def sync_bucket(self, source, dest, **kwargs): + remote[dest.removeprefix("hf://buckets/org/bucket/") + "/pipeline.json"] = (Path(source) / "pipeline.json").read_text() + def space_info(self, repo): return NS(host="https://" + repo.replace("/", "-")) + def spaces(*args): + events.append("deploy") + deployed[0] = True + def get(*args, **kwargs): + return NS(raise_for_status=lambda: None, json=lambda: {"admission": {"active": {"train": 0, "eval": 0}}, + "bundle_sha256": "new" if deployed[0] else "old"}) + def start(*args): + events.append("ui") + return NS(wait=lambda **kwargs: 0) + def submit(api, config, secret, out, args): + events.append(f"{args.role}:{args.arm}:{args.phase}") + job_id = "created" + str(len(jobs)) + labels = {"role": args.role, "arm": args.arm, "phase": args.phase, "run": "test-run"} + job = NS(id=job_id, labels=labels, status=NS(stage="COMPLETED"), + environment={"BUNDLE_SHA256": "new", "RUN_OWNER": job_id}) + jobs.append(job) + remote[f"test-run/jobs/{job_id}/" + ("audit/metrics.jsonl" if args.arm == "blackbox" else "run/metrics.jsonl")] = json.dumps( + {"step": 12, "loss": 0.0, "grad_norm": 1.0, "reward": 0.5}) + "\n" + return {"id": job_id, "stage": "COMPLETED"} + env = {"ARTIFACT_BUCKET": "org/bucket", "RUN_ID": "test-run", "BUNDLE_SHA256": "new", + "BUNDLE_REPO": "org/repro", "BUNDLE_REVISION": "revision", "RUN_OWNER": "setup-owner"} + with patch.dict(os.environ, env), patch("setup_pipeline.ROOT", root), patch("setup_pipeline.BUNDLE", bundle), \ + patch("huggingface_hub.HfApi", API), patch("httpx.get", get), patch("deploy.spaces", spaces), \ + patch("deploy.submit", submit), patch("setup_pipeline.start", start): + if fail_baseline: + with self.assertRaisesRegex(RuntimeError, "ERROR"): + setup_pipeline.run(root / "output") + self.assertEqual(events, []) + self.assertEqual(jobs, []) + return + setup_pipeline.run(root / "output") + self.assertEqual(events[:2], ["deploy", "ui"]) + self.assertEqual(events[2:6], ["train:blackbox:smoke", "train:whitebox:smoke", "train:blackbox:long", "train:whitebox:long"]) + self.assertEqual(len(jobs), 6) + setup_pipeline.run(root / "replayed") + self.assertEqual(len(jobs), 6, "Replay must adopt all existing jobs") + self.assertEqual(len(events), 8, "Replay must not rebuild Spaces or rerun the UI") + state = json.loads((root / "replayed/pipeline.json").read_text()) + self.assertEqual(state["phase"], "completed") + self.assertTrue(state["passed"]) + + def test_complete_baselines_smoke_before_long_and_replay(self): self.execute() + def test_failed_baseline_cannot_deploy_or_allocate_training(self): self.execute(fail_baseline=True) + + +if __name__ == "__main__": + unittest.main() diff --git a/04-data-agent/hf/tests/test_shared_service.py b/04-data-agent/hf/tests/test_shared_service.py new file mode 100644 index 0000000..aa6958c --- /dev/null +++ b/04-data-agent/hf/tests/test_shared_service.py @@ -0,0 +1,63 @@ +"""Regression checks for simultaneous train/eval admission and capture budgets.""" +import asyncio +from concurrent.futures import ThreadPoolExecutor +import unittest + +from common import configure +configure() +from service_policy import Admission, output_limit + + +class SharedServiceTest(unittest.TestCase): + def test_reserved_train_slot_and_cancelled_waiter(self): + async def run(): + gate = Admission(3, 1) + async with gate.slot("test"), gate.slot("test"): + blocked = asyncio.create_task(enter(gate, "test")) + await asyncio.sleep(0.15) + self.assertFalse(blocked.done()) + async with gate.slot("train"): + self.assertEqual(gate.snapshot()["active"], {"train": 1, "eval": 2}) + blocked.cancel() + with self.assertRaises(asyncio.CancelledError): + await blocked + self.assertEqual(gate.snapshot()["active"], {"train": 0, "eval": 0}) + self.assertEqual(gate.snapshot()["waiting"], {"train": 0, "eval": 0}) + async with gate.slot("test"): + pass + async def enter(gate, role): + async with gate.slot(role): + pass + asyncio.run(run()) + + def test_simultaneous_split_caps_reach_engine_unchanged(self): + from fastapi.testclient import TestClient + from openenv.core.harness.capture.server import create_app + class Engine: + served_model = "test-model" + param_fixes = {} + capture_level = "text" + async def completion(self, request): + return {"id": "cap-test", "object": "chat.completion", "model": self.served_model, + "choices": [{"index": 0, "message": {"role": "assistant", "content": str(request['max_tokens'])}, + "finish_reason": "stop"}], + "usage": {"prompt_tokens": 1, "completion_tokens": 1, "total_tokens": 2}} + app = create_app(llm_url="http://unused.invalid/v1", model="test-model", capture_level="text", max_output_tokens=16384) + engine = Engine() + app.state.inference = engine + app.state.upstreams._default = (engine, "text") + sessions = {split: app.state.registry.create(dataset=split, max_output_tokens=output_limit(split)) + for split in ("train", "test")} + with TestClient(app) as client: + def call(split): + r = client.post("/v1/chat/completions", headers={"Authorization": "Bearer " + sessions[split].session_id}, + json={"model": "test-model", "messages": [{"role": "user", "content": split}], "max_tokens": 32768}) + self.assertEqual(r.status_code, 200, r.text) + return split, int(r.json()["choices"][0]["message"]["content"]) + with ThreadPoolExecutor(max_workers=2) as pool: + for split, cap in pool.map(call, ["train", "test"] * 4): + self.assertEqual(cap, 16384 if split == "train" else 4096) + + +if __name__ == "__main__": + unittest.main() diff --git a/04-data-agent/hf/tests/test_standalone_opencode.py b/04-data-agent/hf/tests/test_standalone_opencode.py new file mode 100644 index 0000000..78d294d --- /dev/null +++ b/04-data-agent/hf/tests/test_standalone_opencode.py @@ -0,0 +1,68 @@ +"""Regression checks for the standalone arm's token and task boundaries.""" +import importlib.util +import json +from pathlib import Path +import sys +import unittest +from unittest.mock import patch + +ROOT=Path(__file__).resolve().parents[4] +NATIVE=Path(__file__).resolve().parents[2]/'envs/blackbox-opencode' +sys.path.insert(0,str(ROOT/'OpenEnv/src')) +spec=importlib.util.spec_from_file_location('data_agent_env',NATIVE/'__init__.py',submodule_search_locations=[str(NATIVE)]) +module=importlib.util.module_from_spec(spec);sys.modules[spec.name]=module;spec.loader.exec_module(module) +from data_agent_env.models import DataAgentRolloutResult +from data_agent_env.server.rollout import turns_from_capture, _stage_inputs, _run_agent +from data_agent_env.harness import to_trace_entries +from data_agent_env.config import DataAgentConfig +from data_agent_env.tasks import _frozen_rows +from data_agent_env.task import DataAgentTask + +class StandaloneTests(unittest.TestCase): + def test_agent_deadline_terminates_background_process(self): + from unittest.mock import Mock + sandbox=Mock() + sandbox.start_bg.return_value.wait.side_effect=TimeoutError('agent budget expired') + code=_run_agent(sandbox,'http://capture','session','model',DataAgentConfig(),'solve this') + self.assertEqual(code,124) + sandbox.start_bg.return_value.kill.assert_called_once() + sandbox.exec.assert_not_called() + def test_background_transport_failure_remains_ungraded(self): + from unittest.mock import Mock + sandbox=Mock() + sandbox.start_bg.return_value.wait.side_effect=ConnectionError('transport unavailable') + with self.assertRaises(ConnectionError): + _run_agent(sandbox,'http://capture','session','model',DataAgentConfig(),'solve this') + def test_partial_and_zero_masks_survive_wire_boundary(self): + for mask in ([0,0,1,0],[0,0,0,0]): + raw={'prompt_token_ids':[1,2],'completion_token_ids':[3,4], + 'per_token_logps':[-.1,-.2],'loss_mask':mask} + result=DataAgentRolloutResult.model_validate_json(DataAgentRolloutResult( + rollout_type='train',turns=turns_from_capture([raw])).model_dump_json()) + entries=to_trace_entries(result) + self.assertEqual(entries[0]['loss_mask'] if entries else [],mask if any(mask) else []) + def test_bad_logprobs_cannot_become_trainable(self): + raw={'prompt_token_ids':[1,2],'completion_token_ids':[3,4], + 'per_token_logps':[-.1],'loss_mask':[0,0,1,1]} + with self.assertRaises(ValueError): + to_trace_entries(DataAgentRolloutResult(turns=turns_from_capture([raw]))) + def test_stage_token_is_transient_and_absent_from_shell(self): + from unittest.mock import Mock + task=DataAgentTask(task_id='fixture',instruction='question',answer='a',hf_bucket='owner/bucket',bucket_prefix='data') + sandbox=Mock();sandbox.exec.return_value.exit_code=0 + _stage_inputs(sandbox,task,'fixture-secret',DataAgentConfig()) + call=sandbox.exec.call_args + self.assertNotIn('fixture-secret',call.args[0]) + self.assertEqual(call.kwargs['envs']['HF_TOKEN'],'fixture-secret') + self.assertNotIn('HF_TOKEN',task.env(None)) + def test_fixed_catalog_preserves_identity_and_difficulty(self): + root=ROOT/'experiments/daytona_harness_comparison/logs/20260915/datasets' + if not root.exists():self.skipTest('frozen comparison dataset is unavailable') + from collections import Counter + rows=_frozen_rows(str(root),'test') + self.assertEqual(len(rows),250) + self.assertEqual(Counter(r['difficulty_tier'] for r in rows),{'easy':33,'medium':118,'hard':99}) + manifest=json.loads((root.parent/'test_manifest.json').read_text()) + self.assertEqual([r['task_id'] for r in rows],sorted(t['name'] for t in manifest['tasks'])) + +if __name__=='__main__':unittest.main() diff --git a/04-data-agent/hf/tests/test_standalone_training.py b/04-data-agent/hf/tests/test_standalone_training.py new file mode 100644 index 0000000..4ce7c30 --- /dev/null +++ b/04-data-agent/hf/tests/test_standalone_training.py @@ -0,0 +1,87 @@ +"""Exercise native scheduling, reward semantics and the exact capture admission audit.""" +import importlib.util +import json +from pathlib import Path +import pickle +import sys +import unittest +from unittest.mock import Mock, patch + +ROOT = Path(__file__).resolve().parents[2] +NATIVE = ROOT / "envs/blackbox-opencode" +sys.path.insert(0, str(ROOT / "train")) +spec = importlib.util.spec_from_file_location("data_agent_env", NATIVE / "__init__.py", submodule_search_locations=[str(NATIVE)]) +module = importlib.util.module_from_spec(spec) +sys.modules[spec.name] = module +spec.loader.exec_module(module) +from data_agent_env.models import DataAgentRolloutResult +from data_agent_env.tasks import DataAgentTaskProvider +from standalone_comparison import ScheduledOpenCodeFactory, ComparisonSession +from data_agent_env.client import DataAgentEnv + + +class StandaloneTrainingTests(unittest.TestCase): + def test_sampling_policy_reaches_the_wire_and_native_capture_registry(self): + from data_agent_env.server.capture import mint_session + from openenv.core.harness.capture.sessions import SessionRegistry + from openenv.core.harness.capture.upstream import training_sampling + policy = {"temperature": .8, "top_p": 1., "top_k": 0} + factory = self.factory() + client = object.__new__(DataAgentEnv) + calls = [] + def call(name, **kwargs): + calls.append((name, kwargs)) + return DataAgentRolloutResult().model_dump_json() + client._call = call + session = ComparisonSession(client, "train", 0, "fixture", **factory._rollout_kwargs) + session.wait_for_completion(timeout_s=123) + self.assertEqual(calls[0][1]["sampling"], policy) + self.assertEqual(calls[0][1]["_timeout_s"], 123) + server = Mock(registry=SessionRegistry()) + sid, _ = mint_session(server, llm_url="http://inference", model="fixture", + rollout_id="fixture", capture_level="tokens", sampling=calls[0][1]["sampling"]) + captured = server.registry.get(sid) + self.assertEqual(captured.sampling, training_sampling(policy)) + self.assertEqual(captured.sampling["top_k"], -1) + + def factory(self): + from harness_schedule import make_schedule + self.schedule = make_schedule([{ "name": f"fixture-{i}", "task_index": i, "difficulty": "easy"} for i in range(12)], ["opencode"], easy_start=4) + return ScheduledOpenCodeFactory("http://fixture", harnesses=["opencode"], schedule=self.schedule, + llm_url="http://inference", model="Qwen/Qwen3.5-2B", sampling={"temperature": .8, "top_p": 1., "top_k": 0}) + + def test_all_scheduled_groups_and_resume_preserve_task_identity(self): + factory = self.factory() + tasks = [{"index": t["task_index"], "task_id": t["name"], "instruction": t["name"]} for t in self.schedule["tasks"]] + client = Mock(); client.get_task_range.return_value = tasks + with patch.object(ScheduledOpenCodeFactory, "_new_client", return_value=client): + rows = factory.prompt_rows() + restored = pickle.loads(pickle.dumps(factory)) + for offset in (0, 997, 3999): + restored.group_offset = offset + for group_id in range(20): + expected = self.schedule["groups"][(offset + group_id) % len(self.schedule["groups"])] + row = rows[expected["task_row"]] + for generation in range(8): + session = restored.create(row, seed=group_id, episode_id=str(generation)) + self.assertEqual(session._task_index, expected["task_index"]) + restored.group_offset = 0 + with self.assertRaises(ValueError): + restored.create({"prompt": [{"role": "user", "content": "unknown task"}]}, seed=0) + + def test_reward_matches_binary_comparison_and_preserves_ungraded(self): + for correctness, raw_reward, expected in [(None, None, None), (0., 0., 0.), (.3, .3, 0.), (1., 1.1, 1.)]: + session = ComparisonSession(Mock(), "train", 0, "task") + session._result = DataAgentRolloutResult(correctness=correctness, reward=raw_reward) + self.assertEqual(session.verify([]).env_reward, expected) + self.assertEqual(session.result.reward, raw_reward) + + def test_changed_server_task_identity_is_rejected(self): + factory = self.factory() + tasks = [{"index": t["task_index"], "task_id": t["name"], "instruction": t["name"]} for t in self.schedule["tasks"]] + tasks[0]["task_id"] = "wrong" + client = Mock(); client.get_task_range.return_value = tasks + with patch.object(ScheduledOpenCodeFactory, "_new_client", return_value=client): + with self.assertRaises(ValueError): factory.prompt_rows() + +if __name__ == "__main__": unittest.main() diff --git a/04-data-agent/hf/tests/test_stop_hf_after_checkpoint.py b/04-data-agent/hf/tests/test_stop_hf_after_checkpoint.py new file mode 100644 index 0000000..20ba321 --- /dev/null +++ b/04-data-agent/hf/tests/test_stop_hf_after_checkpoint.py @@ -0,0 +1,45 @@ +"""A stop requires exact full-state checkpoint readback, including optimizer state.""" +import hashlib +import json +from pathlib import Path +import sys +from types import SimpleNamespace + +import pytest + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from stop_hf_after_checkpoint import READY, verify_checkpoint + + +def make_checkpoint(path): + files = {"model.safetensors": b"weights", "optimizer.pt": b"optimizer", + "scheduler.pt": b"scheduler", "rng_state.pth": b"rng", + "trainer_state.json": b'{"global_step":150}'} + for name, data in files.items(): + (path / name).write_bytes(data) + manifest = {"arm": "whitebox", "step": 150, "bundle_sha256": "bundle", + "base_model": "Qwen/Qwen3.5-2B", "base_revision": "15852e8c16360a2fea060d615a32b45270f8a8fc", + "files": {n: hashlib.sha256(v).hexdigest() for n, v in files.items()}} + (path / READY).write_text(json.dumps(manifest)) + return SimpleNamespace(environment={"BUNDLE_SHA256": "bundle"}) + + +def test_complete_state_can_authorize_stop(tmp_path): + job = make_checkpoint(tmp_path) + assert verify_checkpoint(tmp_path, job=job, step=150)["step"] == 150 + + +def test_corrupt_optimizer_prevents_stop(tmp_path): + job = make_checkpoint(tmp_path) + (tmp_path / "optimizer.pt").write_bytes(b"partial upload") + with pytest.raises(ValueError, match="hash mismatch"): + verify_checkpoint(tmp_path, job=job, step=150) + + +def test_wrong_checkpoint_or_bundle_prevents_stop(tmp_path): + job = make_checkpoint(tmp_path) + with pytest.raises(ValueError, match="provenance"): + verify_checkpoint(tmp_path, job=job, step=200) + job.environment["BUNDLE_SHA256"] = "other" + with pytest.raises(ValueError, match="provenance"): + verify_checkpoint(tmp_path, job=job, step=150) diff --git a/04-data-agent/hf/tests/test_training_runtime.py b/04-data-agent/hf/tests/test_training_runtime.py new file mode 100644 index 0000000..5366e6f --- /dev/null +++ b/04-data-agent/hf/tests/test_training_runtime.py @@ -0,0 +1,308 @@ +"""Failure-boundary checks for HF checkpoint dispatch, model loading and launch gates.""" +import copy +import json +import os +from pathlib import Path +import shutil +import sys +import tempfile +from types import SimpleNamespace +import unittest +from unittest.mock import patch + +HF = Path(__file__).resolve().parents[1] +sys.path[:0] = [str(HF), str(HF / "runtime"), str(HF.parent / "train")] +from checkpoint_store import READY, digest, restore_model +from common import MODEL, REVISION +from coordinator import eligible, evaluation_key, submit_once +from launch_gates import validate_proofs, validate_checkpoint_eval + + +class DispatchTest(unittest.TestCase): + def test_ready_steps_and_final(self): + self.assertFalse(eligible({"step": 50}, 100, True)) + self.assertTrue(eligible({"step": 100}, 100, True)) + self.assertTrue(eligible({"step": 151, "final": True}, 100, True)) + self.assertFalse(eligible({"step": 0, "final": True}, 100, True)) + + def test_manifest_and_protocol_bind_eval_identity(self): + key = evaluation_key("train1", "hash1", {"temperature": 0.8}) + self.assertNotEqual(key, evaluation_key("train1", "hash2", {"temperature": 0.8})) + self.assertNotEqual(key, evaluation_key("train1", "hash1", {"temperature": 0.7})) + + def test_ambiguous_submission_is_not_repeated(self): + state, snapshots, calls = {}, [], [] + def persist(): snapshots.append(copy.deepcopy(state)) + def launch(): + calls.append(1) + raise TimeoutError("server may already have accepted the job") + with self.assertRaises(TimeoutError): + submit_once(state, "key", [], persist, launch) + self.assertEqual(snapshots[0]["key"]["status"], "submitting") + with self.assertRaisesRegex(RuntimeError, "Unresolved"): + submit_once(state, "key", [], persist, launch) + self.assertEqual(len(calls), 1) + + def test_adopt_job_after_ambiguous_response(self): + state = {"key": {"status": "submitting"}} + job = SimpleNamespace(id="accepted", labels={"evaluation_key": "key"}) + with patch("builtins.print"): + actual = submit_once(state, "key", [job], lambda: None, + lambda: self.fail("must adopt, not submit")) + self.assertIs(actual, job) + self.assertEqual(state["key"]["job_id"], "accepted") + + def test_failed_intent_persistence_never_submits(self): + def persist(): raise OSError("remote storage unavailable") + with self.assertRaises(OSError): + submit_once({}, "key", [], persist, lambda: self.fail("unsafe submission")) + + +class LaunchGateTest(unittest.TestCase): + def setUp(self): + self.config = json.loads((HF / "configs/deployment.json").read_text()) + self.baseline = {"arm": "blackbox", "comparison_ready": True, "graded_cells": 1000, "tito_pass": True} + self.smoke = {"arm": "blackbox", "passed": True, "bundle_sha256": "new", "remote_restore_verified": True, + "tito_pass": True, "weights_updated": True, "native_optimizer_state_verified": True} + + def check(self, config=None, baseline=None, smoke=None): + validate_proofs(self.config, {"sha256": "new"}, "blackbox", config or self.config, + baseline or self.baseline, smoke or self.smoke) + + def test_matching_evidence(self): self.check() + + def test_native_diagnostic_cannot_be_a_four_harness_curve_baseline(self): + from launch_gates import validate_comparison_baseline + native = {"comparison_ready": True, "graded_cells": 250, "tito_pass": True, + "implementation": "standalone-opencode"} + with self.assertRaisesRegex(ValueError, "four-harness"): + validate_comparison_baseline(native) + matching = {"comparison_ready": True, "graded_cells": 1000, "tito_pass": True, + "harnesses": {name: {"graded": 250} for name in self.config["harness_pins"]}} + validate_comparison_baseline(matching) + matching["harnesses"]["opencode"]["graded"] = 249 + with self.assertRaises(ValueError): + validate_comparison_baseline(matching) + + def test_dataset_change_rejected(self): + old = copy.deepcopy(self.config) + old["data"]["manifest_sha256"]["test_manifest.json"] = "different" + with self.assertRaises(ValueError): self.check(config=old) + + def test_incomplete_baseline_rejected(self): + with self.assertRaises(ValueError): self.check(baseline={**self.baseline, "graded_cells": 999}) + + def test_old_smoke_bundle_rejected(self): + with self.assertRaises(ValueError): self.check(smoke={**self.smoke, "bundle_sha256": "old"}) + + def test_checkpoint_gate_binds_complete_scores_to_actual_smoke_weights(self): + job = SimpleNamespace(status=SimpleNamespace(stage="COMPLETED"), + labels={"role": "eval", "phase": "checkpoint", "arm": "whitebox", "training_job": "smoke"}, + environment={"BUNDLE_SHA256": "runtime", "CHECKPOINT_MANIFEST_SHA": "manifest", + "CHECKPOINT_STEP": "4", "CHECKPOINT_PREFIX": "bucket/smoke/checkpoint-4"}) + score = {"comparison_ready": True, "tito_pass": True, "graded_cells": 250, "arm": "whitebox"} + evidence = {"step": 4, "bundle_sha256": "runtime", "manifest_sha256": "manifest", + "source": "bucket/smoke/checkpoint-4"} + validate_checkpoint_eval({"sha256": "runtime"}, "whitebox", "smoke", job, score, evidence) + for changed in [{**score, "graded_cells": 249}, {**score, "tito_pass": False}]: + with self.assertRaises(ValueError): + validate_checkpoint_eval({"sha256": "runtime"}, "whitebox", "smoke", job, changed, evidence) + with self.assertRaises(ValueError): + validate_checkpoint_eval({"sha256": "runtime"}, "whitebox", "other-smoke", job, score, evidence) + with self.assertRaises(ValueError): + validate_checkpoint_eval({"sha256": "runtime"}, "whitebox", "smoke", job, score, + {**evidence, "manifest_sha256": "other-weights"}) + + def test_dry_run_validates_without_submitting_any_job(self): + from deploy import submit + from unittest.mock import Mock + with tempfile.TemporaryDirectory() as temporary: + out = Path(temporary) + (out / "bundle_uploaded.json").write_text(json.dumps({"sha256": "runtime", "repo": "org/repro", "revision": "pin"})) + config = copy.deepcopy(self.config) + config.pop("training_launch_hold", None) + args = SimpleNamespace(role="train", phase="long", arm="whitebox", baseline_job="baseline", + smoke_job="smoke", checkpoint_eval_job="eval", flavor="h200x2", training_job=None, + external_checkpoint_coordinator=True, dp=1, limit=0, resume_eval_owner=None, + timeout="24h", dry_run=True) + api = Mock() + api.space_info.return_value = SimpleNamespace(host="https://example.hf.space") + proof = {"smoke_prefix": "smoke", "baseline_prefix": "base", "space_bundle_sha256": "space", + "space_url": "https://example.hf.space"} + with patch("launch_gates.verify", return_value=proof) as smoke_gate, \ + patch("launch_gates.verify_checkpoint_eval", return_value={"passed": True}) as eval_gate, \ + patch("builtins.print"): + preview = submit(api, config, {"HF_TOKEN": "never-record-this"}, out, args) + smoke_gate.assert_called_once() + eval_gate.assert_called_once() + api.run_job.assert_not_called() + self.assertFalse(preview["submitted"]) + self.assertNotIn("never-record-this", (out / "launch-preview.json").read_text()) + + def test_native_launch_logs_comparison_baseline_and_retains_diagnostic(self): + from deploy import submit + from unittest.mock import Mock + with tempfile.TemporaryDirectory() as temporary: + out = Path(temporary) + (out / "bundle_uploaded.json").write_text(json.dumps({"sha256": "runtime", "repo": "org/repro", "revision": "pin"})) + args = SimpleNamespace(role="train", phase="long", arm="opencode", baseline_job="native", + comparison_baseline_job="four-harness", smoke_job="smoke", flavor="a100x4", + training_job=None, external_checkpoint_coordinator=False, dp=1, limit=0, + resume_eval_owner=None, timeout="24h", dry_run=True) + api = Mock() + api.space_info.return_value = SimpleNamespace(host="https://example.hf.space") + proof = {"smoke_prefix": "smoke", "baseline_prefix": "native-prefix", "space_bundle_sha256": "space", + "space_url": "https://example.hf.space"} + response = Mock() + response.raise_for_status.return_value.json.return_value = { + "arm": "blackbox", "test_tasks": 250, "bundle_sha256": "space"} + with patch("launch_gates.verify", return_value=proof), \ + patch("launch_gates.verify_comparison_baseline", return_value="comparison-prefix") as gate, \ + patch("runtime.service_contract.check", return_value={"passed": True}), \ + patch("httpx.get", return_value=response), patch("builtins.print"): + result = submit(api, self.config, {"HF_TOKEN": "test-only"}, out, args) + gate.assert_called_once_with(api, self.config, "four-harness", out) + self.assertEqual(result["environment"]["BASELINE_PREFIX"], "comparison-prefix") + self.assertEqual(result["environment"]["BASELINE_JOB"], "four-harness") + self.assertEqual(result["environment"]["NATIVE_BASELINE_PREFIX"], "native-prefix") + api.run_job.assert_not_called() + + +class ModelRestoreTest(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.root = Path(self.tmp.name) + self.source = self.root / "remote" + self.source.mkdir() + import torch + from safetensors.torch import save_file + save_file({"weight": torch.tensor([1.0, 2.0])}, self.source / "model.safetensors") + for name in ["config.json", "tokenizer.json", "tokenizer_config.json"]: + (self.source / name).write_text("{}") + (self.source / "optimizer.pt").write_bytes(b"optimizer must not be fetched for inference") + manifest = {"arm": "blackbox", "bundle_sha256": "bundle", "step": 100, + "base_model": MODEL, "base_revision": REVISION, + "files": {p.name: digest(p) for p in self.source.iterdir()}} + (self.source / READY).write_text(json.dumps(manifest)) + self.sha = digest(self.source / READY) + self.downloaded = [] + case = self + class API: + def download_bucket_files(self, bucket, *, files, **kwargs): + for name, target in files: + case.downloaded.append(Path(name).name) + shutil.copy2(case.source / Path(name).name, target) + self.api = API + + def tearDown(self): self.tmp.cleanup() + + def run_restore(self): + with patch("huggingface_hub.HfApi", self.api): + return restore_model("hf://buckets/org/bucket/run/checkpoint-100", self.root / "model", + arm="blackbox", bundle_sha256="bundle", manifest_sha256=self.sha) + + def test_model_only_restore_has_exact_hashes(self): + self.assertEqual(self.run_restore()["step"], 100) + self.assertNotIn("optimizer.pt", self.downloaded) + self.assertEqual(digest(self.root / "model/model.safetensors"), digest(self.source / "model.safetensors")) + + def test_changed_manifest_is_rejected_before_weights(self): + (self.source / READY).write_text((self.source / READY).read_text() + " ") + with self.assertRaisesRegex(ValueError, "manifest changed"): self.run_restore() + self.assertNotIn("model.safetensors", self.downloaded) + + def test_changed_model_is_rejected(self): + (self.source / "model.safetensors").write_bytes(b"tampered") + with self.assertRaisesRegex(ValueError, "model hash mismatch"): self.run_restore() + + +class CoordinatorLifecycleTest(unittest.TestCase): + def test_saved50_eval100_and_off_interval_final150(self): + self.exercise_lifecycle() + + def test_user_stopped_job_evaluates_only_verified_final_checkpoint(self): + self.exercise_lifecycle(stage="CANCELED", requested=True, expected=[150]) + + def test_normal_cancellation_does_not_launch_an_evaluation(self): + self.exercise_lifecycle(stage="CANCELED", expected=[]) + + def exercise_lifecycle(self, stage="RUNNING", requested=False, expected=None): + from huggingface_hub.errors import EntryNotFoundError + import coordinator + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + config = json.loads((HF / "configs/deployment.json").read_text()) + (root / "hf/configs").mkdir(parents=True) + (root / "hf/configs/deployment.json").write_text(json.dumps(config)) + remote = {} + prefix = "run/jobs/train-owner/run" + folders = [prefix + f"/checkpoint-{n}" for n in [50, 100, 150]] + for n, folder in zip([50, 100, 150], folders): + remote[folder + "/" + READY] = json.dumps({"arm": "blackbox", "bundle_sha256": "bundle", "step": n}) + submitted, snapshots = [], [] + trainer = SimpleNamespace(labels={"role": "train", "arm": "blackbox"}, + environment={"RUN_OWNER": "train-owner", "BUNDLE_SHA256": "bundle", "SPACE_URL": "https://environment.invalid", + "SPACE_BUNDLE_SHA256": "qualified-environment-bundle"}, + status=SimpleNamespace(stage=stage)) + peer = SimpleNamespace(id="coord1", status=SimpleNamespace(stage="RUNNING"), environment={"RUN_OWNER": "coord-owner"}) + class API: + def inspect_job(self, **kwargs): + if submitted and stage == "RUNNING": trainer.status.stage = "COMPLETED" + return trainer + def list_jobs(self, **kwargs): + return [peer] if kwargs["labels"]["role"] == "coordinator" else submitted + def list_bucket_tree(self, bucket, **kwargs): + return [SimpleNamespace(path=f) for f in folders] + def download_bucket_files(self, bucket, *, files, **kwargs): + for source, dest in files: + if source not in remote: raise EntryNotFoundError(source) + Path(dest).write_text(remote[source]) + def sync_bucket(self, source, dest, **kwargs): + snapshots.append(json.loads((Path(source) / "state.json").read_text())) + def run_job(self, **kwargs): + self_case.assertTrue(snapshots[-1][kwargs["labels"]["evaluation_key"]]["status"] == "submitting") + self_case.assertEqual(kwargs["flavor"], "a100-large") + self_case.assertEqual(kwargs["env"]["SPACE_BUNDLE_SHA256"], "qualified-environment-bundle") + job = SimpleNamespace(id=f"eval{len(submitted)}", labels=kwargs["labels"], + environment=kwargs["env"], status=SimpleNamespace(stage="COMPLETED")) + owner = kwargs["env"]["RUN_OWNER"] + remote[f"run/jobs/{owner}/canonical_scores.json"] = json.dumps({"comparison_ready": True}) + remote[f"run/jobs/{owner}/checkpoint_evaluation.json"] = json.dumps({"manifest_sha256": kwargs["env"]["CHECKPOINT_MANIFEST_SHA"]}) + submitted.append(job) + return job + self_case = self + clock = [0] + def sleep(seconds): + clock[0] += seconds + if clock[0] > 300: self.fail("coordinator failed to drain") + env = {"TRAINING_JOB": "train1", "ARTIFACT_BUCKET": "org/bucket", "RUN_ID": "run", + "RUN_OWNER": "coord-owner", "BUNDLE_SHA256": "bundle", "BUNDLE_REPO": "org/bundle", + "BUNDLE_REVISION": "revision", "HF_TOKEN": "test-token"} + request = None + if requested: + import hashlib + request = {"training_job": "train1", "step": 150, + "checkpoint": "hf://buckets/org/bucket/" + folders[-1], + "manifest_sha256": hashlib.sha256(remote[folders[-1] + "/" + READY].encode()).hexdigest(), + "user_requested_stop": True, "full_checkpoint_verified": True} + with patch.dict(os.environ, env), patch("coordinator.ROOT", root), patch("huggingface_hub.HfApi", API), \ + patch("coordinator.time.sleep", sleep), patch("coordinator.time.monotonic", lambda: clock[0]): + coordinator.run(root / "output", "blackbox", final_checkpoint=request) + expected = [100, 150] if expected is None else expected + self.assertEqual([int(j.environment["CHECKPOINT_STEP"]) for j in submitted], expected) + self.assertEqual(len(list((root / "output/decisions/scores").glob("*.json"))), len(expected)) + + def test_stop_authorization_rejects_changed_checkpoint_or_job(self): + from coordinator import validate_requested_final + request = {"training_job": "train1", "step": 150, "checkpoint": "bucket/cp150", + "manifest_sha256": "hash", "user_requested_stop": True, "full_checkpoint_verified": True} + self.assertTrue(validate_requested_final(request, "train1", "bucket/cp150", {"step": 150}, "hash")) + for key, value in [("training_job", "train2"), ("step", 100), ("checkpoint", "bucket/cp100"), + ("manifest_sha256", "changed"), ("full_checkpoint_verified", False), + ("user_requested_stop", False)]: + with self.subTest(key=key), self.assertRaises(ValueError): + validate_requested_final({**request, key: value}, "train1", "bucket/cp150", {"step": 150}, "hash") + + +if __name__ == "__main__": + unittest.main() diff --git a/04-data-agent/hf/tests/test_vllm_control_http.py b/04-data-agent/hf/tests/test_vllm_control_http.py new file mode 100644 index 0000000..a0a96d3 --- /dev/null +++ b/04-data-agent/hf/tests/test_vllm_control_http.py @@ -0,0 +1,69 @@ +"""Real HTTP response-contract checks without importing the GPU inference stack.""" +import ast +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +import threading +import unittest + +import requests + +WORKSPACE = Path(__file__).resolve().parents[4] + + +class ControlHTTPTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + import importlib.util + source = Path(importlib.util.find_spec("trl").origin).parent / "generation/vllm_client.py" + tree = ast.parse(source.read_text()) + client = next(node for node in tree.body if isinstance(node, ast.ClassDef) and node.name == "VLLMClient") + # Execute the production HTTP methods verbatim; constructors require GPUs. + client.body = [node for node in client.body if isinstance(node, ast.FunctionDef) + and node.name in {"_post", "reset_prefix_cache"}] + namespace = {} + exec(compile(ast.Module(body=[client], type_ignores=[]), str(source), "exec"), namespace) + cls.client_type = namespace["VLLMClient"] + + def setUp(self): + self.status, self.body, self.paths = 200, b"", [] + case = self + class Handler(BaseHTTPRequestHandler): + def do_POST(self): + case.paths.append(self.path) + self.send_response(case.status) + self.send_header("Content-Length", str(len(case.body))) + self.end_headers() + self.wfile.write(case.body) + def log_message(self, *args): + pass + self.server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + self.thread = threading.Thread(target=self.server.serve_forever, daemon=True) + self.thread.start() + self.client = self.client_type() + self.client.base_url = f"http://127.0.0.1:{self.server.server_port}" + self.client.session = requests.Session() + + def tearDown(self): + self.client.session.close() + self.server.shutdown() + self.server.server_close() + self.thread.join() + + def test_empty_reset_success(self): + self.assertIsNone(self.client.reset_prefix_cache()) + self.assertEqual(self.paths, ["/reset_prefix_cache"]) + + def test_reset_failure_is_not_suppressed(self): + self.status, self.body = 500, b"cache reset failed" + with self.assertRaisesRegex(Exception, "500, cache reset failed"): + self.client.reset_prefix_cache() + + def test_structured_response_still_requires_json(self): + with self.assertRaises(requests.exceptions.JSONDecodeError): + self.client._post(self.client.base_url + "/v1/completions") + self.body = b'{"choices": []}' + self.assertEqual(self.client._post(self.client.base_url + "/v1/completions"), {"choices": []}) + + +if __name__ == "__main__": + unittest.main() diff --git a/04-data-agent/hf/tests/test_whitebox_tito.py b/04-data-agent/hf/tests/test_whitebox_tito.py new file mode 100644 index 0000000..b670dc0 --- /dev/null +++ b/04-data-agent/hf/tests/test_whitebox_tito.py @@ -0,0 +1,40 @@ +import sys +from pathlib import Path +import unittest + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'runtime')) +from whitebox_tito import audit_rows + + +class WhiteboxProvenanceTest(unittest.TestCase): + def test_identical_tokens_with_distinct_observed_logprobs(self): + # Repeated GRPO samples can emit identical tokens with slightly different + # floating-point logprobs. Matching the first token-identical call is wrong. + calls = [{'prompt_ids': [1], 'completion_ids': [2], 'logprobs': [p]} for p in [-.1, -.10001]] + result = audit_rows([[1], [1]], [[2], [2]], [[1], [1]], [[-.10001], [-.1]], calls) + self.assertTrue(all(row['tito_pass'] for row in result)) + + def test_one_call_cannot_prove_two_occurrences(self): + calls = [{'prompt_ids': [1], 'completion_ids': [2], 'logprobs': [-.1]}] + with self.assertRaisesRegex(AssertionError, 'distinct engine call'): + audit_rows([[1], [1]], [[2], [2]], [[1], [1]], [[-.1], [-.1]], calls) + + def test_changed_probability_is_rejected(self): + calls = [{'prompt_ids': [1], 'completion_ids': [2], 'logprobs': [-.1]}] + with self.assertRaisesRegex(AssertionError, 'provenance'): + audit_rows([[1]], [[2]], [[1]], [[-.2]], calls) + + def test_tool_context_and_budget_trim_preserve_exact_prefix(self): + calls = [{'prompt_ids': [1], 'completion_ids': [2], 'logprobs': [-.1]}, + {'prompt_ids': [1, 2, 3], 'completion_ids': [4, 5], 'logprobs': [-.2, -.3]}] + result = audit_rows([[1]], [[2, 3, 4]], [[1, 0, 1]], [[-.1, 0., -.2]], calls) + self.assertEqual(result[0]['supervised'], 2) + + def test_ambiguous_truncation_preserves_occurrences(self): + calls = [{'prompt_ids': [1], 'completion_ids': [2, 3], 'logprobs': [-.1, -.2]}, + {'prompt_ids': [1], 'completion_ids': [2], 'logprobs': [-.1]}] + self.assertEqual(len(audit_rows([[1], [1]], [[2], [2, 3]], [[1], [1, 1]], + [[-.1], [-.1, -.2]], calls)), 2) + + +if __name__ == '__main__': unittest.main() diff --git a/04-data-agent/hf/tests/ui_smoke.py b/04-data-agent/hf/tests/ui_smoke.py new file mode 100644 index 0000000..b8ead2d --- /dev/null +++ b/04-data-agent/hf/tests/ui_smoke.py @@ -0,0 +1,8 @@ +"""CLI wrapper for the packaged deployment smoke.""" +from pathlib import Path +import sys +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "runtime")) +from ui_smoke import main + +if __name__ == "__main__": + main() diff --git a/04-data-agent/hf/tests/ws_idle_probe.py b/04-data-agent/hf/tests/ws_idle_probe.py new file mode 100644 index 0000000..47bb068 --- /dev/null +++ b/04-data-agent/hf/tests/ws_idle_probe.py @@ -0,0 +1,46 @@ +"""Compare idle HF Space WebSockets with/without keepalive; creates no sandboxes.""" +import argparse +import asyncio +import json +from pathlib import Path +import time + +from dotenv import dotenv_values +from websockets.asyncio.client import connect + + +async def main(args): + token = dotenv_values(args.env_file)["HF_API_KEY"] + url = args.url.replace("https://", "wss://").rstrip("/") + "/ws" + async def one(interval): + row = {"ping_interval_s": interval, "idle_seconds": args.seconds, "started_at": time.time()} + try: + async with connect(url, additional_headers={"Authorization": "Bearer " + token}, + max_size=104857600, ping_interval=interval, ping_timeout=None, + open_timeout=30) as ws: + await ws.send('{"type":"state"}') + initial = json.loads(await asyncio.wait_for(ws.recv(), 30)) + assert initial.get("type") != "error", "Native state request rejected" + print(json.dumps({"connected": True, "ping_interval_s": interval}), flush=True) + await asyncio.sleep(args.seconds) + await ws.send('{"type":"state"}') + after = json.loads(await asyncio.wait_for(ws.recv(), 30)) + assert after.get("type") != "error", "State request rejected after idle" + row["passed"] = True + except Exception as exc: + row.update(passed=False, error_type=type(exc).__name__) + row["finished_at"] = time.time() + print(json.dumps(row), flush=True) + return row + rows = await asyncio.gather(one(None), one(20)) + args.out.parent.mkdir(parents=True, exist_ok=True) + args.out.write_text(json.dumps({"url": args.url, "results": rows}, indent=2) + "\n") + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--url", required=True) + p.add_argument("--env-file", required=True) + p.add_argument("--out", type=Path, required=True) + p.add_argument("--seconds", type=int, default=720) + asyncio.run(main(p.parse_args())) diff --git a/04-data-agent/hf/trackio_app.py b/04-data-agent/hf/trackio_app.py new file mode 100644 index 0000000..1c32055 --- /dev/null +++ b/04-data-agent/hf/trackio_app.py @@ -0,0 +1,24 @@ +"""Open the current training project first in the pinned Trackio 0.33.0 UI.""" +import os + +import trackio +from trackio import server + + +DEFAULT_PROJECT = os.environ.get( + "TRACKIO_DEFAULT_PROJECT", "multi4-qwen35-2b-prod-20260915" +) +_get_all_projects = server.get_all_projects + + +def get_all_projects() -> list[str]: + # The browser selects the first project at the bare Space URL. The project + # argument to show() only changes its printed/browser-launch URL. + # Keep every historical project available in the normal project picker. + return sorted(_get_all_projects(), key=lambda name: name != DEFAULT_PROJECT) + + +server.get_all_projects = get_all_projects + +if __name__ == "__main__": + trackio.show() diff --git a/04-data-agent/project.yaml b/04-data-agent/project.yaml new file mode 100644 index 0000000..2b4a6ba --- /dev/null +++ b/04-data-agent/project.yaml @@ -0,0 +1,43 @@ +name: data-agent +title: Data Agent +tagline: Three agent-loop implementations with exact-token training, fixed pass@1 evaluations, and local or HF Jobs + reproduction. +order: 4 +status: trained +hub: + org: HuggingEnvs + datasets: + - HuggingEnvs/data-agent + - HuggingEnvs/data-agent-harbor-train + - HuggingEnvs/data-agent-harbor-test + - HuggingEnvs/data-agent-harbor-eval +envs: +- name: blackbox-opencode + summary: Native OpenCode owns the sandbox agent loop; capture retains exact engine tokens, log probabilities and + loss masks. + backend: e2b / hf / daytona + tools: agent-owned + frameworks: + openenv: + transport: http-mcp + verified: true + space: HuggingEnvs/data-agent-blackbox-opencode-env +- name: blackbox-harbor + summary: Harbor serves the fixed tasks through multiple harnesses, with exact capture for asynchronous training. + backend: Harbor / Daytona / E2B + tools: agent-owned + frameworks: + openenv: + transport: http-mcp + verified: true + space: HuggingEnvs/data-agent-blackbox-harbor-env +- name: whitebox-bash + summary: TRL owns the synchronous bash/SETA tool loop; the frozen-task adapter preserves the shared grading contract. + backend: e2b / daytona + tools: bash, read, write, edit, grep, glob, ls, submit_solution + trainer: sync + frameworks: + openenv: + transport: http-mcp + verified: true + space: HuggingEnvs/data-agent-seta-whitebox-env diff --git a/04-data-agent/reports/async-comparison-20260916/REPORT.md b/04-data-agent/reports/async-comparison-20260916/REPORT.md new file mode 100644 index 0000000..c21c133 --- /dev/null +++ b/04-data-agent/reports/async-comparison-20260916/REPORT.md @@ -0,0 +1,317 @@ +# Harbor and OpenCode — consolidated training and pass@1 + +Updated: 2026-09-17T10:25:46.725435+00:00 + +[Live Trackio dashboard](https://huggingface.co/spaces/HuggingEnvs/data-agent-training-comparison-trackio) · [Overview image](comparison.png) · [Accepted checkpoint counts](checkpoint_scores.csv) + +Qwen3.5-2B; 1,000 optimizer-step target per run. Recorded training: Harbor multi-harness: 1000 steps; Native OpenCode: 1000 steps; Harbor OpenCode-only: 1000 steps. Every accepted checkpoint has 250 fixed tasks × four harnesses = 1,000 grades. Task difficulty: 33 easy, 118 medium, 99 hard (13.2% / 47.2% / 39.6%). Scores retain first graded attempts; incomplete and failed-audit evaluations are excluded. Missing scores are not estimated. + +Baselines are separate measured cohorts: Harbor/E2B 14.6%; Harbor/Daytona 15.9% for the native OpenCode checkpoint evaluator. The standalone native OpenCode 8.4% baseline uses a different harness protocol and is excluded here. Infrastructure and training recipe histories differ; this is an observational comparison, not a controlled causal experiment. + +## Overall checkpoint curve + +| Checkpoint | Harbor multi-harness | Native OpenCode | Harbor OpenCode-only | +| --- | ---: | ---: | ---: | +| 0 (baseline) | 14.6% | 15.9% | 14.6% | +| 100 | 24.8% | 19.7% | 24.5% | +| 200 | 26.3% | 22.1% | 27.1% | +| 300 | 28.6% | 21.6% | 28.5% | +| 400 | 33.3% | 26.4% | 32.8% | +| 500 | 37.0% | 23.1% | 33.0% | +| 600 | 31.8% | 25.1% | 33.0% | +| 684 (recovery) | 32.1% | Not scheduled | Not scheduled | +| 700 | 28.8% | 23.2% | 39.5% | +| 800 | 27.0% | 25.6% | 33.1% | +| 900 | 22.7% | 25.3% | 29.6% | +| 1000 | 26.3% | 29.8% | 26.4% | + +## Harbor multi-harness + +### Overall and difficulty + +| Checkpoint | Overall | Easy (132 cells) | Medium (472) | Hard (396) | +| --- | ---: | ---: | ---: | ---: | +| 0 | 14.6% | 40.2% | 14.4% | 6.3% | +| 100 | 24.8% | 53.0% | 30.5% | 8.6% | +| 200 | 26.3% | 58.3% | 30.1% | 11.1% | +| 300 | 28.6% | 64.4% | 32.8% | 11.6% | +| 400 | 33.3% | 73.5% | 39.6% | 12.4% | +| 500 | 37.0% | 72.7% | 44.3% | 16.4% | +| 600 | 31.8% | 72.7% | 37.3% | 11.6% | +| 684 | 32.1% | 75.8% | 35.8% | 13.1% | +| 700 | 28.8% | 60.6% | 33.9% | 12.1% | +| 800 | 27.0% | 59.1% | 32.6% | 9.6% | +| 900 | 22.7% | 46.2% | 26.1% | 10.9% | +| 1000 | 26.3% | 42.4% | 32.6% | 13.4% | + +### Harness × difficulty at every checkpoint + +| Checkpoint | Harness | Overall (250) | Easy (33) | Medium (118) | Hard (99) | +| --- | --- | ---: | ---: | ---: | ---: | +| 0 | opencode | 10.8% | 33.3% (11/33) | 8.5% (10/118) | 6.1% (6/99) | +| 0 | claude-code | 16.8% | 42.4% (14/33) | 18.6% (22/118) | 6.1% (6/99) | +| 0 | codex | 16.4% | 42.4% (14/33) | 16.9% (20/118) | 7.1% (7/99) | +| 0 | mini-swe-agent | 14.4% | 42.4% (14/33) | 13.6% (16/118) | 6.1% (6/99) | +| 100 | opencode | 24.4% | 51.5% (17/33) | 31.4% (37/118) | 7.1% (7/99) | +| 100 | claude-code | 27.6% | 60.6% (20/33) | 32.2% (38/118) | 11.1% (11/99) | +| 100 | codex | 28.0% | 57.6% (19/33) | 35.6% (42/118) | 9.1% (9/99) | +| 100 | mini-swe-agent | 19.2% | 42.4% (14/33) | 22.9% (27/118) | 7.1% (7/99) | +| 200 | opencode | 30.4% | 51.5% (17/33) | 34.7% (41/118) | 18.2% (18/99) | +| 200 | claude-code | 30.0% | 63.6% (21/33) | 33.1% (39/118) | 15.2% (15/99) | +| 200 | codex | 26.4% | 63.6% (21/33) | 29.7% (35/118) | 10.1% (10/99) | +| 200 | mini-swe-agent | 18.4% | 54.5% (18/33) | 22.9% (27/118) | 1.0% (1/99) | +| 300 | opencode | 29.6% | 60.6% (20/33) | 33.1% (39/118) | 15.2% (15/99) | +| 300 | claude-code | 33.2% | 66.7% (22/33) | 39.0% (46/118) | 15.2% (15/99) | +| 300 | codex | 29.6% | 69.7% (23/33) | 33.1% (39/118) | 12.1% (12/99) | +| 300 | mini-swe-agent | 22.0% | 60.6% (20/33) | 26.3% (31/118) | 4.0% (4/99) | +| 400 | opencode | 32.8% | 72.7% (24/33) | 38.1% (45/118) | 13.1% (13/99) | +| 400 | claude-code | 36.8% | 75.8% (25/33) | 44.9% (53/118) | 14.1% (14/99) | +| 400 | codex | 34.8% | 72.7% (24/33) | 42.4% (50/118) | 13.1% (13/99) | +| 400 | mini-swe-agent | 28.8% | 72.7% (24/33) | 33.1% (39/118) | 9.1% (9/99) | +| 500 | opencode | 32.8% | 69.7% (23/33) | 40.7% (48/118) | 11.1% (11/99) | +| 500 | claude-code | 44.8% | 75.8% (25/33) | 52.5% (62/118) | 25.3% (25/99) | +| 500 | codex | 39.2% | 75.8% (25/33) | 46.6% (55/118) | 18.2% (18/99) | +| 500 | mini-swe-agent | 31.2% | 69.7% (23/33) | 37.3% (44/118) | 11.1% (11/99) | +| 600 | opencode | 34.0% | 66.7% (22/33) | 41.5% (49/118) | 14.1% (14/99) | +| 600 | claude-code | 30.0% | 72.7% (24/33) | 33.9% (40/118) | 11.1% (11/99) | +| 600 | codex | 32.4% | 66.7% (22/33) | 39.8% (47/118) | 12.1% (12/99) | +| 600 | mini-swe-agent | 30.8% | 84.8% (28/33) | 33.9% (40/118) | 9.1% (9/99) | +| 684 | opencode | 29.2% | 72.7% (24/33) | 30.5% (36/118) | 13.1% (13/99) | +| 684 | claude-code | 35.6% | 72.7% (24/33) | 41.5% (49/118) | 16.2% (16/99) | +| 684 | codex | 33.2% | 84.8% (28/33) | 36.4% (43/118) | 12.1% (12/99) | +| 684 | mini-swe-agent | 30.4% | 72.7% (24/33) | 34.7% (41/118) | 11.1% (11/99) | +| 700 | opencode | 21.2% | 30.3% (10/33) | 28.0% (33/118) | 10.1% (10/99) | +| 700 | claude-code | 31.6% | 78.8% (26/33) | 34.7% (41/118) | 12.1% (12/99) | +| 700 | codex | 32.0% | 72.7% (24/33) | 34.7% (41/118) | 15.2% (15/99) | +| 700 | mini-swe-agent | 30.4% | 60.6% (20/33) | 38.1% (45/118) | 11.1% (11/99) | +| 800 | opencode | 23.2% | 36.4% (12/33) | 33.1% (39/118) | 7.1% (7/99) | +| 800 | claude-code | 32.0% | 66.7% (22/33) | 37.3% (44/118) | 14.1% (14/99) | +| 800 | codex | 26.8% | 66.7% (22/33) | 31.4% (37/118) | 8.1% (8/99) | +| 800 | mini-swe-agent | 26.0% | 66.7% (22/33) | 28.8% (34/118) | 9.1% (9/99) | +| 900 | opencode | 2.4% | 0.0% (0/33) | 3.4% (4/118) | 2.0% (2/99) | +| 900 | claude-code | 36.4% | 72.7% (24/33) | 43.2% (51/118) | 16.2% (16/99) | +| 900 | codex | 19.2% | 48.5% (16/33) | 20.3% (24/118) | 8.1% (8/99) | +| 900 | mini-swe-agent | 32.8% | 63.6% (21/33) | 37.3% (44/118) | 17.2% (17/99) | +| 1000 | opencode | 5.6% | 6.1% (2/33) | 8.5% (10/118) | 2.0% (2/99) | +| 1000 | claude-code | 38.8% | 72.7% (24/33) | 44.9% (53/118) | 20.2% (20/99) | +| 1000 | codex | 24.8% | 45.5% (15/33) | 31.4% (37/118) | 10.1% (10/99) | +| 1000 | mini-swe-agent | 36.0% | 45.5% (15/33) | 45.8% (54/118) | 21.2% (21/99) | + +### Training history + +| Allocation | First optimizer step | Last optimizer step | +| --- | ---: | ---: | +| 78647 | 1 | 17 | +| 78681 | 18 | 25 | +| 78767 | 26 | 30 | +| 78831 | 31 | 53 | +| 78956 | 54 | 196 | +| 79083 | 197 | 684 | +| 80608 | 685 | 1000 | + +### Score provenance + +- Step 0: `experiments/async_grpo_harbor_data_agent/logs/multi4-baseline-20260914/job-78215/canonical_results.json`; SHA256 `8c4f5bced4eff04b0c2e5f41806da9ae1b8a4c0fe356ddf926e1f781c6bb9ac6`. +- Step 100: `experiments/async_grpo_harbor_data_agent/logs/multi4-long-bounded-20260915/checkpoint-evals/step-000100/scores.json`; SHA256 `1e4f42f54526c9b8b71156f5b1f3673529519201401bc88f88ccff805f291181`. +- Step 200: `experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000200/scores.json`; SHA256 `699dce549d1002e879c675555ac06142448b0cbeef534a0816616faf669862af`. +- Step 300: `experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000300/scores.json`; SHA256 `299e5ed3528922d9912a591f3cff0c4d85070fef4de732d37723967934bfd691`. +- Step 400: `experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000400/scores.json`; SHA256 `f8909af81669f4ec092317c5f9889ef8735754c0dcbc826da82a270d2d92dc7a`. +- Step 500: `experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000500/scores.json`; SHA256 `86d56b65edbdc2f5a3f5d54151d0888dae83ca2b81753a7a9dbc2e80ee4f6130`. +- Step 600: `experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000600/scores.json`; SHA256 `02fc5a5e84978c198dc880c143fe223507c30485563b3545cb64b2794e3e60b0`. +- Step 684: `experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000684/scores.json`; SHA256 `fb54bd52f352463e405bee8067ef24b27147bfd02d41429561f440a916f5d776`. +- Step 700: `experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-cont-20260915/checkpoint-evals/step-000700/scores.json`; SHA256 `39c2ead8681847c6c398eb6b22ae8919aabaff3ad6b668d81b5961564139f8be`. +- Step 800: `experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-cont-20260915/checkpoint-evals/step-000800/scores.json`; SHA256 `65130786707d07f68cfa145fcd5ad7308890cb89a305b65382dfbec36ef7a150`. +- Step 900: `experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-cont-20260915/checkpoint-evals/step-000900/scores.json`; SHA256 `2276bc4d8ad85c2913b0bed921013951ec2ca968bfbbece737681ce13df9b250`. +- Step 1000: `experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-cont-20260915/checkpoint-evals/step-001000/scores.json`; SHA256 `3172c293f6386c7732267fa46c9718403b3fa3462f88e9299a01139ad410c99b`. + +## Native OpenCode + +### Overall and difficulty + +| Checkpoint | Overall | Easy (132 cells) | Medium (472) | Hard (396) | +| --- | ---: | ---: | ---: | ---: | +| 0 | 15.9% | 37.9% | 18.0% | 6.1% | +| 100 | 19.7% | 44.7% | 22.5% | 8.1% | +| 200 | 22.1% | 59.8% | 24.6% | 6.6% | +| 300 | 21.6% | 51.5% | 25.6% | 6.8% | +| 400 | 26.4% | 59.1% | 29.4% | 11.9% | +| 500 | 23.1% | 53.0% | 27.3% | 8.1% | +| 600 | 25.1% | 56.1% | 28.8% | 10.4% | +| 700 | 23.2% | 49.2% | 28.8% | 7.8% | +| 800 | 25.6% | 52.3% | 29.7% | 11.9% | +| 900 | 25.3% | 50.0% | 30.3% | 11.1% | +| 1000 | 29.8% | 59.8% | 35.8% | 12.6% | + +### Harness × difficulty at every checkpoint + +| Checkpoint | Harness | Overall (250) | Easy (33) | Medium (118) | Hard (99) | +| --- | --- | ---: | ---: | ---: | ---: | +| 0 | opencode | 12.8% | 24.2% (8/33) | 16.9% (20/118) | 4.0% (4/99) | +| 0 | claude-code | 16.8% | 42.4% (14/33) | 18.6% (22/118) | 6.1% (6/99) | +| 0 | codex | 15.2% | 33.3% (11/33) | 16.9% (20/118) | 7.1% (7/99) | +| 0 | mini-swe-agent | 18.8% | 51.5% (17/33) | 19.5% (23/118) | 7.1% (7/99) | +| 100 | opencode | 19.6% | 42.4% (14/33) | 24.6% (29/118) | 6.1% (6/99) | +| 100 | claude-code | 20.4% | 42.4% (14/33) | 22.9% (27/118) | 10.1% (10/99) | +| 100 | codex | 18.0% | 27.3% (9/33) | 21.2% (25/118) | 11.1% (11/99) | +| 100 | mini-swe-agent | 20.8% | 66.7% (22/33) | 21.2% (25/118) | 5.1% (5/99) | +| 200 | opencode | 17.2% | 48.5% (16/33) | 21.2% (25/118) | 2.0% (2/99) | +| 200 | claude-code | 24.0% | 60.6% (20/33) | 26.3% (31/118) | 9.1% (9/99) | +| 200 | codex | 25.6% | 63.6% (21/33) | 28.8% (34/118) | 9.1% (9/99) | +| 200 | mini-swe-agent | 21.6% | 66.7% (22/33) | 22.0% (26/118) | 6.1% (6/99) | +| 300 | opencode | 20.8% | 48.5% (16/33) | 28.0% (33/118) | 3.0% (3/99) | +| 300 | claude-code | 29.2% | 66.7% (22/33) | 30.5% (36/118) | 15.2% (15/99) | +| 300 | codex | 16.8% | 39.4% (13/33) | 20.3% (24/118) | 5.1% (5/99) | +| 300 | mini-swe-agent | 19.6% | 51.5% (17/33) | 23.7% (28/118) | 4.0% (4/99) | +| 400 | opencode | 20.4% | 48.5% (16/33) | 24.6% (29/118) | 6.1% (6/99) | +| 400 | claude-code | 32.4% | 60.6% (20/33) | 35.6% (42/118) | 19.2% (19/99) | +| 400 | codex | 25.6% | 57.6% (19/33) | 28.0% (33/118) | 12.1% (12/99) | +| 400 | mini-swe-agent | 27.2% | 69.7% (23/33) | 29.7% (35/118) | 10.1% (10/99) | +| 500 | opencode | 18.0% | 48.5% (16/33) | 19.5% (23/118) | 6.1% (6/99) | +| 500 | claude-code | 26.4% | 51.5% (17/33) | 33.1% (39/118) | 10.1% (10/99) | +| 500 | codex | 18.8% | 48.5% (16/33) | 22.0% (26/118) | 5.1% (5/99) | +| 500 | mini-swe-agent | 29.2% | 63.6% (21/33) | 34.7% (41/118) | 11.1% (11/99) | +| 600 | opencode | 16.8% | 42.4% (14/33) | 18.6% (22/118) | 6.1% (6/99) | +| 600 | claude-code | 32.4% | 63.6% (21/33) | 37.3% (44/118) | 16.2% (16/99) | +| 600 | codex | 18.4% | 45.5% (15/33) | 23.7% (28/118) | 3.0% (3/99) | +| 600 | mini-swe-agent | 32.8% | 72.7% (24/33) | 35.6% (42/118) | 16.2% (16/99) | +| 700 | opencode | 18.4% | 42.4% (14/33) | 23.7% (28/118) | 4.0% (4/99) | +| 700 | claude-code | 29.6% | 63.6% (21/33) | 33.1% (39/118) | 14.1% (14/99) | +| 700 | codex | 14.4% | 24.2% (8/33) | 22.9% (27/118) | 1.0% (1/99) | +| 700 | mini-swe-agent | 30.4% | 66.7% (22/33) | 35.6% (42/118) | 12.1% (12/99) | +| 800 | opencode | 20.0% | 36.4% (12/33) | 26.3% (31/118) | 7.1% (7/99) | +| 800 | claude-code | 32.4% | 72.7% (24/33) | 35.6% (42/118) | 15.2% (15/99) | +| 800 | codex | 18.0% | 30.3% (10/33) | 21.2% (25/118) | 10.1% (10/99) | +| 800 | mini-swe-agent | 32.0% | 69.7% (23/33) | 35.6% (42/118) | 15.2% (15/99) | +| 900 | opencode | 18.8% | 33.3% (11/33) | 23.7% (28/118) | 8.1% (8/99) | +| 900 | claude-code | 34.0% | 60.6% (20/33) | 42.4% (50/118) | 15.2% (15/99) | +| 900 | codex | 14.4% | 27.3% (9/33) | 17.8% (21/118) | 6.1% (6/99) | +| 900 | mini-swe-agent | 34.0% | 78.8% (26/33) | 37.3% (44/118) | 15.2% (15/99) | +| 1000 | opencode | 20.4% | 42.4% (14/33) | 25.4% (30/118) | 7.1% (7/99) | +| 1000 | claude-code | 33.2% | 66.7% (22/33) | 37.3% (44/118) | 17.2% (17/99) | +| 1000 | codex | 29.6% | 57.6% (19/33) | 35.6% (42/118) | 13.1% (13/99) | +| 1000 | mini-swe-agent | 36.0% | 72.7% (24/33) | 44.9% (53/118) | 13.1% (13/99) | + +### Training history + +| Allocation | First optimizer step | Last optimizer step | +| --- | ---: | ---: | +| 80626 | 1 | 1000 | + +### Score provenance + +- Step 0: `experiments/daytona_harness_comparison/logs/20260915/blackbox/canonical_scores.json`; SHA256 `ece2b0e03c7c7e0e54988eaaa473ba6b53bd6028315b1e99ec39df5137c7632e`. +- Step 100: `experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80657/canonical_scores.json`; SHA256 `37eab8fe9e97fda23e9803846f965b08c3de2a6e4142363219a4467af41c6f5c`. +- Step 200: `experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80675/canonical_scores.json`; SHA256 `fa67547d19c7f4e63166fc7c1518ca15eb5e2c28ec8f1f3739445029006617ad`. +- Step 300: `experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80748/canonical_scores.json`; SHA256 `a0e0cc489e3195827d5ac035945277bfd9ca67e985a015148e5b3c94ca938b0f`. +- Step 400: `experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80807/canonical_scores.json`; SHA256 `f30bb541e207a2e8b83b2aabd05bf2e3d96eeac40c2fd8226ff1985606397a7b`. +- Step 500: `experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80861/canonical_scores.json`; SHA256 `b6df2570695c2a15ba43f185719b647dc22319eb82ca1494d56e705572e3f1a2`. +- Step 600: `experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80902/canonical_scores.json`; SHA256 `d86128c2cae4813a7ae8b99f06d1cd8ca109cefade9944c1cdbebf2b1550e1d9`. +- Step 700: `experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80956/canonical_scores.json`; SHA256 `7c60cfab333948e63bf44bd01ed4ce3f0a788f76de84c4ceda2177144e9bc9ba`. +- Step 800: `experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80993/canonical_scores.json`; SHA256 `b689c9dd76c3e2230bfea49eea393f2d5842fe8f3630f04f59380a131497ae98`. +- Step 900: `experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-81034/canonical_scores.json`; SHA256 `996f5ea262419b9639fa8f33c1b33fef9b49959c1cbe61e62ba922c0d642985f`. +- Step 1000: `experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-81098/canonical_scores.json`; SHA256 `1355a9a2ecc1ec165cf413120dacfc672e5d8d59ef2807b28bcf02322dca142b`. + +## Harbor OpenCode-only + +### Overall and difficulty + +| Checkpoint | Overall | Easy (132 cells) | Medium (472) | Hard (396) | +| --- | ---: | ---: | ---: | ---: | +| 0 | 14.6% | 40.2% | 14.4% | 6.3% | +| 100 | 24.5% | 50.0% | 29.2% | 10.4% | +| 200 | 27.1% | 62.1% | 32.2% | 9.3% | +| 300 | 28.5% | 64.4% | 32.2% | 12.1% | +| 400 | 32.8% | 70.5% | 39.0% | 12.9% | +| 500 | 33.0% | 71.2% | 37.9% | 14.4% | +| 600 | 33.0% | 66.7% | 39.8% | 13.6% | +| 700 | 39.5% | 71.2% | 44.5% | 23.0% | +| 800 | 33.1% | 65.9% | 37.1% | 17.4% | +| 900 | 29.6% | 63.6% | 32.2% | 15.2% | +| 1000 | 26.4% | 59.1% | 30.3% | 10.9% | + +### Harness × difficulty at every checkpoint + +| Checkpoint | Harness | Overall (250) | Easy (33) | Medium (118) | Hard (99) | +| --- | --- | ---: | ---: | ---: | ---: | +| 0 | opencode | 10.8% | 33.3% (11/33) | 8.5% (10/118) | 6.1% (6/99) | +| 0 | claude-code | 16.8% | 42.4% (14/33) | 18.6% (22/118) | 6.1% (6/99) | +| 0 | codex | 16.4% | 42.4% (14/33) | 16.9% (20/118) | 7.1% (7/99) | +| 0 | mini-swe-agent | 14.4% | 42.4% (14/33) | 13.6% (16/118) | 6.1% (6/99) | +| 100 | opencode | 22.0% | 33.3% (11/33) | 28.8% (34/118) | 10.1% (10/99) | +| 100 | claude-code | 25.2% | 48.5% (16/33) | 29.7% (35/118) | 12.1% (12/99) | +| 100 | codex | 30.4% | 48.5% (16/33) | 36.4% (43/118) | 17.2% (17/99) | +| 100 | mini-swe-agent | 20.4% | 69.7% (23/33) | 22.0% (26/118) | 2.0% (2/99) | +| 200 | opencode | 29.2% | 57.6% (19/33) | 35.6% (42/118) | 12.1% (12/99) | +| 200 | claude-code | 28.4% | 57.6% (19/33) | 37.3% (44/118) | 8.1% (8/99) | +| 200 | codex | 30.8% | 66.7% (22/33) | 37.3% (44/118) | 11.1% (11/99) | +| 200 | mini-swe-agent | 20.0% | 66.7% (22/33) | 18.6% (22/118) | 6.1% (6/99) | +| 300 | opencode | 28.0% | 69.7% (23/33) | 30.5% (36/118) | 11.1% (11/99) | +| 300 | claude-code | 30.4% | 66.7% (22/33) | 33.9% (40/118) | 14.1% (14/99) | +| 300 | codex | 32.0% | 63.6% (21/33) | 36.4% (43/118) | 16.2% (16/99) | +| 300 | mini-swe-agent | 23.6% | 57.6% (19/33) | 28.0% (33/118) | 7.1% (7/99) | +| 400 | opencode | 35.6% | 75.8% (25/33) | 43.2% (51/118) | 13.1% (13/99) | +| 400 | claude-code | 33.6% | 72.7% (24/33) | 42.4% (50/118) | 10.1% (10/99) | +| 400 | codex | 33.2% | 69.7% (23/33) | 34.7% (41/118) | 19.2% (19/99) | +| 400 | mini-swe-agent | 28.8% | 63.6% (21/33) | 35.6% (42/118) | 9.1% (9/99) | +| 500 | opencode | 32.8% | 69.7% (23/33) | 39.8% (47/118) | 12.1% (12/99) | +| 500 | claude-code | 32.4% | 66.7% (22/33) | 38.1% (45/118) | 14.1% (14/99) | +| 500 | codex | 37.2% | 75.8% (25/33) | 42.4% (50/118) | 18.2% (18/99) | +| 500 | mini-swe-agent | 29.6% | 72.7% (24/33) | 31.4% (37/118) | 13.1% (13/99) | +| 600 | opencode | 35.6% | 78.8% (26/33) | 43.2% (51/118) | 12.1% (12/99) | +| 600 | claude-code | 31.2% | 63.6% (21/33) | 36.4% (43/118) | 14.1% (14/99) | +| 600 | codex | 34.4% | 63.6% (21/33) | 42.4% (50/118) | 15.2% (15/99) | +| 600 | mini-swe-agent | 30.8% | 60.6% (20/33) | 37.3% (44/118) | 13.1% (13/99) | +| 700 | opencode | 40.0% | 69.7% (23/33) | 44.1% (52/118) | 25.3% (25/99) | +| 700 | claude-code | 46.4% | 78.8% (26/33) | 53.4% (63/118) | 27.3% (27/99) | +| 700 | codex | 37.2% | 69.7% (23/33) | 42.4% (50/118) | 20.2% (20/99) | +| 700 | mini-swe-agent | 34.4% | 66.7% (22/33) | 38.1% (45/118) | 19.2% (19/99) | +| 800 | opencode | 26.4% | 45.5% (15/33) | 30.5% (36/118) | 15.2% (15/99) | +| 800 | claude-code | 38.8% | 78.8% (26/33) | 44.1% (52/118) | 19.2% (19/99) | +| 800 | codex | 35.6% | 63.6% (21/33) | 39.8% (47/118) | 21.2% (21/99) | +| 800 | mini-swe-agent | 31.6% | 75.8% (25/33) | 33.9% (40/118) | 14.1% (14/99) | +| 900 | opencode | 30.0% | 66.7% (22/33) | 31.4% (37/118) | 16.2% (16/99) | +| 900 | claude-code | 35.2% | 72.7% (24/33) | 39.0% (46/118) | 18.2% (18/99) | +| 900 | codex | 27.2% | 54.5% (18/33) | 29.7% (35/118) | 15.2% (15/99) | +| 900 | mini-swe-agent | 26.0% | 60.6% (20/33) | 28.8% (34/118) | 11.1% (11/99) | +| 1000 | opencode | 22.0% | 51.5% (17/33) | 25.4% (30/118) | 8.1% (8/99) | +| 1000 | claude-code | 30.8% | 57.6% (19/33) | 34.7% (41/118) | 17.2% (17/99) | +| 1000 | codex | 32.4% | 60.6% (20/33) | 39.0% (46/118) | 15.2% (15/99) | +| 1000 | mini-swe-agent | 20.4% | 66.7% (22/33) | 22.0% (26/118) | 3.0% (3/99) | + +### Training history + +| Allocation | First optimizer step | Last optimizer step | +| --- | ---: | ---: | +| 81075 | 1 | 1000 | + +### Score provenance + +- Step 0: `experiments/async_grpo_harbor_data_agent/logs/multi4-baseline-20260914/job-78215/canonical_results.json`; SHA256 `8c4f5bced4eff04b0c2e5f41806da9ae1b8a4c0fe356ddf926e1f781c6bb9ac6`. +- Step 100: `experiments/async_grpo_harbor_data_agent/logs/harbor-opencode-only-20260916/checkpoint-evals/step-000100/scores.json`; SHA256 `08278ea43f152961c9fd3b0b8cc598a0cf5d891b658ef7f5314f95c62ade44d8`. +- Step 200: `experiments/async_grpo_harbor_data_agent/logs/harbor-opencode-only-20260916/checkpoint-evals/step-000200/scores.json`; SHA256 `718d706a3b61a8a171e567e99084c30f0b4e532d55d9cbe1f38b0a0f88e9741b`. +- Step 300: `experiments/async_grpo_harbor_data_agent/logs/harbor-opencode-only-20260916/checkpoint-evals/step-000300/scores.json`; SHA256 `58ccb317e9c50e09afe5211744049ccac6cf8aeb690e9021307591798733118c`. +- Step 400: `experiments/async_grpo_harbor_data_agent/logs/harbor-opencode-only-20260916/checkpoint-evals/step-000400/scores.json`; SHA256 `f473d7a6595f5b461da11eeab86b0265fc0d5a903dd50a7f07c13fe591841071`. +- Step 500: `experiments/async_grpo_harbor_data_agent/logs/harbor-opencode-only-20260916/checkpoint-evals/step-000500/scores.json`; SHA256 `3ea7d52077764f3e662f95eb7d2606942f5a8a5745f9527de1b08409ecf2849d`. +- Step 600: `experiments/async_grpo_harbor_data_agent/logs/harbor-opencode-only-20260916/checkpoint-evals/step-000600/scores.json`; SHA256 `304f219743ea44713603e6e36f6b2ace485ea0e8caf504d5fee6509ca8081c61`. +- Step 700: `experiments/async_grpo_harbor_data_agent/logs/harbor-opencode-only-20260916/checkpoint-evals/step-000700/scores.json`; SHA256 `eff27df90ecb2960693a86592246ba27ed9065b0f391c8dc339a2e14d85d6b1f`. +- Step 800: `experiments/async_grpo_harbor_data_agent/logs/harbor-opencode-only-20260916/checkpoint-evals/step-000800/scores.json`; SHA256 `64bd9dd7395cf0105535abddf60f44b79af65ff60815ad6eb755cba461636845`. +- Step 900: `experiments/async_grpo_harbor_data_agent/logs/harbor-opencode-only-20260916/checkpoint-evals/step-000900/scores.json`; SHA256 `d71b7033e8317d204be052663d2b121c2d32b24714bdbd796a941f4f3fec20d5`. +- Step 1000: `experiments/async_grpo_harbor_data_agent/logs/harbor-opencode-only-20260916/checkpoint-evals/step-001000/scores.json`; SHA256 `c4af433eebf56b98842150a88c467711b62475177a7626f541565392dfd74ea8`. + +## Dashboard metric guide + +Both runs use identical metric names and optimizer-step axes. `eval/pass_at_1` is the overall score; `eval/difficulty/*` aggregates each difficulty; `eval/harness/*` compares each harness; `eval/harness_difficulty/*` contains all twelve intersections. `train/*` preserves recorded loss, reward, learning rate, gradient norm, entropy, KL, staleness, throughput, token, batching and rollout metrics where observed. Missing metrics are not filled with zeros. `train/reward_rolling20` and `train/nonzero_gradient_rolling20` are explicitly derived trailing windows. Raw metrics remain available. Use zero dashboard smoothing for exact checkpoint values. + +The independent CPU publisher refreshes every 60 seconds and admits new evaluations only after their full comparison gates pass. It never changes trainer state. Local SQLite backup, event ledger and remote exact-content verification receipts are kept alongside this report. + +Storage and deployment follow the [Trackio guide](https://huggingface.co/docs/trackio/quickstart) and [environment configuration](https://huggingface.co/docs/trackio/environment_variables). + +- [Overview](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5E%28eval%2Fpass_at_1%7Ctrain%2Freward_rolling20%29%24) +- [Difficulty](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5Eeval%2Fdifficulty%2F) +- [Harness](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5Eeval%2Fharness%2F) +- [Harness × difficulty](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5Eeval%2Fharness_difficulty%2F) +- [Optimizer diagnostics](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5Etrain%2F%28loss%7Cgrad_norm%7Centropy%7Ckl%7Clearning_rate%7Cnonzero_gradient_rolling20%29%24) +- [Throughput and rollout diagnostics](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5Etrain%2F%28perf%7Crollout%7Csample%7Cbatch%29%2F) +- [All metrics](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=) + +[Download checkpoint scores as CSV](checkpoint_scores.csv) diff --git a/04-data-agent/reports/async-comparison-20260916/checkpoint_scores.csv b/04-data-agent/reports/async-comparison-20260916/checkpoint_scores.csv new file mode 100644 index 0000000..9daf258 --- /dev/null +++ b/04-data-agent/reports/async-comparison-20260916/checkpoint_scores.csv @@ -0,0 +1,409 @@ +run,checkpoint,harness,difficulty,correct,graded,pass_at_1 +Harbor multi-harness,0,opencode,easy,11,33,0.3333333333333333 +Harbor multi-harness,0,opencode,medium,10,118,0.0847457627118644 +Harbor multi-harness,0,opencode,hard,6,99,0.06060606060606061 +Harbor multi-harness,0,claude-code,easy,14,33,0.42424242424242425 +Harbor multi-harness,0,claude-code,medium,22,118,0.1864406779661017 +Harbor multi-harness,0,claude-code,hard,6,99,0.06060606060606061 +Harbor multi-harness,0,codex,easy,14,33,0.42424242424242425 +Harbor multi-harness,0,codex,medium,20,118,0.1694915254237288 +Harbor multi-harness,0,codex,hard,7,99,0.0707070707070707 +Harbor multi-harness,0,mini-swe-agent,easy,14,33,0.42424242424242425 +Harbor multi-harness,0,mini-swe-agent,medium,16,118,0.13559322033898305 +Harbor multi-harness,0,mini-swe-agent,hard,6,99,0.06060606060606061 +Harbor multi-harness,100,opencode,easy,17,33,0.5151515151515151 +Harbor multi-harness,100,opencode,medium,37,118,0.3135593220338983 +Harbor multi-harness,100,opencode,hard,7,99,0.0707070707070707 +Harbor multi-harness,100,claude-code,easy,20,33,0.6060606060606061 +Harbor multi-harness,100,claude-code,medium,38,118,0.3220338983050847 +Harbor multi-harness,100,claude-code,hard,11,99,0.1111111111111111 +Harbor multi-harness,100,codex,easy,19,33,0.5757575757575758 +Harbor multi-harness,100,codex,medium,42,118,0.3559322033898305 +Harbor multi-harness,100,codex,hard,9,99,0.09090909090909091 +Harbor multi-harness,100,mini-swe-agent,easy,14,33,0.42424242424242425 +Harbor multi-harness,100,mini-swe-agent,medium,27,118,0.2288135593220339 +Harbor multi-harness,100,mini-swe-agent,hard,7,99,0.0707070707070707 +Harbor multi-harness,200,opencode,easy,17,33,0.5151515151515151 +Harbor multi-harness,200,opencode,medium,41,118,0.3474576271186441 +Harbor multi-harness,200,opencode,hard,18,99,0.18181818181818182 +Harbor multi-harness,200,claude-code,easy,21,33,0.6363636363636364 +Harbor multi-harness,200,claude-code,medium,39,118,0.3305084745762712 +Harbor multi-harness,200,claude-code,hard,15,99,0.15151515151515152 +Harbor multi-harness,200,codex,easy,21,33,0.6363636363636364 +Harbor multi-harness,200,codex,medium,35,118,0.2966101694915254 +Harbor multi-harness,200,codex,hard,10,99,0.10101010101010101 +Harbor multi-harness,200,mini-swe-agent,easy,18,33,0.5454545454545454 +Harbor multi-harness,200,mini-swe-agent,medium,27,118,0.2288135593220339 +Harbor multi-harness,200,mini-swe-agent,hard,1,99,0.010101010101010102 +Harbor multi-harness,300,opencode,easy,20,33,0.6060606060606061 +Harbor multi-harness,300,opencode,medium,39,118,0.3305084745762712 +Harbor multi-harness,300,opencode,hard,15,99,0.15151515151515152 +Harbor multi-harness,300,claude-code,easy,22,33,0.6666666666666666 +Harbor multi-harness,300,claude-code,medium,46,118,0.3898305084745763 +Harbor multi-harness,300,claude-code,hard,15,99,0.15151515151515152 +Harbor multi-harness,300,codex,easy,23,33,0.696969696969697 +Harbor multi-harness,300,codex,medium,39,118,0.3305084745762712 +Harbor multi-harness,300,codex,hard,12,99,0.12121212121212122 +Harbor multi-harness,300,mini-swe-agent,easy,20,33,0.6060606060606061 +Harbor multi-harness,300,mini-swe-agent,medium,31,118,0.2627118644067797 +Harbor multi-harness,300,mini-swe-agent,hard,4,99,0.04040404040404041 +Harbor multi-harness,400,opencode,easy,24,33,0.7272727272727273 +Harbor multi-harness,400,opencode,medium,45,118,0.3813559322033898 +Harbor multi-harness,400,opencode,hard,13,99,0.13131313131313133 +Harbor multi-harness,400,claude-code,easy,25,33,0.7575757575757576 +Harbor multi-harness,400,claude-code,medium,53,118,0.4491525423728814 +Harbor multi-harness,400,claude-code,hard,14,99,0.1414141414141414 +Harbor multi-harness,400,codex,easy,24,33,0.7272727272727273 +Harbor multi-harness,400,codex,medium,50,118,0.423728813559322 +Harbor multi-harness,400,codex,hard,13,99,0.13131313131313133 +Harbor multi-harness,400,mini-swe-agent,easy,24,33,0.7272727272727273 +Harbor multi-harness,400,mini-swe-agent,medium,39,118,0.3305084745762712 +Harbor multi-harness,400,mini-swe-agent,hard,9,99,0.09090909090909091 +Harbor multi-harness,500,opencode,easy,23,33,0.696969696969697 +Harbor multi-harness,500,opencode,medium,48,118,0.4067796610169492 +Harbor multi-harness,500,opencode,hard,11,99,0.1111111111111111 +Harbor multi-harness,500,claude-code,easy,25,33,0.7575757575757576 +Harbor multi-harness,500,claude-code,medium,62,118,0.5254237288135594 +Harbor multi-harness,500,claude-code,hard,25,99,0.25252525252525254 +Harbor multi-harness,500,codex,easy,25,33,0.7575757575757576 +Harbor multi-harness,500,codex,medium,55,118,0.4661016949152542 +Harbor multi-harness,500,codex,hard,18,99,0.18181818181818182 +Harbor multi-harness,500,mini-swe-agent,easy,23,33,0.696969696969697 +Harbor multi-harness,500,mini-swe-agent,medium,44,118,0.3728813559322034 +Harbor multi-harness,500,mini-swe-agent,hard,11,99,0.1111111111111111 +Harbor multi-harness,600,opencode,easy,22,33,0.6666666666666666 +Harbor multi-harness,600,opencode,medium,49,118,0.4152542372881356 +Harbor multi-harness,600,opencode,hard,14,99,0.1414141414141414 +Harbor multi-harness,600,claude-code,easy,24,33,0.7272727272727273 +Harbor multi-harness,600,claude-code,medium,40,118,0.3389830508474576 +Harbor multi-harness,600,claude-code,hard,11,99,0.1111111111111111 +Harbor multi-harness,600,codex,easy,22,33,0.6666666666666666 +Harbor multi-harness,600,codex,medium,47,118,0.3983050847457627 +Harbor multi-harness,600,codex,hard,12,99,0.12121212121212122 +Harbor multi-harness,600,mini-swe-agent,easy,28,33,0.8484848484848485 +Harbor multi-harness,600,mini-swe-agent,medium,40,118,0.3389830508474576 +Harbor multi-harness,600,mini-swe-agent,hard,9,99,0.09090909090909091 +Harbor multi-harness,684,opencode,easy,24,33,0.7272727272727273 +Harbor multi-harness,684,opencode,medium,36,118,0.3050847457627119 +Harbor multi-harness,684,opencode,hard,13,99,0.13131313131313133 +Harbor multi-harness,684,claude-code,easy,24,33,0.7272727272727273 +Harbor multi-harness,684,claude-code,medium,49,118,0.4152542372881356 +Harbor multi-harness,684,claude-code,hard,16,99,0.16161616161616163 +Harbor multi-harness,684,codex,easy,28,33,0.8484848484848485 +Harbor multi-harness,684,codex,medium,43,118,0.3644067796610169 +Harbor multi-harness,684,codex,hard,12,99,0.12121212121212122 +Harbor multi-harness,684,mini-swe-agent,easy,24,33,0.7272727272727273 +Harbor multi-harness,684,mini-swe-agent,medium,41,118,0.3474576271186441 +Harbor multi-harness,684,mini-swe-agent,hard,11,99,0.1111111111111111 +Harbor multi-harness,700,opencode,easy,10,33,0.30303030303030304 +Harbor multi-harness,700,opencode,medium,33,118,0.2796610169491525 +Harbor multi-harness,700,opencode,hard,10,99,0.10101010101010101 +Harbor multi-harness,700,claude-code,easy,26,33,0.7878787878787878 +Harbor multi-harness,700,claude-code,medium,41,118,0.3474576271186441 +Harbor multi-harness,700,claude-code,hard,12,99,0.12121212121212122 +Harbor multi-harness,700,codex,easy,24,33,0.7272727272727273 +Harbor multi-harness,700,codex,medium,41,118,0.3474576271186441 +Harbor multi-harness,700,codex,hard,15,99,0.15151515151515152 +Harbor multi-harness,700,mini-swe-agent,easy,20,33,0.6060606060606061 +Harbor multi-harness,700,mini-swe-agent,medium,45,118,0.3813559322033898 +Harbor multi-harness,700,mini-swe-agent,hard,11,99,0.1111111111111111 +Harbor multi-harness,800,opencode,easy,12,33,0.36363636363636365 +Harbor multi-harness,800,opencode,medium,39,118,0.3305084745762712 +Harbor multi-harness,800,opencode,hard,7,99,0.0707070707070707 +Harbor multi-harness,800,claude-code,easy,22,33,0.6666666666666666 +Harbor multi-harness,800,claude-code,medium,44,118,0.3728813559322034 +Harbor multi-harness,800,claude-code,hard,14,99,0.1414141414141414 +Harbor multi-harness,800,codex,easy,22,33,0.6666666666666666 +Harbor multi-harness,800,codex,medium,37,118,0.3135593220338983 +Harbor multi-harness,800,codex,hard,8,99,0.08080808080808081 +Harbor multi-harness,800,mini-swe-agent,easy,22,33,0.6666666666666666 +Harbor multi-harness,800,mini-swe-agent,medium,34,118,0.288135593220339 +Harbor multi-harness,800,mini-swe-agent,hard,9,99,0.09090909090909091 +Harbor multi-harness,900,opencode,easy,0,33,0.0 +Harbor multi-harness,900,opencode,medium,4,118,0.03389830508474576 +Harbor multi-harness,900,opencode,hard,2,99,0.020202020202020204 +Harbor multi-harness,900,claude-code,easy,24,33,0.7272727272727273 +Harbor multi-harness,900,claude-code,medium,51,118,0.4322033898305085 +Harbor multi-harness,900,claude-code,hard,16,99,0.16161616161616163 +Harbor multi-harness,900,codex,easy,16,33,0.48484848484848486 +Harbor multi-harness,900,codex,medium,24,118,0.2033898305084746 +Harbor multi-harness,900,codex,hard,8,99,0.08080808080808081 +Harbor multi-harness,900,mini-swe-agent,easy,21,33,0.6363636363636364 +Harbor multi-harness,900,mini-swe-agent,medium,44,118,0.3728813559322034 +Harbor multi-harness,900,mini-swe-agent,hard,17,99,0.1717171717171717 +Harbor multi-harness,1000,opencode,easy,2,33,0.06060606060606061 +Harbor multi-harness,1000,opencode,medium,10,118,0.0847457627118644 +Harbor multi-harness,1000,opencode,hard,2,99,0.020202020202020204 +Harbor multi-harness,1000,claude-code,easy,24,33,0.7272727272727273 +Harbor multi-harness,1000,claude-code,medium,53,118,0.4491525423728814 +Harbor multi-harness,1000,claude-code,hard,20,99,0.20202020202020202 +Harbor multi-harness,1000,codex,easy,15,33,0.45454545454545453 +Harbor multi-harness,1000,codex,medium,37,118,0.3135593220338983 +Harbor multi-harness,1000,codex,hard,10,99,0.10101010101010101 +Harbor multi-harness,1000,mini-swe-agent,easy,15,33,0.45454545454545453 +Harbor multi-harness,1000,mini-swe-agent,medium,54,118,0.4576271186440678 +Harbor multi-harness,1000,mini-swe-agent,hard,21,99,0.21212121212121213 +Native OpenCode,0,opencode,easy,8,33,0.24242424242424243 +Native OpenCode,0,opencode,medium,20,118,0.1694915254237288 +Native OpenCode,0,opencode,hard,4,99,0.04040404040404041 +Native OpenCode,0,claude-code,easy,14,33,0.42424242424242425 +Native OpenCode,0,claude-code,medium,22,118,0.1864406779661017 +Native OpenCode,0,claude-code,hard,6,99,0.06060606060606061 +Native OpenCode,0,codex,easy,11,33,0.3333333333333333 +Native OpenCode,0,codex,medium,20,118,0.1694915254237288 +Native OpenCode,0,codex,hard,7,99,0.0707070707070707 +Native OpenCode,0,mini-swe-agent,easy,17,33,0.5151515151515151 +Native OpenCode,0,mini-swe-agent,medium,23,118,0.19491525423728814 +Native OpenCode,0,mini-swe-agent,hard,7,99,0.0707070707070707 +Native OpenCode,100,opencode,easy,14,33,0.42424242424242425 +Native OpenCode,100,opencode,medium,29,118,0.2457627118644068 +Native OpenCode,100,opencode,hard,6,99,0.06060606060606061 +Native OpenCode,100,claude-code,easy,14,33,0.42424242424242425 +Native OpenCode,100,claude-code,medium,27,118,0.2288135593220339 +Native OpenCode,100,claude-code,hard,10,99,0.10101010101010101 +Native OpenCode,100,codex,easy,9,33,0.2727272727272727 +Native OpenCode,100,codex,medium,25,118,0.211864406779661 +Native OpenCode,100,codex,hard,11,99,0.1111111111111111 +Native OpenCode,100,mini-swe-agent,easy,22,33,0.6666666666666666 +Native OpenCode,100,mini-swe-agent,medium,25,118,0.211864406779661 +Native OpenCode,100,mini-swe-agent,hard,5,99,0.050505050505050504 +Native OpenCode,200,opencode,easy,16,33,0.48484848484848486 +Native OpenCode,200,opencode,medium,25,118,0.211864406779661 +Native OpenCode,200,opencode,hard,2,99,0.020202020202020204 +Native OpenCode,200,claude-code,easy,20,33,0.6060606060606061 +Native OpenCode,200,claude-code,medium,31,118,0.2627118644067797 +Native OpenCode,200,claude-code,hard,9,99,0.09090909090909091 +Native OpenCode,200,codex,easy,21,33,0.6363636363636364 +Native OpenCode,200,codex,medium,34,118,0.288135593220339 +Native OpenCode,200,codex,hard,9,99,0.09090909090909091 +Native OpenCode,200,mini-swe-agent,easy,22,33,0.6666666666666666 +Native OpenCode,200,mini-swe-agent,medium,26,118,0.22033898305084745 +Native OpenCode,200,mini-swe-agent,hard,6,99,0.06060606060606061 +Native OpenCode,300,opencode,easy,16,33,0.48484848484848486 +Native OpenCode,300,opencode,medium,33,118,0.2796610169491525 +Native OpenCode,300,opencode,hard,3,99,0.030303030303030304 +Native OpenCode,300,claude-code,easy,22,33,0.6666666666666666 +Native OpenCode,300,claude-code,medium,36,118,0.3050847457627119 +Native OpenCode,300,claude-code,hard,15,99,0.15151515151515152 +Native OpenCode,300,codex,easy,13,33,0.3939393939393939 +Native OpenCode,300,codex,medium,24,118,0.2033898305084746 +Native OpenCode,300,codex,hard,5,99,0.050505050505050504 +Native OpenCode,300,mini-swe-agent,easy,17,33,0.5151515151515151 +Native OpenCode,300,mini-swe-agent,medium,28,118,0.23728813559322035 +Native OpenCode,300,mini-swe-agent,hard,4,99,0.04040404040404041 +Native OpenCode,400,opencode,easy,16,33,0.48484848484848486 +Native OpenCode,400,opencode,medium,29,118,0.2457627118644068 +Native OpenCode,400,opencode,hard,6,99,0.06060606060606061 +Native OpenCode,400,claude-code,easy,20,33,0.6060606060606061 +Native OpenCode,400,claude-code,medium,42,118,0.3559322033898305 +Native OpenCode,400,claude-code,hard,19,99,0.1919191919191919 +Native OpenCode,400,codex,easy,19,33,0.5757575757575758 +Native OpenCode,400,codex,medium,33,118,0.2796610169491525 +Native OpenCode,400,codex,hard,12,99,0.12121212121212122 +Native OpenCode,400,mini-swe-agent,easy,23,33,0.696969696969697 +Native OpenCode,400,mini-swe-agent,medium,35,118,0.2966101694915254 +Native OpenCode,400,mini-swe-agent,hard,10,99,0.10101010101010101 +Native OpenCode,500,opencode,easy,16,33,0.48484848484848486 +Native OpenCode,500,opencode,medium,23,118,0.19491525423728814 +Native OpenCode,500,opencode,hard,6,99,0.06060606060606061 +Native OpenCode,500,claude-code,easy,17,33,0.5151515151515151 +Native OpenCode,500,claude-code,medium,39,118,0.3305084745762712 +Native OpenCode,500,claude-code,hard,10,99,0.10101010101010101 +Native OpenCode,500,codex,easy,16,33,0.48484848484848486 +Native OpenCode,500,codex,medium,26,118,0.22033898305084745 +Native OpenCode,500,codex,hard,5,99,0.050505050505050504 +Native OpenCode,500,mini-swe-agent,easy,21,33,0.6363636363636364 +Native OpenCode,500,mini-swe-agent,medium,41,118,0.3474576271186441 +Native OpenCode,500,mini-swe-agent,hard,11,99,0.1111111111111111 +Native OpenCode,600,opencode,easy,14,33,0.42424242424242425 +Native OpenCode,600,opencode,medium,22,118,0.1864406779661017 +Native OpenCode,600,opencode,hard,6,99,0.06060606060606061 +Native OpenCode,600,claude-code,easy,21,33,0.6363636363636364 +Native OpenCode,600,claude-code,medium,44,118,0.3728813559322034 +Native OpenCode,600,claude-code,hard,16,99,0.16161616161616163 +Native OpenCode,600,codex,easy,15,33,0.45454545454545453 +Native OpenCode,600,codex,medium,28,118,0.23728813559322035 +Native OpenCode,600,codex,hard,3,99,0.030303030303030304 +Native OpenCode,600,mini-swe-agent,easy,24,33,0.7272727272727273 +Native OpenCode,600,mini-swe-agent,medium,42,118,0.3559322033898305 +Native OpenCode,600,mini-swe-agent,hard,16,99,0.16161616161616163 +Native OpenCode,700,opencode,easy,14,33,0.42424242424242425 +Native OpenCode,700,opencode,medium,28,118,0.23728813559322035 +Native OpenCode,700,opencode,hard,4,99,0.04040404040404041 +Native OpenCode,700,claude-code,easy,21,33,0.6363636363636364 +Native OpenCode,700,claude-code,medium,39,118,0.3305084745762712 +Native OpenCode,700,claude-code,hard,14,99,0.1414141414141414 +Native OpenCode,700,codex,easy,8,33,0.24242424242424243 +Native OpenCode,700,codex,medium,27,118,0.2288135593220339 +Native OpenCode,700,codex,hard,1,99,0.010101010101010102 +Native OpenCode,700,mini-swe-agent,easy,22,33,0.6666666666666666 +Native OpenCode,700,mini-swe-agent,medium,42,118,0.3559322033898305 +Native OpenCode,700,mini-swe-agent,hard,12,99,0.12121212121212122 +Native OpenCode,800,opencode,easy,12,33,0.36363636363636365 +Native OpenCode,800,opencode,medium,31,118,0.2627118644067797 +Native OpenCode,800,opencode,hard,7,99,0.0707070707070707 +Native OpenCode,800,claude-code,easy,24,33,0.7272727272727273 +Native OpenCode,800,claude-code,medium,42,118,0.3559322033898305 +Native OpenCode,800,claude-code,hard,15,99,0.15151515151515152 +Native OpenCode,800,codex,easy,10,33,0.30303030303030304 +Native OpenCode,800,codex,medium,25,118,0.211864406779661 +Native OpenCode,800,codex,hard,10,99,0.10101010101010101 +Native OpenCode,800,mini-swe-agent,easy,23,33,0.696969696969697 +Native OpenCode,800,mini-swe-agent,medium,42,118,0.3559322033898305 +Native OpenCode,800,mini-swe-agent,hard,15,99,0.15151515151515152 +Native OpenCode,900,opencode,easy,11,33,0.3333333333333333 +Native OpenCode,900,opencode,medium,28,118,0.23728813559322035 +Native OpenCode,900,opencode,hard,8,99,0.08080808080808081 +Native OpenCode,900,claude-code,easy,20,33,0.6060606060606061 +Native OpenCode,900,claude-code,medium,50,118,0.423728813559322 +Native OpenCode,900,claude-code,hard,15,99,0.15151515151515152 +Native OpenCode,900,codex,easy,9,33,0.2727272727272727 +Native OpenCode,900,codex,medium,21,118,0.17796610169491525 +Native OpenCode,900,codex,hard,6,99,0.06060606060606061 +Native OpenCode,900,mini-swe-agent,easy,26,33,0.7878787878787878 +Native OpenCode,900,mini-swe-agent,medium,44,118,0.3728813559322034 +Native OpenCode,900,mini-swe-agent,hard,15,99,0.15151515151515152 +Native OpenCode,1000,opencode,easy,14,33,0.42424242424242425 +Native OpenCode,1000,opencode,medium,30,118,0.2542372881355932 +Native OpenCode,1000,opencode,hard,7,99,0.0707070707070707 +Native OpenCode,1000,claude-code,easy,22,33,0.6666666666666666 +Native OpenCode,1000,claude-code,medium,44,118,0.3728813559322034 +Native OpenCode,1000,claude-code,hard,17,99,0.1717171717171717 +Native OpenCode,1000,codex,easy,19,33,0.5757575757575758 +Native OpenCode,1000,codex,medium,42,118,0.3559322033898305 +Native OpenCode,1000,codex,hard,13,99,0.13131313131313133 +Native OpenCode,1000,mini-swe-agent,easy,24,33,0.7272727272727273 +Native OpenCode,1000,mini-swe-agent,medium,53,118,0.4491525423728814 +Native OpenCode,1000,mini-swe-agent,hard,13,99,0.13131313131313133 +Harbor OpenCode-only,0,opencode,easy,11,33,0.3333333333333333 +Harbor OpenCode-only,0,opencode,medium,10,118,0.0847457627118644 +Harbor OpenCode-only,0,opencode,hard,6,99,0.06060606060606061 +Harbor OpenCode-only,0,claude-code,easy,14,33,0.42424242424242425 +Harbor OpenCode-only,0,claude-code,medium,22,118,0.1864406779661017 +Harbor OpenCode-only,0,claude-code,hard,6,99,0.06060606060606061 +Harbor OpenCode-only,0,codex,easy,14,33,0.42424242424242425 +Harbor OpenCode-only,0,codex,medium,20,118,0.1694915254237288 +Harbor OpenCode-only,0,codex,hard,7,99,0.0707070707070707 +Harbor OpenCode-only,0,mini-swe-agent,easy,14,33,0.42424242424242425 +Harbor OpenCode-only,0,mini-swe-agent,medium,16,118,0.13559322033898305 +Harbor OpenCode-only,0,mini-swe-agent,hard,6,99,0.06060606060606061 +Harbor OpenCode-only,100,opencode,easy,11,33,0.3333333333333333 +Harbor OpenCode-only,100,opencode,medium,34,118,0.288135593220339 +Harbor OpenCode-only,100,opencode,hard,10,99,0.10101010101010101 +Harbor OpenCode-only,100,claude-code,easy,16,33,0.48484848484848486 +Harbor OpenCode-only,100,claude-code,medium,35,118,0.2966101694915254 +Harbor OpenCode-only,100,claude-code,hard,12,99,0.12121212121212122 +Harbor OpenCode-only,100,codex,easy,16,33,0.48484848484848486 +Harbor OpenCode-only,100,codex,medium,43,118,0.3644067796610169 +Harbor OpenCode-only,100,codex,hard,17,99,0.1717171717171717 +Harbor OpenCode-only,100,mini-swe-agent,easy,23,33,0.696969696969697 +Harbor OpenCode-only,100,mini-swe-agent,medium,26,118,0.22033898305084745 +Harbor OpenCode-only,100,mini-swe-agent,hard,2,99,0.020202020202020204 +Harbor OpenCode-only,200,opencode,easy,19,33,0.5757575757575758 +Harbor OpenCode-only,200,opencode,medium,42,118,0.3559322033898305 +Harbor OpenCode-only,200,opencode,hard,12,99,0.12121212121212122 +Harbor OpenCode-only,200,claude-code,easy,19,33,0.5757575757575758 +Harbor OpenCode-only,200,claude-code,medium,44,118,0.3728813559322034 +Harbor OpenCode-only,200,claude-code,hard,8,99,0.08080808080808081 +Harbor OpenCode-only,200,codex,easy,22,33,0.6666666666666666 +Harbor OpenCode-only,200,codex,medium,44,118,0.3728813559322034 +Harbor OpenCode-only,200,codex,hard,11,99,0.1111111111111111 +Harbor OpenCode-only,200,mini-swe-agent,easy,22,33,0.6666666666666666 +Harbor OpenCode-only,200,mini-swe-agent,medium,22,118,0.1864406779661017 +Harbor OpenCode-only,200,mini-swe-agent,hard,6,99,0.06060606060606061 +Harbor OpenCode-only,300,opencode,easy,23,33,0.696969696969697 +Harbor OpenCode-only,300,opencode,medium,36,118,0.3050847457627119 +Harbor OpenCode-only,300,opencode,hard,11,99,0.1111111111111111 +Harbor OpenCode-only,300,claude-code,easy,22,33,0.6666666666666666 +Harbor OpenCode-only,300,claude-code,medium,40,118,0.3389830508474576 +Harbor OpenCode-only,300,claude-code,hard,14,99,0.1414141414141414 +Harbor OpenCode-only,300,codex,easy,21,33,0.6363636363636364 +Harbor OpenCode-only,300,codex,medium,43,118,0.3644067796610169 +Harbor OpenCode-only,300,codex,hard,16,99,0.16161616161616163 +Harbor OpenCode-only,300,mini-swe-agent,easy,19,33,0.5757575757575758 +Harbor OpenCode-only,300,mini-swe-agent,medium,33,118,0.2796610169491525 +Harbor OpenCode-only,300,mini-swe-agent,hard,7,99,0.0707070707070707 +Harbor OpenCode-only,400,opencode,easy,25,33,0.7575757575757576 +Harbor OpenCode-only,400,opencode,medium,51,118,0.4322033898305085 +Harbor OpenCode-only,400,opencode,hard,13,99,0.13131313131313133 +Harbor OpenCode-only,400,claude-code,easy,24,33,0.7272727272727273 +Harbor OpenCode-only,400,claude-code,medium,50,118,0.423728813559322 +Harbor OpenCode-only,400,claude-code,hard,10,99,0.10101010101010101 +Harbor OpenCode-only,400,codex,easy,23,33,0.696969696969697 +Harbor OpenCode-only,400,codex,medium,41,118,0.3474576271186441 +Harbor OpenCode-only,400,codex,hard,19,99,0.1919191919191919 +Harbor OpenCode-only,400,mini-swe-agent,easy,21,33,0.6363636363636364 +Harbor OpenCode-only,400,mini-swe-agent,medium,42,118,0.3559322033898305 +Harbor OpenCode-only,400,mini-swe-agent,hard,9,99,0.09090909090909091 +Harbor OpenCode-only,500,opencode,easy,23,33,0.696969696969697 +Harbor OpenCode-only,500,opencode,medium,47,118,0.3983050847457627 +Harbor OpenCode-only,500,opencode,hard,12,99,0.12121212121212122 +Harbor OpenCode-only,500,claude-code,easy,22,33,0.6666666666666666 +Harbor OpenCode-only,500,claude-code,medium,45,118,0.3813559322033898 +Harbor OpenCode-only,500,claude-code,hard,14,99,0.1414141414141414 +Harbor OpenCode-only,500,codex,easy,25,33,0.7575757575757576 +Harbor OpenCode-only,500,codex,medium,50,118,0.423728813559322 +Harbor OpenCode-only,500,codex,hard,18,99,0.18181818181818182 +Harbor OpenCode-only,500,mini-swe-agent,easy,24,33,0.7272727272727273 +Harbor OpenCode-only,500,mini-swe-agent,medium,37,118,0.3135593220338983 +Harbor OpenCode-only,500,mini-swe-agent,hard,13,99,0.13131313131313133 +Harbor OpenCode-only,600,opencode,easy,26,33,0.7878787878787878 +Harbor OpenCode-only,600,opencode,medium,51,118,0.4322033898305085 +Harbor OpenCode-only,600,opencode,hard,12,99,0.12121212121212122 +Harbor OpenCode-only,600,claude-code,easy,21,33,0.6363636363636364 +Harbor OpenCode-only,600,claude-code,medium,43,118,0.3644067796610169 +Harbor OpenCode-only,600,claude-code,hard,14,99,0.1414141414141414 +Harbor OpenCode-only,600,codex,easy,21,33,0.6363636363636364 +Harbor OpenCode-only,600,codex,medium,50,118,0.423728813559322 +Harbor OpenCode-only,600,codex,hard,15,99,0.15151515151515152 +Harbor OpenCode-only,600,mini-swe-agent,easy,20,33,0.6060606060606061 +Harbor OpenCode-only,600,mini-swe-agent,medium,44,118,0.3728813559322034 +Harbor OpenCode-only,600,mini-swe-agent,hard,13,99,0.13131313131313133 +Harbor OpenCode-only,700,opencode,easy,23,33,0.696969696969697 +Harbor OpenCode-only,700,opencode,medium,52,118,0.4406779661016949 +Harbor OpenCode-only,700,opencode,hard,25,99,0.25252525252525254 +Harbor OpenCode-only,700,claude-code,easy,26,33,0.7878787878787878 +Harbor OpenCode-only,700,claude-code,medium,63,118,0.5338983050847458 +Harbor OpenCode-only,700,claude-code,hard,27,99,0.2727272727272727 +Harbor OpenCode-only,700,codex,easy,23,33,0.696969696969697 +Harbor OpenCode-only,700,codex,medium,50,118,0.423728813559322 +Harbor OpenCode-only,700,codex,hard,20,99,0.20202020202020202 +Harbor OpenCode-only,700,mini-swe-agent,easy,22,33,0.6666666666666666 +Harbor OpenCode-only,700,mini-swe-agent,medium,45,118,0.3813559322033898 +Harbor OpenCode-only,700,mini-swe-agent,hard,19,99,0.1919191919191919 +Harbor OpenCode-only,800,opencode,easy,15,33,0.45454545454545453 +Harbor OpenCode-only,800,opencode,medium,36,118,0.3050847457627119 +Harbor OpenCode-only,800,opencode,hard,15,99,0.15151515151515152 +Harbor OpenCode-only,800,claude-code,easy,26,33,0.7878787878787878 +Harbor OpenCode-only,800,claude-code,medium,52,118,0.4406779661016949 +Harbor OpenCode-only,800,claude-code,hard,19,99,0.1919191919191919 +Harbor OpenCode-only,800,codex,easy,21,33,0.6363636363636364 +Harbor OpenCode-only,800,codex,medium,47,118,0.3983050847457627 +Harbor OpenCode-only,800,codex,hard,21,99,0.21212121212121213 +Harbor OpenCode-only,800,mini-swe-agent,easy,25,33,0.7575757575757576 +Harbor OpenCode-only,800,mini-swe-agent,medium,40,118,0.3389830508474576 +Harbor OpenCode-only,800,mini-swe-agent,hard,14,99,0.1414141414141414 +Harbor OpenCode-only,900,opencode,easy,22,33,0.6666666666666666 +Harbor OpenCode-only,900,opencode,medium,37,118,0.3135593220338983 +Harbor OpenCode-only,900,opencode,hard,16,99,0.16161616161616163 +Harbor OpenCode-only,900,claude-code,easy,24,33,0.7272727272727273 +Harbor OpenCode-only,900,claude-code,medium,46,118,0.3898305084745763 +Harbor OpenCode-only,900,claude-code,hard,18,99,0.18181818181818182 +Harbor OpenCode-only,900,codex,easy,18,33,0.5454545454545454 +Harbor OpenCode-only,900,codex,medium,35,118,0.2966101694915254 +Harbor OpenCode-only,900,codex,hard,15,99,0.15151515151515152 +Harbor OpenCode-only,900,mini-swe-agent,easy,20,33,0.6060606060606061 +Harbor OpenCode-only,900,mini-swe-agent,medium,34,118,0.288135593220339 +Harbor OpenCode-only,900,mini-swe-agent,hard,11,99,0.1111111111111111 +Harbor OpenCode-only,1000,opencode,easy,17,33,0.5151515151515151 +Harbor OpenCode-only,1000,opencode,medium,30,118,0.2542372881355932 +Harbor OpenCode-only,1000,opencode,hard,8,99,0.08080808080808081 +Harbor OpenCode-only,1000,claude-code,easy,19,33,0.5757575757575758 +Harbor OpenCode-only,1000,claude-code,medium,41,118,0.3474576271186441 +Harbor OpenCode-only,1000,claude-code,hard,17,99,0.1717171717171717 +Harbor OpenCode-only,1000,codex,easy,20,33,0.6060606060606061 +Harbor OpenCode-only,1000,codex,medium,46,118,0.3898305084745763 +Harbor OpenCode-only,1000,codex,hard,15,99,0.15151515151515152 +Harbor OpenCode-only,1000,mini-swe-agent,easy,22,33,0.6666666666666666 +Harbor OpenCode-only,1000,mini-swe-agent,medium,26,118,0.22033898305084745 +Harbor OpenCode-only,1000,mini-swe-agent,hard,3,99,0.030303030303030304 diff --git a/04-data-agent/reports/async-comparison-20260916/comparison.png b/04-data-agent/reports/async-comparison-20260916/comparison.png new file mode 100644 index 0000000..f13a39b Binary files /dev/null and b/04-data-agent/reports/async-comparison-20260916/comparison.png differ diff --git a/04-data-agent/reports/three-run-analysis-20260917/REPORT.md b/04-data-agent/reports/three-run-analysis-20260917/REPORT.md new file mode 100644 index 0000000..d81df6a --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/REPORT.md @@ -0,0 +1,170 @@ +# Three-run training and evaluation analysis + +September 17, 2026. Qwen3.5-2B; 250 fixed test tasks and four evaluation harnesses. + +**The two late declines have different signatures.** Multi-harness training develops long, often truncated responses and loses effective tool use, especially in OpenCode. Harbor OpenCode-only continues using tools but spends more calls, repeats more work, and submits fewer answers. Native OpenCode shows neither extreme and finishes at its best aggregate checkpoint. + +I also found a concrete resume problem: the multi-harness continuation largely revisited tasks already trained on. These observations identify useful diagnostics; they do not establish one causal explanation for all score changes. + +The [training companion](TRAINING.md) adds a census of 13,625 optimizer-admitted rollouts, +with token counts checked against receipts, per-harness tool use, context overhead and +zero-variance groups. See [the concise results overview](../../results.md) for the combined findings. + +![Evaluation and tool behavior](behavior.png) + +## Evidence and scope + +The analysis reconstructs **33,000 unique accepted evaluation cells**, represented by 34 checkpoint cohorts because the two Harbor runs share the same 1,000-cell baseline. Every reconstructed harness score matches its accepted score file, and every task index matches the frozen manifest. Training analysis uses all 3,000 optimizer-step metric records, coverage reports, frozen schedules, and available per-rollout optimizer receipts. + +| Run | Baseline | Best measured checkpoint | Final checkpoint 1,000 | +| --- | ---: | ---: | ---: | +| Harbor multi-harness | 14.6% | 500: **37.0%** | 26.3% | +| Native OpenCode | 15.9% | 1,000: **29.8%** | 29.8% | +| Harbor OpenCode-only | 14.6% | 700: **39.5%** | 26.4% | + +“Tool calls” below means distinct call IDs in the longest captured agent transcript. This transcript can omit discarded branches; it is not a billable-request counter. “Model calls” comes from the capture graph and includes auxiliary calls. A model response can emit several tools, so the 17-model-call ceiling does not imply a 17-tool-call ceiling. + +## 1. Multi-harness regression is concentrated in OpenCode, with output truncation + +From checkpoint 500 to 1,000: + +| Evaluation harness | Pass@1 | Mean recorded tool calls | Rollouts with output truncation | +| --- | ---: | ---: | ---: | +| OpenCode | 32.8% → **5.6%** | 15.48 → **3.61** | 3/250 → **204/250** | +| Claude Code | 44.8% → 38.8% | 16.06 → 12.22 | 5/250 → 132/250 | +| Codex | 39.2% → 24.8% | 16.45 → 9.24 | 1/250 → 162/250 | +| Mini-SWE-Agent | 31.2% → **36.0%** | 14.33 → 11.00 | 0/250 → 58/250 | + +OpenCode accounts for **68 of the 107 net lost successful cells**, about 64% of the aggregate decline. At checkpoint 900 it is worse: 2.4% success, 2.22 tool calls on average, and 87.2% of rollouts have at most two recorded tool calls. The final checkpoint has 74.0% in that category. + +Across all four harnesses, output truncation rises from **9/1,000 to 556/1,000 rollouts**. At the final checkpoint, truncated rollouts score 9.9%, versus 46.8% for those without a truncation warning. This is an association: hard or poorly handled tasks can cause both long outputs and failure. + +Directly inspected examples read a CSV once, then produce a long explanatory response that ends with `finish_reason="length"` at **4,096 output tokens**, without executing the calculation or writing the required answer. A few checkpoint-900 examples instead emit tool-like XML or JSON as plain text, with no parsed tool call. Thus the low call count should not be interpreted as successful efficiency. + +**Budget mismatch is a plausible contributor.** The evaluation manifest caps each response at 4,096 output tokens. Saved late training captures contain individual responses up to **16,384 tokens**. The complete admitted-rollout census finds responses longer than 4,096 in **182/485 (37.5%)** rollouts at steps 901–1,000, versus **10/512 (2.0%)** at steps 401–500. Whole-rollout completion tokens rise **3,521 → 9,480** over those windows. This replaces the earlier small-sample estimate. Worker completion-length telemetry averages 3,658 → 10,657 because it describes generated rollouts and uses different aggregation; [training metric definitions](TRAINING.md#what-is-counted) explain the distinction. + +This suggests a policy increasingly incompatible with the evaluation budget. It does **not** show that a larger budget would recover the score; some inspected responses repeat reasoning or invent facts rather than making progress. + +## 2. OpenCode-only declines through longer tool loops and missing submissions + +Harbor OpenCode-only, checkpoint 700 → 1,000: + +| Measure | Checkpoint 700 | Checkpoint 1,000 | +| --- | ---: | ---: | +| Overall pass@1 | 39.5% | 26.4% | +| Mean recorded tool calls, all harnesses | 16.62 | 20.97 | +| Mean exact repeated calls | 2.09 | 3.03 | +| Rollouts reaching at least 17 captured model calls | 56.4% | 70.2% | +| Answer submission, instrumented subset | 68.9% | 40.7% | +| Output-truncated rollouts | 7/1,000 | 9/1,000 | + +Submission is observable for **86 of the 250 tasks per harness**, or 344 cells. It must not be reported as a whole-test-set rate. Of 73 previously correct cells in this subset that become incorrect, **64 no longer submit an answer**. + +On matched task/harness cells that regress, tool calls rise from **15.26 to 22.48** on average. OpenCode itself goes from 20.16 to 25.80 calls while its score falls from 40.0% to 22.0%. Mini-SWE-Agent reaches at least 17 captured model calls on **95.6%** of final rollouts. + +The signature is continued work without reliable completion, rather than the widespread output truncation in the multi-harness model. Context-budget exhaustion increases too, from 7 to 25 cells, but is too rare to account for the entire decline. Exact repetition is only a proxy for wasted work: repeating a command can sometimes be useful. + +![Submission, generation length, and truncation](completion.png) + +## 3. The multi-harness resume replayed nearly the entire continuation's task set + +The continuation at step 684 resumed with schedule offset **230**. The previous allocation had already trained many later groups, but group 230 was the first hole in its completed-group set. + +The saved trainer code writes only `dataset_start_index + first_untrained` to `rollout_state.json`; it does not persist the later completed-group set. Resuming therefore schedules those later tasks again. + +Evidence: frozen checkpoint-saving code (`experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-cont-20260915/source-snapshot/trl/trl/experimental/async_grpo/async_grpo_trainer.py:1798`, local evidence), continuation resume audit (`experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-cont-20260915/job-80608/audit/resume.json`, local evidence), and optimizer receipts (`experiments/analysis-three-runs-20260917/optimizer_rollouts.csv`, local evidence). + +Optimizer receipts confirm that steps 685–1,000 admitted **1,579 fresh rollouts**, of which **1,575 used previously seen tasks**. About **99.8% of supervised tokens** in that segment came from already-seen tasks. Its 203 task IDs add only one new task to the previous detailed optimizer receipts. Coverage reports count 202 stable tasks for the segment because their callback waits for an additional optimizer boundary. + +This is task replay, not replay of cached model responses. It materially changes data exposure and is a credible contributor to later over-specialization. **It cannot explain the initial 500 → 600 drop**, which occurred before this restart. OpenCode-only also declines without a restart, so resume replay is not a universal explanation. + +Before another resumed run, preserve completed absolute group IDs and explicitly handle unfinished groups; simply advancing to the largest group ID would silently skip gaps. + +## 4. Training on one harness transferred to other harnesses + +Harbor OpenCode-only reaches **46.4% on Claude Code** at checkpoint 700, above its 40.0% on OpenCode. All four harnesses improve substantially from the shared baseline. + +Native OpenCode's final checkpoint scores: + +| Evaluation harness | Baseline | Final | Gain | +| --- | ---: | ---: | ---: | +| OpenCode | 12.8% | 20.4% | +7.6 pp | +| Claude Code | 16.8% | 33.2% | +16.4 pp | +| Codex | 15.2% | 29.6% | +14.4 pp | +| Mini-SWE-Agent | 18.8% | 36.0% | +17.2 pp | + +This contradicts the simple expectation that single-harness training mainly improves that same harness. It is consistent with transfer of task-solving behavior combined with different harness prompting and tool interfaces. These runs do not isolate the marginal benefit of multi-harness training, because exposure, backends and training histories differ. + +## 5. Equal optimizer steps were not equal data or compute budgets + +| Run | Unique training tasks in coverage logs | Supervised tokens | Forwarded tokens | +| --- | ---: | ---: | ---: | +| Harbor multi-harness | 482/1,000 | 22.15M | 1,001.32M | +| Native OpenCode | 566/1,000 | 5.33M | 228.41M | +| Harbor OpenCode-only | 523/1,000 | 14.63M | 419.43M | + +Native OpenCode used about **one quarter of the multi-harness supervised-token count**. It also finishes with lower recorded tool use: 11.97 calls per evaluation rollout, versus 20.97 for Harbor OpenCode-only. This is a useful efficiency observation, but wall-time comparisons are confounded by E2B versus Daytona, serving allocations and retries. + +The 1,000-step cap stopped all three before full task coverage. A future controlled comparison should specify both the task exposure target and supervised-token budget. “1,000 steps on the same 1,000-task dataset” is insufficient. + +![Evaluation against training exposure](training_exposure.png) + +## 6. Balanced harness rollouts produced unequal token weighting + +Detailed optimizer receipts are available from multi-harness step 31 onward: + +| Harness | Share of admitted rollouts | Share of training rows | Share of supervised tokens | +| --- | ---: | ---: | ---: | +| OpenCode | 25.2% | 11.4% | 18.3% | +| Claude Code | 24.8% | **77.3%** | **35.3%** | +| Codex | 24.6% | 5.3% | 31.1% | +| Mini-SWE-Agent | 25.4% | 5.9% | 15.3% | + +The harness schedule was balanced in rollout count. Claude's prompt forks greatly increased row count and repeated context, but **77.3% of rows does not mean 77.3% of the gradient**: these runs use supervised-token normalization. Token shares better describe loss exposure, though actual gradient contributions also depend on advantages, clipping and token gradients. + +This supports measuring rollout, row, context-token and supervised-token shares separately. It does not establish that Claude's row count caused the decline. + +## 7. Difficulty and harness agreement reveal more than the average + +- Multi-harness easy-task pass@1 drops **72.7% → 42.4%** from peak to final, while hard-task pass@1 drops 16.4% → 13.4%. This is not merely a loss on difficult tasks. There are only 33 distinct easy tasks, evaluated under four harnesses. +- OpenCode-only declines across all three difficulty levels: easy 71.2% → 59.1%, medium 44.5% → 30.3%, hard 23.0% → 10.9%. +- At the multi-harness peak, **45 tasks succeed under all four harnesses**; at the final checkpoint, only **7** do. Tasks solved by at least one harness fall less sharply, from 141 to 129. Harness consistency erodes more than the set of tasks solvable by any harness. +- OpenCode-only checkpoint 700 solves 149/250 tasks under at least one harness (59.6%); native final solves 134/250 (53.6%). These are retrospective four-harness oracle rates, **not pass@1** and not deployable routing policies. + +## 8. The declines are clear; the ranking of the two peaks is not + +Paired bootstrap intervals resample the 250 task IDs, keeping each task's four harness outcomes together: + +| Comparison | Score change | Conditional 95% interval | +| --- | ---: | ---: | +| Multi-harness 500 → 1,000 | −10.7 pp | −14.1 to −7.4 pp | +| OpenCode-only 700 → 1,000 | −13.1 pp | −16.6 to −9.7 pp | +| Multi-harness 500 → OpenCode-only 700 | +2.5 pp | −0.6 to +5.7 pp | + +The late declines are larger than the task-level variability captured by this calculation. The best-score difference does not clearly separate the runs. These intervals condition on observed checkpoints and attempts: they do not account for selecting the best of many checkpoints, another sampling seed, another training seed, or protocol differences. + +## What is ruled out, and what remains uncertain + +- Recorded gradient norms are finite throughout; observed maximum staleness never exceeds four. There is no obvious numerical blow-up or staleness-limit violation in these metrics. Neither check proves healthy policy learning. +- All accepted eval cohorts pass their existing TiTO and version gates. TiTO validates token identity and alignment; it cannot guarantee useful tool behavior or improving rewards. +- **All three comparison trainers use binary correctness.** Native raw rollout artifacts retain an efficiency bonus, but `ComparisonSession.verify()` removes it before training. The preliminary suspicion of different reward objectives was rejected after inspecting the frozen adapter. +- The native run uses Daytona and the Harbor runs use E2B. Baselines are separate measured cohorts. Multi-harness also has several resumed allocations and early recipe changes. +- The published metric retains the experiment's “pass@1” name, with first graded attempt selection and retries for ungraded failures. Native baseline logs show preceding ungraded attempts for 830 cells; multi-harness checkpoint 500 has 65 such cells and final has 205. These counts exclude skip markers and are not necessarily unique infrastructure outages. Multi-harness 900/1,000 also have documented verifier-budget reconciliation. Uniform first-attempt accounting is needed for a stronger controlled comparison. + +## Recommended next work + +1. **Fix and test resume task accounting first.** Preserve completed groups and unfinished work across restarts. Add a test with an early missing group and later completed groups; verify the latter are not rescheduled unintentionally. +2. **Run a small, separately labeled output-budget diagnostic.** Compare multi-harness checkpoints 500 and 1,000 on the same tasks and harness with 4,096 versus 16,384 output tokens. Keep the canonical evaluation scores unchanged. Measure submission, truncation and actual executed tool results, not just reward. Prefer a development split for subsequent tuning. +3. **Investigate completion discipline for OpenCode-only.** Compare peak/final trajectories on lost tasks: repeated calls, unbounded file reads, context exhaustion and failure to write the answer. Increasing the tool budget alone is not supported by these results. +4. **Track behavior during training and evaluation.** Add per-harness output-truncation rate, submission rate, recorded/executed tool calls, repeated-call fraction, completion tokens, task coverage and cumulative supervised tokens. Keep them separate from the headline reward. +5. **Make the next ablation controlled.** Same backend, output budgets, task exposure, capture/agent filters and loss normalization; compare multiple seeds and choose checkpoints on validation data. Measure token-weighted harness exposure before changing weighting. + +## Artifacts and reproduction + +- [Harness/checkpoint metrics](by_harness.csv), [difficulty metrics](by_difficulty.csv), [outcome-conditioned metrics](by_outcome.csv), [output warnings](capture_warnings.csv). +- [Paired score differences](paired_differences.csv), [harness agreement](harness_complementarity.csv), [training windows](training_windows.csv), [training exposure](evaluation_vs_training_exposure.csv). +- [Training analysis](TRAINING.md), [admitted-rollout metrics](training_behavior_windows.csv), [training harness breakdown](training_behavior_by_harness.csv), [optimizer accounting](training_accounting.csv), [training figure](training_diagnostics.png). +- Local analysis code and trace hashes: `experiments/analysis-three-runs-20260917/` (`extract.py`, `training.py`, `analyze.py`, `provenance.json`). +- `sampled_training_lengths.json` records the fixed training sample. `training_lineage.csv` and `optimizer_rollouts.csv` support the resume and weighting calculations. Raw captures and accepted scores were not changed. + +Run the three scripts in order with `.venv312/bin/python`. Outputs remain local; no new inference or training jobs were launched for this analysis. diff --git a/04-data-agent/reports/three-run-analysis-20260917/TLDR.md b/04-data-agent/reports/three-run-analysis-20260917/TLDR.md new file mode 100644 index 0000000..5425204 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/TLDR.md @@ -0,0 +1,8 @@ +Qwen3.5-2B comparison is complete: three 1,000-step runs, evaluated on 250 fixed tasks × four harnesses, pass@1. + +- Best scores: **Harbor multi-harness 37.0% @500**, **Harbor OpenCode-only 39.5% @700**, **native OpenCode 29.8% @1,000**. The Harbor runs finish lower, at 26.3% and 26.4%. +- Multi-harness training outputs grow **3.5k → 9.5k tokens/rollout**, while tool calls fall **15.9 → 11.2**. In the final window, 37.5% of admitted rollouts contain a response exceeding the eval's 4k cap; eval truncation rises sharply. +- Harbor OpenCode-only makes more calls but submits fewer answers. Native keeps shorter outputs and uses fewer training tokens, although late context duplication increases its compute cost. +- We found a resume-accounting bug: **1,575/1,579 post-resume rollouts revisit seen tasks**. Also, **35–58% of steps have zero fresh gradient** because groups have no reward contrast. + +Next: fix resume accounting, test matched output budgets, and track completion, tokens, tool use and task coverage. Equal steps were not equal exposure; these results do not establish that multi-harness training is worse. diff --git a/04-data-agent/reports/three-run-analysis-20260917/TRAINING.md b/04-data-agent/reports/three-run-analysis-20260917/TRAINING.md new file mode 100644 index 0000000..757d1d6 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/TRAINING.md @@ -0,0 +1,122 @@ +# Training behavior and its relation to evaluation + +September 17, 2026. Companion to the [evaluation analysis](REPORT.md). + +The training-side evidence supports different failure modes in the two Harbor runs: multi-harness generates much longer responses with fewer tool calls, while OpenCode-only increases work without reliably finishing. Native OpenCode uses substantially fewer supervised tokens. These are observations from the completed runs, not an isolated comparison of training algorithms. + +## What is counted + +- **Optimizer telemetry:** all 3,000 step records, including supervised/forwarded tokens, gradient norms and timing. +- **Admitted rollouts:** 13,625 saved captures matched to optimizer receipts: 5,013 multi-harness, 4,493 native OpenCode and 4,119 Harbor OpenCode-only. Multi-harness receipts begin at step 31; its first 30 steps have telemetry but are excluded from capture-level comparisons. +- **Tool calls:** parsed tool-call emissions across retained agent turns. They are requests, not proof of successful execution. Auxiliary and discarded turns are excluded. This differs from the evaluation report's longest-transcript counter, which may omit earlier branches. +- **Completion tokens:** captured output IDs across those turns. **Supervised tokens** are positions selected by the loss mask. **Forwarded tokens** include context and completion processed by the trainer, including repeated context across forked rows; they are neither unique tokens nor serving-token bills. + +Every capture's masked token count is checked against its optimizer receipt. All 13,625 agree. Per-rollout averages below count each admitted rollout once. The older `tools/call_frequency` and reward dashboard metrics average over training rows inside microbatches, then logging windows; they can overweight rollouts with more rows. Worker completion-length telemetry also includes generated rollouts independently of their later admission. The datasets and denominators must remain distinct. + +## Token growth in multi-harness training + +Comparing optimizer steps **401–500** with **901–1,000**: + +| Measure, admitted rollouts | Earlier window | Final window | +| --- | ---: | ---: | +| Rollouts | 512 | 485 | +| Mean completion tokens | 3,521 | 9,480 | +| Median completion tokens | 2,359 | 6,776 | +| Mean emitted tool calls | 15.86 | 11.24 | +| At least one response longer than 4,096 tokens | 2.0% | 37.5% | +| Completion tokens in turns without a tool call | 0.9% | 12.4% | +| Mean binary training reward | 37.5% | 39.4% | +| Fixed-test pass@1 at window end | 37.0% | 26.3% | + +The median grows too, so this is not just a few enormous outliers. More tokens accompany fewer actions, and slightly higher reward on the changing training cohort accompanies lower held-out performance. Task replay after the step-684 resume further complicates the training reward. + +The worker's previously reported mean completion length was 3,658 → 10,657. That remains a valid telemetry summary, but the table uses the cleaner once-per-admitted-rollout measure. The full capture census supersedes the earlier small sample for estimating how frequently training responses exceed the eval limit. + +Most late completion tokens still occur in turns that eventually emit a tool call. Inspected successful training examples contain a long explanation followed by a short tool request. Under a shorter output limit, that request may never be emitted. This is a testable explanation for the evaluation's long text responses, low tool-call count and high truncation rate; it does not prove that increasing the eval cap would restore accuracy. + +Per-harness token inflation is broad, despite a balanced admitted-rollout mix: + +| Training harness | Completion tokens, mean | Tool calls, mean | Final rollouts with a response >4,096 tokens | +| --- | ---: | ---: | ---: | +| OpenCode | 2,583 → 5,602 | 14.82 → 7.79 | 38.1% | +| Claude Code | 5,924 → 14,148 | 17.22 → 12.68 | 51.3% | +| Codex | 4,199 → 11,193 | 16.44 → 14.13 | 37.5% | +| Mini-SWE-Agent | 1,603 → 7,110 | 15.11 → 10.37 | 24.2% | + +These compare the same two step windows, not matched training tasks. In particular, task replay and changing difficulty prevent interpreting the training-reward changes as generalization gains. + +Length growth also occurs among **successful** admitted rollouts: their mean completion +length rises **2,453 → 9,084 tokens**. It is not confined to failed attempts. This makes +train/eval budget compatibility worth testing even when training reward appears healthy. + +## Harbor OpenCode-only: more output and actions near the end + +Comparing steps **601–700**, ending at the best evaluated checkpoint, with **901–1,000**: + +| Measure, admitted rollouts | Peak window | Final window | +| --- | ---: | ---: | +| Rollouts | 420 | 404 | +| Mean completion tokens | 2,122 | 3,631 | +| Median completion tokens | 1,552 | 2,102 | +| Mean emitted tool calls | 14.63 | 17.43 | +| Mean retained agent turns | 11.70 | 11.49 | +| At least one response longer than 4,096 tokens | 1.2% | 6.4% | +| Mean binary training reward | 46.2% | 39.4% | +| Fixed-test pass@1 at window end | 39.5% | 26.4% | + +Tool calls rise without more model turns: a response can request multiple tools. The +same broad increase in work appears on the fixed evaluation set, where average tool +calls rise 16.62 → 20.97 and submission declines sharply. Training and evaluation +do not agree on every diagnostic: mean exact repetitions fall 3.82 → 3.03 in these +training windows, while rising in eval. Different tasks and harness mixtures matter. + +Training length is also non-monotonic: steps 101–200 average 7,750 tokens per admitted +rollout, higher than the final window, and the model later recovers. Length alone is +therefore not a sufficient explanation or stopping rule. + +## Native OpenCode: short outputs, but growing context overhead + +From steps 401–500 to 901–1,000, admitted native OpenCode rollouts average **1,145 → 1,075 completion tokens** and **7.72 → 6.05 emitted tool calls**. None of the final window's 402 admitted rollouts contains a response longer than 4,096 tokens. Its final held-out score is its highest measured, 29.8%. + +There is nevertheless a late efficiency regression: **rows per admitted rollout grow 1.91 → 4.56**, and forwarded tokens per supervised token grow **22.3× → 99.4×**. Mean forward/backward time rises **4.69 → 15.96 seconds per step**. Short outputs therefore do not guarantee cheap training when history forks into more context-bearing rows. Overall step time stays approximately flat because recorded rollout waiting time falls; this is not evidence of equal GPU work. + +## Compute exposure and zero-variance groups + +| Full run, 1,000 steps | Supervised tokens | Forwarded tokens | Forwarded / supervised | Zero-fresh-gradient steps | +| --- | ---: | ---: | ---: | ---: | +| Harbor multi-harness | 22.15M | 1,001.32M | 45.2× | 349/1,000 | +| Native OpenCode | 5.33M | 228.41M | 42.9× | 583/1,000 | +| Harbor OpenCode-only | 14.63M | 419.43M | 28.7× | 380/1,000 | + +The context overhead is real, but context is required for correct conditional training; these ratios do not mean that all masked tokens are avoidable waste. Prompt forks can increase repeated context substantially. + +Every recorded zero-gradient step coincides with zero within-group reward standard deviation. With binary rewards, a group whose scorable rollouts all agree has zero GRPO advantage and supplies no fresh learning signal. Different uniform groups can share a step, so a zero-gradient step can still have an intermediate average reward. + +Those steps consume **35.0%, 58.8% and 39.1% of forwarded tokens**, respectively, for multi-harness, native and Harbor OpenCode-only. Native's final 100 steps include 72 zero-gradient steps. This suggests investigating rejection of zero-advantage groups before expensive training forwards, while preserving coverage, scheduling and checkpoint semantics. Generation and grading costs would remain. **Zero fresh gradient does not mean unchanged weights:** optimizer momentum and weight decay can still act. + +## Measurement cautions + +- Training rows are not independent task examples. Claude's 77.3% row share corresponds to 35.3% of supervised tokens in the receipt-covered multi-harness segment, not 77.3% of the gradient. +- `completions/clipped_ratio` in the frozen worker checks whether the **last** output token is EOS/pad. It does not directly measure whether **any** earlier response was truncated, or whether a response exceeds the evaluation's 4,096-token cap. The new capture metrics record those separately. +- Tool-failure telemetry infers errors from result text; it is not a structured count of sandbox failures. Emitted calls and successful executions should be logged separately in future runs. +- A token can be selected by the loss mask yet have zero advantage. “Supervised tokens” is accounting for eligible positions, not a count of nonzero gradient contributions. +- Task mix changes throughout training. Training rewards and per-window rollout statistics are not repeated measurements on a fixed dataset. The evaluation cohorts are fixed, subject to the [documented historical retry policy](REPORT.md#what-is-ruled-out-and-what-remains-uncertain). + +## Reproduction + +![Training token, tool and optimizer diagnostics](training_diagnostics.png) + +[Per-window capture metrics](training_behavior_windows.csv) · +[Harness breakdown](training_behavior_by_harness.csv) · +[Success/failure breakdown](training_behavior_by_outcome.csv) · +[Optimizer accounting](training_accounting.csv) · +[Original step-metric aggregation](training_step_diagnostics.csv) + +Use Python with `orjson`, `pandas`, `numpy` and `matplotlib`. The evidence directory must contain the frozen `optimizer_rollouts.csv`, `training_lineage.csv`, `training_metrics.csv` and the saved captures referenced by the lineage. No model loading or GPU jobs are required. + +```bash +python training_behavior.py --evidence /path/to/analysis-three-runs-20260917 --workers 2 +python summarize_training.py --evidence /path/to/analysis-three-runs-20260917 +``` + +The extractor saves per-rollout counts and source hashes in the evidence directory. The summarizer writes aggregate CSVs and `training_diagnostics.png` beside this document. Local raw evidence is preserved under `experiments/analysis-three-runs-20260917/`; it is not copied into the repository. diff --git a/04-data-agent/reports/three-run-analysis-20260917/behavior.png b/04-data-agent/reports/three-run-analysis-20260917/behavior.png new file mode 100644 index 0000000..01bc8f6 Binary files /dev/null and b/04-data-agent/reports/three-run-analysis-20260917/behavior.png differ diff --git a/04-data-agent/reports/three-run-analysis-20260917/by_difficulty.csv b/04-data-agent/reports/three-run-analysis-20260917/by_difficulty.csv new file mode 100644 index 0000000..71b130d --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/by_difficulty.csv @@ -0,0 +1,103 @@ +run,step,difficulty,cells,score,model_calls,tool_calls,tool_calls_median,tool_calls_p90,repeats,repeat_fraction,repeat_rollout_fraction,short_tool_fraction,budget_fraction,python_fraction,answer_path_fraction,tool_error_text_fraction,submission_fraction,submission_observations,trainable_tokens,wall_s,retry_cells,training_rows,packed_tokens +Harbor OpenCode-only,0,easy,132,0.4015151515151515,11.43939393939394,10.037878787878787,9.0,17.0,0.5378787878787878,0.04280062258003434,0.2803030303030303,0.10606060606060606,0.29545454545454547,0.7272727272727273,0.6287878787878788,0.3939393939393939,0.7777777777777778,36,1347.689393939394,197.37515151515152,0,3.462121212121212,106132.51515151515 +Harbor OpenCode-only,0,hard,396,0.06313131313131314,14.926767676767676,13.972222222222221,16.0,18.0,0.5277777777777778,0.03536039196441673,0.3106060606060606,0.05303030303030303,0.6818181818181818,0.7727272727272727,0.31565656565656564,0.6565656565656566,0.3515625,128,3117.5353535353534,300.3289898989899,5,4.292929292929293,158614.65404040404 +Harbor OpenCode-only,0,medium,472,0.1440677966101695,14.254237288135593,13.572033898305085,16.0,18.0,0.5127118644067796,0.034147748041146085,0.2648305084745763,0.06567796610169492,0.6207627118644068,0.798728813559322,0.3644067796610169,0.6483050847457628,0.3611111111111111,180,2774.4639830508477,273.17495762711866,6,4.163135593220339,141250.06991525425 +Harbor OpenCode-only,100,easy,132,0.5,13.840909090909092,13.93939393939394,17.0,18.0,3.696969696969697,0.23140387778516355,0.6742424242424242,0.0,0.6287878787878788,0.9696969696969697,0.8484848484848485,0.3939393939393939,0.7777777777777778,36,1520.2727272727273,160.64651515151516,0,4.424242424242424,135707.05303030304 +Harbor OpenCode-only,100,hard,396,0.10353535353535354,16.373737373737374,16.946969696969695,17.0,19.0,1.893939393939394,0.11147086153407622,0.5580808080808081,0.0,0.8888888888888888,0.9646464646464646,0.40404040404040403,0.7727272727272727,0.421875,128,3529.4570707070707,302.15959595959595,10,5.108585858585859,203949.1590909091 +Harbor OpenCode-only,100,medium,472,0.2923728813559322,15.754237288135593,16.059322033898304,17.0,18.0,2.582627118644068,0.15455961387261238,0.6567796610169492,0.0,0.815677966101695,0.9682203389830508,0.5614406779661016,0.7542372881355932,0.55,180,3074.0254237288136,266.08033898305086,6,5.033898305084746,167436.80508474575 +Harbor OpenCode-only,200,easy,132,0.6212121212121212,14.780303030303031,14.931818181818182,17.0,18.0,2.803030303030303,0.17419060180107185,0.6893939393939394,0.0,0.6666666666666666,0.9696969696969697,0.803030303030303,0.45454545454545453,0.8055555555555556,36,2004.0151515151515,182.47007575757576,1,4.886363636363637,150108.42424242425 +Harbor OpenCode-only,200,hard,396,0.09343434343434344,16.38131313131313,17.23989898989899,17.0,19.0,1.3232323232323233,0.07787139040835915,0.5126262626262627,0.0025252525252525255,0.9015151515151515,0.9292929292929293,0.30808080808080807,0.7297979797979798,0.3515625,128,3795.3055555555557,302.7638383838384,3,5.116161616161616,205488.398989899 +Harbor OpenCode-only,200,medium,472,0.3220338983050847,16.11864406779661,16.593220338983052,17.0,19.0,1.7055084745762712,0.10145528461083442,0.565677966101695,0.0,0.8283898305084746,0.972457627118644,0.5211864406779662,0.7690677966101694,0.5777777777777777,180,3543.3072033898306,281.685593220339,4,5.165254237288136,172365.45974576272 +Harbor OpenCode-only,300,easy,132,0.6439393939393939,15.515151515151516,16.477272727272727,17.0,20.0,3.007575757575758,0.17679510585681427,0.75,0.0,0.7272727272727273,0.9848484848484849,0.8257575757575758,0.49242424242424243,0.8333333333333334,36,2195.8939393939395,209.41825757575756,1,4.96969696969697,161420.0909090909 +Harbor OpenCode-only,300,hard,396,0.12121212121212122,16.43686868686869,18.785353535353536,18.0,22.0,1.1868686868686869,0.06538184289339485,0.5025252525252525,0.007575757575757576,0.9116161616161617,0.9696969696969697,0.2398989898989899,0.7449494949494949,0.2734375,128,3653.313131313131,313.2989141414141,1,5.075757575757576,207065.547979798 +Harbor OpenCode-only,300,medium,472,0.3220338983050847,16.203389830508474,17.76271186440678,17.0,20.0,1.625,0.09379305345879678,0.5550847457627118,0.0,0.8622881355932204,0.9703389830508474,0.4597457627118644,0.7182203389830508,0.4888888888888889,180,3486.5360169491523,300.88974576271187,2,5.25635593220339,183729.25423728814 +Harbor OpenCode-only,400,easy,132,0.7045454545454546,15.340909090909092,16.916666666666668,17.0,20.0,3.6363636363636362,0.20632059927544402,0.803030303030303,0.0,0.7272727272727273,0.9696969696969697,0.8560606060606061,0.38636363636363635,0.8611111111111112,36,1676.3863636363637,180.5655303030303,0,5.128787878787879,149934.71212121213 +Harbor OpenCode-only,400,hard,396,0.12878787878787878,16.414141414141415,19.532828282828284,18.0,25.0,1.505050505050505,0.07031318895954543,0.5025252525252525,0.005050505050505051,0.9166666666666666,0.9823232323232324,0.2727272727272727,0.7348484848484849,0.3125,128,3457.2550505050503,313.50217171717173,5,5.207070707070707,201173.5883838384 +Harbor OpenCode-only,400,medium,472,0.3898305084745763,16.180084745762713,17.372881355932204,18.0,20.0,2.01271186440678,0.11542638188868691,0.6186440677966102,0.0,0.8538135593220338,0.9851694915254238,0.5741525423728814,0.6927966101694916,0.6111111111111112,180,2831.968220338983,271.41398305084743,0,5.298728813559322,180928.74788135593 +Harbor OpenCode-only,500,easy,132,0.7121212121212122,14.734848484848484,15.083333333333334,17.0,18.0,2.992424242424242,0.18970042854436325,0.7727272727272727,0.0,0.6439393939393939,1.0,0.8560606060606061,0.3333333333333333,0.7777777777777778,36,1893.530303030303,183.36272727272728,1,4.795454545454546,142063.48484848486 +Harbor OpenCode-only,500,hard,396,0.14393939393939395,16.38131313131313,17.42929292929293,17.0,20.0,0.9494949494949495,0.055124705453869986,0.4015151515151515,0.005050505050505051,0.8964646464646465,0.9848484848484849,0.2803030303030303,0.7575757575757576,0.328125,128,3985.656565656566,333.39484848484847,2,5.202020202020202,205821.90151515152 +Harbor OpenCode-only,500,medium,472,0.3792372881355932,15.921610169491526,16.569915254237287,17.0,19.0,1.6864406779661016,0.1010791847098716,0.5508474576271186,0.0,0.809322033898305,0.9936440677966102,0.5720338983050848,0.722457627118644,0.6,180,3351.6271186440677,287.58572033898304,2,5.156779661016949,172525.14194915254 +Harbor OpenCode-only,600,easy,132,0.6666666666666666,14.901515151515152,15.696969696969697,17.0,19.900000000000006,3.659090909090909,0.21904873611844314,0.803030303030303,0.0,0.6060606060606061,0.9848484848484849,0.8409090909090909,0.3409090909090909,0.8055555555555556,36,1570.2348484848485,179.39204545454547,0,4.9393939393939394,147863.96212121213 +Harbor OpenCode-only,600,hard,396,0.13636363636363635,16.17676767676768,17.94191919191919,17.0,21.0,1.22979797979798,0.06901818387853778,0.4595959595959596,0.0,0.8686868686868687,0.9696969696969697,0.35353535353535354,0.6944444444444444,0.375,128,3186.2954545454545,319.99123737373736,0,5.361111111111111,212746.1792929293 +Harbor OpenCode-only,600,medium,472,0.3983050847457627,15.766949152542374,16.54449152542373,17.0,19.0,2.315677966101695,0.13637707702339463,0.6292372881355932,0.0,0.7648305084745762,0.972457627118644,0.6292372881355932,0.6546610169491526,0.6444444444444445,180,2482.741525423729,266.1048516949153,1,5.173728813559322,168450.36016949153 +Harbor OpenCode-only,700,easy,132,0.7121212121212122,12.757575757575758,14.977272727272727,13.0,21.0,2.606060606060606,0.17263439056620186,0.7272727272727273,0.0,0.3409090909090909,0.9772727272727273,0.8787878787878788,0.32575757575757575,0.9166666666666666,36,1731.909090909091,172.15318181818182,0,4.583333333333333,123597.16666666667 +Harbor OpenCode-only,700,hard,396,0.2297979797979798,15.242424242424242,18.343434343434343,17.0,25.0,1.8333333333333333,0.09107917556386938,0.5858585858585859,0.0025252525252525255,0.6944444444444444,0.9772727272727273,0.4595959595959596,0.7424242424242424,0.5234375,128,3642.497474747475,332.80845959595956,1,6.05050505050505,232909.63636363635 +Harbor OpenCode-only,700,medium,472,0.4449152542372881,14.260593220338983,15.633474576271187,17.0,20.0,2.1652542372881354,0.1307367474754911,0.635593220338983,0.0,0.5169491525423728,0.9936440677966102,0.7288135593220338,0.652542372881356,0.7611111111111111,180,2623.4004237288136,258.2209957627119,3,5.538135593220339,165609.76059322033 +Harbor OpenCode-only,800,easy,132,0.6590909090909091,11.303030303030303,10.659090909090908,10.0,17.0,1.4545454545454546,0.1265268468019664,0.6590909090909091,0.022727272727272728,0.19696969696969696,0.8257575757575758,0.8181818181818182,0.26515151515151514,0.8055555555555556,36,2056.9772727272725,177.76227272727272,5,4.234848484848484,105009.15151515152 +Harbor OpenCode-only,800,hard,396,0.17424242424242425,15.166666666666666,15.522727272727273,17.0,18.5,1.0176767676767677,0.07053617680409899,0.5404040404040404,0.005050505050505051,0.6843434343434344,0.946969696969697,0.4015151515151515,0.7424242424242424,0.453125,128,4003.3358585858587,325.8557323232323,13,6.898989898989899,246732.48484848486 +Harbor OpenCode-only,800,medium,472,0.3707627118644068,13.542372881355933,13.59322033898305,15.0,18.0,1.0169491525423728,0.07452813333401831,0.527542372881356,0.012711864406779662,0.4576271186440678,0.934322033898305,0.652542372881356,0.5783898305084746,0.6944444444444444,180,2984.476694915254,253.6126906779661,2,6.095338983050848,174970.31779661018 +Harbor OpenCode-only,900,easy,132,0.6363636363636364,13.265151515151516,14.045454545454545,14.0,19.900000000000006,2.5606060606060606,0.18899229005741108,0.7954545454545454,0.0,0.38636363636363635,0.8863636363636364,0.7954545454545454,0.38636363636363635,0.8055555555555556,36,2139.780303030303,212.3985606060606,1,5.0227272727272725,134276.99242424243 +Harbor OpenCode-only,900,hard,396,0.15151515151515152,15.502525252525253,17.80808080808081,17.0,23.0,1.5833333333333333,0.09042994213003926,0.6237373737373737,0.0,0.7474747474747475,0.946969696969697,0.3939393939393939,0.7171717171717171,0.3125,128,3725.429292929293,331.87939393939394,1,6.709595959595959,248034.23484848486 +Harbor OpenCode-only,900,medium,472,0.3220338983050847,14.273305084745763,15.694915254237289,17.0,21.0,1.951271186440678,0.12628037266993286,0.6949152542372882,0.00211864406779661,0.5614406779661016,0.9216101694915254,0.6101694915254238,0.6652542372881356,0.5,180,2919.612288135593,283.6933686440678,1,5.930084745762712,189920.5529661017 +Harbor OpenCode-only,1000,easy,132,0.5909090909090909,13.795454545454545,18.939393939393938,17.0,28.900000000000006,3.5681818181818183,0.16730068526042882,0.8106060606060606,0.0,0.5151515151515151,0.8863636363636364,0.7954545454545454,0.5303030303030303,0.8055555555555556,36,2469.8257575757575,220.0061363636364,0,4.75,134870.32575757575 +Harbor OpenCode-only,1000,hard,396,0.10858585858585859,15.914141414141413,21.994949494949495,21.0,31.0,2.6944444444444446,0.10768922316242634,0.73989898989899,0.0,0.803030303030303,0.9419191919191919,0.42676767676767674,0.7702020202020202,0.2265625,128,3927.467171717172,330.1743434343434,1,5.628787878787879,210299.43181818182 +Harbor OpenCode-only,1000,medium,472,0.3029661016949153,15.14406779661017,20.66949152542373,20.0,29.0,3.1504237288135593,0.13107595833889285,0.7754237288135594,0.0,0.6694915254237288,0.9639830508474576,0.6101694915254238,0.7139830508474576,0.45555555555555555,180,3224.561440677966,283.37841101694914,1,5.351694915254237,172530.56355932204 +Harbor multi-harness,0,easy,132,0.4015151515151515,11.43939393939394,10.037878787878787,9.0,17.0,0.5378787878787878,0.04280062258003434,0.2803030303030303,0.10606060606060606,0.29545454545454547,0.7272727272727273,0.6287878787878788,0.3939393939393939,0.7777777777777778,36,1347.689393939394,197.37515151515152,0,3.462121212121212,106132.51515151515 +Harbor multi-harness,0,hard,396,0.06313131313131314,14.926767676767676,13.972222222222221,16.0,18.0,0.5277777777777778,0.03536039196441673,0.3106060606060606,0.05303030303030303,0.6818181818181818,0.7727272727272727,0.31565656565656564,0.6565656565656566,0.3515625,128,3117.5353535353534,300.3289898989899,5,4.292929292929293,158614.65404040404 +Harbor multi-harness,0,medium,472,0.1440677966101695,14.254237288135593,13.572033898305085,16.0,18.0,0.5127118644067796,0.034147748041146085,0.2648305084745763,0.06567796610169492,0.6207627118644068,0.798728813559322,0.3644067796610169,0.6483050847457628,0.3611111111111111,180,2774.4639830508477,273.17495762711866,6,4.163135593220339,141250.06991525425 +Harbor multi-harness,100,easy,132,0.5303030303030303,15.803030303030303,15.393939393939394,16.0,17.0,4.803030303030303,0.3027108631495693,0.8106060606060606,0.007575757575757576,0.7803030303030303,0.8560606060606061,0.803030303030303,0.4393939393939394,0.7777777777777778,36,1823.6363636363637,200.79045454545454,5,4.871212121212121,157866.45454545456 +Harbor multi-harness,100,hard,396,0.08585858585858586,16.515151515151516,16.11111111111111,17.0,17.0,1.7575757575757576,0.1072761546011851,0.5151515151515151,0.007575757575757576,0.9065656565656566,0.8282828282828283,0.2474747474747475,0.6818181818181818,0.2734375,128,3185.2954545454545,301.60459595959594,15,5.005050505050505,198126.00252525252 +Harbor multi-harness,100,medium,472,0.3050847457627119,16.345338983050848,15.944915254237289,16.5,17.0,3.0487288135593222,0.18845578778813613,0.6207627118644068,0.0,0.8707627118644068,0.9046610169491526,0.5063559322033898,0.6610169491525424,0.5444444444444444,180,2867.959745762712,279.280593220339,24,5.059322033898305,181082.50635593222 +Harbor multi-harness,200,easy,132,0.5833333333333334,16.022727272727273,15.651515151515152,17.0,17.0,5.409090909090909,0.3318122101210337,0.8333333333333334,0.007575757575757576,0.8333333333333334,0.8939393939393939,0.8106060606060606,0.4318181818181818,0.8333333333333334,36,1668.3181818181818,189.73189393939396,5,5.136363636363637,163443.44696969696 +Harbor multi-harness,200,hard,396,0.1111111111111111,16.70959595959596,16.232323232323232,17.0,17.0,2.073232323232323,0.12704229163123362,0.5984848484848485,0.0025252525252525255,0.9318181818181818,0.8661616161616161,0.27525252525252525,0.6666666666666666,0.34375,128,3032.2676767676767,285.23492424242426,38,4.9646464646464645,197455.54545454544 +Harbor multi-harness,200,medium,472,0.3008474576271186,16.665254237288135,16.194915254237287,17.0,17.0,3.2245762711864407,0.19659043545689528,0.6122881355932204,0.00211864406779661,0.9216101694915254,0.934322033898305,0.4978813559322034,0.6991525423728814,0.5277777777777778,180,2803.1228813559323,274.7260805084746,33,5.0826271186440675,168070.72033898305 +Harbor multi-harness,300,easy,132,0.6439393939393939,15.659090909090908,15.272727272727273,17.0,17.0,6.045454545454546,0.3819660496465309,0.8560606060606061,0.0,0.7878787878787878,0.7803030303030303,0.8560606060606061,0.3333333333333333,0.8333333333333334,36,1396.8333333333333,175.7655303030303,3,4.96969696969697,155371.10606060605 +Harbor multi-harness,300,hard,396,0.11616161616161616,16.535353535353536,16.106060606060606,17.0,17.0,2.6313131313131315,0.16167301953726573,0.6666666666666666,0.007575757575757576,0.9141414141414141,0.8106060606060606,0.3282828282828283,0.6616161616161617,0.3671875,128,2608.436868686869,279.7158585858586,13,4.843434343434343,188384.80555555556 +Harbor multi-harness,300,medium,472,0.3283898305084746,16.48728813559322,16.08686440677966,17.0,17.0,3.8262711864406778,0.235899959494308,0.711864406779661,0.0,0.8877118644067796,0.8919491525423728,0.5360169491525424,0.6419491525423728,0.5666666666666667,180,2191.4300847457625,243.57158898305084,13,5.091101694915254,168238.16313559323 +Harbor multi-harness,400,easy,132,0.7348484848484849,16.386363636363637,15.992424242424242,17.0,17.0,7.424242424242424,0.45267999417397814,0.9090909090909091,0.0,0.8939393939393939,0.8787878787878788,0.8863636363636364,0.2878787878787879,0.8611111111111112,36,1567.7045454545455,176.5731818181818,4,5.068181818181818,155643.80303030304 +Harbor multi-harness,400,hard,396,0.12373737373737374,16.613636363636363,16.11868686868687,17.0,17.0,2.121212121212121,0.13048737587566275,0.6212121212121212,0.005050505050505051,0.9267676767676768,0.8131313131313131,0.2398989898989899,0.6666666666666666,0.3359375,128,3232.558080808081,306.61883838383835,27,4.946969696969697,208357.90151515152 +Harbor multi-harness,400,medium,472,0.3961864406779661,16.656779661016948,16.22669491525424,17.0,17.0,3.7436440677966103,0.22888539848597175,0.7033898305084746,0.0,0.9067796610169492,0.913135593220339,0.5550847457627118,0.7033898305084746,0.5888888888888889,180,2700.843220338983,269.51296610169493,13,5.120762711864407,175150.1843220339 +Harbor multi-harness,500,easy,132,0.7272727272727273,14.946969696969697,14.553030303030303,16.0,17.0,6.393939393939394,0.3937819378828737,0.7878787878787878,0.0,0.7424242424242424,0.7803030303030303,0.8787878787878788,0.3560606060606061,0.8888888888888888,36,1651.9015151515152,179.44037878787879,7,5.166666666666667,154823.67424242425 +Harbor multi-harness,500,hard,396,0.16414141414141414,16.454545454545453,15.909090909090908,16.0,17.0,2.1565656565656566,0.13296133201641458,0.6035353535353535,0.0,0.8838383838383839,0.8409090909090909,0.2904040404040404,0.7196969696969697,0.3515625,128,3444.967171717172,320.602601010101,23,5.005050505050505,203945.94191919192 +Harbor multi-harness,500,medium,472,0.4427966101694915,16.04237288135593,15.595338983050848,16.0,17.0,3.7415254237288136,0.22843953201933537,0.652542372881356,0.0,0.8283898305084746,0.8983050847457628,0.6016949152542372,0.6652542372881356,0.6222222222222222,180,2728.8792372881358,264.19088983050847,35,5.148305084745763,177085.14406779662 +Harbor multi-harness,600,easy,132,0.7272727272727273,15.106060606060606,14.659090909090908,16.0,17.0,5.598484848484849,0.3503047828582588,0.7954545454545454,0.0,0.6893939393939394,0.7803030303030303,0.8787878787878788,0.30303030303030304,0.8333333333333334,36,2073.5757575757575,198.59560606060606,7,5.128787878787879,150598.77272727274 +Harbor multi-harness,600,hard,396,0.11616161616161616,16.244949494949495,15.55050505050505,16.0,17.0,2.823232323232323,0.1767592715626654,0.75,0.0025252525252525255,0.8282828282828283,0.73989898989899,0.255050505050505,0.5883838383838383,0.3359375,128,3866.5833333333335,324.36267676767676,36,5.0353535353535355,197541.06313131313 +Harbor multi-harness,600,medium,472,0.3728813559322034,16.158898305084747,15.588983050847459,16.0,17.0,3.4978813559322033,0.21541417257886272,0.7182203389830508,0.00211864406779661,0.8199152542372882,0.8538135593220338,0.5423728813559322,0.6271186440677966,0.5888888888888889,180,3292.677966101695,285.78447033898306,40,5.2690677966101696,189113.47033898305 +Harbor multi-harness,684,easy,132,0.7575757575757576,14.545454545454545,13.931818181818182,16.0,17.0,5.5606060606060606,0.3535762100100336,0.7575757575757576,0.0,0.6515151515151515,0.7348484848484849,0.8560606060606061,0.25757575757575757,0.8333333333333334,36,1858.0984848484848,203.4105303030303,7,4.871212121212121,158956.40151515152 +Harbor multi-harness,684,hard,396,0.13131313131313133,15.691919191919192,15.030303030303031,16.0,17.0,3.275252525252525,0.21139097649118524,0.8131313131313131,0.007575757575757576,0.7803030303030303,0.7095959595959596,0.29797979797979796,0.6212121212121212,0.390625,128,3455.123737373737,312.76631313131315,29,4.896464646464646,211740.76515151514 +Harbor multi-harness,684,medium,472,0.3580508474576271,15.802966101694915,15.292372881355933,16.0,17.0,4.002118644067797,0.24882787577606083,0.7902542372881356,0.0,0.7648305084745762,0.8813559322033898,0.5635593220338984,0.6080508474576272,0.5722222222222222,180,2715.548728813559,265.1866525423729,29,5.129237288135593,190155.84533898305 +Harbor multi-harness,700,easy,132,0.6060606060606061,14.416666666666666,13.856060606060606,16.0,17.0,5.265151515151516,0.33007927591817965,0.7954545454545454,0.022727272727272728,0.6136363636363636,0.6742424242424242,0.7878787878787878,0.2803030303030303,0.8333333333333334,36,1970.878787878788,199.6037878787879,8,4.954545454545454,163062.37121212122 +Harbor multi-harness,700,hard,396,0.12121212121212122,15.79040404040404,15.244949494949495,16.0,17.0,3.111111111111111,0.19627702269923633,0.7929292929292929,0.007575757575757576,0.8181818181818182,0.6742424242424242,0.2702020202020202,0.5934343434343434,0.3359375,128,3476.1111111111113,308.2049494949495,24,4.909090909090909,216072.91919191918 +Harbor multi-harness,700,medium,472,0.3389830508474576,15.434322033898304,14.896186440677965,16.0,17.0,3.7012711864406778,0.23418002384663258,0.8029661016949152,0.006355932203389831,0.7266949152542372,0.8199152542372882,0.5254237288135594,0.559322033898305,0.55,180,2776.2669491525426,264.39697033898307,30,4.961864406779661,178243.32627118644 +Harbor multi-harness,800,easy,132,0.5909090909090909,15.545454545454545,14.795454545454545,16.0,17.0,5.242424242424242,0.32816583854351233,0.8181818181818182,0.007575757575757576,0.6893939393939394,0.7727272727272727,0.7348484848484849,0.32575757575757575,0.7777777777777778,36,2645.75,266.72719696969693,16,5.204545454545454,173656.93181818182 +Harbor multi-harness,800,hard,396,0.09595959595959595,16.01010101010101,15.26010101010101,16.0,17.0,3.101010101010101,0.2001530884666615,0.8560606060606061,0.007575757575757576,0.7727272727272727,0.6388888888888888,0.15656565656565657,0.4797979797979798,0.1953125,128,4195.964646464647,353.6538636363636,40,4.7272727272727275,204053.08333333334 +Harbor multi-harness,800,medium,472,0.326271186440678,15.96822033898305,15.28177966101695,16.0,17.0,3.722457627118644,0.23435742060787976,0.836864406779661,0.01059322033898305,0.7669491525423728,0.8177966101694916,0.4364406779661017,0.5911016949152542,0.45555555555555555,180,3643.9618644067796,319.04599576271187,51,5.065677966101695,183445.52966101695 +Harbor multi-harness,900,easy,132,0.4621212121212121,10.113636363636363,9.090909090909092,8.0,17.0,3.5757575757575757,0.2309408079945302,0.49242424242424243,0.26515151515151514,0.3333333333333333,0.5606060606060606,0.5909090909090909,0.1590909090909091,0.5833333333333334,36,4353.030303030303,268.02113636363634,12,5.151515151515151,168218.24242424243 +Harbor multi-harness,900,hard,396,0.10858585858585859,10.63888888888889,9.542929292929292,10.0,17.0,1.904040404040404,0.13378210546646188,0.48737373737373735,0.25252525252525254,0.3813131313131313,0.48484848484848486,0.1691919191919192,0.4065656565656566,0.25,128,5734.916666666667,351.60782828282834,82,4.694444444444445,192182.9116161616 +Harbor multi-harness,900,medium,472,0.2605932203389831,10.773305084745763,9.88771186440678,10.5,17.0,2.36228813559322,0.156900177786103,0.4894067796610169,0.2478813559322034,0.4173728813559322,0.6101694915254238,0.3686440677966102,0.3961864406779661,0.36666666666666664,180,5214.561440677966,315.79783898305084,55,5.088983050847458,186332.98093220338 +Harbor multi-harness,1000,easy,132,0.42424242424242425,8.946969696969697,7.863636363636363,7.0,16.0,2.7803030303030303,0.21633946057408626,0.5151515151515151,0.20454545454545456,0.19696969696969696,0.5833333333333334,0.6212121212121212,0.21212121212121213,0.5833333333333334,36,4066.7651515151515,234.94992424242423,10,4.151515151515151,128018.90151515152 +Harbor multi-harness,1000,hard,396,0.13383838383838384,10.234848484848484,8.977272727272727,9.0,17.0,1.803030303030303,0.1410369605606612,0.5176767676767676,0.2474747474747475,0.3005050505050505,0.5151515151515151,0.23737373737373738,0.41919191919191917,0.2578125,128,5776.79797979798,344.4250757575757,115,4.3232323232323235,165884.72474747474 +Harbor multi-harness,1000,medium,472,0.326271186440678,10.351694915254237,9.375,9.0,17.0,2.3792372881355934,0.169109094771335,0.4957627118644068,0.2033898305084746,0.2860169491525424,0.6673728813559322,0.4788135593220339,0.461864406779661,0.4888888888888889,180,5047.576271186441,290.01902542372886,80,4.6440677966101696,150476.43008474575 +Native OpenCode,0,easy,132,0.3787878787878788,11.878787878787879,11.409090909090908,11.0,17.0,0.7424242424242424,0.05489180321716207,0.3787878787878788,0.022727272727272728,0.32575757575757575,0.7727272727272727,0.6515151515151515,0.42424242424242425,0.6944444444444444,36,1456.3636363636363,189.78530303030303,108,4.151515151515151,130537.18939393939 +Native OpenCode,0,hard,396,0.06060606060606061,15.222222222222221,15.219696969696969,17.0,18.0,0.6262626262626263,0.03914278073038497,0.3484848484848485,0.005050505050505051,0.7222222222222222,0.8207070707070707,0.30808080808080807,0.7070707070707071,0.375,128,3037.590909090909,285.3653787878788,352,4.275252525252525,169716.2095959596 +Native OpenCode,0,medium,472,0.18008474576271186,14.114406779661017,13.845338983050848,16.0,17.0,0.6610169491525424,0.04324850105837735,0.3432203389830508,0.006355932203389831,0.6165254237288136,0.8516949152542372,0.4343220338983051,0.684322033898305,0.4388888888888889,180,2670.188559322034,257.00951271186443,370,4.213983050847458,140378.36652542374 +Native OpenCode,100,easy,132,0.44696969696969696,10.606060606060606,9.840909090909092,9.0,17.0,0.3409090909090909,0.02672873489261049,0.22727272727272727,0.022727272727272728,0.25757575757575757,0.8863636363636364,0.7803030303030303,0.3560606060606061,0.75,36,1582.1969696969697,112.94318181818181,1,3.5606060606060606,95629.91666666667 +Native OpenCode,100,hard,396,0.08080808080808081,14.883838383838384,14.717171717171718,17.0,18.0,0.6085858585858586,0.03662273736687111,0.32575757575757575,0.0025252525252525255,0.6994949494949495,0.8888888888888888,0.35353535353535354,0.7474747474747475,0.3984375,128,3246.669191919192,142.5600505050505,0,4.585858585858586,184907.19444444444 +Native OpenCode,100,medium,472,0.2245762711864407,13.788135593220339,13.328389830508474,16.0,17.0,0.4322033898305085,0.02840055160326255,0.2754237288135593,0.00423728813559322,0.5677966101694916,0.9491525423728814,0.5190677966101694,0.7266949152542372,0.5444444444444444,180,2862.802966101695,132.84074152542374,0,4.396186440677966,146513.50211864407 +Native OpenCode,200,easy,132,0.5984848484848485,9.727272727272727,8.727272727272727,7.0,16.0,0.2727272727272727,0.020509533544292906,0.16666666666666666,0.015151515151515152,0.1893939393939394,0.9015151515151515,0.9015151515151515,0.3712121212121212,0.8888888888888888,36,1205.1060606060605,104.05166666666666,0,3.5303030303030303,96414.14393939394 +Native OpenCode,200,hard,396,0.06565656565656566,14.906565656565656,14.666666666666666,17.0,17.0,0.6464646464646465,0.03929988557064268,0.3055555555555556,0.0025252525252525255,0.7247474747474747,0.9419191919191919,0.35858585858585856,0.8282828282828283,0.3515625,128,3278.257575757576,145.20815656565657,0,4.578282828282828,180455.54292929292 +Native OpenCode,200,medium,472,0.2457627118644068,12.595338983050848,12.025423728813559,13.0,17.0,0.461864406779661,0.031698056281479994,0.24152542372881355,0.00211864406779661,0.4576271186440678,0.9661016949152542,0.6101694915254238,0.7033898305084746,0.6666666666666666,180,2361.6228813559323,118.78567796610169,0,4.226694915254237,132614.3029661017 +Native OpenCode,300,easy,132,0.5151515151515151,10.219696969696969,9.083333333333334,7.0,17.0,0.5833333333333334,0.0475081013015505,0.30303030303030304,0.0,0.2196969696969697,0.9393939393939394,0.8257575757575758,0.3409090909090909,0.7777777777777778,36,1203.8484848484848,101.6919696969697,0,3.3636363636363638,79576.4696969697 +Native OpenCode,300,hard,396,0.06818181818181818,15.330808080808081,15.093434343434344,17.0,17.0,0.5353535353535354,0.03276041565837747,0.30303030303030304,0.0,0.76010101010101,0.952020202020202,0.2828282828282828,0.8080808080808081,0.296875,128,3543.3661616161617,142.15734848484848,3,4.646464646464646,184838.16666666666 +Native OpenCode,300,medium,472,0.2563559322033898,13.705508474576272,13.120762711864407,16.0,17.0,0.5190677966101694,0.034250818389192736,0.3156779661016949,0.00211864406779661,0.5677966101694916,0.9703389830508474,0.527542372881356,0.7563559322033898,0.5111111111111111,180,2723.3707627118642,122.8614406779661,3,4.122881355932203,126944.3813559322 +Native OpenCode,400,easy,132,0.5909090909090909,10.401515151515152,9.174242424242424,8.0,17.0,0.4696969696969697,0.03480055978384855,0.26515151515151514,0.0,0.26515151515151514,0.9696969696969697,0.8560606060606061,0.49242424242424243,0.8055555555555556,36,1231.7651515151515,100.40598484848485,0,3.492424242424242,70788.75757575757 +Native OpenCode,400,hard,396,0.11868686868686869,15.244949494949495,14.883838383838384,17.0,17.0,0.5833333333333334,0.035786410478865216,0.33080808080808083,0.0025252525252525255,0.7348484848484849,0.9494949494949495,0.3207070707070707,0.797979797979798,0.4140625,128,3534.520202020202,130.27315656565656,0,4.590909090909091,161968.89646464647 +Native OpenCode,400,medium,472,0.2944915254237288,13.264830508474576,12.629237288135593,15.0,17.0,0.5338983050847458,0.034969736066117973,0.2796610169491525,0.0,0.5127118644067796,0.9766949152542372,0.5783898305084746,0.7563559322033898,0.6,180,2593.9025423728813,116.06038135593221,0,4.188559322033898,118681.89406779662 +Native OpenCode,500,easy,132,0.5303030303030303,10.128787878787879,8.651515151515152,7.0,17.0,0.5681818181818182,0.04908322948095676,0.3106060606060606,0.022727272727272728,0.21212121212121213,0.9696969696969697,0.7878787878787878,0.3712121212121212,0.7222222222222222,36,1078.0984848484848,102.65560606060605,0,3.727272727272727,79580.84090909091 +Native OpenCode,500,hard,396,0.08080808080808081,14.828282828282829,14.282828282828282,17.0,17.0,0.9191919191919192,0.06007285763624304,0.3686868686868687,0.0,0.6868686868686869,0.9621212121212122,0.3409090909090909,0.7777777777777778,0.390625,128,3083.530303030303,138.30416666666665,2,4.792929292929293,176411.02777777778 +Native OpenCode,500,medium,472,0.2733050847457627,13.139830508474576,12.209745762711865,15.0,17.0,0.8283898305084746,0.05650738937064172,0.3538135593220339,0.00423728813559322,0.5148305084745762,0.9766949152542372,0.5677966101694916,0.6970338983050848,0.6,180,2329.8072033898306,121.81180084745762,1,4.546610169491525,122263.01694915254 +Native OpenCode,600,easy,132,0.5606060606060606,10.333333333333334,8.681818181818182,7.0,17.0,1.0,0.08159403047437806,0.3712121212121212,0.030303030303030304,0.24242424242424243,0.9318181818181818,0.8106060606060606,0.3560606060606061,0.75,36,1129.7424242424242,103.59560606060607,2,4.295454545454546,93592.34848484848 +Native OpenCode,600,hard,396,0.10353535353535354,14.224747474747474,13.416666666666666,16.0,17.0,1.2601010101010102,0.08298572439234086,0.44696969696969696,0.0025252525252525255,0.6287878787878788,0.9343434343434344,0.43686868686868685,0.7525252525252525,0.4453125,128,3258.3535353535353,134.60962121212123,6,5.0227272727272725,174275.86363636365 +Native OpenCode,600,medium,472,0.288135593220339,12.39406779661017,11.358050847457626,12.0,17.0,1.1716101694915255,0.08242081922307692,0.4067796610169492,0.006355932203389831,0.4258474576271186,0.9766949152542372,0.6377118644067796,0.6716101694915254,0.6611111111111111,180,2190.1991525423728,115.10735169491525,6,4.447033898305085,111872.16949152542 +Native OpenCode,700,easy,132,0.49242424242424243,10.25,8.893939393939394,7.0,17.0,0.7121212121212122,0.061389237742847366,0.3409090909090909,0.022727272727272728,0.2878787878787879,0.9545454545454546,0.8181818181818182,0.3560606060606061,0.75,36,1008.5,100.61128787878788,0,3.553030303030303,77474.76515151515 +Native OpenCode,700,hard,396,0.07828282828282829,14.462121212121213,13.717171717171718,16.0,17.0,0.8560606060606061,0.05696271237744406,0.36363636363636365,0.005050505050505051,0.6666666666666666,0.9671717171717171,0.4444444444444444,0.7954545454545454,0.40625,128,3196.368686868687,135.29641414141415,0,4.547979797979798,169050.83080808082 +Native OpenCode,700,medium,472,0.288135593220339,12.182203389830509,11.14406779661017,11.5,17.0,0.6419491525423728,0.045274267663924346,0.3093220338983051,0.00847457627118644,0.4385593220338983,0.9745762711864406,0.6779661016949152,0.6588983050847458,0.6944444444444444,180,2100.697033898305,114.62976694915254,0,4.252118644067797,113986.72881355933 +Native OpenCode,800,easy,132,0.5227272727272727,9.371212121212121,8.143939393939394,7.0,17.0,0.25757575757575757,0.026358094272532776,0.20454545454545456,0.015151515151515152,0.19696969696969696,0.9621212121212122,0.8636363636363636,0.4318181818181818,0.8055555555555556,36,1181.0984848484848,101.63969696969697,0,3.5757575757575757,78131.18939393939 +Native OpenCode,800,hard,396,0.11868686868686869,14.227272727272727,13.54040404040404,16.0,17.0,0.5353535353535354,0.03464554153086145,0.31565656565656564,0.005050505050505051,0.6186868686868687,0.9747474747474747,0.4671717171717172,0.7929292929292929,0.5234375,128,3752.9747474747473,140.07709595959597,0,4.51010101010101,178000.86363636365 +Native OpenCode,800,medium,472,0.2966101694915254,12.10593220338983,11.194915254237289,11.5,17.0,0.3559322033898305,0.02621620604292654,0.211864406779661,0.014830508474576272,0.4300847457627119,0.9766949152542372,0.6610169491525424,0.6800847457627118,0.6666666666666666,180,2456.8008474576272,117.00741525423729,0,4.1059322033898304,117600.02754237287 +Native OpenCode,900,easy,132,0.5,9.704545454545455,8.25,7.0,17.0,0.4696969696969697,0.0431211174693795,0.25,0.06060606060606061,0.21212121212121213,0.9015151515151515,0.8181818181818182,0.4015151515151515,0.75,36,1190.0378787878788,101.35371212121213,0,3.4318181818181817,83409.48484848485 +Native OpenCode,900,hard,396,0.1111111111111111,13.669191919191919,12.984848484848484,16.0,17.0,0.5025252525252525,0.03297950094290041,0.2727272727272727,0.015151515151515152,0.5984848484848485,0.9570707070707071,0.4595959595959596,0.7474747474747475,0.46875,128,3514.742424242424,134.26853535353536,2,4.290404040404041,170370.898989899 +Native OpenCode,900,medium,472,0.3029661016949153,12.171610169491526,11.226694915254237,12.0,17.0,0.5021186440677966,0.03788172352323649,0.288135593220339,0.019067796610169493,0.4110169491525424,0.9639830508474576,0.6504237288135594,0.690677966101695,0.7,180,2557.326271186441,118.18383474576271,1,4.120762711864407,122658.96398305085 +Native OpenCode,1000,easy,132,0.5984848484848485,10.113636363636363,8.666666666666666,7.0,17.0,0.696969696969697,0.06439452122941428,0.36363636363636365,0.0,0.21212121212121213,0.9772727272727273,0.9393939393939394,0.45454545454545453,0.8611111111111112,36,896.0833333333334,101.40204545454546,0,4.871212121212121,101680.84848484848 +Native OpenCode,1000,hard,396,0.12626262626262627,14.1489898989899,13.469696969696969,16.0,17.0,0.8207070707070707,0.06018674457845581,0.41414141414141414,0.007575757575757576,0.5909090909090909,0.9646464646464646,0.5505050505050505,0.8484848484848485,0.5859375,128,3492.89898989899,144.32358585858586,0,7.53030303030303,228936.2702020202 +Native OpenCode,1000,medium,472,0.3580508474576271,12.540254237288135,11.63135593220339,12.0,17.0,0.7521186440677966,0.059763516115020815,0.3665254237288136,0.00211864406779661,0.4173728813559322,0.9915254237288136,0.7669491525423728,0.7584745762711864,0.7722222222222223,180,2242.133474576271,119.11391949152542,0,6.923728813559322,172306.16313559323 diff --git a/04-data-agent/reports/three-run-analysis-20260917/by_harness.csv b/04-data-agent/reports/three-run-analysis-20260917/by_harness.csv new file mode 100644 index 0000000..50de53b --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/by_harness.csv @@ -0,0 +1,137 @@ +run,step,harness,cells,score,model_calls,tool_calls,tool_calls_median,tool_calls_p90,repeats,repeat_fraction,repeat_rollout_fraction,short_tool_fraction,budget_fraction,python_fraction,answer_path_fraction,tool_error_text_fraction,submission_fraction,submission_observations,trainable_tokens,wall_s,retry_cells,training_rows,packed_tokens +Harbor OpenCode-only,0,claude-code,250,0.168,13.248,12.696,15.0,18.0,0.552,0.03765119948518259,0.232,0.032,0.52,0.768,0.44,0.664,0.4883720930232558,86,3510.776,306.70408000000003,4,12.9,505615.996 +Harbor OpenCode-only,0,codex,250,0.164,14.56,14.244,17.0,19.099999999999994,0.644,0.04206620581633926,0.34,0.08,0.632,0.756,0.376,0.576,0.3953488372093023,86,3011.892,288.04967999999997,0,1.124,19415.248 +Harbor OpenCode-only,0,mini-swe-agent,250,0.144,16.028,15.152,17.0,17.0,0.404,0.026998038725979903,0.276,0.04,0.808,0.844,0.316,0.636,0.3372093023255814,86,2479.664,227.70248,4,1.248,17148.996 +Harbor OpenCode-only,0,opencode,250,0.108,12.76,10.964,12.5,17.099999999999994,0.488,0.03652784315559323,0.292,0.112,0.448,0.748,0.388,0.596,0.38372093023255816,86,1885.612,273.23328000000004,3,1.216,31783.472 +Harbor OpenCode-only,100,claude-code,250,0.252,15.408,15.812,17.0,19.0,2.724,0.16640826366412326,0.644,0.0,0.768,0.936,0.624,0.768,0.5930232558139535,86,3641.972,286.32432,1,15.348,608059.336 +Harbor OpenCode-only,100,codex,250,0.304,15.496,16.648,17.0,20.0,2.544,0.15182294032879173,0.636,0.0,0.796,0.96,0.536,0.704,0.5348837209302325,86,3298.592,285.90372,2,1.076,17268.192 +Harbor OpenCode-only,100,mini-swe-agent,250,0.204,15.156,15.084,17.0,17.0,0.824,0.05239198056845116,0.404,0.0,0.724,0.976,0.392,0.644,0.38372093023255816,86,2140.036,212.45007999999999,11,1.088,12346.168 +Harbor OpenCode-only,100,opencode,250,0.22,16.928,16.98,17.0,18.0,3.736,0.2199364585706691,0.796,0.0,0.992,0.996,0.596,0.74,0.5930232558139535,86,3116.524,281.12372000000005,2,2.42,73155.784 +Harbor OpenCode-only,200,claude-code,250,0.284,15.568,16.092,17.0,19.0,1.832,0.11433170750166054,0.54,0.004,0.776,0.924,0.508,0.748,0.5697674418604651,86,4204.184,290.2222,1,15.52,608079.264 +Harbor OpenCode-only,200,codex,250,0.308,15.924,17.268,18.0,20.0,2.092,0.12244325080925565,0.588,0.0,0.812,0.952,0.492,0.676,0.5116279069767442,86,3497.2,277.06620000000004,0,1.044,18567.156 +Harbor OpenCode-only,200,mini-swe-agent,250,0.2,15.84,15.768,17.0,17.0,0.752,0.047604835687188624,0.404,0.0,0.784,0.968,0.364,0.636,0.43023255813953487,86,2314.748,210.73584,6,1.08,14755.168 +Harbor OpenCode-only,200,opencode,250,0.292,16.852,17.392,17.0,19.0,2.12,0.12248870350495737,0.712,0.0,0.972,0.976,0.532,0.788,0.5581395348837209,86,3743.516,329.72027999999995,1,2.792,88775.272 +Harbor OpenCode-only,300,claude-code,250,0.304,15.784,16.72,17.0,19.0,1.672,0.10361074801180678,0.504,0.012,0.816,0.948,0.432,0.756,0.5,86,4454.876,327.57912,0,15.772,637861.168 +Harbor OpenCode-only,300,codex,250,0.32,16.276,18.028,18.0,21.0,2.116,0.12139348994119253,0.628,0.0,0.868,0.976,0.5,0.728,0.5116279069767442,86,3702.284,318.68788,2,1.084,19064.132 +Harbor OpenCode-only,300,mini-swe-agent,250,0.236,16.208,16.164,17.0,17.0,0.652,0.04040749838396897,0.416,0.0,0.824,0.984,0.328,0.64,0.3488372093023256,86,2457.928,244.22476,1,1.076,14375.756 +Harbor OpenCode-only,300,opencode,250,0.28,16.552,21.08,19.0,24.0,2.096,0.10858220362877542,0.692,0.0,0.948,0.98,0.424,0.672,0.4186046511627907,86,2913.772,284.4264,1,2.656,88801.412 +Harbor OpenCode-only,400,claude-code,250,0.336,15.956,17.244,17.0,20.0,2.224,0.1318628296618753,0.628,0.008,0.848,0.96,0.512,0.732,0.5232558139534884,86,3706.02,288.30676,1,15.952,616640.776 +Harbor OpenCode-only,400,codex,250,0.332,16.192,18.852,18.0,24.0,2.504,0.14037034449347674,0.672,0.0,0.84,0.98,0.5,0.66,0.5232558139534884,86,3238.676,300.32312,1,1.072,18137.108 +Harbor OpenCode-only,400,mini-swe-agent,250,0.288,15.636,15.632,17.0,17.0,0.64,0.04062847936377348,0.404,0.0,0.776,0.988,0.428,0.6,0.5,86,2049.448,231.96988000000002,2,1.12,13630.868 +Harbor OpenCode-only,400,opencode,250,0.356,16.864,20.944,19.0,23.099999999999994,2.736,0.12537672321606977,0.684,0.0,0.984,1.0,0.528,0.684,0.5581395348837209,86,2714.036,283.75588,1,2.816,91009.216 +Harbor OpenCode-only,500,claude-code,250,0.324,15.568,16.032,17.0,19.0,1.584,0.10352479582616418,0.556,0.008,0.764,0.976,0.496,0.78,0.5348837209302325,86,4486.608,320.51367999999997,2,15.56,607378.124 +Harbor OpenCode-only,500,codex,250,0.372,15.768,17.164,18.0,20.0,2.144,0.12828688929781673,0.62,0.0,0.804,0.996,0.524,0.672,0.5697674418604651,86,3544.256,311.3784,2,1.132,19307.52 +Harbor OpenCode-only,500,mini-swe-agent,250,0.296,15.712,15.688,17.0,17.0,0.464,0.03036280386280386,0.34,0.0,0.748,0.992,0.396,0.632,0.45348837209302323,86,2479.62,243.41188,1,1.124,13965.672 +Harbor OpenCode-only,500,opencode,250,0.328,16.74,17.972,18.0,19.0,2.076,0.11614237145580664,0.568,0.0,0.972,1.0,0.56,0.656,0.5116279069767442,86,3130.452,292.57084000000003,0,2.692,86107.564 +Harbor OpenCode-only,600,claude-code,250,0.312,15.46,16.128,17.0,19.0,2.1,0.1315176920271254,0.616,0.0,0.724,0.944,0.592,0.676,0.627906976744186,86,3963.312,291.50164,0,15.452,593529.86 +Harbor OpenCode-only,600,codex,250,0.344,15.844,17.608,18.0,21.0,2.616,0.1513637730010365,0.676,0.0,0.792,0.988,0.572,0.596,0.5465116279069767,86,3014.716,290.20484000000005,0,1.188,18723.596 +Harbor OpenCode-only,600,mini-swe-agent,250,0.308,15.204,15.2,17.0,17.0,0.492,0.03401022457075088,0.36,0.0,0.664,0.976,0.444,0.592,0.4883720930232558,86,1631.68,240.04868,0,1.408,18290.932 +Harbor OpenCode-only,600,opencode,250,0.356,16.752,19.008,19.0,21.0,3.044,0.16557076775539808,0.688,0.0,0.96,0.984,0.584,0.652,0.5813953488372093,86,1953.884,282.23591999999996,1,2.82,102552.012 +Harbor OpenCode-only,700,claude-code,250,0.464,13.644,14.408,16.0,19.099999999999994,2.016,0.13538080056961474,0.636,0.004,0.484,0.988,0.716,0.692,0.7325581395348837,86,4399.992,286.99879999999996,0,13.636,505591.964 +Harbor OpenCode-only,700,codex,250,0.372,13.428,16.54,17.0,23.0,1.772,0.1027915198845654,0.604,0.0,0.468,0.984,0.636,0.6,0.6976744186046512,86,2704.632,270.38576,1,1.484,21606.22 +Harbor OpenCode-only,700,mini-swe-agent,250,0.344,15.264,15.372,17.0,17.0,0.552,0.036614323295698165,0.372,0.0,0.676,0.992,0.524,0.628,0.5930232558139535,86,1786.416,249.399,2,2.528,29388.456 +Harbor OpenCode-only,700,opencode,250,0.4,15.468,20.16,19.0,26.0,4.028,0.2074647077959726,0.9,0.0,0.628,0.976,0.692,0.66,0.7325581395348837,86,2746.104,298.80316,1,4.812,190272.756 +Harbor OpenCode-only,800,claude-code,250,0.388,12.752,12.588,13.0,17.0,0.976,0.07193511025699746,0.444,0.0,0.388,0.92,0.672,0.648,0.7441860465116279,86,4724.304,288.79735999999997,4,12.74,474807.112 +Harbor OpenCode-only,800,codex,250,0.356,13.648,14.192,15.0,19.0,1.172,0.07992012584594628,0.556,0.004,0.432,0.96,0.584,0.62,0.6511627906976745,86,2506.392,264.28364,4,1.68,24492.112 +Harbor OpenCode-only,800,mini-swe-agent,250,0.316,15.924,15.4,17.0,17.0,0.76,0.05157312883195236,0.436,0.0,0.74,0.984,0.508,0.592,0.5581395348837209,86,2379.072,239.972,3,4.524,48372.472 +Harbor OpenCode-only,800,opencode,250,0.264,13.236,13.7,16.0,18.0,1.392,0.11581622996886155,0.764,0.04,0.492,0.836,0.536,0.548,0.5116279069767442,86,3452.292,295.78172,9,5.728,228941.352 +Harbor OpenCode-only,900,claude-code,250,0.352,13.796,14.408,16.5,19.099999999999994,1.324,0.09232925935881918,0.568,0.004,0.516,0.928,0.516,0.696,0.5581395348837209,86,4797.792,323.89572000000004,0,13.784,509683.264 +Harbor OpenCode-only,900,codex,250,0.272,14.192,16.332,17.0,22.0,1.532,0.0915357853779021,0.628,0.0,0.496,0.94,0.48,0.632,0.47674418604651164,86,2731.02,303.751,0,2.016,31778.196 +Harbor OpenCode-only,900,mini-swe-agent,250,0.26,16.752,16.116,17.0,17.0,1.016,0.06716071771365889,0.56,0.0,0.928,0.984,0.372,0.668,0.4186046511627907,86,2291.792,263.71732000000003,2,4.244,60279.032 +Harbor OpenCode-only,900,opencode,250,0.3,13.768,18.4,20.0,25.0,3.672,0.2304205386347483,0.964,0.0,0.508,0.856,0.828,0.6,0.3953488372093023,86,2722.508,282.09244,1,4.432,220613.992 +Harbor OpenCode-only,1000,claude-code,250,0.308,14.284,18.888,19.0,27.0,2.564,0.12207600811203947,0.708,0.0,0.588,0.908,0.556,0.748,0.5,86,4909.94,326.24172,0,14.28,546690.0 +Harbor OpenCode-only,1000,codex,250,0.324,14.164,20.392,20.0,32.0,3.024,0.1276550610578384,0.78,0.0,0.528,0.948,0.448,0.68,0.5116279069767442,86,2927.156,303.94488,0,2.136,35868.228 +Harbor OpenCode-only,1000,mini-swe-agent,250,0.204,16.852,18.788,17.0,24.099999999999994,1.88,0.09276147753946476,0.692,0.0,0.956,0.952,0.288,0.668,0.26744186046511625,86,2458.74,245.79756,0,2.152,38802.564 +Harbor OpenCode-only,1000,opencode,250,0.22,15.784,25.796,25.0,34.099999999999994,4.632,0.16389335394127683,0.884,0.0,0.736,0.972,0.956,0.752,0.3488372093023256,86,3317.312,298.19368,2,2.96,108702.744 +Harbor multi-harness,0,claude-code,250,0.168,13.248,12.696,15.0,18.0,0.552,0.03765119948518259,0.232,0.032,0.52,0.768,0.44,0.664,0.4883720930232558,86,3510.776,306.70408000000003,4,12.9,505615.996 +Harbor multi-harness,0,codex,250,0.164,14.56,14.244,17.0,19.099999999999994,0.644,0.04206620581633926,0.34,0.08,0.632,0.756,0.376,0.576,0.3953488372093023,86,3011.892,288.04967999999997,0,1.124,19415.248 +Harbor multi-harness,0,mini-swe-agent,250,0.144,16.028,15.152,17.0,17.0,0.404,0.026998038725979903,0.276,0.04,0.808,0.844,0.316,0.636,0.3372093023255814,86,2479.664,227.70248,4,1.248,17148.996 +Harbor multi-harness,0,opencode,250,0.108,12.76,10.964,12.5,17.099999999999994,0.488,0.03652784315559323,0.292,0.112,0.448,0.748,0.388,0.596,0.38372093023255816,86,1885.612,273.23328000000004,3,1.216,31783.472 +Harbor multi-harness,100,claude-code,250,0.276,16.26,15.808,17.0,17.0,3.0,0.18587976378626533,0.652,0.008,0.876,0.78,0.504,0.632,0.5232558139534884,86,3830.668,330.60032,13,15.588,640878.472 +Harbor multi-harness,100,codex,250,0.28,16.62,16.456,17.0,18.0,1.852,0.11074251513805289,0.56,0.0,0.904,0.896,0.388,0.688,0.46511627906976744,86,3349.404,320.15596,0,1.1,17822.008 +Harbor multi-harness,100,mini-swe-agent,250,0.192,15.86,15.804,17.0,17.0,0.984,0.06046919126625009,0.484,0.0,0.78,0.932,0.324,0.608,0.3372093023255814,86,1925.932,211.64244,9,1.064,14148.712 +Harbor multi-harness,100,opencode,250,0.244,16.624,15.684,16.0,16.0,5.24,0.3284698217846824,0.72,0.008,0.932,0.864,0.556,0.632,0.5465116279069767,86,2317.092,248.64208000000002,22,2.3,66219.656 +Harbor multi-harness,200,claude-code,250,0.3,16.616,16.076,17.0,17.0,3.616,0.21978729800732763,0.68,0.004,0.908,0.892,0.488,0.672,0.4883720930232558,86,3915.984,332.44284000000005,44,15.9,626332.568 +Harbor multi-harness,200,codex,250,0.264,16.828,16.548,17.0,17.0,2.108,0.12487952724949629,0.584,0.004,0.928,0.936,0.436,0.72,0.5,86,3338.936,312.86604,0,1.016,16807.012 +Harbor multi-harness,200,mini-swe-agent,250,0.184,16.176,16.144,17.0,17.0,1.264,0.07661575809811104,0.552,0.0,0.848,0.864,0.288,0.584,0.313953488372093,86,1583.696,185.23776,21,1.068,13119.784 +Harbor multi-harness,200,opencode,250,0.304,16.772,15.784,16.0,16.0,5.24,0.32631199567546315,0.728,0.004,0.972,0.916,0.592,0.628,0.6627906976744186,86,2137.664,240.12676000000002,11,2.188,60125.88 +Harbor multi-harness,300,claude-code,250,0.332,16.24,15.936,17.0,17.099999999999994,4.148,0.25688259150550175,0.768,0.012,0.876,0.844,0.504,0.652,0.5697674418604651,86,3264.88,309.29952000000003,17,15.692,612400.22 +Harbor multi-harness,300,codex,250,0.296,16.28,15.888,17.0,17.0,2.496,0.15552757522834923,0.604,0.0,0.856,0.932,0.472,0.716,0.46511627906976744,86,2760.52,294.50336,0,1.024,16410.9 +Harbor multi-harness,300,mini-swe-agent,250,0.22,16.336,16.312,17.0,17.0,2.652,0.15764385189973423,0.716,0.0,0.872,0.792,0.356,0.508,0.3372093023255814,86,1409.048,182.90048000000002,4,1.032,10683.584 +Harbor multi-harness,300,opencode,250,0.296,16.732,15.812,16.0,17.0,5.288,0.3330932420520656,0.764,0.0,0.936,0.812,0.652,0.56,0.7093023255813954,86,1572.264,209.03392000000002,8,2.16,58576.424 +Harbor multi-harness,400,claude-code,250,0.368,16.476,15.952,17.0,17.0,3.776,0.23215647616916968,0.78,0.008,0.884,0.832,0.5,0.628,0.5581395348837209,86,3781.936,331.85516,12,15.808,645411.508 +Harbor multi-harness,400,codex,250,0.348,16.764,16.5,17.0,17.0,3.636,0.21900172409290056,0.644,0.0,0.924,0.964,0.476,0.74,0.45348837209302323,86,3134.512,301.95732,0,1.032,17655.752 +Harbor multi-harness,400,mini-swe-agent,250,0.288,16.552,16.476,17.0,17.0,2.652,0.15686939302527536,0.704,0.0,0.924,0.844,0.4,0.572,0.4883720930232558,86,1856.436,206.72688,7,1.076,13024.596 +Harbor multi-harness,400,opencode,250,0.328,16.624,15.684,16.0,16.0,4.284,0.26981507936507937,0.664,0.0,0.92,0.836,0.52,0.596,0.5930232558139535,86,2274.428,247.216,25,2.264,66810.536 +Harbor multi-harness,500,claude-code,250,0.448,16.444,16.064,17.0,17.0,4.388,0.2668000983708414,0.8,0.0,0.888,0.852,0.548,0.616,0.627906976744186,86,3942.864,327.30096000000003,16,15.888,636345.004 +Harbor multi-harness,500,codex,250,0.392,16.86,16.452,17.0,17.0,3.644,0.2180356946974594,0.716,0.0,0.936,0.924,0.508,0.78,0.5581395348837209,86,3375.588,314.03576,0,1.02,17570.668 +Harbor multi-harness,500,mini-swe-agent,250,0.312,14.384,14.328,17.0,17.0,0.9,0.05523096282802165,0.388,0.0,0.612,0.864,0.452,0.596,0.45348837209302323,86,1786.76,199.01392,12,1.124,13975.28 +Harbor multi-harness,500,opencode,250,0.328,16.556,15.484,16.0,16.0,4.924,0.30975469367234076,0.7,0.0,0.92,0.8,0.552,0.592,0.5581395348837209,86,2375.944,261.0208,37,2.344,71243.072 +Harbor multi-harness,600,claude-code,250,0.3,16.392,16.1,17.0,18.0,3.848,0.23563521318722555,0.848,0.004,0.872,0.732,0.444,0.584,0.4883720930232558,86,4780.032,350.56232,5,15.836,642993.316 +Harbor multi-harness,600,codex,250,0.324,16.784,16.14,17.0,17.0,3.504,0.2106117257699301,0.736,0.0,0.872,0.928,0.472,0.672,0.5232558139534884,86,4121.492,349.44771999999995,0,1.06,18950.12 +Harbor multi-harness,600,mini-swe-agent,250,0.308,14.732,14.552,16.0,17.0,1.864,0.11646161550867433,0.644,0.0,0.624,0.804,0.436,0.472,0.4883720930232558,86,1926.224,196.08360000000002,13,1.2,11305.592 +Harbor multi-harness,600,opencode,250,0.34,16.308,15.012,16.0,16.0,4.816,0.3089410148674855,0.736,0.004,0.856,0.732,0.54,0.548,0.5813953488372093,86,2608.344,262.1164,65,2.536,76218.4 +Harbor multi-harness,684,claude-code,250,0.356,15.964,15.64,17.0,17.0,4.424,0.28016906517974627,0.892,0.008,0.828,0.768,0.52,0.608,0.6046511627906976,86,4017.62,326.16508,10,15.504,680171.832 +Harbor multi-harness,684,codex,250,0.332,16.788,16.196,17.0,17.0,4.176,0.2533938959389424,0.816,0.0,0.86,0.92,0.464,0.652,0.46511627906976744,86,3783.12,349.0168,0,1.04,18502.496 +Harbor multi-harness,684,mini-swe-agent,250,0.304,13.936,13.728,16.0,17.0,2.192,0.14189798077739255,0.688,0.0,0.528,0.796,0.5,0.484,0.5348837209302325,86,1764.008,194.05316,8,1.2,12895.984 +Harbor multi-harness,684,opencode,250,0.292,15.684,14.472,16.0,16.0,4.888,0.31585763321645677,0.784,0.004,0.808,0.692,0.504,0.524,0.5232558139534884,86,2016.2,234.25995999999998,47,2.268,66770.276 +Harbor multi-harness,700,claude-code,250,0.316,15.704,15.412,17.0,17.099999999999994,4.228,0.2679575435228686,0.892,0.008,0.792,0.72,0.464,0.584,0.5116279069767442,86,4169.628,335.68408,5,15.192,665029.128 +Harbor multi-harness,700,codex,250,0.32,16.784,16.356,17.0,17.0,4.284,0.2596398617722147,0.88,0.0,0.888,0.884,0.492,0.644,0.5348837209302325,86,3697.384,331.43440000000004,0,1.008,17985.792 +Harbor multi-harness,700,mini-swe-agent,250,0.304,14.388,14.116,16.0,17.0,2.12,0.1378408680071219,0.736,0.0,0.58,0.772,0.456,0.488,0.5348837209302325,86,1896.328,201.05236,11,1.292,13888.552 +Harbor multi-harness,700,opencode,250,0.212,14.888,13.704,16.0,16.0,4.064,0.2618782733606263,0.684,0.028,0.732,0.596,0.424,0.428,0.4186046511627907,86,2025.036,224.59807999999998,46,2.268,67976.364 +Harbor multi-harness,800,claude-code,250,0.32,15.736,15.392,17.0,17.0,4.896,0.3106986468811546,0.912,0.008,0.796,0.688,0.416,0.612,0.46511627906976744,86,4761.008,360.24136,9,15.132,649276.748 +Harbor multi-harness,800,codex,250,0.268,16.62,15.448,16.0,17.0,3.856,0.24653691700456407,0.9,0.0,0.688,0.872,0.384,0.524,0.38372093023255816,86,4708.132,423.90656,0,1.044,19406.28 +Harbor multi-harness,800,mini-swe-agent,250,0.26,15.344,15.228,17.0,17.0,2.22,0.1355879032731974,0.728,0.0,0.684,0.78,0.324,0.448,0.32558139534883723,86,2640.172,239.52272,25,1.116,15126.248 +Harbor multi-harness,800,opencode,250,0.232,16.016,14.768,16.0,16.0,3.736,0.23995739783092726,0.828,0.028,0.868,0.624,0.336,0.464,0.3953488372093023,86,2813.852,279.70788,73,2.508,77446.828 +Harbor multi-harness,900,claude-code,250,0.364,15.512,14.936,16.0,17.0,5.904,0.3726758302861345,0.888,0.02,0.82,0.688,0.492,0.548,0.5930232558139535,86,9519.716,419.02456,34,15.492,664811.652 +Harbor multi-harness,900,codex,250,0.192,9.212,8.444,7.0,17.0,1.884,0.14712137633608222,0.492,0.116,0.192,0.62,0.288,0.336,0.2441860465116279,86,3711.06,337.0398,2,1.1,14518.416 +Harbor multi-harness,900,mini-swe-agent,250,0.328,13.824,12.988,14.0,17.0,1.092,0.07299727102962397,0.508,0.0,0.496,0.852,0.448,0.544,0.5,86,3963.104,307.00915999999995,110,1.112,14503.728 +Harbor multi-harness,900,opencode,250,0.024,3.984,2.216,1.0,3.0,0.484,0.034528688524590165,0.068,0.872,0.06,0.056,0.048,0.048,0.046511627906976744,86,4033.72,231.61476000000002,3,2.06,51199.836 +Harbor multi-harness,1000,claude-code,250,0.388,13.172,12.216,14.0,17.0,3.784,0.2879195793542543,0.864,0.008,0.476,0.688,0.532,0.58,0.5581395348837209,86,9486.256,409.62556,73,13.168,529513.164 +Harbor multi-harness,1000,codex,250,0.248,10.044,9.244,9.0,17.0,2.784,0.21200627215921333,0.58,0.136,0.228,0.692,0.388,0.452,0.3372093023255814,86,4252.036,336.64468,8,1.056,14959.276 +Harbor multi-harness,1000,mini-swe-agent,250,0.36,11.948,11.004,11.0,17.0,1.124,0.08019051993757875,0.424,0.0,0.304,0.832,0.564,0.484,0.627906976744186,86,3199.972,268.63016,120,1.408,13323.572 +Harbor multi-harness,1000,opencode,250,0.056,5.316,3.608,1.0,16.0,1.124,0.076791380188439,0.16,0.74,0.112,0.172,0.124,0.132,0.12790697674418605,86,3889.26,202.2784,4,2.176,56658.872 +Native OpenCode,0,claude-code,250,0.168,13.564,13.444,16.0,18.0,0.624,0.04410033896959594,0.304,0.012,0.544,0.78,0.432,0.684,0.4418604651162791,86,3470.204,300.24328,207,13.332,531470.596 +Native OpenCode,0,codex,250,0.152,14.888,15.444,17.0,19.0,0.844,0.05171758901179685,0.444,0.0,0.684,0.756,0.372,0.592,0.4186046511627907,86,2925.184,298.26488,214,1.164,19845.676 +Native OpenCode,0,mini-swe-agent,250,0.188,15.88,15.62,17.0,17.0,0.632,0.042267335932041815,0.368,0.0,0.768,0.916,0.364,0.676,0.3953488372093023,86,2504.592,217.98556,204,1.244,17376.724 +Native OpenCode,0,opencode,250,0.128,12.7,11.764,14.0,17.0,0.532,0.0345529428603732,0.284,0.02,0.484,0.864,0.484,0.684,0.5116279069767442,86,1721.84,220.96564,205,1.18,34095.472 +Native OpenCode,100,claude-code,250,0.204,12.648,12.48,15.0,18.0,0.58,0.03509144543406221,0.284,0.016,0.5,0.872,0.536,0.692,0.5348837209302325,86,3698.748,143.16423999999998,0,12.584,505121.256 +Native OpenCode,100,codex,250,0.18,14.984,15.22,17.0,18.0,0.516,0.0335499714839573,0.352,0.004,0.66,0.884,0.404,0.692,0.4069767441860465,86,2993.728,158.66124,0,1.304,19672.46 +Native OpenCode,100,mini-swe-agent,250,0.208,15.376,15.172,17.0,17.0,0.416,0.028294982794982795,0.312,0.0,0.728,0.96,0.396,0.692,0.4186046511627907,86,2541.22,90.21448,1,1.292,15771.72 +Native OpenCode,100,opencode,250,0.196,12.2,10.8,12.0,16.0,0.448,0.028807029726379572,0.208,0.004,0.428,0.952,0.616,0.668,0.686046511627907,86,2149.4,144.21248,0,2.264,79437.648 +Native OpenCode,200,claude-code,250,0.24,12.36,12.156,16.0,17.0,0.688,0.04049570551567456,0.256,0.008,0.488,0.944,0.556,0.736,0.6046511627906976,86,3558.216,138.13363999999999,0,12.348,472189.48 +Native OpenCode,200,codex,250,0.256,13.516,13.576,17.0,18.0,0.464,0.029959747012533393,0.272,0.0,0.532,0.912,0.54,0.696,0.5697674418604651,86,2495.156,147.19407999999999,0,1.348,17961.452 +Native OpenCode,200,mini-swe-agent,250,0.216,15.116,14.708,17.0,17.0,0.4,0.03000764594882242,0.272,0.0,0.692,0.96,0.464,0.692,0.5,86,2482.148,83.50396,0,1.528,16233.036 +Native OpenCode,200,opencode,250,0.172,11.536,10.104,9.5,16.0,0.488,0.032465955835906224,0.228,0.008,0.4,0.976,0.636,0.712,0.6162790697674418,86,1752.28,140.38468,0,1.872,80740.084 +Native OpenCode,300,claude-code,250,0.292,12.476,12.104,14.0,17.0,0.392,0.02518707996509854,0.216,0.004,0.448,0.96,0.592,0.76,0.627906976744186,86,3980.676,140.191,0,12.444,487869.28 +Native OpenCode,300,codex,250,0.168,15.132,15.164,17.0,18.0,0.62,0.038108605979503815,0.392,0.0,0.72,0.912,0.372,0.696,0.29069767441860467,86,2723.488,148.16628,0,1.292,17298.944 +Native OpenCode,300,mini-swe-agent,250,0.196,15.668,15.244,17.0,17.0,0.576,0.041733681677799325,0.368,0.0,0.736,0.968,0.356,0.736,0.37209302325581395,86,2633.112,85.03076,4,1.5,16793.236 +Native OpenCode,300,opencode,250,0.208,12.28,10.964,14.0,16.0,0.548,0.0366129533864828,0.26,0.0,0.488,0.996,0.56,0.696,0.5465116279069767,86,2052.772,137.44495999999998,2,1.684,52509.564 +Native OpenCode,400,claude-code,250,0.324,12.264,11.9,15.0,18.0,0.552,0.03280133957889376,0.224,0.004,0.484,0.948,0.544,0.772,0.6046511627906976,86,3900.464,129.74188,0,12.236,446078.456 +Native OpenCode,400,codex,250,0.256,14.96,14.968,17.0,18.0,0.636,0.03993127256300631,0.368,0.0,0.684,0.96,0.42,0.716,0.4186046511627907,86,2660.132,142.42044,0,1.284,17106.884 +Native OpenCode,400,mini-swe-agent,250,0.272,15.504,14.892,17.0,17.0,0.504,0.03642990179754886,0.36,0.0,0.696,0.96,0.452,0.776,0.5116279069767442,86,2719.588,81.37939999999999,0,1.716,18353.556 +Native OpenCode,400,opencode,250,0.204,11.956,10.504,11.0,16.0,0.488,0.031920717517776344,0.24,0.0,0.408,0.992,0.636,0.688,0.6744186046511628,86,1866.156,124.94732,0,1.788,36467.716 +Native OpenCode,500,claude-code,250,0.264,12.184,11.512,12.0,17.0,0.744,0.051082755531362344,0.332,0.012,0.456,0.964,0.56,0.716,0.6046511627906976,86,3580.768,137.15052,0,12.152,460326.656 +Native OpenCode,500,codex,250,0.188,14.748,14.58,17.0,17.0,0.572,0.03571082699550192,0.344,0.004,0.656,0.96,0.384,0.64,0.3488372093023256,86,2350.368,149.06395999999998,0,1.352,17247.444 +Native OpenCode,500,mini-swe-agent,250,0.292,15.336,14.256,16.0,17.0,0.468,0.03840567341155577,0.328,0.0,0.696,0.972,0.456,0.732,0.5116279069767442,86,2713.324,89.19475999999999,3,2.372,21986.98 +Native OpenCode,500,opencode,250,0.18,11.376,9.896,9.0,16.0,1.536,0.10255804685510567,0.412,0.004,0.364,0.984,0.628,0.656,0.6744186046511628,86,1207.764,127.8474,0,2.268,52725.248 +Native OpenCode,600,claude-code,250,0.324,11.48,10.624,9.0,17.0,0.948,0.0612672097768073,0.344,0.016,0.384,0.952,0.628,0.676,0.5930232558139535,86,3209.396,128.68632,1,11.452,434024.612 +Native OpenCode,600,codex,250,0.184,14.524,14.364,17.0,17.0,1.136,0.07073384025675047,0.54,0.0,0.644,0.94,0.448,0.608,0.38372093023255816,86,2301.072,143.4634,0,1.44,17967.132 +Native OpenCode,600,mini-swe-agent,250,0.328,14.712,13.14,15.0,17.0,0.696,0.061164984362043186,0.424,0.0,0.58,0.988,0.568,0.764,0.6395348837209303,86,2964.56,89.3322,5,3.12,27073.072 +Native OpenCode,600,opencode,250,0.168,10.672,9.152,8.0,16.0,1.956,0.13697550782550783,0.364,0.016,0.32,0.936,0.68,0.6,0.7441860465116279,86,1417.804,123.76088,8,2.608,57619.568 +Native OpenCode,700,claude-code,250,0.296,11.404,10.44,9.0,17.0,0.82,0.05602791266181359,0.296,0.024,0.376,0.916,0.704,0.724,0.6511627906976745,86,3060.304,126.11748,0,11.388,444784.676 +Native OpenCode,700,codex,250,0.144,15.484,15.244,17.0,17.0,0.96,0.06026415414650709,0.444,0.0,0.764,0.988,0.384,0.62,0.32558139534883723,86,2521.796,143.21072,0,1.328,16509.94 +Native OpenCode,700,mini-swe-agent,250,0.304,14.548,13.608,16.0,17.0,0.756,0.0629743749714338,0.404,0.0,0.632,0.992,0.568,0.728,0.6046511627906976,86,2763.38,90.39956,0,2.444,23490.592 +Native OpenCode,700,opencode,250,0.184,9.884,8.172,6.0,16.0,0.408,0.028853829503829507,0.196,0.012,0.264,0.98,0.76,0.62,0.7906976744186046,86,1216.172,124.12552000000001,0,1.948,39104.928 +Native OpenCode,800,claude-code,250,0.324,11.424,10.528,9.5,17.0,0.436,0.02966107916850951,0.228,0.02,0.332,0.96,0.712,0.736,0.7325581395348837,86,3660.24,130.67832,0,11.4,451090.5 +Native OpenCode,800,codex,250,0.18,14.792,14.496,17.0,17.0,0.552,0.03512954953543188,0.32,0.008,0.688,0.976,0.412,0.588,0.3488372093023256,86,2570.196,144.53616,0,1.376,17461.052 +Native OpenCode,800,mini-swe-agent,250,0.32,14.352,13.776,17.0,17.0,0.464,0.03996033019268313,0.344,0.0,0.616,0.996,0.552,0.804,0.6046511627906976,86,3235.884,93.91984,0,2.104,23560.276 +Native OpenCode,800,opencode,250,0.2,9.772,8.084,5.5,16.0,0.204,0.013540849673202616,0.116,0.016,0.26,0.964,0.768,0.64,0.8255813953488372,86,1740.452,127.32356,0,1.904,53123.66 +Native OpenCode,900,claude-code,250,0.34,10.984,10.08,8.5,17.0,0.4,0.027455024060906413,0.192,0.032,0.324,0.888,0.7,0.688,0.7674418604651163,86,3712.704,133.01479999999998,0,10.972,444943.16 +Native OpenCode,900,codex,250,0.144,15.312,15.088,17.0,17.0,0.72,0.04548971003197009,0.396,0.004,0.74,0.984,0.308,0.604,0.313953488372093,86,2710.16,143.14632,0,1.296,16285.736 +Native OpenCode,900,mini-swe-agent,250,0.34,14.636,13.84,16.0,17.0,0.672,0.057749588320176556,0.388,0.0,0.608,1.0,0.596,0.78,0.5581395348837209,86,3245.656,91.29476,2,2.124,24792.192 +Native OpenCode,900,opencode,250,0.188,8.824,7.112,5.0,16.0,0.2,0.015833851116204056,0.132,0.056,0.164,0.94,0.784,0.628,0.8372093023255814,86,1355.404,121.87132000000001,1,1.996,59466.748 +Native OpenCode,1000,claude-code,250,0.332,11.224,10.528,10.0,17.0,0.496,0.03395395028088526,0.26,0.012,0.336,0.94,0.752,0.748,0.7558139534883721,86,3596.888,133.73032,0,11.224,449093.488 +Native OpenCode,1000,codex,250,0.296,14.788,14.46,17.0,17.0,0.512,0.03377497992203875,0.324,0.004,0.632,1.0,0.604,0.864,0.5232558139534884,86,2098.572,147.44584,0,4.988,66343.82 +Native OpenCode,1000,mini-swe-agent,250,0.36,14.684,13.736,15.0,17.0,0.836,0.06810367149190678,0.424,0.0,0.6,0.996,0.656,0.724,0.7790697674418605,86,2917.784,98.07616,0,5.208,63338.732 +Native OpenCode,1000,opencode,250,0.204,10.732,9.148,8.0,16.0,1.244,0.10633702735173323,0.532,0.0,0.268,0.98,0.804,0.68,0.7906976744186046,86,1625.788,127.7836,0,6.152,162860.536 diff --git a/04-data-agent/reports/three-run-analysis-20260917/by_outcome.csv b/04-data-agent/reports/three-run-analysis-20260917/by_outcome.csv new file mode 100644 index 0000000..f791904 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/by_outcome.csv @@ -0,0 +1,273 @@ +run,step,harness,reward,cells,score,model_calls,tool_calls,tool_calls_median,tool_calls_p90,repeats,repeat_fraction,repeat_rollout_fraction,short_tool_fraction,budget_fraction,python_fraction,answer_path_fraction,tool_error_text_fraction,submission_fraction,submission_observations,trainable_tokens,wall_s,retry_cells,training_rows,packed_tokens +Harbor OpenCode-only,0,claude-code,0,208,0.0,14.177884615384615,13.759615384615385,16.0,18.0,0.6009615384615384,0.03858411235582612,0.24519230769230768,0.028846153846153848,0.6105769230769231,0.7548076923076923,0.33653846153846156,0.7163461538461539,0.35294117647058826,68,3907.5673076923076,330.2196153846154,4,13.774038461538462,551784.2644230769 +Harbor OpenCode-only,0,claude-code,1,42,1.0,8.642857142857142,7.428571428571429,7.0,13.799999999999997,0.30952380952380953,0.032916666666666664,0.16666666666666666,0.047619047619047616,0.07142857142857142,0.8333333333333334,0.9523809523809523,0.40476190476190477,1.0,18,1545.7142857142858,190.2461904761905,0,8.571428571428571,276973.14285714284 +Harbor OpenCode-only,0,codex,0,209,0.0,15.167464114832535,15.014354066985646,17.0,20.0,0.5933014354066986,0.03703165718455073,0.3492822966507177,0.07655502392344497,0.7129186602870813,0.7416267942583732,0.2727272727272727,0.6172248803827751,0.23529411764705882,68,3264.622009569378,302.87047846889953,0,1.1483253588516746,20704.593301435405 +Harbor OpenCode-only,0,codex,1,41,1.0,11.463414634146341,10.317073170731707,9.0,17.0,0.9024390243902439,0.06859963779468424,0.2926829268292683,0.0975609756097561,0.21951219512195122,0.8292682926829268,0.9024390243902439,0.36585365853658536,1.0,18,1723.5853658536585,212.49975609756098,0,1.0,12842.731707317073 +Harbor OpenCode-only,0,mini-swe-agent,0,214,0.0,16.490654205607477,15.682242990654206,17.0,17.0,0.37850467289719625,0.024792598653963587,0.26635514018691586,0.037383177570093455,0.8878504672897196,0.8317757009345794,0.2102803738317757,0.677570093457944,0.20833333333333334,72,2659.411214953271,232.60500000000002,4,1.1682242990654206,17348.168224299065 +Harbor OpenCode-only,0,mini-swe-agent,1,36,1.0,13.277777777777779,12.0,12.0,16.0,0.5555555555555556,0.040360410927019925,0.3333333333333333,0.05555555555555555,0.3333333333333333,0.9166666666666666,0.9444444444444444,0.3888888888888889,1.0,14,1411.1666666666667,198.5597222222222,0,1.7222222222222223,15965.027777777777 +Harbor OpenCode-only,0,opencode,0,223,0.0,13.179372197309418,11.448430493273543,14.0,18.0,0.5246636771300448,0.038140999359143606,0.3094170403587444,0.10762331838565023,0.4977578475336323,0.7488789237668162,0.33183856502242154,0.6322869955156951,0.3291139240506329,79,2014.8161434977578,275.3364125560538,3,1.1973094170403586,31591.430493273543 +Harbor OpenCode-only,0,opencode,1,27,1.0,9.296296296296296,6.962962962962963,7.0,12.400000000000002,0.18518518518518517,0.022289986228605157,0.14814814814814814,0.14814814814814814,0.037037037037037035,0.7407407407407407,0.8518518518518519,0.2962962962962963,1.0,7,818.4814814814815,255.86296296296297,0,1.3703703703703705,33369.59259259259 +Harbor OpenCode-only,100,claude-code,0,187,0.0,15.737967914438503,16.27807486631016,17.0,19.0,2.1176470588235294,0.1291534628941908,0.5935828877005348,0.0,0.8074866310160428,0.9251336898395722,0.49732620320855614,0.8235294117647058,0.4067796610169492,59,4037.96256684492,313.77401069518714,1,15.657754010695188,648994.1336898396 +Harbor OpenCode-only,100,claude-code,1,63,1.0,14.428571428571429,14.428571428571429,17.0,18.0,4.523809523809524,0.2769899738859863,0.7936507936507936,0.0,0.6507936507936508,0.9682539682539683,1.0,0.6031746031746031,1.0,27,2466.5714285714284,204.84666666666666,0,14.428571428571429,486554.4603174603 +Harbor OpenCode-only,100,codex,0,174,0.0,16.028735632183906,17.339080459770116,17.0,21.0,1.528735632183908,0.09085112631089642,0.5229885057471264,0.0,0.8735632183908046,0.9482758620689655,0.3333333333333333,0.7816091954022989,0.2982456140350877,57,3815.8333333333335,325.60994252873564,1,1.0804597701149425,18601.24712643678 +Harbor OpenCode-only,100,codex,1,76,1.0,14.276315789473685,15.06578947368421,17.0,19.0,4.868421052631579,0.2914163040013415,0.8947368421052632,0.0,0.618421052631579,0.9868421052631579,1.0,0.5263157894736842,1.0,29,2114.3815789473683,194.9973684210526,1,1.0657894736842106,14216.197368421053 +Harbor OpenCode-only,100,mini-swe-agent,0,199,0.0,16.08542713567839,16.0,17.0,17.0,0.7437185929648241,0.04511599675655248,0.3969849246231156,0.0,0.8442211055276382,0.9698492462311558,0.23618090452261306,0.7236180904522613,0.208955223880597,67,2407.542713567839,232.3070854271357,8,1.0954773869346734,13758.788944723618 +Harbor OpenCode-only,100,mini-swe-agent,1,51,1.0,11.529411764705882,11.509803921568627,10.0,17.0,1.1372549019607843,0.08078258406978131,0.43137254901960786,0.0,0.2549019607843137,1.0,1.0,0.3333333333333333,1.0,19,1096.235294117647,134.96882352941176,3,1.0588235294117647,6834.176470588235 +Harbor OpenCode-only,100,opencode,0,195,0.0,16.907692307692308,16.984615384615385,17.0,18.0,3.082051282051282,0.1810856795996115,0.7435897435897436,0.0,0.9897435897435898,0.9948717948717949,0.48205128205128206,0.8,0.43548387096774194,62,3563.723076923077,307.79835897435896,1,2.471794871794872,79324.01025641026 +Harbor OpenCode-only,100,opencode,1,55,1.0,17.0,16.963636363636365,17.0,17.0,6.054545454545455,0.35768012946805516,0.9818181818181818,0.0,1.0,1.0,1.0,0.5272727272727272,1.0,24,1531.0,186.55,1,2.2363636363636363,51286.61818181818 +Harbor OpenCode-only,200,claude-code,0,179,0.0,15.843575418994414,16.54189944134078,17.0,19.0,1.223463687150838,0.07660509892269075,0.45251396648044695,0.00558659217877095,0.8435754189944135,0.9050279329608939,0.3128491620111732,0.7932960893854749,0.3392857142857143,56,4604.905027932961,313.4882122905028,0,15.776536312849162,641328.3743016759 +Harbor OpenCode-only,200,claude-code,1,71,1.0,14.873239436619718,14.95774647887324,17.0,18.0,3.3661971830985915,0.20944526997540122,0.7605633802816901,0.0,0.6056338028169014,0.971830985915493,1.0,0.6338028169014085,1.0,30,3193.9154929577467,231.5656338028169,1,14.873239436619718,524254.04225352115 +Harbor OpenCode-only,200,codex,0,173,0.0,16.67630057803468,18.36416184971098,18.0,21.0,1.4046242774566473,0.07753095167875124,0.4682080924855491,0.0,0.9248554913294798,0.9421965317919075,0.2658959537572254,0.7803468208092486,0.25,56,4099.884393063584,316.54907514450866,0,1.0578034682080926,20914.49710982659 +Harbor OpenCode-only,200,codex,1,77,1.0,14.233766233766234,14.805194805194805,17.0,18.400000000000006,3.6363636363636362,0.22335010469986946,0.8571428571428571,0.0,0.5584415584415584,0.974025974025974,1.0,0.44155844155844154,1.0,30,2143.116883116883,188.35792207792207,0,1.0129870129870129,13293.25974025974 +Harbor OpenCode-only,200,mini-swe-agent,0,200,0.0,16.525,16.47,17.0,17.0,0.67,0.040635387488328664,0.37,0.0,0.885,0.96,0.205,0.72,0.24615384615384617,65,2534.265,224.93630000000002,5,1.065,16091.62 +Harbor OpenCode-only,200,mini-swe-agent,1,50,1.0,13.1,12.96,13.0,17.0,1.08,0.07548262848262849,0.54,0.0,0.38,1.0,1.0,0.3,1.0,21,1436.68,153.934,1,1.14,9409.36 +Harbor OpenCode-only,200,opencode,0,177,0.0,16.819209039548024,17.43502824858757,17.0,19.0,1.5819209039548023,0.0906328115726237,0.615819209039548,0.0,0.9774011299435028,0.9717514124293786,0.3389830508474576,0.8192090395480226,0.3090909090909091,55,3841.7909604519773,338.96146892655366,1,2.7288135593220337,89303.62711864407 +Harbor OpenCode-only,200,opencode,1,73,1.0,16.931506849315067,17.28767123287671,17.0,18.0,3.4246575342465753,0.19972833188883488,0.9452054794520548,0.0,0.958904109589041,0.9863013698630136,1.0,0.7123287671232876,1.0,31,3505.2328767123286,307.3135616438356,0,2.9452054794520546,87494.19178082192 +Harbor OpenCode-only,300,claude-code,0,174,0.0,15.879310344827585,17.166666666666668,17.0,20.0,0.8735632183908046,0.05466958958801767,0.3505747126436782,0.017241379310344827,0.8735632183908046,0.9367816091954023,0.1839080459770115,0.8160919540229885,0.17307692307692307,52,4942.620689655172,354.0668390804598,0,15.862068965517242,642441.3448275862 +Harbor OpenCode-only,300,claude-code,1,76,1.0,15.56578947368421,15.697368421052632,17.0,18.0,3.5,0.21566024229785025,0.8552631578947368,0.0,0.6842105263157895,0.9736842105263158,1.0,0.618421052631579,1.0,34,3338.1973684210525,266.93618421052633,0,15.56578947368421,627374.9736842106 +Harbor OpenCode-only,300,codex,0,170,0.0,16.58823529411765,18.641176470588235,18.0,21.0,1.3058823529411765,0.07170575137739442,0.5,0.0,0.9176470588235294,0.9705882352941176,0.2647058823529412,0.8117647058823529,0.20754716981132076,53,4100.770588235294,347.1957058823529,2,1.0823529411764705,20818.035294117646 +Harbor OpenCode-only,300,codex,1,80,1.0,15.6125,16.725,18.0,19.10000000000001,3.8375,0.2269799343892635,0.9,0.0,0.7625,0.9875,1.0,0.55,1.0,33,2855.5,258.10875,0,1.0875,15337.0875 +Harbor OpenCode-only,300,mini-swe-agent,0,191,0.0,16.79581151832461,16.774869109947645,17.0,17.0,0.5287958115183246,0.031608664408171644,0.36649214659685864,0.0,0.9581151832460733,0.9842931937172775,0.12041884816753927,0.7225130890052356,0.1111111111111111,63,2663.8743455497383,258.0570680628272,0,1.0628272251308901,15376.921465968586 +Harbor OpenCode-only,300,mini-swe-agent,1,59,1.0,14.305084745762711,14.186440677966102,15.0,17.0,1.0508474576271187,0.06889185922087218,0.576271186440678,0.0,0.3898305084745763,0.9830508474576272,1.0,0.3728813559322034,1.0,23,1791.2203389830509,199.445593220339,1,1.11864406779661,11134.694915254237 +Harbor OpenCode-only,300,opencode,0,180,0.0,16.383333333333333,20.872222222222224,19.0,25.0,1.4111111111111112,0.07390547982431366,0.5944444444444444,0.0,0.9333333333333333,0.9722222222222222,0.2,0.7555555555555555,0.12280701754385964,57,3246.2555555555555,306.0449444444444,1,2.6277777777777778,91514.25 +Harbor OpenCode-only,300,opencode,1,70,1.0,16.985714285714284,21.614285714285714,19.0,21.0,3.857142857142857,0.1977509219831057,0.9428571428571428,0.0,0.9857142857142858,1.0,1.0,0.45714285714285713,1.0,29,2058.8142857142857,228.83585714285715,0,2.7285714285714286,81825.54285714286 +Harbor OpenCode-only,400,claude-code,0,166,0.0,16.066265060240966,17.746987951807228,17.0,21.0,1.2650602409638554,0.07470659022909078,0.4819277108433735,0.012048192771084338,0.8855421686746988,0.9457831325301205,0.26506024096385544,0.8072289156626506,0.2545454545454545,55,4236.331325301205,320.7774096385542,1,16.06024096385542,647455.2710843374 +Harbor OpenCode-only,400,claude-code,1,84,1.0,15.738095238095237,16.25,17.0,18.700000000000003,4.119047619047619,0.2448144456838066,0.9166666666666666,0.0,0.7738095238095238,0.9880952380952381,1.0,0.5833333333333334,1.0,31,2658.0238095238096,224.13857142857142,0,15.738095238095237,555745.4642857143 +Harbor OpenCode-only,400,codex,0,167,0.0,16.538922155688624,19.904191616766468,19.0,27.400000000000006,1.4610778443113772,0.07713188883852998,0.5568862275449101,0.0,0.8982035928143712,0.9760479041916168,0.25149700598802394,0.7485029940119761,0.24074074074074073,54,3711.1616766467064,333.80592814371255,1,1.0718562874251496,20052.305389221558 +Harbor OpenCode-only,400,codex,1,83,1.0,15.493975903614459,16.734939759036145,18.0,19.799999999999997,4.602409638554217,0.26760916490764675,0.9036144578313253,0.0,0.7228915662650602,0.9879518072289156,1.0,0.4819277108433735,1.0,32,2288.012048192771,232.95409638554216,0,1.072289156626506,14283.638554216868 +Harbor OpenCode-only,400,mini-swe-agent,0,178,0.0,16.3876404494382,16.382022471910112,17.0,17.0,0.47191011235955055,0.02903264301975472,0.3595505617977528,0.0,0.898876404494382,0.9887640449438202,0.19662921348314608,0.6741573033707865,0.18867924528301888,53,2280.5955056179773,247.88219101123596,2,1.146067415730337,15718.421348314607 +Harbor OpenCode-only,400,mini-swe-agent,1,72,1.0,13.777777777777779,13.777777777777779,16.0,17.0,1.0555555555555556,0.06929596365870877,0.5138888888888888,0.0,0.4722222222222222,0.9861111111111112,1.0,0.4166666666666667,1.0,33,1478.0,192.63111111111112,0,1.0555555555555556,8469.972222222223 +Harbor OpenCode-only,400,opencode,0,161,0.0,16.788819875776397,22.124223602484474,19.0,27.0,1.813664596273292,0.06485506419447218,0.515527950310559,0.0,0.9751552795031055,1.0,0.2670807453416149,0.8571428571428571,0.22448979591836735,49,3299.44099378882,331.341552795031,1,2.8260869565217392,97048.1552795031 +Harbor OpenCode-only,400,opencode,1,89,1.0,17.0,18.808988764044944,18.0,20.0,4.404494382022472,0.2348597243674991,0.9887640449438202,0.0,1.0,1.0,1.0,0.3707865168539326,1.0,37,1655.0449438202247,197.67393258426966,0,2.797752808988764,80084.84269662922 +Harbor OpenCode-only,500,claude-code,0,169,0.0,16.11242603550296,16.928994082840237,17.0,19.0,0.8284023668639053,0.0499699922790218,0.378698224852071,0.011834319526627219,0.8757396449704142,0.9644970414201184,0.25443786982248523,0.8579881656804734,0.25925925925925924,54,5185.940828402367,362.2280473372781,0,16.100591715976332,669836.5739644971 +Harbor OpenCode-only,500,claude-code,1,81,1.0,14.432098765432098,14.160493827160494,16.0,17.0,3.1604938271604937,0.2152625958195847,0.9259259259259259,0.0,0.5308641975308642,1.0,1.0,0.6172839506172839,1.0,32,3027.5061728395062,233.48000000000002,2,14.432098765432098,477063.5802469136 +Harbor OpenCode-only,500,codex,0,157,0.0,16.401273885350317,18.133757961783438,18.0,21.400000000000006,1.1656050955414012,0.06638657863160223,0.4585987261146497,0.0,0.9044585987261147,0.9936305732484076,0.24203821656050956,0.8089171974522293,0.24489795918367346,49,4227.649681528663,359.2949681528662,0,1.1401273885350318,21509.375796178345 +Harbor OpenCode-only,500,codex,1,93,1.0,14.698924731182796,15.526881720430108,17.0,19.0,3.795698924731183,0.2327852632182004,0.8924731182795699,0.0,0.6344086021505376,1.0,1.0,0.44086021505376344,1.0,37,2390.569892473118,230.48698924731184,2,1.118279569892473,15590.408602150537 +Harbor OpenCode-only,500,mini-swe-agent,0,176,0.0,16.619318181818183,16.613636363636363,17.0,17.0,0.35795454545454547,0.021529206088029617,0.29545454545454547,0.0,0.9147727272727273,0.9886363636363636,0.14204545454545456,0.7045454545454546,0.12962962962962962,54,2710.3125,259.2145454545455,0,1.1193181818181819,15618.602272727272 +Harbor OpenCode-only,500,mini-swe-agent,1,74,1.0,13.554054054054054,13.486486486486486,14.0,17.0,0.7162162162162162,0.05137244181361828,0.44594594594594594,0.0,0.35135135135135137,1.0,1.0,0.4594594594594595,1.0,32,1930.945945945946,205.82716216216218,1,1.135135135135135,10034.378378378378 +Harbor OpenCode-only,500,opencode,0,168,0.0,16.613095238095237,18.00595238095238,18.0,20.0,0.9702380952380952,0.05309178575358532,0.3630952380952381,0.0,0.9583333333333334,1.0,0.34523809523809523,0.7797619047619048,0.23636363636363636,55,3617.5476190476193,328.1172619047619,0,2.6488095238095237,91067.48809523809 +Harbor OpenCode-only,500,opencode,1,82,1.0,17.0,17.902439024390244,17.0,18.900000000000006,4.341463414634147,0.2453191811871869,0.9878048780487805,0.0,1.0,1.0,1.0,0.4024390243902439,1.0,31,2132.5,219.74402439024388,0,2.7804878048780486,75945.76829268293 +Harbor OpenCode-only,600,claude-code,0,172,0.0,15.965116279069768,16.906976744186046,17.0,20.0,1.3604651162790697,0.08308600729175007,0.5116279069767442,0.0,0.8197674418604651,0.9244186046511628,0.4069767441860465,0.7558139534883721,0.39622641509433965,53,4603.308139534884,329.1661046511628,0,15.953488372093023,650861.6569767442 +Harbor OpenCode-only,600,claude-code,1,78,1.0,14.346153846153847,14.41025641025641,17.0,18.0,3.730769230769231,0.23831576605897872,0.8461538461538461,0.0,0.5128205128205128,0.9871794871794872,1.0,0.5,1.0,33,2552.0384615384614,208.44666666666666,0,14.346153846153847,467105.89743589744 +Harbor OpenCode-only,600,codex,0,164,0.0,16.414634146341463,18.664634146341463,18.0,22.0,1.8048780487804879,0.09961361347511631,0.573170731707317,0.0,0.8841463414634146,0.9878048780487805,0.3475609756097561,0.7134146341463414,0.2641509433962264,53,3480.298780487805,326.8166463414634,0,1.2073170731707317,20979.896341463416 +Harbor OpenCode-only,600,codex,1,86,1.0,14.755813953488373,15.593023255813954,17.0,19.0,4.162790697674419,0.25005012372488433,0.872093023255814,0.0,0.6162790697674418,0.9883720930232558,1.0,0.37209302325581395,1.0,33,2126.860465116279,220.38697674418603,0,1.1511627906976745,14420.883720930233 +Harbor OpenCode-only,600,mini-swe-agent,0,173,0.0,16.404624277456648,16.404624277456648,17.0,17.0,0.36416184971098264,0.022052389410430893,0.27167630057803466,0.0,0.861271676300578,0.9710982658959537,0.19653179190751446,0.6705202312138728,0.21428571428571427,56,1886.8612716763005,262.41884393063583,0,1.4508670520231215,21945.52023121387 +Harbor OpenCode-only,600,mini-swe-agent,1,77,1.0,12.506493506493506,12.493506493506494,12.0,17.0,0.7792207792207793,0.06087652954133996,0.5584415584415584,0.0,0.22077922077922077,0.987012987012987,1.0,0.4155844155844156,1.0,30,1058.3506493506493,189.78844155844155,0,1.3116883116883118,10079.974025974027 +Harbor OpenCode-only,600,opencode,0,161,0.0,16.70186335403727,19.453416149068325,19.0,22.0,1.6583850931677018,0.08749843522181466,0.515527950310559,0.0,0.9627329192546584,0.9813664596273292,0.35403726708074534,0.8012422360248447,0.3076923076923077,52,2257.472049689441,318.1972049689441,1,3.0683229813664594,124388.36645962733 +Harbor OpenCode-only,600,opencode,1,89,1.0,16.84269662921348,18.202247191011235,18.0,19.0,5.550561797752809,0.3068027400914311,1.0,0.0,0.9550561797752809,0.9887640449438202,1.0,0.38202247191011235,1.0,34,1404.6966292134832,217.18235955056178,0,2.3707865168539324,63050.29213483146 +Harbor OpenCode-only,700,claude-code,0,134,0.0,14.955223880597014,16.32089552238806,17.0,20.700000000000003,1.6940298507462686,0.10036111108362263,0.5597014925373134,0.007462686567164179,0.6865671641791045,0.9776119402985075,0.4701492537313433,0.7910447761194029,0.43902439024390244,41,5475.335820895522,346.01283582089553,0,14.940298507462687,589280.671641791 +Harbor OpenCode-only,700,claude-code,1,116,1.0,12.129310344827585,12.198275862068966,12.0,18.0,2.3879310344827585,0.17583457980343323,0.7241379310344828,0.0,0.25,1.0,1.0,0.5775862068965517,1.0,45,3157.7844827586205,218.82741379310343,0,12.129310344827585,408917.0775862069 +Harbor OpenCode-only,700,codex,0,157,0.0,14.859872611464969,18.910828025477706,19.0,25.400000000000006,1.5923566878980893,0.08045271972899054,0.5668789808917197,0.0,0.6624203821656051,0.9808917197452229,0.42038216560509556,0.7133757961783439,0.46938775510204084,49,3415.5732484076434,328.84222929936305,1,1.6305732484076434,26271.261146496814 +Harbor OpenCode-only,700,codex,1,93,1.0,11.010752688172044,12.53763440860215,12.0,19.0,2.075268817204301,0.14050325778161113,0.6666666666666666,0.0,0.13978494623655913,0.989247311827957,1.0,0.40860215053763443,1.0,37,1504.4408602150538,171.7011827956989,0,1.2365591397849462,13730.827956989247 +Harbor OpenCode-only,700,mini-swe-agent,0,164,0.0,16.5,16.682926829268293,17.0,17.0,0.4573170731707317,0.026926464246577145,0.3170731707317073,0.0,0.8719512195121951,0.9878048780487805,0.27439024390243905,0.7682926829268293,0.3137254901960784,51,2083.548780487805,280.42603658536586,2,2.9207317073170733,37507.32317073171 +Harbor OpenCode-only,700,mini-swe-agent,1,86,1.0,12.906976744186046,12.872093023255815,13.0,17.0,0.7325581395348837,0.055088845203324285,0.47674418604651164,0.0,0.3023255813953488,1.0,1.0,0.36046511627906974,1.0,35,1219.7906976744187,190.23116279069765,0,1.7790697674418605,13905.96511627907 +Harbor OpenCode-only,700,opencode,0,150,0.0,16.053333333333335,21.273333333333333,20.0,27.0,3.4066666666666667,0.1529009440988349,0.8333333333333334,0.0,0.7866666666666666,0.98,0.4866666666666667,0.7866666666666666,0.5208333333333334,48,3196.12,342.27186666666665,0,5.54,238433.54 +Harbor OpenCode-only,700,opencode,1,100,1.0,14.59,18.49,17.0,24.0,4.96,0.28931035334167915,1.0,0.0,0.39,0.97,1.0,0.47,1.0,38,2071.08,233.6001,1,3.72,118031.58 +Harbor OpenCode-only,800,claude-code,0,153,0.0,14.18954248366013,14.30718954248366,17.0,18.0,0.9281045751633987,0.060710275289534106,0.46405228758169936,0.0,0.5620915032679739,0.8954248366013072,0.46405228758169936,0.7320261437908496,0.5686274509803921,51,5360.098039215686,321.1671895424837,3,14.169934640522875,530368.8692810457 +Harbor OpenCode-only,800,claude-code,1,97,1.0,10.484536082474227,9.876288659793815,9.0,16.400000000000006,1.0515463917525774,0.08964026231907883,0.41237113402061853,0.0,0.1134020618556701,0.9587628865979382,1.0,0.5154639175257731,1.0,35,3721.453608247423,237.73979381443297,1,10.484536082474227,387168.4639175258 +Harbor OpenCode-only,800,codex,0,161,0.0,14.503105590062113,15.322981366459627,17.0,19.0,1.0683229813664596,0.06989377183399643,0.5403726708074534,0.006211180124223602,0.5652173913043478,0.9503105590062112,0.35403726708074534,0.7267080745341615,0.4230769230769231,52,2914.1801242236024,298.87801242236026,2,1.9565217391304348,30247.006211180124 +Harbor OpenCode-only,800,codex,1,89,1.0,12.101123595505618,12.146067415730338,12.0,17.0,1.3595505617977528,0.09805768759790048,0.5842696629213483,0.0,0.19101123595505617,0.9775280898876404,1.0,0.42696629213483145,1.0,34,1768.7078651685392,201.70280898876405,2,1.1797752808988764,14081.573033707866 +Harbor OpenCode-only,800,mini-swe-agent,0,171,0.0,16.608187134502923,16.4093567251462,17.0,17.0,0.6257309941520468,0.03928024446600298,0.3684210526315789,0.0,0.9064327485380117,0.9766081871345029,0.2807017543859649,0.7368421052631579,0.2962962962962963,54,2913.12865497076,264.1375438596491,1,5.514619883040936,63107.6432748538 +Harbor OpenCode-only,800,mini-swe-agent,1,79,1.0,14.443037974683545,13.215189873417721,13.0,17.0,1.0506329113924051,0.07818177726964026,0.5822784810126582,0.0,0.379746835443038,1.0,1.0,0.27848101265822783,1.0,32,1223.0759493670887,187.66430379746834,2,2.3797468354430378,16477.354430379746 +Harbor OpenCode-only,800,opencode,0,184,0.0,13.434782608695652,14.065217391304348,17.0,18.0,1.125,0.09618213972391856,0.6793478260869565,0.05434782608695652,0.6086956521739131,0.7934782608695652,0.3695652173913043,0.6141304347826086,0.3114754098360656,61,4090.092391304348,322.4482065217391,7,6.559782608695652,274310.0706521739 +Harbor OpenCode-only,800,opencode,1,66,1.0,12.681818181818182,12.681818181818182,12.0,17.0,2.1363636363636362,0.1705536936820359,1.0,0.0,0.16666666666666666,0.9545454545454546,1.0,0.36363636363636365,1.0,25,1674.1818181818182,221.4387878787879,2,3.409090909090909,102458.86363636363 +Harbor OpenCode-only,900,claude-code,0,162,0.0,15.024691358024691,16.179012345679013,17.0,20.0,1.1049382716049383,0.06768826268428678,0.5185185185185185,0.006172839506172839,0.7345679012345679,0.9135802469135802,0.25308641975308643,0.8271604938271605,0.24,50,5573.407407407408,365.2506790123457,0,15.006172839506172,562142.2160493827 +Harbor OpenCode-only,900,claude-code,1,88,1.0,11.534090909090908,11.147727272727273,11.0,17.0,1.7272727272727273,0.1376910941460266,0.6590909090909091,0.0,0.11363636363636363,0.9545454545454546,1.0,0.45454545454545453,1.0,36,3369.9545454545455,247.765,0,11.534090909090908,413111.1022727273 +Harbor OpenCode-only,900,codex,0,182,0.0,14.95054945054945,17.516483516483518,18.0,23.0,1.5384615384615385,0.08547364529253798,0.6208791208791209,0.0,0.6428571428571429,0.9230769230769231,0.2857142857142857,0.6758241758241759,0.26229508196721313,61,3011.0604395604396,326.71725274725276,0,2.291208791208791,37625.153846153844 +Harbor OpenCode-only,900,codex,1,68,1.0,12.161764705882353,13.161764705882353,13.0,17.300000000000004,1.5147058823529411,0.10776092501814138,0.6470588235294118,0.0,0.10294117647058823,0.9852941176470589,1.0,0.5147058823529411,1.0,25,1981.5,242.2825,0,1.2794117647058822,16128.985294117647 +Harbor OpenCode-only,900,mini-swe-agent,0,185,0.0,16.935135135135134,16.72972972972973,17.0,17.0,0.8216216216216217,0.05056493321199204,0.4702702702702703,0.0,0.9783783783783784,0.9783783783783784,0.15135135135135136,0.7351351351351352,0.18032786885245902,61,2656.8,276.98718918918917,2,4.762162162162162,71131.2054054054 +Harbor OpenCode-only,900,mini-swe-agent,1,65,1.0,16.23076923076923,14.36923076923077,15.0,17.0,1.5692307692307692,0.11439487360301841,0.8153846153846154,0.0,0.7846153846153846,1.0,1.0,0.47692307692307695,1.0,25,1252.923076923077,225.94923076923075,0,2.769230769230769,29392.076923076922 +Harbor OpenCode-only,900,opencode,0,175,0.0,13.725714285714286,18.914285714285715,20.0,26.0,3.222857142857143,0.2060761657218522,0.9485714285714286,0.0,0.6342857142857142,0.8457142857142858,0.7542857142857143,0.6457142857142857,0.07142857142857142,56,3102.0057142857145,292.66497142857145,1,5.228571428571429,276230.77714285714 +Harbor OpenCode-only,900,opencode,1,75,1.0,13.866666666666667,17.2,18.0,21.0,4.72,0.28722407543150574,1.0,0.0,0.21333333333333335,0.88,1.0,0.49333333333333335,1.0,30,1837.0133333333333,257.4232,0,2.5733333333333333,90841.49333333333 +Harbor OpenCode-only,1000,claude-code,0,173,0.0,15.398843930635838,21.14450867052023,21.0,29.0,2.959537572254335,0.1300178697767679,0.7456647398843931,0.0,0.7687861271676301,0.8901734104046243,0.3583815028901734,0.815028901734104,0.21818181818181817,55,5566.254335260116,360.69156069364163,0,15.393063583815028,603630.0173410404 +Harbor OpenCode-only,1000,claude-code,1,77,1.0,11.779220779220779,13.818181818181818,14.0,20.0,1.6753246753246753,0.10423260463154578,0.6233766233766234,0.0,0.18181818181818182,0.948051948051948,1.0,0.5974025974025974,1.0,31,3435.3636363636365,248.8414285714286,0,11.779220779220779,418759.83116883115 +Harbor OpenCode-only,1000,codex,0,169,0.0,15.0,22.159763313609467,21.0,33.20000000000002,3.2071005917159763,0.1257243878254321,0.7928994082840237,0.0,0.6627218934911243,0.9585798816568047,0.1834319526627219,0.7100591715976331,0.20754716981132076,53,3303.2544378698226,331.3844970414201,0,2.42603550295858,41973.36686390533 +Harbor OpenCode-only,1000,codex,1,81,1.0,12.419753086419753,16.703703703703702,17.0,24.0,2.6419753086419755,0.13168325582668605,0.7530864197530864,0.0,0.24691358024691357,0.9259259259259259,1.0,0.6172839506172839,1.0,33,2142.456790123457,246.69432098765435,0,1.5308641975308641,23130.345679012345 +Harbor OpenCode-only,1000,mini-swe-agent,0,199,0.0,16.949748743718594,19.246231155778894,17.0,25.0,1.849246231155779,0.0862166297164493,0.6582914572864321,0.0,0.9849246231155779,0.9547738693467337,0.10552763819095477,0.7236180904522613,0.08695652173913043,69,2559.618090452261,246.21407035175878,0,2.030150753768844,39199.25125628141 +Harbor OpenCode-only,1000,mini-swe-agent,1,51,1.0,16.470588235294116,17.0,16.0,22.0,2.0,0.11829921708417217,0.8235294117647058,0.0,0.8431372549019608,0.9411764705882353,1.0,0.45098039215686275,1.0,17,2065.1176470588234,244.17235294117648,0,2.627450980392157,37254.705882352944 +Harbor OpenCode-only,1000,opencode,0,195,0.0,16.148717948717948,26.92820512820513,27.0,35.599999999999994,4.569230769230769,0.15155612586225803,0.8512820512820513,0.0,0.8564102564102564,0.9743589743589743,0.9435897435897436,0.8,0.0967741935483871,62,3554.9128205128204,314.7382051282051,2,3.0358974358974358,116502.93846153846 +Harbor OpenCode-only,1000,opencode,1,55,1.0,14.49090909090909,21.78181818181818,22.0,28.0,4.8545454545454545,0.2076344353123434,1.0,0.0,0.3090909090909091,0.9636363636363636,1.0,0.5818181818181818,1.0,24,2474.909090909091,239.53581818181817,0,2.690909090909091,81047.5090909091 +Harbor multi-harness,0,claude-code,0,208,0.0,14.177884615384615,13.759615384615385,16.0,18.0,0.6009615384615384,0.03858411235582612,0.24519230769230768,0.028846153846153848,0.6105769230769231,0.7548076923076923,0.33653846153846156,0.7163461538461539,0.35294117647058826,68,3907.5673076923076,330.2196153846154,4,13.774038461538462,551784.2644230769 +Harbor multi-harness,0,claude-code,1,42,1.0,8.642857142857142,7.428571428571429,7.0,13.799999999999997,0.30952380952380953,0.032916666666666664,0.16666666666666666,0.047619047619047616,0.07142857142857142,0.8333333333333334,0.9523809523809523,0.40476190476190477,1.0,18,1545.7142857142858,190.2461904761905,0,8.571428571428571,276973.14285714284 +Harbor multi-harness,0,codex,0,209,0.0,15.167464114832535,15.014354066985646,17.0,20.0,0.5933014354066986,0.03703165718455073,0.3492822966507177,0.07655502392344497,0.7129186602870813,0.7416267942583732,0.2727272727272727,0.6172248803827751,0.23529411764705882,68,3264.622009569378,302.87047846889953,0,1.1483253588516746,20704.593301435405 +Harbor multi-harness,0,codex,1,41,1.0,11.463414634146341,10.317073170731707,9.0,17.0,0.9024390243902439,0.06859963779468424,0.2926829268292683,0.0975609756097561,0.21951219512195122,0.8292682926829268,0.9024390243902439,0.36585365853658536,1.0,18,1723.5853658536585,212.49975609756098,0,1.0,12842.731707317073 +Harbor multi-harness,0,mini-swe-agent,0,214,0.0,16.490654205607477,15.682242990654206,17.0,17.0,0.37850467289719625,0.024792598653963587,0.26635514018691586,0.037383177570093455,0.8878504672897196,0.8317757009345794,0.2102803738317757,0.677570093457944,0.20833333333333334,72,2659.411214953271,232.60500000000002,4,1.1682242990654206,17348.168224299065 +Harbor multi-harness,0,mini-swe-agent,1,36,1.0,13.277777777777779,12.0,12.0,16.0,0.5555555555555556,0.040360410927019925,0.3333333333333333,0.05555555555555555,0.3333333333333333,0.9166666666666666,0.9444444444444444,0.3888888888888889,1.0,14,1411.1666666666667,198.5597222222222,0,1.7222222222222223,15965.027777777777 +Harbor multi-harness,0,opencode,0,223,0.0,13.179372197309418,11.448430493273543,14.0,18.0,0.5246636771300448,0.038140999359143606,0.3094170403587444,0.10762331838565023,0.4977578475336323,0.7488789237668162,0.33183856502242154,0.6322869955156951,0.3291139240506329,79,2014.8161434977578,275.3364125560538,3,1.1973094170403586,31591.430493273543 +Harbor multi-harness,0,opencode,1,27,1.0,9.296296296296296,6.962962962962963,7.0,12.400000000000002,0.18518518518518517,0.022289986228605157,0.14814814814814814,0.14814814814814814,0.037037037037037035,0.7407407407407407,0.8518518518518519,0.2962962962962963,1.0,7,818.4814814814815,255.86296296296297,0,1.3703703703703705,33369.59259259259 +Harbor multi-harness,100,claude-code,0,181,0.0,16.1878453038674,15.773480662983426,17.0,17.0,2.0939226519337018,0.129624268008588,0.5469613259668509,0.011049723756906077,0.8729281767955801,0.7348066298342542,0.3149171270718232,0.6795580110497238,0.2545454545454545,55,4083.745856353591,346.4222099447514,8,15.502762430939226,648460.8342541436 +Harbor multi-harness,100,claude-code,1,69,1.0,16.44927536231884,15.898550724637682,17.0,17.0,5.3768115942028984,0.3334485280726363,0.927536231884058,0.0,0.8840579710144928,0.8985507246376812,1.0,0.5072463768115942,1.0,31,3166.7971014492755,289.0965217391304,5,15.81159420289855,620988.5072463768 +Harbor multi-harness,100,codex,0,180,0.0,16.75,16.511111111111113,17.0,18.0,1.0444444444444445,0.06217952412224858,0.42777777777777776,0.0,0.9166666666666666,0.8722222222222222,0.15,0.75,0.16363636363636364,55,3696.4,353.48133333333334,0,1.0888888888888888,19165.077777777777 +Harbor multi-harness,100,codex,1,70,1.0,16.285714285714285,16.314285714285713,17.0,18.0,3.9285714285714284,0.23561877775012116,0.9,0.0,0.8714285714285714,0.9571428571428572,1.0,0.5285714285714286,1.0,31,2457.1285714285714,234.46214285714282,0,1.1285714285714286,14368.4 +Harbor multi-harness,100,mini-swe-agent,0,202,0.0,16.594059405940595,16.524752475247524,17.0,17.0,0.9554455445544554,0.05683738016658809,0.46534653465346537,0.0,0.905940594059406,0.9207920792079208,0.16336633663366337,0.6831683168316832,0.13636363636363635,66,2094.9752475247524,222.38960396039602,8,1.0792079207920793,15573.10891089109 +Harbor multi-harness,100,mini-swe-agent,1,48,1.0,12.770833333333334,12.770833333333334,12.0,17.0,1.1041666666666667,0.07575306297732769,0.5625,0.0,0.25,0.9791666666666666,1.0,0.2916666666666667,1.0,20,1214.5416666666667,166.41479166666667,1,1.0,8154.375 +Harbor multi-harness,100,opencode,0,189,0.0,16.523809523809526,15.587301587301587,16.0,16.0,3.798941798941799,0.2390842401610744,0.6349206349206349,0.010582010582010581,0.9153439153439153,0.8624338624338624,0.4126984126984127,0.6878306878306878,0.3709677419354839,62,2586.772486772487,266.1631216931217,19,2.3015873015873014,68499.28042328042 +Harbor multi-harness,100,opencode,1,61,1.0,16.934426229508198,15.98360655737705,16.0,16.0,9.704918032786885,0.605418591077501,0.9836065573770492,0.0,0.9836065573770492,0.8688524590163934,1.0,0.45901639344262296,1.0,24,1481.5245901639344,194.35557377049182,3,2.2950819672131146,59156.55737704918 +Harbor multi-harness,200,claude-code,0,175,0.0,16.46857142857143,15.862857142857143,17.0,17.0,2.2857142857142856,0.14150523287838443,0.5771428571428572,0.005714285714285714,0.8857142857142857,0.8742857142857143,0.26857142857142857,0.7371428571428571,0.2542372881355932,59,4198.674285714285,354.312,32,15.645714285714286,645421.8685714286 +Harbor multi-harness,200,claude-code,1,75,1.0,16.96,16.573333333333334,17.0,17.0,6.72,0.40244544997486176,0.92,0.0,0.96,0.9333333333333333,1.0,0.52,1.0,27,3256.3733333333334,281.4148,12,16.493333333333332,581790.8666666667 +Harbor multi-harness,200,codex,0,184,0.0,16.782608695652176,16.434782608695652,17.0,17.0,1.125,0.06736811858610721,0.4782608695652174,0.005434782608695652,0.9130434782608695,0.9184782608695652,0.23369565217391305,0.7880434782608695,0.21818181818181817,55,3704.461956521739,342.4726086956522,0,1.0108695652173914,18122.902173913044 +Harbor multi-harness,200,codex,1,66,1.0,16.954545454545453,16.863636363636363,17.0,17.5,4.848484848484849,0.2852143635231871,0.8787878787878788,0.0,0.9696969696969697,0.9848484848484849,1.0,0.5303030303030303,1.0,31,2319.8939393939395,230.32651515151514,0,1.0303030303030303,13138.469696969696 +Harbor multi-harness,200,mini-swe-agent,0,204,0.0,16.705882352941178,16.666666666666668,17.0,17.0,1.1715686274509804,0.07006045065084858,0.553921568627451,0.0,0.9313725490196079,0.8480392156862745,0.12745098039215685,0.6127450980392157,0.10606060606060606,66,1686.9117647058824,192.04999999999998,18,1.0735294117647058,14190.122549019608 +Harbor multi-harness,200,mini-swe-agent,1,46,1.0,13.826086956521738,13.826086956521738,16.0,17.0,1.673913043478261,0.10568712155988372,0.5434782608695652,0.0,0.4782608695652174,0.9347826086956522,1.0,0.45652173913043476,1.0,20,1125.9565217391305,155.02695652173912,3,1.0434782608695652,8373.065217391304 +Harbor multi-harness,200,opencode,0,174,0.0,16.67241379310345,15.672413793103448,16.0,16.0,3.6494252873563218,0.2275682268269276,0.632183908045977,0.005747126436781609,0.9597701149425287,0.9022988505747126,0.41379310344827586,0.6954022988505747,0.43137254901960786,51,2429.132183908046,261.697816091954,9,2.218390804597701,65169.862068965514 +Harbor multi-harness,200,opencode,1,76,1.0,17.0,16.039473684210527,16.0,16.0,8.881578947368421,0.5523832559339525,0.9473684210526315,0.0,1.0,0.9473684210526315,1.0,0.47368421052631576,1.0,35,1470.3552631578948,190.74039473684212,2,2.1184210526315788,48577.81578947369 +Harbor multi-harness,300,claude-code,0,167,0.0,15.988023952095809,15.676646706586826,17.0,17.400000000000006,2.3473053892215567,0.1516722823368047,0.6646706586826348,0.017964071856287425,0.844311377245509,0.8083832335329342,0.25748502994011974,0.7005988023952096,0.28846153846153844,52,3633.1437125748503,333.1297005988024,13,15.41317365269461,643764.1736526946 +Harbor multi-harness,300,claude-code,1,83,1.0,16.746987951807228,16.457831325301203,17.0,17.0,7.771084337349397,0.4685708039292656,0.9759036144578314,0.0,0.9397590361445783,0.9156626506024096,1.0,0.5542168674698795,1.0,34,2523.9156626506024,261.3520481927711,4,16.253012048192772,549294.4337349398 +Harbor multi-harness,300,codex,0,176,0.0,16.568181818181817,16.136363636363637,17.0,17.0,1.2443181818181819,0.07808195174923116,0.48295454545454547,0.0,0.9034090909090909,0.9318181818181818,0.25,0.8181818181818182,0.22033898305084745,59,3144.2954545454545,331.7754545454546,0,1.0284090909090908,18341.397727272728 +Harbor multi-harness,300,codex,1,74,1.0,15.594594594594595,15.297297297297296,17.0,17.0,5.472972972972973,0.3397225716111165,0.8918918918918919,0.0,0.7432432432432432,0.9324324324324325,1.0,0.47297297297297297,1.0,27,1847.7567567567567,205.8562162162162,0,1.0135135135135136,11819.445945945947 +Harbor multi-harness,300,mini-swe-agent,0,195,0.0,16.77948717948718,16.74871794871795,17.0,17.0,2.482051282051282,0.14659411101718794,0.7333333333333333,0.0,0.9487179487179487,0.764102564102564,0.17435897435897435,0.5384615384615384,0.12307692307692308,65,1517.7846153846153,191.68061538461538,4,1.041025641025641,11854.758974358974 +Harbor multi-harness,300,mini-swe-agent,1,55,1.0,14.763636363636364,14.763636363636364,17.0,17.0,3.2545454545454544,0.196820205937853,0.6545454545454545,0.0,0.6,0.8909090909090909,1.0,0.4,1.0,21,1023.5272727272727,151.77090909090907,0,1.0,6531.236363636363 +Harbor multi-harness,300,opencode,0,176,0.0,16.886363636363637,15.994318181818182,16.0,17.0,4.375,0.2719334893048128,0.6818181818181818,0.0,0.9715909090909091,0.8125,0.5056818181818182,0.6420454545454546,0.5454545454545454,55,1750.25,220.2347159090909,8,2.164772727272727,62165.60227272727 +Harbor multi-harness,300,opencode,1,74,1.0,16.364864864864863,15.378378378378379,16.0,16.700000000000003,7.45945945945946,0.4785542756130992,0.9594594594594594,0.0,0.8513513513513513,0.8108108108108109,1.0,0.36486486486486486,1.0,31,1148.945945945946,182.39418918918918,0,2.1486486486486487,50040.0 +Harbor multi-harness,400,claude-code,0,158,0.0,16.27848101265823,15.740506329113924,17.0,17.30000000000001,2.1835443037974684,0.13532350824009326,0.6582278481012658,0.012658227848101266,0.8670886075949367,0.759493670886076,0.2088607594936709,0.6772151898734177,0.24,50,4191.721518987341,352.5263291139241,11,15.550632911392405,657314.7468354431 +Harbor multi-harness,400,claude-code,1,92,1.0,16.815217391304348,16.315217391304348,17.0,17.0,6.510869565217392,0.3984565732647574,0.9891304347826086,0.0,0.9130434782608695,0.9565217391304348,1.0,0.5434782608695652,1.0,36,3078.1739130434785,296.3546739130435,1,16.25,624968.9891304348 +Harbor multi-harness,400,codex,0,163,0.0,16.7239263803681,16.380368098159508,17.0,17.0,1.5705521472392638,0.09735117472847533,0.49693251533742333,0.0,0.901840490797546,0.9447852760736196,0.19631901840490798,0.7914110429447853,0.12962962962962962,54,3678.5828220858893,347.2230674846626,0,1.0429447852760736,20032.79754601227 +Harbor multi-harness,400,codex,1,87,1.0,16.839080459770116,16.724137931034484,17.0,17.0,7.505747126436781,0.4469217188791225,0.9195402298850575,0.0,0.9655172413793104,1.0,1.0,0.6436781609195402,1.0,32,2115.1609195402298,217.14908045977012,0,1.0114942528735633,13202.206896551725 +Harbor multi-harness,400,mini-swe-agent,0,178,0.0,16.882022471910112,16.775280898876403,17.0,17.0,1.6910112359550562,0.09991325181758097,0.6853932584269663,0.0,0.9662921348314607,0.8089887640449438,0.15730337078651685,0.6460674157303371,0.2542372881355932,59,2064.4438202247193,223.49376404494382,7,1.1067415730337078,14779.460674157302 +Harbor multi-harness,400,mini-swe-agent,1,72,1.0,15.73611111111111,15.73611111111111,17.0,17.0,5.027777777777778,0.29767763101096434,0.75,0.0,0.8194444444444444,0.9305555555555556,1.0,0.3888888888888889,1.0,27,1342.1944444444443,165.27541666666667,0,1.0,8686.180555555555 +Harbor multi-harness,400,opencode,0,168,0.0,16.464285714285715,15.535714285714286,16.0,16.0,2.4047619047619047,0.15308887443866434,0.5178571428571429,0.0,0.9047619047619048,0.8154761904761905,0.2857142857142857,0.6964285714285714,0.3269230769230769,52,2696.1130952380954,277.2372619047619,18,2.3273809523809526,75044.54166666667 +Harbor multi-harness,400,opencode,1,82,1.0,16.951219512195124,15.987804878048781,16.0,16.0,8.134146341463415,0.5089614504338321,0.9634146341463414,0.0,0.9512195121951219,0.8780487804878049,1.0,0.3902439024390244,1.0,34,1410.4878048780488,185.70902439024388,7,2.1341463414634148,49940.865853658535 +Harbor multi-harness,500,claude-code,0,138,0.0,16.028985507246375,15.543478260869565,16.5,17.0,1.8840579710144927,0.12178888254150738,0.6594202898550725,0.0,0.8260869565217391,0.7681159420289855,0.18115942028985507,0.6811594202898551,0.2,40,4573.398550724638,370.31246376811595,9,15.318840579710145,653862.9492753623 +Harbor multi-harness,500,claude-code,1,112,1.0,16.955357142857142,16.705357142857142,17.0,17.0,7.473214285714286,0.44547463216055655,0.9732142857142857,0.0,0.9642857142857143,0.9553571428571429,1.0,0.5357142857142857,1.0,46,3165.9553571428573,274.3046428571428,7,16.589285714285715,614760.3928571428 +Harbor multi-harness,500,codex,0,152,0.0,16.92105263157895,16.394736842105264,17.0,17.0,1.9210526315789473,0.11469066118350173,0.5526315789473685,0.0,0.9276315789473685,0.9013157894736842,0.19078947368421054,0.8355263157894737,0.24,50,3921.5065789473683,359.5023684210526,0,1.0263157894736843,19961.88157894737 +Harbor multi-harness,500,codex,1,98,1.0,16.76530612244898,16.540816326530614,17.0,17.0,6.316326530612245,0.3783259507599243,0.9693877551020408,0.0,0.9489795918367347,0.9591836734693877,1.0,0.6938775510204082,1.0,36,2528.8571428571427,243.51612244897962,0,1.010204081632653,13861.84693877551 +Harbor multi-harness,500,mini-swe-agent,0,172,0.0,15.918604651162791,15.843023255813954,17.0,17.0,1.0290697674418605,0.06115766073871409,0.436046511627907,0.0,0.8313953488372093,0.8604651162790697,0.20348837209302326,0.6686046511627907,0.16071428571428573,56,2113.6337209302324,224.05284883720927,10,1.1686046511627908,17018.738372093023 +Harbor multi-harness,500,mini-swe-agent,1,78,1.0,11.0,10.987179487179487,11.0,17.0,0.6153846153846154,0.04216183410187935,0.28205128205128205,0.0,0.1282051282051282,0.8717948717948718,1.0,0.4358974358974359,1.0,30,1065.9615384615386,143.7998717948718,2,1.0256410256410255,7264.0641025641025 +Harbor multi-harness,500,opencode,0,168,0.0,16.36904761904762,15.267857142857142,16.0,16.0,3.1547619047619047,0.19931215541772265,0.5535714285714286,0.0,0.9107142857142857,0.7619047619047619,0.3333333333333333,0.6666666666666666,0.2830188679245283,53,2765.0476190476193,287.9326785714286,27,2.3988095238095237,77927.45238095238 +Harbor multi-harness,500,opencode,1,82,1.0,16.9390243902439,15.926829268292684,16.0,16.0,8.548780487804878,0.5360272110720461,1.0,0.0,0.9390243902439024,0.8780487804878049,1.0,0.43902439024390244,1.0,33,1578.7560975609756,205.88426829268292,10,2.231707317073171,57548.243902439026 +Harbor multi-harness,600,claude-code,0,175,0.0,16.18285714285714,15.857142857142858,17.0,18.0,2.8,0.1754122161194253,0.8,0.005714285714285714,0.8342857142857143,0.6742857142857143,0.2057142857142857,0.64,0.2,55,5347.417142857143,381.9341142857143,5,15.548571428571428,665576.6685714286 +Harbor multi-harness,600,claude-code,1,75,1.0,16.88,16.666666666666668,17.0,17.0,6.293333333333333,0.3761555396787595,0.96,0.0,0.96,0.8666666666666667,1.0,0.4533333333333333,1.0,31,3456.133333333333,277.3614666666667,0,16.506666666666668,590298.8266666667 +Harbor multi-harness,600,codex,0,169,0.0,16.70414201183432,15.828402366863905,16.0,17.0,2.106508875739645,0.1292100910347929,0.621301775147929,0.0,0.8224852071005917,0.8994082840236687,0.21893491124260356,0.7514792899408284,0.24074074074074073,54,4656.094674556213,394.3240828402367,0,1.0769230769230769,21019.08875739645 +Harbor multi-harness,600,codex,1,81,1.0,16.950617283950617,16.790123456790123,17.0,17.0,6.419753086419753,0.38044970441484605,0.9753086419753086,0.0,0.9753086419753086,0.9876543209876543,1.0,0.5061728395061729,1.0,32,3006.0864197530864,255.8167901234568,0,1.0246913580246915,14633.382716049382 +Harbor multi-harness,600,mini-swe-agent,0,173,0.0,16.190751445086704,15.947976878612717,17.0,17.0,2.398843930635838,0.1467314769932927,0.791907514450867,0.0,0.8323699421965318,0.7687861271676301,0.18497109826589594,0.49710982658959535,0.2413793103448276,58,2236.3294797687863,216.63260115606937,9,1.2658959537572254,13200.306358381504 +Harbor multi-harness,600,mini-swe-agent,1,77,1.0,11.454545454545455,11.415584415584416,11.0,17.0,0.6623376623376623,0.048452705939336954,0.3116883116883117,0.0,0.15584415584415584,0.8831168831168831,1.0,0.4155844155844156,1.0,28,1229.4935064935064,149.91506493506495,4,1.051948051948052,7048.636363636364 +Harbor multi-harness,600,opencode,0,165,0.0,16.01212121212121,14.587878787878788,16.0,16.0,2.9696969696969697,0.19405374037726977,0.6121212121212121,0.006060606060606061,0.8424242424242424,0.6787878787878788,0.30303030303030304,0.5818181818181818,0.28,50,2968.0363636363636,289.64424242424246,45,2.533333333333333,81446.47878787879 +Harbor multi-harness,600,opencode,1,85,1.0,16.88235294117647,15.83529411764706,16.0,16.0,8.4,0.5319574888779041,0.9764705882352941,0.0,0.8823529411764706,0.8352941176470589,1.0,0.4823529411764706,1.0,36,1910.1176470588234,208.67999999999998,20,2.541176470588235,66069.77647058823 +Harbor multi-harness,684,claude-code,0,161,0.0,15.782608695652174,15.46583850931677,17.0,18.0,3.2732919254658386,0.2125965942216928,0.8385093167701864,0.012422360248447204,0.8074534161490683,0.7080745341614907,0.2546583850931677,0.6708074534161491,0.30612244897959184,49,4663.981366459628,362.6972049689441,6,15.248447204968944,733663.4472049689 +Harbor multi-harness,684,claude-code,1,89,1.0,16.292134831460675,15.955056179775282,17.0,17.0,6.50561797752809,0.4024069059016184,0.9887640449438202,0.0,0.8651685393258427,0.8764044943820225,1.0,0.4943820224719101,1.0,37,2848.3595505617977,260.0788764044944,4,15.96629213483146,583406.1011235955 +Harbor multi-harness,684,codex,0,167,0.0,16.748502994011975,15.994011976047904,17.0,17.0,2.7784431137724552,0.17079379413526907,0.7305389221556886,0.0,0.8383233532934131,0.9281437125748503,0.19760479041916168,0.7904191616766467,0.16363636363636364,55,4250.748502994012,385.4654491017964,0,1.0479041916167664,20587.389221556885 +Harbor multi-harness,684,codex,1,83,1.0,16.867469879518072,16.602409638554217,17.0,17.0,6.9879518072289155,0.41958928149573077,0.9879518072289156,0.0,0.9036144578313253,0.9036144578313253,1.0,0.37349397590361444,1.0,31,2842.2289156626507,275.68036144578315,0,1.0240963855421688,14307.590361445784 +Harbor multi-harness,684,mini-swe-agent,0,174,0.0,15.454022988505747,15.172413793103448,17.0,17.0,2.839080459770115,0.17888920016201965,0.8045977011494253,0.0,0.7298850574712644,0.7528735632183908,0.28160919540229884,0.5517241379310345,0.2982456140350877,57,2137.6206896551726,222.47218390804596,8,1.264367816091954,15476.724137931034 +Harbor multi-harness,684,mini-swe-agent,1,76,1.0,10.460526315789474,10.421052631578947,10.0,16.0,0.7105263157894737,0.05720755744943052,0.42105263157894735,0.0,0.06578947368421052,0.8947368421052632,1.0,0.32894736842105265,1.0,29,908.6315789473684,128.98855263157895,0,1.0526315789473684,6987.4473684210525 +Harbor multi-harness,684,opencode,0,177,0.0,15.163841807909604,13.870056497175142,16.0,16.0,3.3389830508474576,0.22240027475570845,0.7062146892655368,0.005649717514124294,0.7457627118644068,0.655367231638418,0.2994350282485876,0.576271186440678,0.31666666666666665,60,2234.4180790960454,248.0429943502825,39,2.3220338983050848,70932.70056497175 +Harbor multi-harness,684,opencode,1,73,1.0,16.945205479452056,15.931506849315069,16.0,17.0,8.643835616438356,0.542459721539093,0.9726027397260274,0.0,0.958904109589041,0.7808219178082192,1.0,0.3972602739726027,1.0,26,1487.0958904109589,200.8408219178082,8,2.136986301369863,56677.82191780822 +Harbor multi-harness,700,claude-code,0,171,0.0,15.362573099415204,15.140350877192983,17.0,18.0,3.1228070175438596,0.2032035172535417,0.8421052631578947,0.011695906432748537,0.7602339181286549,0.6549707602339181,0.21637426900584794,0.6432748538011696,0.20754716981132076,53,4652.7192982456145,361.23532163742686,3,14.859649122807017,694100.2807017544 +Harbor multi-harness,700,claude-code,1,79,1.0,16.443037974683545,16.0,17.0,17.0,6.620253164556962,0.4081213219033104,1.0,0.0,0.8607594936708861,0.8607594936708861,1.0,0.45569620253164556,1.0,33,3123.9493670886077,280.37696202531646,2,15.91139240506329,602102.9620253164 +Harbor multi-harness,700,codex,0,170,0.0,16.74705882352941,16.194117647058825,17.0,17.0,3.1176470588235294,0.19078303568788,0.8352941176470589,0.0,0.8764705882352941,0.8529411764705882,0.2529411764705882,0.7294117647058823,0.21568627450980393,51,4079.929411764706,363.8954117647059,0,1.0058823529411764,19588.45294117647 +Harbor multi-harness,700,codex,1,80,1.0,16.8625,16.7,17.0,17.0,6.7625,0.405960617201426,0.975,0.0,0.9125,0.95,1.0,0.4625,1.0,35,2884.475,262.45475,0,1.0125,14580.1375 +Harbor multi-harness,700,mini-swe-agent,0,174,0.0,15.948275862068966,15.60919540229885,17.0,17.0,2.6839080459770117,0.16887949645102415,0.8390804597701149,0.0,0.7931034482758621,0.735632183908046,0.21839080459770116,0.5804597701149425,0.24528301886792453,53,2263.3563218390805,227.50166666666667,11,1.3793103448275863,16386.827586206895 +Harbor multi-harness,700,mini-swe-agent,1,76,1.0,10.81578947368421,10.697368421052632,11.0,15.5,0.8289473684210527,0.0667787449908193,0.5,0.0,0.09210526315789473,0.8552631578947368,1.0,0.27631578947368424,1.0,33,1056.0263157894738,140.4973684210526,0,1.0921052631578947,8168.815789473684 +Harbor multi-harness,700,opencode,0,197,0.0,14.345177664974619,13.126903553299492,16.0,16.0,3.0609137055837565,0.1993849283823903,0.6091370558375635,0.03553299492385787,0.6802030456852792,0.5431472081218274,0.26903553299492383,0.4467005076142132,0.1935483870967742,62,2101.964467005076,228.04253807106596,33,2.2944162436548226,71687.75126903554 +Harbor multi-harness,700,opencode,1,53,1.0,16.90566037735849,15.849056603773585,16.0,17.0,7.7924528301886795,0.4941648575250129,0.9622641509433962,0.0,0.9245283018867925,0.7924528301886793,1.0,0.3584905660377358,1.0,24,1739.0943396226414,211.79509433962264,13,2.169811320754717,54181.20754716981 +Harbor multi-harness,800,claude-code,0,170,0.0,15.158823529411764,14.770588235294118,16.0,17.0,3.4235294117647057,0.2306327284029433,0.8705882352941177,0.011764705882352941,0.7176470588235294,0.5764705882352941,0.1411764705882353,0.6294117647058823,0.11538461538461539,52,5225.105882352941,382.35835294117646,4,14.5,658475.4588235294 +Harbor multi-harness,800,claude-code,1,80,1.0,16.9625,16.7125,17.0,17.0,8.025,0.48083872364735364,1.0,0.0,0.9625,0.925,1.0,0.575,1.0,34,3774.8,313.24275,5,16.475,629729.4875 +Harbor multi-harness,800,codex,0,183,0.0,16.60655737704918,15.3551912568306,16.0,17.0,3.109289617486339,0.20031003631534008,0.8633879781420765,0.0,0.6830601092896175,0.8306010928961749,0.15846994535519127,0.5737704918032787,0.11666666666666667,60,5050.88524590164,443.0098360655738,0,1.0491803278688525,21167.169398907103 +Harbor multi-harness,800,codex,1,67,1.0,16.65671641791045,15.701492537313433,16.0,17.0,5.895522388059701,0.3727983970960266,1.0,0.0,0.7014925373134329,0.9850746268656716,1.0,0.3880597014925373,1.0,26,3771.955223880597,371.7289552238806,0,1.0298507462686568,14596.686567164179 +Harbor multi-harness,800,mini-swe-agent,0,185,0.0,16.572972972972973,16.427027027027027,17.0,17.0,2.708108108108108,0.16184273728391377,0.8108108108108109,0.0,0.8972972972972973,0.7189189189189189,0.08648648648648649,0.4810810810810811,0.04918032786885246,61,3029.054054054054,264.9401081081081,22,1.145945945945946,17322.75135135135 +Harbor multi-harness,800,mini-swe-agent,1,65,1.0,11.846153846153847,11.815384615384616,12.0,16.0,0.8307692307692308,0.060862606473466196,0.49230769230769234,0.0,0.07692307692307693,0.9538461538461539,1.0,0.35384615384615387,1.0,25,1533.3538461538462,167.18092307692308,3,1.0307692307692307,8874.661538461538 +Harbor multi-harness,800,opencode,0,192,0.0,15.739583333333334,14.453125,16.0,16.0,2.7135416666666665,0.17644656286659963,0.78125,0.036458333333333336,0.84375,0.5729166666666666,0.13541666666666666,0.46875,0.14754098360655737,61,3080.0416666666665,291.22421875000003,62,2.5260416666666665,81832.140625 +Harbor multi-harness,800,opencode,1,58,1.0,16.93103448275862,15.810344827586206,16.0,17.0,7.120689655172414,0.4502001618507704,0.9827586206896551,0.0,0.9482758620689655,0.7931034482758621,1.0,0.4482758620689655,1.0,25,1932.6724137931035,241.5848275862069,11,2.4482758620689653,62929.93103448276 +Harbor multi-harness,900,claude-code,0,159,0.0,14.81132075471698,14.075471698113208,16.0,17.0,3.981132075471698,0.26696447467266565,0.8238993710691824,0.031446540880503145,0.7484276729559748,0.6037735849056604,0.20125786163522014,0.6226415094339622,0.2222222222222222,45,10612.17610062893,454.2730188679246,29,14.779874213836479,634783.3018867924 +Harbor multi-harness,900,claude-code,1,91,1.0,16.736263736263737,16.439560439560438,17.0,18.0,9.263736263736265,0.55738028679758,1.0,0.0,0.945054945054945,0.8351648351648352,1.0,0.4175824175824176,1.0,41,7610.912087912088,357.4365934065934,5,16.736263736263737,717278.7692307692 +Harbor multi-harness,900,codex,0,202,0.0,7.965346534653466,7.138613861386139,6.0,17.0,0.995049504950495,0.0920445462451869,0.37623762376237624,0.14356435643564355,0.11386138613861387,0.5445544554455446,0.1188118811881188,0.3217821782178218,0.015151515151515152,66,3116.232673267327,324.8817326732673,2,1.108910891089109,13871.940594059406 +Harbor multi-harness,900,codex,1,48,1.0,14.458333333333334,13.9375,15.5,17.0,5.625,0.3789030363019334,0.9791666666666666,0.0,0.5208333333333334,0.9375,1.0,0.3958333333333333,1.0,20,6214.291666666667,388.205,0,1.0625,17239.0 +Harbor multi-harness,900,mini-swe-agent,0,168,0.0,15.529761904761905,14.476190476190476,16.0,17.0,1.4047619047619047,0.09060472431008144,0.625,0.0,0.6964285714285714,0.8214285714285714,0.17857142857142858,0.6071428571428571,0.1568627450980392,51,4686.678571428572,356.21607142857147,83,1.1488095238095237,17291.779761904763 +Harbor multi-harness,900,mini-swe-agent,1,82,1.0,10.329268292682928,9.939024390243903,9.0,14.900000000000006,0.45121951219512196,0.03692346430868669,0.2682926829268293,0.0,0.08536585365853659,0.9146341463414634,1.0,0.4146341463414634,1.0,35,2480.6585365853657,206.19500000000002,27,1.0365853658536586,8791.621951219513 +Harbor multi-harness,900,opencode,0,244,0.0,3.6639344262295084,1.8770491803278688,1.0,3.0,0.26229508196721313,0.020430672268907563,0.045081967213114756,0.8934426229508197,0.036885245901639344,0.040983606557377046,0.02459016393442623,0.036885245901639344,0.012048192771084338,83,4063.122950819672,226.0097131147541,3,2.057377049180328,51055.42622950819 +Harbor multi-harness,900,opencode,1,6,1.0,17.0,16.0,16.0,16.0,9.5,0.59375,1.0,0.0,1.0,0.6666666666666666,1.0,0.5,1.0,3,2838.0,459.5533333333333,0,2.1666666666666665,57072.5 +Harbor multi-harness,1000,claude-code,0,153,0.0,13.176470588235293,12.143790849673202,15.0,17.0,3.026143790849673,0.23638500454198869,0.8366013071895425,0.013071895424836602,0.5294117647058824,0.5816993464052288,0.23529411764705882,0.5620915032679739,0.11627906976744186,43,10960.522875816994,462.06437908496736,63,13.176470588235293,534125.8562091503 +Harbor multi-harness,1000,claude-code,1,97,1.0,13.164948453608247,12.329896907216495,12.0,17.0,4.979381443298969,0.36920607364576596,0.9072164948453608,0.0,0.3917525773195876,0.8556701030927835,1.0,0.6082474226804123,1.0,43,7160.865979381443,326.91278350515466,10,13.154639175257731,522237.4742268041 +Harbor multi-harness,1000,codex,0,188,0.0,8.595744680851064,7.718085106382978,6.0,17.0,1.5585106382978724,0.13339811045928066,0.44148936170212766,0.18085106382978725,0.15425531914893617,0.6063829787234043,0.18617021276595744,0.44680851063829785,0.06557377049180328,61,3815.446808510638,341.09920212765957,8,1.0638297872340425,14604.494680851063 +Harbor multi-harness,1000,codex,1,62,1.0,14.435483870967742,13.870967741935484,15.0,17.0,6.5,0.4503665044106221,1.0,0.0,0.45161290322580644,0.9516129032258065,1.0,0.46774193548387094,1.0,25,5575.887096774193,323.1374193548387,0,1.032258064516129,16035.064516129032 +Harbor multi-harness,1000,mini-swe-agent,0,160,0.0,13.71875,12.45,14.0,17.0,1.5875,0.1097099739517019,0.575,0.0,0.45625,0.7875,0.325,0.5,0.36,50,3934.6625,329.1018125,94,1.38125,15135.775 +Harbor multi-harness,1000,mini-swe-agent,1,90,1.0,8.8,8.433333333333334,8.0,13.100000000000009,0.3,0.02771149057913764,0.15555555555555556,0.0,0.03333333333333333,0.9111111111111111,0.9888888888888889,0.45555555555555555,1.0,36,1893.8555555555556,161.125,26,1.4555555555555555,10101.877777777778 +Harbor multi-harness,1000,opencode,0,236,0.0,4.758474576271187,3.0211864406779663,1.0,8.5,0.7161016949152542,0.04913525117288826,0.11016949152542373,0.7838983050847458,0.08050847457627118,0.13559322033898305,0.07203389830508475,0.11016949152542373,0.025974025974025976,77,3998.741525423729,205.5077118644068,4,2.152542372881356,56115.39406779661 +Harbor multi-harness,1000,opencode,1,14,1.0,14.714285714285714,13.5,16.0,16.0,8.0,0.5429946978791517,1.0,0.0,0.6428571428571429,0.7857142857142857,1.0,0.5,1.0,9,2043.7142857142858,147.84142857142857,0,2.5714285714285716,65820.35714285714 +Native OpenCode,0,claude-code,0,208,0.0,14.302884615384615,14.264423076923077,17.0,18.0,0.6586538461538461,0.04440941485464735,0.30288461538461536,0.014423076923076924,0.6298076923076923,0.75,0.3173076923076923,0.7163461538461539,0.29411764705882354,68,3756.2115384615386,315.4400961538461,175,14.052884615384615,570159.0384615385 +Native OpenCode,0,claude-code,1,42,1.0,9.904761904761905,9.380952380952381,8.0,16.9,0.4523809523809524,0.04256967744362702,0.30952380952380953,0.0,0.11904761904761904,0.9285714285714286,1.0,0.5238095238095238,1.0,18,2053.785714285714,224.98285714285717,32,9.761904761904763,339870.6904761905 +Native OpenCode,0,codex,0,212,0.0,15.415094339622641,16.169811320754718,17.0,19.0,0.9009433962264151,0.053540390426595565,0.45754716981132076,0.0,0.7547169811320755,0.7358490566037735,0.2641509433962264,0.6132075471698113,0.2753623188405797,69,3129.948113207547,308.76754716981134,184,1.1603773584905661,20631.580188679247 +Native OpenCode,0,codex,1,38,1.0,11.947368421052632,11.394736842105264,11.0,16.300000000000004,0.5263157894736842,0.041548275855551396,0.3684210526315789,0.0,0.2894736842105263,0.868421052631579,0.9736842105263158,0.47368421052631576,1.0,17,1782.8157894736842,239.67105263157896,30,1.1842105263157894,15461.157894736842 +Native OpenCode,0,mini-swe-agent,0,203,0.0,16.43349753694581,16.300492610837438,17.0,17.0,0.5369458128078818,0.03434724625321554,0.33004926108374383,0.0,0.8719211822660099,0.9064039408866995,0.21674876847290642,0.7339901477832512,0.2463768115942029,69,2708.073891625616,228.48142857142858,165,1.1379310344827587,18054.32512315271 +Native OpenCode,0,mini-swe-agent,1,47,1.0,13.48936170212766,12.680851063829786,13.0,17.0,1.0425531914893618,0.076475382842717,0.5319148936170213,0.0,0.3191489361702128,0.9574468085106383,1.0,0.425531914893617,1.0,17,1625.723404255319,172.65234042553192,39,1.702127659574468,14450.063829787234 +Native OpenCode,0,opencode,0,218,0.0,13.293577981651376,12.412844036697248,16.0,17.0,0.591743119266055,0.038509625964180456,0.3165137614678899,0.022935779816513763,0.5412844036697247,0.8623853211009175,0.40825688073394495,0.7201834862385321,0.4084507042253521,71,1849.3532110091744,228.68224770642203,179,1.1605504587155964,34423.706422018346 +Native OpenCode,0,opencode,1,32,1.0,8.65625,7.34375,7.0,12.600000000000009,0.125,0.007598039215686275,0.0625,0.0,0.09375,0.875,1.0,0.4375,1.0,15,853.15625,168.39625,26,1.3125,31859.375 +Native OpenCode,100,claude-code,0,199,0.0,13.763819095477388,13.763819095477388,17.0,18.0,0.6834170854271356,0.04006487274477853,0.32160804020100503,0.01507537688442211,0.6130653266331658,0.8592964824120602,0.41708542713567837,0.7336683417085427,0.375,64,4123.48743718593,150.66597989949747,0,13.683417085427136,571350.7336683417 +Native OpenCode,100,claude-code,1,51,1.0,8.294117647058824,7.470588235294118,6.0,14.0,0.17647058823529413,0.015685327104012225,0.13725490196078433,0.0196078431372549,0.058823529411764705,0.9215686274509803,1.0,0.5294117647058824,1.0,22,2041.4313725490197,113.89274509803921,0,8.294117647058824,246696.431372549 +Native OpenCode,100,codex,0,205,0.0,15.678048780487805,16.034146341463416,17.0,19.0,0.526829268292683,0.032170745981919505,0.35121951219512193,0.004878048780487805,0.7707317073170732,0.8634146341463415,0.2731707317073171,0.7219512195121951,0.23880597014925373,67,3219.7951219512197,162.1279512195122,0,1.3219512195121952,20868.331707317073 +Native OpenCode,100,codex,1,45,1.0,11.822222222222223,11.511111111111111,11.0,17.0,0.4666666666666667,0.03983310988212949,0.35555555555555557,0.0,0.15555555555555556,0.9777777777777777,1.0,0.5555555555555556,1.0,19,1963.8666666666666,142.86844444444444,0,1.2222222222222223,14224.6 +Native OpenCode,100,mini-swe-agent,0,198,0.0,16.18686868686869,16.08080808080808,17.0,17.0,0.4090909090909091,0.026111279375539625,0.3181818181818182,0.0,0.8636363636363636,0.9545454545454546,0.23737373737373738,0.7575757575757576,0.23076923076923078,65,2800.1161616161617,93.65085858585859,0,1.1666666666666667,16083.61616161616 +Native OpenCode,100,mini-swe-agent,1,52,1.0,12.288461538461538,11.711538461538462,12.0,17.0,0.4423076923076923,0.03660985350747794,0.28846153846153844,0.0,0.21153846153846154,0.9807692307692307,1.0,0.4423076923076923,1.0,21,1555.423076923077,77.1298076923077,1,1.7692307692307692,14584.115384615385 +Native OpenCode,100,opencode,0,201,0.0,13.298507462686567,12.00497512437811,16.0,16.0,0.5522388059701493,0.03520774841589499,0.2537313432835821,0.004975124378109453,0.527363184079602,0.945273631840796,0.5223880597014925,0.7412935323383084,0.5909090909090909,66,2472.7960199004974,149.8694527363184,0,2.2487562189054726,79615.70149253731 +Native OpenCode,100,opencode,1,49,1.0,7.6938775510204085,5.857142857142857,5.0,11.0,0.02040816326530612,0.002551020408163265,0.02040816326530612,0.0,0.02040816326530612,0.9795918367346939,1.0,0.3673469387755102,1.0,20,822.8163265306123,121.0073469387755,0,2.326530612244898,78707.26530612246 +Native OpenCode,200,claude-code,0,190,0.0,13.889473684210527,13.9,17.0,18.0,0.8894736842105263,0.05147545597541524,0.32105263157894737,0.010526315789473684,0.6421052631578947,0.9368421052631579,0.41578947368421054,0.8157894736842105,0.43333333333333335,60,4142.078947368421,147.35847368421054,0,13.878947368421052,545937.9157894737 +Native OpenCode,200,claude-code,1,60,1.0,7.516666666666667,6.633333333333334,6.0,11.100000000000001,0.05,0.005726495726495726,0.05,0.0,0.0,0.9666666666666667,1.0,0.48333333333333334,1.0,26,1709.3166666666666,108.92166666666667,0,7.5,238652.76666666666 +Native OpenCode,200,codex,0,186,0.0,14.634408602150538,14.935483870967742,17.0,18.0,0.5376344086021505,0.033831798553285615,0.3225806451612903,0.0,0.6827956989247311,0.9086021505376344,0.3817204301075269,0.7688172043010753,0.39344262295081966,61,2838.569892473118,153.04629032258066,0,1.3440860215053763,18924.704301075268 +Native OpenCode,200,codex,1,64,1.0,10.265625,9.625,9.0,15.700000000000003,0.25,0.01870659722222222,0.125,0.0,0.09375,0.921875,1.0,0.484375,1.0,25,1497.109375,130.18609375,0,1.359375,15162.0 +Native OpenCode,200,mini-swe-agent,0,196,0.0,15.948979591836734,15.755102040816327,17.0,17.0,0.3622448979591837,0.025260730332759145,0.25510204081632654,0.0,0.8214285714285714,0.9540816326530612,0.3163265306122449,0.7857142857142857,0.3484848484848485,66,2799.1428571428573,87.24484693877552,0,1.3112244897959184,16243.688775510203 +Native OpenCode,200,mini-swe-agent,1,54,1.0,12.092592592592593,10.907407407407407,11.0,15.700000000000003,0.5370370370370371,0.04723719151823727,0.3333333333333333,0.0,0.2222222222222222,0.9814814814814815,1.0,0.35185185185185186,1.0,20,1331.5740740740741,69.92592592592592,0,2.314814814814815,16194.37037037037 +Native OpenCode,200,opencode,0,207,0.0,12.429951690821255,11.096618357487923,12.0,16.0,0.5797101449275363,0.038225724240858454,0.26570048309178745,0.00966183574879227,0.4782608695652174,0.9758454106280193,0.5603864734299517,0.7971014492753623,0.5074626865671642,67,1963.6521739130435,146.0319806763285,0,1.826086956521739,81043.84057971014 +Native OpenCode,200,opencode,1,43,1.0,7.232558139534884,5.325581395348837,4.0,9.800000000000004,0.046511627906976744,0.004872646733111849,0.046511627906976744,0.0,0.023255813953488372,0.9767441860465116,1.0,0.3023255813953488,1.0,19,734.7441860465116,113.19883720930233,0,2.0930232558139537,79277.81395348837 +Native OpenCode,300,claude-code,0,177,0.0,14.124293785310735,14.01129943502825,17.0,18.0,0.5254237288135594,0.03287209548229121,0.2768361581920904,0.0,0.6214689265536724,0.9661016949152542,0.423728813559322,0.847457627118644,0.4482758620689655,58,4781.062146892656,150.71186440677965,0,14.07909604519774,577002.6101694915 +Native OpenCode,300,claude-code,1,73,1.0,8.479452054794521,7.47945205479452,6.0,13.799999999999997,0.0684931506849315,0.0065535491905354916,0.0684931506849315,0.0136986301369863,0.0273972602739726,0.9452054794520548,1.0,0.547945205479452,1.0,28,2040.013698630137,114.68150684931507,0,8.479452054794521,271751.4794520548 +Native OpenCode,300,codex,0,208,0.0,15.745192307692308,15.913461538461538,17.0,18.0,0.6153846153846154,0.03697912532084359,0.39903846153846156,0.0,0.8076923076923077,0.9134615384615384,0.24519230769230768,0.7548076923076923,0.1643835616438356,73,2957.014423076923,151.3935096153846,0,1.2788461538461537,17881.96153846154 +Native OpenCode,300,codex,1,42,1.0,12.095238095238095,11.452380952380953,12.5,17.0,0.6428571428571429,0.0437022244795354,0.35714285714285715,0.0,0.2857142857142857,0.9047619047619048,1.0,0.40476190476190477,1.0,13,1566.9761904761904,132.18380952380954,0,1.3571428571428572,14411.619047619048 +Native OpenCode,300,mini-swe-agent,0,201,0.0,16.36318407960199,16.149253731343283,17.0,17.0,0.5024875621890548,0.03314672312696895,0.3482587064676617,0.0,0.8706467661691543,0.9651741293532339,0.19900497512437812,0.8208955223880597,0.23943661971830985,71,2948.6517412935323,89.15626865671642,4,1.318407960199005,17349.243781094527 +Native OpenCode,300,mini-swe-agent,1,49,1.0,12.816326530612244,11.53061224489796,11.0,16.0,0.8775510204081632,0.0769577361414096,0.4489795918367347,0.0,0.1836734693877551,0.9795918367346939,1.0,0.3877551020408163,1.0,15,1338.7551020408164,68.10775510204083,0,2.2448979591836733,14512.469387755102 +Native OpenCode,300,opencode,0,198,0.0,13.691919191919192,12.474747474747474,16.0,17.0,0.6515151515151515,0.042636004155612,0.30808080808080807,0.0,0.6111111111111112,0.9949494949494949,0.4444444444444444,0.7878787878787878,0.38095238095238093,63,2405.9646464646466,145.3980303030303,2,1.606060606060606,52651.00505050505 +Native OpenCode,300,opencode,1,52,1.0,6.903846153846154,5.211538461538462,4.0,9.799999999999997,0.15384615384615385,0.013679029304029304,0.07692307692307693,0.0,0.019230769230769232,1.0,1.0,0.34615384615384615,1.0,23,707.9230769230769,107.16211538461539,0,1.9807692307692308,51971.0 +Native OpenCode,400,claude-code,0,169,0.0,14.29585798816568,14.248520710059172,17.0,18.0,0.7514792899408284,0.0437959892770562,0.28994082840236685,0.005917159763313609,0.6923076923076923,0.9230769230769231,0.3254437869822485,0.8224852071005917,0.3333333333333333,51,4858.84023668639,138.55562130177515,0,14.254437869822485,548807.5798816568 +Native OpenCode,400,claude-code,1,81,1.0,8.024691358024691,7.0,5.0,14.0,0.13580246913580246,0.009861885270382002,0.08641975308641975,0.0,0.04938271604938271,1.0,1.0,0.6666666666666666,1.0,35,1900.888888888889,111.3527160493827,0,8.024691358024691,231742.38271604938 +Native OpenCode,400,codex,0,186,0.0,16.032258064516128,16.182795698924732,17.0,18.0,0.6881720430107527,0.041173102205634915,0.3817204301075269,0.0,0.8440860215053764,0.946236559139785,0.22043010752688172,0.7634408602150538,0.18032786885245902,61,3020.1666666666665,146.95666666666665,0,1.2688172043010753,17990.56989247312 +Native OpenCode,400,codex,1,64,1.0,11.84375,11.4375,11.5,17.0,0.484375,0.03632220516411693,0.328125,0.0,0.21875,1.0,1.0,0.578125,1.0,25,1613.78125,129.23703125,0,1.328125,14538.671875 +Native OpenCode,400,mini-swe-agent,0,182,0.0,16.483516483516482,16.186813186813186,17.0,17.0,0.45054945054945056,0.03001271783592404,0.32967032967032966,0.0,0.8626373626373627,0.9615384615384616,0.24725274725274726,0.8571428571428571,0.3114754098360656,61,3207.5494505494507,85.6412087912088,0,1.4285714285714286,18521.351648351647 +Native OpenCode,400,mini-swe-agent,1,68,1.0,12.882352941176471,11.426470588235293,11.0,16.0,0.6470588235294118,0.05360530593013292,0.4411764705882353,0.0,0.25,0.9558823529411765,1.0,0.5588235294117647,1.0,25,1413.5735294117646,69.97279411764706,0,2.485294117647059,17904.45588235294 +Native OpenCode,400,opencode,0,199,0.0,13.07035175879397,11.72361809045226,15.0,16.0,0.5879396984924623,0.03827979587660344,0.2864321608040201,0.0,0.5025125628140703,0.9899497487437185,0.542713567839196,0.7839195979899497,0.5625,64,2161.718592964824,129.72386934673366,0,1.6984924623115578,37042.36180904523 +Native OpenCode,400,opencode,1,51,1.0,7.607843137254902,5.745098039215686,5.0,11.0,0.09803921568627451,0.007107843137254903,0.058823529411764705,0.0,0.0392156862745098,1.0,1.0,0.3137254901960784,1.0,22,712.8823529411765,106.30941176470587,0,2.1372549019607843,34225.470588235294 +Native OpenCode,500,claude-code,0,184,0.0,13.619565217391305,13.190217391304348,17.0,17.0,0.8804347826086957,0.05647794774044101,0.3641304347826087,0.010869565217391304,0.592391304347826,0.967391304347826,0.40217391304347827,0.8097826086956522,0.43333333333333335,60,4348.010869565217,147.87760869565219,0,13.576086956521738,536566.4076086957 +Native OpenCode,500,claude-code,1,66,1.0,8.181818181818182,6.833333333333333,5.5,13.0,0.36363636363636365,0.03604161361514302,0.24242424242424243,0.015151515151515152,0.07575757575757576,0.9545454545454546,1.0,0.45454545454545453,1.0,26,1441.7878787878788,107.24469696969696,0,8.181818181818182,247779.4696969697 +Native OpenCode,500,codex,0,203,0.0,15.551724137931034,15.52216748768473,17.0,17.0,0.645320197044335,0.039335488932669006,0.3891625615763547,0.0049261083743842365,0.7733990147783252,0.9556650246305419,0.2413793103448276,0.6798029556650246,0.2,70,2592.881773399015,152.5869458128079,0,1.354679802955665,18126.08866995074 +Native OpenCode,500,codex,1,47,1.0,11.27659574468085,10.51063829787234,10.0,16.4,0.2553191489361702,0.020055372245610045,0.14893617021276595,0.0,0.14893617021276595,0.9787234042553191,1.0,0.46808510638297873,1.0,16,1302.9148936170213,133.84765957446808,0,1.3404255319148937,13452.446808510638 +Native OpenCode,500,mini-swe-agent,0,177,0.0,16.372881355932204,15.824858757062147,17.0,17.0,0.3446327683615819,0.023830714446367487,0.2542372881355932,0.0,0.8757062146892656,0.96045197740113,0.23163841807909605,0.847457627118644,0.25,56,3247.4406779661017,96.2750847457627,3,1.96045197740113,21980.90395480226 +Native OpenCode,500,mini-swe-agent,1,73,1.0,12.821917808219178,10.452054794520548,10.0,15.799999999999997,0.7671232876712328,0.07374495747783419,0.5068493150684932,0.0,0.2602739726027397,1.0,1.0,0.4520547945205479,1.0,30,1418.2739726027398,72.02739726027397,0,3.3698630136986303,22001.712328767124 +Native OpenCode,500,opencode,0,205,0.0,12.302439024390244,10.917073170731708,13.0,16.0,1.824390243902439,0.12018429322948693,0.47804878048780486,0.004878048780487805,0.44390243902439025,0.9804878048780488,0.5463414634146342,0.7170731707317073,0.582089552238806,67,1360.0487804878048,132.32965853658538,0,2.3365853658536584,56299.10243902439 +Native OpenCode,500,opencode,1,45,1.0,7.155555555555556,5.2444444444444445,4.0,10.0,0.2222222222222222,0.022260702260702262,0.1111111111111111,0.0,0.0,1.0,1.0,0.37777777777777777,1.0,19,514.0222222222222,107.42822222222223,0,1.9555555555555555,36444.35555555556 +Native OpenCode,600,claude-code,0,169,0.0,13.094674556213018,12.591715976331361,16.0,17.0,1.2485207100591715,0.07677985064542303,0.42011834319526625,0.023668639053254437,0.5384615384615384,0.9349112426035503,0.44970414201183434,0.757396449704142,0.36363636363636365,55,3993.591715976331,139.73,1,13.053254437869823,512788.8402366864 +Native OpenCode,600,claude-code,1,81,1.0,8.11111111111111,6.518518518518518,5.0,13.0,0.32098765432098764,0.028901329445991757,0.18518518518518517,0.0,0.06172839506172839,0.9876543209876543,1.0,0.5061728395061729,1.0,31,1573.2345679012346,105.64456790123455,0,8.11111111111111,269689.3703703704 +Native OpenCode,600,codex,0,204,0.0,15.583333333333334,15.563725490196079,17.0,17.0,1.2990196078431373,0.07896935690256579,0.5980392156862745,0.0,0.7647058823529411,0.9362745098039216,0.3235294117647059,0.6519607843137255,0.24285714285714285,70,2547.2303921568628,147.71186274509805,0,1.4264705882352942,18717.892156862745 +Native OpenCode,600,codex,1,46,1.0,9.826086956521738,9.043478260869565,8.0,15.0,0.41304347826086957,0.034211114262265155,0.2826086956521739,0.0,0.10869565217391304,0.9565217391304348,1.0,0.41304347826086957,1.0,16,1209.4130434782608,124.62239130434783,0,1.5,14637.673913043478 +Native OpenCode,600,mini-swe-agent,0,168,0.0,15.577380952380953,14.363095238095237,16.0,17.0,0.5119047619047619,0.038403169154219575,0.36904761904761907,0.0,0.7321428571428571,0.9821428571428571,0.35714285714285715,0.8690476190476191,0.40384615384615385,52,3584.9583333333335,96.24154761904762,2,2.744047619047619,27519.95238095238 +Native OpenCode,600,mini-swe-agent,1,82,1.0,12.939024390243903,10.634146341463415,10.0,16.0,1.0731707317073171,0.10779894722685254,0.5365853658536586,0.0,0.2682926829268293,1.0,1.0,0.5487804878048781,1.0,34,1693.5,75.17646341463416,3,3.8902439024390243,26157.51219512195 +Native OpenCode,600,opencode,0,208,0.0,11.60576923076923,10.14423076923077,10.0,16.0,2.3028846153846154,0.15922536998258152,0.4230769230769231,0.014423076923076924,0.38461538461538464,0.9326923076923077,0.6153846153846154,0.6634615384615384,0.6716417910447762,67,1621.110576923077,127.25942307692307,8,2.706730769230769,60274.144230769234 +Native OpenCode,600,opencode,1,42,1.0,6.0476190476190474,4.238095238095238,3.5,7.0,0.23809523809523808,0.026785714285714284,0.07142857142857142,0.023809523809523808,0.0,0.9523809523809523,1.0,0.2857142857142857,1.0,19,410.95238095238096,106.43476190476191,0,2.119047619047619,44473.09523809524 +Native OpenCode,700,claude-code,0,176,0.0,12.829545454545455,12.170454545454545,16.0,17.0,1.1022727272727273,0.07071338780699708,0.375,0.03409090909090909,0.5170454545454546,0.8977272727272727,0.5795454545454546,0.8011363636363636,0.45454545454545453,55,3675.1704545454545,132.98482954545455,0,12.806818181818182,520574.35795454547 +Native OpenCode,700,claude-code,1,74,1.0,8.013513513513514,6.324324324324325,5.5,11.0,0.14864864864864866,0.021100296100296102,0.10810810810810811,0.0,0.04054054054054054,0.9594594594594594,1.0,0.5405405405405406,1.0,31,1597.918918918919,109.78432432432433,0,8.013513513513514,264528.13513513515 +Native OpenCode,700,codex,0,214,0.0,16.149532710280372,16.009345794392523,17.0,17.0,1.0046728971962617,0.06136985380388349,0.4532710280373832,0.0,0.8598130841121495,0.9906542056074766,0.2803738317757009,0.6308411214953271,0.17142857142857143,70,2724.4859813084113,146.12116822429905,0,1.341121495327103,17182.19158878505 +Native OpenCode,700,codex,1,36,1.0,11.527777777777779,10.694444444444445,10.0,17.0,0.6944444444444444,0.0536913839609918,0.3888888888888889,0.0,0.19444444444444445,0.9722222222222222,1.0,0.5555555555555556,1.0,16,1316.9166666666667,125.90972222222223,0,1.25,12513.777777777777 +Native OpenCode,700,mini-swe-agent,0,174,0.0,15.655172413793103,15.057471264367816,17.0,17.0,0.6206896551724138,0.04524330141723651,0.3505747126436782,0.0,0.7701149425287356,0.9885057471264368,0.3793103448275862,0.8390804597701149,0.38181818181818183,55,3373.057471264368,99.63563218390804,0,2.086206896551724,23998.488505747126 +Native OpenCode,700,mini-swe-agent,1,76,1.0,12.013157894736842,10.289473684210526,10.0,16.0,1.0657894736842106,0.1035692012665697,0.5263157894736842,0.0,0.3157894736842105,1.0,1.0,0.47368421052631576,1.0,31,1367.5394736842106,69.25381578947368,0,3.263157894736842,22327.776315789473 +Native OpenCode,700,opencode,0,204,0.0,10.892156862745098,9.245098039215685,8.0,16.0,0.5,0.03536008517626165,0.24019607843137256,0.014705882352941176,0.3235294117647059,0.9754901960784313,0.7058823529411765,0.7205882352941176,0.7313432835820896,67,1408.8970588235295,128.84166666666667,0,1.9068627450980393,39369.55392156863 +Native OpenCode,700,opencode,1,46,1.0,5.413043478260869,3.4130434782608696,3.0,5.0,0.0,0.0,0.0,0.0,0.0,1.0,1.0,0.17391304347826086,1.0,19,361.4782608695652,103.2104347826087,0,2.130434782608696,37931.36956521739 +Native OpenCode,800,claude-code,0,169,0.0,12.899408284023668,12.331360946745562,15.0,17.0,0.6035502958579881,0.03996253541383334,0.3076923076923077,0.029585798816568046,0.47928994082840237,0.9467455621301775,0.5739644970414202,0.8106508875739645,0.5740740740740741,54,4475.852071005917,139.79603550295857,0,12.8698224852071,534636.6804733728 +Native OpenCode,800,claude-code,1,81,1.0,8.345679012345679,6.765432098765432,5.0,12.0,0.08641975308641975,0.008167917372710401,0.06172839506172839,0.0,0.024691358024691357,0.9876543209876543,1.0,0.5802469135802469,1.0,32,1958.530864197531,111.65493827160493,0,8.333333333333334,276778.0987654321 +Native OpenCode,800,codex,0,205,0.0,15.692682926829269,15.526829268292683,17.0,17.0,0.624390243902439,0.03874638194580805,0.3463414634146341,0.00975609756097561,0.8146341463414634,0.975609756097561,0.28292682926829266,0.5951219512195122,0.2,70,2847.756097560976,147.91429268292683,0,1.3609756097560977,18079.11707317073 +Native OpenCode,800,codex,1,45,1.0,10.688888888888888,9.8,10.0,15.600000000000001,0.2222222222222222,0.018652868554829338,0.2,0.0,0.1111111111111111,0.9777777777777777,1.0,0.5555555555555556,1.0,16,1305.7555555555555,129.1468888888889,0,1.4444444444444444,14645.422222222222 +Native OpenCode,800,mini-swe-agent,0,170,0.0,16.0,15.635294117647058,17.0,17.0,0.4470588235294118,0.03224727886492593,0.3176470588235294,0.0,0.8352941176470589,1.0,0.3411764705882353,0.9294117647058824,0.3333333333333333,51,4005.1941176470586,103.559,0,1.8588235294117648,24782.74705882353 +Native OpenCode,800,mini-swe-agent,1,80,1.0,10.85,9.825,9.0,15.100000000000009,0.5,0.0563505642641672,0.4,0.0,0.15,0.9875,1.0,0.5375,1.0,35,1601.1,73.436625,0,2.625,20962.525 +Native OpenCode,800,opencode,0,200,0.0,10.49,8.865,6.0,16.0,0.25,0.016592728758169934,0.14,0.02,0.315,0.955,0.71,0.71,0.7692307692307693,65,1997.44,131.44165,0,1.88,54469.285 +Native OpenCode,800,opencode,1,50,1.0,6.9,4.96,3.0,12.0,0.02,0.0013333333333333333,0.02,0.0,0.04,1.0,1.0,0.36,1.0,21,712.5,110.8512,0,2.0,47741.16 +Native OpenCode,900,claude-code,0,165,0.0,11.969696969696969,11.345454545454546,12.0,17.0,0.4909090909090909,0.0314819058936706,0.22424242424242424,0.04242424242424243,0.44242424242424244,0.8545454545454545,0.5454545454545454,0.7333333333333333,0.6,50,4513.454545454545,141.17763636363637,0,11.951515151515151,497851.6424242424 +Native OpenCode,900,claude-code,1,85,1.0,9.070588235294117,7.623529411764705,6.0,14.600000000000009,0.2235294117647059,0.019638135797305348,0.12941176470588237,0.011764705882352941,0.09411764705882353,0.9529411764705882,1.0,0.6,1.0,36,2158.3058823529414,117.16929411764706,0,9.070588235294117,342238.4588235294 +Native OpenCode,900,codex,0,214,0.0,15.841121495327103,15.719626168224298,17.0,17.0,0.7336448598130841,0.04457102713826333,0.397196261682243,0.004672897196261682,0.8271028037383178,0.9813084112149533,0.19158878504672897,0.5887850467289719,0.15714285714285714,70,2882.448598130841,144.84471962616823,0,1.294392523364486,16528.140186915887 +Native OpenCode,900,codex,1,36,1.0,12.166666666666666,11.333333333333334,11.5,17.0,0.6388888888888888,0.05095076945567142,0.3888888888888889,0.0,0.2222222222222222,1.0,1.0,0.6944444444444444,1.0,16,1686.0,133.05027777777778,0,1.3055555555555556,14844.777777777777 +Native OpenCode,900,mini-swe-agent,0,165,0.0,16.024242424242424,15.50909090909091,17.0,17.0,0.5393939393939394,0.04098105202383277,0.3212121212121212,0.0,0.8,1.0,0.3878787878787879,0.8848484848484849,0.2830188679245283,53,4123.842424242424,101.6440606060606,1,1.896969696969697,26226.58787878788 +Native OpenCode,900,mini-swe-agent,1,85,1.0,11.941176470588236,10.6,10.0,16.0,0.9294117647058824,0.0903002764248439,0.5176470588235295,0.0,0.23529411764705882,1.0,1.0,0.5764705882352941,1.0,33,1540.9411764705883,71.20494117647058,1,2.5647058823529414,22007.776470588236 +Native OpenCode,900,opencode,0,203,0.0,9.443349753694582,7.783251231527093,6.0,16.0,0.2413793103448276,0.019007205808133074,0.15763546798029557,0.059113300492610835,0.2019704433497537,0.9408866995073891,0.7339901477832512,0.6945812807881774,0.7910447761194029,67,1543.2463054187192,125.21029556650245,1,1.9704433497536946,60316.12315270936 +Native OpenCode,900,opencode,1,47,1.0,6.148936170212766,4.212765957446808,3.0,7.0,0.02127659574468085,0.002127659574468085,0.02127659574468085,0.0425531914893617,0.0,0.9361702127659575,1.0,0.3404255319148936,1.0,19,544.0851063829788,107.44978723404256,0,2.106382978723404,55798.17021276596 +Native OpenCode,1000,claude-code,0,167,0.0,12.431137724550899,11.994011976047904,13.0,17.0,0.5808383233532934,0.036901164345501676,0.30538922155688625,0.017964071856287425,0.47904191616766467,0.9161676646706587,0.6287425149700598,0.8023952095808383,0.5961538461538461,52,4404.88622754491,139.98269461077845,0,12.431137724550899,514356.04191616765 +Native OpenCode,1000,claude-code,1,83,1.0,8.795180722891565,7.578313253012048,6.0,14.799999999999997,0.3253012048192771,0.028024013548464292,0.1686746987951807,0.0,0.04819277108433735,0.9879518072289156,1.0,0.6385542168674698,1.0,34,1971.1566265060242,121.15024096385541,0,8.795180722891565,317782.0843373494 +Native OpenCode,1000,codex,0,176,0.0,15.659090909090908,15.494318181818182,17.0,17.0,0.4602272727272727,0.028241647037602922,0.29545454545454547,0.005681818181818182,0.7670454545454546,1.0,0.4375,0.9261363636363636,0.3050847457627119,59,2594.130681818182,154.0847727272727,0,6.034090909090909,83996.02272727272 +Native OpenCode,1000,codex,1,74,1.0,12.716216216216216,12.0,11.0,17.0,0.6351351351351351,0.04693533921475098,0.3918918918918919,0.0,0.3108108108108108,1.0,1.0,0.7162162162162162,1.0,27,919.9459459459459,131.65594594594594,0,2.5,24360.202702702703 +Native OpenCode,1000,mini-swe-agent,0,160,0.0,15.7125,15.19375,17.0,17.0,0.55625,0.04185905444637063,0.30625,0.0,0.7625,0.99375,0.4625,0.85,0.6274509803921569,51,4085.3375,111.4579375,0,5.94375,83023.3875 +Native OpenCode,1000,mini-swe-agent,1,90,1.0,12.855555555555556,11.144444444444444,11.5,16.0,1.3333333333333333,0.11476076846174886,0.6333333333333333,0.0,0.3111111111111111,1.0,1.0,0.5,1.0,35,842.1333333333333,74.28633333333333,0,3.9,28343.78888888889 +Native OpenCode,1000,opencode,0,199,0.0,11.49748743718593,9.984924623115578,10.0,16.0,1.3417085427135678,0.10450114924324207,0.542713567839196,0.0,0.33668341708542715,0.9748743718592965,0.7537688442211056,0.7587939698492462,0.7096774193548387,62,1849.0452261306532,130.6827135678392,0,6.442211055276382,169374.60301507538 +Native OpenCode,1000,opencode,1,51,1.0,7.745098039215686,5.882352941176471,5.0,9.0,0.8627450980392157,0.11350055173584586,0.49019607843137253,0.0,0.0,1.0,1.0,0.37254901960784315,1.0,24,754.6470588235294,116.4713725490196,0,5.019607843137255,137442.9019607843 diff --git a/04-data-agent/reports/three-run-analysis-20260917/capture_warnings.csv b/04-data-agent/reports/three-run-analysis-20260917/capture_warnings.csv new file mode 100644 index 0000000..6f3ac78 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/capture_warnings.csv @@ -0,0 +1,137 @@ +run,step,harness,truncated,context_limit,aux +Harbor OpenCode-only,0,claude-code,4,8,54 +Harbor OpenCode-only,0,codex,1,0,28 +Harbor OpenCode-only,0,mini-swe-agent,0,0,44 +Harbor OpenCode-only,0,opencode,0,2,3 +Harbor OpenCode-only,100,claude-code,2,6,0 +Harbor OpenCode-only,100,codex,2,0,2 +Harbor OpenCode-only,100,mini-swe-agent,0,0,23 +Harbor OpenCode-only,100,opencode,0,2,0 +Harbor OpenCode-only,200,claude-code,1,7,0 +Harbor OpenCode-only,200,codex,4,0,4 +Harbor OpenCode-only,200,mini-swe-agent,0,0,18 +Harbor OpenCode-only,200,opencode,0,4,0 +Harbor OpenCode-only,300,claude-code,1,9,1 +Harbor OpenCode-only,300,codex,1,0,2 +Harbor OpenCode-only,300,mini-swe-agent,0,0,12 +Harbor OpenCode-only,300,opencode,0,12,0 +Harbor OpenCode-only,400,claude-code,0,7,0 +Harbor OpenCode-only,400,codex,2,0,3 +Harbor OpenCode-only,400,mini-swe-agent,1,0,9 +Harbor OpenCode-only,400,opencode,0,4,0 +Harbor OpenCode-only,500,claude-code,2,7,0 +Harbor OpenCode-only,500,codex,4,0,4 +Harbor OpenCode-only,500,mini-swe-agent,0,0,9 +Harbor OpenCode-only,500,opencode,2,5,0 +Harbor OpenCode-only,600,claude-code,1,5,0 +Harbor OpenCode-only,600,codex,3,0,3 +Harbor OpenCode-only,600,mini-swe-agent,0,0,10 +Harbor OpenCode-only,600,opencode,1,4,0 +Harbor OpenCode-only,700,claude-code,4,2,0 +Harbor OpenCode-only,700,codex,2,0,3 +Harbor OpenCode-only,700,mini-swe-agent,1,0,35 +Harbor OpenCode-only,700,opencode,0,5,0 +Harbor OpenCode-only,800,claude-code,5,4,0 +Harbor OpenCode-only,800,codex,3,0,4 +Harbor OpenCode-only,800,mini-swe-agent,1,0,69 +Harbor OpenCode-only,800,opencode,27,1,0 +Harbor OpenCode-only,900,claude-code,2,11,0 +Harbor OpenCode-only,900,codex,1,0,1 +Harbor OpenCode-only,900,mini-swe-agent,1,0,87 +Harbor OpenCode-only,900,opencode,7,33,0 +Harbor OpenCode-only,1000,claude-code,2,11,0 +Harbor OpenCode-only,1000,codex,5,1,4 +Harbor OpenCode-only,1000,mini-swe-agent,0,0,57 +Harbor OpenCode-only,1000,opencode,2,13,0 +Harbor multi-harness,0,claude-code,4,8,54 +Harbor multi-harness,0,codex,1,0,28 +Harbor multi-harness,0,mini-swe-agent,0,0,44 +Harbor multi-harness,0,opencode,0,2,3 +Harbor multi-harness,100,claude-code,8,12,102 +Harbor multi-harness,100,codex,0,0,64 +Harbor multi-harness,100,mini-swe-agent,0,0,14 +Harbor multi-harness,100,opencode,1,8,15 +Harbor multi-harness,200,claude-code,12,9,107 +Harbor multi-harness,200,codex,0,0,71 +Harbor multi-harness,200,mini-swe-agent,0,0,6 +Harbor multi-harness,200,opencode,0,5,22 +Harbor multi-harness,300,claude-code,7,12,85 +Harbor multi-harness,300,codex,0,0,58 +Harbor multi-harness,300,mini-swe-agent,0,0,5 +Harbor multi-harness,300,opencode,0,0,6 +Harbor multi-harness,400,claude-code,7,11,93 +Harbor multi-harness,400,codex,0,0,52 +Harbor multi-harness,400,mini-swe-agent,0,0,18 +Harbor multi-harness,400,opencode,1,8,12 +Harbor multi-harness,500,claude-code,5,12,85 +Harbor multi-harness,500,codex,1,0,62 +Harbor multi-harness,500,mini-swe-agent,0,0,13 +Harbor multi-harness,500,opencode,3,8,26 +Harbor multi-harness,600,claude-code,5,13,91 +Harbor multi-harness,600,codex,7,0,104 +Harbor multi-harness,600,mini-swe-agent,0,0,43 +Harbor multi-harness,600,opencode,15,7,46 +Harbor multi-harness,684,claude-code,6,21,73 +Harbor multi-harness,684,codex,7,0,89 +Harbor multi-harness,684,mini-swe-agent,0,0,43 +Harbor multi-harness,684,opencode,16,7,28 +Harbor multi-harness,700,claude-code,12,24,79 +Harbor multi-harness,700,codex,3,0,69 +Harbor multi-harness,700,mini-swe-agent,0,0,59 +Harbor multi-harness,700,opencode,11,10,17 +Harbor multi-harness,800,claude-code,16,30,90 +Harbor multi-harness,800,codex,35,0,149 +Harbor multi-harness,800,mini-swe-agent,1,0,24 +Harbor multi-harness,800,opencode,30,9,45 +Harbor multi-harness,900,claude-code,132,27,0 +Harbor multi-harness,900,codex,201,0,203 +Harbor multi-harness,900,mini-swe-agent,47,0,108 +Harbor multi-harness,900,opencode,226,0,0 +Harbor multi-harness,1000,claude-code,132,25,0 +Harbor multi-harness,1000,codex,162,0,162 +Harbor multi-harness,1000,mini-swe-agent,58,0,107 +Harbor multi-harness,1000,opencode,204,1,0 +Native OpenCode,0,claude-code,2,9,33 +Native OpenCode,0,codex,4,0,25 +Native OpenCode,0,mini-swe-agent,1,0,48 +Native OpenCode,0,opencode,0,3,7 +Native OpenCode,100,claude-code,0,2,0 +Native OpenCode,100,codex,2,0,3 +Native OpenCode,100,mini-swe-agent,0,0,36 +Native OpenCode,100,opencode,0,2,0 +Native OpenCode,200,claude-code,6,4,0 +Native OpenCode,200,codex,4,0,7 +Native OpenCode,200,mini-swe-agent,0,0,54 +Native OpenCode,200,opencode,0,3,0 +Native OpenCode,300,claude-code,1,2,0 +Native OpenCode,300,codex,1,0,3 +Native OpenCode,300,mini-swe-agent,1,0,58 +Native OpenCode,300,opencode,0,0,0 +Native OpenCode,400,claude-code,4,1,0 +Native OpenCode,400,codex,1,0,1 +Native OpenCode,400,mini-swe-agent,0,0,82 +Native OpenCode,400,opencode,0,1,0 +Native OpenCode,500,claude-code,3,3,0 +Native OpenCode,500,codex,1,0,4 +Native OpenCode,500,mini-swe-agent,2,0,123 +Native OpenCode,500,opencode,0,0,0 +Native OpenCode,600,claude-code,2,4,0 +Native OpenCode,600,codex,1,0,4 +Native OpenCode,600,mini-swe-agent,1,0,175 +Native OpenCode,600,opencode,1,1,0 +Native OpenCode,700,claude-code,2,5,0 +Native OpenCode,700,codex,0,0,2 +Native OpenCode,700,mini-swe-agent,0,0,117 +Native OpenCode,700,opencode,0,0,0 +Native OpenCode,800,claude-code,4,2,0 +Native OpenCode,800,codex,1,0,1 +Native OpenCode,800,mini-swe-agent,0,0,94 +Native OpenCode,800,opencode,0,1,0 +Native OpenCode,900,claude-code,6,4,0 +Native OpenCode,900,codex,1,0,1 +Native OpenCode,900,mini-swe-agent,0,0,109 +Native OpenCode,900,opencode,0,0,0 +Native OpenCode,1000,claude-code,1,7,0 +Native OpenCode,1000,codex,0,0,0 +Native OpenCode,1000,mini-swe-agent,0,0,117 +Native OpenCode,1000,opencode,0,0,0 diff --git a/04-data-agent/reports/three-run-analysis-20260917/completion.png b/04-data-agent/reports/three-run-analysis-20260917/completion.png new file mode 100644 index 0000000..6240a27 Binary files /dev/null and b/04-data-agent/reports/three-run-analysis-20260917/completion.png differ diff --git a/04-data-agent/reports/three-run-analysis-20260917/evaluation_vs_training_exposure.csv b/04-data-agent/reports/three-run-analysis-20260917/evaluation_vs_training_exposure.csv new file mode 100644 index 0000000..8b00f3d --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/evaluation_vs_training_exposure.csv @@ -0,0 +1,35 @@ +run,step,score,supervised_tokens,forwarded_tokens +Harbor multi-harness,0,0.146,0.0,0.0 +Harbor multi-harness,100,0.248,2341485.0,187688023.0 +Harbor multi-harness,200,0.263,3751080.0,288770381.0 +Harbor multi-harness,300,0.286,5083236.0,389874360.0 +Harbor multi-harness,400,0.333,6392092.0,474480006.0 +Harbor multi-harness,500,0.37,8194853.0,564646274.0 +Harbor multi-harness,600,0.318,9879725.0,650324146.0 +Harbor multi-harness,684,0.321,11257452.0,722450575.0 +Harbor multi-harness,700,0.288,11548209.0,736700946.0 +Harbor multi-harness,800,0.27,13319433.0,824426929.0 +Harbor multi-harness,900,0.227,17550674.0,922803659.0 +Harbor multi-harness,1000,0.263,22148436.0,1001322856.0 +Native OpenCode,0,0.159,0.0,0.0 +Native OpenCode,100,0.197,412431.0,28972901.0 +Native OpenCode,200,0.221,951366.0,59911629.0 +Native OpenCode,300,0.216,1695366.0,83719905.0 +Native OpenCode,400,0.264,2489783.0,98136103.0 +Native OpenCode,500,0.231,3086548.0,111429542.0 +Native OpenCode,600,0.251,3534140.0,131234844.0 +Native OpenCode,700,0.232,3982021.0,146777166.0 +Native OpenCode,800,0.256,4421519.0,162968313.0 +Native OpenCode,900,0.253,4895255.0,185471906.0 +Native OpenCode,1000,0.298,5327227.0,228413083.0 +Harbor OpenCode-only,0,0.146,0.0,0.0 +Harbor OpenCode-only,100,0.245,900139.0,22391797.0 +Harbor OpenCode-only,200,0.271,4248088.0,61601074.0 +Harbor OpenCode-only,300,0.285,6397309.0,94738314.0 +Harbor OpenCode-only,400,0.328,7451497.0,121807354.0 +Harbor OpenCode-only,500,0.33,8682354.0,154599334.0 +Harbor OpenCode-only,600,0.33,9761777.0,187407303.0 +Harbor OpenCode-only,700,0.395,10653106.0,231011537.0 +Harbor OpenCode-only,800,0.331,11935793.0,293584910.0 +Harbor OpenCode-only,900,0.296,13158260.0,356542913.0 +Harbor OpenCode-only,1000,0.264,14625038.0,419428466.0 diff --git a/04-data-agent/reports/three-run-analysis-20260917/harness_complementarity.csv b/04-data-agent/reports/three-run-analysis-20260917/harness_complementarity.csv new file mode 100644 index 0000000..cf11f17 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/harness_complementarity.csv @@ -0,0 +1,35 @@ +run,step,solved_by_any,solved_by_all,solved_by_none,oracle_any_rate +Harbor OpenCode-only,0,76,7,174,0.304 +Harbor OpenCode-only,100,114,20,136,0.456 +Harbor OpenCode-only,200,116,24,134,0.464 +Harbor OpenCode-only,300,113,31,137,0.452 +Harbor OpenCode-only,400,130,40,120,0.52 +Harbor OpenCode-only,500,121,40,129,0.484 +Harbor OpenCode-only,600,123,35,127,0.492 +Harbor OpenCode-only,700,149,50,101,0.596 +Harbor OpenCode-only,800,141,25,109,0.564 +Harbor OpenCode-only,900,128,24,122,0.512 +Harbor OpenCode-only,1000,119,26,131,0.476 +Harbor multi-harness,0,76,7,174,0.304 +Harbor multi-harness,100,117,17,133,0.468 +Harbor multi-harness,200,129,16,121,0.516 +Harbor multi-harness,300,127,21,123,0.508 +Harbor multi-harness,400,127,35,123,0.508 +Harbor multi-harness,500,141,45,109,0.564 +Harbor multi-harness,600,124,35,126,0.496 +Harbor multi-harness,684,124,32,126,0.496 +Harbor multi-harness,700,122,24,128,0.488 +Harbor multi-harness,800,107,25,143,0.428 +Harbor multi-harness,900,117,1,133,0.468 +Harbor multi-harness,1000,129,7,121,0.516 +Native OpenCode,0,95,3,155,0.38 +Native OpenCode,100,97,13,153,0.388 +Native OpenCode,200,99,19,151,0.396 +Native OpenCode,300,103,15,147,0.412 +Native OpenCode,400,120,21,130,0.48 +Native OpenCode,500,109,13,141,0.436 +Native OpenCode,600,116,17,134,0.464 +Native OpenCode,700,111,18,139,0.444 +Native OpenCode,800,122,11,128,0.488 +Native OpenCode,900,128,11,122,0.512 +Native OpenCode,1000,134,25,116,0.536 diff --git a/04-data-agent/reports/three-run-analysis-20260917/overall.csv b/04-data-agent/reports/three-run-analysis-20260917/overall.csv new file mode 100644 index 0000000..ef9be67 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/overall.csv @@ -0,0 +1,35 @@ +run,step,cells,score,model_calls,tool_calls,tool_calls_median,tool_calls_p90,repeats,repeat_fraction,repeat_rollout_fraction,short_tool_fraction,budget_fraction,python_fraction,answer_path_fraction,tool_error_text_fraction,submission_fraction,submission_observations,trainable_tokens,wall_s,retry_cells,training_rows,packed_tokens +Harbor OpenCode-only,0,1000,0.146,14.149,13.264,16.0,18.0,0.522,0.03575284067129578,0.285,0.066,0.602,0.779,0.38,0.618,0.4011627906976744,344,2721.986,273.92238000000003,11,4.122,143490.928 +Harbor OpenCode-only,100,1000,0.245,15.747,16.131,17.0,18.0,2.457,0.1476399107830088,0.62,0.0,0.82,0.967,0.537,0.714,0.5261627906976745,344,3049.281,266.45046,16,4.983,177707.37 +Harbor OpenCode-only,200,1000,0.271,16.046,16.63,17.0,19.0,1.699,0.10171712437576555,0.561,0.001,0.836,0.955,0.474,0.712,0.5174418604651163,344,3439.912,276.93613,8,5.109,182544.215 +Harbor OpenCode-only,300,1000,0.285,16.205,17.998,17.0,21.0,1.634,0.09349848499143593,0.56,0.003,0.864,0.972,0.421,0.699,0.44476744186046513,344,3382.215,293.72954,4,5.147,190025.617 +Harbor OpenCode-only,400,1000,0.328,16.162,18.168,18.0,21.0,2.026,0.10955959418379882,0.597,0.002,0.862,0.982,0.492,0.669,0.5261627906976745,344,2927.045,276.08891,5,5.24,184854.492 +Harbor OpenCode-only,500,1000,0.33,15.947,16.714,17.0,19.0,1.567,0.09457921511064785,0.521,0.002,0.822,0.991,0.494,0.685,0.5174418604651163,344,3410.234,291.9687,5,5.127,181689.72 +Harbor OpenCode-only,600,1000,0.33,15.815,16.986,17.0,20.0,2.063,0.12061561433857773,0.585,0.0,0.785,0.973,0.548,0.629,0.561046511627907,344,2640.898,275.99777,1,5.217,183274.1 +Harbor OpenCode-only,700,1000,0.395,14.451,16.62,17.0,22.0,2.092,0.12056283788646273,0.628,0.001,0.564,0.985,0.642,0.645,0.688953488372093,344,2909.286,276.39668,4,5.615,186714.849 +Harbor OpenCode-only,800,1000,0.331,13.89,13.97,16.0,18.0,1.075,0.0798111487259394,0.55,0.011,0.513,0.925,0.575,0.602,0.6162790697674418,344,3265.515,272.20868,20,6.168,194153.262 +Harbor OpenCode-only,900,1000,0.296,14.627,16.314,17.0,22.0,1.886,0.12036157527128212,0.68,0.001,0.612,0.927,0.549,0.649,0.4622093023255814,344,3135.778,293.36412,3,6.119,205588.621 +Harbor OpenCode-only,1000,1000,0.264,15.271,20.966,20.0,30.0,3.025,0.12659647516265488,0.766,0.0,0.702,0.945,0.562,0.712,0.4069767441860465,344,3403.287,293.54446,2,5.382,182515.884 +Harbor multi-harness,0,1000,0.146,14.149,13.264,16.0,18.0,0.522,0.03575284067129578,0.285,0.066,0.602,0.779,0.38,0.618,0.4011627906976744,344,2721.986,273.92238000000003,11,4.122,143490.928 +Harbor multi-harness,100,1000,0.248,16.341,15.938,17.0,17.0,2.769,0.1713903229938127,0.604,0.004,0.873,0.868,0.443,0.64,0.4680232558139535,344,2855.774,277.7602,44,5.013,184767.212 +Harbor multi-harness,200,1000,0.263,16.598,16.138,17.0,17.0,3.057,0.1868986447575995,0.636,0.003,0.914,0.902,0.451,0.651,0.49127906976744184,344,2744.07,267.66835,76,5.043,179096.311 +Harbor multi-harness,300,1000,0.286,16.397,15.987,17.0,17.0,3.646,0.2257868151714127,0.713,0.003,0.885,0.845,0.496,0.609,0.5203488372093024,344,2251.678,248.93432,29,4.977,174517.782 +Harbor multi-harness,400,1000,0.333,16.604,16.153,17.0,17.0,3.587,0.21946066816310625,0.698,0.002,0.913,0.869,0.474,0.634,0.5232558139534884,344,2761.828,271.93884,44,5.045,185725.598 +Harbor multi-harness,500,1000,0.37,16.061,15.582,16.0,17.0,3.464,0.2124553623921658,0.651,0.0,0.839,0.86,0.515,0.646,0.5494186046511628,344,2870.289,275.34286,65,5.094,184783.506 +Harbor multi-harness,600,1000,0.318,16.054,15.451,16.0,17.0,3.508,0.21791239233332885,0.741,0.002,0.806,0.799,0.473,0.569,0.5203488372093024,344,3359.023,289.55251,83,5.158,187366.857 +Harbor multi-harness,684,1000,0.321,15.593,15.009,16.0,17.0,3.92,0.2478296437781345,0.795,0.003,0.756,0.794,0.497,0.567,0.5319767441860465,344,2895.237,275.87375,65,5.003,194585.147 +Harbor multi-harness,700,1000,0.288,15.441,14.897,16.0,17.0,3.674,0.23182913666570787,0.798,0.009,0.748,0.743,0.459,0.536,0.5,344,2947.094,273.19223,62,4.94,191219.959 +Harbor multi-harness,800,1000,0.27,15.929,15.209,16.0,17.0,3.677,0.2331952162474608,0.842,0.009,0.759,0.741,0.365,0.512,0.39244186046511625,344,3730.791,325.84463,107,4.95,190314.026 +Harbor multi-harness,900,1000,0.227,10.633,9.646,10.0,17.0,2.341,0.15756903361464808,0.489,0.252,0.392,0.554,0.319,0.369,0.34593023255813954,344,5306.9,323.67207,149,4.941,186258.408 +Harbor multi-harness,1000,1000,0.263,10.12,9.018,9.0,17.0,2.204,0.16422693790987133,0.507,0.221,0.28,0.596,0.402,0.412,0.4127906976744186,344,5206.881,304.29470000000003,205,4.452,153613.721 +Native OpenCode,0,1000,0.159,14.258,14.068,16.0,18.0,0.658,0.04315955169345195,0.35,0.008,0.62,0.829,0.413,0.659,0.4418604651162791,344,2655.455,259.36484,830,4.23,150697.117 +Native OpenCode,100,1000,0.197,13.802,13.418,16.0,17.0,0.49,0.03143585735984547,0.289,0.006,0.579,0.917,0.488,0.686,0.5116279069767442,344,2845.774,134.06311,1,4.361,155000.771 +Native OpenCode,200,1000,0.221,13.132,12.636,16.0,17.0,0.51,0.0332330306530513,0.257,0.004,0.528,0.948,0.549,0.709,0.5726744186046512,344,2571.95,127.30409,0,4.274,146781.013 +Native OpenCode,300,1000,0.216,13.889,13.369,16.0,17.0,0.534,0.035410580252221116,0.309,0.001,0.598,0.959,0.47,0.722,0.45930232558139533,344,2847.512,127.70825,6,4.23,143617.756 +Native OpenCode,400,1000,0.264,13.671,13.066,16.0,17.0,0.545,0.035270807864306315,0.298,0.001,0.568,0.965,0.513,0.738,0.5523255813953488,344,2786.585,119.62226,0,4.256,129501.653 +Native OpenCode,500,1000,0.231,13.411,12.561,16.0,17.0,0.83,0.05693932569838142,0.354,0.005,0.543,0.97,0.507,0.686,0.5348837209302325,344,2463.056,125.81416,3,4.536,138071.582 +Native OpenCode,600,1000,0.251,12.847,11.82,14.0,17.0,1.184,0.0825353855552772,0.418,0.008,0.482,0.954,0.581,0.662,0.5901162790697675,344,2473.208,121.3107,14,4.655,134171.096 +Native OpenCode,700,1000,0.232,12.83,11.866,14.0,17.0,0.736,0.052030067820895996,0.335,0.009,0.509,0.969,0.604,0.673,0.5930232558139535,344,2390.413,120.96332000000001,0,4.277,130972.534 +Native OpenCode,800,1000,0.256,12.585,11.721,14.0,17.0,0.414,0.029572952142456786,0.252,0.011,0.474,0.974,0.611,0.692,0.627906976744186,344,2801.693,124.11447,0,4.196,136308.872 +Native OpenCode,900,1000,0.253,12.439,11.53,13.0,17.0,0.498,0.03663204338231428,0.277,0.023,0.459,0.953,0.597,0.675,0.6191860465116279,344,2755.981,122.3318,3,4.097,136371.959 +Native OpenCode,1000,1000,0.298,12.857,11.968,13.0,17.0,0.772,0.06054240726164101,0.385,0.004,0.459,0.979,0.704,0.754,0.7122093023255814,344,2559.758,126.75898,0,6.893,185409.144 diff --git a/04-data-agent/reports/three-run-analysis-20260917/paired_differences.csv b/04-data-agent/reports/three-run-analysis-20260917/paired_differences.csv new file mode 100644 index 0000000..450ce60 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/paired_differences.csv @@ -0,0 +1,7 @@ +comparison,delta_pp,ci95_low_pp,ci95_high_pp,lost_correct_cells,gained_correct_cells +Harbor multi-harness: baseline to selected peak,22.400000000000002,18.6,26.3,27,251 +Harbor multi-harness: selected peak to final,-10.7,-14.099999999999998,-7.3999999999999995,174,67 +Native OpenCode: baseline to selected peak,13.900000000000002,11.200000000000001,16.8,47,186 +Harbor OpenCode-only: baseline to selected peak,24.9,21.099999999999998,28.799999999999997,23,272 +Harbor OpenCode-only: selected peak to final,-13.100000000000001,-16.6,-9.700000000000001,190,59 +Multi-harness 500 to OpenCode-only 700,2.5,-0.6,5.7,94,119 diff --git a/04-data-agent/reports/three-run-analysis-20260917/summarize_training.py b/04-data-agent/reports/three-run-analysis-20260917/summarize_training.py new file mode 100644 index 0000000..6bef835 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/summarize_training.py @@ -0,0 +1,134 @@ +"""Build training diagnostics from extracted captures and optimizer-step telemetry.""" + +import argparse +from pathlib import Path + +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + + +RUNS = ["Harbor multi-harness", "Native OpenCode", "Harbor OpenCode-only"] +COLORS = ["#4f46e5", "#e85c41", "#059669"] + + +def summarize(frame, keys): + result = frame.groupby(keys).agg( + admitted_rollouts=("rollout_id", "size"), + unique_tasks=("task_index", "nunique"), + binary_reward_mean=("binary_reward", "mean"), + completion_tokens_mean=("completion_tokens", "mean"), + completion_tokens_median=("completion_tokens", "median"), + completion_tokens_p90=("completion_tokens", lambda x: x.quantile(.9)), + supervised_tokens=("supervised_tokens", "sum"), + completion_tokens=("completion_tokens", "sum"), + text_only_turn_tokens=("completion_tokens_in_text_only_turns", "sum"), + agent_turns_mean=("agent_turns", "mean"), + tool_calls_mean=("emitted_tool_calls", "mean"), + tool_calls_p90=("emitted_tool_calls", lambda x: x.quantile(.9)), + exact_repeated_calls_mean=("exact_repeated_calls", "mean"), + rollout_over4096_fraction=("has_response_over4096", "mean"), + rollout_finish_length_fraction=("has_finish_length", "mean"), + training_rows=("rows", "sum"), + receipt_token_mismatches=("receipt_token_delta", lambda x: (x != 0).sum()), + ).reset_index() + result["text_only_turn_token_share"] = result.text_only_turn_tokens / result.completion_tokens + result["rows_per_rollout"] = result.training_rows / result.admitted_rollouts + return result + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--evidence", type=Path, required=True) + parser.add_argument("--output", type=Path, default=Path(__file__).resolve().parent) + args = parser.parse_args() + output = args.output + output.mkdir(parents=True, exist_ok=True) + captures = pd.read_csv(args.evidence / "admitted_training_behavior.csv") + steps = pd.read_csv(args.evidence / "training_metrics.csv") + for frame in (captures, steps): + frame["window_end"] = ((frame.step - 1) // 100 + 1) * 100 + assert not captures.duplicated(["run", "rollout_id"]).any() + assert len(steps) == 3000 and not steps.duplicated(["run", "step"]).any() + assert np.isfinite(steps.grad_norm).all() + captures["has_response_over4096"] = captures.responses_over_4096 > 0 + captures["has_finish_length"] = captures.responses_finish_length > 0 + # Admission receipts cover steps 31 onward for multi-harness, every step otherwise. + for run, cohort in captures.groupby("run"): + first = 31 if run == "Harbor multi-harness" else 1 + recorded = steps[(steps.run == run) & (steps.step >= first)] + assert cohort.supervised_tokens.sum() == recorded["batch/trained_tokens_per_step"].sum() + for name, keys in [ + ("training_rollout_totals", ["run"]), + ("training_behavior_windows", ["run", "window_end"]), + ("training_behavior_by_harness", ["run", "harness", "window_end"]), + ("training_behavior_by_outcome", ["run", "window_end", "binary_reward"]), + ]: + summarize(captures, keys).to_csv(output / f"{name}.csv", index=False) + windows = summarize(captures, ["run", "window_end"]) + steps["zero_gradient"] = steps.grad_norm == 0 + assert (steps.zero_gradient == (steps.reward_std == 0)).all() + step_keys = ["reward", "reward_std", "tools/call_frequency", "tools/failure_frequency", + "rollout/turns_mean", "rollout/fork_frac", "rollout/samples_per_rollout", + "completions/mean_length", "completions/clipped_ratio", "zero_gradient", + "perf/step_s", "perf/fwd_bwd_s", "perf/rollout_wait_s"] + telemetry = steps.groupby(["run", "window_end"])[step_keys].mean().reset_index() + totals = steps.groupby(["run", "window_end"])[["batch/forwarded_tokens_per_step", "batch/trained_tokens_per_step"]].sum().reset_index() + telemetry = telemetry.merge(totals, on=["run", "window_end"], validate="one_to_one") + telemetry["forwarded_per_supervised_token"] = telemetry["batch/forwarded_tokens_per_step"] / telemetry["batch/trained_tokens_per_step"] + telemetry.to_csv(output / "training_step_diagnostics.csv", index=False) + accounting = [] + for run, group in steps.groupby("run"): + forwarded = group["batch/forwarded_tokens_per_step"] + supervised = group["batch/trained_tokens_per_step"] + zero = group.zero_gradient + accounting.append({ + "run": run, "steps": len(group), "zero_gradient_steps": int(zero.sum()), + "forwarded_tokens": forwarded.sum(), "supervised_tokens": supervised.sum(), + "forwarded_per_supervised_token": forwarded.sum() / supervised.sum(), + "supervised_tokens_in_zero_gradient_steps": supervised[zero].sum(), + "zero_gradient_supervised_token_fraction": supervised[zero].sum() / supervised.sum(), + "zero_gradient_forwarded_token_fraction": forwarded[zero].sum() / forwarded.sum(), + "mean_step_s": group["perf/step_s"].mean(), + "step_timing_observations": group["perf/step_s"].count(), + "receipt_token_mismatches": int((captures.loc[captures.run == run, "receipt_token_delta"] != 0).sum()), + }) + pd.DataFrame(accounting).to_csv(output / "training_accounting.csv", index=False) + + plt.rcParams.update({"font.family": "DejaVu Sans", "font.size": 10, + "axes.spines.top": False, "axes.spines.right": False}) + fig, axes = plt.subplots(2, 3, figsize=(15, 9)) + for run, color in zip(RUNS, COLORS): + w = windows[windows.run == run].sort_values("window_end") + t = telemetry[telemetry.run == run].sort_values("window_end") + options = dict(color=color, marker="o", markersize=4, label=run) + axes[0, 0].plot(w.window_end, w.completion_tokens_mean, **options) + axes[0, 1].plot(w.window_end, w.tool_calls_mean, **options) + axes[0, 2].plot(w.window_end, 100 * w.text_only_turn_token_share, **options) + axes[1, 0].plot(w.window_end, 100 * w.rollout_over4096_fraction, **options) + axes[1, 1].plot(t.window_end, t.forwarded_per_supervised_token, **options) + axes[1, 2].plot(t.window_end, 100 * t.zero_gradient, **options) + labels = ["Mean completion tokens / admitted rollout", "Mean emitted tool calls / admitted rollout", + "Completion tokens in text-only turns (%)", "Admitted rollouts with a response >4,096 tokens (%)", + "Forwarded / supervised tokens", "Steps with zero fresh gradient (%)"] + for ax, label in zip(axes.flat, labels): + ax.set_title(label, fontsize=11, pad=10) + ax.grid(alpha=.18) + ax.set_ylim(bottom=0) + ax.set_xlabel("End of 100-step window") + handles, labels = axes[0, 0].get_legend_handles_labels() + fig.legend(handles, labels, loc="upper center", bbox_to_anchor=(.5, .955), ncol=3, frameon=False) + fig.suptitle("Training behavior, token exposure and available learning signal", fontsize=19, y=.995) + fig.text(.05, .025, "Captured turns from 13,625 admitted rollouts; multi-harness receipts start at step 31. Bottom-right panels use all 3,000 step records.\nTool calls are emitted requests, not confirmed executions. Text-only includes reasoning and final answers. Zero fresh gradient does not rule out optimizer momentum.", fontsize=9, color="#475569") + fig.tight_layout(rect=(0, .075, 1, .90)) + fig.savefig(output / "training_diagnostics.png", dpi=180) + fig.savefig(output / "training_diagnostics.pdf") + plt.close(fig) + print(windows[(windows.window_end.isin([500, 700, 1000]))].to_string(index=False)) + print(pd.DataFrame(accounting).to_string(index=False)) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/reports/three-run-analysis-20260917/tool_call_bins.csv b/04-data-agent/reports/three-run-analysis-20260917/tool_call_bins.csv new file mode 100644 index 0000000..a3b7ac7 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/tool_call_bins.csv @@ -0,0 +1,510 @@ +run,step,harness,tool_call_bin,cells,score +Harbor OpenCode-only,0,claude-code,0–2,8,0.25 +Harbor OpenCode-only,0,claude-code,3–8,63,0.42857142857142855 +Harbor OpenCode-only,0,claude-code,9–16,82,0.13414634146341464 +Harbor OpenCode-only,0,claude-code,17–24,97,0.020618556701030927 +Harbor OpenCode-only,0,codex,0–2,20,0.2 +Harbor OpenCode-only,0,codex,3–8,33,0.48484848484848486 +Harbor OpenCode-only,0,codex,9–16,54,0.24074074074074073 +Harbor OpenCode-only,0,codex,17–24,137,0.058394160583941604 +Harbor OpenCode-only,0,codex,25+,6,0.0 +Harbor OpenCode-only,0,mini-swe-agent,0–2,10,0.2 +Harbor OpenCode-only,0,mini-swe-agent,3–8,3,0.3333333333333333 +Harbor OpenCode-only,0,mini-swe-agent,9–16,68,0.4411764705882353 +Harbor OpenCode-only,0,mini-swe-agent,17–24,169,0.01775147928994083 +Harbor OpenCode-only,0,opencode,0–2,28,0.14285714285714285 +Harbor OpenCode-only,0,opencode,3–8,66,0.19696969696969696 +Harbor OpenCode-only,0,opencode,9–16,112,0.08035714285714286 +Harbor OpenCode-only,0,opencode,17–24,44,0.022727272727272728 +Harbor OpenCode-only,100,claude-code,3–8,22,0.5454545454545454 +Harbor OpenCode-only,100,claude-code,9–16,45,0.28888888888888886 +Harbor OpenCode-only,100,claude-code,17–24,178,0.21348314606741572 +Harbor OpenCode-only,100,claude-code,25+,5,0.0 +Harbor OpenCode-only,100,codex,3–8,25,0.56 +Harbor OpenCode-only,100,codex,9–16,26,0.5384615384615384 +Harbor OpenCode-only,100,codex,17–24,189,0.24867724867724866 +Harbor OpenCode-only,100,codex,25+,10,0.1 +Harbor OpenCode-only,100,mini-swe-agent,3–8,21,0.7619047619047619 +Harbor OpenCode-only,100,mini-swe-agent,9–16,67,0.3880597014925373 +Harbor OpenCode-only,100,mini-swe-agent,17–24,162,0.05555555555555555 +Harbor OpenCode-only,100,opencode,3–8,1,0.0 +Harbor OpenCode-only,100,opencode,9–16,36,0.2222222222222222 +Harbor OpenCode-only,100,opencode,17–24,213,0.22065727699530516 +Harbor OpenCode-only,200,claude-code,0–2,1,0.0 +Harbor OpenCode-only,200,claude-code,3–8,17,0.4117647058823529 +Harbor OpenCode-only,200,claude-code,9–16,44,0.5227272727272727 +Harbor OpenCode-only,200,claude-code,17–24,184,0.22282608695652173 +Harbor OpenCode-only,200,claude-code,25+,4,0.0 +Harbor OpenCode-only,200,codex,3–8,6,0.8333333333333334 +Harbor OpenCode-only,200,codex,9–16,40,0.725 +Harbor OpenCode-only,200,codex,17–24,197,0.2182741116751269 +Harbor OpenCode-only,200,codex,25+,7,0.0 +Harbor OpenCode-only,200,mini-swe-agent,3–8,9,0.8888888888888888 +Harbor OpenCode-only,200,mini-swe-agent,9–16,57,0.49122807017543857 +Harbor OpenCode-only,200,mini-swe-agent,17–24,184,0.07608695652173914 +Harbor OpenCode-only,200,opencode,3–8,2,0.0 +Harbor OpenCode-only,200,opencode,9–16,8,0.5 +Harbor OpenCode-only,200,opencode,17–24,240,0.2875 +Harbor OpenCode-only,300,claude-code,0–2,3,0.0 +Harbor OpenCode-only,300,claude-code,3–8,9,0.3333333333333333 +Harbor OpenCode-only,300,claude-code,9–16,34,0.5882352941176471 +Harbor OpenCode-only,300,claude-code,17–24,198,0.2676767676767677 +Harbor OpenCode-only,300,claude-code,25+,6,0.0 +Harbor OpenCode-only,300,codex,3–8,5,0.8 +Harbor OpenCode-only,300,codex,9–16,27,0.5925925925925926 +Harbor OpenCode-only,300,codex,17–24,209,0.2822966507177033 +Harbor OpenCode-only,300,codex,25+,9,0.1111111111111111 +Harbor OpenCode-only,300,mini-swe-agent,3–8,4,0.75 +Harbor OpenCode-only,300,mini-swe-agent,9–16,52,0.7692307692307693 +Harbor OpenCode-only,300,mini-swe-agent,17–24,194,0.08247422680412371 +Harbor OpenCode-only,300,opencode,3–8,3,0.0 +Harbor OpenCode-only,300,opencode,9–16,7,0.14285714285714285 +Harbor OpenCode-only,300,opencode,17–24,219,0.3059360730593607 +Harbor OpenCode-only,300,opencode,25+,21,0.09523809523809523 +Harbor OpenCode-only,400,claude-code,0–2,2,0.0 +Harbor OpenCode-only,400,claude-code,3–8,10,0.5 +Harbor OpenCode-only,400,claude-code,9–16,25,0.56 +Harbor OpenCode-only,400,claude-code,17–24,204,0.3137254901960784 +Harbor OpenCode-only,400,claude-code,25+,9,0.1111111111111111 +Harbor OpenCode-only,400,codex,3–8,5,0.8 +Harbor OpenCode-only,400,codex,9–16,33,0.5454545454545454 +Harbor OpenCode-only,400,codex,17–24,190,0.32105263157894737 +Harbor OpenCode-only,400,codex,25+,22,0.0 +Harbor OpenCode-only,400,mini-swe-agent,3–8,7,0.7142857142857143 +Harbor OpenCode-only,400,mini-swe-agent,9–16,54,0.6666666666666666 +Harbor OpenCode-only,400,mini-swe-agent,17–24,189,0.164021164021164 +Harbor OpenCode-only,400,opencode,3–8,1,0.0 +Harbor OpenCode-only,400,opencode,9–16,3,0.0 +Harbor OpenCode-only,400,opencode,17–24,223,0.39461883408071746 +Harbor OpenCode-only,400,opencode,25+,23,0.043478260869565216 +Harbor OpenCode-only,500,claude-code,0–2,2,0.0 +Harbor OpenCode-only,500,claude-code,3–8,18,0.6666666666666666 +Harbor OpenCode-only,500,claude-code,9–16,45,0.6666666666666666 +Harbor OpenCode-only,500,claude-code,17–24,182,0.21428571428571427 +Harbor OpenCode-only,500,claude-code,25+,3,0.0 +Harbor OpenCode-only,500,codex,3–8,15,0.6666666666666666 +Harbor OpenCode-only,500,codex,9–16,31,0.7096774193548387 +Harbor OpenCode-only,500,codex,17–24,197,0.30456852791878175 +Harbor OpenCode-only,500,codex,25+,7,0.14285714285714285 +Harbor OpenCode-only,500,mini-swe-agent,3–8,6,0.6666666666666666 +Harbor OpenCode-only,500,mini-swe-agent,9–16,64,0.78125 +Harbor OpenCode-only,500,mini-swe-agent,17–24,180,0.1111111111111111 +Harbor OpenCode-only,500,opencode,3–8,4,0.0 +Harbor OpenCode-only,500,opencode,9–16,3,0.0 +Harbor OpenCode-only,500,opencode,17–24,233,0.34334763948497854 +Harbor OpenCode-only,500,opencode,25+,10,0.2 +Harbor OpenCode-only,600,claude-code,3–8,16,0.5625 +Harbor OpenCode-only,600,claude-code,9–16,56,0.5178571428571429 +Harbor OpenCode-only,600,claude-code,17–24,172,0.22674418604651161 +Harbor OpenCode-only,600,claude-code,25+,6,0.16666666666666666 +Harbor OpenCode-only,600,codex,3–8,12,0.6666666666666666 +Harbor OpenCode-only,600,codex,9–16,37,0.6756756756756757 +Harbor OpenCode-only,600,codex,17–24,190,0.2789473684210526 +Harbor OpenCode-only,600,codex,25+,11,0.0 +Harbor OpenCode-only,600,mini-swe-agent,3–8,10,0.9 +Harbor OpenCode-only,600,mini-swe-agent,9–16,77,0.6883116883116883 +Harbor OpenCode-only,600,mini-swe-agent,17–24,163,0.09202453987730061 +Harbor OpenCode-only,600,opencode,3–8,3,0.0 +Harbor OpenCode-only,600,opencode,9–16,8,0.5 +Harbor OpenCode-only,600,opencode,17–24,227,0.3744493392070485 +Harbor OpenCode-only,600,opencode,25+,12,0.0 +Harbor OpenCode-only,700,claude-code,0–2,1,0.0 +Harbor OpenCode-only,700,claude-code,3–8,42,0.6428571428571429 +Harbor OpenCode-only,700,claude-code,9–16,89,0.7078651685393258 +Harbor OpenCode-only,700,claude-code,17–24,111,0.22522522522522523 +Harbor OpenCode-only,700,claude-code,25+,7,0.14285714285714285 +Harbor OpenCode-only,700,codex,3–8,32,0.6875 +Harbor OpenCode-only,700,codex,9–16,84,0.5833333333333334 +Harbor OpenCode-only,700,codex,17–24,115,0.1826086956521739 +Harbor OpenCode-only,700,codex,25+,19,0.05263157894736842 +Harbor OpenCode-only,700,mini-swe-agent,3–8,8,0.875 +Harbor OpenCode-only,700,mini-swe-agent,9–16,88,0.7045454545454546 +Harbor OpenCode-only,700,mini-swe-agent,17–24,152,0.10526315789473684 +Harbor OpenCode-only,700,mini-swe-agent,25+,2,0.5 +Harbor OpenCode-only,700,opencode,3–8,2,0.0 +Harbor OpenCode-only,700,opencode,9–16,62,0.6612903225806451 +Harbor OpenCode-only,700,opencode,17–24,149,0.33557046979865773 +Harbor OpenCode-only,700,opencode,25+,37,0.24324324324324326 +Harbor OpenCode-only,800,claude-code,3–8,70,0.6142857142857143 +Harbor OpenCode-only,800,claude-code,9–16,87,0.5057471264367817 +Harbor OpenCode-only,800,claude-code,17–24,93,0.10752688172043011 +Harbor OpenCode-only,800,codex,0–2,1,0.0 +Harbor OpenCode-only,800,codex,3–8,27,0.5185185185185185 +Harbor OpenCode-only,800,codex,9–16,110,0.5272727272727272 +Harbor OpenCode-only,800,codex,17–24,109,0.1559633027522936 +Harbor OpenCode-only,800,codex,25+,3,0.0 +Harbor OpenCode-only,800,mini-swe-agent,3–8,5,0.8 +Harbor OpenCode-only,800,mini-swe-agent,9–16,99,0.6565656565656566 +Harbor OpenCode-only,800,mini-swe-agent,17–24,146,0.0684931506849315 +Harbor OpenCode-only,800,opencode,0–2,10,0.0 +Harbor OpenCode-only,800,opencode,3–8,34,0.11764705882352941 +Harbor OpenCode-only,800,opencode,9–16,82,0.6219512195121951 +Harbor OpenCode-only,800,opencode,17–24,124,0.08870967741935484 +Harbor OpenCode-only,900,claude-code,0–2,1,0.0 +Harbor OpenCode-only,900,claude-code,3–8,36,0.5555555555555556 +Harbor OpenCode-only,900,claude-code,9–16,88,0.6590909090909091 +Harbor OpenCode-only,900,claude-code,17–24,121,0.08264462809917356 +Harbor OpenCode-only,900,claude-code,25+,4,0.0 +Harbor OpenCode-only,900,codex,3–8,16,0.3125 +Harbor OpenCode-only,900,codex,9–16,93,0.5376344086021505 +Harbor OpenCode-only,900,codex,17–24,126,0.09523809523809523 +Harbor OpenCode-only,900,codex,25+,15,0.06666666666666667 +Harbor OpenCode-only,900,mini-swe-agent,3–8,2,1.0 +Harbor OpenCode-only,900,mini-swe-agent,9–16,87,0.632183908045977 +Harbor OpenCode-only,900,mini-swe-agent,17–24,160,0.05 +Harbor OpenCode-only,900,mini-swe-agent,25+,1,0.0 +Harbor OpenCode-only,900,opencode,3–8,25,0.0 +Harbor OpenCode-only,900,opencode,9–16,47,0.6170212765957447 +Harbor OpenCode-only,900,opencode,17–24,152,0.3026315789473684 +Harbor OpenCode-only,900,opencode,25+,26,0.0 +Harbor OpenCode-only,1000,claude-code,3–8,19,0.631578947368421 +Harbor OpenCode-only,1000,claude-code,9–16,60,0.6666666666666666 +Harbor OpenCode-only,1000,claude-code,17–24,129,0.18604651162790697 +Harbor OpenCode-only,1000,claude-code,25+,42,0.023809523809523808 +Harbor OpenCode-only,1000,codex,3–8,12,0.75 +Harbor OpenCode-only,1000,codex,9–16,64,0.46875 +Harbor OpenCode-only,1000,codex,17–24,115,0.30434782608695654 +Harbor OpenCode-only,1000,codex,25+,59,0.11864406779661017 +Harbor OpenCode-only,1000,mini-swe-agent,9–16,36,0.8055555555555556 +Harbor OpenCode-only,1000,mini-swe-agent,17–24,189,0.10052910052910052 +Harbor OpenCode-only,1000,mini-swe-agent,25+,25,0.12 +Harbor OpenCode-only,1000,opencode,3–8,1,0.0 +Harbor OpenCode-only,1000,opencode,9–16,19,0.631578947368421 +Harbor OpenCode-only,1000,opencode,17–24,96,0.2916666666666667 +Harbor OpenCode-only,1000,opencode,25+,134,0.11194029850746269 +Harbor multi-harness,0,claude-code,0–2,8,0.25 +Harbor multi-harness,0,claude-code,3–8,63,0.42857142857142855 +Harbor multi-harness,0,claude-code,9–16,82,0.13414634146341464 +Harbor multi-harness,0,claude-code,17–24,97,0.020618556701030927 +Harbor multi-harness,0,codex,0–2,20,0.2 +Harbor multi-harness,0,codex,3–8,33,0.48484848484848486 +Harbor multi-harness,0,codex,9–16,54,0.24074074074074073 +Harbor multi-harness,0,codex,17–24,137,0.058394160583941604 +Harbor multi-harness,0,codex,25+,6,0.0 +Harbor multi-harness,0,mini-swe-agent,0–2,10,0.2 +Harbor multi-harness,0,mini-swe-agent,3–8,3,0.3333333333333333 +Harbor multi-harness,0,mini-swe-agent,9–16,68,0.4411764705882353 +Harbor multi-harness,0,mini-swe-agent,17–24,169,0.01775147928994083 +Harbor multi-harness,0,opencode,0–2,28,0.14285714285714285 +Harbor multi-harness,0,opencode,3–8,66,0.19696969696969696 +Harbor multi-harness,0,opencode,9–16,112,0.08035714285714286 +Harbor multi-harness,0,opencode,17–24,44,0.022727272727272728 +Harbor multi-harness,100,claude-code,0–2,2,0.0 +Harbor multi-harness,100,claude-code,3–8,9,0.1111111111111111 +Harbor multi-harness,100,claude-code,9–16,104,0.27884615384615385 +Harbor multi-harness,100,claude-code,17–24,135,0.28888888888888886 +Harbor multi-harness,100,codex,3–8,5,0.4 +Harbor multi-harness,100,codex,9–16,67,0.1791044776119403 +Harbor multi-harness,100,codex,17–24,177,0.3163841807909605 +Harbor multi-harness,100,codex,25+,1,0.0 +Harbor multi-harness,100,mini-swe-agent,3–8,4,0.75 +Harbor multi-harness,100,mini-swe-agent,9–16,63,0.5238095238095238 +Harbor multi-harness,100,mini-swe-agent,17–24,183,0.06557377049180328 +Harbor multi-harness,100,opencode,0–2,2,0.0 +Harbor multi-harness,100,opencode,3–8,2,0.0 +Harbor multi-harness,100,opencode,9–16,224,0.2544642857142857 +Harbor multi-harness,100,opencode,17–24,22,0.18181818181818182 +Harbor multi-harness,200,claude-code,0–2,1,0.0 +Harbor multi-harness,200,claude-code,3–8,4,0.0 +Harbor multi-harness,200,claude-code,9–16,103,0.22330097087378642 +Harbor multi-harness,200,claude-code,17–24,142,0.36619718309859156 +Harbor multi-harness,200,codex,0–2,1,0.0 +Harbor multi-harness,200,codex,3–8,1,0.0 +Harbor multi-harness,200,codex,9–16,71,0.15492957746478872 +Harbor multi-harness,200,codex,17–24,177,0.3107344632768362 +Harbor multi-harness,200,mini-swe-agent,3–8,6,0.3333333333333333 +Harbor multi-harness,200,mini-swe-agent,9–16,38,0.5789473684210527 +Harbor multi-harness,200,mini-swe-agent,17–24,206,0.10679611650485436 +Harbor multi-harness,200,opencode,0–2,1,0.0 +Harbor multi-harness,200,opencode,3–8,3,0.0 +Harbor multi-harness,200,opencode,9–16,225,0.30666666666666664 +Harbor multi-harness,200,opencode,17–24,21,0.3333333333333333 +Harbor multi-harness,300,claude-code,0–2,3,0.0 +Harbor multi-harness,300,claude-code,3–8,8,0.125 +Harbor multi-harness,300,claude-code,9–16,84,0.2619047619047619 +Harbor multi-harness,300,claude-code,17–24,155,0.3870967741935484 +Harbor multi-harness,300,codex,3–8,10,0.6 +Harbor multi-harness,300,codex,9–16,80,0.225 +Harbor multi-harness,300,codex,17–24,160,0.3125 +Harbor multi-harness,300,mini-swe-agent,3–8,9,0.7777777777777778 +Harbor multi-harness,300,mini-swe-agent,9–16,28,0.5357142857142857 +Harbor multi-harness,300,mini-swe-agent,17–24,213,0.15492957746478872 +Harbor multi-harness,300,opencode,3–8,4,0.75 +Harbor multi-harness,300,opencode,9–16,215,0.2930232558139535 +Harbor multi-harness,300,opencode,17–24,31,0.25806451612903225 +Harbor multi-harness,400,claude-code,0–2,2,0.0 +Harbor multi-harness,400,claude-code,3–8,5,0.0 +Harbor multi-harness,400,claude-code,9–16,90,0.32222222222222224 +Harbor multi-harness,400,claude-code,17–24,153,0.4117647058823529 +Harbor multi-harness,400,codex,3–8,3,0.3333333333333333 +Harbor multi-harness,400,codex,9–16,60,0.15 +Harbor multi-harness,400,codex,17–24,187,0.4117647058823529 +Harbor multi-harness,400,mini-swe-agent,3–8,5,0.8 +Harbor multi-harness,400,mini-swe-agent,9–16,31,0.2903225806451613 +Harbor multi-harness,400,mini-swe-agent,17–24,214,0.2757009345794392 +Harbor multi-harness,400,opencode,3–8,6,0.0 +Harbor multi-harness,400,opencode,9–16,223,0.34080717488789236 +Harbor multi-harness,400,opencode,17–24,21,0.2857142857142857 +Harbor multi-harness,500,claude-code,3–8,9,0.0 +Harbor multi-harness,500,claude-code,9–16,86,0.3023255813953488 +Harbor multi-harness,500,claude-code,17–24,155,0.5548387096774193 +Harbor multi-harness,500,codex,3–8,1,1.0 +Harbor multi-harness,500,codex,9–16,72,0.2222222222222222 +Harbor multi-harness,500,codex,17–24,177,0.4576271186440678 +Harbor multi-harness,500,mini-swe-agent,3–8,35,0.7142857142857143 +Harbor multi-harness,500,mini-swe-agent,9–16,73,0.589041095890411 +Harbor multi-harness,500,mini-swe-agent,17–24,142,0.07042253521126761 +Harbor multi-harness,500,opencode,3–8,8,0.0 +Harbor multi-harness,500,opencode,9–16,224,0.33035714285714285 +Harbor multi-harness,500,opencode,17–24,18,0.4444444444444444 +Harbor multi-harness,600,claude-code,0–2,1,0.0 +Harbor multi-harness,600,claude-code,3–8,7,0.0 +Harbor multi-harness,600,claude-code,9–16,91,0.2087912087912088 +Harbor multi-harness,600,claude-code,17–24,151,0.3708609271523179 +Harbor multi-harness,600,codex,3–8,1,0.0 +Harbor multi-harness,600,codex,9–16,109,0.11009174311926606 +Harbor multi-harness,600,codex,17–24,140,0.4928571428571429 +Harbor multi-harness,600,mini-swe-agent,3–8,23,0.7391304347826086 +Harbor multi-harness,600,mini-swe-agent,9–16,107,0.45794392523364486 +Harbor multi-harness,600,mini-swe-agent,17–24,120,0.09166666666666666 +Harbor multi-harness,600,opencode,0–2,1,0.0 +Harbor multi-harness,600,opencode,3–8,11,0.0 +Harbor multi-harness,600,opencode,9–16,224,0.3482142857142857 +Harbor multi-harness,600,opencode,17–24,14,0.5 +Harbor multi-harness,684,claude-code,0–2,2,0.0 +Harbor multi-harness,684,claude-code,3–8,13,0.38461538461538464 +Harbor multi-harness,684,claude-code,9–16,80,0.2875 +Harbor multi-harness,684,claude-code,17–24,155,0.3935483870967742 +Harbor multi-harness,684,codex,3–8,1,0.0 +Harbor multi-harness,684,codex,9–16,101,0.2079207920792079 +Harbor multi-harness,684,codex,17–24,148,0.4189189189189189 +Harbor multi-harness,684,mini-swe-agent,3–8,36,0.7222222222222222 +Harbor multi-harness,684,mini-swe-agent,9–16,119,0.3865546218487395 +Harbor multi-harness,684,mini-swe-agent,17–24,95,0.042105263157894736 +Harbor multi-harness,684,opencode,0–2,1,0.0 +Harbor multi-harness,684,opencode,3–8,26,0.0 +Harbor multi-harness,684,opencode,9–16,202,0.31683168316831684 +Harbor multi-harness,684,opencode,17–24,21,0.42857142857142855 +Harbor multi-harness,700,claude-code,0–2,2,0.0 +Harbor multi-harness,700,claude-code,3–8,21,0.14285714285714285 +Harbor multi-harness,700,claude-code,9–16,84,0.3333333333333333 +Harbor multi-harness,700,claude-code,17–24,143,0.3356643356643357 +Harbor multi-harness,700,codex,3–8,2,0.0 +Harbor multi-harness,700,codex,9–16,80,0.2 +Harbor multi-harness,700,codex,17–24,168,0.38095238095238093 +Harbor multi-harness,700,mini-swe-agent,3–8,31,0.8064516129032258 +Harbor multi-harness,700,mini-swe-agent,9–16,122,0.3770491803278688 +Harbor multi-harness,700,mini-swe-agent,17–24,97,0.05154639175257732 +Harbor multi-harness,700,opencode,0–2,7,0.0 +Harbor multi-harness,700,opencode,3–8,36,0.0 +Harbor multi-harness,700,opencode,9–16,183,0.25136612021857924 +Harbor multi-harness,700,opencode,17–24,24,0.2916666666666667 +Harbor multi-harness,800,claude-code,0–2,2,0.0 +Harbor multi-harness,800,claude-code,3–8,20,0.0 +Harbor multi-harness,800,claude-code,9–16,85,0.24705882352941178 +Harbor multi-harness,800,claude-code,17–24,143,0.4125874125874126 +Harbor multi-harness,800,codex,9–16,168,0.2619047619047619 +Harbor multi-harness,800,codex,17–24,82,0.2804878048780488 +Harbor multi-harness,800,mini-swe-agent,3–8,14,0.8571428571428571 +Harbor multi-harness,800,mini-swe-agent,9–16,85,0.5764705882352941 +Harbor multi-harness,800,mini-swe-agent,17–24,151,0.026490066225165563 +Harbor multi-harness,800,opencode,0–2,7,0.0 +Harbor multi-harness,800,opencode,3–8,10,0.0 +Harbor multi-harness,800,opencode,9–16,214,0.2336448598130841 +Harbor multi-harness,800,opencode,17–24,19,0.42105263157894735 +Harbor multi-harness,900,claude-code,0–2,5,0.0 +Harbor multi-harness,900,claude-code,3–8,24,0.08333333333333333 +Harbor multi-harness,900,claude-code,9–16,105,0.3047619047619048 +Harbor multi-harness,900,claude-code,17–24,115,0.4956521739130435 +Harbor multi-harness,900,claude-code,25+,1,0.0 +Harbor multi-harness,900,codex,0–2,29,0.0 +Harbor multi-harness,900,codex,3–8,115,0.034782608695652174 +Harbor multi-harness,900,codex,9–16,61,0.3442622950819672 +Harbor multi-harness,900,codex,17–24,45,0.5111111111111111 +Harbor multi-harness,900,mini-swe-agent,3–8,42,0.7142857142857143 +Harbor multi-harness,900,mini-swe-agent,9–16,161,0.3105590062111801 +Harbor multi-harness,900,mini-swe-agent,17–24,47,0.0425531914893617 +Harbor multi-harness,900,opencode,0–2,218,0.0 +Harbor multi-harness,900,opencode,3–8,16,0.0 +Harbor multi-harness,900,opencode,9–16,16,0.375 +Harbor multi-harness,1000,claude-code,0–2,2,0.0 +Harbor multi-harness,1000,claude-code,3–8,64,0.3125 +Harbor multi-harness,1000,claude-code,9–16,126,0.4603174603174603 +Harbor multi-harness,1000,claude-code,17–24,58,0.3275862068965517 +Harbor multi-harness,1000,codex,0–2,34,0.0 +Harbor multi-harness,1000,codex,3–8,89,0.056179775280898875 +Harbor multi-harness,1000,codex,9–16,73,0.410958904109589 +Harbor multi-harness,1000,codex,17–24,54,0.5 +Harbor multi-harness,1000,mini-swe-agent,3–8,87,0.5977011494252874 +Harbor multi-harness,1000,mini-swe-agent,9–16,137,0.27007299270072993 +Harbor multi-harness,1000,mini-swe-agent,17–24,26,0.038461538461538464 +Harbor multi-harness,1000,opencode,0–2,185,0.0 +Harbor multi-harness,1000,opencode,3–8,30,0.1 +Harbor multi-harness,1000,opencode,9–16,31,0.3225806451612903 +Harbor multi-harness,1000,opencode,17–24,4,0.25 +Native OpenCode,0,claude-code,0–2,3,0.0 +Native OpenCode,0,claude-code,3–8,55,0.4 +Native OpenCode,0,claude-code,9–16,74,0.20270270270270271 +Native OpenCode,0,claude-code,17–24,116,0.04310344827586207 +Native OpenCode,0,claude-code,25+,2,0.0 +Native OpenCode,0,codex,3–8,27,0.4074074074074074 +Native OpenCode,0,codex,9–16,71,0.323943661971831 +Native OpenCode,0,codex,17–24,144,0.027777777777777776 +Native OpenCode,0,codex,25+,8,0.0 +Native OpenCode,0,mini-swe-agent,3–8,5,0.6 +Native OpenCode,0,mini-swe-agent,9–16,70,0.5428571428571428 +Native OpenCode,0,mini-swe-agent,17–24,175,0.03428571428571429 +Native OpenCode,0,opencode,0–2,5,0.0 +Native OpenCode,0,opencode,3–8,85,0.2823529411764706 +Native OpenCode,0,opencode,9–16,119,0.05042016806722689 +Native OpenCode,0,opencode,17–24,41,0.04878048780487805 +Native OpenCode,100,claude-code,0–2,4,0.25 +Native OpenCode,100,claude-code,3–8,81,0.4444444444444444 +Native OpenCode,100,claude-code,9–16,46,0.2391304347826087 +Native OpenCode,100,claude-code,17–24,116,0.02586206896551724 +Native OpenCode,100,claude-code,25+,3,0.0 +Native OpenCode,100,codex,0–2,1,0.0 +Native OpenCode,100,codex,3–8,26,0.46153846153846156 +Native OpenCode,100,codex,9–16,56,0.44642857142857145 +Native OpenCode,100,codex,17–24,166,0.04819277108433735 +Native OpenCode,100,codex,25+,1,0.0 +Native OpenCode,100,mini-swe-agent,3–8,19,0.6842105263157895 +Native OpenCode,100,mini-swe-agent,9–16,58,0.5517241379310345 +Native OpenCode,100,mini-swe-agent,17–24,173,0.04046242774566474 +Native OpenCode,100,opencode,0–2,1,0.0 +Native OpenCode,100,opencode,3–8,101,0.39603960396039606 +Native OpenCode,100,opencode,9–16,130,0.06923076923076923 +Native OpenCode,100,opencode,17–24,18,0.0 +Native OpenCode,200,claude-code,0–2,2,0.0 +Native OpenCode,200,claude-code,3–8,88,0.5 +Native OpenCode,200,claude-code,9–16,40,0.4 +Native OpenCode,200,claude-code,17–24,119,0.0 +Native OpenCode,200,claude-code,25+,1,0.0 +Native OpenCode,200,codex,3–8,54,0.5370370370370371 +Native OpenCode,200,codex,9–16,67,0.4626865671641791 +Native OpenCode,200,codex,17–24,127,0.031496062992125984 +Native OpenCode,200,codex,25+,2,0.0 +Native OpenCode,200,mini-swe-agent,3–8,27,0.6296296296296297 +Native OpenCode,200,mini-swe-agent,9–16,65,0.5076923076923077 +Native OpenCode,200,mini-swe-agent,17–24,158,0.02531645569620253 +Native OpenCode,200,opencode,0–2,2,0.0 +Native OpenCode,200,opencode,3–8,109,0.3394495412844037 +Native OpenCode,200,opencode,9–16,128,0.046875 +Native OpenCode,200,opencode,17–24,11,0.0 +Native OpenCode,300,claude-code,0–2,1,1.0 +Native OpenCode,300,claude-code,3–8,88,0.5568181818181818 +Native OpenCode,300,claude-code,9–16,54,0.4074074074074074 +Native OpenCode,300,claude-code,17–24,106,0.009433962264150943 +Native OpenCode,300,claude-code,25+,1,0.0 +Native OpenCode,300,codex,3–8,29,0.5517241379310345 +Native OpenCode,300,codex,9–16,44,0.36363636363636365 +Native OpenCode,300,codex,17–24,176,0.056818181818181816 +Native OpenCode,300,codex,25+,1,0.0 +Native OpenCode,300,mini-swe-agent,3–8,13,0.5384615384615384 +Native OpenCode,300,mini-swe-agent,9–16,65,0.6 +Native OpenCode,300,mini-swe-agent,17–24,172,0.01744186046511628 +Native OpenCode,300,opencode,3–8,102,0.45098039215686275 +Native OpenCode,300,opencode,9–16,127,0.047244094488188976 +Native OpenCode,300,opencode,17–24,21,0.0 +Native OpenCode,400,claude-code,0–2,1,0.0 +Native OpenCode,400,claude-code,3–8,96,0.6354166666666666 +Native OpenCode,400,claude-code,9–16,39,0.46153846153846156 +Native OpenCode,400,claude-code,17–24,113,0.017699115044247787 +Native OpenCode,400,claude-code,25+,1,0.0 +Native OpenCode,400,codex,3–8,32,0.65625 +Native OpenCode,400,codex,9–16,52,0.6346153846153846 +Native OpenCode,400,codex,17–24,165,0.06060606060606061 +Native OpenCode,400,codex,25+,1,0.0 +Native OpenCode,400,mini-swe-agent,3–8,18,0.8333333333333334 +Native OpenCode,400,mini-swe-agent,9–16,86,0.5697674418604651 +Native OpenCode,400,mini-swe-agent,17–24,146,0.0273972602739726 +Native OpenCode,400,opencode,3–8,113,0.3805309734513274 +Native OpenCode,400,opencode,9–16,119,0.06722689075630252 +Native OpenCode,400,opencode,17–24,18,0.0 +Native OpenCode,500,claude-code,0–2,3,0.3333333333333333 +Native OpenCode,500,claude-code,3–8,92,0.532608695652174 +Native OpenCode,500,claude-code,9–16,52,0.25 +Native OpenCode,500,claude-code,17–24,103,0.02912621359223301 +Native OpenCode,500,codex,0–2,1,0.0 +Native OpenCode,500,codex,3–8,35,0.5142857142857142 +Native OpenCode,500,codex,9–16,54,0.4444444444444444 +Native OpenCode,500,codex,17–24,160,0.03125 +Native OpenCode,500,mini-swe-agent,3–8,29,0.7931034482758621 +Native OpenCode,500,mini-swe-agent,9–16,102,0.47058823529411764 +Native OpenCode,500,mini-swe-agent,17–24,119,0.01680672268907563 +Native OpenCode,500,opencode,0–2,1,0.0 +Native OpenCode,500,opencode,3–8,119,0.31092436974789917 +Native OpenCode,500,opencode,9–16,112,0.07142857142857142 +Native OpenCode,500,opencode,17–24,18,0.0 +Native OpenCode,600,claude-code,0–2,4,0.0 +Native OpenCode,600,claude-code,3–8,108,0.5740740740740741 +Native OpenCode,600,claude-code,9–16,52,0.3269230769230769 +Native OpenCode,600,claude-code,17–24,86,0.023255813953488372 +Native OpenCode,600,codex,3–8,42,0.6190476190476191 +Native OpenCode,600,codex,9–16,50,0.32 +Native OpenCode,600,codex,17–24,158,0.02531645569620253 +Native OpenCode,600,mini-swe-agent,3–8,44,0.5909090909090909 +Native OpenCode,600,mini-swe-agent,9–16,150,0.34 +Native OpenCode,600,mini-swe-agent,17–24,56,0.08928571428571429 +Native OpenCode,600,opencode,0–2,4,0.25 +Native OpenCode,600,opencode,3–8,126,0.30952380952380953 +Native OpenCode,600,opencode,9–16,109,0.01834862385321101 +Native OpenCode,600,opencode,17–24,11,0.0 +Native OpenCode,700,claude-code,0–2,6,0.0 +Native OpenCode,700,claude-code,3–8,115,0.5391304347826087 +Native OpenCode,700,claude-code,9–16,40,0.25 +Native OpenCode,700,claude-code,17–24,89,0.02247191011235955 +Native OpenCode,700,codex,3–8,20,0.6 +Native OpenCode,700,codex,9–16,45,0.4222222222222222 +Native OpenCode,700,codex,17–24,185,0.02702702702702703 +Native OpenCode,700,mini-swe-agent,3–8,44,0.7272727272727273 +Native OpenCode,700,mini-swe-agent,9–16,99,0.41414141414141414 +Native OpenCode,700,mini-swe-agent,17–24,107,0.028037383177570093 +Native OpenCode,700,opencode,0–2,3,0.0 +Native OpenCode,700,opencode,3–8,154,0.2987012987012987 +Native OpenCode,700,opencode,9–16,91,0.0 +Native OpenCode,700,opencode,17–24,2,0.0 +Native OpenCode,800,claude-code,0–2,5,0.0 +Native OpenCode,800,claude-code,3–8,106,0.5566037735849056 +Native OpenCode,800,claude-code,9–16,62,0.3387096774193548 +Native OpenCode,800,claude-code,17–24,77,0.012987012987012988 +Native OpenCode,800,codex,0–2,2,0.0 +Native OpenCode,800,codex,3–8,30,0.6 +Native OpenCode,800,codex,9–16,53,0.4528301886792453 +Native OpenCode,800,codex,17–24,165,0.01818181818181818 +Native OpenCode,800,mini-swe-agent,3–8,42,0.8095238095238095 +Native OpenCode,800,mini-swe-agent,9–16,81,0.49382716049382713 +Native OpenCode,800,mini-swe-agent,17–24,127,0.047244094488188976 +Native OpenCode,800,opencode,0–2,4,0.0 +Native OpenCode,800,opencode,3–8,158,0.27848101265822783 +Native OpenCode,800,opencode,9–16,84,0.07142857142857142 +Native OpenCode,800,opencode,17–24,4,0.0 +Native OpenCode,900,claude-code,0–2,8,0.125 +Native OpenCode,900,claude-code,3–8,117,0.48717948717948717 +Native OpenCode,900,claude-code,9–16,54,0.4444444444444444 +Native OpenCode,900,claude-code,17–24,71,0.04225352112676056 +Native OpenCode,900,codex,0–2,1,0.0 +Native OpenCode,900,codex,3–8,27,0.4074074074074074 +Native OpenCode,900,codex,9–16,40,0.475 +Native OpenCode,900,codex,17–24,182,0.03296703296703297 +Native OpenCode,900,mini-swe-agent,3–8,34,0.7941176470588235 +Native OpenCode,900,mini-swe-agent,9–16,98,0.5204081632653061 +Native OpenCode,900,mini-swe-agent,17–24,118,0.059322033898305086 +Native OpenCode,900,opencode,0–2,14,0.14285714285714285 +Native OpenCode,900,opencode,3–8,163,0.25766871165644173 +Native OpenCode,900,opencode,9–16,69,0.043478260869565216 +Native OpenCode,900,opencode,17–24,4,0.0 +Native OpenCode,1000,claude-code,0–2,3,0.0 +Native OpenCode,1000,claude-code,3–8,116,0.5172413793103449 +Native OpenCode,1000,claude-code,9–16,52,0.38461538461538464 +Native OpenCode,1000,claude-code,17–24,79,0.0379746835443038 +Native OpenCode,1000,codex,0–2,1,0.0 +Native OpenCode,1000,codex,3–8,27,0.5925925925925926 +Native OpenCode,1000,codex,9–16,71,0.5633802816901409 +Native OpenCode,1000,codex,17–24,151,0.11920529801324503 +Native OpenCode,1000,mini-swe-agent,3–8,34,0.7647058823529411 +Native OpenCode,1000,mini-swe-agent,9–16,107,0.5514018691588785 +Native OpenCode,1000,mini-swe-agent,17–24,109,0.045871559633027525 +Native OpenCode,1000,opencode,3–8,130,0.3076923076923077 +Native OpenCode,1000,opencode,9–16,110,0.1 +Native OpenCode,1000,opencode,17–24,10,0.0 diff --git a/04-data-agent/reports/three-run-analysis-20260917/training_accounting.csv b/04-data-agent/reports/three-run-analysis-20260917/training_accounting.csv new file mode 100644 index 0000000..d018ebb --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/training_accounting.csv @@ -0,0 +1,4 @@ +run,steps,zero_gradient_steps,forwarded_tokens,supervised_tokens,forwarded_per_supervised_token,supervised_tokens_in_zero_gradient_steps,zero_gradient_supervised_token_fraction,zero_gradient_forwarded_token_fraction,mean_step_s,step_timing_observations,receipt_token_mismatches +Harbor OpenCode-only,1000,380,419428466.0,14625038.0,28.67879495424217,5912749.0,0.40428947945297644,0.39100410509571853,79.62124525297799,999,0 +Harbor multi-harness,1000,349,1001322856.0,22148436.0,45.20964171014152,7357643.0,0.3321969551258608,0.34976226988271203,108.20385101597033,993,0 +Native OpenCode,1000,583,228413083.0,5327227.0,42.87654402562534,3418521.0,0.6417074023689998,0.5876681459616742,36.3195656516987,999,0 diff --git a/04-data-agent/reports/three-run-analysis-20260917/training_behavior.py b/04-data-agent/reports/three-run-analysis-20260917/training_behavior.py new file mode 100644 index 0000000..dc00d26 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/training_behavior.py @@ -0,0 +1,114 @@ +"""Summarize saved optimizer-admitted rollouts without loading a model or launching jobs.""" + +import argparse +import collections +import concurrent.futures +import hashlib +import json +from pathlib import Path + +import orjson +import pandas as pd + + +def extract(row, audit_dir): + path = audit_dir / "rollouts" / (row["rollout_id"] + ".json") + raw = path.read_bytes() + saved = orjson.loads(raw) + result = saved["result"] + assert saved["task_index"] == row["task_index"], path + assert saved["harness"] == row["harness"], path + turns = [t for t in result["turns"] if t.get("trainable", True) and not t.get("discarded", False)] + tokens, supervised, action_tokens, prompt_tokens, calls = [], 0, 0, 0, [] + for turn in turns: + count = len(turn["completion_token_ids"]) + prompt_len = len(turn["prompt_token_ids"]) + tokens.append(count) + prompt_tokens += prompt_len + mask = turn.get("loss_mask") + if mask is None: + supervised += count + elif len(mask) == prompt_len + count: + supervised += sum(mask[prompt_len:]) + else: + assert len(mask) == count, (path, len(mask), prompt_len, count) + supervised += sum(mask) + if turn.get("tool_calls"): + action_tokens += count + for call in turn.get("tool_calls") or []: + fn = call.get("function") or call + args = fn.get("arguments", {}) + if isinstance(args, str): + try: + args = orjson.loads(args) + except orjson.JSONDecodeError: + pass + signature = (fn.get("name", ""), json.dumps(args, sort_keys=True)) + calls.append(signature) + counts = collections.Counter(calls) + reward = result.get("reward") + if row["run"] == "Native OpenCode": + correctness = result.get("correctness") + reward = float(correctness >= 1.0) if correctness is not None else None + return { + **row, "binary_reward": reward, "agent_turns": len(turns), + "emitted_tool_calls": len(calls), "exact_repeated_calls": sum(n - 1 for n in counts.values()), + "completion_tokens": sum(tokens), "masked_completion_tokens": supervised, + "receipt_token_delta": supervised - row["supervised_tokens"], + "completion_tokens_in_tool_turns": action_tokens, + "completion_tokens_in_text_only_turns": sum(tokens) - action_tokens, + "max_response_tokens": max(tokens, default=0), + "responses_over_4096": sum(n > 4096 for n in tokens), + "responses_finish_length": sum(t.get("finish_reason") == "length" for t in turns), + "sum_captured_prompt_tokens": prompt_tokens, + "last_turn_has_tool": bool(turns and turns[-1].get("tool_calls")), + "generation_s": saved["finished_at"] - saved["started_at"], + "tool_names": dict(collections.Counter(name for name, _ in calls)), + "source": str(path), "sha256": hashlib.sha256(raw).hexdigest(), + } + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--evidence", type=Path, required=True, help="Directory containing optimizer_rollouts.csv and training_lineage.csv") + parser.add_argument("--workers", type=int, default=2) + args = parser.parse_args() + receipts = pd.read_csv(args.evidence / "optimizer_rollouts.csv") + lineage = pd.read_csv(args.evidence / "training_lineage.csv") + audit_dirs = {int(r.job): Path(r.coverage_source).parent for r in lineage.itertuples()} + cache_path = args.evidence / "admitted_training_behavior.jsonl" + cached = {} + if cache_path.exists(): + with cache_path.open() as stream: + for line in stream: + item = json.loads(line) + cached[(item["run"], item["rollout_id"])] = item + rows = receipts.to_dict("records") + missing = [r for r in rows if (r["run"], r["rollout_id"]) not in cached] + print(f"Cached {len(cached)}; extracting {len(missing)} optimizer-admitted rollouts", flush=True) + with cache_path.open("a") as out, concurrent.futures.ThreadPoolExecutor(max_workers=args.workers) as pool: + futures = (pool.submit(extract, row, audit_dirs[row["job"]]) for row in missing) + # Keep only a small number of raw captures in flight. + pending = collections.deque() + for future in futures: + pending.append(future) + if len(pending) < args.workers * 2: + continue + item = pending.popleft().result() + out.write(json.dumps(item) + "\n") + cached[(item["run"], item["rollout_id"])] = item + if len(cached) % 500 == 0: + out.flush() + print(f"Extracted {len(cached)}/{len(rows)}", flush=True) + for future in pending: + item = future.result() + out.write(json.dumps(item) + "\n") + cached[(item["run"], item["rollout_id"])] = item + assert len(cached) == len(rows), (len(cached), len(rows)) + frame = pd.DataFrame(cached.values()) + frame.to_csv(args.evidence / "admitted_training_behavior.csv", index=False) + print(frame.groupby("run").agg(rollouts=("rollout_id", "size"), tool_calls=("emitted_tool_calls", "mean"), tokens=("completion_tokens", "mean"), receipt_mismatches=("receipt_token_delta", lambda x: (x != 0).sum())).to_string(), flush=True) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/reports/three-run-analysis-20260917/training_behavior_by_harness.csv b/04-data-agent/reports/three-run-analysis-20260917/training_behavior_by_harness.csv new file mode 100644 index 0000000..d849645 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/training_behavior_by_harness.csv @@ -0,0 +1,61 @@ +run,harness,window_end,admitted_rollouts,unique_tasks,binary_reward_mean,completion_tokens_mean,completion_tokens_median,completion_tokens_p90,supervised_tokens,completion_tokens,text_only_turn_tokens,agent_turns_mean,tool_calls_mean,tool_calls_p90,exact_repeated_calls_mean,rollout_over4096_fraction,rollout_finish_length_fraction,training_rows,receipt_token_mismatches,text_only_turn_token_share,rows_per_rollout +Harbor OpenCode-only,opencode,100,414,53,0.4613526570048309,2174.2487922705313,976.0,4195.299999999999,900139,900139,2772,14.152173913043478,14.577294685990339,17.0,6.584541062801932,0.033816425120772944,0.0,881,0,0.0030795243845672723,2.1280193236714977 +Harbor OpenCode-only,opencode,200,432,55,0.22916666666666666,7749.881944444444,2538.0,20144.0,3347949,3347949,16657,13.5,14.974537037037036,18.0,2.3518518518518516,0.2569444444444444,0.0023148148148148147,1101,0,0.004975284868437363,2.548611111111111 +Harbor OpenCode-only,opencode,300,410,52,0.36097560975609755,5242.002439024391,2272.0,11004.000000000018,2149221,2149221,17393,14.234146341463415,15.75609756097561,18.0,2.292682926829268,0.0951219512195122,0.0024390243902439024,1077,0,0.008092699633960398,2.626829268292683 +Harbor OpenCode-only,opencode,400,409,52,0.3154034229828851,2577.4767726161367,2011.0,4439.8,1054188,1054188,242,14.71393643031785,18.760391198044008,21.0,3.4376528117359415,0.007334963325183374,0.0,977,0,0.00022956057173862727,2.388753056234719 +Harbor OpenCode-only,opencode,500,415,54,0.4578313253012048,2965.9204819277106,1964.0,5172.6,1230857,1230857,352,14.573493975903615,16.896385542168673,19.0,2.746987951807229,0.043373493975903614,0.0,1144,0,0.00028597960607934145,2.756626506024096 +Harbor OpenCode-only,opencode,600,403,53,0.45161290322580644,2678.468982630273,2078.0,4732.8,1079423,1079423,653,14.468982630272953,16.523573200992555,18.0,2.967741935483871,0.004962779156327543,0.0,1061,0,0.0006049528312811567,2.632754342431762 +Harbor OpenCode-only,opencode,700,420,55,0.46190476190476193,2122.211904761905,1552.0,3914.3000000000006,891329,891329,27894,11.7,14.633333333333333,19.0,3.8214285714285716,0.011904761904761904,0.002380952380952381,1470,0,0.031294841747547764,3.5 +Harbor OpenCode-only,opencode,800,400,51,0.4625,3206.7175,1945.5,6440.900000000001,1282687,1282687,177689,10.455,15.2775,20.0,5.415,0.0725,0.015,1767,0,0.13852872914436648,4.4175 +Harbor OpenCode-only,opencode,900,412,54,0.44660194174757284,2967.152912621359,1510.0,6343.600000000004,1222467,1222467,144773,10.094660194174757,12.672330097087379,19.0,2.0679611650485437,0.05825242718446602,0.014563106796116505,1755,0,0.11842691868164948,4.259708737864078 +Harbor OpenCode-only,opencode,1000,404,51,0.3935643564356436,3630.638613861386,2102.0,5953.799999999999,1466778,1466778,75528,11.487623762376238,17.43069306930693,25.0,3.027227722772277,0.06435643564356436,0.0049504950495049506,1566,0,0.05149245489092419,3.876237623762376 +Harbor multi-harness,claude-code,100,82,10,0.2682926829268293,2714.890243902439,2291.5,5157.500000000001,222621,222621,1073,13.365853658536585,13.536585365853659,17.0,3.6707317073170733,0.0,0.0,1096,0,0.004819850777779275,13.365853658536585 +Harbor multi-harness,claude-code,200,130,17,0.3153846153846154,4026.346153846154,2943.5,8447.000000000004,523425,523425,263,15.107692307692307,15.215384615384615,17.0,4.076923076923077,0.015384615384615385,0.0,1964,0,0.0005024597602330802,15.107692307692307 +Harbor multi-harness,claude-code,300,140,18,0.37857142857142856,3982.5714285714284,3335.5,6859.6,557560,557560,16421,16.735714285714284,16.785714285714285,17.0,4.55,0.02142857142857143,0.007142857142857143,2343,0,0.029451538847837005,16.735714285714284 +Harbor multi-harness,claude-code,400,136,17,0.5220588235294118,3549.404411764706,2332.5,6253.5,482719,482719,16675,16.566176470588236,16.625,17.0,5.117647058823529,0.029411764705882353,0.007352941176470588,2253,0,0.03454390649632602,16.566176470588236 +Harbor multi-harness,claude-code,500,120,15,0.2916666666666667,5923.533333333334,5641.5,9395.4,710824,710824,16384,16.933333333333334,17.216666666666665,18.0,3.4166666666666665,0.008333333333333333,0.008333333333333333,2032,0,0.023049306157361036,16.933333333333334 +Harbor multi-harness,claude-code,600,128,16,0.40625,4824.53125,4213.5,8859.8,617540,617540,101,15.296875,15.390625,17.0,4.6328125,0.0234375,0.0,1958,0,0.00016355215856462738,15.296875 +Harbor multi-harness,claude-code,700,134,17,0.5,4083.1492537313434,2715.0,7031.500000000001,547142,547142,33063,15.171641791044776,15.514925373134329,17.0,5.485074626865671,0.04477611940298507,0.014925373134328358,2033,0,0.06042855419616845,15.171641791044776 +Harbor multi-harness,claude-code,800,128,16,0.3984375,4690.1484375,3595.5,8415.399999999998,600339,600339,855,16.703125,16.765625,17.0,5.9765625,0.0546875,0.0,2138,0,0.001424195329638754,16.703125 +Harbor multi-harness,claude-code,900,126,16,0.38095238095238093,10933.753968253968,8429.0,21573.0,1377653,1377653,66336,16.333333333333332,16.444444444444443,17.0,4.73015873015873,0.2857142857142857,0.031746031746031744,2058,0,0.04815145758765088,16.333333333333332 +Harbor multi-harness,claude-code,1000,119,17,0.3865546218487395,14147.81512605042,9810.0,25155.600000000002,1683590,1683590,345749,13.025210084033613,12.680672268907562,17.0,5.092436974789916,0.5126050420168067,0.058823529411764705,1550,0,0.20536413259760394,13.025210084033613 +Harbor multi-harness,codex,100,88,11,0.48863636363636365,3284.284090909091,2609.5,6985.2,289017,289017,1466,15.113636363636363,15.056818181818182,17.0,2.3295454545454546,0.0,0.0,100,0,0.005072365985391862,1.1363636363636365 +Harbor multi-harness,codex,200,136,16,0.3161764705882353,3008.904411764706,2459.0,5115.5,409211,409211,754,16.16176470588235,16.272058823529413,17.0,2.5661764705882355,0.0,0.0,151,0,0.0018425702143881762,1.1102941176470589 +Harbor multi-harness,codex,300,128,16,0.34375,2766.0859375,2386.5,4706.2,354059,354059,712,16.375,16.4375,17.0,3.2265625,0.0078125,0.0,131,0,0.0020109642743158626,1.0234375 +Harbor multi-harness,codex,400,128,16,0.6171875,2406.9609375,1978.0,4208.1,308091,308091,640,15.921875,15.890625,17.0,4.859375,0.0,0.0,132,0,0.0020773083277343383,1.03125 +Harbor multi-harness,codex,500,128,16,0.5078125,4199.0859375,2612.0,8614.8,537483,537483,13,16.4296875,16.4375,17.0,4.5546875,0.0390625,0.0,135,0,2.418681148985177e-05,1.0546875 +Harbor multi-harness,codex,600,128,16,0.625,3459.953125,2739.5,6188.999999999999,442874,442874,69,16.4296875,16.4375,17.0,4.609375,0.0078125,0.0,141,0,0.00015580052114145333,1.1015625 +Harbor multi-harness,codex,700,124,16,0.41935483870967744,4493.5161290322585,3596.5,8010.300000000001,557196,557196,117,16.080645161290324,16.072580645161292,17.0,4.395161290322581,0.024193548387096774,0.0,140,0,0.00020997997114121422,1.1290322580645162 +Harbor multi-harness,codex,800,118,16,0.635593220338983,4894.466101694915,3706.0,7741.899999999998,577547,577547,89,15.88135593220339,15.88135593220339,17.0,5.9491525423728815,0.07627118644067797,0.0,121,0,0.00015410001263966395,1.0254237288135593 +Harbor multi-harness,codex,900,136,17,0.4852941176470588,11910.426470588236,10029.0,24055.5,1619818,1619818,174,15.308823529411764,15.345588235294118,17.0,5.198529411764706,0.3602941176470588,0.0,154,0,0.000107419475521324,1.1323529411764706 +Harbor multi-harness,codex,1000,120,15,0.65,11192.558333333332,10052.5,20081.80000000001,1343107,1343107,7707,14.35,14.133333333333333,17.0,5.558333333333334,0.375,0.0,138,0,0.005738187649978743,1.15 +Harbor multi-harness,mini-swe-agent,100,107,11,0.375,1999.411214953271,1722.0,3551.2000000000007,213937,213937,0,14.261682242990654,14.261682242990654,17.0,1.0934579439252337,0.0,0.0,123,0,0.0,1.1495327102803738 +Harbor multi-harness,mini-swe-agent,200,134,16,0.1791044776119403,1702.1119402985075,1255.5,3141.4,228083,228083,0,14.962686567164178,14.962686567164178,17.0,1.507462686567164,0.0,0.0,142,0,0.0,1.0597014925373134 +Harbor multi-harness,mini-swe-agent,300,112,14,0.3063063063063063,1540.607142857143,1230.0,2424.0,172548,172548,0,15.464285714285714,15.464285714285714,17.0,2.3214285714285716,0.0,0.0,120,0,0.0,1.0714285714285714 +Harbor multi-harness,mini-swe-agent,400,144,18,0.3006993006993007,1901.6041666666667,1262.0,3841.000000000006,273831,273831,0,15.36111111111111,15.36111111111111,17.0,2.798611111111111,0.0,0.0,153,0,0.0,1.0625 +Harbor multi-harness,mini-swe-agent,500,130,17,0.36923076923076925,1602.7615384615385,1377.5,2364.7000000000003,208359,208359,0,15.107692307692307,15.107692307692307,17.0,2.6076923076923078,0.007692307692307693,0.0,143,0,0.0,1.1 +Harbor multi-harness,mini-swe-agent,600,134,17,0.43283582089552236,1897.955223880597,1659.5,3196.5,254326,254326,0,12.761194029850746,12.761194029850746,17.0,0.8507462686567164,0.0,0.0,151,0,0.0,1.126865671641791 +Harbor multi-harness,mini-swe-agent,700,136,17,0.45588235294117646,1323.7794117647059,1026.5,2662.5,180034,180034,0,11.794117647058824,11.794117647058824,17.0,1.8235294117647058,0.0,0.0,152,0,0.0,1.1176470588235294 +Harbor multi-harness,mini-swe-agent,800,134,17,0.417910447761194,1903.4626865671642,1504.5,4014.8,255064,255064,56,11.888059701492537,11.880597014925373,16.0,1.4701492537313432,0.007462686567164179,0.0,181,0,0.00021955273970454474,1.3507462686567164 +Harbor multi-harness,mini-swe-agent,900,112,14,0.41964285714285715,4311.580357142857,2825.0,8690.40000000001,482897,482897,0,12.508928571428571,12.508928571428571,17.0,1.6607142857142858,0.10714285714285714,0.0,120,0,0.0,1.0714285714285714 +Harbor multi-harness,mini-swe-agent,1000,128,16,0.359375,7110.0234375,4868.5,15039.199999999999,910083,910083,0,10.3671875,10.3671875,15.299999999999997,1.4921875,0.2421875,0.0,198,0,0.0,1.546875 +Harbor multi-harness,opencode,100,115,12,0.46956521739130436,1707.304347826087,1066.0,3837.8,196340,196340,972,12.982608695652173,12.747826086956522,16.0,3.8,0.0,0.0,257,0,0.004950595905062647,2.234782608695652 +Harbor multi-harness,opencode,200,120,15,0.44166666666666665,2073.9666666666667,1522.5,4272.4000000000015,248876,248876,68,15.475,15.458333333333334,16.0,6.583333333333333,0.008333333333333333,0.0,263,0,0.00027322843504395765,2.191666666666667 +Harbor multi-harness,opencode,300,136,17,0.2,1823.4485294117646,1494.5,3136.5,247989,247989,181,15.397058823529411,15.375,16.0,4.801470588235294,0.0,0.0,299,0,0.0007298710829915843,2.198529411764706 +Harbor multi-harness,opencode,400,130,17,0.23846153846153847,1878.576923076923,1350.0,3450.4000000000015,244215,244215,47,15.176923076923076,15.169230769230769,16.0,4.915384615384616,0.007692307692307693,0.0,278,0,0.000192453371005057,2.1384615384615384 +Harbor multi-harness,opencode,500,134,17,0.3283582089552239,2582.7985074626868,2016.5,4391.3,346095,346095,354,14.791044776119403,14.82089552238806,16.0,5.723880597014926,0.022388059701492536,0.0,320,0,0.0010228405495600918,2.388059701492537 +Harbor multi-harness,opencode,600,135,17,0.362962962962963,2741.7185185185185,1759.0,6205.000000000002,370132,370132,25316,13.985185185185186,13.955555555555556,16.0,7.066666666666666,0.05185185185185185,0.007407407407407408,333,0,0.06839722045108232,2.466666666666667 +Harbor multi-harness,opencode,700,121,16,0.35537190082644626,3174.4793388429753,2215.0,5755.0,384112,384112,56298,14.462809917355372,14.47107438016529,16.0,5.892561983471074,0.0743801652892562,0.024793388429752067,284,0,0.14656662640063314,2.347107438016529 +Harbor multi-harness,opencode,800,143,18,0.13986013986013987,2365.5524475524476,1940.0,4867.799999999999,338274,338274,19675,12.748251748251748,12.482517482517483,16.0,3.6153846153846154,0.03496503496503497,0.006993006993006993,319,0,0.05816290935750309,2.230769230769231 +Harbor multi-harness,opencode,900,113,15,0.25663716814159293,6644.8938053097345,5180.0,16415.0,750873,750873,335070,9.761061946902656,9.584070796460177,16.0,3.274336283185841,0.4247787610619469,0.1415929203539823,253,0,0.4462405759695714,2.2389380530973453 +Harbor multi-harness,opencode,1000,118,16,0.17796610169491525,5601.542372881356,1893.0,16330.0,660982,660982,217638,8.110169491525424,7.788135593220339,16.0,4.864406779661017,0.3813559322033898,0.06779661016949153,262,0,0.32926463958171326,2.2203389830508473 +Native OpenCode,opencode,100,413,52,0.4406779661016949,998.6222760290557,483.0,2787.8,412431,412431,13465,7.142857142857143,6.428571428571429,14.800000000000011,0.22760290556900725,0.002421307506053269,0.0,990,0,0.032647885343245295,2.3970944309927362 +Native OpenCode,opencode,200,428,55,0.18691588785046728,1259.193925233645,710.5,3299.6,538935,538935,8606,7.822429906542056,7.5046728971962615,15.0,0.2336448598130841,0.0,0.0,809,0,0.015968530527800198,1.8901869158878504 +Native OpenCode,opencode,300,453,58,0.2853982300884956,1642.3841059602648,1004.0,3866.0,744000,744000,9527,8.754966887417218,8.205298013245033,15.0,0.33774834437086093,0.0,0.0,866,0,0.01280510752688172,1.9116997792494481 +Native OpenCode,opencode,400,558,71,0.18100358422939067,1423.6863799283153,875.5,3166.5,794417,794417,10555,8.775985663082437,8.0663082437276,15.0,0.20430107526881722,0.0017921146953405018,0.0,1019,0,0.013286472973262154,1.8261648745519714 +Native OpenCode,opencode,500,521,66,0.2456813819577735,1145.42226487524,743.0,2489.0,596765,596765,7578,8.326295585412668,7.723608445297505,15.0,0.362763915547025,0.0019193857965451055,0.0,997,0,0.012698465895285414,1.9136276391554703 +Native OpenCode,opencode,600,451,57,0.12195121951219512,992.4434589800444,639.0,2268.0,447592,447592,1366,7.490022172949002,7.006651884700665,13.0,1.252771618625277,0.0,0.0,1200,0,0.0030518865395270695,2.6607538802660753 +Native OpenCode,opencode,700,425,54,0.30823529411764705,1053.8376470588234,504.0,2649.6000000000026,447881,447881,1134,6.842352941176471,6.061176470588236,13.0,0.2164705882352941,0.002352941176470588,0.0,856,0,0.002531922541925199,2.0141176470588236 +Native OpenCode,opencode,800,429,54,0.2727272727272727,1024.4708624708624,453.0,2760.1999999999994,439498,439498,1121,6.741258741258742,5.916083916083916,13.0,0.19347319347319347,0.002331002331002331,0.0,832,0,0.002550637318031026,1.9393939393939394 +Native OpenCode,opencode,900,413,53,0.36561743341404357,1147.0605326876514,508.0,2972.2000000000003,473736,473736,1381,6.7409200968523,5.9491525423728815,13.0,0.07263922518159806,0.002421307506053269,0.0,869,0,0.0029151257240319505,2.1041162227602905 +Native OpenCode,opencode,1000,402,53,0.19154228855721392,1074.5572139303483,636.5,2226.4000000000005,431972,431972,1588,6.800995024875622,6.052238805970149,13.0,0.24875621890547264,0.0,0.0,1835,0,0.0036761641958275074,4.564676616915423 diff --git a/04-data-agent/reports/three-run-analysis-20260917/training_behavior_by_outcome.csv b/04-data-agent/reports/three-run-analysis-20260917/training_behavior_by_outcome.csv new file mode 100644 index 0000000..8273a53 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/training_behavior_by_outcome.csv @@ -0,0 +1,61 @@ +run,window_end,binary_reward,admitted_rollouts,unique_tasks,binary_reward_mean,completion_tokens_mean,completion_tokens_median,completion_tokens_p90,supervised_tokens,completion_tokens,text_only_turn_tokens,agent_turns_mean,tool_calls_mean,tool_calls_p90,exact_repeated_calls_mean,rollout_over4096_fraction,rollout_finish_length_fraction,training_rows,receipt_token_mismatches,text_only_turn_token_share,rows_per_rollout +Harbor OpenCode-only,100,0.0,223,45,0.0,3094.9910313901346,1346.0,5601.000000000005,690183,690183,1850,14.094170403587444,14.582959641255606,17.0,4.7174887892376685,0.053811659192825115,0.0,481,0,0.002680448518726193,2.1569506726457397 +Harbor OpenCode-only,100,1.0,191,38,1.0,1099.2460732984293,888.0,1181.0,209956,209956,922,14.219895287958115,14.570680628272251,17.0,8.764397905759163,0.010471204188481676,0.0,400,0,0.004391396292556536,2.094240837696335 +Harbor OpenCode-only,200,0.0,333,53,0.0,8297.465465465466,2808.0,20519.6,2763056,2763056,16533,12.867867867867869,14.414414414414415,18.0,1.8198198198198199,0.2732732732732733,0.003003003003003003,832,0,0.005983592080652727,2.4984984984984986 +Harbor OpenCode-only,200,1.0,99,26,1.0,5908.010101010101,1394.0,18226.8,584893,584893,124,15.626262626262626,16.858585858585858,18.0,4.141414141414141,0.20202020202020202,0.0,269,0,0.00021200458887352046,2.717171717171717 +Harbor OpenCode-only,300,0.0,262,44,0.0,6887.614503816794,3353.5,17885.4,1804555,1804555,17094,13.606870229007633,15.229007633587786,18.0,1.4312977099236641,0.12595419847328243,0.003816793893129771,700,0,0.009472695484482324,2.6717557251908395 +Harbor OpenCode-only,300,1.0,148,32,1.0,2328.824324324324,1459.0,3045.500000000002,344666,344666,299,15.344594594594595,16.68918918918919,18.0,3.8175675675675675,0.04054054054054054,0.0,377,0,0.0008675065135522506,2.5472972972972974 +Harbor OpenCode-only,400,0.0,280,48,0.0,2983.432142857143,2451.0,4738.0999999999985,835361,835361,175,14.553571428571429,19.060714285714287,22.0,3.0892857142857144,0.010714285714285714,0.0,649,0,0.000209490268279223,2.317857142857143 +Harbor OpenCode-only,400,1.0,129,32,1.0,1696.3333333333333,1449.0,2663.600000000001,218827,218827,67,15.062015503875969,18.108527131782946,20.0,4.1937984496124034,0.0,0.0,328,0,0.0003061779396509572,2.5426356589147288 +Harbor OpenCode-only,500,0.0,225,43,0.0,3079.8933333333334,2464.0,5218.0,692976,692976,163,13.955555555555556,16.493333333333332,19.0,1.991111111111111,0.03111111111111111,0.0,601,0,0.00023521738126572927,2.671111111111111 +Harbor OpenCode-only,500,1.0,190,34,1.0,2830.952631578947,1759.0,4324.199999999999,537881,537881,189,15.305263157894737,17.373684210526317,19.0,3.642105263157895,0.05789473684210526,0.0,543,0,0.0003513788365828129,2.857894736842105 +Harbor OpenCode-only,600,0.0,221,43,0.0,3396.9366515837105,3017.0,5509.0,750723,750723,186,14.04524886877828,16.29864253393665,18.0,1.4434389140271493,0.00904977375565611,0.0,594,0,0.0002477611582434533,2.6877828054298645 +Harbor OpenCode-only,600,1.0,182,34,1.0,1806.043956043956,1615.5,2775.2000000000003,328700,328700,467,14.983516483516484,16.796703296703296,18.0,4.818681318681318,0.0,0.0,467,0,0.0014207484027989048,2.565934065934066 +Harbor OpenCode-only,700,0.0,226,46,0.0,2648.2433628318586,1924.5,5291.5,598503,598503,20850,10.641592920353983,13.929203539823009,20.0,2.256637168141593,0.017699115044247787,0.004424778761061947,939,0,0.03483691811068616,4.154867256637168 +Harbor OpenCode-only,700,1.0,194,36,1.0,1509.4123711340205,1405.0,2103.3,292826,292826,7044,12.93298969072165,15.45360824742268,18.0,5.644329896907217,0.005154639175257732,0.0,531,0,0.02405524099635961,2.7371134020618557 +Harbor OpenCode-only,800,0.0,215,43,0.0,4039.874418604651,2815.0,7427.799999999998,868573,868573,147535,10.604651162790697,15.190697674418605,21.0,4.511627906976744,0.09767441860465116,0.027906976744186046,1124,0,0.16985906768918674,5.227906976744186 +Harbor OpenCode-only,800,1.0,185,40,1.0,2238.4540540540543,1620.0,2883.9999999999995,414114,414114,30154,10.281081081081082,15.378378378378379,18.0,6.464864864864865,0.043243243243243246,0.0,643,0,0.0728156980927957,3.4756756756756757 +Harbor OpenCode-only,900,0.0,228,50,0.0,3985.8596491228072,2497.5,7192.600000000003,908776,908776,114572,10.609649122807017,13.62280701754386,20.0,1.6973684210526316,0.07894736842105263,0.02631578947368421,1220,0,0.12607287164273703,5.350877192982456 +Harbor OpenCode-only,900,1.0,184,36,1.0,1704.8423913043478,1322.0,2079.3000000000015,313691,313691,30201,9.456521739130435,11.494565217391305,14.700000000000017,2.527173913043478,0.03260869565217391,0.0,535,0,0.096276271872639,2.907608695652174 +Harbor OpenCode-only,1000,0.0,245,45,0.0,4519.910204081632,3128.0,7891.5999999999985,1107378,1107378,46071,12.346938775510203,18.93469387755102,27.0,2.8285714285714287,0.07346938775510205,0.00816326530612245,1162,0,0.04160368004421255,4.742857142857143 +Harbor OpenCode-only,1000,1.0,159,34,1.0,2260.377358490566,1583.0,2885.000000000001,359400,359400,29457,10.163522012578616,15.11320754716981,20.0,3.3333333333333335,0.050314465408805034,0.0,404,0,0.08196160267111853,2.540880503144654 +Harbor multi-harness,100,0.0,231,41,0.0,2939.3290043290044,2382.0,6648.0,678985,678985,1104,13.987012987012987,14.017316017316018,17.0,1.4415584415584415,0.0,0.0,994,0,0.0016259563907891927,4.303030303030303 +Harbor multi-harness,100,1.0,158,32,1.0,1500.2278481012659,1187.0,2712.3,237036,237036,2407,13.734177215189874,13.575949367088608,17.0,4.563291139240507,0.0,0.0,578,0,0.010154575676268584,3.6582278481012658 +Harbor multi-harness,200,0.0,359,59,0.0,3076.016713091922,2368.0,5619.0,1104290,1104290,476,15.309192200557103,15.384401114206128,17.0,2.211699164345404,0.005571030640668524,0.0,1645,0,0.00043104619257622545,4.582172701949861 +Harbor multi-harness,200,1.0,161,35,1.0,1896.304347826087,1555.0,3316.0,305305,305305,609,15.701863354037267,15.701863354037267,17.0,6.6894409937888195,0.006211180124223602,0.0,875,0,0.0019947265848905193,5.434782608695652 +Harbor multi-harness,300,0.0,356,62,0.0,2811.9438202247193,2210.0,5557.5,1001052,1001052,17102,16.06741573033708,16.098314606741575,17.0,2.938202247191011,0.008426966292134831,0.0028089887640449437,1862,0,0.017084027602961686,5.230337078651686 +Harbor multi-harness,300,1.0,158,38,1.0,2073.487341772152,1688.0,3585.6000000000013,327611,327611,212,15.936708860759493,15.943037974683545,17.0,5.765822784810126,0.006329113924050633,0.0,1027,0,0.0006471089188091975,6.5 +Harbor multi-harness,400,0.0,313,54,0.0,2777.4376996805113,2148.0,5305.800000000001,869338,869338,310,15.670926517571885,15.696485623003195,17.0,2.670926517571885,0.003194888178913738,0.0,1455,0,0.0003565931777973584,4.6485623003194885 +Harbor multi-harness,400,1.0,224,41,1.0,1958.861607142857,1565.5,3077.3000000000006,438785,438785,17052,15.888392857142858,15.866071428571429,17.0,6.799107142857143,0.017857142857142856,0.004464285714285714,1360,0,0.03886185717378671,6.071428571428571 +Harbor multi-harness,500,0.0,320,59,0.0,4161.546875,2809.5,8557.500000000002,1331695,1331695,16738,15.89375,15.984375,17.0,2.475,0.01875,0.003125,1823,0,0.01256894409005065,5.696875 +Harbor multi-harness,500,1.0,192,40,1.0,2453.46875,1742.0,4418.500000000001,471066,471066,13,15.598958333333334,15.651041666666666,17.0,6.807291666666667,0.020833333333333332,0.0,807,0,2.7596982163858142e-05,4.203125 +Harbor multi-harness,600,0.0,286,55,0.0,3893.9685314685316,2718.0,8150.5,1113675,1113675,25339,14.55944055944056,14.594405594405595,17.0,3.2412587412587412,0.024475524475524476,0.0034965034965034965,1442,0,0.022752598379239902,5.041958041958042 +Harbor multi-harness,600,1.0,239,51,1.0,2389.945606694561,1957.0,4490.200000000001,571197,571197,147,14.623430962343097,14.619246861924687,17.0,5.539748953974895,0.016736401673640166,0.0,1141,0,0.0002573542928271682,4.7740585774058575 +Harbor multi-harness,700,0.0,291,59,0.0,4066.979381443299,2837.0,7120.0,1183491,1183491,89227,14.353951890034365,14.426116838487973,17.0,3.5463917525773194,0.05154639175257732,0.01718213058419244,1271,0,0.07539305326360742,4.367697594501718 +Harbor multi-harness,700,1.0,224,41,1.0,2165.1473214285716,1920.0,3744.5000000000005,484993,484993,251,14.303571428571429,14.415178571428571,17.0,5.397321428571429,0.013392857142857142,0.0,1338,0,0.0005175332427478335,5.973214285714286 +Harbor multi-harness,800,0.0,321,61,0.0,3426.6417445482866,2493.0,6097.0,1099952,1099952,20586,14.077881619937695,13.962616822429906,17.0,3.6043613707165107,0.037383177570093455,0.003115264797507788,1720,0,0.018715362124892724,5.358255451713395 +Harbor multi-harness,800,1.0,202,42,1.0,3323.128712871287,2503.5,6228.000000000001,671272,671272,89,14.400990099009901,14.430693069306932,17.0,5.069306930693069,0.04950495049504951,0.0,1039,0,0.00013258410897519932,5.143564356435643 +Harbor multi-harness,900,0.0,297,56,0.0,9487.43771043771,6552.0,20822.400000000005,2817769,2817769,401242,12.973063973063972,12.929292929292929,17.0,2.6161616161616164,0.3164983164983165,0.06734006734006734,1609,0,0.14239705241983996,5.417508417508418 +Harbor multi-harness,900,1.0,190,40,1.0,7439.3263157894735,5507.5,16235.199999999999,1413472,1413472,338,14.689473684210526,14.75263157894737,17.0,5.6947368421052635,0.26842105263157895,0.0,976,0,0.00023912748183197122,5.136842105263158 +Harbor multi-harness,1000,0.0,294,56,0.0,9736.969387755102,6942.0,20111.899999999998,2862669,2862669,548463,10.214285714285714,9.982993197278912,17.0,3.183673469387755,0.3843537414965986,0.047619047619047616,1230,0,0.1915914833325124,4.183673469387755 +Harbor multi-harness,1000,1.0,191,44,1.0,9084.256544502618,6725.0,19028.0,1735093,1735093,22631,13.366492146596858,13.172774869109947,17.0,5.769633507853404,0.3612565445026178,0.005235602094240838,918,0,0.01304310489408925,4.806282722513089 +Native OpenCode,100,0.0,231,44,0.0,1311.090909090909,699.0,3337.0,302862,302862,7642,8.363636363636363,7.766233766233766,16.0,0.354978354978355,0.0,0.0,574,0,0.025232614193923305,2.484848484848485 +Native OpenCode,100,1.0,182,38,1.0,602.0274725274726,373.5,1096.9000000000005,109569,109569,5823,5.593406593406593,4.730769230769231,9.0,0.06593406593406594,0.005494505494505495,0.0,416,0,0.05314459381759439,2.2857142857142856 +Native OpenCode,200,0.0,348,51,0.0,1415.4166666666667,942.0,3478.7000000000003,492565,492565,6383,8.295977011494253,8.112068965517242,15.0,0.28160919540229884,0.0,0.0,643,0,0.012958695806644807,1.8477011494252873 +Native OpenCode,200,1.0,80,19,1.0,579.625,396.5,1067.8000000000006,46370,46370,2223,5.7625,4.8625,8.100000000000009,0.025,0.0,0.0,166,0,0.04794047875781755,2.075 +Native OpenCode,300,0.0,323,51,0.0,2044.108359133127,1563.0,4163.8,660247,660247,5858,10.052631578947368,9.613003095975232,16.0,0.43962848297213625,0.0,0.0,587,0,0.008872437133375842,1.8173374613003095 +Native OpenCode,300,1.0,129,26,1.0,631.3410852713179,408.0,1084.6000000000001,81443,81443,3669,5.4728682170542635,4.6434108527131785,8.0,0.07751937984496124,0.0,0.0,278,0,0.04504991220853849,2.1550387596899223 +Native OpenCode,400,0.0,457,67,0.0,1586.070021881838,1050.0,3408.4000000000005,724834,724834,8396,9.321663019693654,8.669584245076587,16.0,0.22319474835886213,0.002188183807439825,0.0,804,0,0.011583341841028428,1.7592997811816193 +Native OpenCode,400,1.0,101,21,1.0,688.940594059406,455.0,1311.0,69583,69583,2159,6.306930693069307,5.336633663366337,9.0,0.1188118811881188,0.0,0.0,215,0,0.031027693545837346,2.128712871287129 +Native OpenCode,500,0.0,393,57,0.0,1364.7328244274809,946.0,2695.6,536340,536340,5112,9.267175572519085,8.743002544529261,15.0,0.4681933842239186,0.002544529262086514,0.0,738,0,0.009531267479583846,1.8778625954198473 +Native OpenCode,500,1.0,128,27,1.0,472.0703125,356.0,863.0999999999999,60425,60425,2466,5.4375,4.59375,7.0,0.0390625,0.0,0.0,259,0,0.04081092263136119,2.0234375 +Native OpenCode,600,0.0,396,56,0.0,1072.871212121212,724.5,2357.5,424857,424857,1207,7.828282828282828,7.401515151515151,14.0,1.4065656565656566,0.0,0.0,1081,0,0.002840955898102185,2.7297979797979797 +Native OpenCode,600,1.0,55,18,1.0,413.3636363636364,326.0,680.4,22735,22735,159,5.054545454545455,4.163636363636364,6.0,0.14545454545454545,0.0,0.0,119,0,0.006993622168462723,2.1636363636363636 +Native OpenCode,700,0.0,294,48,0.0,1307.9761904761904,642.5,3257.8,384545,384545,741,7.476190476190476,6.76530612244898,14.0,0.272108843537415,0.003401360544217687,0.0,579,0,0.0019269526323317167,1.969387755102041 +Native OpenCode,700,1.0,131,28,1.0,483.48091603053433,351.0,889.0,63336,63336,393,5.419847328244275,4.480916030534351,7.0,0.0916030534351145,0.0,0.0,277,0,0.006205001894657067,2.114503816793893 +Native OpenCode,800,0.0,312,47,0.0,1242.4839743589744,603.5,3175.900000000002,387655,387655,770,7.4006410256410255,6.641025641025641,14.0,0.2564102564102564,0.003205128205128205,0.0,589,0,0.0019863022532922315,1.8878205128205128 +Native OpenCode,800,1.0,117,21,1.0,443.1025641025641,298.0,620.6,51843,51843,351,4.982905982905983,3.982905982905983,5.400000000000006,0.02564102564102564,0.0,0.0,243,0,0.006770441525374689,2.076923076923077 +Native OpenCode,900,0.0,262,42,0.0,1514.2519083969466,754.0,3885.500000000001,396734,396734,910,7.759541984732825,7.022900763358779,14.0,0.11068702290076336,0.003816793893129771,0.0,527,0,0.0022937282915000985,2.0114503816793894 +Native OpenCode,900,1.0,151,27,1.0,509.94701986754967,362.0,844.0,77002,77002,471,4.973509933774834,4.086092715231788,6.0,0.006622516556291391,0.0,0.0,342,0,0.006116724240928807,2.2649006622516556 +Native OpenCode,1000,0.0,325,49,0.0,1214.0553846153846,712.0,2890.2000000000007,394568,394568,1260,7.206153846153846,6.513846153846154,16.0,0.29846153846153844,0.0,0.0,1572,0,0.0031933659090448287,4.836923076923077 +Native OpenCode,1000,1.0,77,18,1.0,485.76623376623377,377.0,919.0000000000003,37404,37404,328,5.090909090909091,4.103896103896104,6.0,0.03896103896103896,0.0,0.0,263,0,0.008769115602609347,3.4155844155844157 diff --git a/04-data-agent/reports/three-run-analysis-20260917/training_behavior_windows.csv b/04-data-agent/reports/three-run-analysis-20260917/training_behavior_windows.csv new file mode 100644 index 0000000..aeab0d0 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/training_behavior_windows.csv @@ -0,0 +1,31 @@ +run,window_end,admitted_rollouts,unique_tasks,binary_reward_mean,completion_tokens_mean,completion_tokens_median,completion_tokens_p90,supervised_tokens,completion_tokens,text_only_turn_tokens,agent_turns_mean,tool_calls_mean,tool_calls_p90,exact_repeated_calls_mean,rollout_over4096_fraction,rollout_finish_length_fraction,training_rows,receipt_token_mismatches,text_only_turn_token_share,rows_per_rollout +Harbor OpenCode-only,100,414,53,0.4613526570048309,2174.2487922705313,976.0,4195.299999999999,900139,900139,2772,14.152173913043478,14.577294685990339,17.0,6.584541062801932,0.033816425120772944,0.0,881,0,0.0030795243845672723,2.1280193236714977 +Harbor OpenCode-only,200,432,55,0.22916666666666666,7749.881944444444,2538.0,20144.0,3347949,3347949,16657,13.5,14.974537037037036,18.0,2.3518518518518516,0.2569444444444444,0.0023148148148148147,1101,0,0.004975284868437363,2.548611111111111 +Harbor OpenCode-only,300,410,52,0.36097560975609755,5242.002439024391,2272.0,11004.000000000018,2149221,2149221,17393,14.234146341463415,15.75609756097561,18.0,2.292682926829268,0.0951219512195122,0.0024390243902439024,1077,0,0.008092699633960398,2.626829268292683 +Harbor OpenCode-only,400,409,52,0.3154034229828851,2577.4767726161367,2011.0,4439.8,1054188,1054188,242,14.71393643031785,18.760391198044008,21.0,3.4376528117359415,0.007334963325183374,0.0,977,0,0.00022956057173862727,2.388753056234719 +Harbor OpenCode-only,500,415,54,0.4578313253012048,2965.9204819277106,1964.0,5172.6,1230857,1230857,352,14.573493975903615,16.896385542168673,19.0,2.746987951807229,0.043373493975903614,0.0,1144,0,0.00028597960607934145,2.756626506024096 +Harbor OpenCode-only,600,403,53,0.45161290322580644,2678.468982630273,2078.0,4732.8,1079423,1079423,653,14.468982630272953,16.523573200992555,18.0,2.967741935483871,0.004962779156327543,0.0,1061,0,0.0006049528312811567,2.632754342431762 +Harbor OpenCode-only,700,420,55,0.46190476190476193,2122.211904761905,1552.0,3914.3000000000006,891329,891329,27894,11.7,14.633333333333333,19.0,3.8214285714285716,0.011904761904761904,0.002380952380952381,1470,0,0.031294841747547764,3.5 +Harbor OpenCode-only,800,400,51,0.4625,3206.7175,1945.5,6440.900000000001,1282687,1282687,177689,10.455,15.2775,20.0,5.415,0.0725,0.015,1767,0,0.13852872914436648,4.4175 +Harbor OpenCode-only,900,412,54,0.44660194174757284,2967.152912621359,1510.0,6343.600000000004,1222467,1222467,144773,10.094660194174757,12.672330097087379,19.0,2.0679611650485437,0.05825242718446602,0.014563106796116505,1755,0,0.11842691868164948,4.259708737864078 +Harbor OpenCode-only,1000,404,51,0.3935643564356436,3630.638613861386,2102.0,5953.799999999999,1466778,1466778,75528,11.487623762376238,17.43069306930693,25.0,3.027227722772277,0.06435643564356436,0.0049504950495049506,1566,0,0.05149245489092419,3.876237623762376 +Harbor multi-harness,100,392,44,0.40616966580976865,2351.8239795918366,1865.5,4838.400000000001,921915,921915,3511,13.89030612244898,13.84438775510204,17.0,2.704081632653061,0.0,0.0,1576,0,0.003808377128043258,4.020408163265306 +Harbor multi-harness,200,520,64,0.3096153846153846,2710.7596153846152,2055.0,5254.500000000002,1409595,1409595,1085,15.430769230769231,15.482692307692307,17.0,3.598076923076923,0.0057692307692307696,0.0,2520,0,0.0007697246372184918,4.846153846153846 +Harbor multi-harness,300,516,65,0.30739299610894943,2581.6976744186045,2040.0,4958.0,1332156,1332156,17314,16.017441860465116,16.040697674418606,17.0,3.804263565891473,0.007751937984496124,0.001937984496124031,2893,0,0.012996976330099478,5.6065891472868215 +Harbor multi-harness,400,538,68,0.4171322160148976,2432.817843866171,1798.5,4695.3,1308856,1308856,17362,15.754646840148698,15.760223048327138,17.0,4.386617100371748,0.00929368029739777,0.0018587360594795538,2816,0,0.013265019222893887,5.234200743494424 +Harbor multi-harness,500,512,65,0.375,3521.017578125,2359.0,7624.7000000000035,1802761,1802761,16751,15.783203125,15.859375,17.0,4.099609375,0.01953125,0.001953125,2630,0,0.009291858432704058,5.13671875 +Harbor multi-harness,600,525,66,0.4552380952380952,3209.28,2222.0,6944.000000000001,1684872,1684872,25486,14.588571428571429,14.605714285714285,17.0,4.287619047619048,0.02095238095238095,0.0019047619047619048,2583,0,0.015126371617547209,4.92 +Harbor multi-harness,700,515,66,0.4349514563106796,3239.7747572815533,2412.0,5951.200000000001,1668484,1668484,89478,14.332038834951456,14.421359223300971,17.0,4.351456310679612,0.03495145631067961,0.009708737864077669,2609,0,0.053628323675863836,5.0660194174757285 +Harbor multi-harness,800,523,67,0.3862332695984704,3386.661567877629,2495.0,6157.000000000001,1771224,1771224,20675,14.202676864244742,14.1434034416826,17.0,4.170172084130019,0.04206500956022945,0.0019120458891013384,2759,0,0.011672718978514292,5.275334608030593 +Harbor multi-harness,900,487,62,0.39014373716632444,8688.379876796715,6081.0,18372.4,4231241,4231241,401580,13.64271047227926,13.640657084188911,17.0,3.817248459958932,0.29774127310061604,0.04106776180698152,2585,0,0.09490832594976273,5.308008213552362 +Harbor multi-harness,1000,485,64,0.3938144329896907,9479.921649484537,6776.0,19807.000000000007,4597762,4597762,571094,11.455670103092784,11.239175257731958,17.0,4.202061855670103,0.3752577319587629,0.030927835051546393,2148,0,0.12421130106343042,4.4288659793814436 +Native OpenCode,100,413,52,0.4406779661016949,998.6222760290557,483.0,2787.8,412431,412431,13465,7.142857142857143,6.428571428571429,14.800000000000011,0.22760290556900725,0.002421307506053269,0.0,990,0,0.032647885343245295,2.3970944309927362 +Native OpenCode,200,428,55,0.18691588785046728,1259.193925233645,710.5,3299.6,538935,538935,8606,7.822429906542056,7.5046728971962615,15.0,0.2336448598130841,0.0,0.0,809,0,0.015968530527800198,1.8901869158878504 +Native OpenCode,300,453,58,0.2853982300884956,1642.3841059602648,1004.0,3866.0,744000,744000,9527,8.754966887417218,8.205298013245033,15.0,0.33774834437086093,0.0,0.0,866,0,0.01280510752688172,1.9116997792494481 +Native OpenCode,400,558,71,0.18100358422939067,1423.6863799283153,875.5,3166.5,794417,794417,10555,8.775985663082437,8.0663082437276,15.0,0.20430107526881722,0.0017921146953405018,0.0,1019,0,0.013286472973262154,1.8261648745519714 +Native OpenCode,500,521,66,0.2456813819577735,1145.42226487524,743.0,2489.0,596765,596765,7578,8.326295585412668,7.723608445297505,15.0,0.362763915547025,0.0019193857965451055,0.0,997,0,0.012698465895285414,1.9136276391554703 +Native OpenCode,600,451,57,0.12195121951219512,992.4434589800444,639.0,2268.0,447592,447592,1366,7.490022172949002,7.006651884700665,13.0,1.252771618625277,0.0,0.0,1200,0,0.0030518865395270695,2.6607538802660753 +Native OpenCode,700,425,54,0.30823529411764705,1053.8376470588234,504.0,2649.6000000000026,447881,447881,1134,6.842352941176471,6.061176470588236,13.0,0.2164705882352941,0.002352941176470588,0.0,856,0,0.002531922541925199,2.0141176470588236 +Native OpenCode,800,429,54,0.2727272727272727,1024.4708624708624,453.0,2760.1999999999994,439498,439498,1121,6.741258741258742,5.916083916083916,13.0,0.19347319347319347,0.002331002331002331,0.0,832,0,0.002550637318031026,1.9393939393939394 +Native OpenCode,900,413,53,0.36561743341404357,1147.0605326876514,508.0,2972.2000000000003,473736,473736,1381,6.7409200968523,5.9491525423728815,13.0,0.07263922518159806,0.002421307506053269,0.0,869,0,0.0029151257240319505,2.1041162227602905 +Native OpenCode,1000,402,53,0.19154228855721392,1074.5572139303483,636.5,2226.4000000000005,431972,431972,1588,6.800995024875622,6.052238805970149,13.0,0.24875621890547264,0.0,0.0,1835,0,0.0036761641958275074,4.564676616915423 diff --git a/04-data-agent/reports/three-run-analysis-20260917/training_diagnostics.png b/04-data-agent/reports/three-run-analysis-20260917/training_diagnostics.png new file mode 100644 index 0000000..cd76770 Binary files /dev/null and b/04-data-agent/reports/three-run-analysis-20260917/training_diagnostics.png differ diff --git a/04-data-agent/reports/three-run-analysis-20260917/training_exposure.png b/04-data-agent/reports/three-run-analysis-20260917/training_exposure.png new file mode 100644 index 0000000..fd8b99e Binary files /dev/null and b/04-data-agent/reports/three-run-analysis-20260917/training_exposure.png differ diff --git a/04-data-agent/reports/three-run-analysis-20260917/training_rollout_totals.csv b/04-data-agent/reports/three-run-analysis-20260917/training_rollout_totals.csv new file mode 100644 index 0000000..aaca2e7 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/training_rollout_totals.csv @@ -0,0 +1,4 @@ +run,admitted_rollouts,unique_tasks,binary_reward_mean,completion_tokens_mean,completion_tokens_median,completion_tokens_p90,supervised_tokens,completion_tokens,text_only_turn_tokens,agent_turns_mean,tool_calls_mean,tool_calls_p90,exact_repeated_calls_mean,rollout_over4096_fraction,rollout_finish_length_fraction,training_rows,receipt_token_mismatches,text_only_turn_token_share,rows_per_rollout +Harbor OpenCode-only,4119,523,0.4032532168001942,3550.628307841709,1836.0,6105.800000000007,14625038,14625038,463953,12.944889536295218,15.738771546491867,19.0,3.4644331148336973,0.0657926681233309,0.004127215343529983,12799,0,0.03172319962519072,3.1073075989317798 +Harbor multi-harness,5013,410,0.3872578390253645,4135.022142429682,2441.0,9114.400000000001,20728866,20728866,1164336,14.553959704767605,14.55016955914622,17.0,3.974266906044285,0.07979253939756632,0.008976660682226212,25119,0,0.056169787580275736,5.010771992818672 +Native OpenCode,4493,566,0.256233303650935,1185.6726018250613,633.0,2940.8,5327227,5327227,56321,7.6107277987981306,6.9603828177164475,15.0,0.3383040284887603,0.0013354106387714222,0.0,10273,0,0.010572292113701932,2.28644558201647 diff --git a/04-data-agent/reports/three-run-analysis-20260917/training_step_diagnostics.csv b/04-data-agent/reports/three-run-analysis-20260917/training_step_diagnostics.csv new file mode 100644 index 0000000..e17d3cc --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/training_step_diagnostics.csv @@ -0,0 +1,31 @@ +run,window_end,reward,reward_std,tools/call_frequency,tools/failure_frequency,rollout/turns_mean,rollout/fork_frac,rollout/samples_per_rollout,completions/mean_length,completions/clipped_ratio,zero_gradient,perf/step_s,perf/fwd_bwd_s,perf/rollout_wait_s,batch/forwarded_tokens_per_step,batch/trained_tokens_per_step,forwarded_per_supervised_token +Harbor OpenCode-only,100,0.4613268398268398,0.2398699027380629,14.713121933621933,0.12279162456462402,14.271011904761904,0.08981528260297379,2.171547619047619,2565.4408452380953,0.0,0.35,72.34649321350504,9.118664061059535,60.09085775610933,22391797.0,900139.0,24.875932494870238 +Harbor OpenCode-only,200,0.24554958440252558,0.20753841008734042,15.661243906420378,0.22326203249355886,13.33732993197279,0.1541956229715745,2.571364795918367,8244.417920918368,0.008928571428571428,0.39,126.94011013682001,18.04403841590007,98.18580851653998,39209277.0,3347949.0,11.71143198417897 +Harbor OpenCode-only,300,0.3708239815739816,0.19694757694321144,16.069899295149295,0.19153211177615806,14.237745098039216,0.12875551244199257,2.6127450980392157,5064.541666666667,0.007352941176470588,0.39,96.98847900031993,14.141838750521856,78.69381559354166,33137240.0,2149221.0,15.418256196082208 +Harbor OpenCode-only,400,0.3105725108225108,0.2271403104089478,19.034032231657232,0.1724393838204193,14.713235294117647,0.1021339224280583,2.394607843137255,2574.9313725490197,0.0,0.33,76.02990748340002,10.865588490648951,62.00304648158941,27069040.0,1054188.0,25.67762106948666 +Harbor OpenCode-only,500,0.46779365079365076,0.18029720685931688,17.237106809856808,0.14127172032796412,14.5453197945845,0.12750780485480157,2.7301159352629942,3332.42470821662,0.0024509803921568627,0.48,75.64743303306008,13.32731223725903,57.816416095330354,32791980.0,1230857.0,26.641583872050123 +Harbor OpenCode-only,600,0.4499239094239094,0.19571289410877754,16.578901348651346,0.1763313325828645,14.329111458716723,0.12504802676395887,2.660502874318664,3108.048309439099,0.0,0.52,60.88787030558,13.256466008439602,43.034404825770906,32807969.0,1079423.0,30.393987343238006 +Harbor OpenCode-only,700,0.4436214711715873,0.2003848720445334,15.192719801408172,0.22907039219006176,11.674995168521955,0.2492364847873423,3.63704745155638,2175.0650726010103,0.002232142857142857,0.43,64.5336026951499,17.211821312580142,43.838990044047826,43604234.0,891329.0,48.920470443573585 +Harbor OpenCode-only,800,0.438452406417842,0.2612226987006718,15.826681698851091,0.2192432145564864,10.194886958279815,0.35625899786629783,4.21397018160411,3152.636801442605,0.014668367346938774,0.26,72.59849311632003,25.298051341829705,42.60313191080044,62573373.0,1282687.0,48.783041381100766 +Harbor OpenCode-only,900,0.4337144914822021,0.2566402646216497,13.61259492586263,0.22333017742691244,10.406444991789819,0.35978850404867146,4.564449917898194,3654.4251231527096,0.029248768472906403,0.31,70.1935425982601,26.00453474922746,38.00356280478198,62958003.0,1222467.0,51.500779162136894 +Harbor OpenCode-only,1000,0.33886414499712514,0.22169366935889592,17.96717597866881,0.14836250005459165,11.04720133667502,0.2651926560421726,3.785609857978279,3874.793149540518,0.004699248120300751,0.34,79.97377342697,26.870135988060063,47.05893725607006,62885553.0,1466778.0,42.87325893898054 +Harbor multi-harness,100,0.37215481064105627,0.2817602841205276,14.091755350686729,0.16644353977207949,13.608209208247729,0.27170567711999943,4.371736225174313,2434.2689411490683,0.0,0.17,219.83294780647236,78.68988377495785,160.96184653661913,187688023.0,2341485.0,80.15768753590136 +Harbor multi-harness,200,0.33429811481932725,0.2209406219192409,15.786707289453354,0.15555696292824975,15.463118242466068,0.26966753878799526,4.875117897400506,2749.4112031285945,0.0,0.41,94.76055106370606,44.1395378899423,47.15803065143642,101082358.0,1409595.0,71.71021321727163 +Harbor multi-harness,300,0.3078947098658278,0.237504531226522,16.205064360533626,0.19417233135332557,16.002894709959925,0.29052063159214797,5.542150281280716,2981.8417820705863,0.0,0.34,89.43567845421029,43.12261901319493,44.12112560480717,101103979.0,1332156.0,75.89499953458905 +Harbor multi-harness,400,0.4208965591133613,0.1347151666009812,16.046035668255467,0.16542645323759192,15.65814536340852,0.25465502643949534,4.856035923141186,2405.253268588137,0.0,0.49,91.19121140880044,33.57061344736023,54.94743524379679,84605646.0,1308856.0,64.64091236927516 +Harbor multi-harness,500,0.32965815112938374,0.21353757183881694,15.979141145963936,0.20272489386589615,15.858448827292111,0.25258061664179454,4.957356076759062,3658.3221304193316,0.0,0.36,87.1897372502496,36.77998640247155,47.74810984188574,90166268.0,1802761.0,50.01565265722966 +Harbor multi-harness,600,0.42514402674199275,0.21481521660147151,14.885919789409268,0.14239050757746663,14.5796066252588,0.28829488482569093,4.903295376121463,3503.119858523119,0.0018115942028985507,0.3,83.39630996603984,34.40547226127121,46.63930073371972,85677872.0,1684872.0,50.8512646658025 +Harbor multi-harness,700,0.43366307997193104,0.21850890470961898,14.809511820629087,0.13326109980800552,13.886473014165322,0.2655647788294409,4.441551397320628,3437.0248391352234,0.008974358974358974,0.36,87.71859020842334,35.783885467979125,49.40421284527634,86376800.0,1668484.0,51.76963039501727 +Harbor multi-harness,800,0.35281411250347294,0.19344944471574962,14.598113697768602,0.12470995081799482,13.979096924317513,0.25206306693000136,4.7138252270605205,3910.5884460514976,0.001225490196078431,0.39,92.73759917171206,36.07318357932381,52.21698128161486,87725983.0,1771224.0,49.528452076078466 +Harbor multi-harness,900,0.35901134931123724,0.2291403835501686,14.18688828568166,0.1429223534360349,13.163304988662132,0.317435083122598,4.824875798804371,9480.489273345702,0.04736394557823129,0.35,115.72125592437573,42.08636902739294,67.09815469327849,98376730.0,4231241.0,23.250089040071224 +Harbor multi-harness,1000,0.41036747714576377,0.24216930597349595,12.230913871451857,0.12421673095427341,11.471771610742199,0.31062348236496173,4.37483839689722,10657.320653243816,0.027328431372549016,0.32,125.29679813764058,33.5303732728539,84.98472009332384,78519197.0,4597762.0,17.077699324149446 +Native OpenCode,100,0.4384733044733045,0.23678973565545625,6.595128038628038,0.1250655540736819,7.402300692602417,0.2642018642181204,2.435050366300366,1097.8185752705149,0.0,0.34,31.587826412050504,11.618848592969988,18.02173868855992,28972901.0,412431.0,70.24908651386535 +Native OpenCode,200,0.20371825396825397,0.11595934084676054,7.46128434065934,0.21892108315969483,7.8879166666666665,0.1851386637967458,1.9196230158730159,1262.6713690476192,0.0,0.58,50.66147141980001,12.501171760049928,34.67551194384007,30938728.0,538935.0,57.40716041823225 +Native OpenCode,300,0.2764642857142857,0.13116962071445037,8.043817460317461,0.24209013075995742,8.756902958152958,0.16471670689892573,1.9301722582972582,1646.2756998557,0.0,0.57,51.83079009951,9.259974337550211,40.43463070367999,23808276.0,744000.0,32.00037096774194 +Native OpenCode,400,0.17345833333333333,0.10571283425688895,8.150039682539681,0.23606811371498868,8.903753306878308,0.13761309659417711,1.8108134920634922,1482.5227237654321,0.0,0.66,41.88394934417998,5.251358338560167,34.75259857053005,14416198.0,794417.0,18.14689010935063 +Native OpenCode,500,0.262,0.10691747367582768,7.858011904761904,0.23863880565256199,8.499986881805064,0.1576233237569586,1.8871507280598188,1167.200844483799,0.0,0.63,36.184499155539996,4.691997400669898,29.273478976599144,13293439.0,596765.0,22.27583554665572 +Native OpenCode,600,0.12442532467532467,0.1306467763651302,7.764659241575418,0.2731742470870204,7.495316159250586,0.266928864144792,2.6837919594067134,1031.9630171740828,0.0,0.59,33.34266252039001,7.050879189619446,22.431043816850252,19805302.0,447592.0,44.248561189654865 +Native OpenCode,700,0.31109498834498833,0.17358571064202213,6.122058441558441,0.26768672319235504,6.757709176788124,0.216580856021291,1.9953385065227172,972.7612516869096,0.0,0.51,27.225210022760002,5.668830248999875,19.533168102390228,15542322.0,447881.0,34.70190072809518 +Native OpenCode,800,0.27081349206349203,0.10170429946793036,5.850321428571428,0.23879520652958153,6.734375,0.21248057847110624,1.9387019230769231,1022.0012019230769,0.0,0.64,26.7776295466,5.835523945260138,18.29469989886984,16191147.0,439498.0,36.84009256014817 +Native OpenCode,900,0.3780271534021534,0.12676802095135348,5.842643134643135,0.24723269360709835,6.7568012290739565,0.23655465123164057,2.0432350982350984,1192.0391753700844,0.0,0.59,30.724514038079977,8.463444579549396,20.111304883080336,22503593.0,473736.0,47.50239162740429 +Native OpenCode,1000,0.18717783882783884,0.10568101863877383,6.7947657674487445,0.28285419085527297,6.825864955357143,0.5819421111620064,4.616029456654457,1564.7171581673535,0.015625,0.72,32.92978656568004,15.960202438620545,14.157918948470396,42941177.0,431972.0,99.40731575194688 diff --git a/04-data-agent/reports/three-run-analysis-20260917/training_totals.csv b/04-data-agent/reports/three-run-analysis-20260917/training_totals.csv new file mode 100644 index 0000000..6aeb1f4 --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/training_totals.csv @@ -0,0 +1,4 @@ +run,forwarded_tokens,supervised_tokens,mean_step_s,step_timing_observations,max_staleness_observed,fraction_steps_staleness_gt4,nonfinite_grad_steps +Harbor OpenCode-only,419428466.0,14625038.0,79.62124525297799,999,4.0,0.0,0 +Harbor multi-harness,1001322856.0,22148436.0,108.20385101597033,993,4.0,0.0,0 +Native OpenCode,228413083.0,5327227.0,36.3195656516987,999,4.0,0.0,0 diff --git a/04-data-agent/reports/three-run-analysis-20260917/training_windows.csv b/04-data-agent/reports/three-run-analysis-20260917/training_windows.csv new file mode 100644 index 0000000..b20b3df --- /dev/null +++ b/04-data-agent/reports/three-run-analysis-20260917/training_windows.csv @@ -0,0 +1,31 @@ +run,window_end,reward,entropy,grad_norm,sample/staleness_mean,sample/staleness_max,tools/call_frequency,tools/failure_frequency,batch/forwarded_tokens_per_step,batch/trained_tokens_per_step,perf/step_s,rollout/samples_per_rollout,completions/mean_length,clip_ratio/region_mean,kl +Harbor OpenCode-only,100,0.4613268398268398,0.09685979937518965,2.667939453125,2.0316666666666667,2.24,14.713121933621933,0.12279162456462402,223917.97,9001.39,72.34649321350504,2.171547619047619,2565.4408452380953,0.004187458868193879,0.005040083591849535 +Harbor OpenCode-only,200,0.24554958440252558,0.09733831891737652,1.1925244140625,1.7242142857142857,2.32,15.661243906420378,0.22326203249355886,392092.77,33479.49,126.94011013682001,2.571364795918367,8244.417920918368,0.002383259688850152,0.0030197780759285025 +Harbor OpenCode-only,300,0.3708239815739816,0.14049491090903823,1.64328125,1.7858333333333334,2.42,16.069899295149295,0.19153211177615806,331372.4,21492.21,96.98847900031993,2.6127450980392157,5064.541666666667,0.003933157082318967,0.002670543493925051 +Harbor OpenCode-only,400,0.3105725108225108,0.13660548355254132,1.80888671875,2.042333333333333,2.46,19.034032231657232,0.1724393838204193,270690.4,10541.88,76.02990748340002,2.394607843137255,2574.9313725490197,0.0030803969387157336,0.0018282461108336781 +Harbor OpenCode-only,500,0.46779365079365076,0.13306280535237497,1.560234375,2.022333333333333,2.33,17.237106809856808,0.14127172032796412,327919.8,12308.57,75.64743303306008,2.7301159352629942,3332.42470821662,0.0025820452011899382,0.002231793405654206 +Harbor OpenCode-only,600,0.4499239094239094,0.1470729348302163,1.78265625,2.184,2.29,16.578901348651346,0.1763313325828645,328079.69,10794.23,60.88787030558,2.660502874318664,3108.048309439099,0.001838536804866822,0.001952234455951008 +Harbor OpenCode-only,700,0.4436214711715873,0.14341516735157175,2.1547265625,1.9445,2.26,15.192719801408172,0.22907039219006176,436042.34,8913.29,64.5336026951499,3.63704745155638,2175.0650726010103,0.002594599951658072,0.0018724857904997049 +Harbor OpenCode-only,800,0.438452406417842,0.17447699038519943,2.55837890625,2.11,2.46,15.826681698851091,0.2192432145564864,625733.73,12826.87,72.59849311632003,4.21397018160411,3152.636801442605,0.003508212072951627,0.001373625607360752 +Harbor OpenCode-only,900,0.4337144914822021,0.17869817124870213,2.6642578125,2.1835,2.35,13.61259492586263,0.22333017742691244,629580.03,12224.67,70.1935425982601,4.564449917898194,3654.4251231527096,0.0030231491391100518,0.001325954932362795 +Harbor OpenCode-only,1000,0.33886414499712514,0.1568996988469791,2.265947265625,2.019,2.47,17.96717597866881,0.14836250005459165,628855.53,14667.78,79.97377342697,3.785609857978279,3874.793149540518,0.0020346568106116418,0.000934227482170192 +Harbor multi-harness,100,0.37215481064105627,0.2637117265651165,4.17544921875,1.7269317640313784,2.1,14.091755350686729,0.16644353977207949,1876880.23,23414.85,219.83294780647236,4.371736225174313,2434.2689411490683,0.009547546282052555,0.008544461813041776 +Harbor multi-harness,200,0.33429811481932725,0.2817592404597404,1.7822265625,1.4976190476190476,1.89,15.786707289453354,0.15555696292824975,1010823.58,14095.95,94.76055106370606,4.875117897400506,2749.4112031285945,0.003997303393562508,0.0039874680662799035 +Harbor multi-harness,300,0.3078947098658278,0.2556433835762293,1.98423828125,1.5767619047619048,1.82,16.205064360533626,0.19417233135332557,1011039.79,13321.56,89.43567845421029,5.542150281280716,2981.8417820705863,0.004483454293340759,0.0031596616756331096 +Harbor multi-harness,400,0.4208965591133613,0.2602957298295186,1.326943359375,1.2613214285714285,1.79,16.046035668255467,0.16542645323759192,846056.46,13088.56,91.19121140880044,4.856035923141186,2405.253268588137,0.0027571686920980134,0.00267292184568319 +Harbor multi-harness,500,0.32965815112938374,0.3421668481281644,1.8252734375,1.516702380952381,1.93,15.979141145963936,0.20272489386589615,901662.68,18027.61,87.1897372502496,4.957356076759062,3658.3221304193316,0.003900711896447942,0.002308183441914882 +Harbor multi-harness,600,0.42514402674199275,0.31104822814197736,2.1425537109375,1.474047619047619,1.85,14.885919789409268,0.14239050757746663,856778.72,16848.72,83.39630996603984,4.903295376121463,3503.119858523119,0.005506542724431796,0.0024274147227585107 +Harbor multi-harness,700,0.43366307997193104,0.34037941961742574,2.196533203125,1.6215357142857143,1.85,14.809511820629087,0.13326109980800552,863768.0,16684.84,87.71859020842334,4.441551397320628,3437.0248391352234,0.004985732428707765,0.0028271833525996036 +Harbor multi-harness,800,0.35281411250347294,0.2978964158105625,1.893984375,1.555404761904762,1.86,14.598113697768602,0.12470995081799482,877259.83,17712.24,92.73759917171206,4.7138252270605205,3910.5884460514976,0.004122921543132058,0.0021502267781378782 +Harbor multi-harness,900,0.35901134931123724,0.3623014800418172,1.49798828125,1.5702738095238096,2.04,14.18688828568166,0.1429223534360349,983767.3,42312.41,115.72125592437573,4.824875798804371,9480.489273345702,0.0035995473452451092,0.0015889896107803862 +Harbor multi-harness,1000,0.41036747714576377,0.32723013623204006,1.3534666442871093,1.5440833333333333,1.96,12.230913871451857,0.12421673095427341,785191.97,45977.62,125.29679813764058,4.37483839689722,10657.320653243816,0.003878371968903037,0.001586671160716524 +Native OpenCode,100,0.4384733044733045,0.17068306005160314,4.484921875,2.1381666666666668,2.38,6.595128038628038,0.1250655540736819,289729.01,4124.31,31.587826412050504,2.435050366300366,1097.8185752705149,0.004409961396235686,0.008285643573207067 +Native OpenCode,200,0.20371825396825397,0.18744950996944607,2.21181640625,1.7630238095238095,2.36,7.46128434065934,0.21892108315969483,309387.28,5389.35,50.66147141980001,1.9196230158730159,1262.6713690476192,0.0007772616376360563,0.003431557870288053 +Native OpenCode,300,0.2764642857142857,0.17872697230910006,2.28291015625,1.7443214285714286,2.11,8.043817460317461,0.24209013075995742,238082.76,7440.0,51.83079009951,1.9301722582972582,1646.2756998557,0.002291983241470562,0.006575444186290422 +Native OpenCode,400,0.17345833333333333,0.19328260360224725,1.5931640625,1.2819761904761904,1.71,8.150039682539681,0.23606811371498868,144161.98,7944.17,41.88394934417998,1.8108134920634922,1482.5227237654321,0.0031121111611040002,0.004288135047909705 +Native OpenCode,500,0.262,0.15426974010064506,1.8798828125,1.4659166666666668,1.88,7.858011904761904,0.23863880565256199,132934.39,5967.65,36.184499155539996,1.8871507280598188,1167.200844483799,0.002459962850667994,0.003597480714192069 +Native OpenCode,600,0.12442532467532467,0.14728269294145113,2.8753125,1.8172857142857142,2.21,7.764659241575418,0.2731742470870204,198053.02,4475.92,33.34266252039001,2.6837919594067134,1031.9630171740828,0.002019672447124215,0.0032964696410401496 +Native OpenCode,700,0.31109498834498833,0.10232620991616216,4.194765625,1.9360238095238094,2.33,6.122058441558441,0.26768672319235504,155423.22,4478.81,27.225210022760002,1.9953385065227172,972.7612516869096,0.001808775189040755,0.001616135894910377 +Native OpenCode,800,0.27081349206349203,0.10113436551513161,2.444013671875,1.7724761904761905,2.31,5.850321428571428,0.23879520652958153,161911.47,4394.98,26.7776295466,1.9387019230769231,1022.0012019230769,0.000773144740999812,0.0009989796439796794 +Native OpenCode,900,0.3780271534021534,0.12660436471971986,2.2771875,1.7313571428571428,2.38,5.842643134643135,0.24723269360709835,225035.93,4737.36,30.724514038079977,2.0432350982350984,1192.0391753700844,0.000942620709915634,0.0011704037453183133 +Native OpenCode,1000,0.18717783882783884,0.14192835536074921,1.9496484375,2.1566666666666667,2.25,6.7947657674487445,0.28285419085527297,429411.77,4319.72,32.92978656568004,4.616029456654457,1564.7171581673535,0.0009637467219425069,0.0021438640694901962 diff --git a/04-data-agent/reproduce.md b/04-data-agent/reproduce.md new file mode 100644 index 0000000..515ab62 --- /dev/null +++ b/04-data-agent/reproduce.md @@ -0,0 +1,149 @@ +# Reproduce training and evaluation + +Use one recipe at a time initially. A training smoke performs **two optimizer steps → save/upload → verified remote restore → two more steps**. It must show exact-token capture, finite updates, changed weights and native optimizer state before a long run is admitted. + +## 1. Requirements and credentials + +Use Linux, Python 3.12, Git, [`uv`](https://docs.astral.sh/uv/), and an HF account. Local runs need Slurm and two suitable CUDA GPUs on one node; HF Jobs need organization Jobs permissions and GPU quota. The default namespace is `HuggingEnvs`; use `--namespace YOUR_ORG` during preparation to create resources in your own organization. + +The frozen task/runtime bundle is public at [HuggingEnvs/data-agent-daytona-repro](https://huggingface.co/datasets/HuggingEnvs/data-agent-daytona-repro). Use the exact archive named in `hf/configs/sources.json`; `hf/build.py --seed-archive PATH` verifies its SHA-256. It never substitutes today's dataset for the measured train/test split. [The public artifact index](https://huggingface.co/datasets/HuggingEnvs/data-agent-experiment-results) links environments, dashboards, results and published checkpoints. + +```bash +cd 04-data-agent +uv venv --python 3.12 .venv-launcher +uv pip install --python .venv-launcher/bin/python 'huggingface-hub==1.26.0' python-dotenv +source .venv-launcher/bin/activate +``` + +Create `.env` (ignored by Git): + +```dotenv +HF_API_KEY=your_hf_token +DAYTONA_API_KEY=your_daytona_key +# Optional for a non-default Daytona deployment: +# DAYTONA_API_URL=https://app.daytona.io/api +# DAYTONA_TARGET=eu +``` + +The HF token needs permission to launch Jobs and manage the selected Spaces and run storage. Public bundles and published results can be read without organization membership. The launcher sends credentials as secrets; they are excluded from the bundle and launch metadata. Live raw artifacts remain private: captured tool output can contain credentials. Public releases use audited copies, with redacted files identified in a manifest. The native adapter also supports HF/E2B sandboxes, but these training recipes select Daytona. + +## 2. Select and freeze a recipe + +Choose `harbor-multi`, `harbor-opencode`, `native-opencode`, or `seta`. The first two use Harbor; the third uses `envs/blackbox-opencode` directly. + +```bash +python reproduce.py prepare --recipe harbor-opencode --env-file .env \ + --run-id my-harbor-opencode-01 +``` + +The default output is `temp/reproduction/harbor-opencode/`. Use the same `--recipe` and, if supplied, `--out` in subsequent commands. Preparing an existing run directory is rejected to preserve its identity. A new run should use a new output directory and run ID. + +The preparation pins OpenEnv and TRL commits, Qwen3.5-2B revision, the 1,000 training tasks, the 250 test tasks and schedule hashes. Python and shell paths are relocated inside the bundle. Package versions are frozen in separate environment/training lockfiles. + +| Setting | Async Harbor / native OpenCode | Sync SETA | +| --- | --- | --- | +| Model | Qwen3.5-2B, pinned revision | Same | +| Training tasks | 150 easy / 600 medium / 250 hard | Same fixed order | +| Initial curriculum | First 32 tasks easy; then shuffled | Same | +| Learning rate / generations | `3e-6` / 8 | Same | +| Sampling | Temperature 0.8, top-p 1, top-k disabled; thinking off | Same | +| Max context / output per call | 131,072 / 16,384 | Same model limits; tool-loop budget differs | +| Staleness | 4 | Synchronous | +| Backpressure | 32 workers, 16 outstanding rollouts; whole-group admission | Native sync batches | +| Save / eval | Every 50 / 100 optimizer steps | Same | +| Step / wall-clock ceiling | 1,000 / about 23 hours plus checkpoint grace | Same | +| Checkpoint suite | Four harnesses × 250 tests | Native SETA × 250 tests | + +The 32-worker ceiling is not a claim that eight generations always run simultaneously. Backpressure, group readiness and provider capacity determine active work. Multi-harness training assigns one harness per task per pass and rotates assignments on later passes. Four thousand task/harness pairs are not four thousand optimizer steps. + +## 3A. HF Jobs and Spaces + +First upload the prepared bundle. Deploy only into an idle environment or a new owned Space; deployment restarts that Space. Existing ongoing runs must finish before changing their environment. + +```bash +python reproduce.py upload --recipe harbor-opencode --env-file .env +python reproduce.py spaces --recipe harbor-opencode --env-file .env +python reproduce.py eval --recipe harbor-opencode --env-file .env --flavor a100-large +python reproduce.py smoke --recipe harbor-opencode --env-file .env --flavor a100x4 +python reproduce.py status --recipe harbor-opencode --env-file .env +``` + +Hub eval uses **TP1/DP1 on one A100 80GB**, concurrency **35**, and the fixed pass@1 cohort. To change concurrency, set `--concurrency` during preparation so the chosen value is frozen. Increasing it does not create more sandbox quota. Training uses separate inference and optimizer GPUs: `h200x2` or `a100x4` (the recipe uses two of the four A100s). SETA's validated HF training allocation is `h200x2`. + +For a smoke against an already-deployed Space, skip the deployment command and pass its exact `/deployment` `bundle_sha256`: + +```bash +python reproduce.py smoke --recipe seta --env-file .env --flavor h200x2 \ + --space-bundle-sha EXACT_DEPLOYED_SHA256 +``` + +This explicitly records two source identities: the trainer bundle and the existing environment bundle. It does not upgrade the live Space or certify untested server changes. Async training checks the advertised rollout API before allocating a Job; an older native OpenCode server without explicit sampling support must be upgraded while idle. The current qualification preserves the active Harbor/SETA deployments and updates the idle native OpenCode Space. + +Once both jobs complete and their evidence passes: + +```bash +python reproduce.py train --recipe harbor-opencode --env-file .env \ + --flavor a100x4 --baseline-job BASELINE_JOB_ID --smoke-job SMOKE_JOB_ID +``` + +The launcher submits a separate CPU coordinator. At steps 100, 200, … it waits for the completed checkpoint manifest, submits a separate A100 eval Job, and verifies the model hash before serving it. Step-50 checkpoints remain available for later evaluations. The final checkpoint is also eligible. A failed or ambiguous eval submission is recorded for reconciliation; it is not blindly duplicated. + +Native OpenCode's baseline command measures its **native** 250-task protocol on the configured sandboxes. Its **checkpoint comparisons** use the four-harness Harbor Space. Deploy the Harbor environment as well when reproducing native OpenCode in a new namespace. Keep that Space's task/harness pins fixed. Native and Harbor baseline percentages are different cohorts and must be labelled accordingly. + +For native OpenCode, run the shared four-harness baseline with the `harbor-opencode` recipe too (or reuse a completed matching baseline). Long-run admission requires both: the native diagnostic verifies that environment's grading, and the Harbor baseline supplies step 0 of the checkpoint curve. + +```bash +python reproduce.py train --recipe native-opencode --env-file .env \ + --baseline-job NATIVE_DIAGNOSTIC_JOB --comparison-baseline-job HARBOR_BASELINE_JOB \ + --smoke-job NATIVE_SMOKE_JOB --flavor a100x4 +``` + +The launcher checks fixed task/model/sampling identity and 250 results for each of the four harnesses. It never places the native diagnostic percentage on the four-harness curve. + +## 3B. Local / Slurm + +Use the same preparation command. The local launcher runs both the environment service and vLLM inside the allocation. Training uses one inference GPU and one optimizer GPU; eval uses TP1/DP2 on two GPUs. Local eval defaults to concurrency 50. + +```bash +python reproduce.py eval --platform local --recipe harbor-opencode --env-file .env \ + --partition YOUR_GPU_PARTITION --submit +python reproduce.py smoke --platform local --recipe harbor-opencode --env-file .env \ + --partition YOUR_GPU_PARTITION --submit +``` + +Omit `--submit` to inspect generated Slurm scripts first. The allocation creates its own hash-locked venvs. To reuse validated local environments without modifying them, supply both `--train-venv /path/to/train-venv` and `--env-venv /path/to/env-venv`. Use a shared filesystem with room for the model, optimizer states and captures. + +Before the long run, qualify the checkpoint controller against the smoke's checkpoint 4. The advanced helper prints the resulting plan: + +```bash +python hf/local_long.py --arm blackbox \ + --smoke-run /absolute/path/to/repro/outputs/local-train-blackbox-JOB_ID \ + --baseline-score /absolute/path/to/baseline/canonical_scores.json \ + --env-file .env --out temp/checkpoint-qualification \ + --coordination-dir temp/eval-admission --qualify-checkpoint-eval +python hf/local_followup.py watch --plan temp/checkpoint-qualification/plan.json --submit +``` + +After the independent checkpoint eval produces `checkpoint_eval_verified.json`: + +```bash +python reproduce.py train --platform local --recipe harbor-opencode --env-file .env \ + --partition YOUR_GPU_PARTITION --cpu-partition YOUR_CPU_PARTITION \ + --smoke-run /absolute/path/to/repro/outputs/local-train-blackbox-JOB_ID \ + --baseline-score /absolute/path/to/baseline/canonical_scores.json \ + --checkpoint-eval-proof /absolute/path/to/checkpoint_eval_verified.json --submit +``` + +Use internal arm `opencode` for native OpenCode and `whitebox` for SETA when invoking advanced helpers. Native long-run admission also checks the frozen task grading/tolerance audit; preserve the `verification.json` next to its canonical baseline score. No recipe uses `hopper-extra` or `hopper-atl` by default. Partition names are explicit deployment settings, not source edits. + +For local native training, add `--comparison-baseline-score /absolute/path/to/harbor-baseline/repro/outputs/local-eval-blackbox-JOB_ID/canonical_scores.json` to the qualification and long-run commands. Keep that baseline inside its prepared runtime: admission reads the adjacent frozen configuration to verify the comparison protocol. `--baseline-score` remains the separate native diagnostic. The controller records the matching baseline at step 0 and rejects a modified score file. + +## Evaluation protocol and logs + +Pass@1 keeps the **first graded** attempt for each task/harness cell, including a zero. Only an ungraded infrastructure failure can be retried. Publication requires complete fixed coverage, exact-token audit, task hashes, harness versions and checkpoint provenance. The test set is 33 easy, 118 medium and 99 hard; overall scores are computed from counts, not an unweighted average of difficulty percentages. + +Job outputs contain `status.json`, `training_recipe.json`, `space_identity.json`, `training_smoke_verified.json`, `canonical_scores.json`, capture audits and native checkpoints. Remote artifacts are stored under the run ID and unique job owner. Checkpoints publish their ready marker only after all files and hashes are verified. Bucket transfers retry transient transport, rate-limit and server errors up to three attempts; permission or validation errors fail immediately. Full optimizer checkpoints are larger than inference-only exports, so the smoke includes their upload and restore time. [HF Bucket sync](https://huggingface.co/docs/huggingface_hub/guides/buckets) compares existing content when retrying. Local source transformations have their own manifest; the original portable bundle remains intact. + +Training logs locally and uploads Trackio events/databases asynchronously. Environment Spaces do not contain a dashboard. The [shared comparison dashboard](https://huggingface.co/spaces/HuggingEnvs/data-agent-training-comparison-trackio) replays audited training and eval artifacts; see `hf/consolidate_async_runs.py` for the historical collector and `hf/runtime/logging_sync.py` for per-run logging. A new reproduction keeps its own run identity rather than overwriting these measured runs. + +`--dry-run` on the beginner CLI prints the exact command without allocating resources. Never reuse a run directory to silently overwrite a baseline. Superseded files go into the ignored `temp/` archive; credentials, raw traces, checkpoints and SQLite databases are not committed. diff --git a/04-data-agent/reproduce.py b/04-data-agent/reproduce.py new file mode 100644 index 0000000..fb64cd8 --- /dev/null +++ b/04-data-agent/reproduce.py @@ -0,0 +1,137 @@ +"""One entry point for the pinned data-agent training and evaluation recipes. + +Start with `python reproduce.py prepare --recipe harbor-opencode --env-file .env`. +See reproduce.md for credentials, hardware and checkpoint evaluation. +""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import shlex +import subprocess +import sys + +PROJECT = Path(__file__).resolve().parent +RECIPES = {"harbor-multi": "blackbox", "harbor-opencode": "blackbox", + "native-opencode": "opencode", "seta": "whitebox"} + + +def run(command, dry_run=False): + command = list(map(str, command)) + print(shlex.join(command), flush=True) + if not dry_run: + subprocess.run(command, check=True) + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("action", choices=["prepare", "upload", "spaces", "smoke", "eval", "train", "status"]) + p.add_argument("--recipe", choices=RECIPES, default="harbor-opencode") + p.add_argument("--platform", choices=["hub", "local"], default="hub") + p.add_argument("--out", type=Path, help="Isolated run directory (default: temp/reproduction/)") + p.add_argument("--env-file", type=Path, default=PROJECT / ".env") + p.add_argument("--namespace", default="HuggingEnvs", help="HF namespace where you can create resources") + p.add_argument("--run-id", help="Unique artifact identity; set when preparing a new experiment") + p.add_argument("--flavor", help="HF hardware: h200x2/a100x4 for training, a100-large for eval") + p.add_argument("--partition", default="hopper-prod", help="Local Slurm GPU partition") + p.add_argument("--cpu-partition", default="hopper-cpu") + p.add_argument("--timeout", help="HF duration, e.g. 2h or 24h") + p.add_argument("--concurrency", type=int, help="Eval concurrency (Hub default 35; local default 50)") + p.add_argument("--space-bundle-sha", help="Pin an existing Space for a training smoke without redeploying it") + p.add_argument("--baseline-job") + p.add_argument("--comparison-baseline-job") + p.add_argument("--smoke-job") + p.add_argument("--smoke-run", type=Path, help="Local smoke output directory") + p.add_argument("--baseline-score", type=Path) + p.add_argument("--comparison-baseline-score", type=Path) + p.add_argument("--checkpoint-eval-proof", type=Path) + p.add_argument("--qualify-checkpoint-eval", action="store_true") + p.add_argument("--train-venv", type=Path, help="Optional existing local training venv; otherwise create locked venvs") + p.add_argument("--env-venv", type=Path) + p.add_argument("--submit", action="store_true", help="Submit a prepared local Slurm script") + p.add_argument("--dry-run", action="store_true", help="Print commands without allocating resources") + a = p.parse_args() + out = (a.out or PROJECT / "temp/reproduction" / a.recipe).resolve() + arm = RECIPES[a.recipe] + if a.action == "prepare": + if a.concurrency is not None and a.concurrency < 1: + p.error("Concurrency must be positive") + if (out / "config.json").exists(): + p.error("This run directory already exists. Use a new --out to preserve its configuration and artifacts.") + config = json.loads((PROJECT / "hf/configs/deployment.json").read_text()) + config["namespace"] = a.namespace + config["run_id"] = a.run_id or f"reproduction-{a.recipe}" + config["recipe"] = a.recipe + if a.recipe == "harbor-multi": + config["arms"][arm]["training_harnesses"] = ["opencode", "claude-code", "codex", "mini-swe-agent"] + if a.namespace != "HuggingEnvs": + # All resources become owned copies. The immutable task seed still + # requires access to the original dataset repository. + def relocate(value): + if isinstance(value, str) and value.startswith("HuggingEnvs/"): + return a.namespace + value[len("HuggingEnvs"):] + if isinstance(value, dict): return {k: relocate(v) for k, v in value.items()} + if isinstance(value, list): return [relocate(v) for v in value] + return value + config["resources"] = relocate(config["resources"]) + if a.concurrency: + config["evaluation"]["concurrency_per_job"] = a.concurrency + config["evaluation"]["concurrency_per_arm"] = {k: a.concurrency for k in RECIPES.values()} + config["evaluation"]["opencode_backend_concurrency"] = {k: a.concurrency for k in ("daytona", "hf")} + if not a.dry_run: + out.mkdir(parents=True, exist_ok=True) + (out / "config.json").write_text(json.dumps(config, indent=2) + "\n") + run([sys.executable, PROJECT / "hf/build.py", "--out", out / "bundle", + "--config", out / "config.json", "--env-file", a.env_file], a.dry_run) + return + if not (out / "config.json").is_file() and not a.dry_run: + p.error("Run prepare first, using the same --recipe and --out") + if a.platform == "local" and a.action == "status": + for path in (out / "baseline/launch.json", out / "smoke/launch.json", out / "long/plan.json"): + if not path.exists(): + continue + record = json.loads(path.read_text()) + jobs = [str(record[k]) for k in ("slurm_job", "training_job", "controller_job") if record.get(k)] + print(json.dumps({"record": str(path), "jobs": jobs})) + if jobs: + if not all(job.isdigit() for job in jobs): + p.error("Stored Slurm job IDs must be numeric") + run(["sacct", "-X", "-j", ",".join(jobs), "--format=JobID,State,Elapsed,ExitCode"], a.dry_run) + return + if a.platform == "local" and a.action in {"eval", "smoke", "train"}: + if a.action == "train": + required = (a.smoke_run, a.baseline_score) + if not all(required): p.error("Local training needs --smoke-run and --baseline-score") + cmd = [sys.executable, PROJECT / "hf/local_long.py", "--arm", arm, + "--smoke-run", a.smoke_run, "--baseline-score", a.baseline_score, + "--env-file", a.env_file.resolve(), "--out", out / "long", + "--coordination-dir", out.parent / "eval-admission", "--partition", a.partition, + "--cpu-partition", a.cpu_partition] + if a.qualify_checkpoint_eval: cmd += ["--qualify-checkpoint-eval"] + if a.checkpoint_eval_proof: cmd += ["--checkpoint-eval-proof", a.checkpoint_eval_proof] + if a.comparison_baseline_score: cmd += ["--comparison-baseline-score", a.comparison_baseline_score] + else: + cmd = [sys.executable, PROJECT / "hf/cluster.py", "--bundle", out / "bundle", + "--out", out / ("baseline" if a.action == "eval" else "smoke"), + "--env-file", a.env_file.resolve(), "--arm", arm, + "--phase", "baseline" if a.action == "eval" else "smoke", "--partition", a.partition] + for key in ("train_venv", "env_venv"): + if getattr(a, key): cmd += ["--" + key.replace("_", "-"), getattr(a, key).resolve()] + if a.submit: cmd += ["--submit"] + else: + action = "job" if a.action in {"smoke", "eval", "train"} else a.action + cmd = [sys.executable, PROJECT / "hf/deploy.py", action, "--config", out / "config.json", + "--out", out, "--env-file", a.env_file.resolve(), "--arm", arm] + if action == "spaces": cmd += ["--only", arm] + if action == "job": + cmd += ["--role", "eval" if a.action == "eval" else "train", "--phase", + {"smoke": "smoke", "eval": "baseline", "train": "long"}[a.action]] + cmd += ["--timeout", a.timeout or ("24h" if a.action == "train" else "4h" if a.action == "eval" else "2h")] + for key in ("flavor", "space_bundle_sha", "baseline_job", "comparison_baseline_job", "smoke_job"): + if getattr(a, key): cmd += ["--" + key.replace("_", "-"), getattr(a, key)] + run(cmd, a.dry_run) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/results.md b/04-data-agent/results.md new file mode 100644 index 0000000..de8dc66 --- /dev/null +++ b/04-data-agent/results.md @@ -0,0 +1,137 @@ +# Data Agent: completed training and evaluation + +Updated September 17, 2026. Three async Qwen3.5-2B runs reached 1,000 optimizer steps. +Every scheduled 100-step checkpoint evaluation is complete: 250 fixed test tasks × +four harnesses, pass@1. The test set contains 33 easy, 118 medium and 99 hard tasks. + +| Run | Baseline | Best measured checkpoint | Final step 1,000 | +| --- | ---: | ---: | ---: | +| Harbor multi-harness | 14.6% | **37.0% at 500** | 26.3% | +| Native OpenCode | 15.9% | **29.8% at 1,000** | 29.8% | +| Harbor OpenCode-only | 14.6% | **39.5% at 700** | 26.4% | + +[Every checkpoint, harness and difficulty](reports/async-comparison-20260916/REPORT.md) · +[Public Trackio comparison](https://huggingface.co/spaces/HuggingEnvs/data-agent-training-comparison-trackio) + +## What training and evaluation show + +**Multi-harness produces longer responses but takes fewer actions.** Between training +steps 401–500 and 901–1,000, completion tokens per admitted rollout grow **3,521 → 9,480**, +while emitted tool calls fall **15.86 → 11.24**. In the final window, 37.5% of admitted +rollouts contain a response longer than the evaluation's 4,096-token output cap. +At evaluation, output-truncated rollouts rise **9/1,000 → 556/1,000** from peak to final. +OpenCode accounts for about 64% of the net lost successful evaluations. Budget mismatch +is a plausible contributor, not a proven explanation for the whole decline. + +**Harbor OpenCode-only continues working but finishes less reliably.** Peak-to-final +eval tool calls rise **16.62 → 20.97**, while submission falls **68.9% → 40.7%** on the +86-task subset with explicit submission instrumentation. Output truncation remains rare. +Training also shows longer outputs (**2,122 → 3,631 tokens**) and more tool use +(**14.63 → 17.43 calls**) per admitted rollout, comparing steps 601–700 with 901–1,000. + +**Native OpenCode finishes at its best aggregate score with shorter training outputs.** +Its final 100 steps average 1,075 completion tokens and 6.05 emitted tool calls per +admitted rollout. However, late prompt forking increases context overhead: forwarded +tokens per supervised token rise **22.3× → 99.4×** between steps 401–500 and 901–1,000. + +**One-harness training transfers to other harnesses.** Harbor OpenCode-only's best +checkpoint scores 46.4% under Claude Code and 40.0% under OpenCode. Native OpenCode +improves all four evaluation harnesses; its largest gains are outside OpenCode. + +## Training accounting changes the interpretation + +| Run | Distinct tasks covered | Supervised tokens | Forwarded tokens | Zero-fresh-gradient steps | +| --- | ---: | ---: | ---: | ---: | +| Harbor multi-harness | 482 | 22.15M | 1,001.32M | 349/1,000 | +| Native OpenCode | 566 | 5.33M | 228.41M | 583/1,000 | +| Harbor OpenCode-only | 523 | 14.63M | 419.43M | 380/1,000 | + +The 1,000-step cap did not cover the whole 1,000-task training pool. Equal optimizer +steps also did not provide equal token exposure. Zero-gradient steps coincide with +zero within-group reward variance and supply no fresh GRPO contrast; optimizer +momentum may still update weights. + +The multi-harness resume after step 684 revisits previously seen tasks in **1,575 of +1,579 admitted rollouts**. The saved schedule cursor does not preserve later completed +groups. This changes late data exposure but cannot explain the initial decline after +step 500, which happened before that restart. + +## Next experiments + +1. Fix and test resume accounting for out-of-order completed groups and unfinished work. +2. Diagnose the train/eval output-budget mismatch on a small, separately labeled cohort; + preserve the canonical scores. Check submission and executed actions, not just reward. +3. Track per-harness tokens, calls, submission, truncation, context duplication, task + coverage and zero-advantage groups. Compare future runs at matched exposure and budgets. + +This is an observational comparison. Backend, rollout filtering, training histories and +historical eval retries differ. The 39.5% versus 37.0% peak difference is not clearly +separated by paired task-level uncertainty. All three trainers use binary correctness; +native raw efficiency bonuses are removed before training. + +Evidence: [evaluation analysis and limitations](reports/three-run-analysis-20260917/REPORT.md), +[training token/tool analysis and reproduction](reports/three-run-analysis-20260917/TRAINING.md). +Raw captures and accepted scores are unchanged. + +[Short message for sharing](reports/three-run-analysis-20260917/TLDR.md) + +## Earlier SETA and infrastructure snapshot + +The following September 16 snapshot is retained for SETA results and qualification provenance. Its pending async evaluations were subsequently completed; use the September 17 tables above for the three async runs. + +# Historical data-agent results + +Snapshot: **2026-09-16 UTC**. Metric: **pass@1** on the fixed 250-task test set (33 easy, 118 medium, 99 hard). Each accepted async checkpoint has 250 tasks × four harnesses = **1,000 first-graded cells**. SETA uses its native bash/SETA evaluator, 250 cells. + +[Public artifact index](https://huggingface.co/datasets/HuggingEnvs/data-agent-experiment-results): code, environments, dashboards, report downloads, qualification evidence and published checkpoints. Credential-bearing raw evidence stays private; redacted public copies are explicitly marked. + +![Training and evaluation comparison](results/2026-09-16/comparison.png) + +The [complete report](results/2026-09-16/REPORT.md) includes training history and **harness × difficulty at every accepted checkpoint**. [CSV](results/2026-09-16/checkpoint_scores.csv) provides the underlying correct/graded counts; [snapshot](results/2026-09-16/snapshot.json.gz) retains audited provenance and training metrics. The [live Trackio dashboard](https://huggingface.co/spaces/HuggingEnvs/data-agent-training-comparison-trackio) may contain newer observations. + +| Checkpoint | Harbor multi-harness | Native OpenCode training, four-harness eval | SETA native eval | +| --- | ---: | ---: | ---: | +| Base | 14.6% | 15.9% | 18.8% | +| 100 | 24.8% | 19.7% | 34.8% | +| 150 (final SETA) | — | — | **38.0%** | +| 200 | 26.3% | 22.1% | — | +| 300 | 28.6% | 21.6% | — | +| 400 | 33.3% | 26.4% | — | +| 500 | **37.0%** | 23.1% | — | +| 600 | 31.8% | 25.1% | — | +| 684 (recovery) | 32.1% | — | — | +| 700 | 28.8% | 23.2% | — | +| 800 | 27.0% | 25.6% | — | +| 900 | Incomplete | 25.3% | — | +| 1000 | Incomplete | **29.8%** | — | + +Harbor multi-harness and native OpenCode reached 1,000 training steps. Native OpenCode's final four-harness evaluation is complete at 29.8%; Harbor multi-harness still lacks accepted step-900/1000 scores. SETA was intentionally stopped after a verified checkpoint 150; its final evaluation completed at **38.0%**. Harbor OpenCode-only is a new run in progress; no post-training checkpoint score is claimed here. + +The async report/CSV/figure retain their 10:50 UTC snapshot. SETA's later result has a separate [checkpoint-150 receipt](results/2026-09-16/seta-checkpoint-150.json), including the verified model manifest, complete scoring and job identity. + +## Baselines and difficulty + +| Measured cohort | Easy | Medium | Hard | Overall | +| --- | ---: | ---: | ---: | ---: | +| Harbor multi-harness base (E2B) | 53/132 = 40.2% | 68/472 = 14.4% | 25/396 = 6.3% | 146/1000 = 14.6% | +| SETA base (HF Job / Daytona) | 14/33 = 42.4% | 27/118 = 22.9% | 6/99 = 6.1% | 47/250 = 18.8% | +| SETA checkpoint 100 | 23/33 = 69.7% | 45/118 = 38.1% | 19/99 = 19.2% | 87/250 = 34.8% | +| SETA checkpoint 150 | 28/33 = 84.8% | 49/118 = 41.5% | 18/99 = 18.2% | 95/250 = 38.0% | + +Native OpenCode's **standalone** base evaluation scored **21/250 = 8.4%**. That is a different protocol from the **15.9%** four-harness Harbor/Daytona baseline used for its checkpoint comparison. Do not mix these denominators or relabel one cohort as the other. The shared Harbor OpenCode-only run reuses the recorded E2B base cohort and has no new measured gain yet. + +## What the runs established + +- Exact captured prompt/completion IDs, real aligned log probabilities and authoritative loss masks are usable across the selected harnesses. Lossless forks preserve supervision when prompts change; more rows still affect token cost and weighting. +- The async recipe admits complete rollout groups and checks retained supervision against capture records. The optimizer, checkpoint, upload and remote-resume paths have real GPU evidence. +- The native OpenCode baseline completed 250 tasks at local concurrency 50. HF SETA exercised 8, 32 and 53 concurrent slots. Earlier scaling failures are preserved; the reproduction defaults to **35** on Hub infrastructure. +- Checkpoint evaluations run on separate GPUs, with fixed test identities and first-graded results. A graded zero is never replaced by a retry. +- Task parsing now preserves explicit zero numerical tolerances. Native baseline qualification deterministically rechecks the unchanged submitted answers and frozen grading parameters. + +Historical qualification receipts: native optimizer smoke **80593**, local SETA **80555**, HF SETA **6aa9a487f76d6a098a70e3d2**; independent checkpoint smokes **80603**, **80576**, and **6aa9af55f76d6a098a70e52d** respectively. These are evidence for their recorded source snapshots, not substitutes for qualifying a changed bundle. Fresh PR qualification is recorded separately in [validation.md](results/validation.md). + +## Limits of the comparison + +Infrastructure, harness protocols, batching and recipe versions changed during bring-up. Async atomic batching and synchronous GRPO/DAPO have different scheduling and token accounting. These curves are observational; they do not isolate a causal effect of sync versus async or multi-harness versus one harness. Training reward is a sampled training signal, not held-out pass@1. + +Scores only enter the accepted table after complete coverage and their recorded TiTO/version/provenance checks. The Harbor decline after step 500 is observed; this report does not assign a cause without a controlled ablation. Failed or incomplete cohorts remain visible in the snapshot's pending section and are not estimated. diff --git a/04-data-agent/results/2026-09-16/REPORT.md b/04-data-agent/results/2026-09-16/REPORT.md new file mode 100644 index 0000000..8664fad --- /dev/null +++ b/04-data-agent/results/2026-09-16/REPORT.md @@ -0,0 +1,245 @@ +# Harbor and OpenCode — consolidated training and pass@1 + +Updated: 2026-09-16T10:50:30.515660+00:00 + +[Live Trackio dashboard](https://huggingface.co/spaces/HuggingEnvs/data-agent-training-comparison-trackio) · [Overview image](comparison.png) · [Snapshot](snapshot.json.gz) + +Qwen3.5-2B; 1,000 optimizer-step target per run. Recorded training: Harbor multi-harness: 1000 steps; Native OpenCode: 1000 steps; Harbor OpenCode-only: 74 steps. Every accepted checkpoint has 250 fixed tasks × four harnesses = 1,000 grades. Task difficulty: 33 easy, 118 medium, 99 hard (13.2% / 47.2% / 39.6%). Scores retain first graded attempts; incomplete and failed-audit evaluations are excluded. Missing scores are not estimated. + +Baselines are separate measured cohorts: Harbor/E2B 14.6%; Harbor/Daytona 15.9% for the native OpenCode checkpoint evaluator. The standalone native OpenCode 8.4% baseline uses a different harness protocol and is excluded here. Infrastructure and training recipe histories differ; this is an observational comparison, not a controlled causal experiment. + +## Overall checkpoint curve + +| Checkpoint | Harbor multi-harness | Native OpenCode | Harbor OpenCode-only | +| --- | ---: | ---: | ---: | +| 0 (baseline) | 14.6% | 15.9% | 14.6% | +| 100 | 24.8% | 19.7% | Pending | +| 200 | 26.3% | 22.1% | Pending | +| 300 | 28.6% | 21.6% | Pending | +| 400 | 33.3% | 26.4% | Pending | +| 500 | 37.0% | 23.1% | Pending | +| 600 | 31.8% | 25.1% | Pending | +| 684 (recovery) | 32.1% | Not scheduled | Not scheduled | +| 700 | 28.8% | 23.2% | Pending | +| 800 | 27.0% | 25.6% | Pending | +| 900 | Pending | 25.3% | Pending | +| 1000 | Pending | 29.8% | Pending | + +## Harbor multi-harness + +### Overall and difficulty + +| Checkpoint | Overall | Easy (132 cells) | Medium (472) | Hard (396) | +| --- | ---: | ---: | ---: | ---: | +| 0 | 14.6% | 40.2% | 14.4% | 6.3% | +| 100 | 24.8% | 53.0% | 30.5% | 8.6% | +| 200 | 26.3% | 58.3% | 30.1% | 11.1% | +| 300 | 28.6% | 64.4% | 32.8% | 11.6% | +| 400 | 33.3% | 73.5% | 39.6% | 12.4% | +| 500 | 37.0% | 72.7% | 44.3% | 16.4% | +| 600 | 31.8% | 72.7% | 37.3% | 11.6% | +| 684 | 32.1% | 75.8% | 35.8% | 13.1% | +| 700 | 28.8% | 60.6% | 33.9% | 12.1% | +| 800 | 27.0% | 59.1% | 32.6% | 9.6% | + +### Harness × difficulty at every checkpoint + +| Checkpoint | Harness | Overall (250) | Easy (33) | Medium (118) | Hard (99) | +| --- | --- | ---: | ---: | ---: | ---: | +| 0 | opencode | 10.8% | 33.3% (11/33) | 8.5% (10/118) | 6.1% (6/99) | +| 0 | claude-code | 16.8% | 42.4% (14/33) | 18.6% (22/118) | 6.1% (6/99) | +| 0 | codex | 16.4% | 42.4% (14/33) | 16.9% (20/118) | 7.1% (7/99) | +| 0 | mini-swe-agent | 14.4% | 42.4% (14/33) | 13.6% (16/118) | 6.1% (6/99) | +| 100 | opencode | 24.4% | 51.5% (17/33) | 31.4% (37/118) | 7.1% (7/99) | +| 100 | claude-code | 27.6% | 60.6% (20/33) | 32.2% (38/118) | 11.1% (11/99) | +| 100 | codex | 28.0% | 57.6% (19/33) | 35.6% (42/118) | 9.1% (9/99) | +| 100 | mini-swe-agent | 19.2% | 42.4% (14/33) | 22.9% (27/118) | 7.1% (7/99) | +| 200 | opencode | 30.4% | 51.5% (17/33) | 34.7% (41/118) | 18.2% (18/99) | +| 200 | claude-code | 30.0% | 63.6% (21/33) | 33.1% (39/118) | 15.2% (15/99) | +| 200 | codex | 26.4% | 63.6% (21/33) | 29.7% (35/118) | 10.1% (10/99) | +| 200 | mini-swe-agent | 18.4% | 54.5% (18/33) | 22.9% (27/118) | 1.0% (1/99) | +| 300 | opencode | 29.6% | 60.6% (20/33) | 33.1% (39/118) | 15.2% (15/99) | +| 300 | claude-code | 33.2% | 66.7% (22/33) | 39.0% (46/118) | 15.2% (15/99) | +| 300 | codex | 29.6% | 69.7% (23/33) | 33.1% (39/118) | 12.1% (12/99) | +| 300 | mini-swe-agent | 22.0% | 60.6% (20/33) | 26.3% (31/118) | 4.0% (4/99) | +| 400 | opencode | 32.8% | 72.7% (24/33) | 38.1% (45/118) | 13.1% (13/99) | +| 400 | claude-code | 36.8% | 75.8% (25/33) | 44.9% (53/118) | 14.1% (14/99) | +| 400 | codex | 34.8% | 72.7% (24/33) | 42.4% (50/118) | 13.1% (13/99) | +| 400 | mini-swe-agent | 28.8% | 72.7% (24/33) | 33.1% (39/118) | 9.1% (9/99) | +| 500 | opencode | 32.8% | 69.7% (23/33) | 40.7% (48/118) | 11.1% (11/99) | +| 500 | claude-code | 44.8% | 75.8% (25/33) | 52.5% (62/118) | 25.3% (25/99) | +| 500 | codex | 39.2% | 75.8% (25/33) | 46.6% (55/118) | 18.2% (18/99) | +| 500 | mini-swe-agent | 31.2% | 69.7% (23/33) | 37.3% (44/118) | 11.1% (11/99) | +| 600 | opencode | 34.0% | 66.7% (22/33) | 41.5% (49/118) | 14.1% (14/99) | +| 600 | claude-code | 30.0% | 72.7% (24/33) | 33.9% (40/118) | 11.1% (11/99) | +| 600 | codex | 32.4% | 66.7% (22/33) | 39.8% (47/118) | 12.1% (12/99) | +| 600 | mini-swe-agent | 30.8% | 84.8% (28/33) | 33.9% (40/118) | 9.1% (9/99) | +| 684 | opencode | 29.2% | 72.7% (24/33) | 30.5% (36/118) | 13.1% (13/99) | +| 684 | claude-code | 35.6% | 72.7% (24/33) | 41.5% (49/118) | 16.2% (16/99) | +| 684 | codex | 33.2% | 84.8% (28/33) | 36.4% (43/118) | 12.1% (12/99) | +| 684 | mini-swe-agent | 30.4% | 72.7% (24/33) | 34.7% (41/118) | 11.1% (11/99) | +| 700 | opencode | 21.2% | 30.3% (10/33) | 28.0% (33/118) | 10.1% (10/99) | +| 700 | claude-code | 31.6% | 78.8% (26/33) | 34.7% (41/118) | 12.1% (12/99) | +| 700 | codex | 32.0% | 72.7% (24/33) | 34.7% (41/118) | 15.2% (15/99) | +| 700 | mini-swe-agent | 30.4% | 60.6% (20/33) | 38.1% (45/118) | 11.1% (11/99) | +| 800 | opencode | 23.2% | 36.4% (12/33) | 33.1% (39/118) | 7.1% (7/99) | +| 800 | claude-code | 32.0% | 66.7% (22/33) | 37.3% (44/118) | 14.1% (14/99) | +| 800 | codex | 26.8% | 66.7% (22/33) | 31.4% (37/118) | 8.1% (8/99) | +| 800 | mini-swe-agent | 26.0% | 66.7% (22/33) | 28.8% (34/118) | 9.1% (9/99) | + +### Training history + +| Allocation | First optimizer step | Last optimizer step | +| --- | ---: | ---: | +| 78647 | 1 | 17 | +| 78681 | 18 | 25 | +| 78767 | 26 | 30 | +| 78831 | 31 | 53 | +| 78956 | 54 | 196 | +| 79083 | 197 | 684 | +| 80608 | 685 | 1000 | + +### Score provenance + +- Step 0: `/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-baseline-20260914/job-78215/canonical_results.json`; SHA256 `8c4f5bced4eff04b0c2e5f41806da9ae1b8a4c0fe356ddf926e1f781c6bb9ac6`. +- Step 100: `/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-long-bounded-20260915/checkpoint-evals/step-000100/scores.json`; SHA256 `1e4f42f54526c9b8b71156f5b1f3673529519201401bc88f88ccff805f291181`. +- Step 200: `/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000200/scores.json`; SHA256 `699dce549d1002e879c675555ac06142448b0cbeef534a0816616faf669862af`. +- Step 300: `/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000300/scores.json`; SHA256 `299e5ed3528922d9912a591f3cff0c4d85070fef4de732d37723967934bfd691`. +- Step 400: `/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000400/scores.json`; SHA256 `f8909af81669f4ec092317c5f9889ef8735754c0dcbc826da82a270d2d92dc7a`. +- Step 500: `/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000500/scores.json`; SHA256 `86d56b65edbdc2f5a3f5d54151d0888dae83ca2b81753a7a9dbc2e80ee4f6130`. +- Step 600: `/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000600/scores.json`; SHA256 `02fc5a5e84978c198dc880c143fe223507c30485563b3545cb64b2794e3e60b0`. +- Step 684: `/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-20260915/checkpoint-evals/step-000684/scores.json`; SHA256 `fb54bd52f352463e405bee8067ef24b27147bfd02d41429561f440a916f5d776`. +- Step 700: `/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-cont-20260915/checkpoint-evals/step-000700/scores.json`; SHA256 `39c2ead8681847c6c398eb6b22ae8919aabaff3ad6b668d81b5961564139f8be`. +- Step 800: `/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-cont-20260915/checkpoint-evals/step-000800/scores.json`; SHA256 `65130786707d07f68cfa145fcd5ad7308890cb89a305b65382dfbec36ef7a150`. + +## Native OpenCode + +### Overall and difficulty + +| Checkpoint | Overall | Easy (132 cells) | Medium (472) | Hard (396) | +| --- | ---: | ---: | ---: | ---: | +| 0 | 15.9% | 37.9% | 18.0% | 6.1% | +| 100 | 19.7% | 44.7% | 22.5% | 8.1% | +| 200 | 22.1% | 59.8% | 24.6% | 6.6% | +| 300 | 21.6% | 51.5% | 25.6% | 6.8% | +| 400 | 26.4% | 59.1% | 29.4% | 11.9% | +| 500 | 23.1% | 53.0% | 27.3% | 8.1% | +| 600 | 25.1% | 56.1% | 28.8% | 10.4% | +| 700 | 23.2% | 49.2% | 28.8% | 7.8% | +| 800 | 25.6% | 52.3% | 29.7% | 11.9% | +| 900 | 25.3% | 50.0% | 30.3% | 11.1% | +| 1000 | 29.8% | 59.8% | 35.8% | 12.6% | + +### Harness × difficulty at every checkpoint + +| Checkpoint | Harness | Overall (250) | Easy (33) | Medium (118) | Hard (99) | +| --- | --- | ---: | ---: | ---: | ---: | +| 0 | opencode | 12.8% | 24.2% (8/33) | 16.9% (20/118) | 4.0% (4/99) | +| 0 | claude-code | 16.8% | 42.4% (14/33) | 18.6% (22/118) | 6.1% (6/99) | +| 0 | codex | 15.2% | 33.3% (11/33) | 16.9% (20/118) | 7.1% (7/99) | +| 0 | mini-swe-agent | 18.8% | 51.5% (17/33) | 19.5% (23/118) | 7.1% (7/99) | +| 100 | opencode | 19.6% | 42.4% (14/33) | 24.6% (29/118) | 6.1% (6/99) | +| 100 | claude-code | 20.4% | 42.4% (14/33) | 22.9% (27/118) | 10.1% (10/99) | +| 100 | codex | 18.0% | 27.3% (9/33) | 21.2% (25/118) | 11.1% (11/99) | +| 100 | mini-swe-agent | 20.8% | 66.7% (22/33) | 21.2% (25/118) | 5.1% (5/99) | +| 200 | opencode | 17.2% | 48.5% (16/33) | 21.2% (25/118) | 2.0% (2/99) | +| 200 | claude-code | 24.0% | 60.6% (20/33) | 26.3% (31/118) | 9.1% (9/99) | +| 200 | codex | 25.6% | 63.6% (21/33) | 28.8% (34/118) | 9.1% (9/99) | +| 200 | mini-swe-agent | 21.6% | 66.7% (22/33) | 22.0% (26/118) | 6.1% (6/99) | +| 300 | opencode | 20.8% | 48.5% (16/33) | 28.0% (33/118) | 3.0% (3/99) | +| 300 | claude-code | 29.2% | 66.7% (22/33) | 30.5% (36/118) | 15.2% (15/99) | +| 300 | codex | 16.8% | 39.4% (13/33) | 20.3% (24/118) | 5.1% (5/99) | +| 300 | mini-swe-agent | 19.6% | 51.5% (17/33) | 23.7% (28/118) | 4.0% (4/99) | +| 400 | opencode | 20.4% | 48.5% (16/33) | 24.6% (29/118) | 6.1% (6/99) | +| 400 | claude-code | 32.4% | 60.6% (20/33) | 35.6% (42/118) | 19.2% (19/99) | +| 400 | codex | 25.6% | 57.6% (19/33) | 28.0% (33/118) | 12.1% (12/99) | +| 400 | mini-swe-agent | 27.2% | 69.7% (23/33) | 29.7% (35/118) | 10.1% (10/99) | +| 500 | opencode | 18.0% | 48.5% (16/33) | 19.5% (23/118) | 6.1% (6/99) | +| 500 | claude-code | 26.4% | 51.5% (17/33) | 33.1% (39/118) | 10.1% (10/99) | +| 500 | codex | 18.8% | 48.5% (16/33) | 22.0% (26/118) | 5.1% (5/99) | +| 500 | mini-swe-agent | 29.2% | 63.6% (21/33) | 34.7% (41/118) | 11.1% (11/99) | +| 600 | opencode | 16.8% | 42.4% (14/33) | 18.6% (22/118) | 6.1% (6/99) | +| 600 | claude-code | 32.4% | 63.6% (21/33) | 37.3% (44/118) | 16.2% (16/99) | +| 600 | codex | 18.4% | 45.5% (15/33) | 23.7% (28/118) | 3.0% (3/99) | +| 600 | mini-swe-agent | 32.8% | 72.7% (24/33) | 35.6% (42/118) | 16.2% (16/99) | +| 700 | opencode | 18.4% | 42.4% (14/33) | 23.7% (28/118) | 4.0% (4/99) | +| 700 | claude-code | 29.6% | 63.6% (21/33) | 33.1% (39/118) | 14.1% (14/99) | +| 700 | codex | 14.4% | 24.2% (8/33) | 22.9% (27/118) | 1.0% (1/99) | +| 700 | mini-swe-agent | 30.4% | 66.7% (22/33) | 35.6% (42/118) | 12.1% (12/99) | +| 800 | opencode | 20.0% | 36.4% (12/33) | 26.3% (31/118) | 7.1% (7/99) | +| 800 | claude-code | 32.4% | 72.7% (24/33) | 35.6% (42/118) | 15.2% (15/99) | +| 800 | codex | 18.0% | 30.3% (10/33) | 21.2% (25/118) | 10.1% (10/99) | +| 800 | mini-swe-agent | 32.0% | 69.7% (23/33) | 35.6% (42/118) | 15.2% (15/99) | +| 900 | opencode | 18.8% | 33.3% (11/33) | 23.7% (28/118) | 8.1% (8/99) | +| 900 | claude-code | 34.0% | 60.6% (20/33) | 42.4% (50/118) | 15.2% (15/99) | +| 900 | codex | 14.4% | 27.3% (9/33) | 17.8% (21/118) | 6.1% (6/99) | +| 900 | mini-swe-agent | 34.0% | 78.8% (26/33) | 37.3% (44/118) | 15.2% (15/99) | +| 1000 | opencode | 20.4% | 42.4% (14/33) | 25.4% (30/118) | 7.1% (7/99) | +| 1000 | claude-code | 33.2% | 66.7% (22/33) | 37.3% (44/118) | 17.2% (17/99) | +| 1000 | codex | 29.6% | 57.6% (19/33) | 35.6% (42/118) | 13.1% (13/99) | +| 1000 | mini-swe-agent | 36.0% | 72.7% (24/33) | 44.9% (53/118) | 13.1% (13/99) | + +### Training history + +| Allocation | First optimizer step | Last optimizer step | +| --- | ---: | ---: | +| 80626 | 1 | 1000 | + +### Score provenance + +- Step 0: `/fsx/adithyaskolavi/projects/trl_prod/experiments/daytona_harness_comparison/logs/20260915/blackbox/canonical_scores.json`; SHA256 `ece2b0e03c7c7e0e54988eaaa473ba6b53bd6028315b1e99ec39df5137c7632e`. +- Step 100: `/fsx/adithyaskolavi/projects/trl_prod/experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80657/canonical_scores.json`; SHA256 `37eab8fe9e97fda23e9803846f965b08c3de2a6e4142363219a4467af41c6f5c`. +- Step 200: `/fsx/adithyaskolavi/projects/trl_prod/experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80675/canonical_scores.json`; SHA256 `fa67547d19c7f4e63166fc7c1518ca15eb5e2c28ec8f1f3739445029006617ad`. +- Step 300: `/fsx/adithyaskolavi/projects/trl_prod/experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80748/canonical_scores.json`; SHA256 `a0e0cc489e3195827d5ac035945277bfd9ca67e985a015148e5b3c94ca938b0f`. +- Step 400: `/fsx/adithyaskolavi/projects/trl_prod/experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80807/canonical_scores.json`; SHA256 `f30bb541e207a2e8b83b2aabd05bf2e3d96eeac40c2fd8226ff1985606397a7b`. +- Step 500: `/fsx/adithyaskolavi/projects/trl_prod/experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80861/canonical_scores.json`; SHA256 `b6df2570695c2a15ba43f185719b647dc22319eb82ca1494d56e705572e3f1a2`. +- Step 600: `/fsx/adithyaskolavi/projects/trl_prod/experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80902/canonical_scores.json`; SHA256 `d86128c2cae4813a7ae8b99f06d1cd8ca109cefade9944c1cdbebf2b1550e1d9`. +- Step 700: `/fsx/adithyaskolavi/projects/trl_prod/experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80956/canonical_scores.json`; SHA256 `7c60cfab333948e63bf44bd01ed4ce3f0a788f76de84c4ceda2177144e9bc9ba`. +- Step 800: `/fsx/adithyaskolavi/projects/trl_prod/experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-80993/canonical_scores.json`; SHA256 `b689c9dd76c3e2230bfea49eea393f2d5842fe8f3630f04f59380a131497ae98`. +- Step 900: `/fsx/adithyaskolavi/projects/trl_prod/experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-81034/canonical_scores.json`; SHA256 `996f5ea262419b9639fa8f33c1b33fef9b49959c1cbe61e62ba922c0d642985f`. +- Step 1000: `/fsx/adithyaskolavi/projects/trl_prod/experiments/daytona_harness_comparison/logs/hf-20260915/local-opencode-smoke-v4/repro/outputs/local-eval-opencode-81098/canonical_scores.json`; SHA256 `1355a9a2ecc1ec165cf413120dacfc672e5d8d59ef2807b28bcf02322dca142b`. + +## Harbor OpenCode-only + +### Overall and difficulty + +| Checkpoint | Overall | Easy (132 cells) | Medium (472) | Hard (396) | +| --- | ---: | ---: | ---: | ---: | +| 0 | 14.6% | 40.2% | 14.4% | 6.3% | + +### Harness × difficulty at every checkpoint + +| Checkpoint | Harness | Overall (250) | Easy (33) | Medium (118) | Hard (99) | +| --- | --- | ---: | ---: | ---: | ---: | +| 0 | opencode | 10.8% | 33.3% (11/33) | 8.5% (10/118) | 6.1% (6/99) | +| 0 | claude-code | 16.8% | 42.4% (14/33) | 18.6% (22/118) | 6.1% (6/99) | +| 0 | codex | 16.4% | 42.4% (14/33) | 16.9% (20/118) | 7.1% (7/99) | +| 0 | mini-swe-agent | 14.4% | 42.4% (14/33) | 13.6% (16/118) | 6.1% (6/99) | + +### Training history + +| Allocation | First optimizer step | Last optimizer step | +| --- | ---: | ---: | +| 81075 | 1 | 74 | + +### Score provenance + +- Step 0: `/fsx/adithyaskolavi/projects/trl_prod/experiments/async_grpo_harbor_data_agent/logs/multi4-baseline-20260914/job-78215/canonical_results.json`; SHA256 `8c4f5bced4eff04b0c2e5f41806da9ae1b8a4c0fe356ddf926e1f781c6bb9ac6`. + +## Dashboard metric guide + +Both runs use identical metric names and optimizer-step axes. `eval/pass_at_1` is the overall score; `eval/difficulty/*` aggregates each difficulty; `eval/harness/*` compares each harness; `eval/harness_difficulty/*` contains all twelve intersections. `train/*` preserves recorded loss, reward, learning rate, gradient norm, entropy, KL, staleness, throughput, token, batching and rollout metrics where observed. Missing metrics are not filled with zeros. `train/reward_rolling20` and `train/nonzero_gradient_rolling20` are explicitly derived trailing windows. Raw metrics remain available. Use zero dashboard smoothing for exact checkpoint values. + +The independent CPU publisher refreshes every 60 seconds and admits new evaluations only after their full comparison gates pass. It never changes trainer state. Local SQLite backup, event ledger and remote exact-content verification receipts are kept alongside this report. + +Storage and deployment follow the [Trackio guide](https://huggingface.co/docs/trackio/quickstart) and [environment configuration](https://huggingface.co/docs/trackio/environment_variables). + +- [Overview](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5E%28eval%2Fpass_at_1%7Ctrain%2Freward_rolling20%29%24) +- [Difficulty](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5Eeval%2Fdifficulty%2F) +- [Harness](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5Eeval%2Fharness%2F) +- [Harness × difficulty](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5Eeval%2Fharness_difficulty%2F) +- [Optimizer diagnostics](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5Etrain%2F%28loss%7Cgrad_norm%7Centropy%7Ckl%7Clearning_rate%7Cnonzero_gradient_rolling20%29%24) +- [Throughput and rollout diagnostics](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=%5Etrain%2F%28perf%7Crollout%7Csample%7Cbatch%29%2F) +- [All metrics](https://huggingenvs-data-agent-training-comparison-trackio.hf.space/?project=qwen35-2b-harbor-vs-opencode-20260916&run_ids=3ae29a23763093285702b71a1f76805a%2Cbeb2604c8f737da4262b1ab19b8b0cbd%2Cc5b445fa337b56b139c1e6b34ae35409&smoothing=0&metric_filter=) + +[Download checkpoint scores as CSV](checkpoint_scores.csv) diff --git a/04-data-agent/results/2026-09-16/checkpoint_scores.csv b/04-data-agent/results/2026-09-16/checkpoint_scores.csv new file mode 100644 index 0000000..147f505 --- /dev/null +++ b/04-data-agent/results/2026-09-16/checkpoint_scores.csv @@ -0,0 +1,265 @@ +run,checkpoint,harness,difficulty,correct,graded,pass_at_1 +Harbor multi-harness,0,opencode,easy,11,33,0.3333333333333333 +Harbor multi-harness,0,opencode,medium,10,118,0.0847457627118644 +Harbor multi-harness,0,opencode,hard,6,99,0.06060606060606061 +Harbor multi-harness,0,claude-code,easy,14,33,0.42424242424242425 +Harbor multi-harness,0,claude-code,medium,22,118,0.1864406779661017 +Harbor multi-harness,0,claude-code,hard,6,99,0.06060606060606061 +Harbor multi-harness,0,codex,easy,14,33,0.42424242424242425 +Harbor multi-harness,0,codex,medium,20,118,0.1694915254237288 +Harbor multi-harness,0,codex,hard,7,99,0.0707070707070707 +Harbor multi-harness,0,mini-swe-agent,easy,14,33,0.42424242424242425 +Harbor multi-harness,0,mini-swe-agent,medium,16,118,0.13559322033898305 +Harbor multi-harness,0,mini-swe-agent,hard,6,99,0.06060606060606061 +Harbor multi-harness,100,opencode,easy,17,33,0.5151515151515151 +Harbor multi-harness,100,opencode,medium,37,118,0.3135593220338983 +Harbor multi-harness,100,opencode,hard,7,99,0.0707070707070707 +Harbor multi-harness,100,claude-code,easy,20,33,0.6060606060606061 +Harbor multi-harness,100,claude-code,medium,38,118,0.3220338983050847 +Harbor multi-harness,100,claude-code,hard,11,99,0.1111111111111111 +Harbor multi-harness,100,codex,easy,19,33,0.5757575757575758 +Harbor multi-harness,100,codex,medium,42,118,0.3559322033898305 +Harbor multi-harness,100,codex,hard,9,99,0.09090909090909091 +Harbor multi-harness,100,mini-swe-agent,easy,14,33,0.42424242424242425 +Harbor multi-harness,100,mini-swe-agent,medium,27,118,0.2288135593220339 +Harbor multi-harness,100,mini-swe-agent,hard,7,99,0.0707070707070707 +Harbor multi-harness,200,opencode,easy,17,33,0.5151515151515151 +Harbor multi-harness,200,opencode,medium,41,118,0.3474576271186441 +Harbor multi-harness,200,opencode,hard,18,99,0.18181818181818182 +Harbor multi-harness,200,claude-code,easy,21,33,0.6363636363636364 +Harbor multi-harness,200,claude-code,medium,39,118,0.3305084745762712 +Harbor multi-harness,200,claude-code,hard,15,99,0.15151515151515152 +Harbor multi-harness,200,codex,easy,21,33,0.6363636363636364 +Harbor multi-harness,200,codex,medium,35,118,0.2966101694915254 +Harbor multi-harness,200,codex,hard,10,99,0.10101010101010101 +Harbor multi-harness,200,mini-swe-agent,easy,18,33,0.5454545454545454 +Harbor multi-harness,200,mini-swe-agent,medium,27,118,0.2288135593220339 +Harbor multi-harness,200,mini-swe-agent,hard,1,99,0.010101010101010102 +Harbor multi-harness,300,opencode,easy,20,33,0.6060606060606061 +Harbor multi-harness,300,opencode,medium,39,118,0.3305084745762712 +Harbor multi-harness,300,opencode,hard,15,99,0.15151515151515152 +Harbor multi-harness,300,claude-code,easy,22,33,0.6666666666666666 +Harbor multi-harness,300,claude-code,medium,46,118,0.3898305084745763 +Harbor multi-harness,300,claude-code,hard,15,99,0.15151515151515152 +Harbor multi-harness,300,codex,easy,23,33,0.696969696969697 +Harbor multi-harness,300,codex,medium,39,118,0.3305084745762712 +Harbor multi-harness,300,codex,hard,12,99,0.12121212121212122 +Harbor multi-harness,300,mini-swe-agent,easy,20,33,0.6060606060606061 +Harbor multi-harness,300,mini-swe-agent,medium,31,118,0.2627118644067797 +Harbor multi-harness,300,mini-swe-agent,hard,4,99,0.04040404040404041 +Harbor multi-harness,400,opencode,easy,24,33,0.7272727272727273 +Harbor multi-harness,400,opencode,medium,45,118,0.3813559322033898 +Harbor multi-harness,400,opencode,hard,13,99,0.13131313131313133 +Harbor multi-harness,400,claude-code,easy,25,33,0.7575757575757576 +Harbor multi-harness,400,claude-code,medium,53,118,0.4491525423728814 +Harbor multi-harness,400,claude-code,hard,14,99,0.1414141414141414 +Harbor multi-harness,400,codex,easy,24,33,0.7272727272727273 +Harbor multi-harness,400,codex,medium,50,118,0.423728813559322 +Harbor multi-harness,400,codex,hard,13,99,0.13131313131313133 +Harbor multi-harness,400,mini-swe-agent,easy,24,33,0.7272727272727273 +Harbor multi-harness,400,mini-swe-agent,medium,39,118,0.3305084745762712 +Harbor multi-harness,400,mini-swe-agent,hard,9,99,0.09090909090909091 +Harbor multi-harness,500,opencode,easy,23,33,0.696969696969697 +Harbor multi-harness,500,opencode,medium,48,118,0.4067796610169492 +Harbor multi-harness,500,opencode,hard,11,99,0.1111111111111111 +Harbor multi-harness,500,claude-code,easy,25,33,0.7575757575757576 +Harbor multi-harness,500,claude-code,medium,62,118,0.5254237288135594 +Harbor multi-harness,500,claude-code,hard,25,99,0.25252525252525254 +Harbor multi-harness,500,codex,easy,25,33,0.7575757575757576 +Harbor multi-harness,500,codex,medium,55,118,0.4661016949152542 +Harbor multi-harness,500,codex,hard,18,99,0.18181818181818182 +Harbor multi-harness,500,mini-swe-agent,easy,23,33,0.696969696969697 +Harbor multi-harness,500,mini-swe-agent,medium,44,118,0.3728813559322034 +Harbor multi-harness,500,mini-swe-agent,hard,11,99,0.1111111111111111 +Harbor multi-harness,600,opencode,easy,22,33,0.6666666666666666 +Harbor multi-harness,600,opencode,medium,49,118,0.4152542372881356 +Harbor multi-harness,600,opencode,hard,14,99,0.1414141414141414 +Harbor multi-harness,600,claude-code,easy,24,33,0.7272727272727273 +Harbor multi-harness,600,claude-code,medium,40,118,0.3389830508474576 +Harbor multi-harness,600,claude-code,hard,11,99,0.1111111111111111 +Harbor multi-harness,600,codex,easy,22,33,0.6666666666666666 +Harbor multi-harness,600,codex,medium,47,118,0.3983050847457627 +Harbor multi-harness,600,codex,hard,12,99,0.12121212121212122 +Harbor multi-harness,600,mini-swe-agent,easy,28,33,0.8484848484848485 +Harbor multi-harness,600,mini-swe-agent,medium,40,118,0.3389830508474576 +Harbor multi-harness,600,mini-swe-agent,hard,9,99,0.09090909090909091 +Harbor multi-harness,684,opencode,easy,24,33,0.7272727272727273 +Harbor multi-harness,684,opencode,medium,36,118,0.3050847457627119 +Harbor multi-harness,684,opencode,hard,13,99,0.13131313131313133 +Harbor multi-harness,684,claude-code,easy,24,33,0.7272727272727273 +Harbor multi-harness,684,claude-code,medium,49,118,0.4152542372881356 +Harbor multi-harness,684,claude-code,hard,16,99,0.16161616161616163 +Harbor multi-harness,684,codex,easy,28,33,0.8484848484848485 +Harbor multi-harness,684,codex,medium,43,118,0.3644067796610169 +Harbor multi-harness,684,codex,hard,12,99,0.12121212121212122 +Harbor multi-harness,684,mini-swe-agent,easy,24,33,0.7272727272727273 +Harbor multi-harness,684,mini-swe-agent,medium,41,118,0.3474576271186441 +Harbor multi-harness,684,mini-swe-agent,hard,11,99,0.1111111111111111 +Harbor multi-harness,700,opencode,easy,10,33,0.30303030303030304 +Harbor multi-harness,700,opencode,medium,33,118,0.2796610169491525 +Harbor multi-harness,700,opencode,hard,10,99,0.10101010101010101 +Harbor multi-harness,700,claude-code,easy,26,33,0.7878787878787878 +Harbor multi-harness,700,claude-code,medium,41,118,0.3474576271186441 +Harbor multi-harness,700,claude-code,hard,12,99,0.12121212121212122 +Harbor multi-harness,700,codex,easy,24,33,0.7272727272727273 +Harbor multi-harness,700,codex,medium,41,118,0.3474576271186441 +Harbor multi-harness,700,codex,hard,15,99,0.15151515151515152 +Harbor multi-harness,700,mini-swe-agent,easy,20,33,0.6060606060606061 +Harbor multi-harness,700,mini-swe-agent,medium,45,118,0.3813559322033898 +Harbor multi-harness,700,mini-swe-agent,hard,11,99,0.1111111111111111 +Harbor multi-harness,800,opencode,easy,12,33,0.36363636363636365 +Harbor multi-harness,800,opencode,medium,39,118,0.3305084745762712 +Harbor multi-harness,800,opencode,hard,7,99,0.0707070707070707 +Harbor multi-harness,800,claude-code,easy,22,33,0.6666666666666666 +Harbor multi-harness,800,claude-code,medium,44,118,0.3728813559322034 +Harbor multi-harness,800,claude-code,hard,14,99,0.1414141414141414 +Harbor multi-harness,800,codex,easy,22,33,0.6666666666666666 +Harbor multi-harness,800,codex,medium,37,118,0.3135593220338983 +Harbor multi-harness,800,codex,hard,8,99,0.08080808080808081 +Harbor multi-harness,800,mini-swe-agent,easy,22,33,0.6666666666666666 +Harbor multi-harness,800,mini-swe-agent,medium,34,118,0.288135593220339 +Harbor multi-harness,800,mini-swe-agent,hard,9,99,0.09090909090909091 +Native OpenCode,0,opencode,easy,8,33,0.24242424242424243 +Native OpenCode,0,opencode,medium,20,118,0.1694915254237288 +Native OpenCode,0,opencode,hard,4,99,0.04040404040404041 +Native OpenCode,0,claude-code,easy,14,33,0.42424242424242425 +Native OpenCode,0,claude-code,medium,22,118,0.1864406779661017 +Native OpenCode,0,claude-code,hard,6,99,0.06060606060606061 +Native OpenCode,0,codex,easy,11,33,0.3333333333333333 +Native OpenCode,0,codex,medium,20,118,0.1694915254237288 +Native OpenCode,0,codex,hard,7,99,0.0707070707070707 +Native OpenCode,0,mini-swe-agent,easy,17,33,0.5151515151515151 +Native OpenCode,0,mini-swe-agent,medium,23,118,0.19491525423728814 +Native OpenCode,0,mini-swe-agent,hard,7,99,0.0707070707070707 +Native OpenCode,100,opencode,easy,14,33,0.42424242424242425 +Native OpenCode,100,opencode,medium,29,118,0.2457627118644068 +Native OpenCode,100,opencode,hard,6,99,0.06060606060606061 +Native OpenCode,100,claude-code,easy,14,33,0.42424242424242425 +Native OpenCode,100,claude-code,medium,27,118,0.2288135593220339 +Native OpenCode,100,claude-code,hard,10,99,0.10101010101010101 +Native OpenCode,100,codex,easy,9,33,0.2727272727272727 +Native OpenCode,100,codex,medium,25,118,0.211864406779661 +Native OpenCode,100,codex,hard,11,99,0.1111111111111111 +Native OpenCode,100,mini-swe-agent,easy,22,33,0.6666666666666666 +Native OpenCode,100,mini-swe-agent,medium,25,118,0.211864406779661 +Native OpenCode,100,mini-swe-agent,hard,5,99,0.050505050505050504 +Native OpenCode,200,opencode,easy,16,33,0.48484848484848486 +Native OpenCode,200,opencode,medium,25,118,0.211864406779661 +Native OpenCode,200,opencode,hard,2,99,0.020202020202020204 +Native OpenCode,200,claude-code,easy,20,33,0.6060606060606061 +Native OpenCode,200,claude-code,medium,31,118,0.2627118644067797 +Native OpenCode,200,claude-code,hard,9,99,0.09090909090909091 +Native OpenCode,200,codex,easy,21,33,0.6363636363636364 +Native OpenCode,200,codex,medium,34,118,0.288135593220339 +Native OpenCode,200,codex,hard,9,99,0.09090909090909091 +Native OpenCode,200,mini-swe-agent,easy,22,33,0.6666666666666666 +Native OpenCode,200,mini-swe-agent,medium,26,118,0.22033898305084745 +Native OpenCode,200,mini-swe-agent,hard,6,99,0.06060606060606061 +Native OpenCode,300,opencode,easy,16,33,0.48484848484848486 +Native OpenCode,300,opencode,medium,33,118,0.2796610169491525 +Native OpenCode,300,opencode,hard,3,99,0.030303030303030304 +Native OpenCode,300,claude-code,easy,22,33,0.6666666666666666 +Native OpenCode,300,claude-code,medium,36,118,0.3050847457627119 +Native OpenCode,300,claude-code,hard,15,99,0.15151515151515152 +Native OpenCode,300,codex,easy,13,33,0.3939393939393939 +Native OpenCode,300,codex,medium,24,118,0.2033898305084746 +Native OpenCode,300,codex,hard,5,99,0.050505050505050504 +Native OpenCode,300,mini-swe-agent,easy,17,33,0.5151515151515151 +Native OpenCode,300,mini-swe-agent,medium,28,118,0.23728813559322035 +Native OpenCode,300,mini-swe-agent,hard,4,99,0.04040404040404041 +Native OpenCode,400,opencode,easy,16,33,0.48484848484848486 +Native OpenCode,400,opencode,medium,29,118,0.2457627118644068 +Native OpenCode,400,opencode,hard,6,99,0.06060606060606061 +Native OpenCode,400,claude-code,easy,20,33,0.6060606060606061 +Native OpenCode,400,claude-code,medium,42,118,0.3559322033898305 +Native OpenCode,400,claude-code,hard,19,99,0.1919191919191919 +Native OpenCode,400,codex,easy,19,33,0.5757575757575758 +Native OpenCode,400,codex,medium,33,118,0.2796610169491525 +Native OpenCode,400,codex,hard,12,99,0.12121212121212122 +Native OpenCode,400,mini-swe-agent,easy,23,33,0.696969696969697 +Native OpenCode,400,mini-swe-agent,medium,35,118,0.2966101694915254 +Native OpenCode,400,mini-swe-agent,hard,10,99,0.10101010101010101 +Native OpenCode,500,opencode,easy,16,33,0.48484848484848486 +Native OpenCode,500,opencode,medium,23,118,0.19491525423728814 +Native OpenCode,500,opencode,hard,6,99,0.06060606060606061 +Native OpenCode,500,claude-code,easy,17,33,0.5151515151515151 +Native OpenCode,500,claude-code,medium,39,118,0.3305084745762712 +Native OpenCode,500,claude-code,hard,10,99,0.10101010101010101 +Native OpenCode,500,codex,easy,16,33,0.48484848484848486 +Native OpenCode,500,codex,medium,26,118,0.22033898305084745 +Native OpenCode,500,codex,hard,5,99,0.050505050505050504 +Native OpenCode,500,mini-swe-agent,easy,21,33,0.6363636363636364 +Native OpenCode,500,mini-swe-agent,medium,41,118,0.3474576271186441 +Native OpenCode,500,mini-swe-agent,hard,11,99,0.1111111111111111 +Native OpenCode,600,opencode,easy,14,33,0.42424242424242425 +Native OpenCode,600,opencode,medium,22,118,0.1864406779661017 +Native OpenCode,600,opencode,hard,6,99,0.06060606060606061 +Native OpenCode,600,claude-code,easy,21,33,0.6363636363636364 +Native OpenCode,600,claude-code,medium,44,118,0.3728813559322034 +Native OpenCode,600,claude-code,hard,16,99,0.16161616161616163 +Native OpenCode,600,codex,easy,15,33,0.45454545454545453 +Native OpenCode,600,codex,medium,28,118,0.23728813559322035 +Native OpenCode,600,codex,hard,3,99,0.030303030303030304 +Native OpenCode,600,mini-swe-agent,easy,24,33,0.7272727272727273 +Native OpenCode,600,mini-swe-agent,medium,42,118,0.3559322033898305 +Native OpenCode,600,mini-swe-agent,hard,16,99,0.16161616161616163 +Native OpenCode,700,opencode,easy,14,33,0.42424242424242425 +Native OpenCode,700,opencode,medium,28,118,0.23728813559322035 +Native OpenCode,700,opencode,hard,4,99,0.04040404040404041 +Native OpenCode,700,claude-code,easy,21,33,0.6363636363636364 +Native OpenCode,700,claude-code,medium,39,118,0.3305084745762712 +Native OpenCode,700,claude-code,hard,14,99,0.1414141414141414 +Native OpenCode,700,codex,easy,8,33,0.24242424242424243 +Native OpenCode,700,codex,medium,27,118,0.2288135593220339 +Native OpenCode,700,codex,hard,1,99,0.010101010101010102 +Native OpenCode,700,mini-swe-agent,easy,22,33,0.6666666666666666 +Native OpenCode,700,mini-swe-agent,medium,42,118,0.3559322033898305 +Native OpenCode,700,mini-swe-agent,hard,12,99,0.12121212121212122 +Native OpenCode,800,opencode,easy,12,33,0.36363636363636365 +Native OpenCode,800,opencode,medium,31,118,0.2627118644067797 +Native OpenCode,800,opencode,hard,7,99,0.0707070707070707 +Native OpenCode,800,claude-code,easy,24,33,0.7272727272727273 +Native OpenCode,800,claude-code,medium,42,118,0.3559322033898305 +Native OpenCode,800,claude-code,hard,15,99,0.15151515151515152 +Native OpenCode,800,codex,easy,10,33,0.30303030303030304 +Native OpenCode,800,codex,medium,25,118,0.211864406779661 +Native OpenCode,800,codex,hard,10,99,0.10101010101010101 +Native OpenCode,800,mini-swe-agent,easy,23,33,0.696969696969697 +Native OpenCode,800,mini-swe-agent,medium,42,118,0.3559322033898305 +Native OpenCode,800,mini-swe-agent,hard,15,99,0.15151515151515152 +Native OpenCode,900,opencode,easy,11,33,0.3333333333333333 +Native OpenCode,900,opencode,medium,28,118,0.23728813559322035 +Native OpenCode,900,opencode,hard,8,99,0.08080808080808081 +Native OpenCode,900,claude-code,easy,20,33,0.6060606060606061 +Native OpenCode,900,claude-code,medium,50,118,0.423728813559322 +Native OpenCode,900,claude-code,hard,15,99,0.15151515151515152 +Native OpenCode,900,codex,easy,9,33,0.2727272727272727 +Native OpenCode,900,codex,medium,21,118,0.17796610169491525 +Native OpenCode,900,codex,hard,6,99,0.06060606060606061 +Native OpenCode,900,mini-swe-agent,easy,26,33,0.7878787878787878 +Native OpenCode,900,mini-swe-agent,medium,44,118,0.3728813559322034 +Native OpenCode,900,mini-swe-agent,hard,15,99,0.15151515151515152 +Native OpenCode,1000,opencode,easy,14,33,0.42424242424242425 +Native OpenCode,1000,opencode,medium,30,118,0.2542372881355932 +Native OpenCode,1000,opencode,hard,7,99,0.0707070707070707 +Native OpenCode,1000,claude-code,easy,22,33,0.6666666666666666 +Native OpenCode,1000,claude-code,medium,44,118,0.3728813559322034 +Native OpenCode,1000,claude-code,hard,17,99,0.1717171717171717 +Native OpenCode,1000,codex,easy,19,33,0.5757575757575758 +Native OpenCode,1000,codex,medium,42,118,0.3559322033898305 +Native OpenCode,1000,codex,hard,13,99,0.13131313131313133 +Native OpenCode,1000,mini-swe-agent,easy,24,33,0.7272727272727273 +Native OpenCode,1000,mini-swe-agent,medium,53,118,0.4491525423728814 +Native OpenCode,1000,mini-swe-agent,hard,13,99,0.13131313131313133 +Harbor OpenCode-only,0,opencode,easy,11,33,0.3333333333333333 +Harbor OpenCode-only,0,opencode,medium,10,118,0.0847457627118644 +Harbor OpenCode-only,0,opencode,hard,6,99,0.06060606060606061 +Harbor OpenCode-only,0,claude-code,easy,14,33,0.42424242424242425 +Harbor OpenCode-only,0,claude-code,medium,22,118,0.1864406779661017 +Harbor OpenCode-only,0,claude-code,hard,6,99,0.06060606060606061 +Harbor OpenCode-only,0,codex,easy,14,33,0.42424242424242425 +Harbor OpenCode-only,0,codex,medium,20,118,0.1694915254237288 +Harbor OpenCode-only,0,codex,hard,7,99,0.0707070707070707 +Harbor OpenCode-only,0,mini-swe-agent,easy,14,33,0.42424242424242425 +Harbor OpenCode-only,0,mini-swe-agent,medium,16,118,0.13559322033898305 +Harbor OpenCode-only,0,mini-swe-agent,hard,6,99,0.06060606060606061 diff --git a/04-data-agent/results/2026-09-16/comparison.png b/04-data-agent/results/2026-09-16/comparison.png new file mode 100644 index 0000000..ca38580 Binary files /dev/null and b/04-data-agent/results/2026-09-16/comparison.png differ diff --git a/04-data-agent/results/2026-09-16/seta-checkpoint-150.json b/04-data-agent/results/2026-09-16/seta-checkpoint-150.json new file mode 100644 index 0000000..8107eb0 --- /dev/null +++ b/04-data-agent/results/2026-09-16/seta-checkpoint-150.json @@ -0,0 +1,65 @@ +{ + "status": { + "arm": "whitebox", + "phase": "checkpoint", + "started_at": 1789555915.8065052, + "passed": true, + "finished_at": 1789557493.4326816 + }, + "canonical_scores": { + "metric": "pass@1", + "arm": "whitebox", + "complete": true, + "graded_cells": 250, + "expected_cells": 250, + "harnesses": { + "whitebox_seta": { + "graded": 250, + "correct": 95.0, + "pass_at_1": 0.38, + "difficulty": { + "easy": { + "graded": 33, + "correct": 28.0, + "pass_at_1": 0.8484848484848485 + }, + "medium": { + "graded": 118, + "correct": 49.0, + "pass_at_1": 0.4152542372881356 + }, + "hard": { + "graded": 99, + "correct": 18.0, + "pass_at_1": 0.18181818181818182 + } + } + } + }, + "tito_pass": true, + "ungraded_attempts": 0, + "harness_versions": { + "whitebox_seta": {} + }, + "harness_versions_match_baseline": true, + "comparison_ready": true, + "average_pass_at_1": 0.38, + "selection": "first graded attempt per fixed task/harness; infrastructure failures excluded and retried" + }, + "checkpoint_evaluation": { + "source": "hf://buckets/HuggingEnvs/data-agent-daytona-artifacts/20260915-hf/jobs/train-whitebox-1789507273/run/checkpoint-150", + "manifest_sha256": "78252d1f78dc0109c0252d53e22d1bc2fd58d720f41164e3a8bae4c5d62bd8ea", + "step": 150, + "bundle_sha256": "f4a288eaafefddf3cb686eb89de184437a0496f75074a00f1903a9335dcd1a0d" + }, + "services": { + "job_id": "6aaa7488f76d6a098a710836", + "public_vllm": "https://6aaa7488f76d6a098a710836--8000.hf.jobs", + "server": "http://127.0.0.1:8100", + "space": "https://huggingenvs-data-agent-seta-whitebox-env.hf.space", + "tp": 1, + "dp": 1, + "flavor": "a100-large" + }, + "job_id": "6aaa7488f76d6a098a710836" +} diff --git a/04-data-agent/results/2026-09-16/snapshot.json.gz b/04-data-agent/results/2026-09-16/snapshot.json.gz new file mode 100644 index 0000000..61b6763 Binary files /dev/null and b/04-data-agent/results/2026-09-16/snapshot.json.gz differ diff --git a/04-data-agent/results/qualification/harbor-v3-upload-failure.json b/04-data-agent/results/qualification/harbor-v3-upload-failure.json new file mode 100644 index 0000000..d262789 --- /dev/null +++ b/04-data-agent/results/qualification/harbor-v3-upload-failure.json @@ -0,0 +1,71 @@ +{ + "id": "6aaa7b18f76d6a098a71093d", + "owner": "train-blackbox-1789557528", + "stage": "ERROR", + "status.json": { + "arm": "blackbox", + "phase": "smoke", + "started_at": 1789557599.0150673, + "passed": false, + "error_type": "TimeoutError", + "finished_at": 1789560537.914167 + }, + "services.json": { + "job_id": "6aaa7b18f76d6a098a71093d", + "public_vllm": "https://6aaa7b18f76d6a098a71093d--8000.hf.jobs", + "server": "http://127.0.0.1:8100", + "space": "https://huggingenvs-data-agent-blackbox-harbor-env.hf.space", + "tp": 1, + "dp": 1, + "flavor": "a100x4" + }, + "trackio_verified.json": { + "passed": true, + "project": "daytona-blackbox-qwen35-2b-smoke", + "run": "train-blackbox-1789557528", + "local_database": "/workspace/repro/outputs/train-blackbox-1789557528/trackio/daytona-blackbox-qwen35-2b-smoke.db", + "mode": "offline", + "remote_storage": "run artifact bucket", + "native_remote_readback": false, + "updated_at": 1789559547.7700548, + "unique_events": 6 + }, + "upload_status.json": { + "last_success": 1789560580.5810785, + "destination": "hf://buckets/HuggingEnvs/data-agent-daytona-artifacts/data-agent-reproduction-20260916/jobs/train-blackbox-1789557528", + "published_checkpoints": [ + "checkpoint-2", + "checkpoint-4" + ] + }, + "failure": "Transient HF Xet upload TimeoutError after all four optimizer updates; final cleanup published both checkpoints. Final integrated smoke validator was not reached.", + "posthoc_capture_audit": { + "blackbox": { + "completed_results": 37, + "tito_pass": 37, + "eligible_tokens": 19880, + "retained_tokens": 19880, + "rows_over_token_budget": 0, + "optimizer_rollouts_verified": 25 + } + }, + "optimizer_updates": [ + { + "step": 1, + "grad_norm": 11.0 + }, + { + "step": 2, + "grad_norm": 10.4375 + }, + { + "step": 3, + "grad_norm": 6.9375 + }, + { + "step": 4, + "grad_norm": 5.59375 + } + ], + "qualifies_long_run": false +} diff --git a/04-data-agent/results/qualification/harbor-v4.json b/04-data-agent/results/qualification/harbor-v4.json new file mode 100644 index 0000000..bc5551f --- /dev/null +++ b/04-data-agent/results/qualification/harbor-v4.json @@ -0,0 +1,56 @@ +{ + "id": "6aaa8a06f76d6a098a710a5e", + "owner": "train-blackbox-1789561350", + "stage": "COMPLETED", + "status.json": { + "arm": "blackbox", + "phase": "smoke", + "started_at": 1789561426.4942746, + "passed": true, + "finished_at": 1789565459.8160994 + }, + "services.json": { + "job_id": "6aaa8a06f76d6a098a710a5e", + "public_vllm": "https://6aaa8a06f76d6a098a710a5e--8000.hf.jobs", + "server": "http://127.0.0.1:8100", + "space": "https://huggingenvs-data-agent-blackbox-harbor-env.hf.space", + "tp": 1, + "dp": 1, + "flavor": "a100x4" + }, + "training_smoke_verified.json": { + "arm": "blackbox", + "passed": true, + "bundle_sha256": "d24c3641bdda424259741e27d451430830df1c54e0a31245ac2c8ae6210a44da", + "optimizer_steps": [ + 1, + 2, + 3, + 4 + ], + "native_optimizer_state_verified": true, + "remote_restore_verified": true, + "tito_pass": true, + "weights_updated": true, + "nonzero_gradient_updates": 4 + }, + "trackio_verified.json": { + "passed": true, + "project": "daytona-blackbox-qwen35-2b-smoke", + "run": "train-blackbox-1789561350", + "local_database": "/workspace/repro/outputs/train-blackbox-1789561350/trackio/daytona-blackbox-qwen35-2b-smoke.db", + "mode": "offline", + "remote_storage": "run artifact bucket", + "native_remote_readback": false, + "updated_at": 1789564559.6583176, + "unique_events": 6 + }, + "upload_status.json": { + "last_success": 1789565418.31508, + "destination": "hf://buckets/HuggingEnvs/data-agent-daytona-artifacts/data-agent-reproduction-20260916/jobs/train-blackbox-1789561350", + "published_checkpoints": [ + "checkpoint-2", + "checkpoint-4" + ] + } +} diff --git a/04-data-agent/results/qualification/opencode-v3.json b/04-data-agent/results/qualification/opencode-v3.json new file mode 100644 index 0000000..8eef827 --- /dev/null +++ b/04-data-agent/results/qualification/opencode-v3.json @@ -0,0 +1,56 @@ +{ + "id": "6aaa7b875527934177ee9d15", + "owner": "train-opencode-1789557638", + "stage": "COMPLETED", + "status.json": { + "arm": "opencode", + "phase": "smoke", + "started_at": 1789557710.0655792, + "passed": true, + "finished_at": 1789561700.7866313 + }, + "services.json": { + "job_id": "6aaa7b875527934177ee9d15", + "public_vllm": "https://6aaa7b875527934177ee9d15--8000.hf.jobs", + "server": "http://127.0.0.1:8100", + "space": "https://huggingenvs-data-agent-blackbox-opencode-env.hf.space", + "tp": 1, + "dp": 1, + "flavor": "a100x4" + }, + "training_smoke_verified.json": { + "arm": "opencode", + "passed": true, + "bundle_sha256": "6527c25ae379ab10f055577c5b87374c9018df3c8d28bb983f84fbd1e7f6302e", + "optimizer_steps": [ + 1, + 2, + 3, + 4 + ], + "native_optimizer_state_verified": true, + "remote_restore_verified": true, + "tito_pass": true, + "weights_updated": true, + "nonzero_gradient_updates": 4 + }, + "trackio_verified.json": { + "passed": true, + "project": "daytona-opencode-qwen35-2b-smoke", + "run": "train-opencode-1789557638", + "local_database": "/workspace/repro/outputs/train-opencode-1789557638/trackio/daytona-opencode-qwen35-2b-smoke.db", + "mode": "offline", + "remote_storage": "run artifact bucket", + "native_remote_readback": false, + "updated_at": 1789561370.6661468, + "unique_events": 6 + }, + "upload_status.json": { + "last_success": 1789561666.6907096, + "destination": "hf://buckets/HuggingEnvs/data-agent-daytona-artifacts/data-agent-reproduction-20260916/jobs/train-opencode-1789557638", + "published_checkpoints": [ + "checkpoint-2", + "checkpoint-4" + ] + } +} diff --git a/04-data-agent/results/qualification/seta-v2.json b/04-data-agent/results/qualification/seta-v2.json new file mode 100644 index 0000000..5fecf43 --- /dev/null +++ b/04-data-agent/results/qualification/seta-v2.json @@ -0,0 +1,47 @@ +{ + "id": "6aaa77a65527934177ee9c34", + "owner": "train-whitebox-1789556646", + "stage": "COMPLETED", + "status.json": { + "arm": "whitebox", + "phase": "smoke", + "started_at": 1789556698.284571, + "passed": true, + "finished_at": 1789558558.68511 + }, + "training_smoke_verified.json": { + "arm": "whitebox", + "passed": true, + "bundle_sha256": "8b02b40687414905830799a458bf253d3552f9a40860f9980c983fb4ededa45a", + "optimizer_steps": [ + 1, + 2, + 3, + 4 + ], + "native_optimizer_state_verified": true, + "remote_restore_verified": true, + "tito_pass": true, + "weights_updated": true, + "nonzero_gradient_updates": 2 + }, + "trackio_verified.json": { + "passed": true, + "project": "daytona-whitebox-qwen35-2b-smoke", + "run": "train-whitebox-1789556646", + "local_database": "/workspace/repro/outputs/train-whitebox-1789556646/trackio/daytona-whitebox-qwen35-2b-smoke.db", + "mode": "offline", + "remote_storage": "run artifact bucket", + "native_remote_readback": false, + "updated_at": 1789558378.5921993, + "unique_events": 6 + }, + "upload_status.json": { + "last_success": 1789558531.14131, + "destination": "hf://buckets/HuggingEnvs/data-agent-daytona-artifacts/data-agent-reproduction-20260916/jobs/train-whitebox-1789556646", + "published_checkpoints": [ + "checkpoint-2", + "checkpoint-4" + ] + } +} diff --git a/04-data-agent/results/qualification/seta-v3.json b/04-data-agent/results/qualification/seta-v3.json new file mode 100644 index 0000000..afc0c09 --- /dev/null +++ b/04-data-agent/results/qualification/seta-v3.json @@ -0,0 +1,56 @@ +{ + "id": "6aaa7f915527934177ee9da4", + "owner": "train-whitebox-1789558672", + "stage": "COMPLETED", + "status.json": { + "arm": "whitebox", + "phase": "smoke", + "started_at": 1789558725.4122534, + "passed": true, + "finished_at": 1789560315.8249245 + }, + "services.json": { + "job_id": "6aaa7f915527934177ee9da4", + "public_vllm": "https://6aaa7f915527934177ee9da4--8000.hf.jobs", + "server": "http://127.0.0.1:8100", + "space": "https://huggingenvs-data-agent-seta-whitebox-env.hf.space", + "tp": 1, + "dp": 1, + "flavor": "h200x2" + }, + "training_smoke_verified.json": { + "arm": "whitebox", + "passed": true, + "bundle_sha256": "6527c25ae379ab10f055577c5b87374c9018df3c8d28bb983f84fbd1e7f6302e", + "optimizer_steps": [ + 1, + 2, + 3, + 4 + ], + "native_optimizer_state_verified": true, + "remote_restore_verified": true, + "tito_pass": true, + "weights_updated": true, + "nonzero_gradient_updates": 2 + }, + "trackio_verified.json": { + "passed": true, + "project": "daytona-whitebox-qwen35-2b-smoke", + "run": "train-whitebox-1789558672", + "local_database": "/workspace/repro/outputs/train-whitebox-1789558672/trackio/daytona-whitebox-qwen35-2b-smoke.db", + "mode": "offline", + "remote_storage": "run artifact bucket", + "native_remote_readback": false, + "updated_at": 1789560105.7279177, + "unique_events": 6 + }, + "upload_status.json": { + "last_success": 1789560265.77855, + "destination": "hf://buckets/HuggingEnvs/data-agent-daytona-artifacts/data-agent-reproduction-20260916/jobs/train-whitebox-1789558672", + "published_checkpoints": [ + "checkpoint-2", + "checkpoint-4" + ] + } +} diff --git a/04-data-agent/results/validation.md b/04-data-agent/results/validation.md new file mode 100644 index 0000000..d577898 --- /dev/null +++ b/04-data-agent/results/validation.md @@ -0,0 +1,53 @@ +# PR preparation validation — 2026-09-16 + +This records validation of the prepared sources separately from the historical learning curves. + +| Check | Evidence | +| --- | --- | +| HuggingEnvs CPU regression suite | 149 passed, 1 skipped; 24 subtests. Includes real CPU optimizer grouping, save/resume boundaries, capture budgets, task dispatch, HTTP controls, eval recovery and artifact provenance. | +| Portable archive | 10,606 packaged files hash-verified; source runtime matches the reviewed files; no configured credential values included. No external local experiments checkout required to build. | +| Frozen native grading | 1,250 task configurations verified; all 250 original first-graded baseline answers replayed with identical scores. | +| Local/Hub commands | CLI help, dry-run commands, Python compilation and fatal-error lint passed; generated project index checked. | +| OpenEnv | 2,393 CPU tests passed with unrelated QED service tests excluded; 107 additional upstream MCP integration tests and 65 Gradio/MCP/TBench tests passed after the current-main merge; 39 client/TiTO and 59 rollout/session regressions passed for the final fixes; 334 passed and 7 skipped for client cancellation/discovery/Harbor regressions after the last upstream merge. GitHub CI is green on Python 3.11/3.12. Harbor capture/UI checks include concurrent trace isolation, session budgets and browser layout. | +| TRL | 245 CPU tests passed, plus HTTP controls and pre-commit checks. Main is merged; all PR CI passed, including the distributed GPU smoke. | + +## Current training qualification + +| Implementation | HF Job | Trainer bundle | State | +| --- | --- | --- | --- | +| Harbor / OpenCode | [6aaa8a06f76d6a098a710a5e](https://huggingface.co/jobs/HuggingEnvs/6aaa8a06f76d6a098a710a5e) | v4 | **Passed**: four nonzero-gradient updates, exact-token retention, native optimizer state, remote restore, changed weights; [receipt](qualification/harbor-v4.json) | +| Native OpenCode | [6aaa7b875527934177ee9d15](https://huggingface.co/jobs/HuggingEnvs/6aaa7b875527934177ee9d15) | v3 | **Passed**: four nonzero-gradient updates, exact-token retention, native optimizer state, remote restore, changed weights; [receipt](qualification/opencode-v3.json) | +| SETA whitebox | [6aaa7f915527934177ee9da4](https://huggingface.co/jobs/HuggingEnvs/6aaa7f915527934177ee9da4) | v3 | **Passed**: four steps, exact-token audit, native optimizer state, remote restore, changed weights; [receipt](qualification/seta-v3.json) | + +- **v2**: SHA256 `8b02b40687414905830799a458bf253d3552f9a40860f9980c983fb4ededa45a`, Hub revision `0e59f18b0ddf0df0f46aa8925b4d8bb66aa95bb5`. +- **v3**: SHA256 `6527c25ae379ab10f055577c5b87374c9018df3c8d28bb983f84fbd1e7f6302e`. Hub revision `599efbda7c93056e9d0a6a2a3324d24ac1ba2f3f`. Uses OpenEnv `b13aeb9f8ecd4817e02d3a37c2a9ae15e41710e3` and TRL `8e87edb45eac7c52d749256379714fa40f0eb746`; 10,604 packaged files. Subsequent OpenEnv PR commits preserve verifier warning diagnostics, clarify timeout scope and merge upstream client cancellation/discovery fixes. The later TRL merge changes only a tiny Gemma2 test-model generator; runtime qualification remains tied to the explicit pins above. + +- **v4**: SHA256 `d24c3641bdda424259741e27d451430830df1c54e0a31245ac2c8ae6210a44da`, Hub revision `d7622b44f55c65387778327229543d36446e6597`; 10,606 packaged files. Model, OpenEnv/TRL pins, training code and settings match v3. It adds the shared transfer retry path and host baseline-cohort checks. + +Harbor and SETA use their existing separately pinned environments. Native OpenCode uses v3, deployed only after confirming no active Jobs used that Space; CPU Basic and sandbox capacity 100 are retained. The active SETA evaluation and Harbor-only Slurm trainer were not restarted. Every future long run still requires proofs matching its own exact bundle, environment and baseline; these receipts do not waive that gate for another bundle. + +The final v3 SETA smoke completed successfully. The earlier successful v2 [Job 6aaa77a65527934177ee9c34](https://huggingface.co/jobs/HuggingEnvs/6aaa77a65527934177ee9c34) and its [receipt](qualification/seta-v2.json) are retained independently. Both have two nonzero-gradient updates and four completed optimizer steps. + +The final host-launcher regression suite additionally verifies that native diagnostic and four-harness comparison baselines remain separate, that checkpoint curves receive a matching baseline at step 0, and that changed score files fail validation. These host-only admission/reporting changes do not alter the GPU trainer runtime used by the qualification bundle. + +## Earlier qualification attempts + +The first trainer bundle was `ccfe97822cf7c88931acda8a4894bd7515e40a939ebfcc4c20c14db45e607de3`, uploaded to `HuggingEnvs/data-agent-daytona-repro` at revision `3808a6d5c48320b5e7745c877dc9b7ed2819310b`. Source pins are in [sources.json](../hf/configs/sources.json). The OpenEnv runtime pin includes the TiTO/UI changes; later OpenEnv PR commits update documentation, optional tests and merge newer upstream MCP behavior. + +These jobs qualify the new trainer against existing separately pinned Spaces. The first attempts did not restart or upgrade those Spaces; the later idle native OpenCode update is recorded above. A smoke proves four optimizer updates with a checkpoint-2 remote restore; it is not a new baseline or evidence of a reward gain. + +| Implementation | Job | GPUs | State | +| --- | --- | --- | --- | +| Harbor / OpenCode | [6aaa75bb5527934177ee9b8b](https://huggingface.co/jobs/HuggingEnvs/6aaa75bb5527934177ee9b8b) | A100 ×4 allocation; two used | Failed before optimizer startup: missing endpoint directory | +| Native OpenCode | [6aaa75bb5527934177ee9b8d](https://huggingface.co/jobs/HuggingEnvs/6aaa75bb5527934177ee9b8d) | A100 ×4 allocation; two used | Failed before optimizer startup: missing endpoint directory | +| SETA whitebox | [6aaa75bbf76d6a098a710867](https://huggingface.co/jobs/HuggingEnvs/6aaa75bbf76d6a098a710867) | H200 ×2 | Failed before optimizer startup: missing endpoint directory | + +The clean-Job failure is fixed by creating the endpoint/log parent directories in `serve/vllm.sh`. The failed cohort is preserved. A second cohort exposed a deployed-server API mismatch in both async arms; those two jobs were stopped before optimizer updates (`6aaa77a65527934177ee9c30`, `6aaa77a65527934177ee9c32`). The Harbor client now omits only default provider/eval arguments. Explicit settings are still sent. The idle native OpenCode Space was upgraded to accept and enforce sampling. Training submission now checks the remote tool schema before allocating a GPU Job. + +Completion requires `training_smoke_verified.json`: exact capture, retained supervision, native optimizer state, remote restoration and changed weights. Pending jobs are not counted as passed. + +The v3 Harbor Job later ended with an HF Xet upload `TimeoutError` after completing all four updates. Its final cleanup published both checkpoints. A separate audit reconciled 37/37 completed captures, all 19,880 eligible supervised tokens and 25 optimizer rollout receipts; this does **not** waive the failed integrated qualification. The [failure receipt](qualification/harbor-v3-upload-failure.json) is preserved. The shared publisher now retries transient transport/429/5xx errors up to three attempts, keeps ready markers last, and allows an hour for an already-active full-checkpoint upload during shutdown. Permission and validation errors remain fatal. Fault-injection and full CPU regression tests passed. The v4 Harbor rerun completed successfully, including both checkpoint publications. + +## Preserved material + +Preparation uses separate Git worktrees. Original dirty worktrees, active services, source snapshots, raw captures and checkpoint files remain intact. Superseded local guides/build inputs moved into ignored `04-data-agent/temp/historical-notes/` and `temp/legacy-hf/`; replaced bundle outputs are archived in `temp/build-archive/`. Committed results contain compact scores, a static figure and a compressed full metrics/provenance snapshot. No raw task answers, credentials, model checkpoints or Trackio databases are committed. diff --git a/04-data-agent/serve/vllm.sh b/04-data-agent/serve/vllm.sh new file mode 100755 index 0000000..20d761a --- /dev/null +++ b/04-data-agent/serve/vllm.sh @@ -0,0 +1,238 @@ +#!/bin/bash +# Core launcher: vLLM OpenAI-compatible server + cloudflared tunnel. +# Invoked from per-model .slurm files that pre-set environment variables. +# +# Required env vars: +# MODEL HF model id (e.g. Qwen/Qwen3-4B) +# TP_SIZE tensor-parallel size (per replica) +# DP_SIZE data-parallel size (replicas, vLLM-managed) +# MAX_MODEL_LEN max prompt+output tokens +# SHORT_NAME short slug for log/url filenames (e.g. qwen3-4b) +# +# Optional env vars: +# PORT default 8000 +# GPU_MEMORY_UTILIZATION default 0.92 +# TOOL_CALL_PARSER default hermes +# REASONING_PARSER default qwen3 (empty disables) +# EXTRA_VLLM_ARGS extra args appended verbatim +# READY_TIMEOUT_SEC default 1800 (30 min, larger models need longer) +# TRL_PROD default current working directory +# +# Outputs: +# VLLM_URL_FILE (default: $TRL_PROD/temp/vllm-url-.txt) +# VLLM_LOG (default: $TRL_PROD/temp/vllm-server-.log) + +set -e + +: "${MODEL:?MODEL is required}" +: "${TP_SIZE:?TP_SIZE is required}" +: "${DP_SIZE:?DP_SIZE is required}" +: "${MAX_MODEL_LEN:?MAX_MODEL_LEN is required}" +: "${SHORT_NAME:?SHORT_NAME is required}" + +# Derive a per-job port so co-located jobs (this cluster does not allocate +# nodes exclusively) don't all collide on 8000. Range 8000-8999. +PORT="${PORT:-$((8000 + ${SLURM_JOB_ID:-0} % 1000))}" +GPU_MEMORY_UTILIZATION="${GPU_MEMORY_UTILIZATION:-0.92}" +TOOL_CALL_PARSER="${TOOL_CALL_PARSER:-hermes}" +# Use `:-` only when var is UNSET (not when explicitly empty), so per-model +# slurm scripts can opt out of a reasoning parser with REASONING_PARSER="". +REASONING_PARSER="${REASONING_PARSER-qwen3}" +EXTRA_VLLM_ARGS="${EXTRA_VLLM_ARGS:-}" +READY_TIMEOUT_SEC="${READY_TIMEOUT_SEC:-1800}" +TRL_PROD="${TRL_PROD:-$PWD}" +# gradio by default: it needs no binary download and no ingress, and these endpoints are reached +# from off-cluster (evals, sandboxed agents). TUNNEL=cloudflared keeps the old path; TUNNEL=none +# serves locally only. +TUNNEL="${TUNNEL:-gradio}" +CLOUDFLARED="${CLOUDFLARED:-$HOME/.local/bin/cloudflared}" + +cd "$TRL_PROD" +# shellcheck disable=SC1091 +source "${VENV:-.venv312}/bin/activate" # .venv312 = the current (cuda-13/vllm-0.25) env +export UV_LINK_MODE="${UV_LINK_MODE:-copy}" +export VLLM_USE_AOT_COMPILE="${VLLM_USE_AOT_COMPILE:-0}" # avoids vLLM distributed-startup errors (trl-internal #206) + +# The API-server process waits VLLM_ENGINE_READY_TIMEOUT_S (default 600s) for +# the engine cores to come up. With full CUDA-graph capture at large context +# on a CPU-contended node, engine init can exceed 600s and the API server +# bails out *after* the engines were nearly ready. Give it the same generous +# budget we use for /health polling. +export VLLM_ENGINE_READY_TIMEOUT_S="${VLLM_ENGINE_READY_TIMEOUT_S:-$READY_TIMEOUT_SEC}" + +NODE_HOSTNAME=$(hostname) +NODE_IP=$(hostname -I | awk '{print $1}') + +TOTAL_GPUS=$((TP_SIZE * DP_SIZE)) + +echo "============================================================" +echo "vLLM + Cloudflare tunnel" +echo " Model: $MODEL" +echo " Short name: $SHORT_NAME" +echo " Node: $NODE_HOSTNAME ($NODE_IP)" +echo " Port: $PORT" +echo " TP size: $TP_SIZE" +echo " DP size: $DP_SIZE" +echo " Total GPUs: $TOTAL_GPUS" +echo " Max model len: $MAX_MODEL_LEN" +echo " GPU mem util: $GPU_MEMORY_UTILIZATION" +echo " Tool parser: $TOOL_CALL_PARSER" +echo " Reasoning parser:$REASONING_PARSER" +echo " Extra args: $EXTRA_VLLM_ARGS" +echo " vllm bin: $(command -v vllm)" +echo " python: $(which python)" +echo " START TIME: $(date)" +echo "============================================================" +nvidia-smi --query-gpu=name,memory.total --format=csv,noheader 2>/dev/null || true + +# Install cloudflared if missing (skipped unless TUNNEL=cloudflared) +if [ "$TUNNEL" = cloudflared ] && [ ! -f "$CLOUDFLARED" ]; then + echo ">>> Installing cloudflared to $CLOUDFLARED ..." + mkdir -p "$(dirname "$CLOUDFLARED")" + curl -sSL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 \ + -o "$CLOUDFLARED" + chmod +x "$CLOUDFLARED" +fi + +VLLM_LOG="${VLLM_LOG:-$TRL_PROD/temp/vllm-server-${SLURM_JOB_ID:-$$}.log}" +mkdir -p "$(dirname "$VLLM_LOG")" +: > "$VLLM_LOG" +echo ">>> vLLM log: $VLLM_LOG" + +# Build vllm args +VLLM_ARGS=( + "$MODEL" + --host 0.0.0.0 + --port "$PORT" + --tensor-parallel-size "$TP_SIZE" + --max-model-len "$MAX_MODEL_LEN" + --gpu-memory-utilization "$GPU_MEMORY_UTILIZATION" + --trust-remote-code + --enable-auto-tool-choice + --tool-call-parser "$TOOL_CALL_PARSER" +) +if [ "$DP_SIZE" -gt 1 ]; then + VLLM_ARGS+=(--data-parallel-size "$DP_SIZE") +fi +if [ -n "$REASONING_PARSER" ]; then + VLLM_ARGS+=(--reasoning-parser "$REASONING_PARSER") +fi +# ENFORCE_EAGER=1 skips torch.compile + CUDA-graph capture. Slower decode but +# much faster, crash-free startup (the Inductor autotune path can fault under +# memory pressure on shared nodes). Recommended for the large TP>=2 models. +if [ "${ENFORCE_EAGER:-0}" = "1" ]; then + VLLM_ARGS+=(--enforce-eager) +fi +# ENABLE_THINKING=0 sets a SERVER-SIDE default of enable_thinking=false for the +# chat template, so every client (incl. opencode, which makes its own API calls +# and can't send per-request kwargs) gets thinking off. vLLM merges this with +# request-level chat_template_kwargs (request wins). NOTE the flag is +# --default-chat-template-kwargs (vLLM 0.18); plain --chat-template-kwargs does +# NOT exist and errors with "unrecognized arguments". +if [ "${ENABLE_THINKING:-1}" = "0" ]; then + VLLM_ARGS+=(--default-chat-template-kwargs '{"enable_thinking": false}') +fi +# shellcheck disable=SC2206 +EXTRA_ARR=($EXTRA_VLLM_ARGS) +VLLM_ARGS+=("${EXTRA_ARR[@]}") + +echo ">>> Starting vLLM:" +echo " vllm serve ${VLLM_ARGS[*]}" +vllm serve "${VLLM_ARGS[@]}" >> "$VLLM_LOG" 2>&1 & +VLLM_PID=$! + +# Stream the vllm log into job stdout so failures are immediately visible. +tail -f -n +1 --pid=$$ "$VLLM_LOG" & +TAIL_PID=$! + +# Wait for /health +echo ">>> Waiting up to ${READY_TIMEOUT_SEC}s for vLLM /health ..." +READY=0 +for i in $(seq 1 "$READY_TIMEOUT_SEC"); do + if curl -s "http://localhost:$PORT/health" > /dev/null 2>&1; then + echo ">>> vLLM ready after ${i}s" + READY=1 + break + fi + if ! kill -0 "$VLLM_PID" 2>/dev/null; then + echo "ERROR: vLLM exited prematurely. Tail of log:" + tail -80 "$VLLM_LOG" + exit 1 + fi + sleep 1 +done +if [ "$READY" -ne 1 ]; then + echo "ERROR: vLLM did not become ready within ${READY_TIMEOUT_SEC}s. Tail:" + tail -80 "$VLLM_LOG" + kill "$VLLM_PID" 2>/dev/null || true + exit 1 +fi + +echo ">>> /v1/models:" +curl -s "http://localhost:$PORT/v1/models" | python -m json.tool || true + +# Start the tunnel +TUNNEL_LOG="/tmp/tunnel_${SHORT_NAME}_${SLURM_JOB_ID:-$$}.log" +TUNNEL_URL="" +TUNNEL_PID="" +echo "" +echo "============================================================" +echo ">>> Starting tunnel: $TUNNEL (log: $TUNNEL_LOG)" +echo "============================================================" +case "$TUNNEL" in + gradio) + # The helper only prints TUNNEL_URL= after proving a request THROUGH the tunnel reaches this + # server, so a URL here is a working endpoint rather than merely a resolving hostname. + python "$(dirname "${BASH_SOURCE[0]}")/../gradio_tunnel.py" \ + --port "$PORT" --verify-path /health > "$TUNNEL_LOG" 2>>"$TUNNEL_LOG" & + TUNNEL_PID=$! + for i in $(seq 1 60); do + TUNNEL_URL=$(grep -o 'TUNNEL_URL=https://[^ ]*' "$TUNNEL_LOG" 2>/dev/null | head -1 | cut -d= -f2-) + [ -n "$TUNNEL_URL" ] && break + if grep -q "TUNNEL_ERROR=" "$TUNNEL_LOG" 2>/dev/null; then + echo "ERROR: gradio tunnel did not reach the server:"; cat "$TUNNEL_LOG"; break + fi + sleep 5 + done + ;; + cloudflared) + "$CLOUDFLARED" tunnel --url "http://localhost:$PORT" 2>&1 | tee "$TUNNEL_LOG" & + TUNNEL_PID=$! + for i in $(seq 1 60); do + TUNNEL_URL=$(grep -o 'https://[a-z0-9-]*\.trycloudflare\.com' "$TUNNEL_LOG" 2>/dev/null | head -1) + [ -n "$TUNNEL_URL" ] && break + sleep 1 + done + ;; + none) + echo ">>> TUNNEL=none, serving locally only" + ;; +esac + +URL_FILE="${VLLM_URL_FILE:-$TRL_PROD/temp/vllm-url-${SLURM_JOB_ID:-$$}.txt}" +mkdir -p "$(dirname "$URL_FILE")" +echo "$TUNNEL_URL" > "$URL_FILE" + +echo "" +echo "============================================================" +echo ">>> TUNNEL URL: ${TUNNEL_URL:-}" +echo "" +echo " OpenAI endpoint: ${TUNNEL_URL}/v1" +echo " Models: ${TUNNEL_URL}/v1/models" +echo " Health: ${TUNNEL_URL}/health" +echo " Local: http://${NODE_HOSTNAME}:${PORT}/v1" +echo " URL file: $URL_FILE" +echo "============================================================" +echo "" + +# Quick self-test against the tunnel (optional, ignore failures) +if [ -n "$TUNNEL_URL" ]; then + echo ">>> Self-test (chat completion via tunnel) ..." + curl -sS --max-time 60 -X POST "${TUNNEL_URL}/v1/chat/completions" \ + -H "Content-Type: application/json" \ + -d "{\"model\":\"$MODEL\",\"messages\":[{\"role\":\"user\",\"content\":\"Reply with the single word PONG.\"}],\"max_tokens\":16,\"temperature\":0}" \ + | python -m json.tool 2>/dev/null | head -40 || echo " (self-test failed or no JSON returned — server may still be warming up)" +fi + +# Keep running until vllm exits (or job time elapses). +wait "$VLLM_PID" diff --git a/04-data-agent/tools/audit_blackbox.py b/04-data-agent/tools/audit_blackbox.py new file mode 100644 index 0000000..a5963d2 --- /dev/null +++ b/04-data-agent/tools/audit_blackbox.py @@ -0,0 +1,10 @@ +"""Use the original baseline's exact-token auditor on the Daytona captures.""" +import argparse +from pathlib import Path +from baseline_checks import audit_captures + +p=argparse.ArgumentParser();p.add_argument('--phase',choices=['smoke','resume','final'],required=True) +p.add_argument('--output-root',type=Path,default=Path(__file__).resolve().parents[1]/'logs/20260915/blackbox') +args=p.parse_args() +root=args.output_root +audit_captures(root,root,args.phase) diff --git a/04-data-agent/tools/audit_multiharness_training.py b/04-data-agent/tools/audit_multiharness_training.py new file mode 100644 index 0000000..a34d4aa --- /dev/null +++ b/04-data-agent/tools/audit_multiharness_training.py @@ -0,0 +1,73 @@ +"""Replay saved training captures through the same lossless TITO checks as the smoke test.""" + +import argparse +import collections +import json +import os +from pathlib import Path + +from openenv.harbor.models import HarborRolloutResult +from smoke_multiharness_tito import audit + + +def save_json(path, value): + temporary = path.with_suffix(f'.{os.getpid()}.tmp') + temporary.write_text(json.dumps(value, indent=2) + '\n') + temporary.replace(path) + + +def summarize_checks(checks, token_budget): + by_harness = collections.defaultdict(list) + for check in checks: + by_harness[check['harness']].append(check) + return {h: { + 'rollouts': len(rows), 'completed_results': sum(r['completed_result'] for r in rows), + 'incomplete_results': sum(not r['completed_result'] for r in rows), + 'tito_pass': sum(r['tito_pass'] for r in rows), + 'graded': sum(r.get('reward') is not None for r in rows), + 'reward_sum': sum(r.get('reward') or 0 for r in rows), + 'task_indices': sorted({r['task_index'] for r in rows}), 'token_budget': token_budget, + 'largest_row_tokens': max(r.get('largest_row_tokens', 0) for r in rows), + **{key: sum(r.get(key, 0) for r in rows) + for key in ('rows', 'eligible_tokens', 'retained_tokens', 'rows_over_40960', 'rows_over_token_budget')}, + } for h, rows in sorted(by_harness.items())} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("directory", type=Path, help="Job's audit directory") + args = parser.parse_args() + config_path = args.directory.parent / "run_config.json" + config = json.loads(config_path.read_text()) if config_path.exists() else {} + training = config.get("training", config) + token_budget = (training['max_model_len'] if training.get('atomic_rollouts') + else training.get("token_budget", 40960)) + output = args.directory / "tito_checks" + output.mkdir(parents=True, exist_ok=True) + checks = [] + for path in sorted((args.directory / "rollouts").glob("*.json")): + cached = output / path.name + if cached.exists(): + previous = json.loads(cached.read_text()) + if previous.get("token_budget") == token_budget and previous.get('audit_schema_version') == 2: + checks.append(previous) + continue + record = json.loads(path.read_text()) + check = {k: record[k] for k in ("group_id", "episode_id", "harness", "task_index")} + check.update(audit_schema_version=2, token_budget=token_budget, + completed_result=record.get('result') is not None) + try: + result = HarborRolloutResult.model_validate(record["result"]) + validation, _ = audit(result, token_budget=token_budget) + check.update(validation, reward=result.reward, ok=result.ok, error=result.error) + except Exception as exc: # noqa: BLE001 - record a failed capture and audit the remaining ones + check.update(tito_pass=False, error=f"{type(exc).__name__}: {exc}") + save_json(cached, check) + checks.append(check) + summary = summarize_checks(checks, token_budget) + save_json(args.directory / "tito_summary.json", summary) + print(json.dumps(summary, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/tools/cleanup_daytona.py b/04-data-agent/tools/cleanup_daytona.py new file mode 100644 index 0000000..8809d34 --- /dev/null +++ b/04-data-agent/tools/cleanup_daytona.py @@ -0,0 +1,50 @@ +"""Delete only this comparison's owned Daytona sandboxes after the owning job exits.""" +import argparse +import concurrent.futures +import json +import time +from pathlib import Path + +from dotenv import load_dotenv +from daytona import Daytona, ListSandboxesQuery + + +def main(): + p=argparse.ArgumentParser(description=__doc__) + group=p.add_mutually_exclusive_group(required=True) + group.add_argument('--legacy-baseline',action='store_true') + group.add_argument('--owner') + p.add_argument('--out',type=Path,required=True) + args=p.parse_args() + repo=Path(__file__).resolve().parents[3] + load_dotenv(repo/'experiments/.env') + client=Daytona() + labels={'experiment':'daytona-harness-comparison','run':'20260915'} + if args.owner: labels['owner']=args.owner + sandboxes=list(client.list(ListSandboxesQuery(labels=labels),request_timeout=30)) + if args.legacy_baseline: + sandboxes=[s for s in sandboxes if 'owner' not in s.labels] + def remove(sandbox): + try: + client.delete(sandbox,timeout=90,wait=True) + return {'sandbox_id':sandbox.id,'deleted':True} + except Exception as exc: + return {'sandbox_id':sandbox.id,'deleted':False,'error_type':type(exc).__name__} + with concurrent.futures.ThreadPoolExecutor(max_workers=8) as pool: + results=list(pool.map(remove,sandboxes)) + # A successful wait=True delete can precede the list index update. Verify + # disappearance with a bounded poll before reporting a leaked sandbox. + deadline=time.monotonic()+60 + while True: + remaining=list(client.list(ListSandboxesQuery(labels=labels),request_timeout=30)) + if args.legacy_baseline: remaining=[s for s in remaining if 'owner' not in s.labels] + if not remaining or time.monotonic()>=deadline: break + time.sleep(5) + report={'labels':labels,'legacy_baseline':args.legacy_baseline,'results':results,'remaining':len(remaining)} + args.out.parent.mkdir(parents=True,exist_ok=True) + args.out.write_text(json.dumps(report,indent=2)+'\n') + print(json.dumps({'selected':len(sandboxes),'remaining':len(remaining)})) + return 2 if remaining else 0 + + +if __name__=='__main__': raise SystemExit(main()) diff --git a/04-data-agent/tools/eval_whitebox_native.py b/04-data-agent/tools/eval_whitebox_native.py new file mode 100644 index 0000000..ee6b3e9 --- /dev/null +++ b/04-data-agent/tools/eval_whitebox_native.py @@ -0,0 +1,234 @@ +"""Resumable pass@1 using frozen TRL's actual token-preserving bash/SETA loop.""" +from __future__ import annotations + +import argparse +import concurrent.futures +import copy +import inspect +import json +import math +import time +import uuid +from pathlib import Path +from types import SimpleNamespace + +import httpx +from transformers import AutoProcessor +from transformers.utils import get_json_schema +from trl.chat_template_utils import ( + add_response_schema, get_training_chat_template, + is_chat_template_prefix_preserving, parse_response, +) +from trl.trainer.grpo_trainer import GRPOTrainer +from whitebox_bash import white_box_bash_env + +MODEL = 'Qwen/Qwen3.5-2B' +REVISION = '15852e8c16360a2fea060d615a32b45270f8a8fc' +SYSTEM = ( + 'You are a terminal agent working in a sandbox. Use the available tools to inspect the ' + 'filesystem and solve the task. Work step by step: look before you act. When you are confident, ' + 'call submit_solution with the final answer and nothing else -- not the command that would ' + 'produce it.' +) + + +class NativeLoop: + _get_tool_suffix_ids = GRPOTrainer._get_tool_suffix_ids + _tool_call_loop = GRPOTrainer._tool_call_loop + + def __init__(self, env, processor, url, model, deadline): + self.processing_class = processor + self._tokenizer = processor.tokenizer + self._is_vlm = True + self.chat_template = ( + None if is_chat_template_prefix_preserving(processor) + else get_training_chat_template(processor) + ) + self.chat_template_kwargs = {'enable_thinking': False} + methods = { + n:m for n,m in inspect.getmembers(env,predicate=inspect.ismethod) + if not n.startswith('_') and n not in {'reset','get_reward'} + } + self.tools = [get_json_schema(m) for m in methods.values()] + self._sync_tool_dicts = [methods] + self._async_tool_dicts = [{}] + self.max_tool_calling_iterations = 16 # first generation + 16 continuations = 17 calls + self.max_completion_length = 16384 # includes masked tool-result tokens, as in sync training + self.use_vllm = True + self.vllm_mode = 'server' + self.model = SimpleNamespace(config=SimpleNamespace(text_config=SimpleNamespace(max_position_embeddings=131072))) + self.url, self.model_name, self.deadline = url.rstrip('/'), model, deadline + self.client = httpx.Client(timeout=120) + self.calls = [] + self.stop_reason = None + + def _generate_single_turn(self, prompts, images, multimodal_fields, has_tool_images=False): + assert len(prompts)==1 and not images and not has_tool_images + remaining = self.deadline-time.monotonic() + if remaining <= 0: + self.stop_reason = 'episode_deadline' + return [[]], [[]] + prompt=prompts[0] + if self.calls: + parent=self.calls[-1]['prompt_ids']+self.calls[-1]['completion_ids'] + assert prompt[:len(parent)]==parent, 'native loop lost the sampled prefix' + try: + response=self.client.post(self.url+'/completions',json={ + 'model':self.model_name,'prompt':prompt,'max_tokens':min(4096,131072-len(prompt)), + 'temperature':0.8,'top_p':1.0,'top_k':-1,'n':1,'logprobs':0, + 'return_token_ids':True,'return_tokens_as_token_ids':True, + },timeout=remaining).raise_for_status().json() + except httpx.ReadTimeout: + if time.monotonic() < self.deadline: + raise # An earlier transport failure remains an ungraded attempt. + self.stop_reason = 'episode_deadline' + # The native loop already accepts an empty continuation on truncation. + # Stop without inventing tokens; grade work done within the episode budget. + return [[]], [[]] + choice=response['choices'][0] + ids=choice['token_ids'] + lp=choice['logprobs'] + assert choice['prompt_token_ids']==prompt, 'engine prompt differs from supplied token IDs' + assert ids and len(ids)==len(lp['tokens'])==len(lp['token_logprobs']) + assert lp['tokens']==[f'token_id:{i}' for i in ids], 'sampled token/logprob pairing mismatch' + assert all(math.isfinite(p) for p in lp['token_logprobs']) + self.calls.append({'prompt_ids':prompt.copy(),'completion_ids':ids.copy(), + 'logprobs':lp['token_logprobs'].copy(),'finish_reason':choice['finish_reason']}) + return [ids], [lp['token_logprobs']] + + def run(self, prompt): + prompts=[[{'role':'system','content':SYSTEM},{'role':'user','content':'Solve the task.'+prompt}]] + tokenized=self.processing_class.apply_chat_template( + prompts[0], tools=self.tools, add_generation_prompt=True,tokenize=True, + chat_template=self.chat_template,return_dict=False,**self.chat_template_kwargs, + ) + ids=tokenized[0] if isinstance(tokenized[0],list) else tokenized + generated,lps=self._generate_single_turn([ids],None,{}) + completions=[[parse_response(self._tokenizer,generated[0],prefix=ids)]] + masks,completions,generated,lps,tools,failures,_=self._tool_call_loop( + copy.deepcopy(prompts),[ids],generated,completions,lps,None,{}, + ) + mask,completion,logprobs=masks[0],generated[0],lps[0] + all_sampled=[t for call in self.calls for t in call['completion_ids']] + all_lp=[p for call in self.calls for p in call['logprobs']] + supervised=[t for t,m in zip(completion,mask,strict=True) if m] + supervised_lp=[p for p,m in zip(logprobs,mask,strict=True) if m] + checks={ + 'sampled_ids_preserved':supervised==all_sampled[:len(supervised)], + 'sampled_logprobs_preserved':supervised_lp==all_lp[:len(supervised_lp)], + 'tool_context_masked':all(p==0.0 for p,m in zip(logprobs,mask,strict=True) if not m), + 'loss_mask_binary':set(mask)<={0,1}, + 'real_logprobs':bool(supervised_lp) and any(p<0 for p in supervised_lp), + 'finite_logprobs':all(math.isfinite(p) for p in logprobs), + } + assert all(checks.values()), checks + return {'prompt_ids':ids,'completion_ids':completion,'logprobs':logprobs,'loss_mask':mask, + 'calls':self.calls,'messages':completions,'tito_checks':checks,'tito_pass':True, + 'turns':len(self.calls),'tool_calls':tools,'tool_failures':failures, + 'stop_reason':self.stop_reason,'budget_policy':'600s episode; empty native continuation at deadline; no fabricated token IDs'} + + +def episode(args,index,processor): + start=time.monotonic() + env=white_box_bash_env(args.server,toolsets='bash,seta',step_limit=17,timeout_s=600)() + loop=None + rec={'index':index,'reward':None,'tito_pass':False,'harness':'whitebox_seta','pass_k':1} + try: + prompt=env.reset(split='test',index=index) + loop=NativeLoop(env,processor,args.vllm_url,args.model,time.monotonic()+600) + capture=loop.run(prompt) + reward=env.get_reward() + if not math.isfinite(reward): + raise RuntimeError('episode ungraded') + assert reward in (0,1) + path=args.out/'captures'/f'{index:03d}-{uuid.uuid4().hex}.json' + path.write_text(json.dumps(capture)+'\n') + rec.update({k:capture[k] for k in ('tito_pass','turns','tool_calls','tool_failures','stop_reason')}) + rec.update(reward=reward,capture_file=str(path),supervised_tokens=sum(capture['loss_mask']), + forwarded_tokens=len(capture['prompt_ids'])+len(capture['completion_ids'])) + except Exception as exc: + rec['error_type']=type(exc).__name__ + # Local exception diagnostic; do not put prompts, provider bodies or signed URLs in reports. + import traceback + (args.out/f'error-{index:03d}-{uuid.uuid4().hex}.txt').write_text(traceback.format_exc()) + finally: + if env._session is not None: + try: env.get_reward() + except Exception: pass + env._mcp.close() + if loop is not None: loop.client.close() + rec['elapsed_s']=time.monotonic()-start + return rec + + +def main(): + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('--run',type=Path,required=True) + p.add_argument('--server',required=True) + p.add_argument('--vllm-url',required=True) + p.add_argument('--model',default=MODEL) + p.add_argument('--out',type=Path,required=True) + p.add_argument('--concurrency',type=int,default=4) + p.add_argument('--max-new-rollouts',type=int,default=0) + p.add_argument('--require-complete',action='store_true') + p.add_argument('--ramp',action='store_true',help='Measure 8 slots, then 32, then 100; stop if a ramp grades under 90%.') + args=p.parse_args() + args.out=args.out.resolve() + args.out.mkdir(parents=True,exist_ok=True) + (args.out/'captures').mkdir(exist_ok=True) + config={'model':args.model,'revision':REVISION,'dataset':str(args.run/'datasets/test'), + 'vllm_url':args.vllm_url,'server':args.server,'pass_k':1,'temperature':0.8,'top_p':1.0, + 'max_output_tokens_per_call':4096,'max_episode_completion_tokens':16384,'max_model_calls':17, + 'toolsets':['bash','seta'],'native_loop_source':inspect.getfile(GRPOTrainer), + 'source_manifest':str(args.run/'source_input_hashes.json')} + manifest=args.out/'eval_config.json' + if manifest.exists(): assert json.loads(manifest.read_text())==config, 'resume config mismatch' + else: manifest.write_text(json.dumps(config,indent=2)+'\n') + records=args.out/'attempts.jsonl' + selected={} + if records.exists(): + for line in records.read_text().splitlines(): + r=json.loads(line) + if r.get('reward') in (0,1) and r.get('tito_pass'): + selected.setdefault(r['index'],r) + indices=list(map(int,(args.run/'test_indices.txt').read_text().replace(',',' ').split())) + processor=AutoProcessor.from_pretrained(MODEL,revision=REVISION) + tokenizer=processor.tokenizer + if not (getattr(tokenizer,'response_template',None) or getattr(tokenizer,'response_schema',None)): + processor=add_response_schema(processor) + start=time.monotonic() + phases=[(8,8),(32,32),(100,0)] if args.ramp else [(args.concurrency,args.max_new_rollouts)] + ramp=[] + for concurrency,limit in phases: + pending=[i for i in indices if i not in selected] + if limit: pending=pending[:limit] + before=len(selected);phase_start=time.monotonic() + with records.open('a') as out, concurrent.futures.ThreadPoolExecutor(max_workers=concurrency) as pool: + futures=[pool.submit(episode,args,i,processor) for i in pending] + for f in concurrent.futures.as_completed(futures): + r=f.result() + out.write(json.dumps(r)+'\n');out.flush() + if r['reward'] in (0,1) and r['tito_pass']: selected.setdefault(r['index'],r) + print(json.dumps({'graded':len(selected),'latest':r}),flush=True) + ramp.append({'concurrency':concurrency,'attempted':len(pending),'graded':len(selected)-before, + 'elapsed_s':time.monotonic()-phase_start}) + (args.out/'ramp.json').write_text(json.dumps(ramp,indent=2)+'\n') + if args.ramp and len(selected)-before < 0.9*len(pending): break + manifest=json.loads((args.run/'test_manifest.json').read_text()) + difficulty={} + for level in ('easy','medium','hard'): + subset=[r for i,r in selected.items() if manifest['tasks'][i]['difficulty']==level] + difficulty[level]={'graded':len(subset),'correct':sum(r['reward'] for r in subset)} + report={'metric':'pass@1','complete':len(selected)==250,'graded':len(selected),'expected':250, + 'correct':sum(r['reward'] for r in selected.values()), + 'pass_at_1':sum(r['reward'] for r in selected.values())/len(selected) if selected else None, + 'graded_indices':sorted(selected),'difficulty':difficulty,'concurrency':args.concurrency, + 'phase_elapsed_s':time.monotonic()-start,'ramp':ramp, + 'tito_pass':bool(selected) and all(r['tito_pass'] for r in selected.values())} + (args.out/'scores.json').write_text(json.dumps(report,indent=2)+'\n') + print(json.dumps(report),flush=True) + return 2 if args.require_complete and not report['complete'] else 0 + + +if __name__=='__main__': + raise SystemExit(main()) diff --git a/04-data-agent/tools/generation_routing.py b/04-data-agent/tools/generation_routing.py new file mode 100644 index 0000000..48ff9a1 --- /dev/null +++ b/04-data-agent/tools/generation_routing.py @@ -0,0 +1,12 @@ +"""Protect native server generation's duplicate-prompt optimization after tools.""" + + +def generation_group_size(prompts, requested): + if requested<1: + raise ValueError('generation group size must be positive') + if len(prompts)%requested: + return 1 + for start in range(0,len(prompts),requested): + if any(prompt!=prompts[start] for prompt in prompts[start:start+requested]): + return 1 + return requested diff --git a/04-data-agent/tools/monitor_multi4.py b/04-data-agent/tools/monitor_multi4.py new file mode 100644 index 0000000..524d18e --- /dev/null +++ b/04-data-agent/tools/monitor_multi4.py @@ -0,0 +1,228 @@ +"""CPU-only Slurm watchdog. Writes local status/alerts; never changes or stops training.""" +import argparse +from datetime import datetime, timezone +import json +import math +import os +from pathlib import Path +import subprocess +import sys +import time + +TERMINAL = {'COMPLETED', 'FAILED', 'CANCELLED', 'TIMEOUT', 'OUT_OF_MEMORY', + 'NODE_FAIL', 'PREEMPTED', 'BOOT_FAIL', 'DEADLINE', 'REVOKED'} + + +def read_json(path, default=None): + return json.loads(path.read_text()) if path.exists() else default + + +def read_metrics(path): + if not path.exists(): + return [] + lines = path.read_text().splitlines(keepends=True) + # A trainer can be in the middle of appending its latest line. + return [json.loads(line) for line in lines if line.endswith('\n') and line.strip()] + + +def slurm_states(jobs): + if not jobs: + return {} + result = subprocess.check_output(['sacct', '-X', '-n', '-P', '-j', ','.join(map(str, jobs)), + '--format=JobID,State'], text=True, timeout=30) + found = {line.split('|')[0]: line.split('|')[1].split()[0].rstrip('+') + for line in result.splitlines() if '|' in line} + return {str(job): found.get(str(job), 'UNKNOWN') for job in jobs} + + +def resolved_empty_captures(root, logs, harness): + """Resolve individually investigated incidents without waiving token validation. + + Historical failed attempts remain failed in the audit and dashboard. Only named + incidents with verified recovery can stop paging; every new failure still alerts. + An acknowledgement can never resolve a capture containing model tokens. + """ + incidents = read_json(root / 'monitor/resolved_incidents.json', {}).get('episodes', {}) + failed, resolved = set(), set() + for path in (logs / 'audit/tito_checks').glob('*.json'): + check = read_json(path) + if (check.get('harness') != harness or not check.get('completed_result') + or check.get('tito_pass')): + continue + failed.add(path.stem) + incident = incidents.get(path.stem, {}) + if not incident.get('verified_at') or not incident.get('recovery_evidence'): + continue + result = (read_json(logs / 'audit/rollouts' / path.name, {}) or {}).get('result') or {} + fatal = [f for f in result.get('findings', []) if '[FATAL]' in f] + if (result.get('n_turns') == 0 and result.get('n_trainable_tokens') == 0 + and result.get('turns') == [] and fatal + and all(f.startswith('[FATAL] no_turns:') for f in fatal)): + resolved.add(path.stem) + return failed, resolved + + +def inspect_run(root, job, states, *, now=None): + now = time.time() if now is None else now + logs = root / f'job-{job}' + config = read_json(root / 'run_config.json', {}) + metrics_path = logs / 'audit/metrics.jsonl' + metrics = read_metrics(metrics_path) + updates = [m for m in metrics if 'grad_norm' in m] + latest = updates[-1] if updates else {} + resume_step = config.get('resume_state', {}).get('step', 0) + step = latest.get('step', resume_step) + alerts = [] + state = states.get(str(job), 'UNKNOWN') + if state in TERMINAL - {'COMPLETED'}: + alerts.append(f'training_{state.lower()}') + if state == 'UNKNOWN': + alerts.append('training_state_unknown') + for m in updates[-5:]: + if any(isinstance(m.get(k), (float, int)) and not math.isfinite(m[k]) + for k in ('loss', 'grad_norm', 'ratio')): + alerts.append('nonfinite_training_metric') + if len(updates) >= 5 and all(m.get('grad_norm', 0) == 0 for m in updates[-5:]): + alerts.append('five_updates_without_gradient') + for metric, label in [('sample/dropped_stale_total', 'stale_rows_dropped'), + ('batch/dropped_oversize_total', 'oversize_rows_dropped')]: + if any(m.get(metric, 0) > 0 for m in updates[-5:]): + alerts.append(label) + age = now - metrics_path.stat().st_mtime if metrics_path.exists() else None + if state == 'RUNNING' and age is not None and age > 1800: + alerts.append('no_optimizer_update_for_30_minutes') + if state == 'RUNNING' and age is None and logs.exists() and now - logs.stat().st_mtime > 1800: + alerts.append('no_optimizer_metrics_after_startup') + tito_path = logs / 'audit/tito_summary.json' + tito = read_json(tito_path, {}) + resolved_incidents = {} + for harness, summary in tito.items(): + if summary['tito_pass'] != summary.get('completed_results', summary['rollouts']): + failed, resolved = resolved_empty_captures(root, logs, harness) + expected_failures = summary.get('completed_results', summary['rollouts']) - summary['tito_pass'] + if failed != resolved or len(failed) != expected_failures: + alerts.append(f'tito_failure:{harness}') + if resolved: + resolved_incidents[harness] = sorted(resolved) + if summary.get('incomplete_results', 0) and state == 'RUNNING': + alerts.append(f'incomplete_rollout:{harness}') + if summary['retained_tokens'] != summary['eligible_tokens'] or summary['rows_over_token_budget']: + alerts.append(f'token_retention_failure:{harness}') + evals = read_json(root / 'checkpoint-evals/state.json', {}) + evaluation = [] + for key, record in evals.items(): + eval_state = states.get(str(record.get('job_id')), record.get('slurm_state', 'UNKNOWN')) + result = record.get('scores', {}) + if eval_state in TERMINAL - {'COMPLETED'}: + alerts.append(f'eval_{eval_state.lower()}:{Path(key).name}') + if eval_state == 'COMPLETED' and result and not result.get('comparison_ready', False): + alerts.append(f'eval_not_comparable:{Path(key).name}') + evaluation.append({'checkpoint': key, 'job_id': record.get('job_id'), 'state': eval_state, + 'scores': result}) + submission = read_json(root / 'submission.json', {}) + watcher = submission.get('eval_watcher') + if watcher and (states.get(str(watcher)) in TERMINAL - {'COMPLETED'} + or states.get(str(watcher)) == 'COMPLETED' and state not in TERMINAL): + alerts.append('eval_watcher_stopped_early') + saved = sorted(int(p.name.split('-')[1]) for p in (logs / 'run').glob('checkpoint-*') + if (p / 'checkpoint.saved.json').exists() or (p / 'checkpoint.ready.json').exists()) + interval = config.get('evaluation', {}).get('interval_optimizer_steps', 100) + submitted = {int(Path(k).name.split('-')[1]) for k in evals} + pending = [s for s in saved if s % interval == 0 and s not in submitted] + return {'checked_at': datetime.fromtimestamp(now, timezone.utc).isoformat(), + 'job_id': job, 'training_state': state, 'optimizer_step': step, + 'resumed_from_step': resume_step, + 'new_optimizer_updates': len(updates), + 'awaiting_first_optimizer_update': not updates and state == 'RUNNING', + 'metrics_age_seconds': age, 'last_metrics': latest, + 'counters': {'stale_rows_dropped': sum(m.get('sample/dropped_stale_total', 0) for m in updates), + 'oversize_rows_dropped': sum(m.get('batch/dropped_oversize_total', 0) for m in updates), + 'nonzero_gradient_updates': sum(m.get('grad_norm', 0) > 0 for m in updates), + 'resolved_empty_capture_incidents': sum(map(len, resolved_incidents.values()))}, + 'resolved_empty_capture_incidents': resolved_incidents, + 'coverage': read_json(logs / 'audit/coverage.json', {}), 'tito': tito, + 'tito_audit_age_seconds': now - tito_path.stat().st_mtime if tito_path.exists() else None, + 'saved_checkpoints': saved, 'queued_eval_steps': pending, 'evaluations': evaluation, + 'job_states': states, 'alerts': sorted(set(alerts)), + 'monitoring_scope': 'local artifacts only; no chat wakeups or external notifications'} + + +def next_interval(previous, current, stable_checks, config): + threshold = config.get('stable_after_optimizer_step', 10) + required = config.get('required_progressing_checks', 2) + if (current['alerts'] or not previous or current['optimizer_step'] < threshold + or current['optimizer_step'] < previous['optimizer_step']): + stable_checks = 0 + elif current['optimizer_step'] > previous['optimizer_step']: + stable_checks += 1 + # An unchanged step during an ordinary rollout wait is not a failure. Keep the + # evidence of healthy updates; inspect_run's stall/error alerts reset it above. + interval = config['stable_interval_seconds'] if stable_checks >= required else config['startup_interval_seconds'] + return stable_checks, interval + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument('--run', type=Path, required=True) + p.add_argument('--train-job', required=True) + p.add_argument('--watch', action='store_true') + p.add_argument('--audit', action='store_true', help='Replay newly captured rollouts on this CPU job') + args = p.parse_args() + config = read_json(args.run / 'run_config.json')['monitoring'] + destination = args.run / 'monitor' + destination.mkdir(exist_ok=True) + previous, stable_checks = None, 0 + while True: + cycle_started = time.monotonic() + try: + submission = read_json(args.run / 'submission.json', {}) + evals = read_json(args.run / 'checkpoint-evals/state.json', {}) + jobs = {args.train_job} | {str(j) for j in submission.values() if str(j).isdigit()} + jobs.update(str(r[k]) for r in evals.values() for k in ('job_id', 'cleanup_job_id') if r.get(k)) + audit_error = None + if args.audit: + audit_dir = args.run / f'job-{args.train_job}/audit' + if audit_dir.exists(): + with (destination / 'audit.log').open('a') as stream: + try: + subprocess.run(['nice', '-n', '10', sys.executable, + str(Path(__file__).with_name('audit_multiharness_training.py')), str(audit_dir)], + check=True, timeout=600, stdout=stream, stderr=subprocess.STDOUT) + except (subprocess.SubprocessError, OSError) as exc: + audit_error = type(exc).__name__ + status = inspect_run(args.run, args.train_job, slurm_states(jobs)) + if audit_error: + status['alerts'].append('capture_audit_failed:' + audit_error) + stable_checks, interval = next_interval(previous, status, stable_checks, config) + status['next_check_seconds'] = interval + temporary = destination / 'status.json.tmp' + temporary.write_text(json.dumps(status, indent=2) + '\n') + temporary.replace(destination / 'status.json') + with (destination / 'history.jsonl').open('a') as stream: + stream.write(json.dumps(status) + '\n') + old_alerts = set(previous['alerts']) if previous else set() + changes = {'new': sorted(set(status['alerts']) - old_alerts), + 'resolved': sorted(old_alerts - set(status['alerts']))} + if changes['new'] or changes['resolved']: + with (destination / 'alerts.jsonl').open('a') as stream: + stream.write(json.dumps({'checked_at': status['checked_at'], **changes}) + '\n') + print(json.dumps({'step': status['optimizer_step'], 'state': status['training_state'], + 'alerts': status['alerts'], 'next_check_seconds': interval}), flush=True) + previous = status + # Continue through final audit/cleanup and outstanding evals; exclude our own monitor job. + other_jobs = {j: s for j, s in status['job_states'].items() if j != str(submission.get('monitor'))} + if status['training_state'] in TERMINAL and all(s in TERMINAL for s in other_jobs.values()): + break + except Exception as exc: + with (destination / 'alerts.jsonl').open('a') as stream: + stream.write(json.dumps({'checked_at': time.time(), 'monitor_error': str(exc)}) + '\n') + if not args.watch: + raise + interval = config['startup_interval_seconds'] + if not args.watch: + break + time.sleep(max(1, interval - (time.monotonic() - cycle_started))) + + +if __name__ == '__main__': + main() diff --git a/04-data-agent/tools/score_comparison.py b/04-data-agent/tools/score_comparison.py new file mode 100644 index 0000000..e16a876 --- /dev/null +++ b/04-data-agent/tools/score_comparison.py @@ -0,0 +1,65 @@ +"""Canonical pass@1 summaries: fixed identities, first graded attempt, pinned harnesses.""" +import argparse +import json +from pathlib import Path + +ROOT=Path(__file__).resolve().parents[1]/'logs/20260915' +HARNESS_VERSIONS={'opencode':'1.18.31','claude-code':'2.1.270','codex':'0.154.0','mini-swe-agent':'2.4.6'} + + +def summarize(arm,output): + manifest=json.loads((ROOT/'test_manifest.json').read_text()) + selected={};ungraded=0 + files=sorted((output/'traces').glob('*.jsonl')) if arm=='blackbox' else [output/'attempts.jsonl'] + for p in files: + for line in p.read_text().splitlines(): + r=json.loads(line) + valid=r.get('reward') in (0,1) and (r.get('n_turns',0)>0 if arm=='blackbox' else r.get('tito_pass',False)) + if not valid: + ungraded+=1;continue + assert r.get('rep',0)==0 and 0<=r['index']<250 + selected.setdefault((r['harness'],r['index']),r) + harnesses=list(HARNESS_VERSIONS) if arm=='blackbox' else ['whitebox_seta'] + expected={(h,i) for h in harnesses for i in range(250)} + complete=set(selected)==expected + if arm=='blackbox': + audit_path=output/'final_tito.json' + audit=json.loads(audit_path.read_text()) if audit_path.exists() else {} + tito=complete and audit.get('tito_pass',False) and audit.get('counts')=={h:250 for h in harnesses} + else: + tito=complete and all(r['tito_pass'] for r in selected.values()) + scores={};versions={h:{} for h in harnesses} + for h in harnesses: + rows=[r for (name,_),r in selected.items() if name==h] + difficulty={} + for level in ('easy','medium','hard'): + subset=[r for r in rows if manifest['tasks'][r['index']]['difficulty']==level] + correct=sum(r['reward'] for r in subset) + difficulty[level]={'graded':len(subset),'correct':correct,'pass_at_1':correct/len(subset) if subset else None} + correct=sum(r['reward'] for r in rows) + scores[h]={'graded':len(rows),'correct':correct,'pass_at_1':correct/len(rows) if rows else None,'difficulty':difficulty} + if arm=='blackbox': + for row in rows: + p=output/'trials'/row.get('trial_name','missing')/'result.json' + data=json.loads(p.read_text()) if p.is_file() else {} + version=(data.get('agent_info') or {}).get('version') or 'unverified' + versions[h][version]=versions[h].get(version,0)+1 + pins=all(versions[h]=={HARNESS_VERSIONS[h]:250} for h in harnesses) if arm=='blackbox' else True + result={'metric':'pass@1','arm':arm,'complete':complete,'graded_cells':len(selected), + 'expected_cells':len(expected),'harnesses':scores,'tito_pass':tito, + 'ungraded_attempts':ungraded,'harness_versions':versions,'harness_versions_match_baseline':pins, + 'comparison_ready':complete and tito and pins, + 'average_pass_at_1':sum(r['reward'] for r in selected.values())/len(selected) if selected else None, + 'selection':'first graded attempt per fixed task/harness; infrastructure failures excluded and retried'} + (output/'canonical_scores.json').write_text(json.dumps(result,indent=2)+'\n') + return result + + +if __name__=='__main__': + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('--arm',choices=['blackbox','whitebox'],required=True) + p.add_argument('--output',type=Path,required=True) + p.add_argument('--require-complete',action='store_true') + a=p.parse_args();result=summarize(a.arm,a.output) + print(json.dumps({k:v for k,v in result.items() if k not in ('harnesses','harness_versions')})) + raise SystemExit(2 if a.require_complete and not result['comparison_ready'] else 0) diff --git a/04-data-agent/tools/smoke_multiharness_tito.py b/04-data-agent/tools/smoke_multiharness_tito.py new file mode 100644 index 0000000..021d4d2 --- /dev/null +++ b/04-data-agent/tools/smoke_multiharness_tito.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python +"""Real Harbor harness smoke with exact token/context retention through TRL. + +Reuses OpenEnv capture, Harbor rollout, and TRL's actual trace reader and builder. +Artifacts contain task data, so keep the output under the experiment's ignored logs/. +Prefix drift is a packing metric, never the TITO admission test. +""" +from __future__ import annotations + +import argparse +import asyncio +import collections +import hashlib +import importlib.metadata +import json +from pathlib import Path +import secrets +import shutil +import socket +import struct +import subprocess +import time + +from harbor_env.harness import to_trace_entries +from openenv.core.harness.capture.export import export_session +from openenv.core.harness.capture.forwarding import GradioForwarder +from openenv.core.harness.capture.runner import CaptureServer +from openenv.core.harness.capture.upstream import training_sampling +from openenv.core.harness.capture.validate import validate_training_turn +from openenv.harbor.rollout import run_rollout +from openenv.harbor.seams import SEAMS +from trl.experimental.async_grpo.async_rollout_worker import _chain_to_sequences +from trl.experimental.async_grpo.openenv_harness import _turns_from_trace + +ROOT = Path(__file__).resolve().parents[3] + + +def write(path, value): + path.write_text(json.dumps(value, indent=2, allow_nan=False, default=str) + "\n") + + +def supervised_positions(ids, masks, logprobs): + """Multiset of exact causal context, sampled id, and behavior logprob. + + Incremental hashing bounds memory even when a harness repeats a long prompt. + Include multiplicity: equal generated tokens are separate training positions. + """ + out = collections.Counter() + context = hashlib.sha256() + for token, mask, lp in zip(ids, masks, logprobs, strict=True): + if mask: + out[(context.hexdigest(), token, float(lp).hex())] += 1 + context.update(struct.pack(">q", token)) + return out + + +def audit(result, token_budget=40960): + entries = to_trace_entries(result) + expected = collections.Counter() + policy = training_sampling({"temperature": 0.8}) + for entry in entries: + p, c, lp, mask = (entry[k] for k in ( + "prompt_token_ids", "completion_token_ids", "per_token_logps", "loss_mask")) + validate_training_turn(p, c, lp, mask) + assert entry["metadata"]["sampling_params"] == policy, "sampling policy mismatch" + expected.update(supervised_positions(p + c, mask, [0.0] * len(p) + lp)) + turns = _turns_from_trace(entries) + rows, tally = _chain_to_sequences(turns, result.session_id, fork_threshold=0) + retained = collections.Counter() + for row in rows: + retained.update(supervised_positions(row.input_ids, row.completion_mask, row.old_log_probs)) + fatal = [x for x in result.findings if "[FATAL]" in x] + checks = { + "train_tier": result.rollout_type == "train" and result.capture_level == "tokens", + "nonempty_supervision": bool(expected), + "exact_context_ids_logprobs_masks_retained": retained == expected, + "no_capture_fatal": not fatal, + "token_count_matches_export": sum(expected.values()) == result.n_trainable_tokens, + "nonzero_logprobs_present": any(lp < 0 for e in entries for lp in e["per_token_logps"]), + } + action_entries = [e for e in entries if e["response"]["choices"][0]["message"].get("tool_calls")] + return { + "checks": checks, "tito_pass": all(checks.values()), "fatal": fatal, + "entries": len(entries), "rows": len(rows), "eligible_tokens": sum(expected.values()), + "retained_tokens": sum(retained.values()), + "packed_tokens": sum(len(row.input_ids) for row in rows), + "largest_row_tokens": max((len(row.input_ids) for row in rows), default=0), + "rows_over_40960": sum(len(row.input_ids) > 40960 for row in rows), + "token_budget": token_budget, + "rows_over_token_budget": sum(len(row.input_ids) > token_budget for row in rows), + "action_entries": len(action_entries), + "action_tokens": sum(sum(e["loss_mask"]) for e in action_entries), "drift": tally, + }, entries + + +async def main(args): + args.output.mkdir(parents=True, exist_ok=True) + tasks = [] + manifests = [] + source_tasks = sorted(p for p in args.tasks.iterdir() if (p / "task.toml").exists()) + for index in args.indices: + source = source_tasks[index] + target = args.output / "tasks" / source.name + if not target.exists(): + shutil.copytree(source, target) + # Isolated resource override, recorded below. Never modify the cached dataset. + config = target / "task.toml" + config.write_text(config.read_text().replace("memory_mb = 1024", "memory_mb = 4096")) + files = {str(p.relative_to(source)): hashlib.sha256(p.read_bytes()).hexdigest() + for p in sorted(source.rglob("*")) if p.is_file()} + manifests.append({"index": index, "source": str(source), "files_sha256": files, + "resource_override": {"memory_mb": 4096}, + "effective_task_sha256": hashlib.sha256((target / "task.toml").read_bytes()).hexdigest()}) + tasks.append((index, target)) + heads = {repo: subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT / repo, text=True).strip() + for repo in ("OpenEnv", "trl", "HuggingEnvs")} + write(args.output / "manifest.json", { + "started_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), + "model": args.model, "inference": args.inference, "git_heads": heads, + "working_tree_diff_sha256": {repo: hashlib.sha256(subprocess.check_output( + ["git", "diff", "HEAD"], cwd=ROOT / repo)).hexdigest() for repo in heads}, + "runner_sha256": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(), + "local_changes": True, "harbor_version": importlib.metadata.version("harbor"), + "harnesses": args.harnesses, "tasks": manifests, "concurrency": args.concurrency, + "sampling": training_sampling({"temperature": 0.8}), "model_call_limit": 17, + "max_output_tokens": 4096, "agent_timeout_sec": 600, + "scope": "Frozen-policy smoke; not optimizer, weight sync, or scale certification.", + }) + with socket.socket() as sock: + sock.bind(("", 0)) + port = sock.getsockname()[1] + capture = CaptureServer(llm_url=args.inference, model=args.model, port=port, + max_output_tokens=4096, admin_key=secrets.token_hex(32)) + capture.start() + forwarder = GradioForwarder() + url = forwarder.start(port) + print(json.dumps({"event": "proxy_ready", "port": port, "url": url}), flush=True) + # Record the upstream response before ingest and dialect replay. No credentials or headers. + completion = capture.inference.completion + proxy_loop = None + raw_dir = args.output / "engine_responses" + raw_dir.mkdir(exist_ok=True) + + async def observed(request): + nonlocal proxy_loop + proxy_loop = asyncio.get_running_loop() + response = await completion(request) + key = hashlib.sha256(json.dumps(response, sort_keys=True).encode()).hexdigest() + write(raw_dir / f"{key}.json", {"request": request, "response": response}) + return response + + capture.inference.completion = observed + # Save graph evidence before run_rollout's finally releases each session, including cancellation. + delete = capture.registry.delete + + def save_and_delete(sid): + session = capture.registry.get(sid) + if session is not None: + doc = export_session(session, include_messages=True, include_discarded=True) + write(args.output / f"capture-{sid}.json", doc) + return delete(sid) + + capture.registry.delete = save_and_delete + results = [] + semaphore = asyncio.Semaphore(args.concurrency) + + async def run(harness, index, task): + async with semaphore: + case = f"{harness}-{index}" + print(json.dumps({"event": "start", "case": case}), flush=True) + record = {"harness": harness, "task_index": index, "task": task.name} + started = time.monotonic() + try: + result = await asyncio.wait_for(run_rollout( + task_dir=task, harness=harness, sandbox="e2b", registry=capture.registry, + intercept_url=url, model=args.model, trials_dir=args.output / "trials", + dataset="AdithyaSK/data_agent_rl_environment_train (local pinned tasks)", + agent_timeout_sec=600, agent_step_limit=17, session_prefix="tito-smoke", + inference=capture.inference, sampling={"temperature": 0.8}), timeout=1200) + write(args.output / f"result-{case}.json", result.model_dump()) + record.update(ok=result.ok, reward=result.reward, error=result.error, + exception_type=result.exception_type, turns=result.n_turns, + roots=result.n_roots, findings=result.findings, + session_id=result.session_id, trial_name=result.trial_name) + audited, entries = audit(result) + record.update(audited) + write(args.output / f"trace-{case}.json", entries) + except Exception as exc: + record.update(tito_pass=False, error=f"{type(exc).__name__}: {exc}") + record["wall_s"] = round(time.monotonic() - started, 2) + results.append(record) + write(args.output / "matrix.json", results) + print(json.dumps({"event": "done", **record}), flush=True) + + try: + cases = [(h, i, p) for h in args.harnesses for i, p in tasks] + # Warm the shared template once before concurrent trials can race its first build. + await run(*cases[0]) + await asyncio.gather(*(run(*case) for case in cases[1:])) + finally: + write(args.output / "cleanup.json", {"remaining_sessions": capture.registry.list_ids(), + "completed_cases": len(results)}) + # Harbor shields sandbox deletion. Let its already-scheduled cleanup finish before closing + # this loop; only the capture client belongs to the server's separate loop. + await asyncio.sleep(2) + if proxy_loop is not None: + future = asyncio.run_coroutine_threadsafe(capture.inference.aclose(), proxy_loop) + await asyncio.wrap_future(future) + forwarder.stop() + capture.stop() + + +if __name__ == "__main__": + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--inference", required=True) + p.add_argument("--model", default="Qwen/Qwen3.5-4B") + p.add_argument("--tasks", type=Path, default=Path("/admin/home/adithyaskolavi/.cache/openenv/harbor-datasets/AdithyaSK__data_agent_rl_environment_train/tasks")) + p.add_argument("--indices", nargs="+", type=int, default=[8, 9]) + p.add_argument("--harnesses", nargs="+", default=[k for k, v in SEAMS.items() if v.status == "validated"]) + p.add_argument("--concurrency", type=int, default=4) + p.add_argument("--output", type=Path, required=True) + asyncio.run(main(p.parse_args())) diff --git a/04-data-agent/tools/trackio_multi4.py b/04-data-agent/tools/trackio_multi4.py new file mode 100644 index 0000000..8401f11 --- /dev/null +++ b/04-data-agent/tools/trackio_multi4.py @@ -0,0 +1,421 @@ +"""Replay durable scalar metrics into Trackio; sync on a CPU job, never on the trainer. + +The small fragment adapter is pinned to Trackio 0.33.0. Its existing log_id deduplication +makes replay/restarts idempotent. SQLite lives on node-local disk; a consistent backup is +atomically published to FSx before any network call. Raw captures/completions stay local. +""" +import argparse +from datetime import datetime, timezone +import fcntl +import hashlib +from importlib.metadata import version +import json +import math +import os +from pathlib import Path +import re +import sqlite3 +import subprocess +import sys +import tempfile +import time + +from monitor_multi4 import TERMINAL, read_json, read_metrics, slurm_states + +TRACKIO_VERSION = '0.33.0' +REMOTE_ENV = ('TRACKIO_SPACE_ID', 'TRACKIO_SERVER_URL', 'TRACKIO_BUCKET_ID', + 'TRACKIO_DATASET_ID', 'TRACKIO_WEBHOOK_URL') + + +def digest(value): + return hashlib.sha256(json.dumps(value, sort_keys=True, allow_nan=False).encode()).hexdigest() + + +def scalars(data, prefix=''): + result = {} + for key, value in data.items(): + name = f'{prefix}{key}' + if isinstance(value, dict): + result.update(scalars(value, name + '/')) + elif isinstance(value, (int, float)): + if math.isfinite(value): + result[name] = value + else: + result[name + '/nonfinite'] = 1 + return result + + +def event(project, run, step, metrics, config, *, identity=None): + """Stable IDs survive source replay, worker restart, and out-of-order eval completion.""" + content = [project, run, step, metrics, identity] + return {'v': 1, 'kind': 'metric', 'project': project, 'run': run, + 'run_id': digest([project, run])[:32], 'step': int(step), 'metrics': metrics, + 'config': config, 'log_id': digest(content), + 'timestamp': datetime.now(timezone.utc).isoformat()} + + +def score_metrics(scores, protocol): + metrics = {'eval/pass_at_1': scores['average_pass_at_1'], + 'eval/delta_from_baseline': scores['average_pass_at_1'] - protocol['average_pass_at_1'], + 'eval/graded_cells': scores['graded_cells']} + totals = {} + for harness, values in scores['harnesses'].items(): + metrics[f'eval/{harness}/pass_at_1'] = values['pass_at_1'] + metrics[f'eval/{harness}/delta_from_baseline'] = values['pass_at_1'] - protocol['scores'][harness]['pass_at_1'] + for difficulty, counts in values.get('difficulty', {}).items(): + n = counts['graded'] + metrics[f'eval/{harness}/{difficulty}/pass_at_1'] = counts['correct'] / n + total = totals.setdefault(difficulty, [0, 0]) + total[0] += counts['correct']; total[1] += n + for difficulty, (correct, n) in totals.items(): + metrics[f'eval/{difficulty}/pass_at_1'] = correct / n + return metrics + + +def evaluation_roots(root, config=None): + """Include explicitly configured earlier allocations whose evals can finish late.""" + config = config if config is not None else read_json(root / 'run_config.json', {}) + roots = [root.resolve()] + for value in config.get('logging', {}).get('evaluation_sources', []): + source = Path(value) + if not source.is_absolute() or not (source / 'run_config.json').is_file(): + raise ValueError('Evaluation source must be an existing absolute run directory') + if source.resolve() not in roots: + roots.append(source.resolve()) + return roots + + +def training_lineage(root, job): + """Follow actual resume checkpoints, excluding abandoned post-checkpoint updates.""" + segments, seen = [], set() + current, current_job, upper = root.resolve(), str(job), None + while True: + if current in seen: + raise ValueError('Cycle in training checkpoint lineage') + seen.add(current) + config = read_json(current / 'run_config.json') + resume = config.get('training', {}).get('resume_from_checkpoint') + lower = 0 + parent = None + if resume: + checkpoint = Path(resume) + checkpoint_match = re.fullmatch(r'checkpoint-(\d+)', checkpoint.name) + job_match = re.fullmatch(r'job-(\d+)', checkpoint.parent.parent.name) + if not checkpoint.is_absolute() or checkpoint.parent.name != 'run' or not checkpoint_match or not job_match: + raise ValueError('Invalid training checkpoint lineage path') + lower = int(checkpoint_match[1]) + parent = (checkpoint.parents[2], job_match[1], lower) + selected = {} + for row in read_metrics(current / f'job-{current_job}/audit/metrics.jsonl'): + # Trainer's final aggregate summary can reuse its last optimizer step. + if 'grad_norm' not in row: + continue + step = row['step'] + if step <= lower or (upper is not None and step > upper): + continue + if step in selected and selected[step] != row: + raise ValueError(f'Conflicting optimizer records at step {step}') + selected[step] = row + if upper is None: + upper = max(selected, default=lower) + if sorted(selected) != list(range(lower + 1, upper + 1)): + raise ValueError(f'Incomplete optimizer history in job {current_job}: expected {lower + 1}..{upper}') + segments.append({'job': current_job, 'root': str(current), 'start_step': lower + 1, + 'end_step': upper, 'resume_from_checkpoint': resume, + 'training': config.get('training', {}), 'rows': list(selected.values())}) + if parent is None: + break + current, current_job, upper = parent + current = current.resolve() + return list(reversed(segments)) + + +def collect(root, job): + config = read_json(root / 'run_config.json') + project = config['logging']['project'] + protocol = read_json(Path(config['evaluation']['protocol_file'])) + # Explicit allowlist: never serialize process environment, credentials, task text or captures. + metadata = {k: config[k] for k in ('model', 'model_revision', 'harnesses', 'harness_versions', + 'training', 'sampling')} + metadata.update(train_job=job, run_directory=str(root), + protocol_sha256=digest(protocol), trackio_version=TRACKIO_VERSION, + schedule_sha256=config['dataset']['schedule_sha256'], + metric_axis='optimizer step', tito_scope='capture and sequence builder; not optimizer consumption') + records, withheld = [], [] + logs = root / f'job-{job}' if job else None + if logs: + for row in read_metrics(logs / 'audit/metrics.jsonl'): + metrics = scalars({k: v for k, v in row.items() if k != 'step'}, 'train/') + metrics['train/global_step'] = row['step'] + records.append(event(project, f'training-{job}', row['step'], metrics, metadata)) + for row in read_metrics(root / 'monitor/history.jsonl'): + metrics = scalars({k: row.get(k, {}) for k in ('counters', 'coverage', 'tito')}, 'audit/') + metrics.update({'audit/alert_count': len(row.get('alerts', [])), + 'audit/eval_backlog': len(row.get('queued_eval_steps', []))}) + for harness, values in row.get('tito', {}).items(): + completed = values.get('completed_results') + if completed: + metrics[f'audit/tito/{harness}/pass_fraction_completed'] = values['tito_pass'] / completed + eligible = values.get('eligible_tokens', 0) + if eligible: + metrics[f'audit/tito/{harness}/retention_fraction'] = values['retained_tokens'] / eligible + graded = values.get('graded', 0) + if graded: + metrics[f'audit/{harness}/reward_mean'] = values['reward_sum'] / graded + records.append(event(project, f'audit-{job}', row['optimizer_step'], metrics, metadata, + identity=row.get('checked_at'))) + if job and config['logging'].get('stitch_training_history'): + segments = training_lineage(root, job) + lineage_metadata = {k: v for k, v in metadata.items() if k != 'training'} + lineage_metadata.update( + description='Continuous optimizer history along the checkpoint resume chain; configuration changed between some allocations.', + training_segments=[{k: v for k, v in segment.items() if k != 'rows'} for segment in segments]) + for segment in segments: + for row in segment['rows']: + metrics = scalars({k: v for k, v in row.items() if k != 'step'}, 'train/') + metrics.update({'train/global_step': row['step'], 'train/source_job_id': int(segment['job'])}) + records.append(event(project, 'training-full-history', row['step'], metrics, lineage_metadata)) + baseline_path = Path(protocol['baseline_run']) / f"job-{protocol['baseline_job']}" / 'canonical_results.json' + baseline = read_json(baseline_path) + if not baseline['coverage_complete']: + raise ValueError('Baseline coverage is incomplete') + baseline_scores = {'average_pass_at_1': protocol['average_pass_at_1'], 'graded_cells': 1000, + 'harnesses': {h: {'pass_at_1': protocol['scores'][h]['pass_at_1'], + 'difficulty': baseline['harnesses'][h]['difficulty']} for h in protocol['harnesses']}} + records.append(event(project, 'evaluation-curve', 0, score_metrics(baseline_scores, protocol), metadata)) + evaluations = {} + for source in evaluation_roots(root, config): + for directory in sorted((source / 'checkpoint-evals').glob('step-*')): + if not directory.is_dir(): + continue + scores = read_json(directory / 'scores.json', {}) + step = int(directory.name.split('-')[1]) + eval_config = read_json(directory / 'eval_plan.json', {}) + comparable = (scores.get('complete') and scores.get('comparison_ready') + and scores.get('graded_cells') == 1000 and eval_config.get('protocol') == protocol + and set(scores.get('harnesses', {})) == set(protocol['harnesses']) + and all(v.get('graded') == 250 for v in scores.get('harnesses', {}).values())) + if not comparable: + withheld.append({'step': step, 'source': str(directory), + 'reason': 'incomplete, audit/version failure, or protocol mismatch'}) + continue + metrics = score_metrics(scores, protocol) + if step in evaluations and evaluations[step] != metrics: + raise ValueError(f'Conflicting evaluation scores at optimizer step {step}') + evaluations[step] = metrics + for step, metrics in sorted(evaluations.items()): + records.append(event(project, 'evaluation-curve', step, metrics, metadata)) + return records, withheld + + +def import_events(records): + if version('trackio') != TRACKIO_VERSION: + raise RuntimeError(f'Trackio adapter requires {TRACKIO_VERSION}; installed {version("trackio")}') + from trackio.fragments import import_records + import_records(records) + + +def backup_project(project, destination): + from trackio.sqlite_storage import SQLiteStorage + destination.mkdir(parents=True, exist_ok=True) + target = destination / SQLiteStorage.get_project_db_filename(project) + temporary = target.with_suffix('.db.tmp') + with sqlite3.connect(SQLiteStorage.get_project_db_path(project)) as source: + with sqlite3.connect(temporary) as output: + source.backup(output) + output.execute('PRAGMA journal_mode=DELETE') + temporary.replace(target) + + +def write_json(path, value): + temp = path.with_suffix('.tmp') + temp.write_text(json.dumps(value, indent=2) + '\n') + temp.replace(path) + + +def configuration_records(project): + """Replay one existing log per run with its allowlisted configuration. + + Trackio 0.33.0's get_all_logs_for_sync emits config=None. Its native bulk_log + endpoint can still store config on an existing log_id without adding a metric. + """ + from trackio.sqlite_storage import SQLiteStorage + records, seen = [], set() + for entry in SQLiteStorage.get_all_logs_for_sync(project): + identity = entry.get('run_id') or entry['run'] + if identity in seen: + continue + seen.add(identity) + config = SQLiteStorage.get_run_config(project, entry['run'], run_id=entry.get('run_id')) + if config: + records.append({**entry, 'config': config}) + return records + + +def sync_project(config): + # Token is inherited or loaded only in the sync subprocess; never logged or put in config. + from dotenv import dotenv_values + values = dotenv_values(os.environ.get('DATA_AGENT_ENV_FILE', '.env')) + token = os.environ.get('HF_TOKEN') or values.get('HF_TOKEN') or values.get('HF_API_KEY') + if token: + os.environ['HF_TOKEN'] = token + from trackio.deploy import create_space_if_not_exists + logging = config['logging'] + # Provision once, then send stable log IDs through Trackio's bulk API. Replacing + # the live Space's mounted SQLite file can leave open readers on an old inode. + from huggingface_hub import HfApi + from huggingface_hub.errors import RepositoryNotFoundError + try: + info = HfApi().space_info(logging['space_id']) + if not info.private or info.sdk != 'gradio': + raise ValueError('Online metrics require the configured private Gradio Space') + except RepositoryNotFoundError: + create_space_if_not_exists(logging['space_id'], bucket_id=logging['bucket_id'], private=True) + from trackio.remote_client import RemoteClient + from trackio.sqlite_storage import SQLiteStorage + client = RemoteClient(logging['space_id'], hf_token=token, httpx_kwargs={'timeout': 30}) + records = SQLiteStorage.get_all_logs_for_sync(logging['project']) + # Multiple allocation collectors share evaluation-curve. Trackio 0.33.0's + # sync_incremental waits for exact remote/local row-count equality, which + # cannot hold when a peer has already published additional checkpoint scores. + # Keep native bulk_log deduplication, then verify our exact event contents. + for start in range(0, len(records), 500): + client.predict(api_name='/bulk_log', logs=records[start:start + 500], hf_token=token) + metadata = configuration_records(logging['project']) + if metadata: + client.predict(api_name='/bulk_log', logs=metadata, hf_token=token) + proof = verify_remote_records(client, logging['project'], records) + for entry in metadata: + summary = client.predict(api_name='/get_run_summary', project=logging['project'], + run_id=entry['run_id']) + if summary.get('config') != entry['config']: + raise RuntimeError('Remote Trackio configuration differs for ' + entry['run']) + return {**proof, 'configuration_runs_verified': len(metadata), + 'checked_at': datetime.now(timezone.utc).isoformat()} + + +def verify_remote_records(client, project, records, timeout=90): + """Allow other publishers' events; require every local ID and scalar payload.""" + expected = {} + for entry in records: + identity = entry.get('log_id', '') + if entry.get('project') != project or not re.fullmatch(r'[0-9a-f]{64}', identity): + raise ValueError('Invalid scalar event identity') + value = {k: entry[k] for k in ('run_id', 'step', 'metrics')} + value['run_name'] = entry['run'] + if identity in expected and expected[identity] != value: + raise ValueError('Conflicting local scalar event') + expected[identity] = value + pending = dict(expected) + deadline = time.monotonic() + timeout + while pending: + keys = sorted(pending) + for start in range(0, len(keys), 100): + # IDs are validated SHA256 hex strings, never arbitrary SQL input. + ids = ','.join("'" + k + "'" for k in keys[start:start + 100]) + result = client.predict(api_name='/query_project', project=project, + query='SELECT log_id, run_id, run_name, step, CAST(metrics AS TEXT) AS metrics FROM metrics WHERE log_id IN (' + ids + ')') + for row in result['rows']: + identity = row['log_id'] + actual = {k: row[k] for k in ('run_id', 'run_name', 'step')} + actual['metrics'] = json.loads(row['metrics']) if isinstance(row['metrics'], str) else row['metrics'] + if identity not in expected or actual != expected[identity]: + raise RuntimeError('Remote Trackio event content mismatch') + pending.pop(identity, None) + if not pending: + break + if time.monotonic() >= deadline: + raise TimeoutError(f'{len(pending)} Trackio events are not remotely visible') + time.sleep(min(2, max(0, deadline - time.monotonic()))) + return {'ok': True, 'events_verified': len(expected), 'verification': 'exact log_id/run_id/step/metrics'} + + +def bounded_sync(root, seconds, log): + try: + with log.open('a') as stream: + result = subprocess.run([sys.executable, '-u', __file__, '--run', str(root), '--sync-only'], + stdout=stream, stderr=subprocess.STDOUT, timeout=seconds) + return {'ok': result.returncode == 0, 'returncode': result.returncode} + except subprocess.TimeoutExpired: + return {'ok': False, 'error': 'sync_timeout', 'timeout_seconds': seconds} + + +def work_finished(root, job): + submission = read_json(root / 'submission.json', {}) + # Do not wait for the monitor: it waits for us. Include watcher to prevent early exit + # between training finishing and a just-saved checkpoint being submitted for evaluation. + jobs = [str(job)] + [str(submission[k]) for k in ('cleanup', 'eval_watcher') if k in submission] + for source in evaluation_roots(root): + for record in read_json(source / 'checkpoint-evals/state.json', {}).values(): + jobs += [str(record[k]) for k in ('job_id', 'cleanup_job_id') if record.get(k)] + # Recovery jobs can be submitted after an allocation's watcher exits. + for path in (source / 'checkpoint-evals').glob('step-*/submission.json'): + record = read_json(path, {}) + jobs += [str(record[k]) for k in ('job_id', 'cleanup_job_id') if record.get(k)] + states = slurm_states(sorted(set(jobs))) + return set(states) == set(jobs) and all(value in TERMINAL for value in states.values()) + + +def main(): + p = argparse.ArgumentParser(description=__doc__) + p.add_argument('--run', type=Path, required=True) + p.add_argument('--train-job', default='') + p.add_argument('--watch', action='store_true') + p.add_argument('--online', action='store_true') + p.add_argument('--sync-only', action='store_true', help=argparse.SUPPRESS) + args = p.parse_args() + root = args.run.resolve() + config = read_json(root / 'run_config.json') + if args.sync_only: + proof = sync_project(config) + write_json(root / 'trackio/sync-receipt.json', proof) + return + if args.watch and not args.train_job: + p.error('--watch requires --train-job') + destination = root / 'trackio' + destination.mkdir(exist_ok=True) + for key in REMOTE_ENV: + os.environ.pop(key, None) + with (destination / '.collector.lock').open('w') as lock, tempfile.TemporaryDirectory(prefix='multi4-trackio-') as scratch: + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + os.environ['TRACKIO_DIR'] = scratch + os.environ['TRACKIO_STORAGE_MODE'] = 'sqlite' + last_synced = None + while True: + start = time.monotonic() + state = {'checked_at': datetime.now(timezone.utc).isoformat(), 'online': args.online} + try: + records, withheld = collect(root, args.train_job) + import_events(records) + backup_project(config['logging']['project'], destination / 'dashboard') + state.update(events=len(records), withheld_evaluations=withheld, local_ok=True, + evaluation_sources=[str(p) for p in evaluation_roots(root, config)], + evaluation_steps=sorted({r['step'] for r in records if r['run'] == 'evaluation-curve'}), + full_history_steps=len([r for r in records if r['run'] == 'training-full-history']), + training_step=max((r['step'] for r in records if r['run'] == f'training-{args.train_job}'), default=None)) + current = digest([r['log_id'] for r in records]) + if args.online and current != last_synced: + state['sync'] = bounded_sync(root, config['logging']['sync_timeout_seconds'], destination / 'sync.log') + if state['sync']['ok']: + last_synced = current + else: + state['sync'] = {'ok': bool(last_synced), 'skipped_unchanged': True} + finished = args.watch and work_finished(root, args.train_job) + except Exception as exc: + state.update(error=type(exc).__name__ + ': ' + str(exc), local_ok=False) + finished = False + write_json(destination / 'status.json', state) + with (destination / 'history.jsonl').open('a') as stream: + stream.write(json.dumps(state) + '\n') + print(json.dumps(state), flush=True) + if not args.watch: + if not state.get('local_ok') or (args.online and not state.get('sync', {}).get('ok')): + raise SystemExit(1) + break + if finished and (not args.online or state.get('sync', {}).get('ok')): + break + time.sleep(max(1, config['logging']['poll_seconds'] - (time.monotonic() - start))) + + +if __name__ == '__main__': + main() diff --git a/04-data-agent/tools/train_whitebox_daytona.py b/04-data-agent/tools/train_whitebox_daytona.py new file mode 100644 index 0000000..f47aa69 --- /dev/null +++ b/04-data-agent/tools/train_whitebox_daytona.py @@ -0,0 +1,151 @@ +"""Isolated synchronous GRPO comparison with actual token and loss-mask audits.""" +from __future__ import annotations + +import argparse +import hashlib +import json +import math +import os +from pathlib import Path + +import torch +from datasets import Dataset +from transformers import TrainerCallback +from trl import GRPOConfig, GRPOTrainer +from whitebox_bash import white_box_bash_env +from eval_whitebox_native import MODEL, REVISION, SYSTEM +from generation_routing import generation_group_size + + +class AuditedTrainer(GRPOTrainer): + """Observe the native loop without replacing its generation, rewards or loss.""" + + def _generate_single_turn(self, prompt_ids, *args, **kwargs): + completion_ids, logprobs = super()._generate_single_turn(prompt_ids,*args,**kwargs) + records=getattr(self,'_captured_calls',[]) + for prompt,completion,lps in zip(prompt_ids,completion_ids,logprobs,strict=True): + assert len(completion)==len(lps) and all(math.isfinite(p) for p in lps) + records.append({'prompt_ids':prompt.copy(),'completion_ids':completion.copy(),'logprobs':lps.copy()}) + self._captured_calls=records + return completion_ids,logprobs + + def _generate(self,*args,**kwargs): + self._captured_calls=[] + return super()._generate(*args,**kwargs) + + def _tool_call_loop(self,prompts,prompt_ids,*args,**kwargs): + result=super()._tool_call_loop(prompts,prompt_ids,*args,**kwargs) + masks,_,completions,lps,_,_,_=result + evidence=Path(self.args.output_dir)/'capture_audit' + evidence.mkdir(exist_ok=True) + (evidence/f'step-{self.state.global_step}.json').write_text(json.dumps({ + 'prompt_ids':prompt_ids,'completions':completions,'masks':masks,'logprobs':lps, + 'calls':self._captured_calls})+'\n') + from whitebox_tito import audit_rows + checks=audit_rows(prompt_ids,completions,masks,lps,self._captured_calls) + path=Path(self.args.output_dir)/'token_audit.jsonl' + with path.open('a') as stream: + stream.write(json.dumps({'step':self.state.global_step,'rows':checks,'calls':len(self._captured_calls)})+'\n') + return result + + def compute_loss(self,model,inputs,*args,**kwargs): + if 'tool_mask' in inputs: + mask=inputs['completion_mask']*inputs['tool_mask'] + assert torch.isfinite(inputs['sampling_per_token_logps'][mask.bool()]).all() + assert set(inputs['tool_mask'].unique().tolist())<={0,1} + return super().compute_loss(model,inputs,*args,**kwargs) + + +def digest(model): + h=hashlib.sha256() + for name,param in model.named_parameters(): + if param.requires_grad: + h.update(name.encode());h.update(param.detach().flatten()[:256].float().cpu().numpy().tobytes()) + return h.hexdigest() + + +class SmokeEvidence(TrainerCallback): + def __init__(self,path): self.path=path + def on_train_begin(self,args,state,control,model=None,**kwargs): + self.initial=digest(model) + self.initial_step=state.global_step + def on_log(self,args,state,control,logs=None,**kwargs): + for key,value in (logs or {}).items(): + if isinstance(value,float): assert math.isfinite(value), f'nonfinite metric: {key}' + with (self.path/'metrics.jsonl').open('a') as stream: + stream.write(json.dumps({'step':state.global_step,**(logs or {})})+'\n') + def on_train_end(self,args,state,control,model=None,**kwargs): + result={'initial_step':self.initial_step,'final_step':state.global_step, + 'initial_weight_digest':self.initial,'final_weight_digest':digest(model)} + result['weights_changed']=result['initial_weight_digest']!=result['final_weight_digest'] + (self.path/f'optimizer_evidence_from_{self.initial_step}.json').write_text(json.dumps(result,indent=2)+'\n') + + +def main(): + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('--run',type=Path,required=True) + p.add_argument('--server',required=True) + p.add_argument('--vllm-url',required=True) + p.add_argument('--output-dir',type=Path,required=True) + p.add_argument('--max-steps',type=int,default=1000) + p.add_argument('--save-steps',type=int,default=50) + p.add_argument('--resume-from-checkpoint') + p.add_argument('--max-train-seconds',type=float,default=0) + p.add_argument('--checkpoint-max-seconds',type=float,default=0) + args=p.parse_args() + args.output_dir.mkdir(parents=True,exist_ok=True) + schedule=json.loads((args.run/'reference_schedule.json').read_text()) + rows=[{'prompt':[{'role':'system','content':SYSTEM},{'role':'user','content':'Solve the task.'}], + 'split':'train','index':g['task_index']} for g in schedule['groups'][:schedule['task_count']]] + config=GRPOConfig( + output_dir=str(args.output_dir),model_init_kwargs={'revision':REVISION,'dtype':'bfloat16'}, + learning_rate=3e-6,lr_scheduler_type='constant',warmup_steps=0,beta=0.0,loss_type='dapo', + num_generations=8,per_device_train_batch_size=1,gradient_accumulation_steps=8, + max_steps=args.max_steps,max_completion_length=16384,max_tool_calling_iterations=16, + temperature=0.8,top_p=1.0,top_k=0,chat_template_kwargs={'enable_thinking':False}, + gradient_checkpointing=True,gradient_checkpointing_kwargs={'use_reentrant':False}, + bf16=True,optim='paged_adamw_8bit',max_grad_norm=1.0,seed=0,shuffle_dataset=False, + use_vllm=True,vllm_mode='server',vllm_server_base_url=args.vllm_url, + vllm_max_model_length=131072,vllm_server_timeout=900, + vllm_group_port=49000+int(os.environ.get('SLURM_JOB_ID','0'))%1000, + generation_kwargs={'max_tokens':16384}, + save_strategy='steps',save_steps=args.save_steps,save_total_limit=None, + logging_steps=1,log_completions=True,num_completions_to_print=1, + report_to='trackio',project='daytona-whitebox-qwen35-2b', + run_name=f'whitebox-{os.environ.get("RUN_OWNER", os.environ.get("SLURM_JOB_ID", "local"))}', + trackio_space_id=None,trackio_static_space_id=False, + ) + (args.output_dir/'comparison_config.json').write_text(json.dumps(config.to_dict(),indent=2,default=str)+'\n') + trainer=AuditedTrainer(model=MODEL,args=config,train_dataset=Dataset.from_list(rows),reward_funcs=[], + environment_factory=white_box_bash_env(args.server,split='train',toolsets='bash,seta',step_limit=17), + callbacks=[SmokeEvidence(args.output_dir)]) + from training_audit import CheckpointReadyCallback + trainer.add_callback(CheckpointReadyCallback(MODEL,REVISION)) + if args.max_train_seconds: + from training_audit import WallTimeCallback + trainer.add_callback(WallTimeCallback(args.max_train_seconds)) + if args.checkpoint_max_seconds: + from training_audit import PeriodicCheckpointCallback + trainer.add_callback(PeriodicCheckpointCallback(args.checkpoint_max_seconds)) + # Check the response's engine IDs and sampled logprob IDs before GRPO consumes them. + generate=trainer.vllm_generation.generate + def checked_generate(*a,**kw): + # Native server generation groups duplicated initial prompts by G. After tools, + # histories differ and need one continuation each; grouping those would replace + # seven trajectories' contexts with the first one's context. + prompts=kw.get('prompts',a[0] if a else None) + kw={**kw,'num_generations':generation_group_size(prompts,kw.get('num_generations',1))} + output=generate(*a,**kw) + returned_prompts,completions,logprobs,token_ids=output + assert returned_prompts==prompts + for ids,lps,lp_ids in zip(completions,logprobs,token_ids,strict=True): + assert len(ids)==len(lps)==len(lp_ids) + assert all(len(ids_at_pos)==1 and ids_at_pos[0]==tok for tok,ids_at_pos in zip(ids,lp_ids,strict=True)) + return output + trainer.vllm_generation.generate=checked_generate + trainer.train(resume_from_checkpoint=args.resume_from_checkpoint) + trainer.save_state() + trainer.save_model(str(args.output_dir/'final')) + + +if __name__=='__main__': main() diff --git a/04-data-agent/tools/validate_training_smoke.py b/04-data-agent/tools/validate_training_smoke.py new file mode 100644 index 0000000..1e50f87 --- /dev/null +++ b/04-data-agent/tools/validate_training_smoke.py @@ -0,0 +1,55 @@ +"""Require real optimizer, save/resume and token-provenance evidence before long runs.""" +import argparse +import json +import math +from pathlib import Path +import sys + +ROOT=Path(__file__).resolve().parents[1]/'logs/20260915' +sys.path.insert(0,str(ROOT/'source/HuggingEnvs/04-data-agent/train')) +from checkpoint_artifacts import finalize_saved, verify_ready + + +def validate(arm,job): + logs=ROOT/arm/f'training-smoke/job-{job}';run=logs/'run' + assert (logs/'exit_code.txt').read_text().strip()=='0' + markers={} + for step in (2,4): + checkpoint=run/f'checkpoint-{step}' + finalize_saved(checkpoint);markers[step]=verify_ready(checkpoint) + assert markers[step]['step']==step + rows=[json.loads(l) for l in (logs/('audit/metrics.jsonl' if arm=='blackbox' else 'run/metrics.jsonl')).read_text().splitlines()] + updates=[r for r in rows if 'grad_norm' in r] + assert {r['step'] for r in updates}>={1,2,3,4} + assert all(math.isfinite(v) for r in updates for v in r.values() if isinstance(v,float)) + assert any(r['grad_norm']>0 for r in updates), 'No optimizer learning signal observed' + if arm=='blackbox': + from checkpoint_artifacts import resume_info + resume=resume_info(run/'checkpoint-2',markers[2]['base_model'],markers[2]['base_revision']) + assert f"resume checkpoint step=2, next schedule group={resume['group_offset']}" in (logs/'train-resumed.log').read_text() + summary=json.loads((logs/'audit/tito_summary.json').read_text()) + assert summary and all(v['tito_pass']==v['completed_results'] and v['retained_tokens']==v['eligible_tokens'] and v['rows_over_token_budget']==0 for v in summary.values()) + else: + initial=json.loads((run/'optimizer_evidence_from_0.json').read_text()) + resumed=json.loads((run/'optimizer_evidence_from_2.json').read_text()) + assert initial['final_step']==2 and resumed['final_step']==4 + assert initial['weights_changed'] or resumed['weights_changed'] + assert initial['final_weight_digest']==resumed['initial_weight_digest'], 'Resume did not load saved parameters' + token_rows=[json.loads(l) for l in (run/'token_audit.jsonl').read_text().splitlines()] + assert {r['step'] for r in token_rows}>={0,1,2,3} + assert all(row['tito_pass'] and row['supervised']>0 for r in token_rows for row in r['rows']) + # A native optimizer has persisted nonempty state at the resumed checkpoint. + import torch + optimizer=torch.load(run/'checkpoint-4/optimizer.pt',map_location='cpu',weights_only=False) + assert optimizer['state'] and optimizer['param_groups'] + report={'arm':arm,'job_id':str(job),'passed':True,'optimizer_steps':[1,2,3,4], + 'nonzero_gradient_updates':sum(r['grad_norm']>0 for r in updates), + 'checkpoint_steps':[2,4],'native_optimizer_state_verified':True, + 'resume_verified':True,'tito_pass':True,'weights_updated':True} + target=ROOT/arm/'training-smoke/validation.json';target.write_text(json.dumps(report,indent=2)+'\n') + return report + + +if __name__=='__main__': + p=argparse.ArgumentParser(description=__doc__);p.add_argument('--arm',choices=['blackbox','whitebox'],required=True) + p.add_argument('--job',required=True);a=p.parse_args();print(json.dumps(validate(a.arm,a.job))) diff --git a/04-data-agent/train/LOGGING.md b/04-data-agent/train/LOGGING.md new file mode 100644 index 0000000..2bbc413 --- /dev/null +++ b/04-data-agent/train/LOGGING.md @@ -0,0 +1,13 @@ +# Training and evaluation logging + +The durable scalar source is `audit/metrics.jsonl` for async training and `run/metrics.jsonl` for SETA. Optimizer steps are the horizontal axis. Raw captures and token/mask audits are stored separately. + +`hf/runtime/logging_sync.py` replays events into Trackio in a separate process. Deterministic log IDs prevent duplicates after a restart. It writes `trackio-events.jsonl`, a consistent `trackio-backup/` database snapshot and `trackio_verified.json`. The asynchronous artifact publisher uploads these alongside run metadata; network requests do not execute in the optimizer callback. + +The default reproduction uses local Trackio plus remote artifact storage. A public dashboard is a separate presentation service, not part of an environment Space. The recorded runs use the [shared comparison dashboard](https://huggingface.co/spaces/HuggingEnvs/data-agent-training-comparison-trackio); `hf/consolidate_async_runs.py` contains the historical collector. Use a distinct project/run identity for a new reproduction. + +For offline viewing, download a verified database backup, copy it to a local writable directory, and point `TRACKIO_DIR` there before running `trackio show --project PROJECT_NAME`. Avoid a writable SQLite database on a shared network filesystem. The pinned Trackio version is 0.33.0. + +Completed checkpoint scores are replayed only after fixed coverage, TiTO, harness-version and checkpoint checks. Evaluations that finish after training remain in the coordinator's score artifacts for later replay. The scalar payload includes reward, loss, gradient norm, staleness, token throughput, fork/row counts and pass@1 by harness/difficulty. Training reward and held-out pass@1 remain separate metrics. + +The current frozen results and full metrics snapshot are in [../results.md](../results.md). Historical operator notes and retired dashboard repair instructions are preserved locally under ignored `temp/historical-notes/`. diff --git a/04-data-agent/train/_pypath/data_agent_env b/04-data-agent/train/_pypath/data_agent_env new file mode 120000 index 0000000..84b66a2 --- /dev/null +++ b/04-data-agent/train/_pypath/data_agent_env @@ -0,0 +1 @@ +../../envs/blackbox-opencode \ No newline at end of file diff --git a/04-data-agent/train/_pypath/harbor_reward.py b/04-data-agent/train/_pypath/harbor_reward.py new file mode 120000 index 0000000..19b7164 --- /dev/null +++ b/04-data-agent/train/_pypath/harbor_reward.py @@ -0,0 +1 @@ +../harbor_reward.py \ No newline at end of file diff --git a/04-data-agent/train/_pypath/whitebox_bash b/04-data-agent/train/_pypath/whitebox_bash new file mode 120000 index 0000000..98792a1 --- /dev/null +++ b/04-data-agent/train/_pypath/whitebox_bash @@ -0,0 +1 @@ +../../envs/whitebox-bash \ No newline at end of file diff --git a/04-data-agent/train/atomic_rollouts.py b/04-data-agent/train/atomic_rollouts.py new file mode 100644 index 0000000..cff332b --- /dev/null +++ b/04-data-agent/train/atomic_rollouts.py @@ -0,0 +1,459 @@ +"""Single-GPU AsyncGRPO recipe that consumes every row of an admitted rollout. + +The reference uses four token-packed batches per update. A forked rollout can +exceed one batch: keep it as one admission unit and stream its exact rows through +bounded forwards before the optimizer changes weights. No TRL source patch or +token rewriting is needed. The loss is a token mean over the entire update. +""" + +from __future__ import annotations + +import asyncio +import json +import queue +import time +from dataclasses import dataclass, replace +from pathlib import Path + +import torch +from torch.utils.data import DataLoader, IterableDataset +from transformers import TrainerCallback +from trl.experimental.async_grpo import AsyncGRPOTrainer +from trl.experimental.async_grpo.async_grpo_trainer import DataCollatorForRollout +from trl.experimental.async_grpo.openenv_harness import ( + HarnessRolloutWorker, + _HarnessRolloutLoop, +) + + +@dataclass +class RolloutBundle: + rows: list + rollout_id: str + enqueued_at: float | None = None + + @property + def model_version(self): + return self.rows[0].model_version + + @property + def group_id(self): + return self.rows[0].group_id + + @property + def prompt(self): + return self.rows[0].prompt + + @property + def completion(self): + return self.rows[0].completion + + @property + def advantage(self): + return self.rows[0].advantage + + @property + def metrics(self): + return self.rows[0].metrics + + @property + def forwarded_tokens(self): + return sum(len(row.input_ids) for row in self.rows) + + +class CreditQueue: + """Native queue plus a spawn-safe budget, passed through native worker IPC. + + Credits cover generation, scoring and queued work until consumption. Putting + the counter beside the native queue avoids the worker's ordinary-pickle + validation of loop kwargs (shared values must use the multiprocessing spawn path). + """ + + def __init__(self, queue, credits, capacity): + self.queue, self.credits, self.capacity = queue, credits, capacity + + def reserve_group(self, count): + # Reserve all generations together: individually competing waiters can + # otherwise occupy every credit with several incomplete GRPO groups. + with self.credits.get_lock(): + if self.credits.value < count: + return False + self.credits.value -= count + return True + + def release(self, count): + with self.credits.get_lock(): + if self.credits.value + count > self.capacity: + raise RuntimeError("Rollout credit released more than once") + self.credits.value += count + + def __getattr__(self, name): + queue = self.__dict__.get("queue") + if queue is None: + raise AttributeError(name) + return getattr(queue, name) + + +class AtomicHarnessLoop(_HarnessRolloutLoop): + async def _reserve_group(self): + while not self.rollout_buffer.reserve_group(self.num_generations): + if self._stop_event.is_set(): + return False + await asyncio.sleep(0.05) + return True + + async def _generate_one(self, prompt, tool_dict, tools, group_id=0): + credits = getattr(self.rollout_buffer, "credits", None) + if credits is None: + return await super()._generate_one(prompt, tool_dict, tools, group_id) + if not hasattr(self, "_group_reservations"): + self._group_reservations = {} + if group_id not in self._group_reservations: + self._group_reservations[group_id] = asyncio.create_task( + self._reserve_group() + ) + if not await self._group_reservations[group_id]: + return self._EMPTY_ROLLOUT + # Native groups can be created before credit becomes available. Record + # actual dispatch, never relabel an older sampled policy as a newer one. + version = self.model_version + try: + result = await super()._generate_one(prompt, tool_dict, tools, group_id) + except BaseException: + self.rollout_buffer.release(1) + raise + sequences = result[2] + if sequences: + if not hasattr(self, "_dispatch_versions"): + self._dispatch_versions = {} + self._dispatch_versions[sequences[0].rollout_id] = version + else: + self.rollout_buffer.release(1) + return result + + async def _score_group(self, group): + getattr(self, "_group_reservations", {}).pop(group.group_id, None) + versions = getattr(self, "_dispatch_versions", {}) + actual = [ + versions.pop(sequences[0].rollout_id) + for sequences in group.completions_sequences + if sequences and sequences[0].rollout_id in versions + ] + if actual: + if len(actual) != sum( + bool(sequences) for sequences in group.completions_sequences + ): + raise RuntimeError("A scored rollout has no dispatch policy version") + group = replace(group, model_version=min(actual)) + rows = await super()._score_group(group) + bundles, offset = [], 0 + for sequences in group.completions_sequences: + count = len(sequences) + if count: + selected = rows[offset : offset + count] + assert len(selected) == count + rollout_id = sequences[0].rollout_id + assert all(seq.rollout_id == rollout_id for seq in sequences) + bundles.append(RolloutBundle(selected, rollout_id)) + offset += count + assert offset == len(rows) + return bundles + + +class AtomicHarnessWorker(HarnessRolloutWorker): + _loop_cls = AtomicHarnessLoop + + def __init__(self, *, max_outstanding_rollouts=0, **kwargs): + super().__init__(**kwargs) + self.max_outstanding_rollouts = max_outstanding_rollouts + self.num_generations = kwargs["num_generations"] + if max_outstanding_rollouts: + if max_outstanding_rollouts < 2 * self.num_generations: + raise ValueError("Outstanding budget must allow two complete groups") + self.rollout_buffer = CreditQueue( + self.rollout_buffer, + self._mp_ctx.Value("i", max_outstanding_rollouts), + max_outstanding_rollouts, + ) + + def release_rollouts(self, count): + credits = getattr(self.rollout_buffer, "credits", None) + if credits is not None: + self.rollout_buffer.release(count) + + +class AtomicRolloutDataset(IterableDataset): + def __init__( + self, + worker, + metrics, + target_tokens, + max_row_tokens, + max_staleness, + heartbeat_seconds, + max_rollouts_per_unit=None, + rejection_path=None, + group_offset=0, + ): + self.worker, self.metrics = worker, metrics + self.target_tokens, self.max_row_tokens = target_tokens, max_row_tokens + self.max_staleness, self.heartbeat_seconds = max_staleness, heartbeat_seconds + self.wait_s = 0.0 + self.pending = None + self.max_rollouts_per_unit = max_rollouts_per_unit + self.rejection_path = rejection_path + self.group_offset = group_offset + + def _next_bundle(self): + if self.pending is not None: + bundle, self.pending = self.pending, None + return bundle + started = time.monotonic() + while True: + try: + bundle = self.worker.rollout_buffer.get(timeout=5) + self.wait_s += time.monotonic() - started + return bundle + except queue.Empty: + self.worker.check_health(self.heartbeat_seconds) + + def __iter__(self): + while True: + bundles, tokens = [], 0 + while tokens < self.target_tokens and ( + self.max_rollouts_per_unit is None + or len(bundles) < self.max_rollouts_per_unit + ): + bundle = self._next_bundle() + channel = self.worker.rollout_buffer + if isinstance(channel, CreditQueue): + self.metrics["admission/outstanding_rollouts_max"].append( + float(channel.capacity - channel.credits.value) + ) + if not isinstance(bundle, RolloutBundle) or not bundle.rows: + raise RuntimeError( + "Atomic trainer requires nonempty rollout bundles" + ) + staleness = self.worker.model_version - bundle.model_version + if staleness > self.max_staleness: + self.metrics["admission/stale_rollouts_dropped_total"].append(1.0) + self.metrics["sample/dropped_stale_total"].append( + float(len(bundle.rows)) + ) + if self.rejection_path: + self.rejection_path.parent.mkdir(parents=True, exist_ok=True) + with self.rejection_path.open("a") as stream: + stream.write( + json.dumps( + { + "rollout_id": bundle.rollout_id, + "group_id": bundle.group_id + self.group_offset, + "rows": len(bundle.rows), + "model_version": bundle.model_version, + "current_model_version": self.worker.model_version, + "reason": "whole_rollout_staleness_limit", + } + ) + + "\n" + ) + if hasattr(self.worker, "release_rollouts"): + self.worker.release_rollouts(1) + continue + if any(len(row.input_ids) > self.max_row_tokens for row in bundle.rows): + raise RuntimeError( + "Captured row exceeds the tested context limit; refusing to discard it" + ) + if bundles and tokens + bundle.forwarded_tokens > self.target_tokens: + self.pending = bundle + break + # Recheck pending bundles above when they are actually admitted next time. + bundles.append(bundle) + tokens += bundle.forwarded_tokens + self.metrics["sample/staleness_mean"].append(float(staleness)) + self.metrics["sample/staleness_max"].append(float(staleness)) + self.metrics["sample/rollout_queue_size"].append( + float(self.worker.rollout_buffer.qsize()) + ) + if bundle.enqueued_at is not None: + self.metrics["sample/time_in_queue_s"].append( + time.time() - bundle.enqueued_at + ) + yield {"rollouts": bundles} + + +def pack_rows(bundles, target_tokens, max_row_tokens): + """Keep exact sequences intact; a long sequence gets one dedicated forward.""" + packed, tokens = [], 0 + for bundle in bundles: + for row in bundle.rows: + size = len(row.input_ids) + if size > max_row_tokens: + raise ValueError("Row exceeds maximum context") + if packed and tokens + size > target_tokens: + yield packed + packed, tokens = [], 0 + packed.append( + { + key: getattr(row, key) + for key in ( + "input_ids", + "completion_mask", + "old_log_probs", + "advantage", + "group_id", + "metrics", + ) + } + ) + tokens += size + if tokens >= target_tokens: + yield packed + packed, tokens = [], 0 + if packed: + yield packed + + +def identity(value): + return value + + +class AtomicRolloutTrainer(AsyncGRPOTrainer): + def __init__(self, *args, max_row_tokens=131072, admission_dir=None, **kwargs): + self.max_row_tokens = max_row_tokens + self.admission_dir = Path(admission_dir) if admission_dir else None + self._atomic_finished = [] + super().__init__(*args, **kwargs) + if self.accelerator.num_processes != 1 or self.aux_loss_enabled: + raise ValueError( + "Atomic recipe is validated only for a single-GPU dense trainer" + ) + self.add_callback(AtomicAdmissionCallback(self)) + + def get_train_dataloader(self): + outstanding = getattr(self.rollout_worker, "max_outstanding_rollouts", 0) + max_per_unit = None + if outstanding: + # Leave capacity for an entire new GRPO group while accumulating. + # Otherwise a short partial batch can hold all credits and wait + # forever for a group whose final generation cannot start. + max_per_unit = ( + outstanding - self.rollout_worker.num_generations + ) // self.args.gradient_accumulation_steps + if max_per_unit < 1: + raise ValueError("Outstanding budget cannot fill an optimizer update") + dataset = AtomicRolloutDataset( + self.rollout_worker, + self._metrics["train"], + self.args.token_budget, + self.max_row_tokens, + self.args.max_staleness, + self.args.heartbeat_stale_after_s, + max_rollouts_per_unit=max_per_unit, + rejection_path=( + self.admission_dir / "rejected_rollouts.jsonl" + if self.admission_dir + else None + ), + group_offset=self._groups_before_resume, + ) + self._rollout_dataset = dataset + self._atomic_collator = DataCollatorForRollout( + self.processing_class.pad_token_id, + groups_trained=self._trained_groups, + metrics=self._metrics["train"], + token_budget=self.max_row_tokens, + ) + # There is exactly one rank. Dispatcher prefetch/slicing would split the + # nested rollout container; inner tensors move to the GPU in training_step. + return DataLoader(dataset, batch_size=None, num_workers=0, collate_fn=identity) + + def get_batch_samples(self, epoch_iterator, num_batches, device): + batches = [next(epoch_iterator) for _ in range(num_batches)] + count = sum( + sum(row.completion_mask[1:]) + for batch in batches + for bundle in batch["rollouts"] + for row in bundle.rows + ) + if count <= 0: + raise RuntimeError("Optimizer update has no supervised tokens") + for batch in batches: + batch["normalization_tokens"] = count + return batches, None + + def compute_loss( + self, model, inputs, return_outputs=False, num_items_in_batch=None + ): + loss = super().compute_loss(model, inputs, return_outputs, num_items_in_batch) + # Native loss divides by this forward's token count and by GAS. Undo + # those factors and normalize once across all exact tokens in the update. + # Keep native counters untouched: they report actual forwarded/trained tokens. + return loss * ( + self.current_gradient_accumulation_steps + * inputs["global_n_tokens"][0] + / self._atomic_normalization_tokens + ) + + def training_step(self, model, inputs, num_items_in_batch): + self._atomic_normalization_tokens = inputs["normalization_tokens"] + total_loss = torch.zeros((), device=self.args.device) + rows_done = tokens_done = 0 + for rows in pack_rows( + inputs["rollouts"], self.args.token_budget, self.max_row_tokens + ): + tensors = self._atomic_collator([[rows]]) + tokens_done += int(tensors["global_n_tokens"][0]) + rows_done += len(rows) + total_loss += super().training_step(model, tensors, None) + expected_rows = sum(len(bundle.rows) for bundle in inputs["rollouts"]) + expected_tokens = sum( + sum(row.completion_mask[1:]) + for bundle in inputs["rollouts"] + for row in bundle.rows + ) + assert rows_done == expected_rows and tokens_done == expected_tokens + self._atomic_finished.extend( + { + "rollout_id": bundle.rollout_id, + "local_group_id": bundle.group_id, + "group_id": bundle.group_id + self._groups_before_resume, + "model_version": bundle.model_version, + "rows": len(bundle.rows), + "supervised_tokens": sum( + sum(row.completion_mask[1:]) for row in bundle.rows + ), + } + for bundle in inputs["rollouts"] + ) + return total_loss + + def floating_point_ops(self, inputs): + # Native per-forward token/timing metrics cover the nested batches. + return 0 + + +class AtomicAdmissionCallback(TrainerCallback): + def __init__(self, trainer): + self.trainer = trainer + + def on_step_end(self, args, state, control, **kwargs): + rows = self.trainer._atomic_finished + if self.trainer.admission_dir: + self.trainer.admission_dir.mkdir(parents=True, exist_ok=True) + with (self.trainer.admission_dir / "optimizer_rollouts.jsonl").open( + "a" + ) as stream: + stream.write( + json.dumps( + { + "step": state.global_step, + "rollouts": rows, + "all_admitted_rows_consumed": True, + "normalization": "update_supervised_token_mean", + } + ) + + "\n" + ) + self.trainer._atomic_finished = [] + if hasattr(self.trainer.rollout_worker, "release_rollouts"): + self.trainer.rollout_worker.release_rollouts(len(rows)) diff --git a/04-data-agent/train/checkpoint_artifacts.py b/04-data-agent/train/checkpoint_artifacts.py new file mode 100644 index 0000000..40acebc --- /dev/null +++ b/04-data-agent/train/checkpoint_artifacts.py @@ -0,0 +1,187 @@ +"""Publish completed full-model checkpoints and stage read-only evaluation inputs.""" +import argparse +import hashlib +import json +from pathlib import Path +import shutil + +READY = 'checkpoint.ready.json' +SAVED = 'checkpoint.saved.json' +REQUIRED = ('config.json', 'trainer_state.json', 'tokenizer.json', 'tokenizer_config.json', + 'training_args.bin', 'optimizer.pt', 'scheduler.pt', 'rng_state.pth') +METADATA = ('config.json', 'generation_config.json', 'tokenizer.json', 'tokenizer_config.json', + 'preprocessor_config.json', 'video_preprocessor_config.json', 'processor_config.json', 'chat_template.json', + 'chat_template.jinja', 'special_tokens_map.json', 'vocab.json', 'merges.txt', + 'added_tokens.json', 'model.safetensors.index.json') + + +def digest(path): + h = hashlib.sha256() + with Path(path).open('rb') as stream: + for block in iter(lambda: stream.read(8 * 1024 * 1024), b''): + h.update(block) + return h.hexdigest() + + +def write_json(path, data): + temporary = path.with_suffix(path.suffix + '.tmp') + temporary.write_text(json.dumps(data, indent=2) + '\n') + temporary.replace(path) + + +def model_files(checkpoint): + from safetensors import safe_open + index = checkpoint / 'model.safetensors.index.json' + weight_map = json.loads(index.read_text())['weight_map'] if index.exists() else None + names = set(weight_map.values()) if weight_map else {'model.safetensors'} + if not names or any(Path(n).name != n or not n.endswith('.safetensors') for n in names): + raise ValueError('Invalid checkpoint shard index') + all_keys = set() + for name in sorted(names): + with safe_open(checkpoint / name, framework='numpy') as tensors: + keys = set(tensors.keys()) + if not keys or all_keys.intersection(keys): + raise ValueError('Empty shard or duplicated tensor keys') + if weight_map and keys != {k for k, v in weight_map.items() if v == name}: + raise ValueError(f'Shard contents disagree with index: {name}') + all_keys.update(keys) + return sorted(names) + + +def mark_saved(checkpoint, step, base_model, base_revision, *, final=False): + """Publish a small handoff after save; leave weight hashing to the CPU watcher.""" + checkpoint = Path(checkpoint).resolve() + for name in REQUIRED: + if not (checkpoint / name).is_file() or (checkpoint / name).stat().st_size == 0: + raise ValueError(f'Incomplete checkpoint: {name}') + if json.loads((checkpoint / 'trainer_state.json').read_text())['global_step'] != step: + raise ValueError('Checkpoint step disagrees with trainer state') + names = set(REQUIRED) | set(model_files(checkpoint)) + names.update(n for n in METADATA if (checkpoint / n).exists()) + marker = {'schema_version': 1, 'checkpoint': str(checkpoint), 'step': step, 'final': final, + 'base_model': base_model, 'base_revision': base_revision, + 'file_stats': {n: [(checkpoint / n).stat().st_size, (checkpoint / n).stat().st_mtime_ns] + for n in sorted(names)}} + write_json(checkpoint / SAVED, marker) + return marker + + +def finalize_saved(checkpoint): + checkpoint = Path(checkpoint).resolve() + marker = json.loads((checkpoint / SAVED).read_text()) + if marker['checkpoint'] != str(checkpoint): + raise ValueError('Saved checkpoint path mismatch') + for name, expected in marker['file_stats'].items(): + if Path(name).name != name: + raise ValueError('Invalid saved checkpoint filename') + stat = (checkpoint / name).stat() + if [stat.st_size, stat.st_mtime_ns] != expected: + raise ValueError(f'Checkpoint changed after save: {name}') + return mark_ready(checkpoint, marker['step'], marker['base_model'], marker['base_revision'], + final=marker.get('final', False)) + + +def mark_ready(checkpoint, step, base_model, base_revision, *, final=False): + checkpoint = Path(checkpoint).resolve() + for name in REQUIRED: + if not (checkpoint / name).is_file() or (checkpoint / name).stat().st_size == 0: + raise ValueError(f'Incomplete checkpoint: {name}') + if json.loads((checkpoint / 'trainer_state.json').read_text())['global_step'] != step: + raise ValueError('Checkpoint step disagrees with trainer state') + names = model_files(checkpoint) + [n for n in METADATA if (checkpoint / n).exists()] + names = sorted(set(names + ['trainer_state.json'])) + before = {n: ((checkpoint / n).stat().st_size, (checkpoint / n).stat().st_mtime_ns) for n in names} + hashes = {n: digest(checkpoint / n) for n in names} + after = {n: ((checkpoint / n).stat().st_size, (checkpoint / n).stat().st_mtime_ns) for n in names} + if before != after: + raise ValueError('Checkpoint changed during finalization') + marker = {'schema_version': 1, 'step': step, 'checkpoint': str(checkpoint), 'final': final, + 'base_model': base_model, 'base_revision': base_revision, + 'files': hashes, 'file_stats': after, 'training_state_present': list(REQUIRED)} + write_json(checkpoint / READY, marker) + return marker + + +def verify_ready(checkpoint): + checkpoint = Path(checkpoint).resolve() + marker = json.loads((checkpoint / READY).read_text()) + if marker['checkpoint'] != str(checkpoint): + raise ValueError('Checkpoint path differs from its completion marker') + for name, expected in marker['files'].items(): + if Path(name).name != name or digest(checkpoint / name) != expected: + raise ValueError(f'Checkpoint changed after completion: {name}') + model_files(checkpoint) + return marker + + +def resume_info(checkpoint, base_model, base_revision): + """Validate a completed local Trainer checkpoint, including its rollout cursor.""" + checkpoint = Path(checkpoint).resolve() + path = checkpoint / SAVED if (checkpoint / SAVED).exists() else checkpoint / READY + if not path.is_file(): + raise ValueError('Resume requires a completed checkpoint marker') + marker = json.loads(path.read_text()) + if (marker['checkpoint'] != str(checkpoint) or marker['base_model'] != base_model + or marker['base_revision'] != base_revision): + raise ValueError('Resume checkpoint path or base model/revision differs') + for name in REQUIRED + ('rollout_state.json',): + if not (checkpoint / name).is_file() or (checkpoint / name).stat().st_size == 0: + raise ValueError(f'Incomplete resume checkpoint: {name}') + for name, expected in marker['file_stats'].items(): + if Path(name).name != name: + raise ValueError('Invalid checkpoint filename') + stat = (checkpoint / name).stat() + if [stat.st_size, stat.st_mtime_ns] != expected: + raise ValueError(f'Resume checkpoint changed after save: {name}') + model_files(checkpoint) + state = json.loads((checkpoint / 'trainer_state.json').read_text()) + rollout = json.loads((checkpoint / 'rollout_state.json').read_text()) + if state['global_step'] != marker['step'] or marker['step'] <= 0: + raise ValueError('Resume step disagrees with completion marker') + if any(type(rollout.get(k)) is not int or rollout[k] < 0 for k in ('prompt_index', 'model_version')): + raise ValueError('Invalid rollout cursor/model version') + return {'checkpoint': str(checkpoint), 'step': marker['step'], + 'group_offset': rollout['prompt_index'], 'model_version': rollout['model_version'], + 'rollout_state_sha256': digest(checkpoint / 'rollout_state.json')} + + +def stage_model(checkpoint, target, base_metadata): + checkpoint, target, base_metadata = map(Path, (checkpoint, target, base_metadata)) + marker = verify_ready(checkpoint) + target.mkdir(parents=True, exist_ok=False) + for name in model_files(checkpoint): + (target / name).symlink_to((checkpoint / name).resolve()) + origins = {} + for name in METADATA: + source = checkpoint / name + if not source.exists(): + if name == 'config.json' or name == 'model.safetensors.index.json': + continue + source = base_metadata / name + if source.exists(): + shutil.copy2(source, target / name) + origins[name] = str(source.resolve()) + assert (target / 'config.json').read_bytes() == (checkpoint / 'config.json').read_bytes() + record = {'checkpoint': marker, 'metadata_origins': origins, + 'files': {p.name: digest(p) for p in target.iterdir() if p.is_file()}} + write_json(target / 'checkpoint_source.json', record) + return record + + +def verify_stage(target): + target = Path(target) + record = json.loads((target / 'checkpoint_source.json').read_text()) + for name, expected in record['files'].items(): + if Path(name).name != name or digest(target / name) != expected: + raise ValueError(f'Staged checkpoint changed: {name}') + model_files(target) + return record + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('command', choices=['verify']) + parser.add_argument('directory', type=Path) + args = parser.parse_args() + record = verify_stage(args.directory) + print(f"Verified checkpoint step {record['checkpoint']['step']}: {args.directory}") diff --git a/04-data-agent/train/continue_allocation.py b/04-data-agent/train/continue_allocation.py new file mode 100644 index 0000000..cbd7594 --- /dev/null +++ b/04-data-agent/train/continue_allocation.py @@ -0,0 +1,237 @@ +"""Resume a frozen multi-harness run after its allocation, without resetting training. + +Run ``execute`` in a CPU Slurm job with afterany: dependency. Preparation +reuses the parent's immutable source and tasks; GPU submission uses its existing +launcher. Explicit cancellation and STOP_AFTER_STEP are respected. +""" +from __future__ import annotations + +import argparse +from copy import deepcopy +from datetime import datetime, timezone +import fcntl +import importlib.util +import json +import os +from pathlib import Path +import shlex +import shutil +import subprocess +import sys + +ROLES = ("training", "cleanup", "eval_watcher", "monitor", "logging") +RESUMABLE_STATES = {"COMPLETED", "TIMEOUT", "NODE_FAIL", "BOOT_FAIL", "PREEMPTED"} + + +def read(path): + return json.loads(Path(path).read_text()) + + +def save(path, value): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + tmp = path.with_suffix(path.suffix + ".tmp") + tmp.write_text(json.dumps(value, indent=2) + "\n") + tmp.replace(path) + + +def checkpoint_validator(parent): + source = parent / "source-snapshot/HuggingEnvs/04-data-agent/train/checkpoint_artifacts.py" + spec = importlib.util.spec_from_file_location("continuation_checkpoint_artifacts", source) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module.resume_info + + +def select_checkpoint(parent, job, config, validate=None): + validate = validate or checkpoint_validator(parent) + candidates = [p for p in (parent / f"job-{job}/run").glob("checkpoint-*") + if p.is_dir() and p.name.removeprefix("checkpoint-").isdigit()] + rejected = [] + for path in sorted(candidates, key=lambda p: int(p.name.split("-")[-1]), reverse=True): + try: + resume = validate(path, config["model"], config["model_revision"]) + if resume["step"] != int(path.name.split("-")[-1]): + raise ValueError("Checkpoint directory and saved step disagree") + return resume, rejected + except (ValueError, OSError, KeyError) as exc: + rejected.append({"checkpoint": str(path), "reason": str(exc)}) + raise ValueError(f"No valid full checkpoint found; rejected={rejected}") + + +def may_continue(parent, job, state): + stopped = (parent / f"job-{job}/STOP_AFTER_STEP").exists() + stopped |= (parent / "operations/allocation-continuation/STOP").exists() + return not stopped and state in RESUMABLE_STATES + + +def prepare(parent, output, resume, *, walltime="24:00:00", seconds=82200): + config = deepcopy(read(parent / "run_config.json")) + if seconds <= 0 or seconds >= 24 * 3600: + raise ValueError("The soft training limit must leave room before the 24-hour allocation ends") + output.mkdir(parents=True, exist_ok=False) + for name in ("manifest.json", "indices.txt", "harness_schedule.json", "pairs.jsonl", + "runtime_versions.json", "source_hashes.json"): + shutil.copy2(parent / name, output / name) + if (parent / "schedule_summary.json").exists(): + shutil.copy2(parent / "schedule_summary.json", output / "schedule_summary.json") + # No working-tree overlays: the continuation runs the identical frozen code. + snapshot = output / "source-snapshot" + snapshot.symlink_to((parent / "source-snapshot").resolve(), target_is_directory=True) + evaluation = output / "checkpoint-evals/eval-source" + evaluation.parent.mkdir() + evaluation.symlink_to((parent / "checkpoint-evals/eval-source").resolve(), target_is_directory=True) + config.update(status="prepared", source_snapshot=str(snapshot), frozen_eval_source=str(evaluation), + restart_of=str(parent), resume_state=resume, + restart_reason="User-authorized allocation continuation; running-job extension denied by Slurm", + initialization="Full checkpoint: model, optimizer, scheduler, RNG and rollout cursor") + config.pop("job_id", None) + config.pop("replaced_by", None) + config.pop("allocation_continuation", None) + config["training"].update(resume_from_checkpoint=resume["checkpoint"], soft_max_train_seconds=seconds) + config["resources"]["slurm_walltime"] = walltime + config["dataset"]["schedule_file"] = str(output / "harness_schedule.json") + config["evaluation"]["protocol_file"] = str(evaluation / "protocol.json") + # Preserve the same Trackio project and evaluation curve; training-{job} records + # distinguish allocation histories while retaining global optimizer step numbers. + config["logging"].update(local_directory=str(output / "trackio"), + collector_file=str(snapshot / "tools/trackio_multi4.py")) + config["monitoring"]["stable_after_optimizer_step"] = resume["step"] + 3 + config["monitoring"]["support_job_supervisor"].update( + host="Slurm CPU job", status_file=str(output / "supervisor/status.json"), + source_file=str(snapshot / "tools/supervise_multi4.py")) + save(output / "run_config.json", config) + save(output / "validation.json", {"prepared": True, "resume": resume, + "parent_validation_file": str(parent / "validation.json"), + "live_resume_validated": False, "frozen_source_reused": True}) + save(output / "operations/ALLOCATION_CONTINUATION.json", { + "parent": str(parent), "resume": resume, "frozen_source": str(snapshot.resolve()), + "changed_training_fields": ["resume_from_checkpoint", "soft_max_train_seconds"], + "gpu_walltime": walltime, "target_step": config["training"]["max_steps"]}) + return config + + +def preflight(output): + env = {**os.environ, "TRAIN_RUN_ROOT": str(output), "MULTI4_PREFLIGHT_ONLY": "1", + "SLURM_JOB_ID": "preflight"} + log = output / "operations/continuation-preflight.log" + with log.open("w") as stream: + subprocess.run(["bash", str(output / "source-snapshot/tools/launch_multi4_long.sh")], + env=env, stdout=stream, stderr=subprocess.STDOUT, check=True) + + +def parent_state(job): + result = subprocess.check_output( + ["sacct", "-X", "-n", "-P", "-j", str(job), "--format=JobIDRaw,State"], text=True) + for line in result.splitlines(): + fields = line.split("|") + if fields[0] == str(job): + return fields[1].split()[0].rstrip("+") + return "UNKNOWN" + + +def existing_submission(output): + path = output / "submission.json" + if not path.exists(): + return None + record = read(path) + if not all(str(record.get(k, "")).isdigit() for k in ROLES): + raise RuntimeError("Incomplete submission record: reconcile Slurm before retrying; no duplicate GPU job submitted") + return record + + +def execute(parent, output, expected_job): + operations = parent / "operations/allocation-continuation" + operations.mkdir(parents=True, exist_ok=True) + status_path = operations / "status.json" + with (operations / ".lock").open("w") as lock: + fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) + status = read(status_path) if status_path.exists() else {} + try: + actual_job = str(read(parent / "submission.json")["training"]) + if actual_job != str(expected_job): + raise ValueError("Parent training job changed") + state = parent_state(actual_job) + if not may_continue(parent, actual_job, state): + status.update(state="skipped", reason="Parent is not eligible for automatic allocation continuation", + parent_state=state) + save(status_path, status) + return status + config = read(parent / "run_config.json") + resume, rejected = select_checkpoint(parent, actual_job, config) + if resume["step"] >= config["training"]["max_steps"]: + status.update(state="complete", reason="Parent already reached target step", resume=resume) + save(status_path, status) + return status + if not output.exists(): + prepare(parent, output, resume) + else: + prepared = read(output / "run_config.json") + if prepared.get("restart_of") != str(parent) or prepared.get("resume_state") != resume: + raise ValueError("Existing continuation directory has different provenance") + status.update(state="prepared", resume=resume, rejected_checkpoints=rejected, + output=str(output), parent_job=actual_job) + save(status_path, status) + submission = existing_submission(output) + if submission is None: + preflight(output) + env = {k: v for k, v in os.environ.items() if k not in { + "MULTI4_PREFLIGHT_ONLY", "TRAIN_RUN_ROOT", "TRAIN_JOB_ID", "CODE_ROOT", "TRAIN_AUDIT_TOOLS"}} + with (output / "operations/submission.log").open("a") as stream: + subprocess.run([sys.executable, str(output / "source-snapshot/tools/submit_multi4_long.py"), + "--run", str(output), "--submit"], env=env, + stdout=stream, stderr=subprocess.STDOUT, check=True) + submission = existing_submission(output) + status.update(state="submitted", submission=submission) + save(status_path, status) + if not status.get("supervisor_job"): + intent = operations / "supervisor-submission.json" + if intent.exists(): + raise RuntimeError("Reconcile existing supervisor submission intent before retrying") + command = [sys.executable, "-u", str(output / "source-snapshot/tools/supervise_multi4.py"), + "--run", str(output)] + save(intent, {"state": "submitting", "command": command}) + job = subprocess.check_output(["sbatch", "--parsable", "--partition=hopper-cpu", + "--ntasks=1", "--cpus-per-task=1", "--mem=2G", "--time=48:00:00", + "--job-name=multi4-support-supervisor", "--output=/fsx/%u/logs/%x-%j.out", + "--error=/fsx/%u/logs/%x-%j.err", "--wrap", shlex.join(command)], text=True).strip().split(";")[0] + if not job.isdigit(): + raise RuntimeError("Unrecognized supervisor submission response") + status["supervisor_job"] = job + save(intent, {"state": "submitted", "job": job}) + status.update(state="submitted", checked_at=datetime.now(timezone.utc).isoformat()) + save(status_path, status) + return status + except Exception as exc: + status.update(state="error", error=str(exc), checked_at=datetime.now(timezone.utc).isoformat()) + save(status_path, status) + raise + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("action", choices=["inspect", "prepare", "execute"]) + parser.add_argument("--parent", type=Path, required=True) + parser.add_argument("--output", type=Path) + parser.add_argument("--parent-job", required=True) + args = parser.parse_args() + parent = args.parent.resolve() + if args.action == "execute": + if args.output is None: + parser.error("--output is required") + result = execute(parent, args.output.resolve(), args.parent_job) + else: + config = read(parent / "run_config.json") + resume, rejected = select_checkpoint(parent, args.parent_job, config) + result = {"resume": resume, "rejected_checkpoints": rejected} + if args.action == "prepare": + if args.output is None: + parser.error("--output is required") + prepare(parent, args.output.resolve(), resume) + preflight(args.output.resolve()) + result.update(prepared=str(args.output.resolve()), preflight_passed=True, submitted=False) + print(json.dumps(result, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/train/harbor_reward.py b/04-data-agent/train/harbor_reward.py new file mode 100644 index 0000000..fb81c3a --- /dev/null +++ b/04-data-agent/train/harbor_reward.py @@ -0,0 +1,128 @@ +"""Trainer-side reward: correctness from Harbor's verifier, efficiency from the trace. + + reward = correctness x (1 + W_EFF * TOOL_BUDGET / (TOOL_BUDGET + n_tool_calls)) + +MULTIPLICATIVE, NOT ADDITIVE-WITH-A-GATE. An efficiency bonus that is merely ADDED has a trapdoor: +zero tool calls scores the MAXIMUM efficiency, so for a policy that cannot solve the task, doing +nothing becomes the best move available (0.300 vs 0.030 for a real attempt that fails). The policy +stops calling tools, `train_turn_fn=has_tool_call` then yields no trainable turns, and the group is +empty. Jobs 72452 and 72473 wedged at step 7 and 10 of 100 exactly this way, spending 4,076 E2B +sandboxes on 11 productive groups. A `if correct` gate patches that; multiplying STRUCTURALLY removes +it -- `correctness == 0` zeroes the product, and reward is monotone non-decreasing in BOTH arguments, +so efficiency can never be traded for correctness. The property survives refactoring; a gate may not. + +WHY IT IS COMPUTED HERE AND NOT IN THE VERIFIER. A reward belongs in the sandbox only if the sandbox +is what makes it computable. Correctness needs the data, the gold answer and the tolerances. A +tool-call count needs the TRACE, which lives in the capture proxy. The suite's own grader tried to +read it from `/workdir/.n_tool_calls` and `$N_TOOL_CALLS`; nothing writes either, so it emitted `null` +forever, Harbor's `dict[str, float|int]` rejected the whole dict, and `correctness` went down with it +-- 86 of 250 tasks silently unscored until that was fixed at source (dataset rev 291c8e50). + +WHY 1/(1+n/B) AND NOT THE REFERENCE'S clip(1 - n/B). Measured over run 77284 (Qwen3.5-2B, opencode, +118 logged steps), tool calls per rollout: p10 8.4, p50 14.7, p75 25.5, p90 56.6, max 125.5 -- a 15x +range, drifting 13.1 -> 43.0 between the first and last 30 steps because nothing bounded it. A linear +clamp cannot be both sensitive at 10 and unsaturated at 100: + + n_tool_calls 5 8 15 30 66 95 125 + clip(1-n/15) 0.667 0.467 0.000 0.000 0.000 0.000 0.000 <- inert above the MEDIAN + clip(1-n/60) 0.917 0.867 0.750 0.500 0.000 0.000 0.000 <- inert exactly where it is needed + 15/(15+n) 0.750 0.652 0.500 0.333 0.185 0.136 0.107 <- graded across the whole range + +The reciprocal keeps a gradient everywhere, and keeps it strongest near the budget, which is where we +want the policy to land. `TOOL_BUDGET` stays the reference's 15 and now reads as a half-credit point +rather than a cliff: efficiency is 0.5 at exactly 15 calls. + +THE SECOND REASON, WHICH IS THE LARGER ONE. 19 of 118 steps in run 77284 logged `reward_std == 0`, +and ALL NINETEEN were groups where every generation SOLVED the task. Under pure-correctness reward an +all-correct group has zero advantage for every member: 8 sandboxes, no gradient, 16% of the run. Those +generations were not identical -- they differed in how long they took. Efficiency makes precisely +those groups trainable. This term buys signal from rollouts already paid for. + +EFFICIENCY IS A TIE-BREAKER, NOT A COMPETING OBJECTIVE. Over the observed range the efficiency term +moves the reward by at most 0.3 x (0.750 - 0.107) = 0.193, against the 1.0 swing of correctness. When +a group disagrees about correctness, correctness dominates ~5:1; only when it agrees does efficiency +decide. That ratio is the design, so keep `W_EFF` well under 1. + +A SOFT INCENTIVE IS NOT A BOUND. This shapes behaviour over many steps; it does not stop one rollout +running 235 turns (77284's observed `turns_max`) and blowing the packed row, which grows with the +SQUARE of the turn count -- `row_tokens_max` reached 40,598 against a 40,960 budget. The hard bound is +`max_model_calls` in the capture proxy, the only harness-agnostic step limit, since only 1 of 29 seams +honours `agent_step_limit` at all. Ship both; this one alone will not save the row. + +Reads `train/tools/call_frequency` in trackio, which is `float(n_calls)` per rollout +(`async_rollout_worker.py:1052`) -- the exact quantity this reward acts on, already on the dashboard. +""" + +from __future__ import annotations + +import logging +import os +from typing import TYPE_CHECKING + +if TYPE_CHECKING: # annotation-only, so this module imports (and unit-tests) without torch or vLLM + from trl.experimental.async_grpo.openenv_harness import HarnessRolloutOutcome + +logger = logging.getLogger(__name__) + +W_EFF = float(os.environ.get("REWARD_W_TOOL_EFFICIENCY", "0.3")) +TOOL_BUDGET = float(os.environ.get("TOOL_BUDGET", "15")) + +_announced = False + + +def tool_efficiency(n_tool_calls: int | None) -> float | None: + """`B / (B + n)` in `(0, 1]`, or `None` when the count is unknown. + + Args: + n_tool_calls (`int`, *optional*): + Tool calls the agent made across every real turn, framework aux calls already dropped. + + Returns: + `float` or `None`: `1.0` at zero calls, `0.5` at exactly `TOOL_BUDGET`, asymptotically `0` -- + never actually `0`, which is what keeps a gradient in the 60-125 call regime where the + reference's `clip(1 - n/15)` is flat. + """ + if n_tool_calls is None or TOOL_BUDGET <= 0: + return None + return TOOL_BUDGET / (TOOL_BUDGET + max(0, int(n_tool_calls))) + + +def data_agent_reward(outcome: "HarnessRolloutOutcome") -> float | None: + """`correctness x (1 + W_EFF * efficiency)`, or `None` when the rollout is unscorable. + + Args: + outcome (`HarnessRolloutOutcome`): + What the rollout produced -- verifier reward, transcript, tool-call counts, timeout flag. + + Returns: + `float` or `None`: `None` means UNSCORABLE and DROPS the rollout from its group baseline. + Scoring it `0.0` would teach the policy that a crashed sandbox is as bad as a wrong + answer, and would poison the baseline with a value nothing produced. + """ + global _announced + + correctness = outcome.env_reward + if correctness is None: + logger.warning( + "verifier did not run (tool_calls=%d); rollout unscorable, dropped from the baseline", + outcome.tool_call_count, + ) + return None + + correctness = float(correctness) + if not _announced: + _announced = True + logger.warning( + "reward = correctness x (1 + %.2f * %.0f/(%.0f + tool_calls)); " + "max %.3f at 0 calls, %.3f at %.0f calls, ->1.0 as calls->inf", + W_EFF, TOOL_BUDGET, TOOL_BUDGET, 1.0 + W_EFF, 1.0 + W_EFF * 0.5, TOOL_BUDGET, + ) + if outcome.timed_out: + # Kept, not zeroed: the verifier graded whatever work landed, and that is a measurement. + logger.warning("agent timed out; keeping the verifier's %.3f on the partial work", correctness) + + eff = tool_efficiency(outcome.tool_call_count) + if eff is None or correctness <= 0.0: + # Nothing to scale, or scaling would invert the sign on a negative verifier score. + return correctness + return correctness * (1.0 + W_EFF * eff) diff --git a/04-data-agent/train/harness_schedule.py b/04-data-agent/train/harness_schedule.py new file mode 100644 index 0000000..75aeb18 --- /dev/null +++ b/04-data-agent/train/harness_schedule.py @@ -0,0 +1,82 @@ +"""Deterministic, difficulty-balanced task/harness rotation without changing TRL.""" +from collections import Counter +import random + + +def make_schedule(tasks, harnesses, *, seed=0, easy_start=32): + n, h = len(tasks), len(harnesses) + if not n or not h or n % h or len(set(harnesses)) != h: + raise ValueError('A balanced rotation requires distinct harnesses and a task count divisible by them') + if not 0 <= easy_start <= n or easy_start % h: + raise ValueError('The easy introduction must contain complete rounds of harnesses') + if any(t['difficulty'] != 'easy' for t in tasks[:easy_start]): + raise ValueError('The introduction must consist of easy tasks') + base, cursor = {}, 0 + for tier in ['easy', 'medium', 'hard']: + for row, task in enumerate(tasks): + if task['difficulty'] == tier: + base[row] = cursor % h + cursor += 1 + if len(base) != n: + raise ValueError('Unknown task difficulty') + groups = [] + for pass_index in range(h): + rng = random.Random(seed + pass_index) + introduction = list(range(easy_start)) if pass_index == 0 else [] + buckets = [[] for _ in harnesses] + for row in range(n): + if row not in introduction: + buckets[(base[row] + pass_index) % h].append(row) + for bucket in buckets: + rng.shuffle(bucket) + order = introduction + [bucket[i] for i in range(len(buckets[0])) for bucket in buckets] + for row in order: + groups.append({'group_in_cycle': len(groups), 'pass_index': pass_index, + 'task_row': row, 'task_index': tasks[row]['task_index'], + 'task_name': tasks[row]['name'], 'difficulty': tasks[row]['difficulty'], + 'harness': harnesses[(base[row] + pass_index) % h]}) + result = {'schema_version': 1, 'mode': 'one_harness_per_task_per_pass', 'seed': seed, + 'harnesses': harnesses, 'tasks': tasks, 'task_count': n, + 'groups_per_pass': n, 'passes_per_cycle': h, 'groups_per_cycle': len(groups), + 'easy_start_task_count': easy_start, 'groups': groups} + validate_schedule(result) + return result + + +def validate_schedule(schedule): + tasks, harnesses, groups = schedule['tasks'], schedule['harnesses'], schedule['groups'] + n, h = len(tasks), len(harnesses) + if n == 0 or h == 0 or n % h or len(groups) != n * h or len(set(harnesses)) != h: + raise ValueError('Incomplete rotation cycle') + expected_metadata = {'schema_version': 1, 'mode': 'one_harness_per_task_per_pass', + 'task_count': n, 'groups_per_pass': n, 'passes_per_cycle': h, + 'groups_per_cycle': n * h} + if any(schedule.get(k) != v for k, v in expected_metadata.items()): + raise ValueError('Schedule metadata disagrees with the rotation') + if any(t['difficulty'] not in {'easy', 'medium', 'hard'} for t in tasks): + raise ValueError('Unknown task difficulty') + if len({t['name'] for t in tasks}) != n or len({t['task_index'] for t in tasks}) != n: + raise ValueError('Duplicate training tasks') + pairs = set() + for p in range(h): + section = groups[p * n:(p + 1) * n] + if {g['task_row'] for g in section} != set(range(n)): + raise ValueError('Each pass must contain every task exactly once') + if Counter(g['harness'] for g in section) != Counter({name: n // h for name in harnesses}): + raise ValueError('Harness counts are not balanced') + for tier in ['easy', 'medium', 'hard']: + counts = [sum(g['harness'] == name and g['difficulty'] == tier for g in section) + for name in harnesses] + if max(counts) - min(counts) > 1: + raise ValueError('Difficulty counts are not balanced') + for i, g in enumerate(section): + row = g['task_row'] + task = tasks[row] + if (g['group_in_cycle'] != p * n + i or g['pass_index'] != p + or g['task_name'] != task['name'] or g['task_index'] != task['task_index'] + or g['difficulty'] != task['difficulty']): + raise ValueError('Group identity disagrees with task metadata') + pairs.add((row, g['harness'])) + if len(pairs) != n * h: + raise ValueError('Rotation repeats a task/harness pair') + return schedule diff --git a/04-data-agent/train/launch.slurm b/04-data-agent/train/launch.slurm new file mode 100755 index 0000000..69de3cf --- /dev/null +++ b/04-data-agent/train/launch.slurm @@ -0,0 +1,207 @@ +#!/bin/bash +#SBATCH --job-name=da-blackbox-2b +#SBATCH --ntasks-per-node=1 +#SBATCH --gres=gpu:2 +#SBATCH --partition=hopper-extra +#SBATCH --output=/fsx/%u/logs/%x-%j.out +#SBATCH --error=/fsx/%u/logs/%x-%j.err +#SBATCH --time=0-12:00:00 +set -euo pipefail + +# One job, three processes, in this order and for these reasons: +# +# GPU 0 vLLM, with the capture flags. The TRAINER's engine -- the agent calls the same weights the +# optimizer updates, which is what makes the rollouts on-policy. +# GPU 1 the trainer. +# cpu the env server, which starts the capture proxy and publishes it. +# +# The sandboxes must reach CAPTURE, not vLLM: opencode is pointed at the proxy and its api key is the +# capture session id, so vLLM is never exposed and never sees a sandbox. Capture is published with a +# gradio tunnel because E2B runs off-cluster; `direct` only works for a sandbox on this host and fails +# silently -- opencode cannot reach the engine, makes zero model calls, and the rollout returns an +# empty answer that grades exactly like a model that could not do the task. + +REPO=/fsx/$USER/projects/trl_prod +ENVDIR=$REPO/HuggingEnvs/04-data-agent/envs/blackbox-opencode +TRAINDIR=$REPO/HuggingEnvs/04-data-agent/train + +# TWO INTERPRETERS, ON PURPOSE. +# .venv312 torch / vLLM / TRL. The heavy stack, shared and reproducible via install.sh. +# $ENVDIR/.venv the environment server only. Deliberately light -- no torch, no vLLM. +# The trainer needs two LIGHT packages that .venv312 does not carry: the local `openenv` (the capture +# stack is in no release) and `data_agent_env`. Both go on PYTHONPATH rather than being pip-installed, +# because install.sh owns what is in .venv312 and an ad hoc install there stops being reproducible. +# `data_agent_env` needs the symlink in _pypath/: the directory is named `blackbox-opencode`, which is +# not a legal Python identifier, so PYTHONPATH alone cannot reach it. +PY312=$REPO/.venv312/bin/python +export PYTHONPATH="$TRAINDIR/_pypath:$REPO/OpenEnv/src${PYTHONPATH:+:$PYTHONPATH}" +export TRL_EXPERIMENTAL_SILENCE=1 + +cd "$ENVDIR" + +MODEL="${MODEL:-Qwen/Qwen3.5-2B}" + +# PORTS DERIVED PER JOB. Fixed ports are a trap on a shared node and it is not a theoretical one: +# two of these jobs landed on ip-10-53-93-25 together, the second one's /server_info probe answered +# 200 because it was talking to the FIRST job's vLLM, and the NCCL weight-transfer group then tried to +# attach to another job's engine and died with "NCCL error: unhandled cuda error". Every preflight +# check passed while pointing at the wrong process. +# +# Worse, had the models matched, it would have trained one job against the other's weights and looked +# perfectly healthy. The reference launcher derives ports the same way and says so. +_OFF=$(( ${SLURM_JOB_ID:-0} % 200 * 4 )) +VLLM_PORT="${VLLM_PORT:-$(( 18000 + _OFF ))}" +ENV_PORT="${ENV_PORT:-$(( 18001 + _OFF ))}" +CAP_PORT="${CAP_PORT:-$(( 18002 + _OFF ))}" +VLLM_URL="http://127.0.0.1:$VLLM_PORT" +LOGS="$ENVDIR/logs/job-${SLURM_JOB_ID:-local}" +mkdir -p "$LOGS/run" + +# BRIDGE THE CHECKPOINTS TO THE EVAL WATCHER. +# +# eval_watcher.py polls experiments/temp_asyncgrpo_code/logs/ckpt-/checkpoint-* . Without +# this link it never sees a checkpoint from this run, and the 400-step job would finish with nothing +# evaluated -- silently, because "no checkpoints yet" and "wrong directory" look identical to a poller. +# +# The link, rather than writing there directly: the run's artifacts belong with the environment, and +# the eval stack is the one that produced the +0.2028 reference, so reusing it keeps the comparison +# apples-to-apples instead of introducing a second evaluator. +WATCHDIR=/fsx/$USER/projects/trl_prod/experiments/temp_asyncgrpo_code/logs/ckpt-${SLURM_JOB_ID:-local} +ln -sfn "$LOGS/run" "$WATCHDIR" +echo "checkpoints visible to eval_watcher at $WATCHDIR -> $LOGS/run" + +set -a; . "$REPO/experiments/.env"; set +a +. "$ENVDIR/../blackbox-harbor/tools/hf_token.sh" + +cleanup() { pkill -P $$ 2>/dev/null || true; } +trap cleanup EXIT + +# SAMPLING, FOR THE ENGINE AND THE TRAINER, FROM ONE VARIABLE SO THEY CANNOT DRIFT. +# opencode sends NO sampling parameters, so whatever the engine defaults to is what actually +# generated the actions -- and Qwen3.5-2B ships no generation_config.json, so that default is +# vLLM's own 1.0/1.0, NOT the 0.8 the trainer divides logits by when it recomputes logprobs for +# the importance ratio. Leaving the engine unpinned means the gradient is computed against a +# distribution that never produced the samples. Measured, unpinned: entropy climbed 0.229 -> 0.587 +# over 24 steps while reward fell 0.592 -> 0.216, with turns/mean 10.4 against the reference's 6.1 +# (job 76577). The reference pins both from one variable (run_cluster.py:165) and holds entropy +# flat at 0.17-0.21. top_k=-1 because top_k truncation is likewise unmodelled by the recomputation. +TEMPERATURE="${TEMPERATURE:-0.8}" +TOP_P="${TOP_P:-0.95}" + +echo "== vLLM on GPU 0 == (ports vllm=$VLLM_PORT env=$ENV_PORT capture=$CAP_PORT, derived from job ${SLURM_JOB_ID:-0})" +# EVERY ONE OF THESE FLAGS IS LOad-BEARING. They are the set the +0.2028 run served with; a +# hand-rolled `vllm serve` that omits any of them fails silently rather than loudly. +# +# --enable-auto-tool-choice / --tool-call-parser qwen3_xml +# Without them vLLM never parses tool calls, so `has_tool_call` is False for EVERY turn and the +# whole rollout is discarded with no error anywhere. The agent path is entirely tool calls. +# --default-chat-template-kwargs {"enable_thinking": false} +# Pins thinking OFF server-side. Qwen3.5-4B's template opens by default and -2B's +# closes it -- INVERTED defaults between two models of the same family. Leaving it to the +# default is what made a re-rendered prompt match the 2B and diverge on the 4B, forking 100% of +# turn transitions. This is what `think_template_patch` used to monkeypatch; it belongs here. +# --return-tokens-as-token-ids / --logprobs-mode processed_logprobs +# The capture tier. Without them capture degrades to text and every rollout is untrainable -- +# and looks completely normal while being so. +# --gdn-prefill-backend triton +# Qwen3.5 is hybrid Gated-DeltaNet; this is the working prefill backend for it. +# VLLM_USE_DEEP_GEMM=0 / VLLM_DEEP_GEMM_WARMUP=skip +# On Hopper a stale importable deep_gemm kills startup with "DeepGEMM backend is not available +# or outdated". +# VLLM_SERVER_DEV_MODE=1 +# Gates /server_info, /pause and /init_weight_transfer_engine. TRL's weight transfer reads +# /server_info for the server's dtype, so WITHOUT this the trainer dies at on_train_begin with +# "404 Client Error: Not Found for url: .../server_info?config_format=json" -- after vLLM is +# healthy, after the env server is up, after the dataset loads. Job 76488 died exactly there. +# VLLM_USE_FLASHINFER_SAMPLER=0 +# flashinfer JIT-compiles its sampling kernel and needs nvcc; the PyTorch sampler does not. +# --weight-transfer-config {"backend":"nccl"} +# The trainer syncs weights into this engine over NCCL. Without it there is no transfer engine +# to initialise. +CUDA_VISIBLE_DEVICES=0 \ + VLLM_SERVER_DEV_MODE=1 \ + VLLM_USE_FLASHINFER_SAMPLER=0 \ + VLLM_USE_DEEP_GEMM=0 VLLM_DEEP_GEMM_WARMUP=skip \ + "$PY312" -m vllm.entrypoints.openai.api_server \ + --model "$MODEL" --port "$VLLM_PORT" --served-model-name "$MODEL" \ + --trust-remote-code \ + --max-model-len "${MAX_MODEL_LEN:-131072}" \ + --enable-auto-tool-choice --tool-call-parser qwen3_xml \ + --reasoning-parser qwen3 \ + --gdn-prefill-backend triton \ + --default-chat-template-kwargs '{"enable_thinking": false}' \ + --return-tokens-as-token-ids --logprobs-mode processed_logprobs \ + --override-generation-config "{\"temperature\": $TEMPERATURE, \"top_p\": $TOP_P, \"top_k\": -1}" \ + --weight-transfer-config '{"backend":"nccl"}' \ + > "$LOGS/vllm.log" 2>&1 & + +# vLLM's /health returns 200 with an EMPTY body, so check the STATUS and parse nothing. A probe that +# json.loads() it throws, and a caller treating that as "not ready" waits out the whole budget on a +# server that came up minutes ago. +echo "waiting for vLLM (up to 30 min: weights + torch.compile)" +for _ in $(seq 1 900); do + code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 "$VLLM_URL/health" || true) + [ "$code" = "200" ] && break + sleep 2 +done +[ "$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$VLLM_URL/health")" = "200" ] || { + echo "FATAL: vLLM never became healthy; its log follows"; tail -60 "$LOGS/vllm.log"; exit 1; } +echo "vLLM healthy" +# CHECK THE ENDPOINT THE TRAINER ACTUALLY NEEDS, not just /health. /health passing says nothing about +# whether VLLM_SERVER_DEV_MODE took effect, and the trainer only finds out ~5 minutes later at +# on_train_begin, after the env server and the whole dataset have loaded. +si=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$VLLM_URL/server_info?config_format=json" || true) +[ "$si" = "200" ] || { echo "FATAL: /server_info -> $si; VLLM_SERVER_DEV_MODE=1 did not take effect."; exit 1; } +# Confirm the engine on this port is OURS. Reachability is not identity: a neighbouring job's vLLM +# answers /health and /server_info exactly the same way, and the first sign of talking to the wrong +# one is an NCCL failure minutes later -- or, if the models happen to match, no sign at all. +served=$(curl -s --max-time 10 "$VLLM_URL/v1/models" | tr -d ' \n' | grep -o "\"id\":\"[^\"]*\"" | head -1) +echo "vLLM /server_info ok; serving $served on :$VLLM_PORT" + +echo "== env server + capture ==" +PORT="$ENV_PORT" CAPTURE_PORT="$CAP_PORT" EXPOSE=gradio \ + SPLITS="${SPLITS:-train}" SANDBOX="${SANDBOX:-e2b}" \ + MAX_CONCURRENT="${MAX_CONCURRENT:-48}" \ + LLM_URL="$VLLM_URL/v1" MODEL="$MODEL" \ + ./serve.sh > "$LOGS/env.log" 2>&1 & + +for _ in $(seq 1 180); do + curl -s -o /dev/null --max-time 3 "http://127.0.0.1:$ENV_PORT/health" && break + sleep 2 +done +curl -s -o /dev/null --max-time 5 "http://127.0.0.1:$ENV_PORT/health" || { + echo "FATAL: env server never became healthy; its log follows"; tail -60 "$LOGS/env.log"; exit 1; } +grep -a "capture proxy ready" "$LOGS/env.log" || echo "WARNING: capture did not warm at boot" +echo "env server healthy" + +# max_inflight 32 is the REFERENCE value, and it matters beyond throughput: at 16 this run +# measured samples/step 7.2 against the reference's 16.1, i.e. less than half the training +# signal per optimizer step, which over 400 steps is a different experiment rather than a +# slower one. 16 was chosen after an EVAL collapsed at 64 concurrent MCP clients -- but that +# eval ran ~16-turn rollouts (64 x 16 = ~1024 concurrent turn-slots) where training runs ~8.5 +# (32 x 8.5 = ~272), about a quarter of the load that broke it. +echo "== trainer on GPU 1 ==" +# NOT from $REPO: the `trl/` submodule DIRECTORY shadows the `trl` package as a namespace package +# there, and the import dies with "cannot import name '__version__' from trl (unknown location)". +cd "$TRAINDIR" +CUDA_VISIBLE_DEVICES=1 TRACKIO_STORAGE_MODE=sqlite \ + "$PY312" -u train_blackbox_opencode.py \ + --server "http://127.0.0.1:$ENV_PORT" \ + --vllm-url "$VLLM_URL" \ + --model "$MODEL" \ + --split "${SPLITS:-train}" \ + --sandbox "${SANDBOX:-e2b}" \ + --learning-rate "${LR:-3e-6}" \ + --temperature "$TEMPERATURE" \ + --num-generations "${NUM_GENERATIONS:-8}" \ + --max-inflight "${MAX_INFLIGHT:-32}" \ + --grad-accum "${GRAD_ACCUM:-4}" \ + --curriculum "${CURRICULUM:-warmup:125}" \ + --agent-step-limit "${AGENT_STEP_LIMIT:-17}" \ + --token-budget "${TOKEN_BUDGET:-40960}" \ + --max-completion-length "${MAX_COMPLETION_LENGTH:-16384}" \ + --heartbeat-stale-after-s "${HEARTBEAT_STALE_AFTER_S:-900}" \ + --dtype "${TRAIN_DTYPE:-bfloat16}" \ + --max-steps "${MAX_STEPS:-400}" \ + --save-steps "${SAVE_STEPS:-100}" \ + --output-dir "$LOGS/run" diff --git a/04-data-agent/train/launch_harbor_multi.slurm b/04-data-agent/train/launch_harbor_multi.slurm new file mode 100644 index 0000000..4fffbaf --- /dev/null +++ b/04-data-agent/train/launch_harbor_multi.slurm @@ -0,0 +1,329 @@ +#!/bin/bash +#SBATCH --job-name=agrpo-harbor-multi +#SBATCH --ntasks-per-node=1 +#SBATCH --gres=gpu:2 +#SBATCH --partition=hopper-extra +#SBATCH --output=/fsx/%u/logs/%x-%j.out +#SBATCH --error=/fsx/%u/logs/%x-%j.err +#SBATCH --time=0-12:00:00 +set -euo pipefail + +# One job, three processes, in this order and for these reasons: +# +# GPU 0 vLLM, with the capture flags. The TRAINER's engine -- the agent calls the same weights the +# optimizer updates, which is what makes the rollouts on-policy. +# GPU 1 the trainer. +# cpu the env server, which starts the capture proxy and publishes it. +# +# The sandboxes must reach CAPTURE, not vLLM: opencode is pointed at the proxy and its api key is the +# capture session id, so vLLM is never exposed and never sees a sandbox. Capture is published with a +# gradio tunnel because E2B runs off-cluster; `direct` only works for a sandbox on this host and fails +# silently -- opencode cannot reach the engine, makes zero model calls, and the rollout returns an +# empty answer that grades exactly like a model that could not do the task. + +REPO=/fsx/$USER/projects/trl_prod +CODE_ROOT="${CODE_ROOT:-$REPO}" +ENVDIR=$REPO/HuggingEnvs/04-data-agent/envs/blackbox-opencode +TRAINDIR=$CODE_ROOT/HuggingEnvs/04-data-agent/train + +# TWO INTERPRETERS, ON PURPOSE. +# .venv312 torch / vLLM / TRL. The heavy stack, shared and reproducible via install.sh. +# $ENVDIR/.venv the environment server only. Deliberately light -- no torch, no vLLM. +# The trainer needs two LIGHT packages that .venv312 does not carry: the local `openenv` (the capture +# stack is in no release) and `data_agent_env`. Both go on PYTHONPATH rather than being pip-installed, +# because install.sh owns what is in .venv312 and an ad hoc install there stops being reproducible. +# `data_agent_env` needs the symlink in _pypath/: the directory is named `blackbox-opencode`, which is +# not a legal Python identifier, so PYTHONPATH alone cannot reach it. +PY312=$REPO/.venv312/bin/python +# `harbor_env` lives under OpenEnv/envs, not OpenEnv/src -- the blackbox launcher never needed it. +export PYTHONPATH="$TRAINDIR:$TRAINDIR/_pypath:$CODE_ROOT/trl:$CODE_ROOT/OpenEnv/src:$CODE_ROOT/OpenEnv/envs${PYTHONPATH:+:$PYTHONPATH}" +export TRL_EXPERIMENTAL_SILENCE=1 + +cd "$ENVDIR" + +MODEL="${MODEL:-Qwen/Qwen3.5-2B}" + +# PORTS DERIVED PER JOB. Fixed ports are a trap on a shared node and it is not a theoretical one: +# two of these jobs landed on ip-10-53-93-25 together, the second one's /server_info probe answered +# 200 because it was talking to the FIRST job's vLLM, and the NCCL weight-transfer group then tried to +# attach to another job's engine and died with "NCCL error: unhandled cuda error". Every preflight +# check passed while pointing at the wrong process. +# +# Worse, had the models matched, it would have trained one job against the other's weights and looked +# perfectly healthy. The reference launcher derives ports the same way and says so. +_OFF=$(( ${SLURM_JOB_ID:-0} % 200 * 4 )) +VLLM_PORT="${VLLM_PORT:-$(( 18000 + _OFF ))}" +ENV_PORT="${ENV_PORT:-$(( 18001 + _OFF ))}" +CAP_PORT="${CAP_PORT:-$(( 18002 + _OFF ))}" +VLLM_URL="http://127.0.0.1:$VLLM_PORT" +LOGS="${TRAIN_LOGS:-$ENVDIR/logs/job-${SLURM_JOB_ID:-local}}" +mkdir -p "$LOGS/run" +export OPENENV_HARBOR_TRIALS_DIR="${OPENENV_HARBOR_TRIALS_DIR:-$LOGS/trials}" +IFS=',' read -ra JOB_GPUS <<< "${CUDA_VISIBLE_DEVICES:-0,1}" +[ "${#JOB_GPUS[@]}" -ge 2 ] || { echo "FATAL: this launcher needs two allocated GPUs"; exit 1; } + +# BRIDGE THE CHECKPOINTS TO THE EVAL WATCHER. +# +# eval_watcher.py polls experiments/temp_asyncgrpo_code/logs/ckpt-/checkpoint-* . Without +# this link it never sees a checkpoint from this run, and the 400-step job would finish with nothing +# evaluated -- silently, because "no checkpoints yet" and "wrong directory" look identical to a poller. +# +# The link, rather than writing there directly: the run's artifacts belong with the environment, and +# the eval stack is the one that produced the +0.2028 reference, so reusing it keeps the comparison +# apples-to-apples instead of introducing a second evaluator. +WATCHDIR=/fsx/$USER/projects/trl_prod/experiments/temp_asyncgrpo_code/logs/ckpt-${SLURM_JOB_ID:-local} +ln -sfn "$LOGS/run" "$WATCHDIR" +echo "checkpoints visible to eval_watcher at $WATCHDIR -> $LOGS/run" + +set -a; . "$REPO/experiments/.env"; set +a +. "$ENVDIR/../blackbox-harbor/tools/hf_token.sh" + +VLLM_PID="" +HARBOR_PID="" +cleanup() { + [ -z "$HARBOR_PID" ] || kill -TERM -- "-$HARBOR_PID" 2>/dev/null || true + [ -z "$VLLM_PID" ] || kill -TERM -- "-$VLLM_PID" 2>/dev/null || true +} +trap cleanup EXIT + +# SAMPLING, FOR THE ENGINE AND THE TRAINER, FROM ONE VARIABLE SO THEY CANNOT DRIFT. +# opencode sends NO sampling parameters, so whatever the engine defaults to is what actually +# generated the actions -- and Qwen3.5-2B ships no generation_config.json, so that default is +# vLLM's own 1.0/1.0, NOT the 0.8 the trainer divides logits by when it recomputes logprobs for +# the importance ratio. Leaving the engine unpinned means the gradient is computed against a +# distribution that never produced the samples. Measured, unpinned: entropy climbed 0.229 -> 0.587 +# over 24 steps while reward fell 0.592 -> 0.216, with turns/mean 10.4 against the reference's 6.1 +# (job 76577). The reference pins both from one variable (run_cluster.py:165) and holds entropy +# flat at 0.17-0.21. top_k=-1 because top_k truncation is likewise unmodelled by the recomputation. +TEMPERATURE="${TEMPERATURE:-0.8}" +# 1.0, NOT the reference launcher's 0.95. `--logprobs-mode processed_logprobs` takes the logprob +# AFTER truncation, so a truncating top_p renormalises every captured logprob over the kept set +# while the trainer recomputes over the full vocabulary -- the step-0 importance ratio then +# lands at kept_mass rather than 1, and the reordering is worse than a uniform shift. TRL's +# AsyncGRPOConfig already defaults top_p to 1.0, so 0.95 here is an ENGINE/TRAINER MISMATCH, +# not a policy choice. Measured: the reference sat at ratio 0.985-0.993 (the truncation +# signature); at 1.0 it moves to 0.9984-0.9999. +TOP_P="${TOP_P:-1.0}" + +echo "== vLLM on GPU 0 == (ports vllm=$VLLM_PORT env=$ENV_PORT capture=$CAP_PORT, derived from job ${SLURM_JOB_ID:-0})" +# EVERY ONE OF THESE FLAGS IS LOad-BEARING. They are the set the +0.2028 run served with; a +# hand-rolled `vllm serve` that omits any of them fails silently rather than loudly. +# +# --enable-auto-tool-choice / --tool-call-parser qwen3_xml +# Without them vLLM never parses tool calls, so `has_tool_call` is False for EVERY turn and the +# whole rollout is discarded with no error anywhere. The agent path is entirely tool calls. +# --default-chat-template-kwargs {"enable_thinking": false} +# Pins thinking OFF server-side. Qwen3.5-4B's template opens by default and -2B's +# closes it -- INVERTED defaults between two models of the same family. Leaving it to the +# default is what made a re-rendered prompt match the 2B and diverge on the 4B, forking 100% of +# turn transitions. This is what `think_template_patch` used to monkeypatch; it belongs here. +# --return-tokens-as-token-ids / --logprobs-mode processed_logprobs +# The capture tier. Without them capture degrades to text and every rollout is untrainable -- +# and looks completely normal while being so. +# --gdn-prefill-backend triton +# Qwen3.5 is hybrid Gated-DeltaNet; this is the working prefill backend for it. +# VLLM_USE_DEEP_GEMM=0 / VLLM_DEEP_GEMM_WARMUP=skip +# On Hopper a stale importable deep_gemm kills startup with "DeepGEMM backend is not available +# or outdated". +# VLLM_SERVER_DEV_MODE=1 +# Gates /server_info, /pause and /init_weight_transfer_engine. TRL's weight transfer reads +# /server_info for the server's dtype, so WITHOUT this the trainer dies at on_train_begin with +# "404 Client Error: Not Found for url: .../server_info?config_format=json" -- after vLLM is +# healthy, after the env server is up, after the dataset loads. Job 76488 died exactly there. +# VLLM_USE_FLASHINFER_SAMPLER=0 +# flashinfer JIT-compiles its sampling kernel and needs nvcc; the PyTorch sampler does not. +# --weight-transfer-config {"backend":"nccl"} +# The trainer syncs weights into this engine over NCCL. Without it there is no transfer engine +# to initialise. +VLLM_EXECUTION_FLAGS=() +if [[ "${VLLM_ENFORCE_EAGER:-1}" == 1 ]]; then + VLLM_EXECUTION_FLAGS+=(--enforce-eager) +fi +CUDA_VISIBLE_DEVICES="${JOB_GPUS[0]}" \ + VLLM_SERVER_DEV_MODE=1 \ + VLLM_USE_FLASHINFER_SAMPLER=0 \ + VLLM_USE_DEEP_GEMM=0 VLLM_DEEP_GEMM_WARMUP=skip \ + setsid "$PY312" -m vllm.entrypoints.openai.api_server \ + --model "$MODEL" --port "$VLLM_PORT" --served-model-name "$MODEL" \ + ${MODEL_REVISION:+--revision "$MODEL_REVISION"} \ + --trust-remote-code \ + --dtype "${TRAIN_DTYPE:-bfloat16}" --generation-config vllm \ + --gpu-memory-utilization "${VLLM_GPU_MEMORY_UTILIZATION:-0.9}" \ + "${VLLM_EXECUTION_FLAGS[@]}" --no-enable-prefix-caching \ + --limit-mm-per-prompt '{"image":0,"video":0}' \ + --max-model-len "${MAX_MODEL_LEN:-131072}" \ + --enable-auto-tool-choice --tool-call-parser qwen3_xml \ + --reasoning-parser qwen3 \ + --gdn-prefill-backend triton \ + --default-chat-template-kwargs '{"enable_thinking": false}' \ + --return-tokens-as-token-ids --logprobs-mode processed_logprobs \ + --override-generation-config "{\"temperature\": $TEMPERATURE, \"top_p\": $TOP_P, \"top_k\": -1}" \ + --weight-transfer-config '{"backend":"nccl"}' \ + > "$LOGS/vllm.log" 2>&1 & +VLLM_PID=$! + +# vLLM's /health returns 200 with an EMPTY body, so check the STATUS and parse nothing. A probe that +# json.loads() it throws, and a caller treating that as "not ready" waits out the whole budget on a +# server that came up minutes ago. +echo "waiting for vLLM (up to 30 min: weights + torch.compile)" +for _ in $(seq 1 900); do + kill -0 "$VLLM_PID" 2>/dev/null || { tail -60 "$LOGS/vllm.log"; exit 1; } + code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 "$VLLM_URL/health" || true) + [ "$code" = "200" ] && break + sleep 2 +done +[ "$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$VLLM_URL/health")" = "200" ] || { + echo "FATAL: vLLM never became healthy; its log follows"; tail -60 "$LOGS/vllm.log"; exit 1; } +echo "vLLM healthy" +# CHECK THE ENDPOINT THE TRAINER ACTUALLY NEEDS, not just /health. /health passing says nothing about +# whether VLLM_SERVER_DEV_MODE took effect, and the trainer only finds out ~5 minutes later at +# on_train_begin, after the env server and the whole dataset have loaded. +si=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$VLLM_URL/server_info?config_format=json" || true) +[ "$si" = "200" ] || { echo "FATAL: /server_info -> $si; VLLM_SERVER_DEV_MODE=1 did not take effect."; exit 1; } +# Confirm the engine on this port is OURS. Reachability is not identity: a neighbouring job's vLLM +# answers /health and /server_info exactly the same way, and the first sign of talking to the wrong +# one is an NCCL failure minutes later -- or, if the models happen to match, no sign at all. +served=$(curl -s --max-time 10 "$VLLM_URL/v1/models" | tr -d ' \n' | grep -o "\"id\":\"[^\"]*\"" | head -1) +echo "vLLM /server_info ok; serving $served on :$VLLM_PORT" + +echo "== harbor server ==" +# OWNED BY THE JOB unless SERVER is passed. The reference launcher boots its own env server too, and +# isolation matters more than the boot cost here: a shared server means a shared 400-session budget, +# a shared gradio tunnel, and sessions that outlive the job. Measured tonight: two arms on one server +# cost 6x throughput, and sessions leaked by a killed client filled it to 400/400 and dropped a run +# from 82% graded to 18%. The dataset tree is cached, so booting our own is cheap. +if [ -n "${SERVER:-}" ]; then + echo " using EXTERNAL server $SERVER (shared: its session budget and tunnel are not ours)" +else + OPENENV_BIN="${OPENENV_BIN:-$REPO/OpenEnv/.venv/bin/openenv}" + [ -x "$OPENENV_BIN" ] || { echo "FATAL: no openenv CLI at $OPENENV_BIN"; exit 1; } + # Ports are derived from the job id further up, so two jobs on one node cannot collide. + MAX_CONCURRENT_ENVS="${MAX_CONCURRENT_ENVS:-400}" \ + setsid "$OPENENV_BIN" harbor serve \ + --dataset "${SPLIT:-AdithyaSK/data_agent_rl_environment_train}" \ + --llm-url "$VLLM_URL/v1" --model "$MODEL" \ + --port "$ENV_PORT" --capture-port "$CAP_PORT" --expose gradio \ + --max-output-tokens "${MAX_OUTPUT_TOKENS:-12288}" \ + > "$LOGS/env.log" 2>&1 & + HARBOR_PID=$! + SERVER="http://$(hostname -s):$ENV_PORT" + echo " booting our own on :$ENV_PORT (capture :$CAP_PORT) -> $SERVER" + for _ in $(seq 1 180); do + curl -s -o /dev/null --max-time 3 "http://127.0.0.1:$ENV_PORT/health" && break + sleep 2 + done + curl -s -o /dev/null --max-time 5 "http://127.0.0.1:$ENV_PORT/health" || { + echo "FATAL: our harbor server never became healthy; log follows"; tail -60 "$LOGS/env.log"; exit 1; } + grep -a "capture" "$LOGS/env.log" | head -3 +fi +export SERVER +# IDENTITY, not just reachability: a server answering /health may host a different split, and every +# rollout would then run tasks this job never asked for. +SPLITS_JSON=$(curl -s --max-time 10 "$SERVER/harbor_env/splits") +echo " splits $SPLITS_JSON" +case "$SPLITS_JSON" in + *"${SPLIT:-AdithyaSK/data_agent_rl_environment_train}"*) echo " split OK" ;; + *) echo "FATAL: $SERVER does not host ${SPLIT:-AdithyaSK/data_agent_rl_environment_train}"; exit 1 ;; +esac +# The server probes the engine PER ROLLOUT from ITS host. If it cannot reach this node's vLLM the +# tier grades `text`, every rollout returns zero trainable turns, and the run burns sandboxes while +# looking healthy. Prove the path the workload actually uses. +echo " engine from server host: $(curl -s -o /dev/null -w '%{http_code}' --max-time 8 "$VLLM_URL/health")" +echo "harbor server healthy" + +# Reachability does not establish identity: gradio has reused a live URL across jobs. +# Probe only during startup, before any rollouts; a busy /health endpoint is not a watchdog. +if [ -n "$HARBOR_PID" ]; then +"$PY312" - "$VLLM_URL" "$MODEL" "$CAP_PORT" "$LOGS/env.log" "$LOGS" <<'PY' +import json +import pathlib +import re +import sys +import time + +import requests + +engine, model, cap_port, env_log, logs = sys.argv[1:] +models = requests.get(engine + "/v1/models", timeout=15) +models.raise_for_status() +assert [entry["id"] for entry in models.json()["data"]] == [model], "wrong inference model" +local = requests.get(f"http://127.0.0.1:{cap_port}/health", timeout=15) +local.raise_for_status() +identity = local.json()["instance"] +match = re.search(r"^capture\s+:\d+\s+->\s+(https://\S+)", pathlib.Path(env_log).read_text(), re.M) +if match is None: + raise RuntimeError("capture tunnel URL missing from the owned Harbor server log") +url = match[1] +for attempt in range(20): + try: + public = requests.get(url + "/health", timeout=10) + public.raise_for_status() + public_identity = public.json()["instance"] + break + except (requests.RequestException, ValueError, KeyError): + if attempt == 19: + raise + time.sleep(2) +assert public_identity == identity, "capture tunnel points to a different job" +pathlib.Path(logs, "endpoint_identity.json").write_text(json.dumps({ + "model": model, "engine": engine, "capture_url": url, "capture_instance": identity, + "public_matches_local": True, +}, indent=2) + "\n") +print("Capture tunnel identity verified; private and public instance IDs match", flush=True) +PY +fi + +# max_inflight 32 is the REFERENCE value, and it matters beyond throughput: at 16 this run +# measured samples/step 7.2 against the reference's 16.1, i.e. less than half the training +# signal per optimizer step, which over 400 steps is a different experiment rather than a +# slower one. 16 was chosen after an EVAL collapsed at 64 concurrent MCP clients -- but that +# eval ran ~16-turn rollouts (64 x 16 = ~1024 concurrent turn-slots) where training runs ~8.5 +# (32 x 8.5 = ~272), about a quarter of the load that broke it. +echo "== trainer on GPU 1 ==" +# NOT from $REPO: the `trl/` submodule DIRECTORY shadows the `trl` package as a namespace package +# there, and the import dies with "cannot import name '__version__' from trl (unknown location)". +cd "$TRAINDIR" +# Keep the trainer independent of Hub/network availability. JSONL fragments are safe on FSx. +unset TRACKIO_SPACE_ID TRACKIO_SERVER_URL TRACKIO_BUCKET_ID TRACKIO_DATASET_ID TRACKIO_WEBHOOK_URL +export TRACKIO_DIR="$LOGS/trackio" +export TRACKIO_STORAGE_MODE=jsonl +CUDA_VISIBLE_DEVICES="${JOB_GPUS[1]}" \ + "$PY312" -u train_harbor_multi.py \ + --server "${SERVER:?harbor server was neither booted nor supplied}" \ + --vllm-url "$VLLM_URL" \ + --model "$MODEL" \ + ${MODEL_REVISION:+--model-revision "$MODEL_REVISION"} \ + ${RESUME_FROM_CHECKPOINT:+--resume-from-checkpoint "$RESUME_FROM_CHECKPOINT"} \ + --split "${SPLIT:-AdithyaSK/data_agent_rl_environment_train}" \ + --harnesses "${HARNESSES:-opencode+mini-swe-agent}" \ + ${ALL_TASK_HARNESS_PAIRS:+--all-task-harness-pairs} \ + ${HARNESS_SCHEDULE:+--harness-schedule "$HARNESS_SCHEDULE"} \ + --sandbox "${SANDBOX:-e2b}" \ + --learning-rate "${LR:-3e-6}" \ + --temperature "$TEMPERATURE" \ + --num-generations "${NUM_GENERATIONS:-8}" \ + --max-inflight "${MAX_INFLIGHT:-32}" \ + --max-staleness "${MAX_STALENESS:-4}" \ + --grad-accum "${GRAD_ACCUM:-4}" \ + ${ATOMIC_ROLLOUTS:+--atomic-rollouts} \ + --max-outstanding-rollouts "${MAX_OUTSTANDING_ROLLOUTS:-0}" \ + --max-row-tokens "${MAX_ROW_TOKENS:-131072}" \ + --per-device-batch-size "${PER_DEVICE_BATCH_SIZE:-4}" \ + ${TASK_INDICES:+--task-indices "$TASK_INDICES"} \ + ${REWARD_KEY:+--reward-key "$REWARD_KEY"} \ + ${AGENT_TURN_FILTER:+--agent-turn-filter "$AGENT_TURN_FILTER"} \ + --agent-step-limit "${AGENT_STEP_LIMIT:-17}" \ + --agent-timeout "${AGENT_TIMEOUT:-600}" \ + --token-budget "${TOKEN_BUDGET:-40960}" \ + --max-completion-length "${MAX_COMPLETION_LENGTH:-16384}" \ + --heartbeat-stale-after-s "${HEARTBEAT_STALE_AFTER_S:-900}" \ + --dtype "${TRAIN_DTYPE:-bfloat16}" \ + --top-p "${TOP_P:-1.0}" \ + --max-steps "${MAX_STEPS:-400}" \ + --max-train-seconds "${MAX_TRAIN_SECONDS:-0}" \ + --coverage-min-steps "${COVERAGE_MIN_STEPS:-0}" \ + --audit-dir "$LOGS/audit" \ + --project "${TRACKIO_PROJECT:-data-agent-harbor-multi}" \ + --save-steps "${SAVE_STEPS:-100}" \ + --checkpoint-max-seconds "${CHECKPOINT_MAX_SECONDS:-0}" \ + --output-dir "$LOGS/run" diff --git a/04-data-agent/train/launch_harbor_opencode.slurm b/04-data-agent/train/launch_harbor_opencode.slurm new file mode 100644 index 0000000..af282e0 --- /dev/null +++ b/04-data-agent/train/launch_harbor_opencode.slurm @@ -0,0 +1,251 @@ +#!/bin/bash +#SBATCH --job-name=agrpo-harbor-opencode +#SBATCH --ntasks-per-node=1 +#SBATCH --gres=gpu:2 +#SBATCH --partition=hopper-extra +#SBATCH --output=/fsx/%u/logs/%x-%j.out +#SBATCH --error=/fsx/%u/logs/%x-%j.err +#SBATCH --time=0-12:00:00 +set -euo pipefail + +# One job, three processes, in this order and for these reasons: +# +# GPU 0 vLLM, with the capture flags. The TRAINER's engine -- the agent calls the same weights the +# optimizer updates, which is what makes the rollouts on-policy. +# GPU 1 the trainer. +# cpu the env server, which starts the capture proxy and publishes it. +# +# The sandboxes must reach CAPTURE, not vLLM: opencode is pointed at the proxy and its api key is the +# capture session id, so vLLM is never exposed and never sees a sandbox. Capture is published with a +# gradio tunnel because E2B runs off-cluster; `direct` only works for a sandbox on this host and fails +# silently -- opencode cannot reach the engine, makes zero model calls, and the rollout returns an +# empty answer that grades exactly like a model that could not do the task. + +REPO=/fsx/$USER/projects/trl_prod +ENVDIR=$REPO/HuggingEnvs/04-data-agent/envs/blackbox-opencode +TRAINDIR=$REPO/HuggingEnvs/04-data-agent/train + +# TWO INTERPRETERS, ON PURPOSE. +# .venv312 torch / vLLM / TRL. The heavy stack, shared and reproducible via install.sh. +# $ENVDIR/.venv the environment server only. Deliberately light -- no torch, no vLLM. +# The trainer needs two LIGHT packages that .venv312 does not carry: the local `openenv` (the capture +# stack is in no release) and `data_agent_env`. Both go on PYTHONPATH rather than being pip-installed, +# because install.sh owns what is in .venv312 and an ad hoc install there stops being reproducible. +# `data_agent_env` needs the symlink in _pypath/: the directory is named `blackbox-opencode`, which is +# not a legal Python identifier, so PYTHONPATH alone cannot reach it. +# `harbor_reward` is symlinked there too. The trainer imports it fine (Python puts the SCRIPT's dir on +# sys.path[0], whatever the cwd), but `rollout_reward_fn` is pickled into the SPAWNED rollout child, +# which must import it by name. Resting that on spawn's sys.path propagation would fail in the child +# only -- after the sandboxes are already paid for. PYTHONPATH is inherited by any child either way. +PY312=$REPO/.venv312/bin/python +# `harbor_env` lives under OpenEnv/envs, not OpenEnv/src -- the blackbox launcher never needed it. +export PYTHONPATH="$TRAINDIR/_pypath:$REPO/OpenEnv/src:$REPO/OpenEnv/envs${PYTHONPATH:+:$PYTHONPATH}" +export TRL_EXPERIMENTAL_SILENCE=1 + +cd "$ENVDIR" + +MODEL="${MODEL:-Qwen/Qwen3.5-2B}" + +# PORTS DERIVED PER JOB. Fixed ports are a trap on a shared node and it is not a theoretical one: +# two of these jobs landed on ip-10-53-93-25 together, the second one's /server_info probe answered +# 200 because it was talking to the FIRST job's vLLM, and the NCCL weight-transfer group then tried to +# attach to another job's engine and died with "NCCL error: unhandled cuda error". Every preflight +# check passed while pointing at the wrong process. +# +# Worse, had the models matched, it would have trained one job against the other's weights and looked +# perfectly healthy. The reference launcher derives ports the same way and says so. +_OFF=$(( ${SLURM_JOB_ID:-0} % 200 * 4 )) +VLLM_PORT="${VLLM_PORT:-$(( 18000 + _OFF ))}" +ENV_PORT="${ENV_PORT:-$(( 18001 + _OFF ))}" +CAP_PORT="${CAP_PORT:-$(( 18002 + _OFF ))}" +VLLM_URL="http://127.0.0.1:$VLLM_PORT" +LOGS="$ENVDIR/logs/job-${SLURM_JOB_ID:-local}" +mkdir -p "$LOGS/run" + +# BRIDGE THE CHECKPOINTS TO THE EVAL WATCHER. +# +# eval_watcher.py polls experiments/temp_asyncgrpo_code/logs/ckpt-/checkpoint-* . Without +# this link it never sees a checkpoint from this run, and the 400-step job would finish with nothing +# evaluated -- silently, because "no checkpoints yet" and "wrong directory" look identical to a poller. +# +# The link, rather than writing there directly: the run's artifacts belong with the environment, and +# the eval stack is the one that produced the +0.2028 reference, so reusing it keeps the comparison +# apples-to-apples instead of introducing a second evaluator. +WATCHDIR=/fsx/$USER/projects/trl_prod/experiments/temp_asyncgrpo_code/logs/ckpt-${SLURM_JOB_ID:-local} +ln -sfn "$LOGS/run" "$WATCHDIR" +echo "checkpoints visible to eval_watcher at $WATCHDIR -> $LOGS/run" + +set -a; . "$REPO/experiments/.env"; set +a +. "$ENVDIR/../blackbox-harbor/tools/hf_token.sh" + +cleanup() { pkill -P $$ 2>/dev/null || true; } +trap cleanup EXIT + +# SAMPLING, FOR THE ENGINE AND THE TRAINER, FROM ONE VARIABLE SO THEY CANNOT DRIFT. +# opencode sends NO sampling parameters, so whatever the engine defaults to is what actually +# generated the actions -- and Qwen3.5-2B ships no generation_config.json, so that default is +# vLLM's own 1.0/1.0, NOT the 0.8 the trainer divides logits by when it recomputes logprobs for +# the importance ratio. Leaving the engine unpinned means the gradient is computed against a +# distribution that never produced the samples. Measured, unpinned: entropy climbed 0.229 -> 0.587 +# over 24 steps while reward fell 0.592 -> 0.216, with turns/mean 10.4 against the reference's 6.1 +# (job 76577). The reference pins both from one variable (run_cluster.py:165) and holds entropy +# flat at 0.17-0.21. top_k=-1 because top_k truncation is likewise unmodelled by the recomputation. +TEMPERATURE="${TEMPERATURE:-0.8}" +# 1.0, NOT the reference launcher's 0.95. `--logprobs-mode processed_logprobs` takes the logprob +# AFTER truncation, so a truncating top_p renormalises every captured logprob over the kept set +# while the trainer recomputes over the full vocabulary -- the step-0 importance ratio then +# lands at kept_mass rather than 1, and the reordering is worse than a uniform shift. TRL's +# AsyncGRPOConfig already defaults top_p to 1.0, so 0.95 here is an ENGINE/TRAINER MISMATCH, +# not a policy choice. Measured: the reference sat at ratio 0.985-0.993 (the truncation +# signature); at 1.0 it moves to 0.9984-0.9999. +TOP_P="${TOP_P:-1.0}" + +echo "== vLLM on GPU 0 == (ports vllm=$VLLM_PORT env=$ENV_PORT capture=$CAP_PORT, derived from job ${SLURM_JOB_ID:-0})" +# EVERY ONE OF THESE FLAGS IS LOad-BEARING. They are the set the +0.2028 run served with; a +# hand-rolled `vllm serve` that omits any of them fails silently rather than loudly. +# +# --enable-auto-tool-choice / --tool-call-parser qwen3_xml +# Without them vLLM never parses tool calls, so `has_tool_call` is False for EVERY turn and the +# whole rollout is discarded with no error anywhere. The agent path is entirely tool calls. +# --default-chat-template-kwargs {"enable_thinking": false} +# Pins thinking OFF server-side. Qwen3.5-4B's template opens by default and -2B's +# closes it -- INVERTED defaults between two models of the same family. Leaving it to the +# default is what made a re-rendered prompt match the 2B and diverge on the 4B, forking 100% of +# turn transitions. This is what `think_template_patch` used to monkeypatch; it belongs here. +# --return-tokens-as-token-ids / --logprobs-mode processed_logprobs +# The capture tier. Without them capture degrades to text and every rollout is untrainable -- +# and looks completely normal while being so. +# --gdn-prefill-backend triton +# Qwen3.5 is hybrid Gated-DeltaNet; this is the working prefill backend for it. +# VLLM_USE_DEEP_GEMM=0 / VLLM_DEEP_GEMM_WARMUP=skip +# On Hopper a stale importable deep_gemm kills startup with "DeepGEMM backend is not available +# or outdated". +# VLLM_SERVER_DEV_MODE=1 +# Gates /server_info, /pause and /init_weight_transfer_engine. TRL's weight transfer reads +# /server_info for the server's dtype, so WITHOUT this the trainer dies at on_train_begin with +# "404 Client Error: Not Found for url: .../server_info?config_format=json" -- after vLLM is +# healthy, after the env server is up, after the dataset loads. Job 76488 died exactly there. +# VLLM_USE_FLASHINFER_SAMPLER=0 +# flashinfer JIT-compiles its sampling kernel and needs nvcc; the PyTorch sampler does not. +# --weight-transfer-config {"backend":"nccl"} +# The trainer syncs weights into this engine over NCCL. Without it there is no transfer engine +# to initialise. +CUDA_VISIBLE_DEVICES=0 \ + VLLM_SERVER_DEV_MODE=1 \ + VLLM_USE_FLASHINFER_SAMPLER=0 \ + VLLM_USE_DEEP_GEMM=0 VLLM_DEEP_GEMM_WARMUP=skip \ + "$PY312" -m vllm.entrypoints.openai.api_server \ + --model "$MODEL" --port "$VLLM_PORT" --served-model-name "$MODEL" \ + --trust-remote-code \ + --max-model-len "${MAX_MODEL_LEN:-131072}" \ + --enable-auto-tool-choice --tool-call-parser qwen3_xml \ + --reasoning-parser qwen3 \ + --gdn-prefill-backend triton \ + --default-chat-template-kwargs '{"enable_thinking": false}' \ + --return-tokens-as-token-ids --logprobs-mode processed_logprobs \ + --override-generation-config "{\"temperature\": $TEMPERATURE, \"top_p\": $TOP_P, \"top_k\": -1}" \ + --weight-transfer-config '{"backend":"nccl"}' \ + > "$LOGS/vllm.log" 2>&1 & + +# vLLM's /health returns 200 with an EMPTY body, so check the STATUS and parse nothing. A probe that +# json.loads() it throws, and a caller treating that as "not ready" waits out the whole budget on a +# server that came up minutes ago. +echo "waiting for vLLM (up to 30 min: weights + torch.compile)" +for _ in $(seq 1 900); do + code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 "$VLLM_URL/health" || true) + [ "$code" = "200" ] && break + sleep 2 +done +[ "$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$VLLM_URL/health")" = "200" ] || { + echo "FATAL: vLLM never became healthy; its log follows"; tail -60 "$LOGS/vllm.log"; exit 1; } +echo "vLLM healthy" +# CHECK THE ENDPOINT THE TRAINER ACTUALLY NEEDS, not just /health. /health passing says nothing about +# whether VLLM_SERVER_DEV_MODE took effect, and the trainer only finds out ~5 minutes later at +# on_train_begin, after the env server and the whole dataset have loaded. +si=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$VLLM_URL/server_info?config_format=json" || true) +[ "$si" = "200" ] || { echo "FATAL: /server_info -> $si; VLLM_SERVER_DEV_MODE=1 did not take effect."; exit 1; } +# Confirm the engine on this port is OURS. Reachability is not identity: a neighbouring job's vLLM +# answers /health and /server_info exactly the same way, and the first sign of talking to the wrong +# one is an NCCL failure minutes later -- or, if the models happen to match, no sign at all. +served=$(curl -s --max-time 10 "$VLLM_URL/v1/models" | tr -d ' \n' | grep -o "\"id\":\"[^\"]*\"" | head -1) +echo "vLLM /server_info ok; serving $served on :$VLLM_PORT" + +echo "== harbor server ==" +# OWNED BY THE JOB unless SERVER is passed. The reference launcher boots its own env server too, and +# isolation matters more than the boot cost here: a shared server means a shared 400-session budget, +# a shared gradio tunnel, and sessions that outlive the job. Measured tonight: two arms on one server +# cost 6x throughput, and sessions leaked by a killed client filled it to 400/400 and dropped a run +# from 82% graded to 18%. The dataset tree is cached, so booting our own is cheap. +if [ -n "${SERVER:-}" ]; then + echo " using EXTERNAL server $SERVER (shared: its session budget and tunnel are not ours)" +else + OPENENV_BIN="${OPENENV_BIN:-$REPO/OpenEnv/.venv/bin/openenv}" + [ -x "$OPENENV_BIN" ] || { echo "FATAL: no openenv CLI at $OPENENV_BIN"; exit 1; } + # Ports are derived from the job id further up, so two jobs on one node cannot collide. + MAX_CONCURRENT_ENVS="${MAX_CONCURRENT_ENVS:-400}" \ + setsid "$OPENENV_BIN" harbor serve \ + --dataset "${SPLIT:-AdithyaSK/data_agent_rl_environment_train}" \ + --llm-url "$VLLM_URL/v1" --model "$MODEL" \ + --port "$ENV_PORT" --capture-port "$CAP_PORT" --expose gradio \ + --max-output-tokens "${MAX_OUTPUT_TOKENS:-12288}" \ + > "$LOGS/env.log" 2>&1 & + SERVER="http://$(hostname -s):$ENV_PORT" + echo " booting our own on :$ENV_PORT (capture :$CAP_PORT) -> $SERVER" + for _ in $(seq 1 180); do + curl -s -o /dev/null --max-time 3 "http://127.0.0.1:$ENV_PORT/health" && break + sleep 2 + done + curl -s -o /dev/null --max-time 5 "http://127.0.0.1:$ENV_PORT/health" || { + echo "FATAL: our harbor server never became healthy; log follows"; tail -60 "$LOGS/env.log"; exit 1; } + grep -a "capture" "$LOGS/env.log" | head -3 +fi +export SERVER +# IDENTITY, not just reachability: a server answering /health may host a different split, and every +# rollout would then run tasks this job never asked for. +SPLITS_JSON=$(curl -s --max-time 10 "$SERVER/harbor_env/splits") +echo " splits $SPLITS_JSON" +case "$SPLITS_JSON" in + *"${SPLIT:-AdithyaSK/data_agent_rl_environment_train}"*) echo " split OK" ;; + *) echo "FATAL: $SERVER does not host ${SPLIT:-AdithyaSK/data_agent_rl_environment_train}"; exit 1 ;; +esac +# The server probes the engine PER ROLLOUT from ITS host. If it cannot reach this node's vLLM the +# tier grades `text`, every rollout returns zero trainable turns, and the run burns sandboxes while +# looking healthy. Prove the path the workload actually uses. +echo " engine from server host: $(curl -s -o /dev/null -w '%{http_code}' --max-time 8 "$VLLM_URL/health")" +echo "harbor server healthy" + +# max_inflight 32 is the REFERENCE value, and it matters beyond throughput: at 16 this run +# measured samples/step 7.2 against the reference's 16.1, i.e. less than half the training +# signal per optimizer step, which over 400 steps is a different experiment rather than a +# slower one. 16 was chosen after an EVAL collapsed at 64 concurrent MCP clients -- but that +# eval ran ~16-turn rollouts (64 x 16 = ~1024 concurrent turn-slots) where training runs ~8.5 +# (32 x 8.5 = ~272), about a quarter of the load that broke it. +echo "== trainer on GPU 1 ==" +# NOT from $REPO: the `trl/` submodule DIRECTORY shadows the `trl` package as a namespace package +# there, and the import dies with "cannot import name '__version__' from trl (unknown location)". +cd "$TRAINDIR" +CUDA_VISIBLE_DEVICES=1 TRACKIO_STORAGE_MODE=sqlite \ + "$PY312" -u train_harbor_opencode.py \ + --server "${SERVER:?harbor server was neither booted nor supplied}" \ + --vllm-url "$VLLM_URL" \ + --model "$MODEL" \ + --split "${SPLIT:-AdithyaSK/data_agent_rl_environment_train}" \ + --sandbox "${SANDBOX:-e2b}" \ + --learning-rate "${LR:-3e-6}" \ + --temperature "$TEMPERATURE" \ + --num-generations "${NUM_GENERATIONS:-8}" \ + --max-inflight "${MAX_INFLIGHT:-32}" \ + --grad-accum "${GRAD_ACCUM:-4}" \ + ${TASK_INDICES:+--task-indices "$TASK_INDICES"} \ + ${REWARD_KEY:+--reward-key "$REWARD_KEY"} \ + ${AGENT_TURN_FILTER:+--agent-turn-filter "$AGENT_TURN_FILTER"} \ + --agent-step-limit "${AGENT_STEP_LIMIT:-17}" \ + --reward "${REWARD:-efficiency}" \ + --token-budget "${TOKEN_BUDGET:-40960}" \ + --max-completion-length "${MAX_COMPLETION_LENGTH:-16384}" \ + --heartbeat-stale-after-s "${HEARTBEAT_STALE_AFTER_S:-900}" \ + --dtype "${TRAIN_DTYPE:-bfloat16}" \ + --top-p "${TOP_P:-1.0}" \ + --max-steps "${MAX_STEPS:-400}" \ + --save-steps "${SAVE_STEPS:-100}" \ + --output-dir "$LOGS/run" diff --git a/04-data-agent/train/multi_harness.py b/04-data-agent/train/multi_harness.py new file mode 100644 index 0000000..bda797e --- /dev/null +++ b/04-data-agent/train/multi_harness.py @@ -0,0 +1,142 @@ +"""Route each GRPO group to a harness, without changing TRL. + +WHY THIS SHAPE. `HarnessRolloutWorker` hands the factory only `(prompt, seed, episode_id)` -- +`async_rollout_worker.py` pulls `(group_id, row)` but calls `_generate_one(prompt, ..., group_id)` +and drops the row. So a factory cannot read a harness off a dataset column. What it CAN read is +`seed`, which `_run_session` sets to `group_id`, and `_repeat_iterator` yields the SAME group_id for +all `num_generations` of a group. + +That is the load-bearing property: **harness is constant within a group.** Measured pass@4 across +harnesses on this suite spans 0.320 to 0.020, so a group whose members ran under different harnesses +would have a baseline averaging two competence levels, and the advantage would encode WHICH HARNESS +rather than which action. Constant-within-group makes the spread a BETWEEN-group constant, which +advantage normalisation removes entirely. + +Group -> row is `group_id % len(dataset)`. An explicit frozen schedule maps the same group ID +to both its task and its harness: one harness per task per pass, rotating over later passes. +The legacy modulo route pads rows to ensure that tasks rotate across harnesses over time. +""" + +from __future__ import annotations + +import logging +from math import gcd +from typing import Any + +from harbor_env.harness import HarborSession, HarborSessionFactory + +logger = logging.getLogger(__name__) + + +class MultiHarborSessionFactory(HarborSessionFactory): + """A HarborSessionFactory whose harness is chosen per GROUP, from `seed`.""" + + def __init__(self, *args: Any, harnesses: list[str], schedule=None, group_offset=0, **kw: Any) -> None: + super().__init__(*args, **kw) + if not harnesses: + raise ValueError("harnesses must be non-empty") + self.harnesses = list(harnesses) + self.schedule = schedule + if not isinstance(group_offset, int) or group_offset < 0: + raise ValueError('group_offset must be a nonnegative integer') + self.group_offset = group_offset + if schedule is not None: + from harness_schedule import validate_schedule + validate_schedule(schedule) + if schedule['harnesses'] != self.harnesses: + raise ValueError('Schedule harness order differs from the configured harnesses') + # group_id -> harness, so a violation is detectable rather than merely unlikely. + self._group_harness: dict[int, str] = {} + + def __getstate__(self) -> dict[str, Any]: + # The factory is pickled into a spawned child; the parent's live client must not go with it. + state = super().__getstate__() + state["harnesses"] = self.harnesses + state["_group_harness"] = {} + return state + + def harness_for(self, seed: int | None) -> str: + seed = (seed or 0) + getattr(self, 'group_offset', 0) + if getattr(self, 'schedule', None) is not None: + return self.schedule['groups'][(seed or 0) % len(self.schedule['groups'])]['harness'] + return self.harnesses[(seed or 0) % len(self.harnesses)] + + def create(self, task: Any, seed: int | None = None, episode_id: str | None = None) -> HarborSession: + harness = self.harness_for(seed) + + # HARD failure, not a warning. If two generations of one group ran under different harnesses + # the group's central claim is void, and a warning in a log nobody reads is how that ships. + previous = self._group_harness.setdefault(int(seed or 0), harness) + if previous != harness: + raise RuntimeError( + f"group {seed} mixed harnesses ({previous} then {harness}). The GRPO baseline would " + f"average two competence levels (measured pass@4 spread 0.320-0.020 on this suite), " + f"so the advantage would encode which harness, not which action." + ) + + instruction = _instruction_of(task) + self.tasks() # builds the instruction -> index map + index = self._by_instruction.get(_instruction_id(instruction)) + if index is None: + # Verbatim from HarborSessionFactory: a lookup failure must never silently run task 0. + raise KeyError( + "this prompt does not match any task on the server. Build the dataset from " + "`prompt_rows()` so the instruction the trainer sends is the one the server has." + ) + if self.schedule is not None: + absolute_group = (seed or 0) + self.group_offset + expected = self.schedule['groups'][absolute_group % len(self.schedule['groups'])] + if index != expected['task_index']: + raise ValueError(f'Group {seed} received task {index}, expected {expected["task_index"]}') + return HarborSession( + env=self.new_client(), # one client PER SESSION: a shared MCP socket raises + owns_env=True, # ConcurrencyError on concurrent recv, making every rollout unscorable + split=self._split, + task_index=index, + instruction=instruction, + harness=harness, # <-- the only thing that varies + sandbox=self.sandbox, + llm_url=self.llm_url, + model=self.model, + sampling=self.sampling, + reward_key=self.reward_key, + api_key=self.api_key, + auth_header=self.auth_header, + agent_timeout_sec=self.agent_timeout_sec, + agent_step_limit=self.agent_step_limit, + ) + + +def _instruction_of(task: Any) -> str: + from harbor_env.harness import _instruction_of as f # reuse, never reimplement + return f(task) + + +def _instruction_id(text: str) -> str: + from harbor_env.harness import instruction_id as f + return f(text) + + +def pair_rows(factory: MultiHarborSessionFactory, *, all_pairs: bool = False) -> list[dict[str, Any]]: + """Align prompt rows with the explicit schedule, Cartesian mode, or legacy modulo route.""" + rows = list(factory.prompt_rows()) + h = len(factory.harnesses) + schedule = getattr(factory, 'schedule', None) + if schedule is not None: + if all_pairs: + raise ValueError('Choose either a rotating schedule or Cartesian scheduling') + expected = [(t['name'], t['task_index']) for t in schedule['tasks']] + if [(r['task_name'], r['task_index']) for r in rows] != expected: + raise ValueError('Server task identities/order differ from the frozen schedule') + return [dict(rows[g['task_row']]) for g in schedule['groups']] + if all_pairs: + # The worker repeats each row num_generations times with a fixed group ID. + # H consecutive rows per task align exactly with harness_for(group_id). + # This schedule repeats without coprime padding because its length is a multiple of H. + return [dict(row) for row in rows for _ in factory.harnesses] + if h > 1: + while len(rows) > 1 and gcd(len(rows), h) != 1: + rows.append(dict(rows[len(rows) % len(rows)])) # duplicate one row to break the common factor + assert gcd(len(rows), h) == 1, f"gcd({len(rows)},{h}) != 1" + logger.info("pair_rows: %d rows x %d harnesses, gcd=%d", len(rows), h, gcd(len(rows), h)) + return rows diff --git a/04-data-agent/train/prepare_harbor_opencode_run.py b/04-data-agent/train/prepare_harbor_opencode_run.py new file mode 100644 index 0000000..9cb6e5e --- /dev/null +++ b/04-data-agent/train/prepare_harbor_opencode_run.py @@ -0,0 +1,134 @@ +"""Prepare a fresh OpenCode-only ablation of the last stable Harbor recipe.""" +import argparse +import copy +import hashlib +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys + +REPO = Path(__file__).resolve().parents[3] +REFERENCE = REPO / 'experiments/async_grpo_harbor_data_agent/logs/multi4-long-prod-cont-20260915' +TOOLS = REPO / 'experiments/async_grpo_harbor_data_agent/tools' +DEFAULT_OUT = REPO / 'experiments/async_grpo_harbor_data_agent/logs/harbor-opencode-only-20260916' +sys.path.insert(0, str(Path(__file__).parent)) +from harness_schedule import validate_schedule + + +def read(path): return json.loads(path.read_text()) +def digest(path): return hashlib.sha256(path.read_bytes()).hexdigest() +def write(path, value): path.write_text(json.dumps(value, indent=2) + '\n') + + +def single_harness_schedule(reference): + validate_schedule(reference) + schedule = copy.deepcopy(reference) + n = schedule['task_count'] + schedule.update(harnesses=['opencode'], passes_per_cycle=1, groups_per_cycle=n) + schedule['groups'] = schedule['groups'][:n] + for group in schedule['groups']: + group['harness'] = 'opencode' + validate_schedule(schedule) + assert all({k:v for k,v in a.items() if k != 'harness'} == {k:v for k,v in b.items() if k != 'harness'} + for a,b in zip(reference['groups'][:n], schedule['groups'], strict=True)) + return schedule + + +def prepare(root): + if root.exists(): + raise ValueError('Run directory exists; inspect its submission record before retrying') + config = read(REFERENCE/'run_config.json') + prior = copy.deepcopy(config) + manifest = read(REFERENCE/'manifest.json') + schedule = single_harness_schedule(read(REFERENCE/'harness_schedule.json')) + root.mkdir(parents=True) + for name in ('indices.txt','runtime_versions.json'): + shutil.copyfile(REFERENCE/name,root/name) + manifest.update(harnesses=['opencode'],pairs_per_cycle=1000,passes_per_cycle=1, + groups_per_pass=1000,rollouts_per_scheduled_cycle=8000) + write(root/'manifest.json',manifest);write(root/'harness_schedule.json',schedule) + (root/'pairs.jsonl').write_text(''.join(json.dumps(g)+'\n' for g in schedule['groups'])) + snap=root/'source-snapshot' + shutil.copytree(REFERENCE/'source-snapshot',snap,ignore=shutil.ignore_patterns('__pycache__','*.pyc')) + # Keep model/trainer/optimizer code identical to the completed stable run. + # Only transport receives the already-qualified keepalive fix used by eval800. + qualified=REFERENCE/'checkpoint-evals/step-000800/recovery-20260916-keepalive/source-snapshot' + assert read(REFERENCE/'checkpoint-evals/step-000800/scores.json')['comparison_ready'] + transport_changes=[] + for name in ('server.py','sse.py'): + rel=Path('OpenEnv/src/openenv/core/harness/capture')/name + before=digest(snap/rel);shutil.copyfile(qualified/rel,snap/rel) + transport_changes.append({'file':str(rel),'before':before,'after':digest(snap/rel)}) + for name in ('monitor_multi4.py','trackio_multi4.py','supervise_multi4.py'): + shutil.copyfile(TOOLS/name,snap/'tools'/name) + shutil.copyfile(REPO/'HuggingEnvs/04-data-agent/eval/checkpoint_evals.py',snap/'HuggingEnvs/04-data-agent/eval/checkpoint_evals.py') + launch=snap/'tools/launch_multi4_long.sh';text=launch.read_text() + text=text.replace("assert m['task_count']==1000 and m['pairs_per_cycle']==4000", "assert m['task_count']==1000 and m['pairs_per_cycle']==1000 * len(m['harnesses'])") + launch.write_text(text) + log=snap/'tools/launch_multi4_trackio.sh';log.write_text(log.read_text().replace('--watch --online','--watch')) + supervisor=snap/'tools/supervise_multi4.py';text=supervisor.read_text() + text=text.replace("'--train-job', train_job, '--watch', '--online']", "'--train-job', train_job, '--watch']\n if read_json(root / 'run_config.json', {}).get('logging', {}).get('online', True):\n command.append('--online')") + supervisor.write_text(text) + submit=snap/'tools/submit_multi4_long.py';text=submit.read_text() + text=text.replace("'--partition=hopper-extra'", "'--partition=' + config['resources']['partition']") + text=text.replace("'--job-name=multi4-long-2b'", "'--job-name=harbor-opencode-only'") + submit.write_text(text) + evaluator=root/'checkpoint-evals/eval-source';evaluator.parent.mkdir() + shutil.copytree(qualified,evaluator,ignore=shutil.ignore_patterns('__pycache__','*.pyc')) + write(evaluator/'source_hashes.json',{str(p.relative_to(evaluator)):digest(p) for p in evaluator.rglob('*') if p.is_file() and p!=evaluator/'source_hashes.json'}) + watcher=snap/'tools/launch_multi4_eval_watcher.sh';text=watcher.read_text() + text=text.replace('--interval "$INTERVAL"', '--protocol "$TRAIN_RUN_ROOT/checkpoint-evals/eval-source/protocol.json" --interval "$INTERVAL"') + watcher.write_text(text) + config.update(status='prepared',harnesses=['opencode'],initialization='Fresh pinned Qwen3.5-2B base; independent optimizer and scheduler', + source_snapshot=str(snap),frozen_eval_source=str(evaluator),reference_run=str(REFERENCE),reference_job=80608) + for key in ('restart_of','restart_reason','resume_state','job_id','replaced_by'): + config.pop(key,None) + config['harness_versions']={'opencode':prior['harness_versions']['opencode']} + config['training'].pop('resume_from_checkpoint',None) + config['training'].pop('budget_reference',None) + config['training']['soft_max_train_seconds']=82200 + config['resources'].update(partition='hopper-prod',slurm_walltime='24:00:00') + config['dataset'].update(pairs_per_cycle=1000,rollouts_per_scheduled_cycle=8000,passes_per_cycle=1, + harness_groups_per_pass={'opencode':1000},schedule_file=str(root/'harness_schedule.json'), + schedule_sha256=digest(root/'harness_schedule.json'),manifest_sha256=digest(root/'manifest.json')) + config['evaluation'].update(protocol_file=str(evaluator/'protocol.json'),partition='hopper-prod', + max_active_eval_jobs=1,interval_optimizer_steps=100) + config['logging'].update(project=root.name,space_id='HuggingEnvs/data-agent-training-comparison-trackio', + bucket_id='HuggingEnvs/data-agent-training-comparison-trackio',private=False,online=False, + online_via='Uniform comparison publisher',evaluation_sources=[],local_directory=str(root/'trackio'), + collector_file=str(snap/'tools/trackio_multi4.py')) + config['logging'].pop('parent_project',None) + config['monitoring'].update(stable_after_optimizer_step=10,startup_interval_seconds=120,stable_interval_seconds=600) + config['monitoring']['support_job_supervisor'].update(status_file=str(root/'supervisor/status.json'),source_file=str(snap/'tools/supervise_multi4.py')) + config['limitations']=[ + 'Matches the last stable Harbor recipe, not every historical recipe used earlier in the resumed reference.', + 'The first pass preserves the reference task order exactly; the same 1000-task single-harness pass repeats if exhausted.', + '1000 is the optimizer-step target and task-pool size; it does not guarantee full task coverage.', + 'Worker ceiling32; total outstanding rollouts16; eight generations per task; staleness at most4.', + 'Separate eval GPUs, endpoints and node; FSx and E2B quota remain shared.', + 'Checkpoint capture transport uses the already-qualified streaming keepalive repair; sampled tokens and loss are unchanged.', + 'CPU monitoring writes local alerts every2minutes initially and every10minutes after stability; no automatic chat wakeups.', + 'Failed or incomplete evaluations are recorded as incomplete, never converted to final pass@1 scores.'] + write(root/'run_config.json',config) + hashes={str(p.relative_to(snap)):digest(p) for p in snap.rglob('*') if p.is_file()} + write(root/'source_hashes.json',hashes) + excluded={'resume_from_checkpoint','budget_reference'} + assert {k:v for k,v in prior['training'].items() if k not in excluded} == config['training'] + proof={'prepared':True,'fresh_base':True,'same_training_hyperparameters':True,'same_sampling':config['sampling']==prior['sampling'], + 'first_pass_task_order_identical':True,'training_harnesses':['opencode'],'evaluation_harnesses':read(evaluator/'protocol.json')['harnesses'], + 'reference_job':80608,'source_files':len(hashes),'transport_changes':transport_changes,'live_startup_verified':False} + write(root/'validation.json',proof) + env={**os.environ,'TRAIN_RUN_ROOT':str(root),'SLURM_JOB_ID':'preflight','MULTI4_PREFLIGHT_ONLY':'1'} + with (root/'preflight.log').open('w') as stream: + subprocess.run(['bash',str(launch)],env=env,stdout=stream,stderr=subprocess.STDOUT,check=True) + env.update(TRAIN_JOB_ID='PREFLIGHT',MULTI4_EVAL_PREFLIGHT_ONLY='1') + with (root/'eval-preflight.log').open('w') as stream: + subprocess.run(['bash',str(watcher)],env=env,stdout=stream,stderr=subprocess.STDOUT,check=True) + print(json.dumps({'run':str(root),'validation':proof},indent=2)) + + +if __name__=='__main__': + parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--out',type=Path,default=DEFAULT_OUT) + prepare(parser.parse_args().out.resolve()) diff --git a/04-data-agent/train/run_whitebox_bash.slurm b/04-data-agent/train/run_whitebox_bash.slurm new file mode 100755 index 0000000..f46071a --- /dev/null +++ b/04-data-agent/train/run_whitebox_bash.slurm @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +#SBATCH --job-name=wb-bash-run +#SBATCH --partition=hopper-extra,hopper-dev +#SBATCH --gres=gpu:1 +#SBATCH --cpus-per-task=24 +#SBATCH --output=/fsx/%u/logs/%x-%j.out +#SBATCH --error=/fsx/%u/logs/%x-%j.err +#SBATCH --time=0-04:00:00 +# +# A REAL (small) run: one pass over the 100-task generated train split, checkpoints saved, so the +# result can be evaluated against base on the DISJOINT 30-task test split. +# +# SPLIT DEFAULTS TO train:easy, AND THAT IS A GRADIENT DECISION, NOT A DIFFICULTY PREFERENCE. +# GRPO needs variance WITHIN a group: if every rollout of a prompt scores the same, the advantages are +# all zero and the step contributes nothing. Base pass@1 for this model by tier is easy 0.214, +# medium 0.062, hard 0.060, so P(all `num_generations` rollouts fail) is +# train:easy g=4 -> 0.38 g=8 -> 0.15 g=16 -> 0.02 +# train:medium g=4 -> 0.77 g=8 -> 0.60 g=16 -> 0.36 +# Measured: `train` at g=4 produced 0 reward and grad_norm 0 on 7 of 7 steps (job 76712). easy at +# g=8 is the smallest configuration that actually learns on one GPU. +# +# Same 1-GPU colocate shape as the smoke. Everything here that looks incidental is load-bearing -- +# see smoke_whitebox_bash.slurm for why PATH, the ninja gate and the derived ports exist. +set -euo pipefail + +TRL_PROD=/fsx/adithyaskolavi/projects/trl_prod +cd "$TRL_PROD" +PY="$TRL_PROD/.venv312/bin/python" +export PATH="$TRL_PROD/.venv312/bin:$PATH" +module load cuda/12.9 2>/dev/null || module load cuda/13.0 2>/dev/null || true +command -v ninja >/dev/null || { echo "FATAL: ninja not on PATH; flashinfer's JIT dies at step 0"; exit 1; } + +set -a; source "$TRL_PROD/experiments/.env" 2>/dev/null || true; set +a +export HF_TOKEN="${HF_TOKEN:-${HF_API_KEY:-}}" +echo "E2B_API_KEY: ${E2B_API_KEY:+present}" +[ -n "${E2B_API_KEY:-}" ] || { echo "FATAL: E2B_API_KEY unset"; exit 1; } + +export PYTHONPATH="$TRL_PROD/trl:$TRL_PROD/HuggingEnvs/04-data-agent/train:$TRL_PROD/HuggingEnvs/04-data-agent/train/_pypath:$TRL_PROD/OpenEnv/src" +export TRL_EXPERIMENTAL_SILENCE=1 HF_HUB_ENABLE_HF_TRANSFER=1 TOKENIZERS_PARALLELISM=false +export PYTORCH_CUDA_ALLOC_CONF="${PYTORCH_CUDA_ALLOC_CONF:-expandable_segments:True}" + +PORT=$(( 8400 + ${SLURM_JOB_ID:-0} % 150 )) +export WHITE_BOX_BASH_URL="http://127.0.0.1:${PORT}" +LOGS="$TRL_PROD/HuggingEnvs/04-data-agent/envs/whitebox-bash/logs/job-${SLURM_JOB_ID:-local}" +mkdir -p "$LOGS" + +echo "== env server on :$PORT ==" +"$PY" -m uvicorn whitebox_bash.server.app:app --host 127.0.0.1 --port "$PORT" > "$LOGS/server.log" 2>&1 & +SERVER_PID=$! +trap 'kill $SERVER_PID 2>/dev/null || true' EXIT +for _ in $(seq 1 60); do + curl -sf "http://127.0.0.1:${PORT}/health" >/dev/null 2>&1 && break + kill -0 $SERVER_PID 2>/dev/null || { echo "FATAL: env server died"; tail -30 "$LOGS/server.log"; exit 1; } + sleep 2 +done +SPLIT="${SPLIT:-train:easy}" +N=$(curl -sf -X POST "http://127.0.0.1:${PORT}/white_box_bash/num_tasks" -H 'content-type: application/json' \ + -d "{\"split\":\"$SPLIT\"}" | "$PY" -c 'import json,sys; print(json.load(sys.stdin)["num_tasks"])') +NT=$(curl -sf -X POST "http://127.0.0.1:${PORT}/white_box_bash/num_tasks" -H 'content-type: application/json' \ + -d '{"split":"test"}' | "$PY" -c 'import json,sys; print(json.load(sys.stdin)["num_tasks"])') +echo "== server healthy: $SPLIT=$N test=$NT ==" +[ "$N" -ge 100 ] || { echo "FATAL: split $SPLIT has only $N tasks"; exit 1; } +# Tasks come from HuggingEnvs/data-agent, staged per task out of an HF bucket into the shared +# E2B template. Both are required: the default E2B base has no huggingface_hub and no writable +# /workdir, so staging dies before the agent starts. +echo "== task source: ${WHITE_BOX_BASH_TASK_SOURCE:-data-agent} template: ${E2B_TEMPLATE:-data-agent-opencode} ==" + +export MODEL="${MODEL:-Qwen/Qwen3.5-2B}" +export VLLM_GPU_MEM_UTIL="${VLLM_GPU_MEM_UTIL:-0.3}" + +echo "== trainer: ${MAX_STEPS:-100} steps over $N tasks, saving every ${SAVE_STEPS:-50} ==" +"$PY" "$TRL_PROD/HuggingEnvs/04-data-agent/train/train_whitebox_bash.py" \ + --model "$MODEL" --server "$WHITE_BOX_BASH_URL" --split "$SPLIT" \ + --toolsets "${TOOLSETS:-bash,seta}" --output-dir "$LOGS/run" \ + --max-steps "${MAX_STEPS:-100}" --save-steps "${SAVE_STEPS:-50}" \ + --num-generations "${NUM_GENERATIONS:-8}" --per-device-train-batch-size "${PER_DEVICE_BS:-2}" \ + --gradient-accumulation-steps "${GRAD_ACCUM:-4}" \ + --max-tool-calling-iterations "${MAX_TOOL_ITERS:-8}" --step-limit "${STEP_LIMIT:-12}" \ + --max-completion-length "${MAX_COMPLETION_LENGTH:-1024}" --report-to "${REPORT_TO:-none}" + +echo "== run finished; checkpoints: ==" +ls -d "$LOGS/run"/checkpoint-* 2>/dev/null || echo " (none)" diff --git a/04-data-agent/train/smoke_whitebox_bash.slurm b/04-data-agent/train/smoke_whitebox_bash.slurm new file mode 100755 index 0000000..d516539 --- /dev/null +++ b/04-data-agent/train/smoke_whitebox_bash.slurm @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +#SBATCH --job-name=wb-bash-smoke +#SBATCH --partition=hopper-extra,hopper-dev +#SBATCH --gres=gpu:1 +#SBATCH --cpus-per-task=24 +#SBATCH --output=/fsx/%u/logs/%x-%j.out +#SBATCH --error=/fsx/%u/logs/%x-%j.err +#SBATCH --time=0-02:00:00 +# +# SMOKE: sync GRPO on the WHITE-BOX bash/SETA environment. Prove the loop runs end to end. +# +# ONE GPU. vLLM runs COLOCATE -- in-process on the training card -- so there is no separate serve and +# no 2-GPU allocation. `vllm_gpu_memory_utilization` is what makes that fit: the trainer and the +# engine share one card and the default 0.9 would leave nothing for the optimizer states. +# +# The env server runs on THIS node, on a port derived from the job id. Fixed ports are how one job +# ends up talking to another job's server -- that happened on the async side and would have trained +# against the wrong weights. +set -euo pipefail + +TRL_PROD=/fsx/adithyaskolavi/projects/trl_prod +cd "$TRL_PROD" +PY="$TRL_PROD/.venv312/bin/python" + +# THE VENV'S bin/ MUST BE ON PATH, not just its python. +# Qwen3.5 is hybrid Gated-DeltaNet, and vLLM routes its prefill through flashinfer, which JIT-COMPILES +# a kernel at first generation. That compile shells out to `ninja` and `nvcc`. Calling +# .venv312/bin/python directly (rather than activating) leaves .venv312/bin off PATH, so `ninja` is +# not found and the run dies at step 0 with a bare +# FileNotFoundError: [Errno 2] No such file or directory: 'ninja' +# thirty frames below flashinfer, naming nothing that suggests PATH. The backend is chosen from vLLM's +# `additional_config["gdn_prefill_backend"]`, which colocate mode does not expose, so PATH is the fix. +export PATH="$TRL_PROD/.venv312/bin:$PATH" +# nvcc for the same JIT. Already present at /usr/local/cuda-12.9/bin on these nodes; the module load +# is belt-and-braces and must not fail the job when the module system has no such module. +module load cuda/12.9 2>/dev/null || module load cuda/13.0 2>/dev/null || true +command -v ninja >/dev/null || { echo "FATAL: ninja not on PATH; flashinfer's JIT will fail at step 0"; exit 1; } +command -v nvcc >/dev/null || echo "WARN: nvcc not on PATH; flashinfer's JIT may fail" +echo "ninja=$(command -v ninja) nvcc=$(command -v nvcc)" + +set -a; source "$TRL_PROD/experiments/.env" 2>/dev/null || true; set +a +export HF_TOKEN="${HF_TOKEN:-${HF_API_KEY:-}}" +# Presence only -- never the value. +echo "E2B_API_KEY: ${E2B_API_KEY:+present}" +[ -n "${E2B_API_KEY:-}" ] || { echo "FATAL: E2B_API_KEY unset; every episode would fail at sandbox creation"; exit 1; } + +export PYTHONPATH="$TRL_PROD/trl:$TRL_PROD/HuggingEnvs/04-data-agent/train:$TRL_PROD/HuggingEnvs/04-data-agent/train/_pypath:$TRL_PROD/OpenEnv/src" +export TRL_EXPERIMENTAL_SILENCE=1 +export HF_HUB_ENABLE_HF_TRANSFER=1 +export PYTORCH_CUDA_ALLOC_CONF="${PYTORCH_CUDA_ALLOC_CONF:-expandable_segments:True}" +export TOKENIZERS_PARALLELISM=false + +PORT=$(( 8400 + ${SLURM_JOB_ID:-0} % 150 )) +export WHITE_BOX_BASH_URL="http://127.0.0.1:${PORT}" +LOGS="$TRL_PROD/HuggingEnvs/04-data-agent/envs/whitebox-bash/logs/job-${SLURM_JOB_ID:-local}" +mkdir -p "$LOGS" + +echo "== env server on :$PORT ==" +"$PY" -m uvicorn whitebox_bash.server.app:app --host 127.0.0.1 --port "$PORT" \ + > "$LOGS/server.log" 2>&1 & +SERVER_PID=$! +# Kill the server on any exit, including failure: a survivor holds the port and the next job's +# server cannot bind, which presents as "stuck loading forever" rather than as a port clash. +trap 'kill $SERVER_PID 2>/dev/null || true' EXIT + +for _ in $(seq 1 60); do + curl -sf "http://127.0.0.1:${PORT}/health" >/dev/null 2>&1 && break + kill -0 $SERVER_PID 2>/dev/null || { echo "FATAL: env server died during startup"; tail -30 "$LOGS/server.log"; exit 1; } + sleep 2 +done +curl -sf "http://127.0.0.1:${PORT}/health" >/dev/null || { echo "FATAL: env server never became healthy"; tail -30 "$LOGS/server.log"; exit 1; } + +# GATE: the server must actually serve tasks. A healthy server with an empty split would train on +# nothing and report clean numbers doing it. +N=$(curl -sf -X POST "http://127.0.0.1:${PORT}/white_box_bash/num_tasks" \ + -H 'content-type: application/json' -d "{\"split\":\"${SPLIT:-train}\"}" | "$PY" -c 'import json,sys; print(json.load(sys.stdin)["num_tasks"])') +echo "== server healthy, split=${SPLIT:-train} has $N tasks ==" +[ "$N" -gt 0 ] || { echo "FATAL: split has no tasks"; exit 1; } + +export MODEL="${MODEL:-Qwen/Qwen3.5-2B}" +export VLLM_GPU_MEM_UTIL="${VLLM_GPU_MEM_UTIL:-0.3}" +export VLLM_MAX_MODEL_LEN="${VLLM_MAX_MODEL_LEN:-16384}" + +echo "== trainer (colocate vLLM, 1 GPU) ==" +"$PY" "$TRL_PROD/HuggingEnvs/04-data-agent/train/train_whitebox_bash.py" \ + --model "$MODEL" \ + --server "$WHITE_BOX_BASH_URL" \ + --split "${SPLIT:-train}" \ + --toolsets "${TOOLSETS:-bash,seta}" \ + --output-dir "$LOGS/run" \ + --max-steps "${MAX_STEPS:-4}" \ + --num-generations "${NUM_GENERATIONS:-4}" \ + --per-device-train-batch-size "${PER_DEVICE_BS:-4}" \ + --max-tool-calling-iterations "${MAX_TOOL_ITERS:-8}" \ + --step-limit "${STEP_LIMIT:-12}" \ + --max-completion-length "${MAX_COMPLETION_LENGTH:-1024}" \ + --report-to "${REPORT_TO:-none}" + +echo "== smoke finished ==" diff --git a/04-data-agent/train/standalone_comparison.py b/04-data-agent/train/standalone_comparison.py new file mode 100644 index 0000000..1c8ef60 --- /dev/null +++ b/04-data-agent/train/standalone_comparison.py @@ -0,0 +1,90 @@ +"""Native OpenCode sessions with the reference task schedule and binary reward. + +Only the session boundary differs from the multi-harness trainer. GPU loss, +whole-rollout admission, backpressure and checkpoint callbacks are shared. +""" +from __future__ import annotations + +from data_agent_env import DataAgentSessionFactory +from data_agent_env.harness import DataAgentSession, _instruction_of +from data_agent_env.task import instruction_id +from openenv.core.harness import VerifyResult + + +class ComparisonSession(DataAgentSession): + @property + def result(self): + return self._result + + @property + def _task_index(self): + return self._index + + def verify(self, transcript, final_state=None): + native = super().verify(transcript, final_state) + correctness = self._result.correctness if self._result is not None else None + # The comparison trains binary task success, like the reference and SETA. + # Keep partial chat credit and efficiency bonuses in the raw artifact only. + reward = None if correctness is None else float(correctness >= 1.0) + return VerifyResult(env_reward=reward, done=True, metrics=native.metrics, + artifacts={**native.artifacts, "reward_policy": "binary_correctness"}) + + +class ScheduledOpenCodeFactory(DataAgentSessionFactory): + def __init__(self, server, *, harnesses, schedule, group_offset=0, split="train", + sandbox="daytona", llm_url, model, sampling, reward_key="", + api_key="", agent_timeout_sec=600, agent_step_limit=17, + indices=None, num_tasks=None): + from harness_schedule import validate_schedule + validate_schedule(schedule) + if harnesses != ["opencode"] or schedule["harnesses"] != harnesses: + raise ValueError("Standalone training requires an OpenCode-only schedule") + if sampling != {"temperature": 0.8, "top_p": 1.0, "top_k": 0}: + raise ValueError("Standalone comparison requires the pinned sampling policy") + if group_offset < 0: + raise ValueError("Negative schedule offset") + super().__init__(server, split="train", llm_url=llm_url, model=model, + sandbox=sandbox, api_key=api_key, agent_step_limit=agent_step_limit, + agent_timeout_s=agent_timeout_sec, sampling=sampling) + self.harnesses, self.schedule, self.group_offset = harnesses, schedule, group_offset + self._rows, self._by_instruction = None, None + + def harness_for(self, seed): + return "opencode" + + def _new_client(self): + from data_agent_env import DataAgentEnv + return DataAgentEnv(self._server, message_timeout_s=1800) + + def prompt_rows(self): + if self._rows is None: + client = self._new_client() + try: + tasks = client.get_task_range("train") + finally: + client.close() + by_index = {task["index"]: task for task in tasks} + rows, lookup = [], {} + for expected in self.schedule["tasks"]: + task = by_index[expected["task_index"]] + if task["task_id"] != expected["name"]: + raise ValueError("Native Space task identity differs from the frozen schedule") + key = instruction_id(task["instruction"]) + if key in lookup: + raise ValueError("Ambiguous training instruction") + lookup[key] = expected["task_index"] + rows.append({"prompt": [{"role": "user", "content": task["instruction"]}], + "task_name": expected["name"], "task_index": expected["task_index"]}) + self._rows, self._by_instruction = rows, lookup + return self._rows + + def create(self, task, seed=None, episode_id=None): + self.prompt_rows() + instruction = _instruction_of(task) + index = self._by_instruction.get(instruction_id(instruction)) + absolute = (seed or 0) + self.group_offset + expected = self.schedule["groups"][absolute % len(self.schedule["groups"])] + if index != expected["task_index"]: + raise ValueError(f"Native schedule mismatch at group {absolute}: {index}") + return ComparisonSession(self._new_client(), "train", index, instruction, + **self._rollout_kwargs) diff --git a/04-data-agent/train/test_atomic_rollouts.py b/04-data-agent/train/test_atomic_rollouts.py new file mode 100644 index 0000000..615f9dd --- /dev/null +++ b/04-data-agent/train/test_atomic_rollouts.py @@ -0,0 +1,431 @@ +import asyncio +import pickle +import queue +from collections import defaultdict +from types import SimpleNamespace + +import pytest +import torch +from atomic_rollouts import ( + AtomicHarnessLoop, + AtomicRolloutDataset, + AtomicRolloutTrainer, + RolloutBundle, + pack_rows, +) +from transformers import Trainer, TrainingArguments +from trl.experimental.async_grpo.async_grpo_trainer import DataCollatorForRollout +from trl.experimental.async_grpo.async_rollout_worker import RolloutSample +from trl.experimental.async_grpo.openenv_harness import _HarnessRolloutLoop + + +def row(ids, mask=None, group=0, version=1, advantage=0.7): + return RolloutSample( + [], + [], + ids, + mask or [0] + [1] * (len(ids) - 1), + [-0.8] * len(ids), + advantage, + version, + group, + {"reward": 1.0}, + ) + + +def worker(bundles, version=1): + q = queue.Queue() + for b in bundles: + q.put(b) + return SimpleNamespace( + rollout_buffer=q, + model_version=version, + check_health=lambda timeout: pytest.fail("Unexpected empty queue"), + ) + + +def test_bundle_survives_worker_process_pickle(): + original = RolloutBundle([row([1, 2, 3]), row([1, 4])], "episode") + copy = pickle.loads(pickle.dumps(original)) + assert copy.rollout_id == "episode" and len(copy.rows) == 2 + assert copy.model_version == 1 and copy.group_id == 0 + + +def test_native_scored_rows_keep_their_rollout_and_advantage(monkeypatch): + original = [row([1, 2]), row([1, 3]), row([1, 4], advantage=-0.2)] + + async def score(self, group): + return original + + monkeypatch.setattr(_HarnessRolloutLoop, "_score_group", score) + group = SimpleNamespace( + group_id=0, + completions_sequences=[ + [SimpleNamespace(rollout_id="a"), SimpleNamespace(rollout_id="a")], + [], + [SimpleNamespace(rollout_id="c")], + ], + ) + bundles = asyncio.run( + AtomicHarnessLoop._score_group(object.__new__(AtomicHarnessLoop), group) + ) + assert [b.rollout_id for b in bundles] == ["a", "c"] + assert bundles[0].rows == original[:2] and bundles[1].rows == original[2:] + assert bundles[1].advantage == -0.2 + + +def test_forked_rollout_is_admitted_whole_and_forwarded_without_token_changes(): + bundle = RolloutBundle( + [row([1, 2, 3]), row([4, 5, 6, 7, 8, 9]), row([1, 4])], "fork" + ) + data = AtomicRolloutDataset(worker([bundle]), defaultdict(list), 4, 8, 4, 60) + unit = next(iter(data)) + assert unit["rollouts"] == [bundle] + packs = list(pack_rows(unit["rollouts"], 4, 8)) + result = [r for pack in packs for r in pack] + assert [r["input_ids"] for r in result] == [r.input_ids for r in bundle.rows] + assert [r["completion_mask"] for r in result] == [ + r.completion_mask for r in bundle.rows + ] + assert [r["old_log_probs"] for r in result] == [ + r.old_log_probs for r in bundle.rows + ] + + +def test_pending_rollout_is_rechecked_for_staleness_and_never_partially_dropped(): + a = RolloutBundle([row([1, 2, 3])], "a") + b = RolloutBundle([row([1, 2, 3]), row([1, 4])], "b") + c = RolloutBundle([row([1, 2, 3, 4], version=6)], "c") + w = worker([a, b, c]) + metrics = defaultdict(list) + data = AtomicRolloutDataset(w, metrics, 4, 8, 4, 60) + it = iter(data) + assert [x.rollout_id for x in next(it)["rollouts"]] == ["a"] + w.model_version = 6 + assert [x.rollout_id for x in next(it)["rollouts"]] == ["c"] + assert metrics["admission/stale_rollouts_dropped_total"] == [1] + assert metrics["sample/dropped_stale_total"] == [2] + + +def test_context_overflow_fails_instead_of_silently_losing_rows(): + data = AtomicRolloutDataset( + worker([RolloutBundle([row(list(range(10)))], "long")]), + defaultdict(list), + 4, + 8, + 4, + 60, + ) + with pytest.raises(RuntimeError, match="refusing to discard"): + next(iter(data)) + + +class ToyModel(torch.nn.Module): + def __init__(self): + super().__init__() + self.theta = torch.nn.Parameter(torch.tensor(0.02)) + + def forward(self, input_ids, **kwargs): + lp = self.theta - 0.8 + input_ids[:, 1:] * 0.003 + return {"log_probs": lp, "entropy": torch.ones_like(lp)} + + +def native_loss_trainer(): + trainer = object.__new__(AtomicRolloutTrainer) + trainer.epsilon_low = trainer.epsilon_high = 0.2 + trainer.aux_loss_enabled = False + trainer.accelerator = SimpleNamespace( + num_processes=1, + reduce=lambda value, reduction: value, + gather=lambda value: value, + ) + trainer._metrics = {"train": defaultdict(list)} + trainer.current_gradient_accumulation_steps = 4 + for name in [ + "_step_forward_tokens", + "_step_trained_tokens", + "_step_seq_len_weighted", + "_step_samples", + "_step_forward_s", + ]: + setattr(trainer, name, 0.0) + return trainer + + +@pytest.mark.parametrize("target", [4, 8, 100]) +def test_actual_trl_loss_gradient_is_invariant_to_fork_packing(target): + bundles = [ + RolloutBundle( + [row([1, 2, 3], [0, 0, 1]), row([1, 4, 5, 6], advantage=-0.2)], "a" + ), + RolloutBundle([row([1, 7, 8, 9], [0, 0, 1, 1])], "b"), + ] + trainer = native_loss_trainer() + model = ToyModel() + denominator = sum(sum(r.completion_mask) for b in bundles for r in b.rows) + trainer._atomic_normalization_tokens = denominator + collator = DataCollatorForRollout(0) + total = torch.zeros(()) + for packed in pack_rows(bundles, target, 16): + loss = trainer.compute_loss(model, collator([[packed]])) + loss.backward() + total += loss.detach() + expected_model = ToyModel() + expected = torch.zeros(()) + for b in bundles: + for r in b.rows: + ids = torch.tensor(r.input_ids[1:]) + mask = torch.tensor(r.completion_mask[1:]) + expected += ( + -torch.exp(expected_model.theta + ids * 0.003) * r.advantage * mask + ).sum() + expected /= denominator + expected.backward() + torch.testing.assert_close(total, expected.detach()) + torch.testing.assert_close(model.theta.grad, expected_model.theta.grad) + assert trainer._step_trained_tokens == denominator + + +@pytest.mark.parametrize("credit_limit", [0, 8]) +def test_real_hf_optimizer_loop_consumes_all_forks_before_updating( + tmp_path, credit_limit +): + import json + + from atomic_rollouts import AtomicAdmissionCallback + + class CPUTrainer(AtomicRolloutTrainer): + _inner_training_loop = Trainer._inner_training_loop + log = Trainer.log + + def __init__(self): + args = TrainingArguments( + output_dir=str(tmp_path), + use_cpu=True, + max_steps=2, + gradient_accumulation_steps=4, + learning_rate=1e-3, + report_to=[], + save_strategy="no", + logging_strategy="no", + disable_tqdm=True, + ) + args.token_budget = 4 + args.max_staleness = 4 + args.heartbeat_stale_after_s = 60 + Trainer.__init__( + self, + model=ToyModel(), + args=args, + compute_loss_func="native AsyncGRPO disables HF loss scaling", + ) + self.model_accepts_loss_kwargs = False + self.processing_class = SimpleNamespace(pad_token_id=0) + self.rollout_worker = worker( + [ + RolloutBundle( + [row([1, 2, 3], group=i // 4), row([1, 4, 5], group=i // 4)], + f"episode-{i}", + ) + for i in range(8) + ] + ) + self.released_rollouts = [] + if credit_limit: + self.rollout_worker.max_outstanding_rollouts = credit_limit + self.rollout_worker.num_generations = 4 + self.rollout_worker.release_rollouts = self.released_rollouts.append + self.max_row_tokens = 8 + self._trained_groups = set() + self._groups_before_resume = 0 + self._metrics = {"train": defaultdict(list)} + self.epsilon_low = self.epsilon_high = 0.2 + self.aux_loss_enabled = False + self.admission_dir = tmp_path + self._atomic_finished = [] + for name in [ + "_step_forward_tokens", + "_step_trained_tokens", + "_step_seq_len_weighted", + "_step_samples", + "_step_forward_s", + "_step_microbatches", + "_current_train_step_time", + ]: + setattr(self, name, 0.0) + self.add_callback(AtomicAdmissionCallback(self)) + + trainer = CPUTrainer() + before = trainer.model.theta.detach().clone() + trainer.train() + assert trainer.state.global_step == 2 + assert trainer._step_microbatches == 16 + assert not torch.equal(before, trainer.model.theta.detach()) + receipts = [ + json.loads(line) + for line in (tmp_path / "optimizer_rollouts.jsonl").read_text().splitlines() + ] + assert [r["step"] for r in receipts] == [1, 2] + assert all( + len(r["rollouts"]) == 4 and r["all_admitted_rows_consumed"] for r in receipts + ) + assert sum(x["rows"] for r in receipts for x in r["rollouts"]) == 16 + assert len({x["rollout_id"] for r in receipts for x in r["rollouts"]}) == 8 + assert trainer.released_rollouts == ([4, 4] if credit_limit else []) + + +def _use_spawned_credits(channel, result): + first = channel.reserve_group(4) + second = channel.reserve_group(4) + channel.release(2) + result.put((first, second, channel.credits.value)) + + +def test_credit_queue_uses_native_spawn_ipc(): + import multiprocessing + + from atomic_rollouts import CreditQueue + + ctx = multiprocessing.get_context("spawn") + channel = CreditQueue(ctx.Queue(), ctx.Value("i", 6), 6) + result = ctx.Queue() + child = ctx.Process(target=_use_spawned_credits, args=(channel, result)) + child.start() + try: + assert result.get(timeout=150) == (True, False, 4) + child.join(timeout=15) + assert child.exitcode == 0 and channel.credits.value == 4 + with pytest.raises(RuntimeError, match="more than once"): + channel.release(3) + finally: + if child.is_alive(): + child.terminate() + child.join() + channel.close() + result.close() + + +def test_generation_reserves_complete_groups_and_tags_actual_dispatch(monkeypatch): + import multiprocessing + + from atomic_rollouts import CreditQueue + from trl.experimental.async_grpo.async_rollout_worker import RolloutGroup + + ctx = multiprocessing.get_context("spawn") + channel = CreditQueue(queue.Queue(), ctx.Value("i", 4), 4) + loop = object.__new__(AtomicHarnessLoop) + loop.rollout_buffer = channel + loop.num_generations = 2 + loop._model_version_value = SimpleNamespace(value=1) + loop._stop_event = asyncio.Event() + called = [] + + async def generate(self, prompt, tool_dict, tools, group_id=0): + called.append((group_id, self.model_version)) + return ([], [], [SimpleNamespace(rollout_id=f"{group_id}-{prompt}")], 0, 0, 1.0) + + async def score(self, group): + assert group.model_version == 5 + return [row([1, 2], version=5) for _ in group.completions_sequences] + + monkeypatch.setattr(_HarnessRolloutLoop, "_generate_one", generate) + monkeypatch.setattr(_HarnessRolloutLoop, "_score_group", score) + + async def exercise(): + tasks = [ + asyncio.create_task(loop._generate_one(i, {}, [], i // 2)) for i in range(6) + ] + await asyncio.sleep(0.15) + assert len(called) == 4 and channel.credits.value == 0 + assert sum(task.done() for task in tasks) == 4 + loop._model_version_value.value = 5 + channel.release(2) # two whole rollouts consumed by the optimizer + results = await asyncio.wait_for(asyncio.gather(*tasks), 2) + assert called[-2:] == [(2, 5), (2, 5)] + group = RolloutGroup( + [], {}, [], [], [r[2] for r in results[-2:]], [], [], 1, 2, [], [] + ) + bundles = await loop._score_group(group) + assert [b.model_version for b in bundles] == [5, 5] + assert channel.credits.value == 0 # scoring alone does not release credits + + asyncio.run(exercise()) + + +def test_empty_generation_returns_its_reserved_credit(monkeypatch): + import multiprocessing + + from atomic_rollouts import CreditQueue + + ctx = multiprocessing.get_context("spawn") + loop = object.__new__(AtomicHarnessLoop) + loop.rollout_buffer = CreditQueue(queue.Queue(), ctx.Value("i", 4), 4) + loop.num_generations = 2 + loop._model_version_value = SimpleNamespace(value=1) + loop._stop_event = asyncio.Event() + + async def empty(*args, **kwargs): + return ([], [], [], 0, 0, None) + + monkeypatch.setattr(_HarnessRolloutLoop, "_generate_one", empty) + + async def exercise(): + await asyncio.gather( + loop._generate_one(0, {}, [], 0), loop._generate_one(1, {}, [], 0) + ) + assert loop.rollout_buffer.credits.value == 4 + + asyncio.run(exercise()) + + +def test_small_rows_cannot_hold_all_credits_waiting_for_token_target(): + # Sixteen outstanding generations, G8 and GAS4: each unit may consume at + # most two rollouts, reserving room for a complete new group until update. + bundles = [RolloutBundle([row([1, 2])], f"episode-{i}") for i in range(8)] + data = AtomicRolloutDataset( + worker(bundles), + defaultdict(list), + 40960, + 131072, + 4, + 60, + max_rollouts_per_unit=2, + ) + it = iter(data) + batches = [next(it) for _ in range(4)] + assert [len(b["rollouts"]) for b in batches] == [2, 2, 2, 2] + + +def test_multiple_stale_rollouts_release_credits_and_log_exact_totals(tmp_path): + import json + + from trl.experimental.async_grpo.async_grpo_trainer import _reduce_metric + + old = [RolloutBundle([row([1, 2])] * n, f"old-{n}") for n in (2, 3)] + new = RolloutBundle([row([1, 2, 3, 4], version=6)], "new") + w = worker(old + [new], version=6) + released = [] + w.release_rollouts = released.append + metrics = defaultdict(list) + path = tmp_path / "rejections.jsonl" + data = AtomicRolloutDataset( + w, metrics, 4, 8, 4, 60, rejection_path=path, group_offset=81 + ) + assert next(iter(data))["rollouts"] == [new] + assert released == [1, 1] + assert ( + _reduce_metric( + "admission/stale_rollouts_dropped_total", + metrics["admission/stale_rollouts_dropped_total"], + ) + == 2 + ) + assert ( + _reduce_metric( + "sample/dropped_stale_total", metrics["sample/dropped_stale_total"] + ) + == 5 + ) + records = [json.loads(line) for line in path.read_text().splitlines()] + assert [r["rows"] for r in records] == [2, 3] + assert all(r["group_id"] == 81 for r in records) diff --git a/04-data-agent/train/test_checkpoint_artifacts.py b/04-data-agent/train/test_checkpoint_artifacts.py new file mode 100644 index 0000000..caa9da3 --- /dev/null +++ b/04-data-agent/train/test_checkpoint_artifacts.py @@ -0,0 +1,99 @@ +"""A partial or changed checkpoint must never be evaluated as completed weights.""" +import json + +import numpy as np +import pytest +from safetensors.numpy import save_file + +from checkpoint_artifacts import REQUIRED, mark_ready, mark_saved, finalize_saved, stage_model, verify_stage, resume_info + + +def test_trainer_handoff_does_not_hash_weights_and_cpu_finalizes(tmp_path, monkeypatch): + root = checkpoint(tmp_path / 'checkpoint-50', sharded=True) + with monkeypatch.context() as patch: + patch.setattr('checkpoint_artifacts.digest', lambda _: pytest.fail('Weight hashing ran on trainer')) + mark_saved(root, 50, 'base', 'revision') + assert (root / 'checkpoint.saved.json').exists() + assert not (root / 'checkpoint.ready.json').exists() + assert finalize_saved(root)['step'] == 50 + + +def test_cpu_rejects_checkpoint_modified_after_handoff(tmp_path): + root = checkpoint(tmp_path / 'checkpoint-50') + mark_saved(root, 50, 'base', 'revision') + (root / 'optimizer.pt').write_text('{"changed":true}') + with pytest.raises(ValueError, match='changed after save'): + finalize_saved(root) + + +def checkpoint(root, *, sharded=False): + root.mkdir() + for name in REQUIRED: + (root / name).write_text('{}') + (root / 'config.json').write_text('{"trained_config":true}') + (root / 'trainer_state.json').write_text('{"global_step":50}') + if sharded: + save_file({'a': np.ones(2)}, root / 'model-1.safetensors') + save_file({'b': np.ones(2)}, root / 'model-2.safetensors') + (root / 'model.safetensors.index.json').write_text(json.dumps({ + 'weight_map': {'a': 'model-1.safetensors', 'b': 'model-2.safetensors'}})) + else: + save_file({'weight': np.ones(2)}, root / 'model.safetensors') + return root + + +def test_missing_optimizer_state_cannot_publish(tmp_path): + root = checkpoint(tmp_path / 'checkpoint-50') + (root / 'optimizer.pt').unlink() + with pytest.raises(ValueError, match='optimizer.pt'): + mark_ready(root, 50, 'base', 'revision') + assert not (root / 'checkpoint.ready.json').exists() + + +def test_missing_shard_cannot_publish(tmp_path): + root = checkpoint(tmp_path / 'checkpoint-50', sharded=True) + (root / 'model-2.safetensors').unlink() + with pytest.raises(FileNotFoundError): + mark_ready(root, 50, 'base', 'revision') + assert not (root / 'checkpoint.ready.json').exists() + + +def test_stage_preserves_trained_config_and_detects_changed_weights(tmp_path): + root = checkpoint(tmp_path / 'checkpoint-50', sharded=True) + mark_ready(root, 50, 'base', 'revision') + metadata = tmp_path / 'base-metadata' + metadata.mkdir() + (metadata / 'config.json').write_text('{"wrong_base_config":true}') + (metadata / 'video_preprocessor_config.json').write_text('{}') + target = tmp_path / 'staged' + stage_model(root, target, metadata) + assert json.loads((target / 'config.json').read_text()) == {'trained_config': True} + assert (target / 'video_preprocessor_config.json').exists() + assert (target / 'model-1.safetensors').is_symlink() + assert verify_stage(target)['checkpoint']['step'] == 50 + save_file({'a': np.zeros(2)}, root / 'model-1.safetensors') + with pytest.raises(ValueError, match='Staged checkpoint changed'): + verify_stage(target) + + +def test_wrong_optimizer_step_cannot_publish(tmp_path): + root = checkpoint(tmp_path / 'checkpoint-50') + with pytest.raises(ValueError, match='trainer state'): + mark_ready(root, 100, 'base', 'revision') + + +def test_resume_requires_complete_unchanged_optimizer_and_cursor(tmp_path): + root = checkpoint(tmp_path / 'checkpoint-50') + with pytest.raises(ValueError, match='completed checkpoint'): + resume_info(root, 'base', 'revision') + mark_saved(root, 50, 'base', 'revision') + with pytest.raises(ValueError, match='rollout_state'): + resume_info(root, 'base', 'revision') + (root / 'rollout_state.json').write_text('{"prompt_index":37,"model_version":50}') + info = resume_info(root, 'base', 'revision') + assert info['step'] == 50 and info['group_offset'] == 37 and info['model_version'] == 50 + with pytest.raises(ValueError, match='model/revision'): + resume_info(root, 'wrong-model', 'revision') + (root / 'optimizer.pt').write_text('{"changed":true}') + with pytest.raises(ValueError, match='changed after save'): + resume_info(root, 'base', 'revision') diff --git a/04-data-agent/train/test_continue_allocation.py b/04-data-agent/train/test_continue_allocation.py new file mode 100644 index 0000000..e222814 --- /dev/null +++ b/04-data-agent/train/test_continue_allocation.py @@ -0,0 +1,101 @@ +import json +from pathlib import Path + +import pytest + +import continue_allocation as continuation + + +def parent_run(tmp_path): + root = tmp_path / "parent" + root.mkdir() + config = { + "model": "model", "model_revision": "revision", + "training": {"max_steps": 1000, "save_steps": 50, "soft_max_train_seconds": 42000, + "learning_rate": 3e-6, "gradient_accumulation_steps": 4, "num_generations": 8}, + "resources": {"slurm_walltime": "12:00:00"}, + "dataset": {"schedule_sha256": "fixed"}, + "evaluation": {"interval_optimizer_steps": 100, "concurrency": 100}, + "logging": {"project": "existing-project", "space_id": "existing-space"}, + "monitoring": {"support_job_supervisor": {}}, + } + continuation.save(root / "run_config.json", config) + continuation.save(root / "submission.json", {"training": "123"}) + for name in ("manifest.json", "indices.txt", "harness_schedule.json", "pairs.jsonl", + "runtime_versions.json", "source_hashes.json"): + (root / name).write_text("{}") + (root / "source-snapshot").mkdir() + (root / "checkpoint-evals/eval-source").mkdir(parents=True) + return root, config + + +def test_latest_incomplete_checkpoint_is_never_selected(tmp_path): + root, config = parent_run(tmp_path) + for step in (450, 500): + (root / f"job-123/run/checkpoint-{step}").mkdir(parents=True) + + def validate(path, model, revision): + if path.name == "checkpoint-500": + raise ValueError("optimizer save is incomplete") + return {"step": 450, "checkpoint": str(path)} + + resume, rejected = continuation.select_checkpoint(root, "123", config, validate) + assert resume["step"] == 450 + assert len(rejected) == 1 + + +def test_prepare_preserves_training_and_frozen_sources(tmp_path): + root, config = parent_run(tmp_path) + target = tmp_path / "next" + resume = {"step": 650, "checkpoint": str(root / "job-123/run/checkpoint-650")} + prepared = continuation.prepare(root, target, resume) + for key, value in config["training"].items(): + if key != "soft_max_train_seconds": + assert prepared["training"][key] == value + assert prepared["training"]["soft_max_train_seconds"] == 82200 + assert prepared["resources"]["slurm_walltime"] == "24:00:00" + assert prepared["logging"]["project"] == config["logging"]["project"] + assert prepared["evaluation"]["interval_optimizer_steps"] == 100 + assert (target / "source-snapshot").resolve() == root / "source-snapshot" + assert (target / "checkpoint-evals/eval-source").resolve() == root / "checkpoint-evals/eval-source" + assert continuation.read(root / "run_config.json") == config + + +def test_cancellation_and_explicit_stop_are_respected(tmp_path): + root, _ = parent_run(tmp_path) + assert continuation.may_continue(root, "123", "TIMEOUT") + assert not continuation.may_continue(root, "123", "CANCELLED") + assert not continuation.may_continue(root, "123", "RUNNING") + (root / "job-123").mkdir() + (root / "job-123/STOP_AFTER_STEP").touch() + assert not continuation.may_continue(root, "123", "COMPLETED") + + +def test_partial_submission_requires_reconciliation(tmp_path): + continuation.save(tmp_path / "submission.json", {"training": "456"}) + with pytest.raises(RuntimeError, match="no duplicate GPU"): + continuation.existing_submission(tmp_path) + + +def test_completed_target_submits_nothing(tmp_path, monkeypatch): + root, _ = parent_run(tmp_path) + monkeypatch.setattr(continuation, "parent_state", lambda job: "COMPLETED") + monkeypatch.setattr(continuation, "select_checkpoint", lambda *args: ({"step": 1000}, [])) + result = continuation.execute(root, tmp_path / "next", "123") + assert result["state"] == "complete" + assert not (tmp_path / "next").exists() + + +def test_completed_submission_is_not_duplicated(tmp_path, monkeypatch): + root, _ = parent_run(tmp_path) + target = tmp_path / "next" + resume = {"step": 650, "checkpoint": str(root / "job-123/run/checkpoint-650")} + continuation.prepare(root, target, resume) + jobs = dict(zip(continuation.ROLES, map(str, range(456, 461)))) + continuation.save(target / "submission.json", jobs) + continuation.save(root / "operations/allocation-continuation/status.json", {"supervisor_job": "461"}) + monkeypatch.setattr(continuation, "parent_state", lambda job: "COMPLETED") + monkeypatch.setattr(continuation, "select_checkpoint", lambda *args: (resume, [])) + monkeypatch.setattr(continuation.subprocess, "run", lambda *a, **kw: pytest.fail("duplicate submission")) + monkeypatch.setattr(continuation.subprocess, "check_output", lambda *a, **kw: pytest.fail("duplicate supervisor")) + assert continuation.execute(root, target, "123")["submission"] == jobs diff --git a/04-data-agent/train/test_multi_harness_schedule.py b/04-data-agent/train/test_multi_harness_schedule.py new file mode 100644 index 0000000..dd28427 --- /dev/null +++ b/04-data-agent/train/test_multi_harness_schedule.py @@ -0,0 +1,163 @@ +"""Guard complete task/harness dispatch and stopping at a saved optimizer boundary.""" +from types import SimpleNamespace +from collections import Counter +from copy import deepcopy + +import pytest + +from multi_harness import pair_rows, MultiHarborSessionFactory +from training_audit import PairCoverageCallback, PeriodicCheckpointCallback, WallTimeCallback +from harness_schedule import make_schedule, validate_schedule + + +def test_balanced_rotation_routes_tasks_and_harnesses_across_four_passes(tmp_path): + harnesses = ['opencode', 'claude-code', 'codex', 'mini-swe-agent'] + tiers = ['easy'] * 150 + ['medium'] * 600 + ['hard'] * 250 + tasks = [{'name': f'task-{i}', 'task_index': 999 - i, 'difficulty': tier} + for i, tier in enumerate(tiers)] + schedule = make_schedule(tasks, harnesses) + assert schedule == make_schedule(tasks, harnesses) + factory = SimpleNamespace(harnesses=harnesses, schedule=schedule, + prompt_rows=lambda: [{'task_name': t['name'], 'task_index': t['task_index']} for t in tasks]) + rows = pair_rows(factory) + assert len(rows) == 4000 + from itertools import islice + from trl.experimental.async_grpo.async_rollout_worker import _AsyncRolloutLoop + worker = SimpleNamespace(dataset=rows, _dataset_iter=iter(rows), num_generations=4) + dispatched = list(islice(_AsyncRolloutLoop._repeat_iterator(worker), 16004)) + for group_id, row in dispatched: + expected = schedule['groups'][group_id % 4000] + assert row['task_index'] == expected['task_index'] + assert MultiHarborSessionFactory.harness_for(factory, group_id) == expected['harness'] + for p in range(4): + groups = schedule['groups'][p * 1000:(p + 1) * 1000] + assert len({g['task_name'] for g in groups}) == 1000 + assert Counter(g['harness'] for g in groups) == {h: 250 for h in harnesses} + assert [g['harness'] for g in groups] == harnesses * 250 + for g in groups: + for _ in range(4): # GRPO generations share their group ID and route. + assert MultiHarborSessionFactory.harness_for(factory, g['group_in_cycle']) == g['harness'] + assert rows[g['group_in_cycle']]['task_index'] == g['task_index'] + assert [g['task_row'] for g in schedule['groups'][:32]] == list(range(32)) + assert len({(g['task_name'], g['harness']) for g in schedule['groups']}) == 4000 + trainer = SimpleNamespace(_trained_groups=set(range(1000))) + callback = PairCoverageCallback(trainer, len(rows), harnesses, 0, tmp_path, schedule=schedule) + control = SimpleNamespace(should_training_stop=False) + callback.on_step_end(SimpleNamespace(max_steps=1000), SimpleNamespace(global_step=200), control) + callback.on_step_end(SimpleNamespace(max_steps=1000), SimpleNamespace(global_step=201), control) + import json + coverage = json.loads((tmp_path / 'coverage.json').read_text()) + assert coverage['unique_tasks_covered'] == 1000 + assert not coverage['pair_coverage_complete'] + assert coverage['harness_pair_counts'] == {h: 250 for h in harnesses} + with pytest.raises(ValueError, match='Cartesian'): + pair_rows(factory, all_pairs=True) + factory.prompt_rows = lambda: list(reversed(rows[:1000])) + with pytest.raises(ValueError, match='Server task identities'): + pair_rows(factory) + changed = deepcopy(schedule) + changed['task_count'] = 4000 + with pytest.raises(ValueError, match='metadata'): + validate_schedule(changed) + + +def test_cartesian_dispatch_reaches_each_pair_once_per_cycle(tmp_path): + harnesses = ['opencode', 'claude-code', 'codex', 'mini-swe-agent'] + factory = SimpleNamespace(harnesses=harnesses, prompt_rows=lambda: [{'task': i} for i in range(5)]) + rows = pair_rows(factory, all_pairs=True) + assert len(rows) == 20 + for cycle in range(2): + pairs = [(rows[g % 20]['task'], MultiHarborSessionFactory.harness_for(factory, g)) + for g in range(cycle * 20, (cycle + 1) * 20)] + assert len(set(pairs)) == 20 + assert set(pairs) == {(i, h) for i in range(5) for h in harnesses} + trainer = SimpleNamespace(_trained_groups=set(range(20))) + callback = PairCoverageCallback(trainer, len(rows), harnesses, 20, tmp_path, all_pairs=True) + control = SimpleNamespace(should_training_stop=False) + callback.on_step_end(SimpleNamespace(max_steps=40), SimpleNamespace(global_step=20), control) + assert not control.should_training_stop # a prefetched row is not proof it was trained + callback.on_step_end(SimpleNamespace(max_steps=40), SimpleNamespace(global_step=21), control) + assert control.should_training_stop + + +def test_wall_time_stop_requests_a_checkpoint(monkeypatch, tmp_path): + clock = [0] + monkeypatch.setattr('training_audit.time.monotonic', lambda: clock[0]) + callback = WallTimeCallback(60) + control = SimpleNamespace(should_training_stop=False, should_save=False) + args = SimpleNamespace(output_dir=str(tmp_path / 'run')) + callback.on_train_begin(None, None, control) + clock[0] = 59 + callback.on_step_end(args, None, control) + assert not control.should_training_stop + clock[0] = 60 + callback.on_step_end(args, None, control) + assert control.should_training_stop and control.should_save + + +def test_resume_keeps_task_and_harness_aligned_across_schedule_wrap(tmp_path): + from itertools import islice + import json + from trl.experimental.async_grpo.async_rollout_worker import _AsyncRolloutLoop + harnesses = ['opencode', 'claude-code', 'codex', 'mini-swe-agent'] + tasks = [{'name': f'task-{i}', 'task_index': i, 'difficulty': 'easy'} for i in range(1000)] + schedule = make_schedule(tasks, harnesses) + factory = SimpleNamespace(harnesses=harnesses, schedule=schedule, group_offset=3997, + prompt_rows=lambda: [{'task_name': t['name'], 'task_index': t['task_index']} for t in tasks]) + rows = pair_rows(factory) + worker = SimpleNamespace(dataset=rows, _dataset_iter=iter(rows[3997:]), num_generations=8) + for local_group, row in islice(_AsyncRolloutLoop._repeat_iterator(worker), 64): + expected = schedule['groups'][(3997 + local_group) % 4000] + assert row['task_index'] == expected['task_index'] + assert MultiHarborSessionFactory.harness_for(factory, local_group) == expected['harness'] + callback = PairCoverageCallback(SimpleNamespace(_trained_groups={0, 1, 2, 3}), len(rows), + harnesses, 0, tmp_path, schedule=schedule, group_offset=3997) + for step in [51, 52]: + callback.on_step_end(SimpleNamespace(max_steps=1000), SimpleNamespace(global_step=step), + SimpleNamespace(should_training_stop=False)) + coverage = json.loads((tmp_path / 'coverage.json').read_text()) + assert coverage['collated_group_ids'] == [3997, 3998, 3999, 4000] + expected = {(schedule['groups'][g % 4000]['task_row'], schedule['groups'][g % 4000]['harness']) + for g in coverage['collated_group_ids']} + assert {(p['task_row'], p['harness']) for p in coverage['covered_pairs']} == expected + + +def test_operator_stop_saves_at_optimizer_boundary(tmp_path): + callback = WallTimeCallback(82800) + args = SimpleNamespace(output_dir=str(tmp_path / 'run')) + control = SimpleNamespace(should_training_stop=False, should_save=False) + callback.on_step_end(args, None, control) + assert not control.should_save + (tmp_path / 'STOP_AFTER_STEP').touch() + callback.on_step_end(args, None, control) + assert control.should_training_stop and control.should_save + + +def test_hourly_checkpoint_saves_without_stopping_and_regular_save_resets_clock(monkeypatch): + clock = [0] + monkeypatch.setattr('training_audit.time.monotonic', lambda: clock[0]) + callback = PeriodicCheckpointCallback(3600) + control = SimpleNamespace(should_training_stop=False, should_save=False) + callback.on_train_begin(None, None, control) + clock[0] = 3599 + callback.on_step_end(None, None, control) + assert not control.should_save + clock[0] = 3700 # saves only after a complete optimizer update + callback.on_step_end(None, None, control) + assert control.should_save and not control.should_training_stop + callback.on_save(None, None, control) + control.should_save = False + clock[0] = 4000 + callback.on_save(None, None, control) # regular checkpoint-50 resets the same timer + clock[0] = 7300 + callback.on_step_end(None, None, control) + assert not control.should_save + clock[0] = 7600 + callback.on_step_end(None, None, control) + assert control.should_save and not control.should_training_stop + + +@pytest.mark.parametrize('seconds', [0, -1]) +def test_hourly_checkpoint_requires_positive_interval(seconds): + with pytest.raises(ValueError, match='positive'): + PeriodicCheckpointCallback(seconds) diff --git a/04-data-agent/train/test_prepare_harbor_opencode_run.py b/04-data-agent/train/test_prepare_harbor_opencode_run.py new file mode 100644 index 0000000..f993ac4 --- /dev/null +++ b/04-data-agent/train/test_prepare_harbor_opencode_run.py @@ -0,0 +1,32 @@ +from copy import deepcopy +from types import SimpleNamespace + +from harness_schedule import make_schedule, validate_schedule +from prepare_harbor_opencode_run import single_harness_schedule + + +def test_single_harness_preserves_reference_order_without_reshuffling(): + tasks=[{'name':f'task-{i}','task_index':999-i,'difficulty':tier} + for i,tier in enumerate(['easy']*150+['medium']*600+['hard']*250)] + reference=make_schedule(tasks,['opencode','claude-code','codex','mini-swe-agent']) + before=deepcopy(reference) + actual=single_harness_schedule(reference) + assert reference==before + assert actual['harnesses']==['opencode'] + assert actual['groups_per_cycle']==1000 and actual['passes_per_cycle']==1 + assert [g['task_index'] for g in actual['groups']]==[g['task_index'] for g in reference['groups'][:1000]] + assert {g['harness'] for g in actual['groups']}=={'opencode'} + validate_schedule(actual) + + +def test_all_generations_route_through_opencode_across_cycle_boundary(): + from multi_harness import pair_rows,MultiHarborSessionFactory + tasks=[{'name':f'task-{i}','task_index':i,'difficulty':'easy'} for i in range(40)] + schedule=single_harness_schedule(make_schedule(tasks,['opencode','claude-code','codex','mini-swe-agent'])) + factory=SimpleNamespace(schedule=schedule,harnesses=['opencode'],group_offset=0, + prompt_rows=lambda:[{'task_name':t['name'],'task_index':t['task_index']} for t in tasks]) + rows=pair_rows(factory) + for group in range(85): + for generation in range(8): + assert MultiHarborSessionFactory.harness_for(factory,group)=='opencode' + assert rows[group%40]['task_index']==schedule['groups'][group%40]['task_index'] diff --git a/04-data-agent/train/train_blackbox_opencode.py b/04-data-agent/train/train_blackbox_opencode.py new file mode 100644 index 0000000..8d99598 --- /dev/null +++ b/04-data-agent/train/train_blackbox_opencode.py @@ -0,0 +1,212 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""AsyncGRPO on `blackbox-opencode`. TRL hosts vLLM and orchestrates; the env does the rest. + +WHAT IS ABSENT IS THE POINT. The run this reproduces needed two monkeypatches and got one of them +silently wrong: + + * `prompt_ids_patch` rebound TRL's `_turns_from_trace` to prefer the engine's prompt ids. It never + took effect. The rollout loop runs in a multiprocessing child created with `spawn`, which + re-imports every module, so a parent-side rebind of a module function is simply lost -- the patch + logged "installed" and its counters stayed at zero across 12,000 rollouts. Both production runs + trained on re-tokenised prompts for a night. + * `think_template_patch` mutated the tokenizer OBJECT, which IS pickled into the child, so that one + survived. That asymmetry is the whole reason the first went unnoticed. + +Neither is here. The ids come from the environment on the wire, and TRL raises rather than falling +back to a re-render. There is also no `chat_template_kwargs`: in loop-owning mode `_sample_turn` +never runs, so nothing applies a chat template at all. + +The engine MUST be the trainer's own vLLM. That is what makes the rollouts on-policy -- the agent +calls the same weights the optimizer is updating, through the capture proxy. +""" + +from __future__ import annotations + +import argparse +import os + +from data_agent_env import DataAgentSessionFactory, opencode_agent_turns +from datasets import Dataset +from transformers import AutoTokenizer + +from trl.experimental.async_grpo import AsyncGRPOConfig, AsyncGRPOTrainer +from trl.experimental.async_grpo.openenv_harness import HarnessRolloutWorker, has_tool_call + + +def main() -> None: + p = argparse.ArgumentParser(description=__doc__) + p.add_argument("--server", default="http://127.0.0.1:8200", help="a running blackbox-opencode") + p.add_argument("--vllm-url", required=True, help="the trainer's OWN vLLM; on-policy depends on it") + p.add_argument("--model", default="Qwen/Qwen3.5-2B") + p.add_argument("--split", default="train") + # The +0.2028 arm: 125 easy prompts first, then medium with hard sprinkled through. Without + # it the run meets the hard tiers at step 0, where a group of all-zero rollouts gives no + # gradient at all. + p.add_argument("--curriculum", default="warmup:125") + p.add_argument("--seed", type=int, default=0) + p.add_argument("--sandbox", default="e2b", choices=["e2b", "hf"]) + # The arm that produced +0.2028 [+0.146,+0.259] at step 200 and held it at 400. + p.add_argument("--learning-rate", type=float, default=3e-6) + p.add_argument("--num-generations", type=int, default=8) + p.add_argument("--max-inflight", type=int, default=32) + p.add_argument("--grad-accum", type=int, default=4) + p.add_argument("--max-steps", type=int, default=400) + p.add_argument("--temperature", type=float, default=0.8) + p.add_argument("--max-staleness", type=int, default=4) + # 17: the value the reference +0.2028 run used, read off its own launch line -- + # STEP_PERSIST_CAP=0 STEP_SOFT_CAP=0 STEP_HARD_CAP=17 STEP_FORCE_TOOL= + # -- where the hard cap fired 4,627 times, so it bound constantly rather than sitting unused. + # + # It has to sit at or below the reward's step_budget of 30: above it there is a band where the + # agent is allowed to act and punished for acting, and the policy escapes by not acting at all, + # which under train_turn_fn=has_tool_call yields no rows and therefore no gradient. + # + # 10 was far too tight -- 197 of 224 eval rollouts (88%) cut off mid-task, turns pinned at 9. But + # 25 is not the reference either, and the difference shows up in the DYNAMICS rather than as an + # error: longer rollouts mean fewer complete per optimizer step, measured at 9.8 samples/step + # against the reference's 16.1, which is a different effective batch and a different rate of + # consuming the curriculum. + # + # The reference deliberately ran with BOTH nudges off (SOFT_CAP=0, PERSIST_CAP=0), so the absence + # of prompt injection here matches it rather than departing from it. + p.add_argument("--agent-step-limit", type=int, default=17) + # PINNED, and not optional. Unset, token_budget defaults to the vLLM server's max_model_len -- + # 131072 here -- which tripled the trained row and killed job 69906 with torch.OutOfMemoryError in + # fla/ops/gated_delta_rule/chunk.py before step 1. At 40960 the rows already reach 40,870 (99.8%), + # so this is the measured ceiling for a 4B-class model on one 80 GB card, not a safety margin. + p.add_argument("--token-budget", type=int, default=40960) + # 900, against agent_timeout_s=600. The default 300 killed job 69319 with "heartbeat stale: 302s > + # 300s; child is hung" on a worker that was not hung but BUSY: the worker ticks its heartbeat at + # the top of the dispatch loop, which does not re-iterate while every max_inflight slot is full. + p.add_argument("--heartbeat-stale-after-s", type=float, default=900.0) + # opencode asks for 32,000 output tokens and capture clamps it to 8192; the TRL default is 2048. + p.add_argument("--max-completion-length", type=int, default=16384) + p.add_argument("--per-device-batch-size", type=int, default=4) + p.add_argument("--optim", default="paged_adamw_8bit") + # bfloat16, to MATCH THE SERVER. AsyncGRPOConfig defaults dtype="float32" deliberately -- TRL + # prefers fp32 on the trainer because the training-inference mismatch is sensitive to it -- but its + # own docstring adds that closing that gap end to end "also requires serving the vLLM server in the + # same dtype", and a precision GAP BIASES THE IMPORTANCE RATIO + # (https://huggingface.co/papers/2510.26788). + # + # TRL's preferred direction, serving fp32, is impossible here and that was measured rather than + # assumed: Qwen3.5 is hybrid Gated-DeltaNet and vLLM asserts + # ChunkGatedDeltaRuleFunction does not support float32. Please use bfloat16. + # (qwen_gdn_linear_attn.py:1165, job 72978). So the match is made on the trainer's side. + # + # Left at the default, job 72939 warned "serves in bfloat16 but the weights sent to it are + # float32" with embed_tokens.weight at 2.54 GB against a 1 GB transfer buffer. Halving the + # optimizer state is a side benefit on a card this work has already OOMed. + p.add_argument("--dtype", default="bfloat16") + p.add_argument("--save-steps", type=int, default=200) + p.add_argument("--output-dir", default="") + p.add_argument("--run-name", default="") + p.add_argument("--project", default="data-agent-blackbox") + args = p.parse_args() + + # Trackio keys a run by name inside a project, so two relaunches of one config land on top of each + # other and the earlier metrics read as part of the later run's history -- worst exactly when + # relaunching after a crash. Stamping with the job id keeps them apart. + stamp = os.environ.get("SLURM_JOB_ID", "local") + run_name = args.run_name or f"{args.model.split('/')[-1]}-lr{args.learning_rate:g}-{stamp}" + output_dir = args.output_dir or f"runs/{run_name}" + + factory = DataAgentSessionFactory( + args.server, + split=args.split, + llm_url=args.vllm_url, + model=args.model, + sandbox=args.sandbox, + sampling={"temperature": args.temperature, "top_p": 1.0, "top_k": 0}, + agent_step_limit=args.agent_step_limit, + curriculum=args.curriculum, + seed=args.seed, + ) + # Built FROM THE FACTORY so the instruction the trainer sends is one the server can resolve back + # to a task. All `num_generations` rollouts of a group share a row, so they get the same task and + # the group baseline is well formed without any seed plumbing. + dataset = Dataset.from_list(factory.prompt_rows()) + tokenizer = AutoTokenizer.from_pretrained(args.model) + + print(f"server {args.server}") + print(f"vllm {args.vllm_url} model {args.model}") + print(f"tasks {len(dataset)} from {args.split} [{args.curriculum or 'shuffled'}], sandbox {args.sandbox}") + print(f"run {run_name} -> {output_dir}") + print(f"budgets token_budget={args.token_budget} max_completion={args.max_completion_length} " + f"heartbeat={args.heartbeat_stale_after_s:g}s agent_steps={args.agent_step_limit} " + f"dtype={args.dtype}") + + worker = HarnessRolloutWorker( + harness_session_factory=factory, + harness_adapter=None, # loop-owning: the agent drives itself; we read what it did + # Reinforce turns that took an ACTION rather than prose -- right for an agent whose job is to + # inspect data and write a file. It works only because the env hands TRL tool calls in the + # nested OpenAI shape; flattened, `has_tool_call` is False for every turn and the whole + # rollout is silently discarded. + train_turn_fn=has_tool_call, + # Drop opencode's own title/summarizer calls. An earlier revision left this out on the theory + # that capture removes aux roots structurally -- it does not, and the run that assumed so + # collapsed: fork_frac 0.02-0.06 (reference: 0), drift_tokens_max 32,770 (reference: 0), + # samples_per_rollout up to 1.31 (reference: exactly 1.0), and the policy trained on title and + # summary tokens carrying the task's advantage. See `opencode_agent_turns` for the full + # measurement. + agent_turn_fn=opencode_agent_turns, + model_name=args.model, + dataset=dataset, + reward_funcs=[], # the environment's verify() is the reward + processing_class=tokenizer, + num_generations=args.num_generations, + max_inflight_tasks=args.max_inflight, + vllm_server_url=args.vllm_url, + max_tokens=args.max_completion_length, + temperature=args.temperature, + top_p=1.0, + top_k=0, + log_completions=True, + num_completions_to_print=2, + ) + + AsyncGRPOTrainer( + model=args.model, + args=AsyncGRPOConfig( + output_dir=output_dir, + save_strategy="steps", + save_steps=args.save_steps, + # Keep every checkpoint: the eval watcher picks them up asynchronously, and a + # save_total_limit would delete one out from under a queued evaluation. + save_total_limit=None, + num_generations=args.num_generations, + per_device_train_batch_size=args.per_device_batch_size, + gradient_accumulation_steps=args.grad_accum, + max_steps=args.max_steps, + max_completion_length=args.max_completion_length, + token_budget=args.token_budget, + heartbeat_stale_after_s=args.heartbeat_stale_after_s, + optim=args.optim, + dtype=args.dtype, + learning_rate=args.learning_rate, + temperature=args.temperature, + max_staleness=args.max_staleness, + vllm_server_base_url=args.vllm_url, + bf16=True, + gradient_checkpointing=True, + # Required: the reentrant checkpointer does not see inputs that reach a block through + # anything but positional args. + gradient_checkpointing_kwargs={"use_reentrant": False}, + report_to="trackio", + project=args.project, + run_name=run_name, + # Every rollout costs a sandbox and minutes, so nothing is logged in arrears. + logging_steps=1, + log_completions=True, + ), + train_dataset=dataset, + processing_class=tokenizer, + rollout_worker=worker, + ).train() + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/train/train_harbor_multi.py b/04-data-agent/train/train_harbor_multi.py new file mode 100644 index 0000000..5b24524 --- /dev/null +++ b/04-data-agent/train/train_harbor_multi.py @@ -0,0 +1,321 @@ +"""Async GRPO on the data-agent task set via OpenEnv x Harbor, SEVERAL harnesses in one run. + +Identical to train_harbor_opencode.py in every training knob; the only change is that each GRPO +GROUP is routed to a harness (see multi_harness.py). Harness is constant WITHIN a group and varies +BETWEEN groups, which keeps the advantage encoding "which action" rather than "which harness" -- +measured pass@4 across harnesses on this suite spans 0.320 to 0.020. + +ADMISSION. Run the 10-harness smoke and inspect exact engine token ids, sampled logprobs, +per-token masks, retained supervision, and sampling policy. Prefix drift increases packed context; +it does not invalidate per-call TITO or rollout-level rewards. The loop-owning worker uses lossless +reconciliation: exact prefixes merge, every rewritten history starts a new row. + +The default trains all retained agent turns. Some harnesses (for example Terminus) express actions +as text, so a universal `has_tool_call` filter would silently remove their entire training signal. +`--train-turn-filter tool_calls` is an explicit native-tool-call-only ablation. Auxiliary calls are +already removed by Harbor's capture/ATIF reconciliation. + +THE TRAPDOOR. Jobs 72452/72473 wedged at step 7 and 10 of 100, spending 4,076 E2B sandboxes on 11 +productive groups, because an UNGATED efficiency term made zero tool calls the highest-scoring move +while `train_turn_fn=has_tool_call` then yielded no trainable turns. The `_train` suite emits a +single float with no efficiency term (verified across all 2,238 graders), so the first leg is absent +here. The default all-agent-turn filter also avoids dropping text-action rollouts. Constant-reward +groups can still have no advantage signal: band the task indices and watch reward_std from step 1. +""" + +from __future__ import annotations + +import argparse +import json +import os +from typing import Any + +from datasets import Dataset +from transformers import AutoTokenizer + +from trl.experimental.async_grpo import AsyncGRPOConfig, AsyncGRPOTrainer +from trl.experimental.async_grpo.openenv_harness import HarnessRolloutWorker, has_tool_call + +TRAIN_SPLIT = "AdithyaSK/data_agent_rl_environment_train" + + +def tool_calling_turns(trace: list[dict[str, Any]]) -> list[dict[str, Any]]: + """Optional filter for turns whose request offered native tools. + + Harbor already removes auxiliary calls. This additional restriction is an ablation and must + not be used for text-action harnesses; prefix drift alone is not evidence of an auxiliary call. + """ + return [e for e in trace if ((e.get("metadata") or {}).get("n_tools") or 0) > 0] + + +def build(argv=None): + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument("--server", default="http://127.0.0.1:8200", help="a running `openenv harbor serve`") + p.add_argument("--vllm-url", required=True, help="the engine AsyncGRPO also syncs weights into") + p.add_argument("--model", default="Qwen/Qwen3.5-2B") + p.add_argument("--model-revision", default=None) + p.add_argument("--resume-from-checkpoint", default="", help="Completed local checkpoint including optimizer and rollout cursor") + p.add_argument("--split", default=TRAIN_SPLIT) + # '+'-separated, never commas: `sbatch --export=ALL,VAR=a,b,c` truncates at the first comma + # SILENTLY, and the job then runs a harness set it was never given. + p.add_argument("--harnesses", default="opencode+mini-swe-agent") + p.add_argument("--sandbox", default="e2b") + p.add_argument("--reward-key", default="", help="'' lets the server pick; required on a multi-reward suite") + p.add_argument("--task-indices", default="", help="comma-separated, or @file") + p.add_argument("--n-tasks", type=int, default=0, help="0 = the whole split") + p.add_argument("--all-task-harness-pairs", action="store_true", + help="Schedule every task under every harness before repeating the dataset") + p.add_argument("--harness-schedule", default="", help="Frozen one-harness-per-task rotation JSON") + p.add_argument("--agent-turn-filter", default="none", choices=["none", "tools"], + help="Optional tool-manifest filter; incompatible with harnesses that express actions as text") + p.add_argument("--train-turn-filter", default="all", choices=["all", "tool_calls"], + help="Train all selected agent turns, or explicitly restrict to native tool-call turns") + + # ---- the reference's values, unchanged --------------------------------------------------- + p.add_argument("--learning-rate", type=float, default=3e-6) + p.add_argument("--num-generations", type=int, default=8) + p.add_argument("--max-inflight", type=int, default=32) + p.add_argument("--grad-accum", type=int, default=4) + p.add_argument("--atomic-rollouts", action="store_true", + help="Keep all rows of each admitted rollout in one update (single dense trainer GPU)") + p.add_argument("--max-outstanding-rollouts", type=int, default=0, + help="Atomic recipe: bound generating plus queued rollouts until optimizer consumption") + p.add_argument("--max-row-tokens", type=int, default=131072, + help="Hard context limit for atomic rollout forwards; token-budget is the packing target") + p.add_argument("--per-device-batch-size", type=int, default=4) + p.add_argument("--max-steps", type=int, default=400) + p.add_argument("--max-train-seconds", type=float, default=0, + help="If positive, save and stop at the first update boundary after this duration") + p.add_argument("--coverage-min-steps", type=int, default=0, + help="If positive, stop once this many updates and every task/harness pair are covered; max-steps remains a hard ceiling") + p.add_argument("--audit-dir", default="", help="Save per-rollout capture results and pair coverage locally") + p.add_argument("--max-staleness", type=int, default=4) + p.add_argument("--optim", default="paged_adamw_8bit") + # Pinned, and the SAME value must reach `vllm serve --override-generation-config`. opencode sends + # no sampling params and Qwen3.5 ships no generation_config.json, so an unpinned engine samples at + # 1.0 while the trainer divides logits by this -- gradients against a distribution that never + # produced the samples. Measured unpinned: entropy 0.229 -> 0.587 over 24 steps, reward 0.592 -> 0.216. + p.add_argument("--temperature", type=float, default=0.8) + # NEUTRAL, and this is a deliberate DEPARTURE from the reference's 0.95. processed_logprobs are + # taken AFTER truncation, so a truncating top_p renormalises every captured logprob over the kept + # set while the trainer recomputes full-vocab; the step-0 importance ratio then lands at + # kept_mass rather than 1. Validated: `ratio` moved from the reference's 0.985-0.993 signature to + # 0.9984-0.9999 once this was 1.0. + p.add_argument("--top-p", type=float, default=1.0) + p.add_argument("--top-k", type=int, default=0) + # 17, read off the reference run's own STEP_HARD_CAP, which fired 4,627 times. Must sit at or + # below the reward's step_budget, else there is a band where acting is allowed and punished and + # the policy escapes by not acting -- which under has_tool_call yields no rows and no gradient. + p.add_argument("--agent-step-limit", type=int, default=17) + p.add_argument("--agent-timeout", type=float, default=600.0) + # PINNED, not optional: unset, token_budget falls back to the engine's max_model_len, which + # tripled the trained row and OOMed job 69906 in fla/ops/gated_delta_rule/chunk.py before step 1. + p.add_argument("--token-budget", type=int, default=40960) + # 900 against agent_timeout 600. The 300 default killed job 69319 on a worker that was BUSY, not hung. + p.add_argument("--heartbeat-stale-after-s", type=float, default=900.0) + p.add_argument("--max-completion-length", type=int, default=16384) + # MATCH THE SERVER. AsyncGRPOConfig defaults to float32; a precision gap biases the importance ratio. + p.add_argument("--dtype", default="bfloat16") + p.add_argument("--save-steps", type=int, default=200) + p.add_argument("--checkpoint-max-seconds", type=float, default=0, + help="Also save at the first optimizer boundary after this interval; 0 disables") + p.add_argument("--output-dir", default="") + p.add_argument("--run-name", default="") + p.add_argument("--project", default="data-agent-harbor-multi") + p.add_argument("--seed", type=int, default=0) + return p.parse_args(argv) + + +def indices_of(spec: str) -> list[int] | None: + """`@file` form exists because `sbatch --export=ALL,VAR=a,b,c` truncates at the first comma, + silently -- the job runs with a task list it was never given.""" + if not spec: + return None + if spec.startswith("@"): + spec = open(spec[1:]).read() + out, seen = [], set() + for tok in spec.replace("\n", ",").split(","): + tok = tok.strip() + if tok and int(tok) not in seen: + seen.add(int(tok)); out.append(int(tok)) + return out or None + + +def main(argv=None, *, session_factory_class=None, agent_turn_selector=None) -> None: + args = build(argv) + from multi_harness import MultiHarborSessionFactory, pair_rows + resume = None + if args.resume_from_checkpoint: + from checkpoint_artifacts import resume_info + resume = resume_info(args.resume_from_checkpoint, args.model, args.model_revision) + group_offset = resume['group_offset'] if resume else 0 + + harnesses = [h.strip() for h in args.harnesses.replace(",", "+").split("+") if h.strip()] + if args.harness_schedule and args.all_task_harness_pairs: + raise ValueError('Choose either a rotating schedule or Cartesian scheduling') + schedule = None + if args.harness_schedule: + with open(args.harness_schedule) as stream: + schedule = json.load(stream) + factory_class = session_factory_class or MultiHarborSessionFactory + factory = factory_class( + args.server, + harnesses=harnesses, + schedule=schedule, + group_offset=group_offset, + split=args.split, + sandbox=args.sandbox, + # THE SAME engine the trainer syncs weights into. That is what makes the rollouts on-policy: + # the agent's calls and the weight updates go to one vLLM. It must be the node's ROUTABLE + # address -- the harbor server probes it from ANOTHER host, and with localhost the probe + # fails, the tier grades `text`, and every rollout comes back with no trainable turns. + llm_url=os.environ.get("ROLLOUT_LLM_URL", args.vllm_url), + api_key=os.environ.get("ROLLOUT_LLM_API_KEY", ""), + model=args.model, + sampling={"temperature": args.temperature, "top_p": args.top_p, "top_k": args.top_k}, + reward_key=args.reward_key, + agent_timeout_sec=args.agent_timeout, + agent_step_limit=args.agent_step_limit, + indices=indices_of(args.task_indices), + num_tasks=args.n_tasks or None, + ) + if args.coverage_min_steps and not 0 < args.coverage_min_steps <= args.max_steps: + raise ValueError("coverage-min-steps must be between 1 and max-steps") + if args.audit_dir: + from training_audit import AuditedFactory + factory = AuditedFactory(factory, args.audit_dir) + + # Built FROM the factory so the instruction TRL sends is one the server can resolve: `create()` + # hashes the prompt back to a task index and RAISES on a miss rather than silently running task 0. + # pair_rows pads so gcd(len(rows), n_harnesses) == 1. Without it, group->row and group->harness + # stay in lockstep and each task meets only ONE harness: at 40 tasks and 2 harnesses, 0 of 40 + # tasks meet both. The run looks multi-harness and is a disjoint partition. + rows = pair_rows(factory, all_pairs=args.all_task_harness_pairs) + dataset = Dataset.from_list(rows) + tokenizer = AutoTokenizer.from_pretrained(args.model, revision=args.model_revision, trust_remote_code=True) + + implementation = "standalone-opencode" if session_factory_class else "harbor" + run_name = args.run_name or ( + f"{args.model.split('/')[-1]}-multi{len(harnesses)}-{implementation}-{args.max_steps}steps" + # Stamped with the job id: trackio keys a run by name inside a project, so relaunches + # otherwise stack on top of each other. + f"-{os.environ.get('SLURM_JOB_ID', 'local')}" + ) + out_dir = args.output_dir or f"/fsx/{os.environ.get('USER','x')}/runs/agrpo_harbor/{run_name}" + if resume: + from training_audit import write_json + write_json(os.path.join(args.audit_dir or out_dir, 'resume.json'), resume) + print(f"resume checkpoint step={resume['step']}, next schedule group={group_offset}", flush=True) + + print(f"model {args.model}") + print(f"server {args.server} vllm {args.vllm_url}") + print(f"rollouts {'+'.join(harnesses)} on {args.sandbox}, {args.num_generations}x{args.max_inflight}") + print(f"routing {'frozen rotation' if schedule else 'modulo harness routing'}; constant within each group") + print(f"tasks {len(dataset)} from {args.split}") + print(f"sampling temperature={args.temperature} top_p={args.top_p} top_k={args.top_k}" + f" (explicit capture session policy; checked against trainer recompute)") + print(f"budgets token_budget={args.token_budget} max_completion={args.max_completion_length} " + f"heartbeat={args.heartbeat_stale_after_s:g}s agent_steps={args.agent_step_limit} dtype={args.dtype}") + print(f"admission atomic={args.atomic_rollouts} max_outstanding_rollouts={args.max_outstanding_rollouts}") + print(f"aux agent_turn_fn={agent_turn_selector.__name__ if agent_turn_selector else args.agent_turn_filter}; " + f"train_turn_filter={args.train_turn_filter}; implementation={implementation}") + print(f"output {out_dir}") + + worker_class, trainer_class = HarnessRolloutWorker, AsyncGRPOTrainer + if args.atomic_rollouts: + from atomic_rollouts import AtomicHarnessWorker, AtomicRolloutTrainer + worker_class, trainer_class = AtomicHarnessWorker, AtomicRolloutTrainer + worker = worker_class( + **({"max_outstanding_rollouts": args.max_outstanding_rollouts} if args.atomic_rollouts else {}), + harness_session_factory=factory, + harness_adapter=None, # loop-owning: the agent drives itself; we read what it did + # Text-action harnesses have no native tool_calls. Keep their supervision by default. + train_turn_fn=has_tool_call if args.train_turn_filter == "tool_calls" else None, + lossless_capture=True, + fork_threshold_tokens=0, + agent_turn_fn=agent_turn_selector or (tool_calling_turns if args.agent_turn_filter == "tools" else None), + model_name=args.model, + dataset=dataset, + reward_funcs=[], # the environment's verify() IS the reward; None means UNSCORED, never 0.0 + processing_class=tokenizer, + num_generations=args.num_generations, + max_inflight_tasks=args.max_inflight, + vllm_server_url=args.vllm_url, + max_tokens=args.max_completion_length, + temperature=args.temperature, + top_p=args.top_p, + top_k=args.top_k, + log_completions=True, + num_completions_to_print=2, + ) + + config = AsyncGRPOConfig( + output_dir=out_dir, + save_strategy="steps" if args.save_steps else "no", + save_steps=args.save_steps or 500, + save_total_limit=None, # never rob an eval watcher of a checkpoint + per_device_train_batch_size=args.per_device_batch_size, + gradient_accumulation_steps=args.grad_accum, + num_generations=args.num_generations, + max_completion_length=args.max_completion_length, + max_steps=args.max_steps, + learning_rate=args.learning_rate, + temperature=args.temperature, + top_p=args.top_p, + top_k=args.top_k, + max_staleness=args.max_staleness, + max_inflight_tasks=args.max_inflight, + fork_threshold_tokens=0, + vllm_server_base_url=args.vllm_url, + optim=args.optim, + bf16=True, + dtype=args.dtype, + trust_remote_code=True, # Qwen3_5ForConditionalGeneration is a custom arch + model_init_kwargs={"revision": args.model_revision} if args.model_revision else None, + token_budget=args.token_budget, + heartbeat_stale_after_s=args.heartbeat_stale_after_s, + gradient_checkpointing=True, + # Required: the reentrant checkpointer does not see inputs arriving through anything but + # positional args, and the hybrid-attention path passes state that way. + gradient_checkpointing_kwargs={"use_reentrant": False}, + report_to="trackio", + project=args.project, + run_name=run_name, + trackio_space_id=None, + trackio_bucket_id=None, + trackio_static_space_id=False, # CPU logger owns online sync; never publish/freeze from trainer + log_completions=True, + logging_steps=1, # every rollout costs a sandbox and minutes; nothing is logged in arrears + seed=args.seed, + ) + + trainer_kwargs = ({"max_row_tokens": args.max_row_tokens, "admission_dir": args.audit_dir} + if args.atomic_rollouts else {}) + trainer = trainer_class( + model=args.model, args=config, train_dataset=dataset, rollout_worker=worker, + **trainer_kwargs, + ) + from training_audit import CheckpointReadyCallback + trainer.add_callback(CheckpointReadyCallback(args.model, args.model_revision)) + if args.checkpoint_max_seconds > 0: + from training_audit import PeriodicCheckpointCallback + trainer.add_callback(PeriodicCheckpointCallback(args.checkpoint_max_seconds)) + if args.max_train_seconds: + from training_audit import WallTimeCallback + trainer.add_callback(WallTimeCallback(args.max_train_seconds)) + if args.audit_dir or args.coverage_min_steps: + from training_audit import PairCoverageCallback + trainer.add_callback(PairCoverageCallback( + trainer, len(rows), harnesses, args.coverage_min_steps, args.audit_dir or out_dir, + all_pairs=args.all_task_harness_pairs, + schedule=schedule, + group_offset=group_offset, + )) + trainer.train(resume_from_checkpoint=resume['checkpoint'] if resume else None) + trainer.save_state() + trainer.save_model(os.path.join(out_dir, "final")) + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/train/train_harbor_opencode.py b/04-data-agent/train/train_harbor_opencode.py new file mode 100644 index 0000000..3dc2f1f --- /dev/null +++ b/04-data-agent/train/train_harbor_opencode.py @@ -0,0 +1,276 @@ +"""Async GRPO on the data-agent task set via OpenEnv x Harbor, opencode harness. + +A deliberate replication of HuggingEnvs/04-data-agent/train/train_blackbox_opencode.py -- the arm +that reached +0.2343 (CI [+0.178, +0.291], p=0.0) on Qwen3.5-2B -- with ONE variable changed: the +environment is Harbor through the OpenEnv capture proxy instead of the bespoke blackbox-opencode env. +Every training knob below is the reference's value, so a difference in outcome is attributable to the +environment and not to the recipe. + +TOKEN-IN-TOKEN-OUT, NO RE-RENDER. `to_trace_entries` (envs/harbor_env/harness.py) carries the +engine's own `prompt_token_ids`, and TRL's `_turns_from_trace` reads them and RAISES if absent. A +local re-render matched the engine on 0 of 28 measured turns, so this path is not optional. +Verify from the metrics, not from reading this file: rollout/fork_frac == 0, +rollout/samples_per_rollout == 1.00, rollout/drift_tokens_max == 0. + +WHY NO agent_turn_fn, AND HOW TO KNOW IF THAT IS WRONG. The reference passes `opencode_agent_turns` +to strip opencode's title/summarizer calls, anchored on the first tool-enabled turn's SYSTEM PROMPT. +That filter cannot be ported: Harbor's TraceEntry carries no `request`, and HarborTurn has no +system_digest. It should not need to be. Harbor assigns roles structurally in capture/export.py -- +a path that never uses tools is AUXILIARY, `trainable` requires role == AGENT, and +`to_trace_entries` skips anything not trainable. So the aux calls are dropped BEFORE TRL sees them. + +That is a claim about Harbor, and claims get checked: if `rollout/fork_frac` is non-zero AT STEP 1 +(structural, not a later collapse) or `samples_per_rollout` != 1.00, the drop did not happen and +`--agent-turn-filter tools` supplies a fallback that keeps only turns that called a tool. + +THE TRAPDOOR THIS SCRIPT IS SAFE FROM, AND WHY IT IS WORTH KNOWING ANYWAY. Jobs 72452/72473 wedged +at step 7 and 10 of 100, having spent 4,076 E2B sandboxes on 11 productive groups (294 and 201 EMPTY +groups). Two individually reasonable settings combined: + + reward = correctness + 0.1 * clamp(1 - n_tool_calls/15, 0, 1) # UNGATED efficiency + train_turn_fn = has_tool_call + +Zero tool calls scores the MAXIMUM efficiency bonus, so inaction is the best move available to a +policy that cannot solve the task (0.100 vs 0.033 for a real attempt that fails). The policy learns +to stop calling tools -- and `has_tool_call` then yields NO trainable turns, so the group is empty +and the run starves while the logs keep moving. + +The first leg is absent here: the `_train` suite emits a single float from `grader.py` with no +efficiency term at all (verified: zero occurrences across all 2,238 task graders). `--reward efficiency` +(the default) adds one back TRAINER-side, from the trace, in a form that closes the first leg +STRUCTURALLY rather than by a gate -- see `harbor_reward.py`. The SECOND leg is still present. A model that makes no tool calls for any +reason -- including simply being too weak for the task -- still produces empty groups. That is what +banded tasks are for: pick indices the model can SOMETIMES solve, so reward_std > 0 and the group +carries gradient. Watch `reward_std` and the empty-group count from step 1. +""" + +from __future__ import annotations + +import argparse +import os +from typing import Any + +from datasets import Dataset +from transformers import AutoTokenizer + +from trl.experimental.async_grpo import AsyncGRPOConfig, AsyncGRPOTrainer +from trl.experimental.async_grpo.openenv_harness import HarnessRolloutWorker, has_tool_call + +# Module-level import, not a lambda: the reward crosses the spawn boundary and must pickle. +from harbor_reward import TOOL_BUDGET, W_EFF, data_agent_reward + +TRAIN_SPLIT = "AdithyaSK/data_agent_rl_environment_train" + + +def tool_calling_turns(trace: list[dict[str, Any]]) -> list[dict[str, Any]]: + """Fallback `agent_turn_fn`: keep only turns that actually called a tool. + + The Harbor-native stand-in for the reference's system-prompt anchor, which cannot be ported + (no `request` on a Harbor TraceEntry). opencode's bookkeeping calls -- the conversation-title + generator and the context summarizer -- use no tools, so `metadata.n_tools > 0` separates them + from real agent steps. Weaker than anchoring on the system prompt, which is why it is OFF by + default and gated on measured fork_frac rather than switched on out of caution. + """ + return [e for e in trace if ((e.get("metadata") or {}).get("n_tools") or 0) > 0] + + +def build(argv=None): + p = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument("--server", default="http://127.0.0.1:8200", help="a running `openenv harbor serve`") + p.add_argument("--vllm-url", required=True, help="the engine AsyncGRPO also syncs weights into") + p.add_argument("--model", default="Qwen/Qwen3.5-2B") + p.add_argument("--split", default=TRAIN_SPLIT) + p.add_argument("--harness", default="opencode") + p.add_argument("--sandbox", default="e2b") + p.add_argument("--reward-key", default="", help="'' lets the server pick; required on a multi-reward suite") + p.add_argument("--task-indices", default="", help="comma-separated, or @file") + p.add_argument("--n-tasks", type=int, default=0, help="0 = the whole split") + p.add_argument("--agent-turn-filter", default="none", choices=["none", "tools"], + help="'tools' keeps only turns with n_tools>0; use ONLY if fork_frac != 0 at step 1") + p.add_argument("--reward", choices=["efficiency", "correctness"], default="efficiency", + help="efficiency: correctness x (1 + W_EFF*B/(B+tool_calls)), tuned by " + "$REWARD_W_TOOL_EFFICIENCY and $TOOL_BUDGET. correctness: the verifier's " + "float untouched, which is the pure-correctness arm runs 76585/77284 used.") + + # ---- the reference's values, unchanged --------------------------------------------------- + p.add_argument("--learning-rate", type=float, default=3e-6) + p.add_argument("--num-generations", type=int, default=8) + p.add_argument("--max-inflight", type=int, default=32) + p.add_argument("--grad-accum", type=int, default=4) + p.add_argument("--per-device-batch-size", type=int, default=4) + p.add_argument("--max-steps", type=int, default=400) + p.add_argument("--max-staleness", type=int, default=4) + p.add_argument("--optim", default="paged_adamw_8bit") + # Pinned, and the SAME value must reach `vllm serve --override-generation-config`. opencode sends + # no sampling params and Qwen3.5 ships no generation_config.json, so an unpinned engine samples at + # 1.0 while the trainer divides logits by this -- gradients against a distribution that never + # produced the samples. Measured unpinned: entropy 0.229 -> 0.587 over 24 steps, reward 0.592 -> 0.216. + p.add_argument("--temperature", type=float, default=0.8) + # NEUTRAL, and this is a deliberate DEPARTURE from the reference's 0.95. processed_logprobs are + # taken AFTER truncation, so a truncating top_p renormalises every captured logprob over the kept + # set while the trainer recomputes full-vocab; the step-0 importance ratio then lands at + # kept_mass rather than 1. Validated: `ratio` moved from the reference's 0.985-0.993 signature to + # 0.9984-0.9999 once this was 1.0. + p.add_argument("--top-p", type=float, default=1.0) + p.add_argument("--top-k", type=int, default=0) + # 17, read off the reference run's own STEP_HARD_CAP, which fired 4,627 times. Must sit at or + # below the reward's step_budget, else there is a band where acting is allowed and punished and + # the policy escapes by not acting -- which under has_tool_call yields no rows and no gradient. + p.add_argument("--agent-step-limit", type=int, default=17) + p.add_argument("--agent-timeout", type=float, default=600.0) + # PINNED, not optional: unset, token_budget falls back to the engine's max_model_len, which + # tripled the trained row and OOMed job 69906 in fla/ops/gated_delta_rule/chunk.py before step 1. + p.add_argument("--token-budget", type=int, default=40960) + # 900 against agent_timeout 600. The 300 default killed job 69319 on a worker that was BUSY, not hung. + p.add_argument("--heartbeat-stale-after-s", type=float, default=900.0) + p.add_argument("--max-completion-length", type=int, default=16384) + # MATCH THE SERVER. AsyncGRPOConfig defaults to float32; a precision gap biases the importance ratio. + p.add_argument("--dtype", default="bfloat16") + p.add_argument("--save-steps", type=int, default=200) + p.add_argument("--output-dir", default="") + p.add_argument("--run-name", default="") + p.add_argument("--project", default="data-agent-harbor-opencode") + p.add_argument("--seed", type=int, default=0) + return p.parse_args(argv) + + +def indices_of(spec: str) -> list[int] | None: + """`@file` form exists because `sbatch --export=ALL,VAR=a,b,c` truncates at the first comma, + silently -- the job runs with a task list it was never given.""" + if not spec: + return None + if spec.startswith("@"): + spec = open(spec[1:]).read() + out, seen = [], set() + for tok in spec.replace("\n", ",").split(","): + tok = tok.strip() + if tok and int(tok) not in seen: + seen.add(int(tok)); out.append(int(tok)) + return out or None + + +def main() -> None: + args = build() + from harbor_env.harness import HarborSessionFactory + + factory = HarborSessionFactory( + args.server, + split=args.split, + harness=args.harness, + sandbox=args.sandbox, + # THE SAME engine the trainer syncs weights into. That is what makes the rollouts on-policy: + # the agent's calls and the weight updates go to one vLLM. It must be the node's ROUTABLE + # address -- the harbor server probes it from ANOTHER host, and with localhost the probe + # fails, the tier grades `text`, and every rollout comes back with no trainable turns. + llm_url=args.vllm_url, + model=args.model, + sampling={"temperature": args.temperature, "top_p": args.top_p, "top_k": args.top_k}, + reward_key=args.reward_key, + agent_timeout_sec=args.agent_timeout, + agent_step_limit=args.agent_step_limit, + indices=indices_of(args.task_indices), + num_tasks=args.n_tasks or None, + ) + + # Built FROM the factory so the instruction TRL sends is one the server can resolve: `create()` + # hashes the prompt back to a task index and RAISES on a miss rather than silently running task 0. + rows = factory.prompt_rows() + dataset = Dataset.from_list(rows) + tokenizer = AutoTokenizer.from_pretrained(args.model, trust_remote_code=True) + + run_name = args.run_name or ( + f"{args.model.split('/')[-1]}-{args.harness}-harbor-{args.max_steps}steps" + # Stamped with the job id: trackio keys a run by name inside a project, so relaunches + # otherwise stack on top of each other. + f"-{os.environ.get('SLURM_JOB_ID', 'local')}" + ) + out_dir = args.output_dir or f"/fsx/{os.environ.get('USER','x')}/runs/agrpo_harbor/{run_name}" + + print(f"model {args.model}") + print(f"server {args.server} vllm {args.vllm_url}") + print(f"rollouts {args.harness} on {args.sandbox}, {args.num_generations}x{args.max_inflight}") + print(f"tasks {len(dataset)} from {args.split}") + print(f"sampling temperature={args.temperature} top_p={args.top_p} top_k={args.top_k}" + f" <-- the SAME values must be on `vllm serve --override-generation-config`") + print(f"budgets token_budget={args.token_budget} max_completion={args.max_completion_length} " + f"heartbeat={args.heartbeat_stale_after_s:g}s agent_steps={args.agent_step_limit} dtype={args.dtype}") + print(f"aux agent_turn_fn={args.agent_turn_filter} (Harbor drops AUXILIARY-role turns " + f"server-side; check rollout/fork_frac at STEP 1)") + print(f"output {out_dir}") + + reward_fn = data_agent_reward if args.reward == "efficiency" else None + if reward_fn is None: + print("reward correctness only (the verifier's float, untouched)") + else: + print(f"reward correctness x (1 + {W_EFF:g} * {TOOL_BUDGET:g}/({TOOL_BUDGET:g} + tool_calls))" + f" max {1 + W_EFF:.3f} | {1 + W_EFF * 0.5:.3f} at {TOOL_BUDGET:g} calls | ->1.0 unbounded") + print(" watch train/tools/call_frequency -- that IS the penalised quantity") + + worker = HarnessRolloutWorker( + harness_session_factory=factory, + lossless_capture=True, + fork_threshold_tokens=0, + harness_adapter=None, # loop-owning: the agent drives itself; we read what it did + # Reinforce turns that took an ACTION rather than prose. Works only because the env hands TRL + # tool calls in the NESTED OpenAI shape; flattened, this is False for every turn and the whole + # rollout is discarded with no error anywhere. + train_turn_fn=has_tool_call, + agent_turn_fn=tool_calling_turns if args.agent_turn_filter == "tools" else None, + model_name=args.model, + dataset=dataset, + reward_funcs=[], # the environment's verify() IS the reward; None means UNSCORED, never 0.0 + # Replaces env_reward with correctness x efficiency. Returning None still means UNSCORED. + rollout_reward_fn=reward_fn, + processing_class=tokenizer, + num_generations=args.num_generations, + max_inflight_tasks=args.max_inflight, + vllm_server_url=args.vllm_url, + max_tokens=args.max_completion_length, + temperature=args.temperature, + log_completions=True, + num_completions_to_print=2, + ) + + config = AsyncGRPOConfig( + output_dir=out_dir, + save_strategy="steps" if args.save_steps else "no", + save_steps=args.save_steps or 500, + save_total_limit=None, # never rob an eval watcher of a checkpoint + per_device_train_batch_size=args.per_device_batch_size, + gradient_accumulation_steps=args.grad_accum, + num_generations=args.num_generations, + max_completion_length=args.max_completion_length, + max_steps=args.max_steps, + learning_rate=args.learning_rate, + temperature=args.temperature, + top_p=args.top_p, + top_k=args.top_k, + max_staleness=args.max_staleness, + fork_threshold_tokens=0, + vllm_server_base_url=args.vllm_url, + optim=args.optim, + bf16=True, + dtype=args.dtype, + trust_remote_code=True, # Qwen3_5ForConditionalGeneration is a custom arch + token_budget=args.token_budget, + heartbeat_stale_after_s=args.heartbeat_stale_after_s, + gradient_checkpointing=True, + # Required: the reentrant checkpointer does not see inputs arriving through anything but + # positional args, and the hybrid-attention path passes state that way. + gradient_checkpointing_kwargs={"use_reentrant": False}, + report_to="trackio", + project=args.project, + run_name=run_name, + log_completions=True, + logging_steps=1, # every rollout costs a sandbox and minutes; nothing is logged in arrears + seed=args.seed, + ) + + AsyncGRPOTrainer( + model=args.model, args=config, train_dataset=dataset, rollout_worker=worker + ).train() + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/train/train_standalone_comparison.py b/04-data-agent/train/train_standalone_comparison.py new file mode 100644 index 0000000..ca49980 --- /dev/null +++ b/04-data-agent/train/train_standalone_comparison.py @@ -0,0 +1,7 @@ +"""Train the standalone blackbox-opencode environment with the reference async recipe.""" +from data_agent_env import opencode_agent_turns +from standalone_comparison import ScheduledOpenCodeFactory +from train_harbor_multi import main + +if __name__ == "__main__": + main(session_factory_class=ScheduledOpenCodeFactory, agent_turn_selector=opencode_agent_turns) diff --git a/04-data-agent/train/train_whitebox_bash.py b/04-data-agent/train/train_whitebox_bash.py new file mode 100644 index 0000000..a0a9a8f --- /dev/null +++ b/04-data-agent/train/train_whitebox_bash.py @@ -0,0 +1,186 @@ +# Copyright 2026 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Train the white-box bash/SETA agent with TRL's SYNCHRONOUS GRPOTrainer. + +HOW THE DATASET SELECTS THE TASK +TRL forwards the whole dataset row to `reset()` as kwargs +(`grpo_trainer.py`: `reset_kwargs = x; environment.reset(**reset_kwargs)`), so a row carrying +`split` and `index` lands on `WhiteBoxBashEnv.reset(split=..., index=...)` and starts that exact +episode. That is the entire task-selection mechanism -- there is no side channel, and the row is the +single place a task is chosen. + +`prompt` is the role framing only. The task text arrives from `reset()`, which TRL appends to the +last user message; the gold answer never leaves the server, so the trainer cannot see it. + +WHY NO REWARD FUNCTION +The environment owns its reward through `get_reward()`, so `reward_funcs` is empty. TRL exposes the +env's reward as a column named after the env class. Passing a reward function as well would add a +second, unweighted reward source and quietly change the objective. + +ONE GPU +vLLM runs in COLOCATE mode, in-process on the training GPU, so the smoke needs no separate server and +no 2-GPU allocation. `vllm_gpu_memory_utilization` is the knob that makes that fit -- the trainer and +the engine share one card, and the default 0.9 would leave nothing for the optimizer states. +""" + +from __future__ import annotations + +import argparse +import os + +from datasets import Dataset +from trl import GRPOConfig, GRPOTrainer + +from whitebox_bash import exposed_tool_names, white_box_bash_env + + +SYSTEM = ( + "You are a terminal agent working in a sandbox. Use the available tools to inspect the " + "filesystem and solve the task. Work step by step: look before you act. When you are confident, " + "call submit_solution with the final answer and nothing else -- not the command that would " + "produce it." +) + + +def build_dataset(server: str, split: str, limit: int = 0) -> Dataset: + """One row per task: the role framing plus the coordinates `reset()` needs. + + Task text is deliberately NOT baked in here. It comes from `reset()` at rollout time, which keeps + one source of truth (the server) and means the trainer never holds anything the agent should have + had to discover. + """ + import httpx + + # Only the COUNT is needed: the row carries `split`/`index`, and the task text comes from + # `reset()`. The Task API routes are POST with a JSON body, not GET with query params. + r = httpx.post(f"{server.rstrip('/')}/white_box_bash/num_tasks", + json={"split": split}, timeout=60.0) + r.raise_for_status() + payload = r.json() + n = int(payload if isinstance(payload, int) else payload.get("num_tasks", payload.get("count", 0))) + if limit: + n = min(n, limit) + if n <= 0: + raise SystemExit(f"split {split!r} has no tasks; nothing to train on") + return Dataset.from_list([ + { + "prompt": [{"role": "system", "content": SYSTEM}, + {"role": "user", "content": "Solve the task."}], + # Read by `reset()` via TRL's row-as-kwargs forwarding. The names must match the + # signature exactly: a misspelling is silently dropped and every rollout gets index 0. + "split": split, + "index": i, + } + for i in range(n) + ]) + + +def main() -> None: + p = argparse.ArgumentParser() + p.add_argument("--model", default=os.environ.get("MODEL", "Qwen/Qwen3.5-2B")) + p.add_argument("--server", default=os.environ.get("WHITE_BOX_BASH_URL", "http://127.0.0.1:8412")) + p.add_argument("--split", default=os.environ.get("SPLIT", "train")) + p.add_argument("--toolsets", default=os.environ.get("TOOLSETS", "bash,seta")) + p.add_argument("--output-dir", default=os.environ.get("OUTPUT_DIR", "runs/whitebox-bash")) + p.add_argument("--max-steps", type=int, default=int(os.environ.get("MAX_STEPS", "4"))) + p.add_argument("--num-generations", type=int, default=int(os.environ.get("NUM_GENERATIONS", "4"))) + p.add_argument("--per-device-train-batch-size", type=int, + default=int(os.environ.get("PER_DEVICE_BS", "4"))) + # 1e-6, not 3e-6, and beta 0.04 rather than 0. Both come from a previous SYNC GRPO run on this + # same dataset (experiments/rollout_control/harbor_trl, FAILURE_MODES.md §E2): with `beta=0` and + # LR 2e-6 BOTH Qwen3.5-2B runs collapsed -- entropy fell 0.4 -> 0.09 by step 40, the policy + # degenerated into malformed repetition, and reward went to 0. Nothing anchors a small policy + # without a KL term. The 4B tolerated it; the 2B does not. + p.add_argument("--learning-rate", type=float, default=float(os.environ.get("LR", "1e-6"))) + p.add_argument("--kl-beta", type=float, default=float(os.environ.get("KL_BETA", "0.04"))) + p.add_argument("--warmup-steps", type=int, default=int(os.environ.get("WARMUP_STEPS", "10"))) + # The LOGITS batch, and the thing that OOMs. The loss materialises + # `per_device_batch x seq_len x vocab` in fp32, and Qwen3.5's vocab is ~152k: at batch 8 and a + # 4096 completion budget that is ~24 GB and the run dies in `logits / temperature` (job 76717). + # Keep this small and recover the effective batch with gradient accumulation -- GRPO only needs + # the GROUP intact, and `per_device_bs * grad_accum` still covers `num_generations`. + p.add_argument("--gradient-accumulation-steps", type=int, + default=int(os.environ.get("GRAD_ACCUM", "4"))) + p.add_argument("--temperature", type=float, default=float(os.environ.get("TEMPERATURE", "0.8"))) + # Bounds the WHOLE multi-turn completion, tool-result tokens included -- they sit in + # `completion_ids` (masked out of the loss and out of the length metric, but still counted here). + # 1024 was too small for real data: one clipped CSV read consumed it and the agent could never + # take a second turn. + p.add_argument("--max-completion-length", type=int, + default=int(os.environ.get("MAX_COMPLETION_LENGTH", "4096"))) + # The multi-turn cap on the SYNC path. Each iteration is one generate + one tool call, so this is + # the real bound on episode length; the client's own step_limit backs it up from the other side. + p.add_argument("--max-tool-calling-iterations", type=int, + default=int(os.environ.get("MAX_TOOL_ITERS", "8"))) + p.add_argument("--step-limit", type=int, default=int(os.environ.get("STEP_LIMIT", "12"))) + p.add_argument("--limit-tasks", type=int, default=int(os.environ.get("LIMIT_TASKS", "0"))) + p.add_argument("--report-to", default=os.environ.get("REPORT_TO", "none")) + # Saving is OFF by default because the 4-step smoke has nothing worth keeping; a real run turns + # it on. `save_steps` is in TRAINER steps, and with num_generations completions per prompt a step + # is one prompt, so 100 steps is one pass over a 100-task split. + p.add_argument("--save-steps", type=int, default=int(os.environ.get("SAVE_STEPS", "0"))) + args = p.parse_args() + + dataset = build_dataset(args.server, args.split, args.limit_tasks) + factory = white_box_bash_env( + args.server, split=args.split, toolsets=args.toolsets, step_limit=args.step_limit, + ) + print(f"[whitebox-bash] model={args.model} server={args.server} split={args.split} " + f"tasks={len(dataset)} tools={exposed_tool_names(args.toolsets)}", flush=True) + + config = GRPOConfig( + output_dir=args.output_dir, + learning_rate=args.learning_rate, + # KL anchor to the reference model. Costs a second model in memory, which is why + # vllm_gpu_memory_utilization is kept low. + beta=args.kl_beta, + warmup_steps=args.warmup_steps, + num_generations=args.num_generations, + per_device_train_batch_size=args.per_device_train_batch_size, + gradient_accumulation_steps=args.gradient_accumulation_steps, + max_steps=args.max_steps, + max_completion_length=args.max_completion_length, + max_tool_calling_iterations=args.max_tool_calling_iterations, + temperature=args.temperature, + # Qwen3.5's template opens by default on the 4B and closes it on the 2B -- INVERTED + # between two models of the same family. Pinning it here removes that as a variable. + chat_template_kwargs={"enable_thinking": False}, + gradient_checkpointing=True, + gradient_checkpointing_kwargs={"use_reentrant": False}, + log_completions=True, + num_completions_to_print=1, + logging_steps=1, + save_strategy="steps" if args.save_steps > 0 else "no", + **({"save_steps": args.save_steps, "save_total_limit": None} if args.save_steps > 0 else {}), + report_to=args.report_to, + # vLLM in-process on the training GPU. 0.3 leaves room for the optimizer states; the default + # 0.9 would OOM the trainer on the same card. + use_vllm=True, + vllm_mode="colocate", + vllm_gpu_memory_utilization=float(os.environ.get("VLLM_GPU_MEM_UTIL", "0.3")), + vllm_max_model_length=int(os.environ.get("VLLM_MAX_MODEL_LEN", "16384")), + ) + + GRPOTrainer( + model=args.model, + args=config, + train_dataset=dataset, + environment_factory=factory, + reward_funcs=[], # the environment owns the reward via get_reward() + ).train() + + +if __name__ == "__main__": + main() diff --git a/04-data-agent/train/training_audit.py b/04-data-agent/train/training_audit.py new file mode 100644 index 0000000..5c8f6dc --- /dev/null +++ b/04-data-agent/train/training_audit.py @@ -0,0 +1,182 @@ +"""Local capture artifacts and bounded coverage checks for the multiharness recipe.""" + +from __future__ import annotations + +import json +import math +import time +from pathlib import Path + +from transformers import TrainerCallback + + +def write_json(path, value): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + temporary = path.with_suffix(".tmp") + temporary.write_text(json.dumps(value, indent=2) + "\n") + temporary.replace(path) + + +class AuditedSession: + def __init__(self, session, path, metadata): + self.session = session + self.path = path + self.metadata = metadata + + def __getattr__(self, name): + return getattr(self.session, name) + + def wait_for_completion(self, *args, **kwargs): + started = time.time() + try: + return self.session.wait_for_completion(*args, **kwargs) + finally: + result = self.session.result + write_json(self.path, { + **self.metadata, "started_at": started, "finished_at": time.time(), + "result": result.model_dump(mode="json") if result is not None else None, + }) + + +class AuditedFactory: + def __init__(self, factory, directory): + self.factory = factory + self.directory = str(directory) + + def __getattr__(self, name): + # During unpickling the wrapped factory has not been assigned yet. + factory = self.__dict__.get("factory") + if factory is None: + raise AttributeError(name) + return getattr(factory, name) + + def create(self, task, seed=None, episode_id=None): + session = self.factory.create(task, seed=seed, episode_id=episode_id) + return AuditedSession( + session, Path(self.directory) / "rollouts" / f"{episode_id}.json", + {"group_id": (seed or 0) + getattr(self.factory, 'group_offset', 0), + "local_group_id": seed, "episode_id": episode_id, + "harness": self.factory.harness_for(seed), "task_index": session._task_index}, + ) + + +class PairCoverageCallback(TrainerCallback): + """Use TRL's existing collated group counter; require coverage to persist a full update. + + The extra update avoids stopping on the dataloader's prefetched batch. Coverage counts a + group once any of its rows enters training, and does not claim every forked row was consumed. + """ + + def __init__(self, trainer, n_tasks, harnesses, min_steps, directory, *, all_pairs=False, schedule=None, + group_offset=0): + self.trainer = trainer + self.n_rows = n_tasks + self.all_pairs = all_pairs + self.schedule = schedule + self.group_offset = group_offset + if all_pairs and n_tasks % len(harnesses): + raise ValueError("Cartesian task/harness schedule has an incomplete task") + self.n_tasks = n_tasks // len(harnesses) if all_pairs else n_tasks + if schedule is not None: + self.n_tasks = schedule['task_count'] + self.harnesses = harnesses + self.min_steps = min_steps + self.directory = Path(directory) + self.directory.mkdir(parents=True, exist_ok=True) + self.previous_pairs = set() + + def on_step_end(self, args, state, control, **kwargs): + group_ids = sorted(g + self.group_offset for g in self.trainer._trained_groups) + pairs = { + ((g % self.n_rows) // len(self.harnesses) if self.all_pairs else g % self.n_tasks, + g % len(self.harnesses)) for g in group_ids + } + if self.schedule is not None: + groups = self.schedule['groups'] + pairs = {(groups[g % len(groups)]['task_row'], + self.harnesses.index(groups[g % len(groups)]['harness'])) for g in group_ids} + stable_pairs = pairs & self.previous_pairs + complete = len(stable_pairs) == self.n_tasks * len(self.harnesses) + report = { + "optimizer_steps": state.global_step, "target_min_steps": self.min_steps, + "resumed_group_offset": self.group_offset, + "hard_max_steps": args.max_steps, "collated_group_ids": group_ids, + "covered_pairs": [{"task_row": t, "harness": self.harnesses[h]} + for t, h in sorted(stable_pairs)], + "pair_coverage_complete": complete, + "unique_tasks_covered": len({t for t, _ in stable_pairs}), + "target_unique_tasks": self.n_tasks, + "harness_pair_counts": {name: sum(h == i for _, h in stable_pairs) + for i, name in enumerate(self.harnesses)}, + "coverage_semantics": "at least one row per pair; stable over two optimizer boundaries", + } + write_json(self.directory / "coverage.json", report) + print(f"PAIR_COVERAGE step={state.global_step} pairs={len(stable_pairs)}/" + f"{self.n_tasks * len(self.harnesses)}", flush=True) + self.previous_pairs = pairs + if self.min_steps and state.global_step >= self.min_steps and complete: + control.should_training_stop = True + + def on_log(self, args, state, control, logs=None, **kwargs): + with (self.directory / "metrics.jsonl").open("a") as output: + output.write(json.dumps({"step": state.global_step, **(logs or {})}) + "\n") + for key in ("loss", "grad_norm", "ratio"): + value = (logs or {}).get(key) + if isinstance(value, (int, float)) and not math.isfinite(value): + raise RuntimeError(f"Non-finite {key}: stopping the training check") + + +class CheckpointReadyCallback(TrainerCallback): + """Publish a completion marker after Trainer has finished writing a checkpoint.""" + + def __init__(self, base_model, base_revision): + self.base_model = base_model + self.base_revision = base_revision + + def on_save(self, args, state, control, **kwargs): + if state.is_world_process_zero: + from checkpoint_artifacts import mark_saved + mark_saved(Path(args.output_dir) / f"checkpoint-{state.global_step}", + state.global_step, self.base_model, self.base_revision, + final=control.should_training_stop or state.global_step >= args.max_steps) + + +class PeriodicCheckpointCallback(TrainerCallback): + """Bound recovery loss when optimizer updates are too slow for step-based saves.""" + + def __init__(self, seconds): + if seconds <= 0: + raise ValueError("Checkpoint interval must be positive") + self.seconds = seconds + self.last_saved = None + + def on_train_begin(self, args, state, control, **kwargs): + self.last_saved = time.monotonic() + + def on_step_end(self, args, state, control, **kwargs): + if self.last_saved is not None and time.monotonic() - self.last_saved >= self.seconds: + control.should_save = True + + def on_save(self, args, state, control, **kwargs): + # A regular step-based checkpoint also resets the recovery interval. + self.last_saved = time.monotonic() + + +class WallTimeCallback(TrainerCallback): + """Save and stop at an optimizer boundary before the allocation expires.""" + + def __init__(self, seconds): + if seconds <= 0: + raise ValueError("Training wall-time budget must be positive") + self.seconds = seconds + self.started = None + + def on_train_begin(self, args, state, control, **kwargs): + self.started = time.monotonic() + + def on_step_end(self, args, state, control, **kwargs): + requested = (Path(args.output_dir).parent / 'STOP_AFTER_STEP').exists() + if requested or (self.started is not None and time.monotonic() - self.started >= self.seconds): + control.should_save = True + control.should_training_stop = True diff --git a/README.md b/README.md index db28a9a..ce81c75 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ results and README, plus the Hub repos it owns. They read in order but stand alo | **01** | **[LaTeX OCR](./01-latex-ocr/)** | Train Qwen3-VL-2B to read math images into LaTeX, with a verifiable reward. | 1 | 1 | 1 | ✅ stable | | **02** | **[Watercolour](./02-watercolour/)** | Train Qwen3.5-35B-A3B to paint watercolours by writing p5.brush sketches, rewarded by an aesthetic preference model. | 1 | 1 | 0 | ✅ trained | | **03** | **[GeoGuesser](./03-geoguesser/)** | Drop a VLM at a random street corner on Earth and score it on kilometres of error. | 1 | 1 | 1 | ✅ stable | +| **04** | **[Data Agent](./04-data-agent/)** | Three agent-loop implementations with exact-token training, fixed pass@1 evaluations, and local or HF Jobs reproduction. | 3 | 1 | 3 | ✅ trained | Generated from each project's `project.yaml` by `tools/build_index.py`. Adding a project means diff --git a/content/README.md b/content/README.md index c1cf2b2..2c5cc51 100644 --- a/content/README.md +++ b/content/README.md @@ -8,6 +8,7 @@ Each item ships to the Hub as a Space; the source of truth is here. | Article | Source | Live | |---|---|---| | **The ultimate guide to RL environments** | [`articles/rl-environments-guide/`](./articles/rl-environments-guide/) | [▶️ Space](https://huggingface.co/spaces/AdithyaSK/rl-environments-guide) | +| **The ultimate guide to multi-harness RL** | [`articles/multi-harness-rl/`](./articles/multi-harness-rl/) | 🚧 drafting | Built with [research-article-template](https://huggingface.co/spaces/tfrere/research-article-template) (Astro), served as a Docker Space. diff --git a/content/articles/multi-harness-rl/.ai/skills/article-frontmatter/SKILL.md b/content/articles/multi-harness-rl/.ai/skills/article-frontmatter/SKILL.md new file mode 100644 index 0000000..0d7c314 --- /dev/null +++ b/content/articles/multi-harness-rl/.ai/skills/article-frontmatter/SKILL.md @@ -0,0 +1,116 @@ +--- +name: article-frontmatter +description: Configure article metadata via MDX frontmatter. Use when the user asks about titles, authors, affiliations, template variants, banner, DOI, PDF export, or any article.mdx frontmatter field. +--- + +# Article Frontmatter Reference + +All metadata lives in `app/src/content/article.mdx` frontmatter (YAML block). + +## Frontmatter fields + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `title` | string | required | Article title. Supports `\n` for line breaks (rendered as `
`). | +| `description` | string | `""` | Short description / subtitle shown below the title. | +| `authors` | array | `[]` | List of authors (see below). | +| `affiliations` | array | `[]` | List of affiliations (see below). | +| `published` | string | — | Publication date, e.g. `"Apr. 04, 2026"`. | +| `template` | `"article"` or `"paper"` | `"article"` | Layout variant (see below). | +| `banner` | string | `"banner.html"` | Banner embed filename in `embeds/`. | +| `doi` | string | — | DOI identifier, shown in footer. | +| `showPdf` | boolean | `true` | Show PDF download button in metadata bar. | +| `tableOfContentsAutoCollapse` | boolean | `false` | Auto-collapse TOC sections on scroll. | +| `licence` | string | — | Licence text (HTML allowed), shown in footer. | +| `pdfProOnly` | boolean | `false` | Gate PDF download behind HF Pro badge. | +| `seoThumbImage` | string | — | Custom OG image URL for social sharing. | +| `links` | array | `[]` | External links shown in paper template hero (see below). | + +## Title line breaks + +Long titles are automatically balanced across lines (`text-wrap: balance`). Titles longer than 60 characters are automatically downsized for readability (>100 chars: even smaller). + +To force a manual line break, use `\n` inside the title string: + +```yaml +title: "Why Open-Source LLMs\nAre Reshaping the AI Landscape" +``` + +This renders as two lines in the Hero section. The plain-text version (for SEO / PDF) strips the break automatically. + +## Template variants + +| Value | Layout | Features | +|-------|--------|----------| +| `article` (default) | Full layout | Banner, sidebar TOC, figure numbering, citation block, DOI, PDF export | +| `paper` | Single centered column | No TOC sidebar, no figure numbering, no citation/DOI block, lighter footer | + +## Authors and affiliations + +```yaml +authors: + - name: "Alice Martin" + url: "https://example.com/alice" + affiliations: [1] + - name: "Bob Chen" + affiliations: [1, 2] +affiliations: + - name: "Hugging Face" + url: "https://huggingface.co" + - name: "MIT" + url: "https://mit.edu" +``` + +Affiliation indices are 1-based and rendered as superscript numbers next to author names. + +## External links (paper template) + +The `links` field adds buttons below the author/affiliation line in the `paper` template hero. Each link has a `label` and a `url`: + +```yaml +links: + - label: "Paper" + url: "https://arxiv.org/abs/..." + - label: "Code" + url: "https://github.com/..." + - label: "Demo" + url: "https://huggingface.co/spaces/..." + - label: "Data" + url: "https://huggingface.co/datasets/..." +``` + +Links are rendered as pill-shaped buttons and only visible in the `paper` template. They are hidden in the `article` template. + +## README tag (critical) + +The project `README.md` contains a YAML frontmatter block with a `tags` field: + +```yaml +tags: + - research-article-template +``` + +**NEVER remove the `research-article-template` tag from the README.** This tag is used by the [Research Article Gallery](https://huggingface.co/spaces/tfrere/research-article-gallery) to discover and list all articles built with this template. Removing it will make the Space invisible in the gallery. + +## Complete example + +```yaml +--- +title: "Scaling Laws for\nNeural Language Models" +description: "An empirical study of scaling behavior across model size, data, and compute" +authors: + - name: "Alice Martin" + url: "https://example.com/alice" + affiliations: [1] +affiliations: + - name: "Hugging Face" + url: "https://huggingface.co" +published: "Apr. 04, 2026" +template: "article" +banner: "banner.html" +doi: "10.1234/example.2026" +showPdf: true +tableOfContentsAutoCollapse: true +licence: "This work is licensed under CC BY 4.0." +--- +``` diff --git a/content/articles/multi-harness-rl/.ai/skills/create-html-embed/SKILL.md b/content/articles/multi-harness-rl/.ai/skills/create-html-embed/SKILL.md new file mode 100644 index 0000000..436aeb8 --- /dev/null +++ b/content/articles/multi-harness-rl/.ai/skills/create-html-embed/SKILL.md @@ -0,0 +1,133 @@ +--- +name: create-html-embed +description: Create self-contained D3 HTML embed charts for the research article template. Use when the user asks to create a chart, visualization, embed, D3 chart, line chart, bar chart, scatter plot, sankey diagram, or any data visualization as an HTML embed file. +--- + +# Create HTML Embed + +Create self-contained D3.js chart embeds for the research article template. + +## Before you start + +**Read the full directives file** for all conventions, patterns, and checklists: + +- [directives.md](directives.md) — single source of truth for embed authoring rules + +This covers: colors & palettes, layout, SVG scope, mounting, theming, controls, tooltips, data loading, responsiveness, legends, accessibility, performance, error handling, printing, and the full agent checklist. + +## Workflow + +### Step 1: Understand the request + +Clarify with the user: +- What type of chart? (line, bar, scatter, sankey, waffle, heatmap, custom) +- What data source? (CSV path, JSON, inline data) +- Interactive controls needed? (metric selector, filters) +- Any specific design requirements? + +### Step 2: Create the HTML file + +- Location: `app/src/content/embeds/` +- Naming: `d3-.html` (e.g., `d3-training-loss.html`) +- Root class: `.d3-` (must match filename) + +### Step 3: Follow the mandatory structure + +Every embed must have this structure: + +```html +
+ + +``` + +### Step 4: Integrate in MDX + +Import and use the `HtmlEmbed` component: + +```mdx +import HtmlEmbed from '../../components/HtmlEmbed.astro'; + + +``` + +#### HtmlEmbed props + +| Prop | Type | Description | +|------|------|-------------| +| `src` | string | Path to HTML file in `embeds/` (required) | +| `title` | string | Title above the card | +| `desc` | string | Description below (supports HTML) | +| `frameless` | boolean | Removes card background/border | +| `wide` | boolean | Wide layout (~1100px) | +| `data` | string or string[] | Path(s) to data files | +| `config` | object | JSON config passed via `data-config` attribute | + +#### Usage examples + +```mdx + + + + + + + + + + + + + + +``` + +## Key conventions (quick reference) + +Full details in the directives file. The critical ones: + +1. **Colors**: Use `window.ColorPalettes.getColors('categorical', n)` — never hardcode palettes +2. **CSS variables**: `--text-color`, `--surface-bg`, `--border-color`, `--axis-color`, `--tick-color`, `--grid-color` +3. **Dark mode**: Check `document.documentElement.getAttribute('data-theme') === 'dark'` +4. **Mount guard**: Always set `container.dataset.mounted = 'true'` +5. **Data loading**: Try `/data/` first, then `./assets/data/` — use `fetchFirstAvailable()` +6. **Responsiveness**: `ResizeObserver` on container, recompute on resize +7. **Legend**: HTML-based, title "Legend", swatch 14x14px +8. **Controls**: HTML only (no SVG UI), selects labeled "Metric" when applicable +9. **Tooltip**: Single `.d3-tooltip` absolutely positioned inside container +10. **No globals**: Everything in IIFE, nothing on `window` + +## Data files + +- Store data in: `app/src/content/assets/data/` +- Served from: `/data/` (public) at build time +- Formats: CSV (preferred for tabular), JSON (for nested/hierarchical) + +## Post-creation checklist + +After creating the embed, verify against the **Agent Checklist** (section 14.1) and **Definition of Done** (section 14.2) in [directives.md](directives.md). diff --git a/content/articles/multi-harness-rl/.ai/skills/create-html-embed/directives.md b/content/articles/multi-harness-rl/.ai/skills/create-html-embed/directives.md new file mode 100644 index 0000000..329833c --- /dev/null +++ b/content/articles/multi-harness-rl/.ai/skills/create-html-embed/directives.md @@ -0,0 +1,504 @@ +## Embed Chart Authoring Guidelines + +### Quickstart (TL;DR) +- Create a single self-contained HTML fragment: root div + scoped style + IIFE script. +- Draw marks/axes in SVG; render UI (legend and controls) in HTML. +- Place legend and controls BELOW the chart (header appended after the chart). Include a legend title "Legend" and a select labeled "Metric" when relevant. +- Load data from public `/data` first, then fall back to `assets/data`. +- Use `window.ColorPalettes` for colors; stick to CSS variables for theming. + +Minimal header markup: +```html +
+
Legend
+
+ +
+
+
+ + +
+ +
+``` + +See also: `d3-line-simple.html`, `d3-line-quad.html`, `d3-benchmark.html`. + +Authoring rules for creating a new interactive chart as a single self-contained `.html` file under `src/content/embeds/`. These conventions are derived from `d3-bar.html`, `d3-comparison.html`, `d3-neural.html`, `d3-line.html`, and `d3-pie.html`. + +### A) Colors & palettes (MANDATORY) +- Always obtain color arrays from `window.ColorPalettes`; do not hardcode palettes. +- Use the categorical/sequential/diverging helpers and the current primary color. +- If you change `--primary-color` dynamically, call `window.ColorPalettes.refresh()` so listeners update. + +Usage: +```js +// Usage (with explicit counts) +const cat = window.ColorPalettes.getColors('categorical', 8); +const seq = window.ColorPalettes.getColors('sequential', 8); +const div = window.ColorPalettes.getColors('diverging', 7); + +// For current primary color string +const primaryHex = window.ColorPalettes.getPrimary(); + +// If you change --primary-color dynamically, call refresh to notify listeners +document.documentElement.style.setProperty('--primary-color', '#6D4AFF'); +window.ColorPalettes.refresh(); +``` + +Notes: +- Keep chart accents (lines, markers, selection) aligned with `--primary-color`. +- Prefer CSS variables for fills/strokes when possible; derive series colors via `ColorPalettes`. +- Provide a graceful fallback to CSS variables if `window.ColorPalettes` is unavailable. + +### B) Layout & form elements (HTML-only) +- All UI controls (labels, selects, sliders, buttons, toggles) must be plain HTML inside the root container. +- Do not draw controls with SVG; style them consistently (rounded 8px, custom caret, focus ring). +- Use `