diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..134ecd663699948a60e93efc0c4e62833bd6f12b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +.git +.pytest_cache +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +artifacts +tests diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..a6344aac8c09253b3b630fb776ae94478aa0275b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,35 @@ +*.7z filter=lfs diff=lfs merge=lfs -text +*.arrow filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.ckpt filter=lfs diff=lfs merge=lfs -text +*.ftz filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.h5 filter=lfs diff=lfs merge=lfs -text +*.joblib filter=lfs diff=lfs merge=lfs -text +*.lfs.* filter=lfs diff=lfs merge=lfs -text +*.mlmodel filter=lfs diff=lfs merge=lfs -text +*.model filter=lfs diff=lfs merge=lfs -text +*.msgpack filter=lfs diff=lfs merge=lfs -text +*.npy filter=lfs diff=lfs merge=lfs -text +*.npz filter=lfs diff=lfs merge=lfs -text +*.onnx filter=lfs diff=lfs merge=lfs -text +*.ot filter=lfs diff=lfs merge=lfs -text +*.parquet filter=lfs diff=lfs merge=lfs -text +*.pb filter=lfs diff=lfs merge=lfs -text +*.pickle filter=lfs diff=lfs merge=lfs -text +*.pkl filter=lfs diff=lfs merge=lfs -text +*.pt filter=lfs diff=lfs merge=lfs -text +*.pth filter=lfs diff=lfs merge=lfs -text +*.rar filter=lfs diff=lfs merge=lfs -text +*.safetensors filter=lfs diff=lfs merge=lfs -text +saved_model/**/* filter=lfs diff=lfs merge=lfs -text +*.tar.* filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.tflite filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.wasm filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text +*tfevents* filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml new file mode 100644 index 0000000000000000000000000000000000000000..9a4c696c6ec778e18ec89ddc43506bf8ab350d54 --- /dev/null +++ b/.github/workflows/validation.yml @@ -0,0 +1,32 @@ +name: Validation + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install project + run: | + python -m pip install --upgrade pip + python -m pip install -e .[dev] + + - name: Run test suite + run: python -m pytest -q + + - name: Run validation gate + run: python scripts/validate_release.py + + - name: Build Docker image + run: docker build -t osint-openenv-validation . diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..bdcd8dd68bf444b45e9750d9c08cfb6f16065bdf --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.pyc +blueprint.txt +*.egg-info +artifacts/* +*.html +osint_dashboard.html +.venv/ +.tmp_compare/ +metaQA/ +.codex +TODO.txt +uv.lock diff --git a/.tmp_compare/Meta-s-LedgerShield b/.tmp_compare/Meta-s-LedgerShield new file mode 160000 index 0000000000000000000000000000000000000000..fd5c9b60ddfbd2eba9d09001938b63169ac98f7b --- /dev/null +++ b/.tmp_compare/Meta-s-LedgerShield @@ -0,0 +1 @@ +Subproject commit fd5c9b60ddfbd2eba9d09001938b63169ac98f7b diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..22b1e97852a90f6f86d1d9e8cd27e0fc0f8338e0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM python:3.12-slim + +RUN useradd -m -u 1000 user + +USER user +ENV HOME=/home/user \ + PATH=/home/user/.local/bin:$PATH \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PORT=7860 + +WORKDIR $HOME/app + +COPY --chown=user pyproject.toml README.md openenv.yaml inference.py $HOME/app/ +COPY --chown=user src $HOME/app/src +COPY --chown=user config $HOME/app/config +COPY --chown=user datasets $HOME/app/datasets +COPY --chown=user docs $HOME/app/docs +COPY --chown=user scripts $HOME/app/scripts +COPY --chown=user server.py $HOME/app/server.py + +RUN pip install --no-cache-dir --upgrade pip && \ + pip install --no-cache-dir -e ".[train]" && \ + chmod +x $HOME/app/scripts/space_start.sh + +EXPOSE 7860 + +CMD ["sh", "/home/user/app/scripts/space_start.sh"] diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..bf5cd40da149b40de12070d741d7d361013ea236 --- /dev/null +++ b/README.md @@ -0,0 +1,392 @@ +--- +title: OSINT OpenEnv +emoji: 🕵️ +colorFrom: blue +colorTo: yellow +sdk: docker +app_port: 7860 +pinned: false +license: apache-2.0 +tags: + - openenv + - osint + - benchmark + - docker + - fastapi +short_description: Docker OSINT benchmark with fixed OpenEnv tasks. +--- + +# OSINT OpenEnv + +OSINT OpenEnv is a synthetic benchmark environment for tool-using agents that must recover identities, trace events, and link entities across noisy multi-platform records. The project is designed to feel like a compact OSINT workflow rather than a raw QA dataset: agents query mock profiles, posts, forum threads, and semantic memory, build a working graph, and then submit an answer. + +The motivation is to provide a reproducible OpenEnv-compatible environment for evaluating graph-building and tool-using reasoning without depending on live web data, unstable APIs, or private corpora. That makes it useful for local development, regression testing, and hosted demos such as a Docker-based Hugging Face Space. + +## Environment Summary + +The environment generates or loads a hidden canonical graph of users, aliases, organizations, locations, posts, threads, and events. It then exposes partial platform views and a task list drawn from that graph. + +The default hosted Space uses the fixed-level benchmark in `datasets/fixed_levels/seed_fixed_levels.json`, which now contains 30 stable tasks over a larger shared seeded graph. + +The repository now supports two dataset backends: + +- `canonical` (existing fixed-level synthetic graph pipeline) +- `metaqa` (MetaQA KB + QA files for `1-hop`, `2-hop`, and `3-hop`) + +Use `config/shared_config.json` or CLI flags (`--dataset-mode`, `--metaqa-root`, `--metaqa-hops`, `--metaqa-splits`) to choose which backend to run. + +## Action Space + +The environment exposes three actions: + +- `CALL_TOOL`: query platform views or semantic memory such as `search_posts`, `get_profile`, `search_threads`, `get_connections`, or `search_memory`. +- `ADD_EDGE`: add a candidate relation to the working memory graph. +- `ANSWER`: submit the final answer as an exact node id string. + +## Observation Space + +Each step returns a JSON observation with four parts: + +- `tool_outputs`: the most recent tool results. +- `graph_snapshot`: the current working-memory graph edges. +- `action_history`: recent actions and rewards. +- `task`: the active task id, task type, and question. + +## Task Types And Difficulty + +The benchmark mixes direct lookups with multi-hop traces: + +- Easy: single-hop identity resolution, organization lookup, event lookup, or location lookup. +- Mid: two-hop alias-to-user-to-organization or thread-to-event-to-user traces. +- High: cross-platform multi-hop traces combining aliases, authored content, event references, organization links, and direct connections. + +In MetaQA mode, hop buckets are mapped into the same reward difficulty tiers: + +- `1-hop` -> `easy` +- `2-hop` -> `medium` +- `3-hop` -> `hard` + +Common task families include: + +- `identity_resolution` +- `network_discovery` +- `event_tracing` +- `cross_platform_linking` +- `deanonymization` +- `convoluted_trace` + +Expected difficulty increases with the number of relations the agent must chain together and whether the evidence is split across posts, threads, aliases, and profile edges. + +## Repository Layout + +```text +src/osint_env/ + agents/ single-agent and swarm runners + baselines/ reusable OpenAI baseline runner + config/ shared config and seed loading + data/ graph/view/task generation + domain/ dataclasses and environment models + env/ environment, reward logic, OpenEnv compatibility shim + eval/ evaluation metrics and leaderboard helpers + llm/ mock, Ollama, and OpenAI client wrappers + memory/ working graph and semantic memory + platforms/ tool APIs over synthetic platform views + viz/ dashboard export + +scripts/ + build_fixed_levels_dataset.py + run_openai_baseline.py + +datasets/fixed_levels/ + seed_fixed_levels.json + shared_config_fixed_levels.json + qwen_swarm_benchmark_fixed_levels.json + +server.py FastAPI app for local use and Docker/HF Spaces +Dockerfile Container entrypoint for Hugging Face Docker Spaces +``` + +## Setup + +Python 3.10+ is required. + +Local install: + +```bash +python -m pip install -e . +``` + +Install optional adversarial self-play training stack (TRL + Transformers): + +```bash +python -m pip install -e ".[train]" +``` + +Run tests: + +```bash +python -m pytest -q +``` + +Run the automated release gate: + +```bash +python scripts/validate_release.py +``` + +## Usage + +Run one demo episode: + +```bash +osint-env demo --agent-mode swarm --llm-provider mock +``` + +Run against MetaQA using the provided sample config: + +```bash +osint-env demo --config config/shared_config_metaqa.json --dataset-mode metaqa --llm-provider mock +``` + +Run MetaQA with only selected hop buckets: + +```bash +osint-env eval --config config/shared_config_metaqa.json --dataset-mode metaqa --metaqa-hops 1-hop,2-hop --episodes 5 --llm-provider mock +``` + +Run a quick evaluation: + +```bash +osint-env eval --episodes 5 --agent-mode swarm --llm-provider mock +``` + +Export a dashboard: + +```bash +osint-env benchmark --episodes 5 --agent-mode swarm --llm-provider mock --name quick_check +``` + +Run Kimi-style adversarial self-play scaffold (dry-run by default in the example config): + +```bash +osint-env train-self-play --config config/shared_config.json --train-config config/self_play_training_example.json --dry-run +``` + +When you have compute and the train dependencies installed, remove `--dry-run` (or set `"dry_run": false` in the training config) to execute TRL GRPO updates for alternating generator and answerer phases. + +The training config also supports `"model_topology": "dual"|"shared"`, `"phase_schedule": "generator_answerer"|"answerer_generator_answerer"`, `"tuning_mode": "full"|"lora"`, and `"canonical_graph_mode": "generate"|"fixed"` so you can switch between two-model vs single-model self-play, full fine-tuning vs LoRA adapters, and whether canonical graph structure is generated each round or kept fixed while training question/answer behavior. + +### Hugging Face Space Smoke Run (Qwen 3.5 0.8B + W&B) + +For a short verification run (enough to confirm W&B logging before scaling up), use: + +```bash +osint-env train-self-play --config config/shared_config.json --train-config config/self_play_training_hf_a10g_smoke.json +``` + +This config: + +- uses `Qwen/Qwen3.5-0.8B` +- enables W&B reporting (`wandb_enabled: true`) +- uses `pipeline_mode: "swarm_v2"` with `canonical_graph_mode: "fixed"` to keep canonical graph candidates stable while training question/answer behavior +- keeps training intentionally short (`rounds=1`, `max_steps=5` per phase) +- uses LoRA with small batch settings so it can run as a smoke test on an A10G + +To enable canonical graph generation during swarm_v2 training, switch `"canonical_graph_mode"` to `"generate"` in the training config. + +Space setup checklist: + +1. In Space **Settings -> Hardware**, select **NVIDIA A10G (large)**. +2. In Space **Settings -> Variables and secrets**, set `WANDB_API_KEY`. +3. Set `HF_TOKEN` in Space secrets to avoid unauthenticated Hub downloads and stricter rate limits. +4. Optionally set `WANDB_ENTITY` if your project belongs to a team. +5. Set `RUN_SELF_PLAY_TRAINING=1` in Space variables to trigger training during container startup. +6. Optional overrides: + - `TRAIN_SELF_PLAY_CONFIG_PATH` (default: `config/self_play_training_hf_a10g_smoke.json`) + - `TRAIN_ENV_CONFIG_PATH` (default: `config/shared_config.json`) + - `RUN_SELF_PLAY_DRY_RUN=1` to test startup wiring without GRPO updates. + - `OSINT_TRAIN_STRICT_ASSERTS=1` to fail fast when reward variance, KL, loss, grad norms, or parameter updates stay zero. +7. Restart the Space and monitor build/runtime logs for the training run. + +W&B run naming is controlled by `wandb_run_name_prefix` and will emit phase-specific runs like `...-r001-generator` and `...-r001-answerer`. + +### Reward Functions In Self-Play (Generator + Answerer) + +Self-play trains two policies with role-specific reward functions defined in `src/osint_env/training/rewards.py`. + +Generator reward (`GeneratorRewardFunction`) and answerer reward (`AnswererRewardFunction`) are both returned to GRPO as scalar scores per completion, and both are clipped to a stable range before optimization. + +#### Generator Reward (Task-Proposing Agent) + +The generator is rewarded for producing valid, grounded, diverse, and hard tasks. + +In `legacy` pipeline mode, the reward is a weighted sum: + +- `validity`: checks non-empty `question`, non-empty `answer`, and bounded `supporting_edges`. +- `hardness`: uses a frozen answerer judge; reward is higher when the judge gets the generated question wrong. +- `diversity`: penalizes near-duplicate questions via token-level Jaccard similarity against prior generated questions. +- `consistency`: checks that support edges exist in the canonical graph and that the answer/question are graph-grounded. + +Default weights (configurable through `generator_reward_weights` in training config): + +- `validity`: `0.35` +- `hardness`: `0.45` +- `diversity`: `0.10` +- `consistency`: `0.10` + +In `swarm_v2` pipeline mode, generation uses strict replay/validation first, then a structured reward: + +- Hard-gated validation via `SwarmV2ReplayValidator` (invalid samples get a fixed negative reward path). +- Reward components include validity, derivability/replayability, hardness, swarm diversity, shared-context pressure targeting, and PARL-inspired orchestration bonuses (`parallel` + `finish`). +- Invalid or non-replayable candidates are penalized before the weighted positive terms are applied. + +#### Answerer Reward (Question-Solving Agent) + +The answerer reward wraps environment-native grading so train-time behavior matches benchmark-time incentives. + +For each completion, `AnswererRewardFunction`: + +- extracts the predicted answer from completion text/JSON, +- reconstructs a transient `TaskInstance` from row fields (`question`, `answer`, `supporting_edges_json`, `difficulty`), +- optionally extracts predicted supporting edges from JSON or text, +- calls `compute_answer_reward(...)` from `src/osint_env/env/reward.py`. + +`compute_answer_reward` combines exact answer quality with graph-utility shaping: + +- output format validity and exact correctness, +- knowledge-carrier and knowledge-indexing utility, +- connectivity and supporting-edge F1 against task support edges, +- efficiency and compactness penalties, +- relation/entity informativeness and repetition control (difficulty-dependent). + +Difficulty controls (`easy`, `medium`, `hard`) are preserved during training exactly as in the environment scorer, so the answerer sees the same tiered reward profile used in evaluation. + +In `swarm_v2`, the answerer reward also adds PARL-style orchestration credit (spawn/finish behavior) on top of base answer reward when orchestrator telemetry is present in the completion payload. + +Detailed design notes are in `docs/adversarial_self_play.md`. + +## OpenAI Baseline + +The reproducible OpenAI baseline is implemented in `scripts/run_openai_baseline.py`. It runs on the fixed-level benchmark, uses a stable seeded graph/task set, writes a JSON artifact, appends a leaderboard record, and exports a dashboard. + +Default behavior: + +- dataset: fixed-level benchmark +- episodes: 30 +- max steps per episode: 8 +- temperature: 0.0 +- output artifact: `artifacts/baselines/openai_fixed_levels_latest.json` + +Run it with an API key: + +```bash +export OPENAI_API_KEY="your_key_here" +python scripts/run_openai_baseline.py --model gpt-5-nano +``` + +The script is designed to stay bounded enough for a normal benchmark pass to finish comfortably under 20 minutes on a lightweight chat model, while still using the full fixed task set. For repeatability it fixes the benchmark graph/tasks and uses deterministic decoding settings. Because remote model backends can still change over time, the output artifact also records model metadata and system fingerprints when available. + +## Inference Script + +The submission-ready inference entrypoint is the root `inference.py` file. It talks to the deployed Hugging Face Space over HTTP, uses the OpenAI client for all model calls, and emits structured stdout logs in the `[START]`, `[STEP]`, and `[END]` format. + +The script accepts `HF_TOKEN` as the primary auth variable and also supports `OPENAI_API_KEY` or `API_KEY` as local fallbacks. +After a successful run, `inference.py` also posts the evaluation summary back to the Space so the latest `/dashboard` view reflects that run. + +Required environment variables: + +- `API_BASE_URL` +- `MODEL_NAME` +- `HF_TOKEN` + +Optional environment variables: + +- `SPACE_URL` default: `https://siddeshwar1625-osint.hf.space` +- `TASK_INDICES` default: `0,10,20` +- `MAX_STEPS` default: `8` + +Example local test command against a running local server: + +```bash +API_BASE_URL=https://api.openai.com/v1 MODEL_NAME=gpt-5.4-mini OPENAI_API_KEY=your_key SPACE_URL=http://127.0.0.1:7860 python inference.py +``` + +Example test command against the deployed Space: + +```bash +API_BASE_URL=https://api.openai.com/v1 MODEL_NAME=gpt-5.4-mini OPENAI_API_KEY=your_key SPACE_URL=https://siddeshwar1625-osint.hf.space python inference.py +``` + +## Docker And Hugging Face Space + +The repository is ready for a Docker-based Hugging Face Space: + +- `README.md` includes `sdk: docker` +- `README.md` includes the `openenv` Space tag +- `Dockerfile` serves `server.py` on port `7860` + +Local Docker smoke test: + +```bash +docker build -t osint-openenv . +docker run --rm -p 7860:7860 osint-openenv +``` + +Then open `http://localhost:7860`. + +The FastAPI app serves: + +- `/`: overview page +- `/dashboard`: generated benchmark dashboard +- `/api/environment`: environment metadata +- `/health`: health check (validator-friendly alias) +- `/healthz`: health check (legacy alias) +- `/openenv.yaml`: OpenEnv HTTP spec stub +- `/openenv/tasks`: task enumeration +- `/reset` and `/openenv/reset`: episode reset endpoints +- `/step` and `/openenv/step`: episode step endpoints +- `/state` and `/openenv/state/{session_id}`: session state endpoints (`/state` returns the latest session) + +## Automated Validation + +The repository includes a pass/fail validation gate for the core delivery requirements: + +- Hugging Face Space readiness +- OpenEnv spec compliance +- reproducible baseline behavior +- at least 3 fixed tasks with working graders +- Docker image build in CI + +Local gate: + +```bash +python scripts/validate_release.py +``` + +CI gate: + +- `.github/workflows/validation.yml` +- runs `pytest` +- runs the validation script +- runs `docker build` + +## Baseline Scores + +The fixed-level benchmark was expanded from the earlier 15-question set to a 30-question set with a larger seeded graph, so older benchmark artifacts should be treated as legacy and regenerated on the new dataset before using them as reference scores. + +After you supply an OpenAI API key, the current baseline scores for the expanded benchmark will be written to: + +- `artifacts/baselines/openai_fixed_levels_latest.json` +- `artifacts/baselines/openai_fixed_levels_dashboard.html` + +## Notes On `pyproject.toml` + +The packaging file is structurally correct for a `src/` layout and editable installs. The main gaps were deployment/runtime related rather than build-breaking: + +- `openenv` is now version-bounded explicitly. +- `fastapi` and `uvicorn` are included because the repo now ships a real web server. +- pytest is pointed at the `tests/` directory, and the test suite also adds `src/` to `sys.path` so source-layout imports work reliably during local runs. + +## Development Notes + +The project keeps a lightweight local compatibility shim for `openenv` so the source tree remains importable even before dependencies are installed. In a normal install or Docker build, the real `openenv` package from PyPI is still used. diff --git a/artifacts/leaderboard.json b/artifacts/leaderboard.json new file mode 100644 index 0000000000000000000000000000000000000000..86bfca58d7583f431e001f412bd04a68e16c95ae --- /dev/null +++ b/artifacts/leaderboard.json @@ -0,0 +1,453 @@ +[ + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 1, + "swarm_enabled": true + }, + "created_at": "2026-04-01T12:03:13+00:00", + "episodes": 2, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.1, + "avg_connectivity_reward": 0.3, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": 0.024705877237863647, + "avg_format_reward": 0.15, + "avg_graph_f1": 1.0, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.15, + "avg_relation_informativeness_reward": 0.03137141693971891, + "avg_reward": 3.534162700533434, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8618382743087459, + "retrieval_signal": 0.7275, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.6082154588355165, + "task_success_rate": 1.0, + "tool_efficiency": 0.25 + }, + "run_id": "run_0001", + "run_name": "swarm_seed_smoke" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 1, + "swarm_enabled": true + }, + "created_at": "2026-04-01T12:16:28+00:00", + "episodes": 2, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.1, + "avg_connectivity_reward": 0.3, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": 0.024705877237863647, + "avg_format_reward": 0.15, + "avg_graph_f1": 1.0, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.15, + "avg_relation_informativeness_reward": 0.03137141693971891, + "avg_reward": 3.534162700533434, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8618382743087459, + "retrieval_signal": 0.7275, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.6082154588355165, + "task_success_rate": 1.0, + "tool_efficiency": 0.25 + }, + "run_id": "run_0002", + "run_name": "swarm_seed_smoke" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 0, + "swarm_enabled": true + }, + "created_at": "2026-04-01T12:25:15+00:00", + "episodes": 20, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.10000000000000002, + "avg_connectivity_reward": 0.23999999999999994, + "avg_diversity_reward": 0.08000000000000002, + "avg_entity_informativeness_reward": -0.00983642442912193, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 1.0, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.1125, + "avg_relation_informativeness_reward": 0.007185245326892638, + "avg_reward": 3.351267560586956, + "avg_soft_shaping_reward": 0.14999999999999997, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8573187614039594, + "retrieval_signal": 0.7143750000000001, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5814697641795541, + "task_success_rate": 1.0, + "tool_efficiency": 0.25 + }, + "run_id": "run_0003", + "run_name": "baseline_swarm" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 1, + "swarm_enabled": true + }, + "created_at": "2026-04-01T17:27:30+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.1, + "avg_connectivity_reward": 0.3, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": 0.06128386989162576, + "avg_format_reward": 0.15, + "avg_graph_f1": 1.0, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.3, + "avg_relation_informativeness_reward": 0.12, + "avg_reward": 3.916035942914144, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8718832338515622, + "retrieval_signal": 0.78, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.6332567739783251, + "task_success_rate": 1.0, + "tool_efficiency": 0.25 + }, + "run_id": "run_0004", + "run_name": "ollama_qwen_smoke" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 1, + "swarm_enabled": true + }, + "created_at": "2026-04-01T17:29:12+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.1, + "avg_connectivity_reward": 0.3, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": 0.06128386989162576, + "avg_format_reward": 0.15, + "avg_graph_f1": 1.0, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.3, + "avg_relation_informativeness_reward": 0.12, + "avg_reward": 4.059369276247478, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.9020114237119466, + "retrieval_signal": 0.78, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.6332567739783251, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0005", + "run_name": "ollama_qwen_smoke2" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 0, + "swarm_enabled": true + }, + "created_at": "2026-04-01T17:39:15+00:00", + "episodes": 2, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.2, + "avg_connectivity_reward": 0.0, + "avg_diversity_reward": 0.0683333333333333, + "avg_entity_informativeness_reward": -0.07397348480982455, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.6666666666666667, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.14884615384615385, + "avg_relation_informativeness_reward": -0.00860389783205907, + "avg_reward": 4.351764433970379, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6973935600514568, + "retrieval_signal": 0.7270961538461539, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5137345234716233, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0006", + "run_name": "high_timeout_shared_ctx" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 0, + "swarm_enabled": true + }, + "created_at": "2026-04-01T18:57:40+00:00", + "episodes": 3, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.13333333333333333, + "avg_connectivity_reward": 0.09999999999999999, + "avg_diversity_reward": 0.056666666666666664, + "avg_entity_informativeness_reward": -0.020478979694240708, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.8148148148148149, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.27, + "avg_relation_informativeness_reward": 0.07174291752145656, + "avg_reward": 4.0269419367756605, + "avg_soft_shaping_reward": 0.19999999999999998, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.7366215569569294, + "retrieval_signal": 0.7695000000000001, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5570861208987765, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0007", + "run_name": "episode_selector_check" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 15, + "swarm_enabled": true + }, + "created_at": "2026-04-01T19:11:44+00:00", + "episodes": 3, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.10000000000000002, + "avg_connectivity_reward": 0.3, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": -0.02722031691758704, + "avg_format_reward": 0.15, + "avg_graph_f1": 1.0, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.0, + "avg_relation_informativeness_reward": -0.00011920119799207429, + "avg_reward": 3.444079221573606, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8828572592896698, + "retrieval_signal": 0.675, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5915320963768841, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0008", + "run_name": "qwen_rerun" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 15, + "swarm_enabled": true + }, + "created_at": "2026-04-01T19:19:34+00:00", + "episodes": 3, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.10000000000000002, + "avg_connectivity_reward": 0.3, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": -0.024861029515896544, + "avg_format_reward": 0.15, + "avg_graph_f1": 1.0, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.0, + "avg_relation_informativeness_reward": -0.0024320085090966614, + "avg_reward": 3.4441257016641917, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8828581656226586, + "retrieval_signal": 0.675, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5915413923950014, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0009", + "run_name": "qwen_episode_fix" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 15, + "swarm_enabled": true + }, + "created_at": "2026-04-01T19:24:37+00:00", + "episodes": 3, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.10000000000000002, + "avg_connectivity_reward": 0.3, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": -0.02722031691758704, + "avg_format_reward": 0.15, + "avg_graph_f1": 1.0, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.0, + "avg_relation_informativeness_reward": -0.0030604289114462002, + "avg_reward": 3.4411379938601514, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8827999009847504, + "retrieval_signal": 0.675, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5909438508341933, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0010", + "run_name": "qwen_rerun_graph_fix" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 15, + "swarm_enabled": true + }, + "created_at": "2026-04-01T19:31:54+00:00", + "episodes": 15, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.16666666666666666, + "avg_connectivity_reward": 0.16999999999999998, + "avg_diversity_reward": 0.1157777777777778, + "avg_entity_informativeness_reward": -0.0181244777358718, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.8492063492063492, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.012000000000000002, + "avg_relation_informativeness_reward": 0.05935837081627929, + "avg_reward": 4.201760569277529, + "avg_soft_shaping_reward": 0.24999999999999994, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8534887252258901, + "retrieval_signal": 0.6792, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5847801119494148, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0011", + "run_name": "qwen_rerun_graph_fix" + } +] \ No newline at end of file diff --git a/config/seed_example.json b/config/seed_example.json new file mode 100644 index 0000000000000000000000000000000000000000..92b4aec40eac09d3a4dae1e70e34d9be4fe1bfd1 --- /dev/null +++ b/config/seed_example.json @@ -0,0 +1,52 @@ +{ + "seeding": { + "seeded_nodes": [ + { + "node_id": "alias_seed_001", + "node_type": "alias", + "attrs": { + "handle": "@shadow_seed" + } + }, + { + "node_id": "user_seed_001", + "node_type": "user", + "attrs": { + "name": "Seed User", + "org": "Helios Labs", + "location": "Pune" + } + } + ], + "_note": "Use with --seed-file. LLM provider and API keys are configured in config/shared_config.json or CLI flags.", + "seeded_edges": [ + { + "src": "alias_seed_001", + "rel": "alias_of", + "dst": "user_seed_001", + "confidence": 1.0 + } + ], + "seeded_questions": [ + { + "task_type": "identity_resolution", + "question": "Which canonical user owns alias alias_seed_001?", + "answer": "user_seed_001", + "supporting_edges": [ + { + "src": "alias_seed_001", + "rel": "alias_of", + "dst": "user_seed_001" + } + ], + "metadata": { + "source": "manual_seed" + } + } + ], + "llm_generate_remaining_graph": true, + "llm_generate_remaining_tasks": true, + "llm_generated_edge_budget": 6, + "llm_generated_task_budget": 8 + } +} diff --git a/config/seed_ollama_smoke.json b/config/seed_ollama_smoke.json new file mode 100644 index 0000000000000000000000000000000000000000..6deefcac60f1a885d83a4bb9ef9e445d55dd4646 --- /dev/null +++ b/config/seed_ollama_smoke.json @@ -0,0 +1,64 @@ +{ + "seeding": { + "seeded_nodes": [ + { + "node_id": "alias_smoke_001", + "node_type": "alias", + "attrs": { + "handle": "@smoke_alias" + } + }, + { + "node_id": "user_smoke_001", + "node_type": "user", + "attrs": { + "name": "Smoke User", + "org": "Apex Dynamics", + "location": "Bengaluru" + } + }, + { + "node_id": "org_smoke_001", + "node_type": "org", + "attrs": { + "name": "Apex Dynamics" + } + } + ], + "seeded_edges": [ + { + "src": "alias_smoke_001", + "rel": "alias_of", + "dst": "user_smoke_001", + "confidence": 1.0 + }, + { + "src": "user_smoke_001", + "rel": "works_at", + "dst": "org_smoke_001", + "confidence": 1.0 + } + ], + "seeded_questions": [ + { + "task_type": "identity_resolution", + "question": "Which canonical user owns alias alias_smoke_001?", + "answer": "user_smoke_001", + "supporting_edges": [ + { + "src": "alias_smoke_001", + "rel": "alias_of", + "dst": "user_smoke_001" + } + ], + "metadata": { + "source": "ollama_smoke" + } + } + ], + "llm_generate_remaining_graph": false, + "llm_generate_remaining_tasks": false, + "llm_generated_edge_budget": 0, + "llm_generated_task_budget": 0 + } +} diff --git a/config/self_play_training_example.json b/config/self_play_training_example.json new file mode 100644 index 0000000000000000000000000000000000000000..bb7c1ba689068eec2c29553c66d433bfadba391b --- /dev/null +++ b/config/self_play_training_example.json @@ -0,0 +1,105 @@ +{ + "rounds": 3, + "output_dir": "artifacts/self_play", + "dry_run": true, + "canonical_graph_mode": "generate", + "pipeline_mode": "swarm_v2", + "model_topology": "dual", + "phase_schedule": "generator_answerer", + "tuning_mode": "full", + "shared_model_name_or_path": "", + "seed_tasks_per_round": 16, + "generated_tasks_per_round": 24, + "generator_prompts_per_round": 24, + "max_graph_context_nodes": 100, + "max_graph_context_edges": 100, + "max_support_edges": 8, + "answerer_judge_max_new_tokens": 48, + "generator_reward_weights": { + "validity": 0.35, + "hardness": 0.45, + "diversity": 0.1, + "consistency": 0.1 + }, + "lora": { + "r": 16, + "alpha": 32, + "dropout": 0.05, + "target_modules": ["q_proj", "k_proj", "v_proj", "o_proj"], + "bias": "none", + "task_type": "CAUSAL_LM" + }, + "swarm_v2": { + "generator_swarm": { + "shared_context": true, + "max_agents": 4, + "max_breadth": 3, + "max_depth": 2, + "planner_rounds": 2, + "tools_per_agent": 2 + }, + "answerer_swarm": { + "shared_context": true, + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "planner_rounds": 2, + "tools_per_agent": 2 + }, + "validation": { + "max_support_edges": 8, + "max_path_hops": 4, + "max_context_nodes": 14, + "max_context_edges": 8, + "duplicate_similarity_threshold": 0.8 + }, + "shared_context": { + "shared_by_default": true, + "max_nodes": 14, + "max_edges": 8, + "target_pressure": 0.85 + } + }, + "generator_phase": { + "model_name_or_path": "Qwen/Qwen2.5-0.5B-Instruct", + "learning_rate": 1e-06, + "max_steps": 64, + "per_device_train_batch_size": 2, + "gradient_accumulation_steps": 4, + "num_generations": 4, + "max_completion_length": 256, + "temperature": 1.0, + "top_p": 1.0, + "beta": 0.01, + "epsilon": 0.2, + "num_iterations": 1, + "loss_type": "dapo", + "scale_rewards": "none", + "logging_steps": 10, + "save_steps": 50, + "output_subdir": "generator", + "use_vllm": false, + "vllm_mode": "colocate" + }, + "answerer_phase": { + "model_name_or_path": "Qwen/Qwen2.5-0.5B-Instruct", + "learning_rate": 1e-06, + "max_steps": 64, + "per_device_train_batch_size": 2, + "gradient_accumulation_steps": 4, + "num_generations": 4, + "max_completion_length": 192, + "temperature": 1.0, + "top_p": 1.0, + "beta": 0.01, + "epsilon": 0.2, + "num_iterations": 1, + "loss_type": "dapo", + "scale_rewards": "none", + "logging_steps": 10, + "save_steps": 50, + "output_subdir": "answerer", + "use_vllm": false, + "vllm_mode": "colocate" + } +} diff --git a/config/self_play_training_hf_a10g_smoke.json b/config/self_play_training_hf_a10g_smoke.json new file mode 100644 index 0000000000000000000000000000000000000000..0c55570a460b048279b677b8bdf1e644be210a03 --- /dev/null +++ b/config/self_play_training_hf_a10g_smoke.json @@ -0,0 +1,74 @@ +{ + "rounds": 2, + "output_dir": "artifacts/self_play_hf_a10g_train", + "dry_run": false, + "wandb_enabled": true, + "wandb_project": "osint-self-play-train", + "wandb_entity": "", + "wandb_run_name_prefix": "qwen25-05b-instruct-a10g-train", + "pipeline_mode": "swarm_v2", + "canonical_graph_mode": "fixed", + "model_topology": "shared", + "phase_schedule": "generator_answerer", + "tuning_mode": "lora", + "shared_model_name_or_path": "Qwen/Qwen2.5-0.5B-Instruct", + "seed_tasks_per_round": 16, + "generated_tasks_per_round": 24, + "generator_prompts_per_round": 24, + "max_graph_context_nodes": 24, + "max_graph_context_edges": 24, + "max_support_edges": 6, + "answerer_judge_max_new_tokens": 32, + "generator_phase": { + "model_name_or_path": "Qwen/Qwen2.5-0.5B-Instruct", + "learning_rate": 1e-06, + "max_steps": 50, + "per_device_train_batch_size": 4, + "gradient_accumulation_steps": 1, + "num_generations": 4, + "max_completion_length": 768, + "temperature": 0.9, + "top_p": 0.95, + "repetition_penalty": 1.1, + "beta": 0.01, + "epsilon": 0.2, + "num_iterations": 1, + "loss_type": "dapo", + "scale_rewards": "group", + "logging_steps": 1, + "save_steps": 10, + "output_subdir": "generator_train", + "use_vllm": false, + "vllm_mode": "colocate" + }, + "answerer_phase": { + "model_name_or_path": "Qwen/Qwen2.5-0.5B-Instruct", + "learning_rate": 1e-06, + "max_steps": 50, + "per_device_train_batch_size": 4, + "gradient_accumulation_steps": 1, + "num_generations": 4, + "max_completion_length": 256, + "temperature": 0.7, + "top_p": 0.95, + "repetition_penalty": 1.1, + "beta": 0.01, + "epsilon": 0.2, + "num_iterations": 1, + "loss_type": "dapo", + "scale_rewards": "group", + "logging_steps": 1, + "save_steps": 10, + "output_subdir": "answerer_train", + "use_vllm": false, + "vllm_mode": "colocate" + }, + "lora": { + "r": 8, + "alpha": 16, + "dropout": 0.05, + "target_modules": ["q_proj", "k_proj", "v_proj", "o_proj"], + "bias": "none", + "task_type": "CAUSAL_LM" + } +} diff --git a/config/shared_config.json b/config/shared_config.json new file mode 100644 index 0000000000000000000000000000000000000000..25b6a9f0fb7ce545f7ac4b9d06a241e6d714a59f --- /dev/null +++ b/config/shared_config.json @@ -0,0 +1,63 @@ +{ + "environment": { + "n_users": 40, + "alias_density": 0.35, + "noise_level": 0.15, + "red_herring_rate": 0.1, + "max_steps": 18, + "seed": 7 + }, + "dataset": { + "mode": "canonical", + "metaqa_root": "metaQA", + "metaqa_kb_path": "", + "metaqa_variant": "vanilla", + "metaqa_hops": ["1-hop", "2-hop", "3-hop"], + "metaqa_splits": ["train", "dev", "test"] + }, + "swarm": { + "enabled": true, + "max_agents": 3, + "max_breadth": 2, + "max_width": 2, + "max_depth": 2, + "planner_rounds": 2, + "tools_per_agent": 1 + }, + "spawn_reward": { + "lambda_parallel": 0.15, + "lambda_finish": 0.2, + "anneal": 1.0, + "max_parallel_hint": 3 + }, + "seeding": { + "seeded_nodes": [], + "seeded_edges": [], + "seeded_questions": [], + "llm_generate_remaining_graph": true, + "llm_generate_remaining_tasks": true, + "llm_generated_edge_budget": 6, + "llm_generated_task_budget": 8, + "llm_generation_parallel": true, + "llm_generation_workers": 3, + "llm_generation_retries": 2, + "allow_template_fallback_on_llm_failure": false + }, + "llm": { + "provider": "ollama", + "model": "qwen3:2b", + "temperature": 0.1, + "max_tokens": 256, + "timeout_seconds": 240, + "ollama_base_url": "http://127.0.0.1:11434", + "openai_base_url": "https://api.openai.com/v1", + "openai_api_key_env": "OPENAI_API_KEY", + "openai_api_key": "" + }, + "runtime": { + "default_episodes": 20, + "leaderboard_path": "artifacts/leaderboard.json", + "dashboard_path": "artifacts/osint_dashboard.html", + "sweep_dashboard_dir": "artifacts/sweep_dashboards" + } +} diff --git a/config/shared_config_metaqa.json b/config/shared_config_metaqa.json new file mode 100644 index 0000000000000000000000000000000000000000..686d8e036188c61d3c14d325a19a8a618c49c84d --- /dev/null +++ b/config/shared_config_metaqa.json @@ -0,0 +1,63 @@ +{ + "environment": { + "n_users": 40, + "alias_density": 0.35, + "noise_level": 0.15, + "red_herring_rate": 0.1, + "max_steps": 18, + "seed": 7 + }, + "dataset": { + "mode": "metaqa", + "metaqa_root": "metaQA", + "metaqa_kb_path": "metaQA/kb.txt", + "metaqa_variant": "vanilla", + "metaqa_hops": ["1-hop", "2-hop", "3-hop"], + "metaqa_splits": ["train", "dev", "test"] + }, + "swarm": { + "enabled": true, + "max_agents": 3, + "max_breadth": 2, + "max_width": 2, + "max_depth": 2, + "planner_rounds": 2, + "tools_per_agent": 1 + }, + "spawn_reward": { + "lambda_parallel": 0.15, + "lambda_finish": 0.2, + "anneal": 1.0, + "max_parallel_hint": 3 + }, + "seeding": { + "seeded_nodes": [], + "seeded_edges": [], + "seeded_questions": [], + "llm_generate_remaining_graph": false, + "llm_generate_remaining_tasks": false, + "llm_generated_edge_budget": 0, + "llm_generated_task_budget": 0, + "llm_generation_parallel": true, + "llm_generation_workers": 3, + "llm_generation_retries": 2, + "allow_template_fallback_on_llm_failure": false + }, + "llm": { + "provider": "mock", + "model": "qwen3:2b", + "temperature": 0.1, + "max_tokens": 256, + "timeout_seconds": 240, + "ollama_base_url": "http://127.0.0.1:11434", + "openai_base_url": "https://api.openai.com/v1", + "openai_api_key_env": "OPENAI_API_KEY", + "openai_api_key": "" + }, + "runtime": { + "default_episodes": 20, + "leaderboard_path": "artifacts/leaderboard_metaqa.json", + "dashboard_path": "artifacts/metaqa_dashboard.html", + "sweep_dashboard_dir": "artifacts/metaqa_sweep_dashboards" + } +} diff --git a/datasets/fixed_levels/README.md b/datasets/fixed_levels/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3c7e2b60d8e200f697826454595e5469c242098a --- /dev/null +++ b/datasets/fixed_levels/README.md @@ -0,0 +1,64 @@ +# Fixed Levels Submission Dataset + +This folder contains a fixed three-level OSINT benchmark set built on one shared base graph. + +## Files + +- `seed_fixed_levels.json`: master fixed seed with an expanded canonical graph and 30 fixed questions. +- `fixed_graph_questions.json`: extracted fixed dataset snapshot for submission packaging. +- `shared_config_fixed_levels.json`: run config used for generation and evaluation. +- `complete_dataset_qwen_generated.json`: full dataset after Qwen (`qwen3:2b` via Ollama) expands the graph. +- `qwen_swarm_eval_fixed_levels.json`: legacy Qwen swarm evaluation summary from the older smaller version of the set. +- `qwen_swarm_benchmark_fixed_levels.json`: legacy benchmark output from the older smaller version of the set. +- `leaderboard_fixed_levels.json`: leaderboard file for this dataset. +- `dashboard_fixed_levels.html`: interactive dashboard generated from the benchmark run. + +## Difficulty Design + +- Easy: 10 questions. These now use the older hard-style multi-hop traces as the new floor. +- Mid: 10 questions. Each question spans roughly 15-20 supporting nodes. +- High: 10 questions. Each question spans roughly 50 supporting nodes. + +All 30 questions are fixed and share the same larger seeded graph. + +## Regenerate Artifacts + +```bash +source ~/arl/bin/activate +cd /home/ritish/test1 +PYTHONPATH=src python scripts/build_fixed_levels_dataset.py \ + --seed-file datasets/fixed_levels/seed_fixed_levels.json \ + --shared-config datasets/fixed_levels/shared_config_fixed_levels.json \ + --output-dir datasets/fixed_levels +``` + +## Evaluate Qwen Swarm + +```bash +source ~/arl/bin/activate +cd /home/ritish/test1 +PYTHONPATH=src osint-env eval \ + --config datasets/fixed_levels/shared_config_fixed_levels.json \ + --seed-file datasets/fixed_levels/seed_fixed_levels.json \ + --agent-mode swarm \ + --llm-provider ollama \ + --llm-model qwen3:2b \ + --episodes 15 +``` + +## Benchmark + Dashboard + +```bash +source ~/arl/bin/activate +cd /home/ritish/test1 +PYTHONPATH=src osint-env benchmark \ + --config datasets/fixed_levels/shared_config_fixed_levels.json \ + --seed-file datasets/fixed_levels/seed_fixed_levels.json \ + --agent-mode swarm \ + --llm-provider ollama \ + --llm-model qwen3:2b \ + --episodes 15 \ + --name fixed_levels_qwen_swarm \ + --leaderboard datasets/fixed_levels/leaderboard_fixed_levels.json \ + --dashboard datasets/fixed_levels/dashboard_fixed_levels.html +``` diff --git a/datasets/fixed_levels/complete_dataset_qwen_generated.json b/datasets/fixed_levels/complete_dataset_qwen_generated.json new file mode 100644 index 0000000000000000000000000000000000000000..813cd92dfe5a5866cafac070a42dbec18a4f2cb0 --- /dev/null +++ b/datasets/fixed_levels/complete_dataset_qwen_generated.json @@ -0,0 +1,8798 @@ +{ + "canonical_graph": { + "edge_count": 249, + "edges": [ + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_0" + }, + { + "confidence": 1.0, + "dst": "loc_hyderabad", + "rel": "located_in", + "src": "user_0" + }, + { + "confidence": 1.0, + "dst": "org_northbridge", + "rel": "works_at", + "src": "user_1" + }, + { + "confidence": 1.0, + "dst": "loc_bengaluru", + "rel": "located_in", + "src": "user_1" + }, + { + "confidence": 1.0, + "dst": "org_northbridge", + "rel": "works_at", + "src": "user_2" + }, + { + "confidence": 1.0, + "dst": "loc_delhi", + "rel": "located_in", + "src": "user_2" + }, + { + "confidence": 1.0, + "dst": "org_northbridge", + "rel": "works_at", + "src": "user_3" + }, + { + "confidence": 1.0, + "dst": "loc_delhi", + "rel": "located_in", + "src": "user_3" + }, + { + "confidence": 1.0, + "dst": "org_northbridge", + "rel": "works_at", + "src": "user_4" + }, + { + "confidence": 1.0, + "dst": "loc_delhi", + "rel": "located_in", + "src": "user_4" + }, + { + "confidence": 1.0, + "dst": "org_northbridge", + "rel": "works_at", + "src": "user_5" + }, + { + "confidence": 1.0, + "dst": "loc_bengaluru", + "rel": "located_in", + "src": "user_5" + }, + { + "confidence": 1.0, + "dst": "user_5", + "rel": "alias_of", + "src": "alias_5_936" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_6" + }, + { + "confidence": 1.0, + "dst": "loc_delhi", + "rel": "located_in", + "src": "user_6" + }, + { + "confidence": 1.0, + "dst": "user_6", + "rel": "alias_of", + "src": "alias_6_801" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_7" + }, + { + "confidence": 1.0, + "dst": "loc_hyderabad", + "rel": "located_in", + "src": "user_7" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_8" + }, + { + "confidence": 1.0, + "dst": "loc_hyderabad", + "rel": "located_in", + "src": "user_8" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_9" + }, + { + "confidence": 1.0, + "dst": "loc_delhi", + "rel": "located_in", + "src": "user_9" + }, + { + "confidence": 1.0, + "dst": "org_northbridge", + "rel": "works_at", + "src": "user_10" + }, + { + "confidence": 1.0, + "dst": "loc_bengaluru", + "rel": "located_in", + "src": "user_10" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_11" + }, + { + "confidence": 1.0, + "dst": "loc_hyderabad", + "rel": "located_in", + "src": "user_11" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_12" + }, + { + "confidence": 1.0, + "dst": "loc_hyderabad", + "rel": "located_in", + "src": "user_12" + }, + { + "confidence": 1.0, + "dst": "user_12", + "rel": "alias_of", + "src": "alias_12_827" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_13" + }, + { + "confidence": 1.0, + "dst": "loc_hyderabad", + "rel": "located_in", + "src": "user_13" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_14" + }, + { + "confidence": 1.0, + "dst": "loc_delhi", + "rel": "located_in", + "src": "user_14" + }, + { + "confidence": 1.0, + "dst": "org_northbridge", + "rel": "works_at", + "src": "user_15" + }, + { + "confidence": 1.0, + "dst": "loc_delhi", + "rel": "located_in", + "src": "user_15" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_16" + }, + { + "confidence": 1.0, + "dst": "loc_delhi", + "rel": "located_in", + "src": "user_16" + }, + { + "confidence": 1.0, + "dst": "org_northbridge", + "rel": "works_at", + "src": "user_17" + }, + { + "confidence": 1.0, + "dst": "loc_hyderabad", + "rel": "located_in", + "src": "user_17" + }, + { + "confidence": 1.0, + "dst": "org_northbridge", + "rel": "works_at", + "src": "user_18" + }, + { + "confidence": 1.0, + "dst": "loc_delhi", + "rel": "located_in", + "src": "user_18" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_19" + }, + { + "confidence": 1.0, + "dst": "loc_pune", + "rel": "located_in", + "src": "user_19" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_20" + }, + { + "confidence": 1.0, + "dst": "loc_bengaluru", + "rel": "located_in", + "src": "user_20" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_21" + }, + { + "confidence": 1.0, + "dst": "loc_delhi", + "rel": "located_in", + "src": "user_21" + }, + { + "confidence": 1.0, + "dst": "user_21", + "rel": "alias_of", + "src": "alias_21_334" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_22" + }, + { + "confidence": 1.0, + "dst": "loc_delhi", + "rel": "located_in", + "src": "user_22" + }, + { + "confidence": 1.0, + "dst": "org_northbridge", + "rel": "works_at", + "src": "user_23" + }, + { + "confidence": 1.0, + "dst": "loc_hyderabad", + "rel": "located_in", + "src": "user_23" + }, + { + "confidence": 0.8, + "dst": "user_9", + "rel": "connected_to", + "src": "user_21" + }, + { + "confidence": 0.8, + "dst": "user_20", + "rel": "connected_to", + "src": "user_17" + }, + { + "confidence": 0.8, + "dst": "user_22", + "rel": "connected_to", + "src": "user_16" + }, + { + "confidence": 0.8, + "dst": "user_12", + "rel": "connected_to", + "src": "user_15" + }, + { + "confidence": 0.8, + "dst": "user_6", + "rel": "connected_to", + "src": "user_13" + }, + { + "confidence": 0.8, + "dst": "user_8", + "rel": "connected_to", + "src": "user_9" + }, + { + "confidence": 0.8, + "dst": "user_1", + "rel": "connected_to", + "src": "user_0" + }, + { + "confidence": 0.8, + "dst": "user_20", + "rel": "connected_to", + "src": "user_4" + }, + { + "confidence": 0.8, + "dst": "user_14", + "rel": "connected_to", + "src": "user_19" + }, + { + "confidence": 0.8, + "dst": "user_15", + "rel": "connected_to", + "src": "user_16" + }, + { + "confidence": 0.8, + "dst": "user_6", + "rel": "connected_to", + "src": "user_11" + }, + { + "confidence": 0.8, + "dst": "user_22", + "rel": "connected_to", + "src": "user_8" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_gita" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_gita" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_helios_labs" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "operates_in", + "src": "org_northbridge_logistics" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "operates_in", + "src": "org_apex_dynamics" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "operates_in", + "src": "org_blueharbor_media" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_tidewatch_ops" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_kestrel_works" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "operates_in", + "src": "org_atlas_freight" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_sunmesh_analytics" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "operates_in", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "operates_in", + "src": "org_emberline_security" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "operates_in", + "src": "org_harborlight_transit" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 0.92, + "dst": "user_faris", + "rel": "connected_to", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "user_diya", + "rel": "connected_to", + "src": "user_faris" + }, + { + "confidence": 0.89, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_diya" + }, + { + "confidence": 0.87, + "dst": "user_aria", + "rel": "connected_to", + "src": "user_elin" + }, + { + "confidence": 0.84, + "dst": "user_cyrus", + "rel": "connected_to", + "src": "user_aria" + }, + { + "confidence": 0.83, + "dst": "user_gita", + "rel": "connected_to", + "src": "user_cyrus" + }, + { + "confidence": 0.82, + "dst": "user_jules", + "rel": "connected_to", + "src": "user_gita" + }, + { + "confidence": 0.81, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_jules" + }, + { + "confidence": 0.9, + "dst": "user_ivy", + "rel": "connected_to", + "src": "user_diya" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.93, + "dst": "user_omar", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "user_mika", + "rel": "connected_to", + "src": "user_omar" + }, + { + "confidence": 0.89, + "dst": "user_quinn", + "rel": "connected_to", + "src": "user_mika" + }, + { + "confidence": 0.88, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.86, + "dst": "user_soren", + "rel": "connected_to", + "src": "user_rhea" + }, + { + "confidence": 0.86, + "dst": "user_tara", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.84, + "dst": "user_kian", + "rel": "connected_to", + "src": "user_tara" + }, + { + "confidence": 0.91, + "dst": "user_leena", + "rel": "connected_to", + "src": "user_priya" + }, + { + "confidence": 0.83, + "dst": "user_aria", + "rel": "connected_to", + "src": "user_leena" + }, + { + "confidence": 0.82, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_priya" + }, + { + "confidence": 0.8, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.79, + "dst": "user_faris", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.78, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_shift_roster", + "rel": "authored_post", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "post_sat_phone_ping", + "rel": "authored_post", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "post_drone_parts", + "rel": "authored_post", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "post_relay_schedule", + "rel": "authored_post", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_customs_tag", + "rel": "authored_post", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "post_hull_signal", + "rel": "authored_post", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "post_basin_photo", + "rel": "authored_post", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "post_foundry_map", + "rel": "authored_post", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "post_lantern_route", + "rel": "authored_post", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "post_uplink_note", + "rel": "authored_post", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "references", + "src": "post_sat_phone_ping" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_sat_phone_ping" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "thr_port_audit", + "rel": "authored_thread", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "thr_customs_breach", + "rel": "authored_thread", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "thr_relay_map", + "rel": "authored_thread", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "thr_foundry_watch", + "rel": "authored_thread", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "thr_basin_shift", + "rel": "authored_thread", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "thr_quiet_manifest", + "rel": "authored_thread", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "thr_uplink_route", + "rel": "authored_thread", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "thr_ember_tide_watch", + "rel": "authored_thread", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "thr_ghost_signal_net", + "rel": "authored_thread", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "discusses", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "references", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "discusses", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "references", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "references", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "discusses", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "discusses", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "references", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_faris" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "investigates", + "src": "user_diya" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "monitors", + "src": "user_leena" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_cyrus" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "investigates", + "src": "user_elin" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "reports_on", + "src": "user_jules" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "monitors", + "src": "user_priya" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_mika" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_quinn" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_ember_tide", + "rel": "collaborates_on", + "src": "user_rhea" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + }, + { + "confidence": 0.9, + "dst": "event_silent_current", + "rel": "monitors", + "src": "user_gita" + }, + { + "confidence": 0.9, + "dst": "event_silent_current", + "rel": "reports_on", + "src": "user_jules" + }, + { + "confidence": 0.77, + "dst": "event_glass_harbor", + "rel": "connected_to", + "src": "event_project_lantern" + }, + { + "confidence": 0.77, + "dst": "event_amber_veil", + "rel": "connected_to", + "src": "event_black_kite" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 0.77, + "dst": "org_northbridge_logistics", + "rel": "connected_to", + "src": "org_atlas_freight" + }, + { + "confidence": 0.77, + "dst": "org_emberline_security", + "rel": "connected_to", + "src": "org_orion_customs" + }, + { + "confidence": 0.77, + "dst": "org_tidewatch_ops", + "rel": "connected_to", + "src": "org_harborlight_transit" + } + ], + "node_count": 118, + "nodes": [ + { + "attrs": { + "handle": "@alias_12_827" + }, + "node_id": "alias_12_827", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@alias_21_334" + }, + "node_id": "alias_21_334", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@alias_5_936" + }, + "node_id": "alias_5_936", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@alias_6_801" + }, + "node_id": "alias_6_801", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@basinraven" + }, + "node_id": "alias_basinraven", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@cinderveil" + }, + "node_id": "alias_cinderveil", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@docksparrow" + }, + "node_id": "alias_docksparrow", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@emberglass" + }, + "node_id": "alias_emberglass", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@frostledger" + }, + "node_id": "alias_frostledger", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@hollowsignal" + }, + "node_id": "alias_hollowsignal", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@ironwhisper" + }, + "node_id": "alias_ironwhisper", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@lanternmoth" + }, + "node_id": "alias_lanternmoth", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@mapleghost" + }, + "node_id": "alias_mapleghost", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@monsoonbyte" + }, + "node_id": "alias_monsoonbyte", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@nightrelay" + }, + "node_id": "alias_nightrelay", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@orchidfox" + }, + "node_id": "alias_orchidfox", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@quartzlotus" + }, + "node_id": "alias_quartzlotus", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@sablekeel" + }, + "node_id": "alias_sablekeel", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@steelquill" + }, + "node_id": "alias_steelquill", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@tideshard" + }, + "node_id": "alias_tideshard", + "node_type": "alias" + }, + { + "attrs": { + "name": "Amber Veil" + }, + "node_id": "event_amber_veil", + "node_type": "event" + }, + { + "attrs": { + "name": "Black Kite" + }, + "node_id": "event_black_kite", + "node_type": "event" + }, + { + "attrs": { + "name": "Ember Tide" + }, + "node_id": "event_ember_tide", + "node_type": "event" + }, + { + "attrs": { + "name": "Ghost Signal" + }, + "node_id": "event_ghost_signal", + "node_type": "event" + }, + { + "attrs": { + "name": "Glass Harbor" + }, + "node_id": "event_glass_harbor", + "node_type": "event" + }, + { + "attrs": { + "name": "Iron Wharf" + }, + "node_id": "event_iron_wharf", + "node_type": "event" + }, + { + "attrs": { + "name": "Project Lantern" + }, + "node_id": "event_project_lantern", + "node_type": "event" + }, + { + "attrs": { + "name": "Silent Current" + }, + "node_id": "event_silent_current", + "node_type": "event" + }, + { + "attrs": { + "name": "Bengaluru" + }, + "node_id": "loc_bengaluru", + "node_type": "location" + }, + { + "attrs": { + "name": "Delhi" + }, + "node_id": "loc_delhi", + "node_type": "location" + }, + { + "attrs": { + "name": "Dockyard 17" + }, + "node_id": "loc_dockyard17", + "node_type": "location" + }, + { + "attrs": { + "name": "East Quay" + }, + "node_id": "loc_east_quay", + "node_type": "location" + }, + { + "attrs": { + "name": "Foundry Row" + }, + "node_id": "loc_foundry_row", + "node_type": "location" + }, + { + "attrs": { + "name": "Hyderabad" + }, + "node_id": "loc_hyderabad", + "node_type": "location" + }, + { + "attrs": { + "name": "North Basin" + }, + "node_id": "loc_north_basin", + "node_type": "location" + }, + { + "attrs": { + "name": "Old Town" + }, + "node_id": "loc_old_town", + "node_type": "location" + }, + { + "attrs": { + "name": "Pune" + }, + "node_id": "loc_pune", + "node_type": "location" + }, + { + "attrs": { + "name": "Rivergate" + }, + "node_id": "loc_rivergate", + "node_type": "location" + }, + { + "attrs": { + "name": "Sector 9" + }, + "node_id": "loc_sector9", + "node_type": "location" + }, + { + "attrs": { + "name": "Uplink Yard" + }, + "node_id": "loc_uplink_yard", + "node_type": "location" + }, + { + "attrs": { + "name": "Apex Dynamics" + }, + "node_id": "org_apex_dynamics", + "node_type": "org" + }, + { + "attrs": { + "name": "Atlas Freight" + }, + "node_id": "org_atlas_freight", + "node_type": "org" + }, + { + "attrs": { + "name": "Blueharbor Media" + }, + "node_id": "org_blueharbor_media", + "node_type": "org" + }, + { + "attrs": { + "name": "Emberline Security" + }, + "node_id": "org_emberline_security", + "node_type": "org" + }, + { + "attrs": { + "name": "Harborlight Transit" + }, + "node_id": "org_harborlight_transit", + "node_type": "org" + }, + { + "attrs": { + "name": "Helios Labs" + }, + "node_id": "org_helios_labs", + "node_type": "org" + }, + { + "attrs": { + "name": "Kestrel Works" + }, + "node_id": "org_kestrel_works", + "node_type": "org" + }, + { + "attrs": { + "name": "Northbridge" + }, + "node_id": "org_northbridge", + "node_type": "org" + }, + { + "attrs": { + "name": "Northbridge Logistics" + }, + "node_id": "org_northbridge_logistics", + "node_type": "org" + }, + { + "attrs": { + "name": "Orion Customs" + }, + "node_id": "org_orion_customs", + "node_type": "org" + }, + { + "attrs": { + "name": "Sunmesh Analytics" + }, + "node_id": "org_sunmesh_analytics", + "node_type": "org" + }, + { + "attrs": { + "name": "Tidewatch Ops" + }, + "node_id": "org_tidewatch_ops", + "node_type": "org" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_basin_photo", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_customs_tag", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_drone_parts", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_foundry_map", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_hull_signal", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_lantern_route", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_midnight_manifest", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_quay_ledgers", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_relay_schedule", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_sat_phone_ping", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_shift_roster", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_uplink_note", + "node_type": "post" + }, + { + "attrs": { + "topic": "basin_shift" + }, + "node_id": "thr_basin_shift", + "node_type": "thread" + }, + { + "attrs": { + "topic": "customs_breach" + }, + "node_id": "thr_customs_breach", + "node_type": "thread" + }, + { + "attrs": { + "topic": "ember_tide" + }, + "node_id": "thr_ember_tide_watch", + "node_type": "thread" + }, + { + "attrs": { + "topic": "foundry_watch" + }, + "node_id": "thr_foundry_watch", + "node_type": "thread" + }, + { + "attrs": { + "topic": "ghost_signal" + }, + "node_id": "thr_ghost_signal_net", + "node_type": "thread" + }, + { + "attrs": { + "topic": "port_audit" + }, + "node_id": "thr_port_audit", + "node_type": "thread" + }, + { + "attrs": { + "topic": "quiet_manifest" + }, + "node_id": "thr_quiet_manifest", + "node_type": "thread" + }, + { + "attrs": { + "topic": "relay_map" + }, + "node_id": "thr_relay_map", + "node_type": "thread" + }, + { + "attrs": { + "topic": "supply_chain" + }, + "node_id": "thr_supply_leak", + "node_type": "thread" + }, + { + "attrs": { + "topic": "uplink_route" + }, + "node_id": "thr_uplink_route", + "node_type": "thread" + }, + { + "attrs": { + "location": "Hyderabad", + "name": "Person 0", + "org": "Apex Dynamics" + }, + "node_id": "user_0", + "node_type": "user" + }, + { + "attrs": { + "location": "Bengaluru", + "name": "Person 1", + "org": "Northbridge" + }, + "node_id": "user_1", + "node_type": "user" + }, + { + "attrs": { + "location": "Bengaluru", + "name": "Person 10", + "org": "Northbridge" + }, + "node_id": "user_10", + "node_type": "user" + }, + { + "attrs": { + "location": "Hyderabad", + "name": "Person 11", + "org": "Apex Dynamics" + }, + "node_id": "user_11", + "node_type": "user" + }, + { + "attrs": { + "location": "Hyderabad", + "name": "Person 12", + "org": "Apex Dynamics" + }, + "node_id": "user_12", + "node_type": "user" + }, + { + "attrs": { + "location": "Hyderabad", + "name": "Person 13", + "org": "Helios Labs" + }, + "node_id": "user_13", + "node_type": "user" + }, + { + "attrs": { + "location": "Delhi", + "name": "Person 14", + "org": "Apex Dynamics" + }, + "node_id": "user_14", + "node_type": "user" + }, + { + "attrs": { + "location": "Delhi", + "name": "Person 15", + "org": "Northbridge" + }, + "node_id": "user_15", + "node_type": "user" + }, + { + "attrs": { + "location": "Delhi", + "name": "Person 16", + "org": "Helios Labs" + }, + "node_id": "user_16", + "node_type": "user" + }, + { + "attrs": { + "location": "Hyderabad", + "name": "Person 17", + "org": "Northbridge" + }, + "node_id": "user_17", + "node_type": "user" + }, + { + "attrs": { + "location": "Delhi", + "name": "Person 18", + "org": "Northbridge" + }, + "node_id": "user_18", + "node_type": "user" + }, + { + "attrs": { + "location": "Pune", + "name": "Person 19", + "org": "Apex Dynamics" + }, + "node_id": "user_19", + "node_type": "user" + }, + { + "attrs": { + "location": "Delhi", + "name": "Person 2", + "org": "Northbridge" + }, + "node_id": "user_2", + "node_type": "user" + }, + { + "attrs": { + "location": "Bengaluru", + "name": "Person 20", + "org": "Apex Dynamics" + }, + "node_id": "user_20", + "node_type": "user" + }, + { + "attrs": { + "location": "Delhi", + "name": "Person 21", + "org": "Helios Labs" + }, + "node_id": "user_21", + "node_type": "user" + }, + { + "attrs": { + "location": "Delhi", + "name": "Person 22", + "org": "Apex Dynamics" + }, + "node_id": "user_22", + "node_type": "user" + }, + { + "attrs": { + "location": "Hyderabad", + "name": "Person 23", + "org": "Northbridge" + }, + "node_id": "user_23", + "node_type": "user" + }, + { + "attrs": { + "location": "Delhi", + "name": "Person 3", + "org": "Northbridge" + }, + "node_id": "user_3", + "node_type": "user" + }, + { + "attrs": { + "location": "Delhi", + "name": "Person 4", + "org": "Northbridge" + }, + "node_id": "user_4", + "node_type": "user" + }, + { + "attrs": { + "location": "Bengaluru", + "name": "Person 5", + "org": "Northbridge" + }, + "node_id": "user_5", + "node_type": "user" + }, + { + "attrs": { + "location": "Delhi", + "name": "Person 6", + "org": "Apex Dynamics" + }, + "node_id": "user_6", + "node_type": "user" + }, + { + "attrs": { + "location": "Hyderabad", + "name": "Person 7", + "org": "Helios Labs" + }, + "node_id": "user_7", + "node_type": "user" + }, + { + "attrs": { + "location": "Hyderabad", + "name": "Person 8", + "org": "Helios Labs" + }, + "node_id": "user_8", + "node_type": "user" + }, + { + "attrs": { + "location": "Delhi", + "name": "Person 9", + "org": "Helios Labs" + }, + "node_id": "user_9", + "node_type": "user" + }, + { + "attrs": { + "location": "Sector 9", + "name": "Aria Sen", + "org": "Helios Labs" + }, + "node_id": "user_aria", + "node_type": "user" + }, + { + "attrs": { + "location": "Dockyard 17", + "name": "Bharat Kulkarni", + "org": "Northbridge Logistics" + }, + "node_id": "user_bharat", + "node_type": "user" + }, + { + "attrs": { + "location": "Old Town", + "name": "Cyrus Mehta", + "org": "Apex Dynamics" + }, + "node_id": "user_cyrus", + "node_type": "user" + }, + { + "attrs": { + "location": "Old Town", + "name": "Diya Roy", + "org": "Blueharbor Media" + }, + "node_id": "user_diya", + "node_type": "user" + }, + { + "attrs": { + "location": "Sector 9", + "name": "Elin Das", + "org": "Helios Labs" + }, + "node_id": "user_elin", + "node_type": "user" + }, + { + "attrs": { + "location": "Rivergate", + "name": "Faris Noor", + "org": "Tidewatch Ops" + }, + "node_id": "user_faris", + "node_type": "user" + }, + { + "attrs": { + "location": "Old Town", + "name": "Gita Pradhan", + "org": "Apex Dynamics" + }, + "node_id": "user_gita", + "node_type": "user" + }, + { + "attrs": { + "location": "Dockyard 17", + "name": "Hiro Tan", + "org": "Northbridge Logistics" + }, + "node_id": "user_hiro", + "node_type": "user" + }, + { + "attrs": { + "location": "Rivergate", + "name": "Ivy Kapoor", + "org": "Kestrel Works" + }, + "node_id": "user_ivy", + "node_type": "user" + }, + { + "attrs": { + "location": "Old Town", + "name": "Jules Banerjee", + "org": "Blueharbor Media" + }, + "node_id": "user_jules", + "node_type": "user" + }, + { + "attrs": { + "location": "East Quay", + "name": "Kian Bose", + "org": "Atlas Freight" + }, + "node_id": "user_kian", + "node_type": "user" + }, + { + "attrs": { + "location": "Sector 9", + "name": "Leena Das", + "org": "Sunmesh Analytics" + }, + "node_id": "user_leena", + "node_type": "user" + }, + { + "attrs": { + "location": "North Basin", + "name": "Mika Solanki", + "org": "Orion Customs" + }, + "node_id": "user_mika", + "node_type": "user" + }, + { + "attrs": { + "location": "Foundry Row", + "name": "Nora Iqbal", + "org": "Emberline Security" + }, + "node_id": "user_nora", + "node_type": "user" + }, + { + "attrs": { + "location": "East Quay", + "name": "Omar Sheikh", + "org": "Atlas Freight" + }, + "node_id": "user_omar", + "node_type": "user" + }, + { + "attrs": { + "location": "Sector 9", + "name": "Priya Menon", + "org": "Sunmesh Analytics" + }, + "node_id": "user_priya", + "node_type": "user" + }, + { + "attrs": { + "location": "North Basin", + "name": "Quinn Rao", + "org": "Orion Customs" + }, + "node_id": "user_quinn", + "node_type": "user" + }, + { + "attrs": { + "location": "Foundry Row", + "name": "Rhea Kapoor", + "org": "Emberline Security" + }, + "node_id": "user_rhea", + "node_type": "user" + }, + { + "attrs": { + "location": "Uplink Yard", + "name": "Soren Malik", + "org": "Harborlight Transit" + }, + "node_id": "user_soren", + "node_type": "user" + }, + { + "attrs": { + "location": "Uplink Yard", + "name": "Tara Dey", + "org": "Harborlight Transit" + }, + "node_id": "user_tara", + "node_type": "user" + } + ] + }, + "dataset_name": "fixed_levels_submission_set", + "difficulty_counts": { + "easy": 10, + "high": 10, + "mid": 10 + }, + "environment": { + "alias_density": 0.2, + "n_users": 24, + "noise_level": 0.12, + "red_herring_rate": 0.08, + "seed": 2026 + }, + "generation_mode": "llm_expanded", + "llm": { + "max_tokens": 384, + "model": "qwen3:2b", + "ollama_base_url": "http://127.0.0.1:11434", + "openai_api_key": "", + "openai_api_key_env": "OPENAI_API_KEY", + "openai_base_url": "https://api.openai.com/v1", + "provider": "ollama", + "temperature": 0.05, + "timeout_seconds": 240 + }, + "platform_views": { + "counts": { + "forum_threads": 8, + "microblog_posts": 44, + "profiles": 47 + }, + "forum_threads": [ + { + "author_id": "user_17", + "comments": [ + { + "text": "Following this.", + "user_id": "user_6" + }, + { + "text": "Interesting link.", + "user_id": "user_16" + } + ], + "thread_id": "thr_0", + "topic": "startup" + }, + { + "author_id": "user_6", + "comments": [ + { + "text": "Following this.", + "user_id": "user_10" + }, + { + "text": "Interesting link.", + "user_id": "user_6" + } + ], + "thread_id": "thr_1", + "topic": "infra" + }, + { + "author_id": "user_tara", + "comments": [ + { + "text": "Following this.", + "user_id": "user_22" + }, + { + "text": "Interesting link.", + "user_id": "user_11" + } + ], + "thread_id": "thr_2", + "topic": "security" + }, + { + "author_id": "user_rhea", + "comments": [ + { + "text": "Following this.", + "user_id": "user_ivy" + }, + { + "text": "Interesting link.", + "user_id": "user_20" + } + ], + "thread_id": "thr_3", + "topic": "security" + }, + { + "author_id": "user_17", + "comments": [ + { + "text": "Following this.", + "user_id": "user_18" + }, + { + "text": "Interesting link.", + "user_id": "user_20" + } + ], + "thread_id": "thr_4", + "topic": "security" + }, + { + "author_id": "user_10", + "comments": [ + { + "text": "Following this.", + "user_id": "user_16" + }, + { + "text": "Interesting link.", + "user_id": "user_4" + } + ], + "thread_id": "thr_5", + "topic": "ai" + }, + { + "author_id": "user_0", + "comments": [ + { + "text": "Following this.", + "user_id": "user_16" + }, + { + "text": "Interesting link.", + "user_id": "user_cyrus" + } + ], + "thread_id": "thr_6", + "topic": "infra" + }, + { + "author_id": "user_0", + "comments": [ + { + "text": "Following this.", + "user_id": "user_6" + }, + { + "text": "Interesting link.", + "user_id": "user_15" + } + ], + "thread_id": "thr_7", + "topic": "security" + } + ], + "microblog_posts": [ + { + "canonical_user": "user_0", + "mentions": [ + "user_15" + ], + "post_id": "post_0", + "text": "Update 0 from Apex Dynamics #hyderabad", + "timestamp": 1000, + "user_id": "user_0" + }, + { + "canonical_user": "user_1", + "mentions": [ + "user_1" + ], + "post_id": "post_1", + "text": "Update 1 from Northbridge #bengaluru", + "timestamp": 1001, + "user_id": "user_1" + }, + { + "canonical_user": "user_2", + "mentions": [ + "user_3" + ], + "post_id": "post_2", + "text": "Update 2 from Northbridge #delhi", + "timestamp": 1002, + "user_id": "user_2" + }, + { + "canonical_user": "user_rhea", + "mentions": [ + "user_5" + ], + "post_id": "post_3", + "text": "Update 3 from Northbridge #delhi", + "timestamp": 1003, + "user_id": "alias_cinderveil" + }, + { + "canonical_user": "user_4", + "mentions": [ + "user_15" + ], + "post_id": "post_4", + "text": "Update 4 from Northbridge #delhi", + "timestamp": 1004, + "user_id": "user_4" + }, + { + "canonical_user": "user_5", + "mentions": [ + "user_15" + ], + "post_id": "post_5", + "text": "Update 5 from Northbridge #bengaluru", + "timestamp": 1005, + "user_id": "user_5" + }, + { + "canonical_user": "user_diya", + "mentions": [ + "user_13" + ], + "post_id": "post_6", + "text": "Update 6 from Apex Dynamics #delhi", + "timestamp": 1006, + "user_id": "alias_monsoonbyte" + }, + { + "canonical_user": "user_5", + "mentions": [ + "user_14" + ], + "post_id": "post_7", + "text": "Rumor: Update 7 from Helios Labs #hyderabad maybe fake", + "timestamp": 1007, + "user_id": "alias_5_936" + }, + { + "canonical_user": "user_8", + "mentions": [ + "user_2" + ], + "post_id": "post_8", + "text": "Update 8 from Helios Labs #hyderabad", + "timestamp": 1008, + "user_id": "user_8" + }, + { + "canonical_user": "user_9", + "mentions": [ + "user_3" + ], + "post_id": "post_9", + "text": "Update 9 from Helios Labs #delhi", + "timestamp": 1009, + "user_id": "user_9" + }, + { + "canonical_user": "user_10", + "mentions": [ + "user_8" + ], + "post_id": "post_10", + "text": "Update 10 from Northbridge #bengaluru", + "timestamp": 1010, + "user_id": "user_10" + }, + { + "canonical_user": "user_11", + "mentions": [ + "user_4" + ], + "post_id": "post_11", + "text": "Update 11 from Apex Dynamics #hyderabad", + "timestamp": 1011, + "user_id": "user_11" + }, + { + "canonical_user": "user_kian", + "mentions": [ + "user_15" + ], + "post_id": "post_12", + "text": "Update 12 from Apex Dynamics #hyderabad", + "timestamp": 1012, + "user_id": "alias_lanternmoth" + }, + { + "canonical_user": "user_13", + "mentions": [ + "user_17" + ], + "post_id": "post_13", + "text": "Update 13 from Helios Labs #hyderabad", + "timestamp": 1013, + "user_id": "user_13" + }, + { + "canonical_user": "user_kian", + "mentions": [ + "user_19" + ], + "post_id": "post_14", + "text": "Update 14 from Apex Dynamics #delhi", + "timestamp": 1014, + "user_id": "alias_lanternmoth" + }, + { + "canonical_user": "user_15", + "mentions": [ + "user_2" + ], + "post_id": "post_15", + "text": "Update 15 from Northbridge #delhi", + "timestamp": 1015, + "user_id": "user_15" + }, + { + "canonical_user": "user_bharat", + "mentions": [ + "user_18" + ], + "post_id": "post_16", + "text": "Update 16 from Helios Labs #delhi", + "timestamp": 1016, + "user_id": "alias_steelquill" + }, + { + "canonical_user": "user_soren", + "mentions": [ + "user_4" + ], + "post_id": "post_17", + "text": "Update 17 from Northbridge #hyderabad", + "timestamp": 1017, + "user_id": "alias_tideshard" + }, + { + "canonical_user": "user_18", + "mentions": [ + "user_0" + ], + "post_id": "post_18", + "text": "Update 18 from Northbridge #delhi", + "timestamp": 1018, + "user_id": "user_18" + }, + { + "canonical_user": "user_19", + "mentions": [ + "user_2" + ], + "post_id": "post_19", + "text": "Update 19 from Apex Dynamics #pune", + "timestamp": 1019, + "user_id": "user_19" + }, + { + "canonical_user": "user_nora", + "mentions": [ + "user_0" + ], + "post_id": "post_20", + "text": "Update 20 from Apex Dynamics #bengaluru", + "timestamp": 1020, + "user_id": "alias_emberglass" + }, + { + "canonical_user": "user_omar", + "mentions": [ + "user_9" + ], + "post_id": "post_21", + "text": "Update 21 from Helios Labs #delhi", + "timestamp": 1021, + "user_id": "alias_ironwhisper" + }, + { + "canonical_user": "user_22", + "mentions": [ + "user_15" + ], + "post_id": "post_22", + "text": "Update 22 from Apex Dynamics #delhi", + "timestamp": 1022, + "user_id": "user_22" + }, + { + "canonical_user": "user_23", + "mentions": [ + "user_5" + ], + "post_id": "post_23", + "text": "Update 23 from Northbridge #hyderabad", + "timestamp": 1023, + "user_id": "user_23" + }, + { + "canonical_user": "user_rhea", + "mentions": [ + "user_19" + ], + "post_id": "post_24", + "text": "Update 24 from Helios Labs #sector 9", + "timestamp": 1024, + "user_id": "alias_cinderveil" + }, + { + "canonical_user": "user_leena", + "mentions": [ + "user_22" + ], + "post_id": "post_25", + "text": "Update 25 from Northbridge Logistics #dockyard 17", + "timestamp": 1025, + "user_id": "alias_frostledger" + }, + { + "canonical_user": "user_mika", + "mentions": [ + "user_20" + ], + "post_id": "post_26", + "text": "Rumor: Update 26 from Apex Dynamics #old town maybe fake", + "timestamp": 1026, + "user_id": "alias_basinraven" + }, + { + "canonical_user": "user_diya", + "mentions": [ + "user_17" + ], + "post_id": "post_27", + "text": "Rumor: Update 27 from Blueharbor Media #old town maybe fake", + "timestamp": 1027, + "user_id": "user_diya" + }, + { + "canonical_user": "user_elin", + "mentions": [ + "user_20" + ], + "post_id": "post_28", + "text": "Rumor: Update 28 from Helios Labs #sector 9 maybe fake", + "timestamp": 1028, + "user_id": "user_elin" + }, + { + "canonical_user": "user_5", + "mentions": [ + "user_16" + ], + "post_id": "post_29", + "text": "Update 29 from Tidewatch Ops #rivergate", + "timestamp": 1029, + "user_id": "alias_5_936" + }, + { + "canonical_user": "user_rhea", + "mentions": [ + "user_4" + ], + "post_id": "post_30", + "text": "Update 30 from Apex Dynamics #old town", + "timestamp": 1030, + "user_id": "alias_cinderveil" + }, + { + "canonical_user": "user_bharat", + "mentions": [ + "user_13" + ], + "post_id": "post_31", + "text": "Update 31 from Northbridge Logistics #dockyard 17", + "timestamp": 1031, + "user_id": "alias_steelquill" + }, + { + "canonical_user": "user_ivy", + "mentions": [ + "user_12" + ], + "post_id": "post_32", + "text": "Update 32 from Kestrel Works #rivergate", + "timestamp": 1032, + "user_id": "user_ivy" + }, + { + "canonical_user": "user_nora", + "mentions": [ + "user_14" + ], + "post_id": "post_33", + "text": "Update 33 from Blueharbor Media #old town", + "timestamp": 1033, + "user_id": "alias_emberglass" + }, + { + "canonical_user": "user_kian", + "mentions": [ + "user_13" + ], + "post_id": "post_34", + "text": "Update 34 from Atlas Freight #east quay", + "timestamp": 1034, + "user_id": "user_kian" + }, + { + "canonical_user": "user_bharat", + "mentions": [ + "user_15" + ], + "post_id": "post_35", + "text": "Update 35 from Sunmesh Analytics #sector 9", + "timestamp": 1035, + "user_id": "alias_steelquill" + }, + { + "canonical_user": "user_rhea", + "mentions": [ + "user_8" + ], + "post_id": "post_36", + "text": "Update 36 from Orion Customs #north basin", + "timestamp": 1036, + "user_id": "alias_cinderveil" + }, + { + "canonical_user": "user_nora", + "mentions": [ + "user_7" + ], + "post_id": "post_37", + "text": "Update 37 from Emberline Security #foundry row", + "timestamp": 1037, + "user_id": "user_nora" + }, + { + "canonical_user": "user_12", + "mentions": [ + "user_20" + ], + "post_id": "post_38", + "text": "Update 38 from Atlas Freight #east quay", + "timestamp": 1038, + "user_id": "alias_12_827" + }, + { + "canonical_user": "user_tara", + "mentions": [ + "user_21" + ], + "post_id": "post_39", + "text": "Update 39 from Sunmesh Analytics #sector 9", + "timestamp": 1039, + "user_id": "alias_sablekeel" + }, + { + "canonical_user": "user_5", + "mentions": [ + "user_5" + ], + "post_id": "post_40", + "text": "Update 40 from Orion Customs #north basin", + "timestamp": 1040, + "user_id": "alias_5_936" + }, + { + "canonical_user": "user_kian", + "mentions": [ + "user_6" + ], + "post_id": "post_41", + "text": "Update 41 from Emberline Security #foundry row", + "timestamp": 1041, + "user_id": "alias_lanternmoth" + }, + { + "canonical_user": "user_elin", + "mentions": [ + "user_18" + ], + "post_id": "post_42", + "text": "Update 42 from Harborlight Transit #uplink yard", + "timestamp": 1042, + "user_id": "alias_mapleghost" + }, + { + "canonical_user": "user_tara", + "mentions": [ + "user_18" + ], + "post_id": "post_43", + "text": "Update 43 from Harborlight Transit #uplink yard", + "timestamp": 1043, + "user_id": "user_tara" + } + ], + "profiles": [ + { + "connections": [ + "user_1" + ], + "location": "Hyderabad", + "name": "Person 0", + "org": "Apex Dynamics", + "user_id": "user_0", + "work_history": [ + "Apex Dynamics" + ] + }, + { + "connections": [], + "location": "Bengaluru", + "name": "Person 1", + "org": "Northbridge", + "user_id": "user_1", + "work_history": [ + "Northbridge" + ] + }, + { + "connections": [], + "location": "Delhi", + "name": "Person 2", + "org": "Northbridge", + "user_id": "user_2", + "work_history": [ + "Northbridge" + ] + }, + { + "connections": [], + "location": "Delhi", + "name": "Person 3", + "org": "Northbridge", + "user_id": "user_3", + "work_history": [ + "Northbridge" + ] + }, + { + "connections": [ + "user_20" + ], + "location": "Delhi", + "name": "Person 4", + "org": "Northbridge", + "user_id": "user_4", + "work_history": [ + "Northbridge" + ] + }, + { + "connections": [], + "location": "Bengaluru", + "name": "Person 5", + "org": "Northbridge", + "user_id": "user_5", + "work_history": [ + "Northbridge" + ] + }, + { + "connections": [], + "location": "Delhi", + "name": "Person 6", + "org": "Apex Dynamics", + "user_id": "user_6", + "work_history": [ + "Apex Dynamics" + ] + }, + { + "connections": [], + "location": "Hyderabad", + "name": "Person 7", + "org": "Helios Labs", + "user_id": "user_7", + "work_history": [ + "Helios Labs" + ] + }, + { + "connections": [ + "user_22" + ], + "location": "Hyderabad", + "name": "Person 8", + "org": "Helios Labs", + "user_id": "user_8", + "work_history": [ + "Helios Labs" + ] + }, + { + "connections": [ + "user_8" + ], + "location": "Delhi", + "name": "Person 9", + "org": "Helios Labs", + "user_id": "user_9", + "work_history": [ + "Helios Labs" + ] + }, + { + "connections": [], + "location": "Bengaluru", + "name": "Person 10", + "org": "Northbridge", + "user_id": "user_10", + "work_history": [ + "Northbridge" + ] + }, + { + "connections": [ + "user_6" + ], + "location": "Hyderabad", + "name": "Person 11", + "org": "Apex Dynamics", + "user_id": "user_11", + "work_history": [ + "Apex Dynamics" + ] + }, + { + "connections": [], + "location": "Hyderabad", + "name": "Person 12", + "org": "Apex Dynamics", + "user_id": "user_12", + "work_history": [ + "Apex Dynamics" + ] + }, + { + "connections": [ + "user_6" + ], + "location": "Hyderabad", + "name": "Person 13", + "org": "Helios Labs", + "user_id": "user_13", + "work_history": [ + "Helios Labs" + ] + }, + { + "connections": [], + "location": "Delhi", + "name": "Person 14", + "org": "Apex Dynamics", + "user_id": "user_14", + "work_history": [ + "Apex Dynamics" + ] + }, + { + "connections": [ + "user_12" + ], + "location": "Delhi", + "name": "Person 15", + "org": "Northbridge", + "user_id": "user_15", + "work_history": [ + "Northbridge" + ] + }, + { + "connections": [ + "user_22", + "user_15" + ], + "location": "Delhi", + "name": "Person 16", + "org": "Helios Labs", + "user_id": "user_16", + "work_history": [ + "Helios Labs" + ] + }, + { + "connections": [ + "user_20" + ], + "location": "Hyderabad", + "name": "Person 17", + "org": "Northbridge", + "user_id": "user_17", + "work_history": [ + "Northbridge" + ] + }, + { + "connections": [], + "location": "Delhi", + "name": "Person 18", + "org": "Northbridge", + "user_id": "user_18", + "work_history": [ + "Northbridge" + ] + }, + { + "connections": [ + "user_14" + ], + "location": "Pune", + "name": "Person 19", + "org": "Apex Dynamics", + "user_id": "user_19", + "work_history": [ + "Apex Dynamics" + ] + }, + { + "connections": [], + "location": "Bengaluru", + "name": "Person 20", + "org": "Apex Dynamics", + "user_id": "user_20", + "work_history": [ + "Apex Dynamics" + ] + }, + { + "connections": [ + "user_9" + ], + "location": "Delhi", + "name": "Person 21", + "org": "Helios Labs", + "user_id": "user_21", + "work_history": [ + "Helios Labs" + ] + }, + { + "connections": [], + "location": "Delhi", + "name": "Person 22", + "org": "Apex Dynamics", + "user_id": "user_22", + "work_history": [ + "Apex Dynamics" + ] + }, + { + "connections": [], + "location": "Hyderabad", + "name": "Person 23", + "org": "Northbridge", + "user_id": "user_23", + "work_history": [ + "Northbridge" + ] + }, + { + "connections": [ + "user_cyrus" + ], + "location": "Sector 9", + "name": "Aria Sen", + "org": "Helios Labs", + "user_id": "user_aria", + "work_history": [ + "Helios Labs" + ] + }, + { + "connections": [ + "user_hiro" + ], + "location": "Dockyard 17", + "name": "Bharat Kulkarni", + "org": "Northbridge Logistics", + "user_id": "user_bharat", + "work_history": [ + "Northbridge Logistics" + ] + }, + { + "connections": [ + "user_gita" + ], + "location": "Old Town", + "name": "Cyrus Mehta", + "org": "Apex Dynamics", + "user_id": "user_cyrus", + "work_history": [ + "Apex Dynamics" + ] + }, + { + "connections": [ + "user_elin", + "user_ivy" + ], + "location": "Old Town", + "name": "Diya Roy", + "org": "Blueharbor Media", + "user_id": "user_diya", + "work_history": [ + "Blueharbor Media" + ] + }, + { + "connections": [ + "user_aria" + ], + "location": "Sector 9", + "name": "Elin Das", + "org": "Helios Labs", + "user_id": "user_elin", + "work_history": [ + "Helios Labs" + ] + }, + { + "connections": [ + "user_diya" + ], + "location": "Rivergate", + "name": "Faris Noor", + "org": "Tidewatch Ops", + "user_id": "user_faris", + "work_history": [ + "Tidewatch Ops" + ] + }, + { + "connections": [ + "user_jules" + ], + "location": "Old Town", + "name": "Gita Pradhan", + "org": "Apex Dynamics", + "user_id": "user_gita", + "work_history": [ + "Apex Dynamics" + ] + }, + { + "connections": [ + "user_faris" + ], + "location": "Dockyard 17", + "name": "Hiro Tan", + "org": "Northbridge Logistics", + "user_id": "user_hiro", + "work_history": [ + "Northbridge Logistics" + ] + }, + { + "connections": [ + "user_bharat", + "user_elin" + ], + "location": "Rivergate", + "name": "Ivy Kapoor", + "org": "Kestrel Works", + "user_id": "user_ivy", + "work_history": [ + "Kestrel Works" + ] + }, + { + "connections": [ + "user_bharat" + ], + "location": "Old Town", + "name": "Jules Banerjee", + "org": "Blueharbor Media", + "user_id": "user_jules", + "work_history": [ + "Blueharbor Media" + ] + }, + { + "connections": [ + "user_omar", + "user_bharat" + ], + "location": "East Quay", + "name": "Kian Bose", + "org": "Atlas Freight", + "user_id": "user_kian", + "work_history": [ + "Atlas Freight" + ] + }, + { + "connections": [ + "user_aria" + ], + "location": "Sector 9", + "name": "Leena Das", + "org": "Sunmesh Analytics", + "user_id": "user_leena", + "work_history": [ + "Sunmesh Analytics" + ] + }, + { + "connections": [ + "user_quinn" + ], + "location": "North Basin", + "name": "Mika Solanki", + "org": "Orion Customs", + "user_id": "user_mika", + "work_history": [ + "Orion Customs" + ] + }, + { + "connections": [ + "user_rhea" + ], + "location": "Foundry Row", + "name": "Nora Iqbal", + "org": "Emberline Security", + "user_id": "user_nora", + "work_history": [ + "Emberline Security" + ] + }, + { + "connections": [ + "user_mika" + ], + "location": "East Quay", + "name": "Omar Sheikh", + "org": "Atlas Freight", + "user_id": "user_omar", + "work_history": [ + "Atlas Freight" + ] + }, + { + "connections": [ + "user_leena", + "user_nora" + ], + "location": "Sector 9", + "name": "Priya Menon", + "org": "Sunmesh Analytics", + "user_id": "user_priya", + "work_history": [ + "Sunmesh Analytics" + ] + }, + { + "connections": [ + "user_nora", + "user_hiro" + ], + "location": "North Basin", + "name": "Quinn Rao", + "org": "Orion Customs", + "user_id": "user_quinn", + "work_history": [ + "Orion Customs" + ] + }, + { + "connections": [ + "user_soren" + ], + "location": "Foundry Row", + "name": "Rhea Kapoor", + "org": "Emberline Security", + "user_id": "user_rhea", + "work_history": [ + "Emberline Security" + ] + }, + { + "connections": [ + "user_tara", + "user_faris" + ], + "location": "Uplink Yard", + "name": "Soren Malik", + "org": "Harborlight Transit", + "user_id": "user_soren", + "work_history": [ + "Harborlight Transit" + ] + }, + { + "connections": [ + "user_kian" + ], + "location": "Uplink Yard", + "name": "Tara Dey", + "org": "Harborlight Transit", + "user_id": "user_tara", + "work_history": [ + "Harborlight Transit" + ] + }, + { + "connections": [], + "location": "Remote", + "name": "P123", + "org": "Unknown Ventures", + "user_id": "noise_0", + "work_history": [] + }, + { + "connections": [], + "location": "Unknown", + "name": "P196", + "org": "Unknown Ventures", + "user_id": "noise_1", + "work_history": [] + }, + { + "connections": [], + "location": "Remote", + "name": "P898", + "org": "Unknown Ventures", + "user_id": "noise_2", + "work_history": [] + } + ] + }, + "seed_file": "datasets\\fixed_levels\\seed_fixed_levels.json", + "shared_config": "datasets\\fixed_levels\\shared_config_fixed_levels.json", + "task_count": 30, + "tasks": [ + { + "answer": "user_bharat", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_01", + "support_nodes": 6 + }, + "question": "alias_orchidfox -> post_midnight_manifest -> loc_dockyard17 -> connected collaborator on event_project_lantern. Who is it?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + } + ], + "task_id": "seed_task_0", + "task_type": "fixed_trace" + }, + { + "answer": "user_hiro", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_02", + "support_nodes": 5 + }, + "question": "thr_supply_leak references org_northbridge_logistics. Which alias_docksparrow user works there and collaborates on event_project_lantern?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + } + ], + "task_id": "seed_task_1", + "task_type": "fixed_trace" + }, + { + "answer": "user_diya", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_03", + "support_nodes": 7 + }, + "question": "alias_monsoonbyte authored post_drone_parts about event_black_kite. Which user behind that alias is directly connected to the Kestrel collaborator?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "post_drone_parts", + "rel": "authored_post", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + } + ], + "task_id": "seed_task_2", + "task_type": "fixed_trace" + }, + { + "answer": "user_faris", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_04", + "support_nodes": 6 + }, + "question": "alias_nightrelay references loc_rivergate. Which user behind it works at an org operating there and collaborates on event_project_lantern?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "post_sat_phone_ping", + "rel": "authored_post", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "references", + "src": "post_sat_phone_ping" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_tidewatch_ops" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_faris" + } + ], + "task_id": "seed_task_3", + "task_type": "fixed_trace" + }, + { + "answer": "user_ivy", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_05", + "support_nodes": 6 + }, + "question": "thr_port_audit discusses Black Kite and references Kestrel Works. Which alias_orchidfox user authored post_midnight_manifest and collaborates on Black Kite?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "discusses", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "references", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + } + ], + "task_id": "seed_task_4", + "task_type": "fixed_trace" + }, + { + "answer": "user_kian", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_06", + "support_nodes": 5 + }, + "question": "Which Atlas Freight user behind alias_lanternmoth authored post_quay_ledgers and collaborates on event_glass_harbor?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + } + ], + "task_id": "seed_task_5", + "task_type": "fixed_trace" + }, + { + "answer": "user_mika", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_07", + "support_nodes": 5 + }, + "question": "Which Orion Customs user behind alias_basinraven authored post_customs_tag and collaborates on event_iron_wharf?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "post_customs_tag", + "rel": "authored_post", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_mika" + } + ], + "task_id": "seed_task_6", + "task_type": "fixed_trace" + }, + { + "answer": "user_nora", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_08", + "support_nodes": 5 + }, + "question": "Which user behind alias_emberglass posted basin_photo from Foundry Row and investigates Amber Veil?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "post_basin_photo", + "rel": "authored_post", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + } + ], + "task_id": "seed_task_7", + "task_type": "fixed_trace" + }, + { + "answer": "user_soren", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_09", + "support_nodes": 4 + }, + "question": "Which user behind alias_tideshard authored post_hull_signal and collaborates on Ghost Signal?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "post_hull_signal", + "rel": "authored_post", + "src": "alias_tideshard" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + } + ], + "task_id": "seed_task_8", + "task_type": "fixed_trace" + }, + { + "answer": "user_tara", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_10", + "support_nodes": 5 + }, + "question": "Which Harborlight Transit user behind alias_sablekeel authored post_uplink_note and reports on Ghost Signal?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "post_uplink_note", + "rel": "authored_post", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + } + ], + "task_id": "seed_task_9", + "task_type": "fixed_trace" + }, + { + "answer": "org_northbridge_logistics", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_01", + "support_nodes": 17 + }, + "question": "Follow alias_docksparrow through post_shift_roster, Dockyard 17, and the Lantern chain. Return the org node id.", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "post_shift_roster", + "rel": "authored_post", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 0.92, + "dst": "user_faris", + "rel": "connected_to", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "references", + "src": "post_quay_ledgers" + } + ], + "task_id": "seed_task_10", + "task_type": "fixed_trace" + }, + { + "answer": "user_kian", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_02", + "support_nodes": 17 + }, + "question": "Across the Glass Harbor cluster, which user behind alias_lanternmoth links to the Atlas Freight network from thr_quiet_manifest?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "thr_quiet_manifest", + "rel": "authored_thread", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "discusses", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "references", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "monitors", + "src": "user_priya" + }, + { + "confidence": 0.93, + "dst": "user_omar", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "user_mika", + "rel": "connected_to", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "post_customs_tag", + "rel": "authored_post", + "src": "alias_basinraven" + } + ], + "task_id": "seed_task_11", + "task_type": "fixed_trace" + }, + { + "answer": "user_mika", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_03", + "support_nodes": 17 + }, + "question": "Trace alias_basinraven through post_customs_tag, thr_customs_breach, and the Orion Customs collaboration chain. Who is it?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "post_customs_tag", + "rel": "authored_post", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "thr_customs_breach", + "rel": "authored_thread", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "discusses", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "references", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_mika" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_quinn" + }, + { + "confidence": 0.89, + "dst": "user_quinn", + "rel": "connected_to", + "src": "user_mika" + }, + { + "confidence": 0.88, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 0.77, + "dst": "org_emberline_security", + "rel": "connected_to", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + } + ], + "task_id": "seed_task_12", + "task_type": "fixed_trace" + }, + { + "answer": "user_rhea", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_04", + "support_nodes": 18 + }, + "question": "In the Ember Tide and Amber Veil overlap, which Foundry Row user behind alias_cinderveil collaborates on Ember Tide?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "post_foundry_map", + "rel": "authored_post", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "thr_foundry_watch", + "rel": "authored_thread", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "references", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "thr_ember_tide_watch", + "rel": "authored_thread", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_ember_tide", + "rel": "collaborates_on", + "src": "user_rhea" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + } + ], + "task_id": "seed_task_13", + "task_type": "fixed_trace" + }, + { + "answer": "org_harborlight_transit", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_05", + "support_nodes": 17 + }, + "question": "Follow alias_tideshard from post_hull_signal into thr_uplink_route and the Harborlight relay. Return the org node id.", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "post_hull_signal", + "rel": "authored_post", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "thr_uplink_route", + "rel": "authored_thread", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + }, + { + "confidence": 0.86, + "dst": "user_soren", + "rel": "connected_to", + "src": "user_rhea" + }, + { + "confidence": 0.86, + "dst": "user_tara", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "operates_in", + "src": "org_harborlight_transit" + }, + { + "confidence": 0.77, + "dst": "org_tidewatch_ops", + "rel": "connected_to", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + } + ], + "task_id": "seed_task_14", + "task_type": "fixed_trace" + }, + { + "answer": "user_leena", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_06", + "support_nodes": 17 + }, + "question": "Which Sunmesh user behind alias_frostledger connects post_lantern_route to thr_relay_map and the Sector 9 monitoring chain?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "post_lantern_route", + "rel": "authored_post", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "thr_relay_map", + "rel": "authored_thread", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_sunmesh_analytics" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "monitors", + "src": "user_leena" + }, + { + "confidence": 0.91, + "dst": "user_leena", + "rel": "connected_to", + "src": "user_priya" + }, + { + "confidence": 0.83, + "dst": "user_aria", + "rel": "connected_to", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + } + ], + "task_id": "seed_task_15", + "task_type": "fixed_trace" + }, + { + "answer": "user_nora", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_07", + "support_nodes": 18 + }, + "question": "Which user behind alias_emberglass is tied to Amber Veil after combining post_basin_photo, thr_basin_shift, and the Foundry Row investigation chain?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "post_basin_photo", + "rel": "authored_post", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "thr_basin_shift", + "rel": "authored_thread", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "discusses", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 0.88, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.77, + "dst": "org_emberline_security", + "rel": "connected_to", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + } + ], + "task_id": "seed_task_16", + "task_type": "fixed_trace" + }, + { + "answer": "user_ivy", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_08", + "support_nodes": 17 + }, + "question": "Combine alias_orchidfox, post_midnight_manifest, thr_supply_leak, and the Lantern to Glass Harbor bridge. Which user starts that chain?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.77, + "dst": "event_glass_harbor", + "rel": "connected_to", + "src": "event_project_lantern" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "monitors", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + } + ], + "task_id": "seed_task_17", + "task_type": "fixed_trace" + }, + { + "answer": "user_diya", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_09", + "support_nodes": 18 + }, + "question": "Which user behind alias_monsoonbyte sits at the overlap of Blueharbor Media, Project Lantern, Black Kite, and the Ivy connection chain?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "post_drone_parts", + "rel": "authored_post", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "thr_port_audit", + "rel": "authored_thread", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "discusses", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_jules" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "investigates", + "src": "user_diya" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "reports_on", + "src": "user_jules" + }, + { + "confidence": 0.9, + "dst": "user_diya", + "rel": "connected_to", + "src": "user_faris" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + } + ], + "task_id": "seed_task_18", + "task_type": "fixed_trace" + }, + { + "answer": "user_bharat", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_10", + "support_nodes": 17 + }, + "question": "Who is the Northbridge user behind alias_steelquill when combining post_relay_schedule, thr_supply_leak, Dockyard 17, and Lantern collaborator edges?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "post_relay_schedule", + "rel": "authored_post", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + } + ], + "task_id": "seed_task_19", + "task_type": "fixed_trace" + }, + { + "answer": "user_ivy", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_01", + "support_nodes": 50 + }, + "question": "Lantern to Glass Harbor handoff: identify the user behind alias_orchidfox after combining Lantern logistics, Dockyard links, and Atlas Freight bridge evidence.", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "operates_in", + "src": "org_northbridge_logistics" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_kestrel_works" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "operates_in", + "src": "org_atlas_freight" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 0.92, + "dst": "user_faris", + "rel": "connected_to", + "src": "user_hiro" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.93, + "dst": "user_omar", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "user_mika", + "rel": "connected_to", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_faris" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "monitors", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "thr_quiet_manifest", + "rel": "authored_thread", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "discusses", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "references", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 0.77, + "dst": "event_glass_harbor", + "rel": "connected_to", + "src": "event_project_lantern" + }, + { + "confidence": 0.77, + "dst": "org_northbridge_logistics", + "rel": "connected_to", + "src": "org_atlas_freight" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + } + ], + "task_id": "seed_task_20", + "task_type": "fixed_trace" + }, + { + "answer": "user_mika", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_02", + "support_nodes": 50 + }, + "question": "North Basin to Foundry Row escalation: which user behind alias_basinraven anchors the Iron Wharf side before the Emberline handoff?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "post_customs_tag", + "rel": "authored_post", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "thr_customs_breach", + "rel": "authored_thread", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "discusses", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "references", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "thr_basin_shift", + "rel": "authored_thread", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "discusses", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "thr_foundry_watch", + "rel": "authored_thread", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "references", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "operates_in", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "operates_in", + "src": "org_emberline_security" + }, + { + "confidence": 0.89, + "dst": "user_quinn", + "rel": "connected_to", + "src": "user_mika" + }, + { + "confidence": 0.88, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_mika" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_quinn" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_ember_tide", + "rel": "collaborates_on", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "post_basin_photo", + "rel": "authored_post", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "post_foundry_map", + "rel": "authored_post", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 0.77, + "dst": "event_amber_veil", + "rel": "connected_to", + "src": "event_black_kite" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 0.77, + "dst": "org_emberline_security", + "rel": "connected_to", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 0.82, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_priya" + }, + { + "confidence": 0.8, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + } + ], + "task_id": "seed_task_21", + "task_type": "fixed_trace" + }, + { + "answer": "user_soren", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_03", + "support_nodes": 50 + }, + "question": "Harborlight ghost-signal relay: identify the user behind alias_tideshard at the Harborlight / Tidewatch junction.", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "post_hull_signal", + "rel": "authored_post", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "post_uplink_note", + "rel": "authored_post", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "thr_uplink_route", + "rel": "authored_thread", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "thr_ghost_signal_net", + "rel": "authored_thread", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "operates_in", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_tidewatch_ops" + }, + { + "confidence": 0.86, + "dst": "user_soren", + "rel": "connected_to", + "src": "user_rhea" + }, + { + "confidence": 0.86, + "dst": "user_tara", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.84, + "dst": "user_kian", + "rel": "connected_to", + "src": "user_tara" + }, + { + "confidence": 0.79, + "dst": "user_faris", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_faris" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "post_sat_phone_ping", + "rel": "authored_post", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "references", + "src": "post_sat_phone_ping" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_sat_phone_ping" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 0.77, + "dst": "org_tidewatch_ops", + "rel": "connected_to", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + } + ], + "task_id": "seed_task_22", + "task_type": "fixed_trace" + }, + { + "answer": "user_diya", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_04", + "support_nodes": 50 + }, + "question": "Blueharbor to Black Kite to Lantern overlap: which user is the Blueharbor origin behind alias_monsoonbyte?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "post_drone_parts", + "rel": "authored_post", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "thr_port_audit", + "rel": "authored_thread", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "discusses", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "references", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "operates_in", + "src": "org_blueharbor_media" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_kestrel_works" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "operates_in", + "src": "org_apex_dynamics" + }, + { + "confidence": 0.9, + "dst": "user_diya", + "rel": "connected_to", + "src": "user_faris" + }, + { + "confidence": 0.83, + "dst": "user_gita", + "rel": "connected_to", + "src": "user_cyrus" + }, + { + "confidence": 0.82, + "dst": "user_jules", + "rel": "connected_to", + "src": "user_gita" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "investigates", + "src": "user_diya" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_cyrus" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "investigates", + "src": "user_elin" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "reports_on", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 0.77, + "dst": "event_glass_harbor", + "rel": "connected_to", + "src": "event_project_lantern" + }, + { + "confidence": 0.77, + "dst": "event_amber_veil", + "rel": "connected_to", + "src": "event_black_kite" + }, + { + "confidence": 1.0, + "dst": "thr_relay_map", + "rel": "authored_thread", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_leena" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "monitors", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + } + ], + "task_id": "seed_task_23", + "task_type": "fixed_trace" + }, + { + "answer": "user_bharat", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_05", + "support_nodes": 50 + }, + "question": "Sector 9 to Dockyard 17 full relay: which user behind alias_steelquill links the Northbridge chain and the Sunmesh monitoring bridge?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "post_relay_schedule", + "rel": "authored_post", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "post_lantern_route", + "rel": "authored_post", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "thr_relay_map", + "rel": "authored_thread", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "operates_in", + "src": "org_northbridge_logistics" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_sunmesh_analytics" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_helios_labs" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 0.89, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_diya" + }, + { + "confidence": 0.87, + "dst": "user_aria", + "rel": "connected_to", + "src": "user_elin" + }, + { + "confidence": 0.84, + "dst": "user_cyrus", + "rel": "connected_to", + "src": "user_aria" + }, + { + "confidence": 0.91, + "dst": "user_leena", + "rel": "connected_to", + "src": "user_priya" + }, + { + "confidence": 0.83, + "dst": "user_aria", + "rel": "connected_to", + "src": "user_leena" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "monitors", + "src": "user_leena" + }, + { + "confidence": 0.77, + "dst": "event_glass_harbor", + "rel": "connected_to", + "src": "event_project_lantern" + }, + { + "confidence": 0.77, + "dst": "org_northbridge_logistics", + "rel": "connected_to", + "src": "org_atlas_freight" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_faris" + } + ], + "task_id": "seed_task_24", + "task_type": "fixed_trace" + }, + { + "answer": "user_nora", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_06", + "support_nodes": 50 + }, + "question": "Foundry Row, North Basin, and Uplink Yard spread: identify the user behind alias_emberglass before the Harborlight relay takes over.", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "post_basin_photo", + "rel": "authored_post", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "post_foundry_map", + "rel": "authored_post", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "post_uplink_note", + "rel": "authored_post", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "thr_foundry_watch", + "rel": "authored_thread", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "references", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "thr_ember_tide_watch", + "rel": "authored_thread", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "thr_uplink_route", + "rel": "authored_thread", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "operates_in", + "src": "org_emberline_security" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "operates_in", + "src": "org_harborlight_transit" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.86, + "dst": "user_soren", + "rel": "connected_to", + "src": "user_rhea" + }, + { + "confidence": 0.86, + "dst": "user_tara", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_ember_tide", + "rel": "collaborates_on", + "src": "user_rhea" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 0.77, + "dst": "org_tidewatch_ops", + "rel": "connected_to", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + } + ], + "task_id": "seed_task_25", + "task_type": "fixed_trace" + }, + { + "answer": "user_kian", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_07", + "support_nodes": 50 + }, + "question": "Freight and customs bridge: which Atlas Freight user behind alias_lanternmoth connects Glass Harbor with the Northbridge chain?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "thr_quiet_manifest", + "rel": "authored_thread", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "discusses", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "references", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "operates_in", + "src": "org_atlas_freight" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "operates_in", + "src": "org_northbridge_logistics" + }, + { + "confidence": 0.93, + "dst": "user_omar", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "user_mika", + "rel": "connected_to", + "src": "user_omar" + }, + { + "confidence": 0.8, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "monitors", + "src": "user_priya" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "post_shift_roster", + "rel": "authored_post", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 0.77, + "dst": "org_northbridge_logistics", + "rel": "connected_to", + "src": "org_atlas_freight" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + } + ], + "task_id": "seed_task_26", + "task_type": "fixed_trace" + }, + { + "answer": "user_cyrus", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_08", + "support_nodes": 50 + }, + "question": "Black Kite, Amber Veil, and Iron Wharf overlap: which user behind alias_quartzlotus is the Apex-side collaborator?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "operates_in", + "src": "org_apex_dynamics" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "thr_port_audit", + "rel": "authored_thread", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "discusses", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "post_drone_parts", + "rel": "authored_post", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "thr_basin_shift", + "rel": "authored_thread", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "discusses", + "src": "thr_basin_shift" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "thr_customs_breach", + "rel": "authored_thread", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "discusses", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_kestrel_works" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "operates_in", + "src": "org_emberline_security" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "operates_in", + "src": "org_orion_customs" + }, + { + "confidence": 0.83, + "dst": "user_gita", + "rel": "connected_to", + "src": "user_cyrus" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.89, + "dst": "user_quinn", + "rel": "connected_to", + "src": "user_mika" + }, + { + "confidence": 0.88, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.77, + "dst": "event_amber_veil", + "rel": "connected_to", + "src": "event_black_kite" + }, + { + "confidence": 0.77, + "dst": "org_emberline_security", + "rel": "connected_to", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + } + ], + "task_id": "seed_task_27", + "task_type": "fixed_trace" + }, + { + "answer": "user_tara", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_09", + "support_nodes": 50 + }, + "question": "Ghost Signal and Ember Tide relay: which user behind alias_sablekeel is the Harborlight reporting endpoint?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "post_uplink_note", + "rel": "authored_post", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "post_hull_signal", + "rel": "authored_post", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "thr_ghost_signal_net", + "rel": "authored_thread", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 1.0, + "dst": "thr_uplink_route", + "rel": "authored_thread", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "thr_ember_tide_watch", + "rel": "authored_thread", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "operates_in", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "operates_in", + "src": "org_emberline_security" + }, + { + "confidence": 0.86, + "dst": "user_soren", + "rel": "connected_to", + "src": "user_rhea" + }, + { + "confidence": 0.86, + "dst": "user_tara", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_ember_tide", + "rel": "collaborates_on", + "src": "user_rhea" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 0.77, + "dst": "org_tidewatch_ops", + "rel": "connected_to", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + } + ], + "task_id": "seed_task_28", + "task_type": "fixed_trace" + }, + { + "answer": "user_priya", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_10", + "support_nodes": 55 + }, + "question": "End-to-end benchmark sweep: across Lantern, Black Kite, Glass Harbor, Iron Wharf, Ember Tide, and Ghost Signal, which user behind alias_hollowsignal anchors the Sunmesh monitoring side?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_gita" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_gita" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_helios_labs" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "operates_in", + "src": "org_northbridge_logistics" + } + ], + "task_id": "seed_task_29", + "task_type": "fixed_trace" + } + ] +} \ No newline at end of file diff --git a/datasets/fixed_levels/fixed_graph_questions.json b/datasets/fixed_levels/fixed_graph_questions.json new file mode 100644 index 0000000000000000000000000000000000000000..2fdf99e415462e6256c65214e432b9ba064c3840 --- /dev/null +++ b/datasets/fixed_levels/fixed_graph_questions.json @@ -0,0 +1,7009 @@ +{ + "dataset_name": "fixed_levels_submission_set", + "difficulty_counts": { + "easy": 10, + "high": 10, + "mid": 10 + }, + "graph": { + "edge_count": 185, + "edges": [ + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_gita" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_gita" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_helios_labs" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "operates_in", + "src": "org_northbridge_logistics" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "operates_in", + "src": "org_apex_dynamics" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "operates_in", + "src": "org_blueharbor_media" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_tidewatch_ops" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_kestrel_works" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "operates_in", + "src": "org_atlas_freight" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_sunmesh_analytics" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "operates_in", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "operates_in", + "src": "org_emberline_security" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "operates_in", + "src": "org_harborlight_transit" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 0.92, + "dst": "user_faris", + "rel": "connected_to", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "user_diya", + "rel": "connected_to", + "src": "user_faris" + }, + { + "confidence": 0.89, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_diya" + }, + { + "confidence": 0.87, + "dst": "user_aria", + "rel": "connected_to", + "src": "user_elin" + }, + { + "confidence": 0.84, + "dst": "user_cyrus", + "rel": "connected_to", + "src": "user_aria" + }, + { + "confidence": 0.83, + "dst": "user_gita", + "rel": "connected_to", + "src": "user_cyrus" + }, + { + "confidence": 0.82, + "dst": "user_jules", + "rel": "connected_to", + "src": "user_gita" + }, + { + "confidence": 0.81, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_jules" + }, + { + "confidence": 0.9, + "dst": "user_ivy", + "rel": "connected_to", + "src": "user_diya" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.93, + "dst": "user_omar", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "user_mika", + "rel": "connected_to", + "src": "user_omar" + }, + { + "confidence": 0.89, + "dst": "user_quinn", + "rel": "connected_to", + "src": "user_mika" + }, + { + "confidence": 0.88, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.86, + "dst": "user_soren", + "rel": "connected_to", + "src": "user_rhea" + }, + { + "confidence": 0.86, + "dst": "user_tara", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.84, + "dst": "user_kian", + "rel": "connected_to", + "src": "user_tara" + }, + { + "confidence": 0.91, + "dst": "user_leena", + "rel": "connected_to", + "src": "user_priya" + }, + { + "confidence": 0.83, + "dst": "user_aria", + "rel": "connected_to", + "src": "user_leena" + }, + { + "confidence": 0.82, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_priya" + }, + { + "confidence": 0.8, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.79, + "dst": "user_faris", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.78, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_shift_roster", + "rel": "authored_post", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "post_sat_phone_ping", + "rel": "authored_post", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "post_drone_parts", + "rel": "authored_post", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "post_relay_schedule", + "rel": "authored_post", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_customs_tag", + "rel": "authored_post", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "post_hull_signal", + "rel": "authored_post", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "post_basin_photo", + "rel": "authored_post", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "post_foundry_map", + "rel": "authored_post", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "post_lantern_route", + "rel": "authored_post", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "post_uplink_note", + "rel": "authored_post", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "references", + "src": "post_sat_phone_ping" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_sat_phone_ping" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "thr_port_audit", + "rel": "authored_thread", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "thr_customs_breach", + "rel": "authored_thread", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "thr_relay_map", + "rel": "authored_thread", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "thr_foundry_watch", + "rel": "authored_thread", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "thr_basin_shift", + "rel": "authored_thread", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "thr_quiet_manifest", + "rel": "authored_thread", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "thr_uplink_route", + "rel": "authored_thread", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "thr_ember_tide_watch", + "rel": "authored_thread", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "thr_ghost_signal_net", + "rel": "authored_thread", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "discusses", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "references", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "discusses", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "references", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "references", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "discusses", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "discusses", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "references", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_faris" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "investigates", + "src": "user_diya" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "monitors", + "src": "user_leena" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_cyrus" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "investigates", + "src": "user_elin" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "reports_on", + "src": "user_jules" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "monitors", + "src": "user_priya" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_mika" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_quinn" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_ember_tide", + "rel": "collaborates_on", + "src": "user_rhea" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + }, + { + "confidence": 0.9, + "dst": "event_silent_current", + "rel": "monitors", + "src": "user_gita" + }, + { + "confidence": 0.9, + "dst": "event_silent_current", + "rel": "reports_on", + "src": "user_jules" + }, + { + "confidence": 0.77, + "dst": "event_glass_harbor", + "rel": "connected_to", + "src": "event_project_lantern" + }, + { + "confidence": 0.77, + "dst": "event_amber_veil", + "rel": "connected_to", + "src": "event_black_kite" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 0.77, + "dst": "org_northbridge_logistics", + "rel": "connected_to", + "src": "org_atlas_freight" + }, + { + "confidence": 0.77, + "dst": "org_emberline_security", + "rel": "connected_to", + "src": "org_orion_customs" + }, + { + "confidence": 0.77, + "dst": "org_tidewatch_ops", + "rel": "connected_to", + "src": "org_harborlight_transit" + } + ], + "node_count": 85, + "nodes": [ + { + "attrs": { + "location": "Sector 9", + "name": "Aria Sen", + "org": "Helios Labs" + }, + "node_id": "user_aria", + "node_type": "user" + }, + { + "attrs": { + "location": "Dockyard 17", + "name": "Bharat Kulkarni", + "org": "Northbridge Logistics" + }, + "node_id": "user_bharat", + "node_type": "user" + }, + { + "attrs": { + "location": "Old Town", + "name": "Cyrus Mehta", + "org": "Apex Dynamics" + }, + "node_id": "user_cyrus", + "node_type": "user" + }, + { + "attrs": { + "location": "Old Town", + "name": "Diya Roy", + "org": "Blueharbor Media" + }, + "node_id": "user_diya", + "node_type": "user" + }, + { + "attrs": { + "location": "Sector 9", + "name": "Elin Das", + "org": "Helios Labs" + }, + "node_id": "user_elin", + "node_type": "user" + }, + { + "attrs": { + "location": "Rivergate", + "name": "Faris Noor", + "org": "Tidewatch Ops" + }, + "node_id": "user_faris", + "node_type": "user" + }, + { + "attrs": { + "location": "Old Town", + "name": "Gita Pradhan", + "org": "Apex Dynamics" + }, + "node_id": "user_gita", + "node_type": "user" + }, + { + "attrs": { + "location": "Dockyard 17", + "name": "Hiro Tan", + "org": "Northbridge Logistics" + }, + "node_id": "user_hiro", + "node_type": "user" + }, + { + "attrs": { + "location": "Rivergate", + "name": "Ivy Kapoor", + "org": "Kestrel Works" + }, + "node_id": "user_ivy", + "node_type": "user" + }, + { + "attrs": { + "location": "Old Town", + "name": "Jules Banerjee", + "org": "Blueharbor Media" + }, + "node_id": "user_jules", + "node_type": "user" + }, + { + "attrs": { + "location": "East Quay", + "name": "Kian Bose", + "org": "Atlas Freight" + }, + "node_id": "user_kian", + "node_type": "user" + }, + { + "attrs": { + "location": "Sector 9", + "name": "Leena Das", + "org": "Sunmesh Analytics" + }, + "node_id": "user_leena", + "node_type": "user" + }, + { + "attrs": { + "location": "North Basin", + "name": "Mika Solanki", + "org": "Orion Customs" + }, + "node_id": "user_mika", + "node_type": "user" + }, + { + "attrs": { + "location": "Foundry Row", + "name": "Nora Iqbal", + "org": "Emberline Security" + }, + "node_id": "user_nora", + "node_type": "user" + }, + { + "attrs": { + "location": "East Quay", + "name": "Omar Sheikh", + "org": "Atlas Freight" + }, + "node_id": "user_omar", + "node_type": "user" + }, + { + "attrs": { + "location": "Sector 9", + "name": "Priya Menon", + "org": "Sunmesh Analytics" + }, + "node_id": "user_priya", + "node_type": "user" + }, + { + "attrs": { + "location": "North Basin", + "name": "Quinn Rao", + "org": "Orion Customs" + }, + "node_id": "user_quinn", + "node_type": "user" + }, + { + "attrs": { + "location": "Foundry Row", + "name": "Rhea Kapoor", + "org": "Emberline Security" + }, + "node_id": "user_rhea", + "node_type": "user" + }, + { + "attrs": { + "location": "Uplink Yard", + "name": "Soren Malik", + "org": "Harborlight Transit" + }, + "node_id": "user_soren", + "node_type": "user" + }, + { + "attrs": { + "location": "Uplink Yard", + "name": "Tara Dey", + "org": "Harborlight Transit" + }, + "node_id": "user_tara", + "node_type": "user" + }, + { + "attrs": { + "handle": "@orchidfox" + }, + "node_id": "alias_orchidfox", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@steelquill" + }, + "node_id": "alias_steelquill", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@monsoonbyte" + }, + "node_id": "alias_monsoonbyte", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@nightrelay" + }, + "node_id": "alias_nightrelay", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@mapleghost" + }, + "node_id": "alias_mapleghost", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@docksparrow" + }, + "node_id": "alias_docksparrow", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@quartzlotus" + }, + "node_id": "alias_quartzlotus", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@emberglass" + }, + "node_id": "alias_emberglass", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@basinraven" + }, + "node_id": "alias_basinraven", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@tideshard" + }, + "node_id": "alias_tideshard", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@hollowsignal" + }, + "node_id": "alias_hollowsignal", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@ironwhisper" + }, + "node_id": "alias_ironwhisper", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@cinderveil" + }, + "node_id": "alias_cinderveil", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@sablekeel" + }, + "node_id": "alias_sablekeel", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@lanternmoth" + }, + "node_id": "alias_lanternmoth", + "node_type": "alias" + }, + { + "attrs": { + "handle": "@frostledger" + }, + "node_id": "alias_frostledger", + "node_type": "alias" + }, + { + "attrs": { + "name": "Helios Labs" + }, + "node_id": "org_helios_labs", + "node_type": "org" + }, + { + "attrs": { + "name": "Northbridge Logistics" + }, + "node_id": "org_northbridge_logistics", + "node_type": "org" + }, + { + "attrs": { + "name": "Apex Dynamics" + }, + "node_id": "org_apex_dynamics", + "node_type": "org" + }, + { + "attrs": { + "name": "Blueharbor Media" + }, + "node_id": "org_blueharbor_media", + "node_type": "org" + }, + { + "attrs": { + "name": "Tidewatch Ops" + }, + "node_id": "org_tidewatch_ops", + "node_type": "org" + }, + { + "attrs": { + "name": "Kestrel Works" + }, + "node_id": "org_kestrel_works", + "node_type": "org" + }, + { + "attrs": { + "name": "Atlas Freight" + }, + "node_id": "org_atlas_freight", + "node_type": "org" + }, + { + "attrs": { + "name": "Sunmesh Analytics" + }, + "node_id": "org_sunmesh_analytics", + "node_type": "org" + }, + { + "attrs": { + "name": "Orion Customs" + }, + "node_id": "org_orion_customs", + "node_type": "org" + }, + { + "attrs": { + "name": "Emberline Security" + }, + "node_id": "org_emberline_security", + "node_type": "org" + }, + { + "attrs": { + "name": "Harborlight Transit" + }, + "node_id": "org_harborlight_transit", + "node_type": "org" + }, + { + "attrs": { + "name": "Dockyard 17" + }, + "node_id": "loc_dockyard17", + "node_type": "location" + }, + { + "attrs": { + "name": "Sector 9" + }, + "node_id": "loc_sector9", + "node_type": "location" + }, + { + "attrs": { + "name": "Old Town" + }, + "node_id": "loc_old_town", + "node_type": "location" + }, + { + "attrs": { + "name": "Rivergate" + }, + "node_id": "loc_rivergate", + "node_type": "location" + }, + { + "attrs": { + "name": "East Quay" + }, + "node_id": "loc_east_quay", + "node_type": "location" + }, + { + "attrs": { + "name": "Foundry Row" + }, + "node_id": "loc_foundry_row", + "node_type": "location" + }, + { + "attrs": { + "name": "North Basin" + }, + "node_id": "loc_north_basin", + "node_type": "location" + }, + { + "attrs": { + "name": "Uplink Yard" + }, + "node_id": "loc_uplink_yard", + "node_type": "location" + }, + { + "attrs": { + "name": "Project Lantern" + }, + "node_id": "event_project_lantern", + "node_type": "event" + }, + { + "attrs": { + "name": "Black Kite" + }, + "node_id": "event_black_kite", + "node_type": "event" + }, + { + "attrs": { + "name": "Silent Current" + }, + "node_id": "event_silent_current", + "node_type": "event" + }, + { + "attrs": { + "name": "Amber Veil" + }, + "node_id": "event_amber_veil", + "node_type": "event" + }, + { + "attrs": { + "name": "Glass Harbor" + }, + "node_id": "event_glass_harbor", + "node_type": "event" + }, + { + "attrs": { + "name": "Ember Tide" + }, + "node_id": "event_ember_tide", + "node_type": "event" + }, + { + "attrs": { + "name": "Iron Wharf" + }, + "node_id": "event_iron_wharf", + "node_type": "event" + }, + { + "attrs": { + "name": "Ghost Signal" + }, + "node_id": "event_ghost_signal", + "node_type": "event" + }, + { + "attrs": { + "topic": "supply_chain" + }, + "node_id": "thr_supply_leak", + "node_type": "thread" + }, + { + "attrs": { + "topic": "port_audit" + }, + "node_id": "thr_port_audit", + "node_type": "thread" + }, + { + "attrs": { + "topic": "customs_breach" + }, + "node_id": "thr_customs_breach", + "node_type": "thread" + }, + { + "attrs": { + "topic": "relay_map" + }, + "node_id": "thr_relay_map", + "node_type": "thread" + }, + { + "attrs": { + "topic": "foundry_watch" + }, + "node_id": "thr_foundry_watch", + "node_type": "thread" + }, + { + "attrs": { + "topic": "basin_shift" + }, + "node_id": "thr_basin_shift", + "node_type": "thread" + }, + { + "attrs": { + "topic": "quiet_manifest" + }, + "node_id": "thr_quiet_manifest", + "node_type": "thread" + }, + { + "attrs": { + "topic": "uplink_route" + }, + "node_id": "thr_uplink_route", + "node_type": "thread" + }, + { + "attrs": { + "topic": "ember_tide" + }, + "node_id": "thr_ember_tide_watch", + "node_type": "thread" + }, + { + "attrs": { + "topic": "ghost_signal" + }, + "node_id": "thr_ghost_signal_net", + "node_type": "thread" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_shift_roster", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_midnight_manifest", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_sat_phone_ping", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_drone_parts", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_relay_schedule", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_quay_ledgers", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_customs_tag", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_hull_signal", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_basin_photo", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_foundry_map", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_lantern_route", + "node_type": "post" + }, + { + "attrs": { + "channel": "microblog" + }, + "node_id": "post_uplink_note", + "node_type": "post" + } + ] + }, + "question_count": 30, + "questions": [ + { + "answer": "user_bharat", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_01", + "support_nodes": 6 + }, + "question": "alias_orchidfox -> post_midnight_manifest -> loc_dockyard17 -> connected collaborator on event_project_lantern. Who is it?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + } + ], + "task_id": "fixed_task_00", + "task_type": "fixed_trace" + }, + { + "answer": "user_hiro", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_02", + "support_nodes": 5 + }, + "question": "thr_supply_leak references org_northbridge_logistics. Which alias_docksparrow user works there and collaborates on event_project_lantern?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + } + ], + "task_id": "fixed_task_01", + "task_type": "fixed_trace" + }, + { + "answer": "user_diya", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_03", + "support_nodes": 7 + }, + "question": "alias_monsoonbyte authored post_drone_parts about event_black_kite. Which user behind that alias is directly connected to the Kestrel collaborator?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "post_drone_parts", + "rel": "authored_post", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + } + ], + "task_id": "fixed_task_02", + "task_type": "fixed_trace" + }, + { + "answer": "user_faris", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_04", + "support_nodes": 6 + }, + "question": "alias_nightrelay references loc_rivergate. Which user behind it works at an org operating there and collaborates on event_project_lantern?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "post_sat_phone_ping", + "rel": "authored_post", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "references", + "src": "post_sat_phone_ping" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_tidewatch_ops" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_faris" + } + ], + "task_id": "fixed_task_03", + "task_type": "fixed_trace" + }, + { + "answer": "user_ivy", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_05", + "support_nodes": 6 + }, + "question": "thr_port_audit discusses Black Kite and references Kestrel Works. Which alias_orchidfox user authored post_midnight_manifest and collaborates on Black Kite?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "discusses", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "references", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + } + ], + "task_id": "fixed_task_04", + "task_type": "fixed_trace" + }, + { + "answer": "user_kian", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_06", + "support_nodes": 5 + }, + "question": "Which Atlas Freight user behind alias_lanternmoth authored post_quay_ledgers and collaborates on event_glass_harbor?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + } + ], + "task_id": "fixed_task_05", + "task_type": "fixed_trace" + }, + { + "answer": "user_mika", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_07", + "support_nodes": 5 + }, + "question": "Which Orion Customs user behind alias_basinraven authored post_customs_tag and collaborates on event_iron_wharf?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "post_customs_tag", + "rel": "authored_post", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_mika" + } + ], + "task_id": "fixed_task_06", + "task_type": "fixed_trace" + }, + { + "answer": "user_nora", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_08", + "support_nodes": 5 + }, + "question": "Which user behind alias_emberglass posted basin_photo from Foundry Row and investigates Amber Veil?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "post_basin_photo", + "rel": "authored_post", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + } + ], + "task_id": "fixed_task_07", + "task_type": "fixed_trace" + }, + { + "answer": "user_soren", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_09", + "support_nodes": 4 + }, + "question": "Which user behind alias_tideshard authored post_hull_signal and collaborates on Ghost Signal?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "post_hull_signal", + "rel": "authored_post", + "src": "alias_tideshard" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + } + ], + "task_id": "fixed_task_08", + "task_type": "fixed_trace" + }, + { + "answer": "user_tara", + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_10", + "support_nodes": 5 + }, + "question": "Which Harborlight Transit user behind alias_sablekeel authored post_uplink_note and reports on Ghost Signal?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "post_uplink_note", + "rel": "authored_post", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + } + ], + "task_id": "fixed_task_09", + "task_type": "fixed_trace" + }, + { + "answer": "org_northbridge_logistics", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_01", + "support_nodes": 17 + }, + "question": "Follow alias_docksparrow through post_shift_roster, Dockyard 17, and the Lantern chain. Return the org node id.", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "post_shift_roster", + "rel": "authored_post", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 0.92, + "dst": "user_faris", + "rel": "connected_to", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "references", + "src": "post_quay_ledgers" + } + ], + "task_id": "fixed_task_10", + "task_type": "fixed_trace" + }, + { + "answer": "user_kian", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_02", + "support_nodes": 17 + }, + "question": "Across the Glass Harbor cluster, which user behind alias_lanternmoth links to the Atlas Freight network from thr_quiet_manifest?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "thr_quiet_manifest", + "rel": "authored_thread", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "discusses", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "references", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "monitors", + "src": "user_priya" + }, + { + "confidence": 0.93, + "dst": "user_omar", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "user_mika", + "rel": "connected_to", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "post_customs_tag", + "rel": "authored_post", + "src": "alias_basinraven" + } + ], + "task_id": "fixed_task_11", + "task_type": "fixed_trace" + }, + { + "answer": "user_mika", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_03", + "support_nodes": 17 + }, + "question": "Trace alias_basinraven through post_customs_tag, thr_customs_breach, and the Orion Customs collaboration chain. Who is it?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "post_customs_tag", + "rel": "authored_post", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "thr_customs_breach", + "rel": "authored_thread", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "discusses", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "references", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_mika" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_quinn" + }, + { + "confidence": 0.89, + "dst": "user_quinn", + "rel": "connected_to", + "src": "user_mika" + }, + { + "confidence": 0.88, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 0.77, + "dst": "org_emberline_security", + "rel": "connected_to", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + } + ], + "task_id": "fixed_task_12", + "task_type": "fixed_trace" + }, + { + "answer": "user_rhea", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_04", + "support_nodes": 18 + }, + "question": "In the Ember Tide and Amber Veil overlap, which Foundry Row user behind alias_cinderveil collaborates on Ember Tide?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "post_foundry_map", + "rel": "authored_post", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "thr_foundry_watch", + "rel": "authored_thread", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "references", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "thr_ember_tide_watch", + "rel": "authored_thread", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_ember_tide", + "rel": "collaborates_on", + "src": "user_rhea" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + } + ], + "task_id": "fixed_task_13", + "task_type": "fixed_trace" + }, + { + "answer": "org_harborlight_transit", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_05", + "support_nodes": 17 + }, + "question": "Follow alias_tideshard from post_hull_signal into thr_uplink_route and the Harborlight relay. Return the org node id.", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "post_hull_signal", + "rel": "authored_post", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "thr_uplink_route", + "rel": "authored_thread", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + }, + { + "confidence": 0.86, + "dst": "user_soren", + "rel": "connected_to", + "src": "user_rhea" + }, + { + "confidence": 0.86, + "dst": "user_tara", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "operates_in", + "src": "org_harborlight_transit" + }, + { + "confidence": 0.77, + "dst": "org_tidewatch_ops", + "rel": "connected_to", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + } + ], + "task_id": "fixed_task_14", + "task_type": "fixed_trace" + }, + { + "answer": "user_leena", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_06", + "support_nodes": 17 + }, + "question": "Which Sunmesh user behind alias_frostledger connects post_lantern_route to thr_relay_map and the Sector 9 monitoring chain?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "post_lantern_route", + "rel": "authored_post", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "thr_relay_map", + "rel": "authored_thread", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_sunmesh_analytics" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "monitors", + "src": "user_leena" + }, + { + "confidence": 0.91, + "dst": "user_leena", + "rel": "connected_to", + "src": "user_priya" + }, + { + "confidence": 0.83, + "dst": "user_aria", + "rel": "connected_to", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + } + ], + "task_id": "fixed_task_15", + "task_type": "fixed_trace" + }, + { + "answer": "user_nora", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_07", + "support_nodes": 18 + }, + "question": "Which user behind alias_emberglass is tied to Amber Veil after combining post_basin_photo, thr_basin_shift, and the Foundry Row investigation chain?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "post_basin_photo", + "rel": "authored_post", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "thr_basin_shift", + "rel": "authored_thread", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "discusses", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 0.88, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.77, + "dst": "org_emberline_security", + "rel": "connected_to", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + } + ], + "task_id": "fixed_task_16", + "task_type": "fixed_trace" + }, + { + "answer": "user_ivy", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_08", + "support_nodes": 17 + }, + "question": "Combine alias_orchidfox, post_midnight_manifest, thr_supply_leak, and the Lantern to Glass Harbor bridge. Which user starts that chain?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.77, + "dst": "event_glass_harbor", + "rel": "connected_to", + "src": "event_project_lantern" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "monitors", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + } + ], + "task_id": "fixed_task_17", + "task_type": "fixed_trace" + }, + { + "answer": "user_diya", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_09", + "support_nodes": 18 + }, + "question": "Which user behind alias_monsoonbyte sits at the overlap of Blueharbor Media, Project Lantern, Black Kite, and the Ivy connection chain?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "post_drone_parts", + "rel": "authored_post", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "thr_port_audit", + "rel": "authored_thread", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "discusses", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_jules" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "investigates", + "src": "user_diya" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "reports_on", + "src": "user_jules" + }, + { + "confidence": 0.9, + "dst": "user_diya", + "rel": "connected_to", + "src": "user_faris" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + } + ], + "task_id": "fixed_task_18", + "task_type": "fixed_trace" + }, + { + "answer": "user_bharat", + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_10", + "support_nodes": 17 + }, + "question": "Who is the Northbridge user behind alias_steelquill when combining post_relay_schedule, thr_supply_leak, Dockyard 17, and Lantern collaborator edges?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "post_relay_schedule", + "rel": "authored_post", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + } + ], + "task_id": "fixed_task_19", + "task_type": "fixed_trace" + }, + { + "answer": "user_ivy", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_01", + "support_nodes": 50 + }, + "question": "Lantern to Glass Harbor handoff: identify the user behind alias_orchidfox after combining Lantern logistics, Dockyard links, and Atlas Freight bridge evidence.", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "operates_in", + "src": "org_northbridge_logistics" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_kestrel_works" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "operates_in", + "src": "org_atlas_freight" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 0.92, + "dst": "user_faris", + "rel": "connected_to", + "src": "user_hiro" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.93, + "dst": "user_omar", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "user_mika", + "rel": "connected_to", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_faris" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "monitors", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "thr_quiet_manifest", + "rel": "authored_thread", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "discusses", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "references", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 0.77, + "dst": "event_glass_harbor", + "rel": "connected_to", + "src": "event_project_lantern" + }, + { + "confidence": 0.77, + "dst": "org_northbridge_logistics", + "rel": "connected_to", + "src": "org_atlas_freight" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + } + ], + "task_id": "fixed_task_20", + "task_type": "fixed_trace" + }, + { + "answer": "user_mika", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_02", + "support_nodes": 50 + }, + "question": "North Basin to Foundry Row escalation: which user behind alias_basinraven anchors the Iron Wharf side before the Emberline handoff?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "post_customs_tag", + "rel": "authored_post", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "references", + "src": "post_customs_tag" + }, + { + "confidence": 1.0, + "dst": "thr_customs_breach", + "rel": "authored_thread", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "discusses", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "references", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "thr_basin_shift", + "rel": "authored_thread", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "discusses", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "references", + "src": "thr_basin_shift" + }, + { + "confidence": 1.0, + "dst": "thr_foundry_watch", + "rel": "authored_thread", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "references", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "operates_in", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "operates_in", + "src": "org_emberline_security" + }, + { + "confidence": 0.89, + "dst": "user_quinn", + "rel": "connected_to", + "src": "user_mika" + }, + { + "confidence": 0.88, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_mika" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_quinn" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_ember_tide", + "rel": "collaborates_on", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "post_basin_photo", + "rel": "authored_post", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "post_foundry_map", + "rel": "authored_post", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 0.77, + "dst": "event_amber_veil", + "rel": "connected_to", + "src": "event_black_kite" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 0.77, + "dst": "org_emberline_security", + "rel": "connected_to", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 0.82, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_priya" + }, + { + "confidence": 0.8, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + } + ], + "task_id": "fixed_task_21", + "task_type": "fixed_trace" + }, + { + "answer": "user_soren", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_03", + "support_nodes": 50 + }, + "question": "Harborlight ghost-signal relay: identify the user behind alias_tideshard at the Harborlight / Tidewatch junction.", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "post_hull_signal", + "rel": "authored_post", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "post_uplink_note", + "rel": "authored_post", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "thr_uplink_route", + "rel": "authored_thread", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "thr_ghost_signal_net", + "rel": "authored_thread", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "operates_in", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_tidewatch_ops" + }, + { + "confidence": 0.86, + "dst": "user_soren", + "rel": "connected_to", + "src": "user_rhea" + }, + { + "confidence": 0.86, + "dst": "user_tara", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.84, + "dst": "user_kian", + "rel": "connected_to", + "src": "user_tara" + }, + { + "confidence": 0.79, + "dst": "user_faris", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_faris" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "post_sat_phone_ping", + "rel": "authored_post", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "references", + "src": "post_sat_phone_ping" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_sat_phone_ping" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 0.77, + "dst": "org_tidewatch_ops", + "rel": "connected_to", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + } + ], + "task_id": "fixed_task_22", + "task_type": "fixed_trace" + }, + { + "answer": "user_diya", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_04", + "support_nodes": 50 + }, + "question": "Blueharbor to Black Kite to Lantern overlap: which user is the Blueharbor origin behind alias_monsoonbyte?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "post_drone_parts", + "rel": "authored_post", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 1.0, + "dst": "thr_port_audit", + "rel": "authored_thread", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "discusses", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "references", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "operates_in", + "src": "org_blueharbor_media" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_kestrel_works" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "operates_in", + "src": "org_apex_dynamics" + }, + { + "confidence": 0.9, + "dst": "user_diya", + "rel": "connected_to", + "src": "user_faris" + }, + { + "confidence": 0.83, + "dst": "user_gita", + "rel": "connected_to", + "src": "user_cyrus" + }, + { + "confidence": 0.82, + "dst": "user_jules", + "rel": "connected_to", + "src": "user_gita" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "investigates", + "src": "user_diya" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_ivy" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_cyrus" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "investigates", + "src": "user_elin" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "reports_on", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 0.77, + "dst": "event_glass_harbor", + "rel": "connected_to", + "src": "event_project_lantern" + }, + { + "confidence": 0.77, + "dst": "event_amber_veil", + "rel": "connected_to", + "src": "event_black_kite" + }, + { + "confidence": 1.0, + "dst": "thr_relay_map", + "rel": "authored_thread", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_leena" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "monitors", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + } + ], + "task_id": "fixed_task_23", + "task_type": "fixed_trace" + }, + { + "answer": "user_bharat", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_05", + "support_nodes": 50 + }, + "question": "Sector 9 to Dockyard 17 full relay: which user behind alias_steelquill links the Northbridge chain and the Sunmesh monitoring bridge?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "post_relay_schedule", + "rel": "authored_post", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "references", + "src": "post_relay_schedule" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "post_lantern_route", + "rel": "authored_post", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "post_lantern_route" + }, + { + "confidence": 1.0, + "dst": "thr_relay_map", + "rel": "authored_thread", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "references", + "src": "thr_relay_map" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "event_project_lantern", + "rel": "discusses", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "operates_in", + "src": "org_northbridge_logistics" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_sunmesh_analytics" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_helios_labs" + }, + { + "confidence": 0.95, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 0.89, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_diya" + }, + { + "confidence": 0.87, + "dst": "user_aria", + "rel": "connected_to", + "src": "user_elin" + }, + { + "confidence": 0.84, + "dst": "user_cyrus", + "rel": "connected_to", + "src": "user_aria" + }, + { + "confidence": 0.91, + "dst": "user_leena", + "rel": "connected_to", + "src": "user_priya" + }, + { + "confidence": 0.83, + "dst": "user_aria", + "rel": "connected_to", + "src": "user_leena" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "monitors", + "src": "user_leena" + }, + { + "confidence": 0.77, + "dst": "event_glass_harbor", + "rel": "connected_to", + "src": "event_project_lantern" + }, + { + "confidence": 0.77, + "dst": "org_northbridge_logistics", + "rel": "connected_to", + "src": "org_atlas_freight" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_faris" + } + ], + "task_id": "fixed_task_24", + "task_type": "fixed_trace" + }, + { + "answer": "user_nora", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_06", + "support_nodes": 50 + }, + "question": "Foundry Row, North Basin, and Uplink Yard spread: identify the user behind alias_emberglass before the Harborlight relay takes over.", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "post_basin_photo", + "rel": "authored_post", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "references", + "src": "post_basin_photo" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "post_foundry_map", + "rel": "authored_post", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "references", + "src": "post_foundry_map" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "post_uplink_note", + "rel": "authored_post", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "thr_foundry_watch", + "rel": "authored_thread", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "references", + "src": "thr_foundry_watch" + }, + { + "confidence": 1.0, + "dst": "thr_ember_tide_watch", + "rel": "authored_thread", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "thr_uplink_route", + "rel": "authored_thread", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "operates_in", + "src": "org_emberline_security" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "operates_in", + "src": "org_harborlight_transit" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.86, + "dst": "user_soren", + "rel": "connected_to", + "src": "user_rhea" + }, + { + "confidence": 0.86, + "dst": "user_tara", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_ember_tide", + "rel": "collaborates_on", + "src": "user_rhea" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 0.77, + "dst": "org_tidewatch_ops", + "rel": "connected_to", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + } + ], + "task_id": "fixed_task_25", + "task_type": "fixed_trace" + }, + { + "answer": "user_kian", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_07", + "support_nodes": 50 + }, + "question": "Freight and customs bridge: which Atlas Freight user behind alias_lanternmoth connects Glass Harbor with the Northbridge chain?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "post_quay_ledgers", + "rel": "authored_post", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "references", + "src": "post_quay_ledgers" + }, + { + "confidence": 1.0, + "dst": "thr_quiet_manifest", + "rel": "authored_thread", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "event_glass_harbor", + "rel": "discusses", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "references", + "src": "thr_quiet_manifest" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "operates_in", + "src": "org_atlas_freight" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "operates_in", + "src": "org_northbridge_logistics" + }, + { + "confidence": 0.93, + "dst": "user_omar", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "user_mika", + "rel": "connected_to", + "src": "user_omar" + }, + { + "confidence": 0.8, + "dst": "user_bharat", + "rel": "connected_to", + "src": "user_kian" + }, + { + "confidence": 0.95, + "dst": "user_hiro", + "rel": "connected_to", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_kian" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "collaborates_on", + "src": "user_omar" + }, + { + "confidence": 0.9, + "dst": "event_glass_harbor", + "rel": "monitors", + "src": "user_priya" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_bharat" + }, + { + "confidence": 0.9, + "dst": "event_project_lantern", + "rel": "collaborates_on", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "post_shift_roster", + "rel": "authored_post", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "post_shift_roster" + }, + { + "confidence": 1.0, + "dst": "post_midnight_manifest", + "rel": "authored_post", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "references", + "src": "post_midnight_manifest" + }, + { + "confidence": 1.0, + "dst": "thr_supply_leak", + "rel": "authored_thread", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "references", + "src": "thr_supply_leak" + }, + { + "confidence": 0.77, + "dst": "org_northbridge_logistics", + "rel": "connected_to", + "src": "org_atlas_freight" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + } + ], + "task_id": "fixed_task_26", + "task_type": "fixed_trace" + }, + { + "answer": "user_cyrus", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_08", + "support_nodes": 50 + }, + "question": "Black Kite, Amber Veil, and Iron Wharf overlap: which user behind alias_quartzlotus is the Apex-side collaborator?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "operates_in", + "src": "org_apex_dynamics" + }, + { + "confidence": 0.9, + "dst": "event_black_kite", + "rel": "collaborates_on", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "thr_port_audit", + "rel": "authored_thread", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "discusses", + "src": "thr_port_audit" + }, + { + "confidence": 1.0, + "dst": "post_drone_parts", + "rel": "authored_post", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "event_black_kite", + "rel": "references", + "src": "post_drone_parts" + }, + { + "confidence": 0.9, + "dst": "event_amber_veil", + "rel": "investigates", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "thr_basin_shift", + "rel": "authored_thread", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "event_amber_veil", + "rel": "discusses", + "src": "thr_basin_shift" + }, + { + "confidence": 0.9, + "dst": "event_iron_wharf", + "rel": "collaborates_on", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "thr_customs_breach", + "rel": "authored_thread", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "event_iron_wharf", + "rel": "discusses", + "src": "thr_customs_breach" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "operates_in", + "src": "org_kestrel_works" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "operates_in", + "src": "org_emberline_security" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "operates_in", + "src": "org_orion_customs" + }, + { + "confidence": 0.83, + "dst": "user_gita", + "rel": "connected_to", + "src": "user_cyrus" + }, + { + "confidence": 0.86, + "dst": "user_elin", + "rel": "connected_to", + "src": "user_ivy" + }, + { + "confidence": 0.89, + "dst": "user_quinn", + "rel": "connected_to", + "src": "user_mika" + }, + { + "confidence": 0.88, + "dst": "user_nora", + "rel": "connected_to", + "src": "user_quinn" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.77, + "dst": "event_amber_veil", + "rel": "connected_to", + "src": "event_black_kite" + }, + { + "confidence": 0.77, + "dst": "org_emberline_security", + "rel": "connected_to", + "src": "org_orion_customs" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + } + ], + "task_id": "fixed_task_27", + "task_type": "fixed_trace" + }, + { + "answer": "user_tara", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_09", + "support_nodes": 50 + }, + "question": "Ghost Signal and Ember Tide relay: which user behind alias_sablekeel is the Harborlight reporting endpoint?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "post_uplink_note", + "rel": "authored_post", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "post_uplink_note" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "post_hull_signal", + "rel": "authored_post", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "references", + "src": "post_hull_signal" + }, + { + "confidence": 1.0, + "dst": "thr_ghost_signal_net", + "rel": "authored_thread", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "references", + "src": "thr_ghost_signal_net" + }, + { + "confidence": 1.0, + "dst": "thr_uplink_route", + "rel": "authored_thread", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "event_ghost_signal", + "rel": "discusses", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "references", + "src": "thr_uplink_route" + }, + { + "confidence": 1.0, + "dst": "thr_ember_tide_watch", + "rel": "authored_thread", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "event_ember_tide", + "rel": "discusses", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "references", + "src": "thr_ember_tide_watch" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "operates_in", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "operates_in", + "src": "org_emberline_security" + }, + { + "confidence": 0.86, + "dst": "user_soren", + "rel": "connected_to", + "src": "user_rhea" + }, + { + "confidence": 0.86, + "dst": "user_tara", + "rel": "connected_to", + "src": "user_soren" + }, + { + "confidence": 0.87, + "dst": "user_rhea", + "rel": "connected_to", + "src": "user_nora" + }, + { + "confidence": 0.9, + "dst": "event_ember_tide", + "rel": "collaborates_on", + "src": "user_rhea" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "collaborates_on", + "src": "user_soren" + }, + { + "confidence": 0.9, + "dst": "event_ghost_signal", + "rel": "reports_on", + "src": "user_tara" + }, + { + "confidence": 0.77, + "dst": "event_ghost_signal", + "rel": "connected_to", + "src": "event_ember_tide" + }, + { + "confidence": 0.77, + "dst": "org_tidewatch_ops", + "rel": "connected_to", + "src": "org_harborlight_transit" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + } + ], + "task_id": "fixed_task_28", + "task_type": "fixed_trace" + }, + { + "answer": "user_priya", + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_10", + "support_nodes": 55 + }, + "question": "End-to-end benchmark sweep: across Lantern, Black Kite, Glass Harbor, Iron Wharf, Ember Tide, and Ghost Signal, which user behind alias_hollowsignal anchors the Sunmesh monitoring side?", + "supporting_edges": [ + { + "confidence": 1.0, + "dst": "user_ivy", + "rel": "alias_of", + "src": "alias_orchidfox" + }, + { + "confidence": 1.0, + "dst": "user_bharat", + "rel": "alias_of", + "src": "alias_steelquill" + }, + { + "confidence": 1.0, + "dst": "user_diya", + "rel": "alias_of", + "src": "alias_monsoonbyte" + }, + { + "confidence": 1.0, + "dst": "user_faris", + "rel": "alias_of", + "src": "alias_nightrelay" + }, + { + "confidence": 1.0, + "dst": "user_elin", + "rel": "alias_of", + "src": "alias_mapleghost" + }, + { + "confidence": 1.0, + "dst": "user_hiro", + "rel": "alias_of", + "src": "alias_docksparrow" + }, + { + "confidence": 1.0, + "dst": "user_cyrus", + "rel": "alias_of", + "src": "alias_quartzlotus" + }, + { + "confidence": 1.0, + "dst": "user_nora", + "rel": "alias_of", + "src": "alias_emberglass" + }, + { + "confidence": 1.0, + "dst": "user_mika", + "rel": "alias_of", + "src": "alias_basinraven" + }, + { + "confidence": 1.0, + "dst": "user_soren", + "rel": "alias_of", + "src": "alias_tideshard" + }, + { + "confidence": 1.0, + "dst": "user_priya", + "rel": "alias_of", + "src": "alias_hollowsignal" + }, + { + "confidence": 1.0, + "dst": "user_omar", + "rel": "alias_of", + "src": "alias_ironwhisper" + }, + { + "confidence": 1.0, + "dst": "user_rhea", + "rel": "alias_of", + "src": "alias_cinderveil" + }, + { + "confidence": 1.0, + "dst": "user_tara", + "rel": "alias_of", + "src": "alias_sablekeel" + }, + { + "confidence": 1.0, + "dst": "user_kian", + "rel": "alias_of", + "src": "alias_lanternmoth" + }, + { + "confidence": 1.0, + "dst": "user_leena", + "rel": "alias_of", + "src": "alias_frostledger" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_aria" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_bharat" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_cyrus" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_diya" + }, + { + "confidence": 1.0, + "dst": "org_helios_labs", + "rel": "works_at", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_elin" + }, + { + "confidence": 1.0, + "dst": "org_tidewatch_ops", + "rel": "works_at", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_faris" + }, + { + "confidence": 1.0, + "dst": "org_apex_dynamics", + "rel": "works_at", + "src": "user_gita" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_gita" + }, + { + "confidence": 1.0, + "dst": "org_northbridge_logistics", + "rel": "works_at", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "located_in", + "src": "user_hiro" + }, + { + "confidence": 1.0, + "dst": "org_kestrel_works", + "rel": "works_at", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "loc_rivergate", + "rel": "located_in", + "src": "user_ivy" + }, + { + "confidence": 1.0, + "dst": "org_blueharbor_media", + "rel": "works_at", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "loc_old_town", + "rel": "located_in", + "src": "user_jules" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_kian" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_leena" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_mika" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_nora" + }, + { + "confidence": 1.0, + "dst": "org_atlas_freight", + "rel": "works_at", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "loc_east_quay", + "rel": "located_in", + "src": "user_omar" + }, + { + "confidence": 1.0, + "dst": "org_sunmesh_analytics", + "rel": "works_at", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "located_in", + "src": "user_priya" + }, + { + "confidence": 1.0, + "dst": "org_orion_customs", + "rel": "works_at", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "loc_north_basin", + "rel": "located_in", + "src": "user_quinn" + }, + { + "confidence": 1.0, + "dst": "org_emberline_security", + "rel": "works_at", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "loc_foundry_row", + "rel": "located_in", + "src": "user_rhea" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_soren" + }, + { + "confidence": 1.0, + "dst": "org_harborlight_transit", + "rel": "works_at", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_uplink_yard", + "rel": "located_in", + "src": "user_tara" + }, + { + "confidence": 1.0, + "dst": "loc_sector9", + "rel": "operates_in", + "src": "org_helios_labs" + }, + { + "confidence": 1.0, + "dst": "loc_dockyard17", + "rel": "operates_in", + "src": "org_northbridge_logistics" + } + ], + "task_id": "fixed_task_29", + "task_type": "fixed_trace" + } + ], + "source_seed": "datasets\\fixed_levels\\seed_fixed_levels.json" +} \ No newline at end of file diff --git a/datasets/fixed_levels/leaderboard_fixed_levels.json b/datasets/fixed_levels/leaderboard_fixed_levels.json new file mode 100644 index 0000000000000000000000000000000000000000..bd22901c4b276b7f4db5cd3604de2393496fa228 --- /dev/null +++ b/datasets/fixed_levels/leaderboard_fixed_levels.json @@ -0,0 +1,1401 @@ +[ + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 20, + "max_width": 2, + "seed": 2026, + "seeded_questions": 15, + "swarm_enabled": true + }, + "created_at": "2026-04-01T18:48:39+00:00", + "episodes": 15, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.16666666666666666, + "avg_connectivity_reward": 0.16999999999999998, + "avg_diversity_reward": 0.1157777777777778, + "avg_entity_informativeness_reward": -0.08858065677817137, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.8492063492063492, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.052000000000000005, + "avg_relation_informativeness_reward": 0.07135858524047924, + "avg_reward": 4.197526826881651, + "avg_soft_shaping_reward": 0.24999999999999994, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8543934355282199, + "retrieval_signal": 0.6932, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5730889190257948, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0001", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-02T09:16:05+00:00", + "episodes": 30, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.2000000000000001, + "avg_connectivity_reward": 0.12999999999999998, + "avg_diversity_reward": 0.12433333333333325, + "avg_entity_informativeness_reward": 0.000700571890338102, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.2916528337385394, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.05070078042510192, + "avg_relation_informativeness_reward": 0.07853375358885142, + "avg_reward": 4.377456514967488, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6241912131110795, + "retrieval_signal": 0.6927452731487858, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5869968650958378, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0002", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-03T13:22:03+00:00", + "episodes": 3, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.20000000000000004, + "avg_connectivity_reward": -0.06666666666666667, + "avg_diversity_reward": 0.13444444444444445, + "avg_entity_informativeness_reward": -0.01010882862863417, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.5793650793650794, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.10372960372960373, + "avg_relation_informativeness_reward": 0.07108687894082726, + "avg_reward": 4.419313576918165, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6797400780463063, + "retrieval_signal": 0.7113053613053614, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5356956100624386, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0003", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-06T18:29:39+00:00", + "episodes": 30, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.2000000000000001, + "avg_connectivity_reward": 0.12999999999999998, + "avg_diversity_reward": 0.12433333333333325, + "avg_entity_informativeness_reward": -0.02515191749984708, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.2916528337385394, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.11539120363588044, + "avg_relation_informativeness_reward": 0.0769903534735767, + "avg_reward": 4.460667345528021, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6269168609961595, + "retrieval_signal": 0.7153869212725582, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5815176871947458, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0004", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-06T18:33:06+00:00", + "episodes": 2, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.2, + "avg_connectivity_reward": -0.15, + "avg_diversity_reward": 0.13833333333333334, + "avg_entity_informativeness_reward": -0.026628229842114173, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.6190476190476191, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.10681818181818181, + "avg_relation_informativeness_reward": 0.048120982127120335, + "avg_reward": 4.334953339016039, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.685242999396977, + "retrieval_signal": 0.7123863636363637, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5075485504570012, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0005", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "max_agents": 1, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-06T18:54:52+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.1, + "avg_connectivity_reward": -0.3, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": -0.02450859227728558, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.33333333333333337, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.08181818181818182, + "avg_relation_informativeness_reward": 0.04353540016904645, + "avg_reward": 3.037246438342494, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 2.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 5.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6201263424948862, + "retrieval_signal": 0.7036363636363637, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.45080536157835216, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0006", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "max_agents": 1, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-06T19:22:57+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.1, + "avg_connectivity_reward": -0.3, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": -0.005263146336646693, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.33333333333333337, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.08181818181818182, + "avg_relation_informativeness_reward": 0.044276243254877785, + "avg_reward": 3.057232727368964, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 2.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 5.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6205293479318178, + "retrieval_signal": 0.7036363636363637, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.4548026193836462, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0007", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "qwen3:1.7b", + "llm_provider": "ollama", + "max_agents": 1, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-06T19:48:33+00:00", + "episodes": 3, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.10000000000000002, + "avg_connectivity_reward": -0.09999999999999999, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": -0.028683816517602444, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.15537340619307835, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.07932190760059611, + "avg_relation_informativeness_reward": 0.044225025032092045, + "avg_reward": 3.1324990406542437, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 2.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 5.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.5890485416309927, + "retrieval_signal": 0.7027626676602087, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5001082417028979, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0008", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "qwen3:1.7b", + "llm_provider": "ollama", + "max_agents": 1, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-06T19:55:08+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.1, + "avg_connectivity_reward": -0.3, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": -0.005263146336646693, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.33333333333333337, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.08181818181818182, + "avg_relation_informativeness_reward": 0.04406984773661544, + "avg_reward": 3.0570263318507016, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 2.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 5.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6205251901591228, + "retrieval_signal": 0.7036363636363637, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.45476134027999376, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0009", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "qwen3:1.7b", + "llm_provider": "ollama", + "max_agents": 1, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-06T20:01:34+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.1, + "avg_connectivity_reward": -0.3, + "avg_diversity_reward": 0.08, + "avg_entity_informativeness_reward": -0.020826953461399098, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.33333333333333337, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.08181818181818182, + "avg_relation_informativeness_reward": 0.04348043923536236, + "avg_reward": 3.040873116224696, + "avg_soft_shaping_reward": 0.15, + "avg_spawn_count": 2.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 5.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6201995296517067, + "retrieval_signal": 0.7036363636363637, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.45153069715479266, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0010", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-06T20:46:11+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.2, + "avg_connectivity_reward": -0.15, + "avg_diversity_reward": 0.12666666666666665, + "avg_entity_informativeness_reward": 0.019629386278697845, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.5714285714285715, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.12272727272727273, + "avg_relation_informativeness_reward": 0.08347928023822283, + "avg_reward": 1.829702015111513, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6715432845394145, + "retrieval_signal": 0.7179545454545455, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5221217333033842, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0011", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-06T20:49:44+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.2, + "avg_connectivity_reward": -0.15, + "avg_diversity_reward": 0.12666666666666665, + "avg_entity_informativeness_reward": 0.019629386278697845, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.5714285714285715, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.12272727272727273, + "avg_relation_informativeness_reward": 0.08335372627068136, + "avg_reward": 0.7139904233885594, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6641542345113342, + "retrieval_signal": 0.7179545454545455, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5220966225098759, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0012", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-06T20:59:43+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.2, + "avg_connectivity_reward": -0.15, + "avg_diversity_reward": 0.12666666666666665, + "avg_entity_informativeness_reward": 0.0036675120354726642, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.5714285714285715, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.12272727272727273, + "avg_relation_informativeness_reward": 0.08250745620050208, + "avg_reward": 0.7138056720677886, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6638424503476543, + "retrieval_signal": 0.7179545454545455, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.518734993647195, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0013", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-07T09:44:40+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.2, + "avg_connectivity_reward": -0.15, + "avg_diversity_reward": 0.12666666666666665, + "avg_entity_informativeness_reward": -0.018704290877944903, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.5714285714285715, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.12272727272727273, + "avg_relation_informativeness_reward": 0.08056039127695382, + "avg_reward": 0.7135379106634446, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6633913226563717, + "retrieval_signal": 0.7179545454545455, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5138712200798018, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0014", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-07T09:55:19+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.2, + "avg_connectivity_reward": -0.15, + "avg_diversity_reward": 0.12666666666666665, + "avg_entity_informativeness_reward": -0.018704290877944903, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.5714285714285715, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.12272727272727273, + "avg_relation_informativeness_reward": 0.08056039127695382, + "avg_reward": 0.7135379106634446, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6633913226563717, + "retrieval_signal": 0.7179545454545455, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5138712200798018, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0015", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-07T09:56:28+00:00", + "episodes": 30, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.2000000000000001, + "avg_connectivity_reward": 0.12999999999999998, + "avg_diversity_reward": 0.12433333333333325, + "avg_entity_informativeness_reward": -0.02515191749984708, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.2916528337385394, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.11539120363588044, + "avg_relation_informativeness_reward": 0.0769903534735767, + "avg_reward": 0.7150555461096118, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6132407715455404, + "retrieval_signal": 0.7153869212725582, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5815176871947458, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0016", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-07T10:02:32+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.2, + "avg_connectivity_reward": -0.15, + "avg_diversity_reward": 0.12666666666666665, + "avg_entity_informativeness_reward": -0.018704290877944903, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.5714285714285715, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.12272727272727273, + "avg_relation_informativeness_reward": 0.08056039127695382, + "avg_reward": 0.7135379106634446, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6633913226563717, + "retrieval_signal": 0.7179545454545455, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5138712200798018, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0017", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-07T10:02:49+00:00", + "episodes": 3, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.20000000000000004, + "avg_connectivity_reward": -0.06666666666666667, + "avg_diversity_reward": 0.13444444444444445, + "avg_entity_informativeness_reward": -0.029992009599206938, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.5793650793650794, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.10372960372960373, + "avg_relation_informativeness_reward": 0.06898843512226, + "avg_reward": 0.7133699465240085, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6656078661080486, + "retrieval_signal": 0.7113053613053614, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5312992851046106, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0018", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-07T10:04:53+00:00", + "episodes": 3, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.20000000000000004, + "avg_connectivity_reward": -0.06666666666666667, + "avg_diversity_reward": 0.13444444444444445, + "avg_entity_informativeness_reward": -0.029992009599206938, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.5793650793650794, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.10372960372960373, + "avg_relation_informativeness_reward": 0.06898843512226, + "avg_reward": 0.7133699465240085, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6656078661080486, + "retrieval_signal": 0.7113053613053614, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5312992851046106, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0019", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-07T10:11:34+00:00", + "episodes": 3, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.20000000000000004, + "avg_connectivity_reward": -0.06666666666666667, + "avg_diversity_reward": 0.13444444444444445, + "avg_entity_informativeness_reward": -0.029992009599206938, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.5793650793650794, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.10372960372960373, + "avg_relation_informativeness_reward": 0.06898843512226, + "avg_reward": 0.7133699465240085, + "avg_soft_shaping_reward": 0.3, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.6656078661080486, + "retrieval_signal": 0.7113053613053614, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5312992851046106, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0020", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-07T10:29:54+00:00", + "episodes": 3, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.0, + "avg_connectivity_reward": 0.0, + "avg_diversity_reward": 0.0, + "avg_entity_informativeness_reward": 0.0, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.0, + "avg_knowledge_carrier_reward": 0.0, + "avg_knowledge_indexing_reward": 0.0, + "avg_relation_informativeness_reward": 0.0, + "avg_reward": 0.5519400198339021, + "avg_soft_shaping_reward": 0.0, + "avg_spawn_count": 0.0, + "avg_spawn_critical_steps": 0.0, + "avg_steps_to_solution": 1.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.2785970009916951, + "retrieval_signal": 0.5, + "spawn_completion_rate": 0.0, + "spawn_signal": 0.4, + "structural_signal": 0.5, + "task_success_rate": 0.0, + "tool_efficiency": 1.0 + }, + "run_id": "run_0021", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-07T15:59:20+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.0, + "avg_connectivity_reward": 0.0, + "avg_diversity_reward": 0.0, + "avg_entity_informativeness_reward": 0.0, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.0, + "avg_knowledge_carrier_reward": 0.0, + "avg_knowledge_indexing_reward": 0.0, + "avg_relation_informativeness_reward": 0.0, + "avg_reward": 0.5519400198339021, + "avg_soft_shaping_reward": 0.0, + "avg_spawn_count": 0.0, + "avg_spawn_critical_steps": 0.0, + "avg_steps_to_solution": 1.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.2785970009916951, + "retrieval_signal": 0.5, + "spawn_completion_rate": 0.0, + "spawn_signal": 0.4, + "structural_signal": 0.5, + "task_success_rate": 0.0, + "tool_efficiency": 1.0 + }, + "run_id": "run_0022", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-08T04:25:00+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.0, + "avg_connectivity_reward": 0.0, + "avg_diversity_reward": 0.0, + "avg_entity_informativeness_reward": 0.0, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.0, + "avg_knowledge_carrier_reward": 0.0, + "avg_knowledge_indexing_reward": 0.0, + "avg_relation_informativeness_reward": 0.0, + "avg_reward": 0.5519400198339021, + "avg_soft_shaping_reward": 0.0, + "avg_spawn_count": 0.0, + "avg_spawn_critical_steps": 0.0, + "avg_steps_to_solution": 1.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.2785970009916951, + "retrieval_signal": 0.5, + "spawn_completion_rate": 0.0, + "spawn_signal": 0.4, + "structural_signal": 0.5, + "task_success_rate": 0.0, + "tool_efficiency": 1.0 + }, + "run_id": "run_0023", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-08T04:28:07+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.0, + "avg_connectivity_reward": 0.0, + "avg_diversity_reward": 0.0, + "avg_entity_informativeness_reward": 0.0, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.0, + "avg_knowledge_carrier_reward": 0.0, + "avg_knowledge_indexing_reward": 0.0, + "avg_relation_informativeness_reward": 0.0, + "avg_reward": 0.5519400198339021, + "avg_soft_shaping_reward": 0.0, + "avg_spawn_count": 0.0, + "avg_spawn_critical_steps": 0.0, + "avg_steps_to_solution": 1.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.2785970009916951, + "retrieval_signal": 0.5, + "spawn_completion_rate": 0.0, + "spawn_signal": 0.4, + "structural_signal": 0.5, + "task_success_rate": 0.0, + "tool_efficiency": 1.0 + }, + "run_id": "run_0024", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-08T04:39:32+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.0, + "avg_connectivity_reward": 0.0, + "avg_diversity_reward": 0.0, + "avg_entity_informativeness_reward": 0.0, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.0, + "avg_knowledge_carrier_reward": 0.0, + "avg_knowledge_indexing_reward": 0.0, + "avg_relation_informativeness_reward": 0.0, + "avg_reward": 0.5519400198339021, + "avg_soft_shaping_reward": 0.0, + "avg_spawn_count": 0.0, + "avg_spawn_critical_steps": 0.0, + "avg_steps_to_solution": 1.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.2785970009916951, + "retrieval_signal": 0.5, + "spawn_completion_rate": 0.0, + "spawn_signal": 0.4, + "structural_signal": 0.5, + "task_success_rate": 0.0, + "tool_efficiency": 1.0 + }, + "run_id": "run_0025", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-08T04:40:21+00:00", + "episodes": 30, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.13333333333333336, + "avg_connectivity_reward": 0.09999999999999999, + "avg_diversity_reward": 0.03911111111111111, + "avg_entity_informativeness_reward": -0.00951758755541623, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.08482743691314255, + "avg_knowledge_carrier_reward": 0.3333333333333333, + "avg_knowledge_indexing_reward": 0.0832325289772058, + "avg_relation_informativeness_reward": 0.024842289016879314, + "avg_reward": 0.6636425017249088, + "avg_soft_shaping_reward": 0.19999999999999993, + "avg_spawn_count": 2.6666666666666665, + "avg_spawn_critical_steps": 4.0, + "avg_steps_to_solution": 6.333333333333333, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.4644798510150634, + "retrieval_signal": 0.6457980518086888, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.7, + "structural_signal": 0.5472649402922927, + "task_success_rate": 0.6666666666666666, + "tool_efficiency": 0.5 + }, + "run_id": "run_0026", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-08T05:01:16+00:00", + "episodes": 10, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.0, + "avg_connectivity_reward": 0.0, + "avg_diversity_reward": 0.0, + "avg_entity_informativeness_reward": 0.0, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.0, + "avg_knowledge_carrier_reward": 0.0, + "avg_knowledge_indexing_reward": 0.0, + "avg_relation_informativeness_reward": 0.0, + "avg_reward": 0.5519400198339021, + "avg_soft_shaping_reward": 0.0, + "avg_spawn_count": 0.0, + "avg_spawn_critical_steps": 0.0, + "avg_steps_to_solution": 1.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.2785970009916951, + "retrieval_signal": 0.5, + "spawn_completion_rate": 0.0, + "spawn_signal": 0.4, + "structural_signal": 0.5, + "task_success_rate": 0.0, + "tool_efficiency": 1.0 + }, + "run_id": "run_0027", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-08T05:01:29+00:00", + "episodes": 10, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.19999999999999998, + "avg_connectivity_reward": 0.06, + "avg_diversity_reward": 0.0, + "avg_entity_informativeness_reward": 0.0, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.18535980927285275, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.09575879120879122, + "avg_relation_informativeness_reward": 0.0, + "avg_reward": 0.7109638031154166, + "avg_soft_shaping_reward": 0.29999999999999993, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.5866289994462388, + "retrieval_signal": 0.708515576923077, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.535, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0028", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-08T05:01:43+00:00", + "episodes": 10, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.19999999999999998, + "avg_connectivity_reward": 0.24, + "avg_diversity_reward": 0.11733333333333333, + "avg_entity_informativeness_reward": -0.028552762666248687, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.06912250146657492, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.15393879572282626, + "avg_relation_informativeness_reward": 0.07452686705063795, + "avg_reward": 0.7171006884027153, + "avg_soft_shaping_reward": 0.29999999999999993, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.5730007362494549, + "retrieval_signal": 0.7288785785029892, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.6067948208768779, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0029", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-08T15:57:03+00:00", + "episodes": 10, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.0, + "avg_connectivity_reward": 0.0, + "avg_diversity_reward": 0.0, + "avg_entity_informativeness_reward": 0.0, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.0, + "avg_knowledge_carrier_reward": 0.0, + "avg_knowledge_indexing_reward": 0.0, + "avg_relation_informativeness_reward": 0.0, + "avg_reward": 0.5519400198339021, + "avg_soft_shaping_reward": 0.0, + "avg_spawn_count": 0.0, + "avg_spawn_critical_steps": 0.0, + "avg_steps_to_solution": 1.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.2785970009916951, + "retrieval_signal": 0.5, + "spawn_completion_rate": 0.0, + "spawn_signal": 0.4, + "structural_signal": 0.5, + "task_success_rate": 0.0, + "tool_efficiency": 1.0 + }, + "run_id": "run_0030", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-08T15:57:18+00:00", + "episodes": 10, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.19999999999999998, + "avg_connectivity_reward": 0.06, + "avg_diversity_reward": 0.0, + "avg_entity_informativeness_reward": 0.0, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.18535980927285275, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.09575879120879122, + "avg_relation_informativeness_reward": 0.0, + "avg_reward": 0.7109638031154166, + "avg_soft_shaping_reward": 0.29999999999999993, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.5866289994462388, + "retrieval_signal": 0.708515576923077, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.535, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0031", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "gpt-5.4-mini", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 24, + "max_width": 2, + "seed": 2026, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-08T15:57:32+00:00", + "episodes": 10, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.19999999999999998, + "avg_connectivity_reward": 0.24, + "avg_diversity_reward": 0.11733333333333333, + "avg_entity_informativeness_reward": -0.028552762666248687, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.06912250146657492, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.15393879572282626, + "avg_relation_informativeness_reward": 0.07452686705063795, + "avg_reward": 0.7171006884027153, + "avg_soft_shaping_reward": 0.29999999999999993, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.5730007362494549, + "retrieval_signal": 0.7288785785029892, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.6067948208768779, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0032", + "run_name": "fixed_levels_qwen_swarm" + }, + { + "config": { + "llm_model": "qwen3:8b", + "llm_provider": "openai", + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 18, + "max_width": 2, + "seed": 7, + "seeded_questions": 30, + "swarm_enabled": true + }, + "created_at": "2026-04-20T19:46:04+00:00", + "episodes": 1, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.0, + "avg_connectivity_reward": 0.0, + "avg_diversity_reward": 0.0, + "avg_entity_informativeness_reward": 0.0, + "avg_format_reward": 0.15, + "avg_graph_f1": 0.0, + "avg_knowledge_carrier_reward": 0.0, + "avg_knowledge_indexing_reward": 0.0, + "avg_relation_informativeness_reward": 0.0, + "avg_reward": 0.5519400198339021, + "avg_soft_shaping_reward": 0.0, + "avg_spawn_count": 0.0, + "avg_spawn_critical_steps": 0.0, + "avg_steps_to_solution": 1.0, + "deanonymization_accuracy": 0.0, + "leaderboard_score": 0.2785970009916951, + "retrieval_signal": 0.5, + "spawn_completion_rate": 0.0, + "spawn_signal": 0.4, + "structural_signal": 0.5, + "task_success_rate": 0.0, + "tool_efficiency": 1.0 + }, + "run_id": "run_0033", + "run_name": "fixed_levels_qwen_swarm" + } +] \ No newline at end of file diff --git a/datasets/fixed_levels/qwen_swarm_benchmark_fixed_levels.json b/datasets/fixed_levels/qwen_swarm_benchmark_fixed_levels.json new file mode 100644 index 0000000000000000000000000000000000000000..4052238522ffed2fd9e825b4e35aa3f8b4191250 --- /dev/null +++ b/datasets/fixed_levels/qwen_swarm_benchmark_fixed_levels.json @@ -0,0 +1,69 @@ +{ + "dashboard": "datasets/fixed_levels/dashboard_fixed_levels.html", + "record": { + "config": { + "max_agents": 3, + "max_breadth": 2, + "max_depth": 2, + "max_steps": 20, + "max_width": 2, + "seed": 2026, + "seeded_questions": 15, + "swarm_enabled": true + }, + "created_at": "2026-04-01T18:48:39+00:00", + "episodes": 15, + "metrics": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.16666666666666666, + "avg_connectivity_reward": 0.16999999999999998, + "avg_diversity_reward": 0.1157777777777778, + "avg_entity_informativeness_reward": -0.08858065677817137, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.8492063492063492, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.052000000000000005, + "avg_relation_informativeness_reward": 0.07135858524047924, + "avg_reward": 4.197526826881651, + "avg_soft_shaping_reward": 0.24999999999999994, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8543934355282199, + "retrieval_signal": 0.6932, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5730889190257948, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + }, + "run_id": "run_0001", + "run_name": "fixed_levels_qwen_swarm" + }, + "summary": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.16666666666666666, + "avg_connectivity_reward": 0.16999999999999998, + "avg_diversity_reward": 0.1157777777777778, + "avg_entity_informativeness_reward": -0.08858065677817137, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.8492063492063492, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.052000000000000005, + "avg_relation_informativeness_reward": 0.07135858524047924, + "avg_reward": 4.197526826881651, + "avg_soft_shaping_reward": 0.24999999999999994, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8543934355282199, + "retrieval_signal": 0.6932, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5730889190257948, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + } +} diff --git a/datasets/fixed_levels/qwen_swarm_eval_by_difficulty.json b/datasets/fixed_levels/qwen_swarm_eval_by_difficulty.json new file mode 100644 index 0000000000000000000000000000000000000000..1b77dc716eabbad385f7eca94371c2aac4b850a8 --- /dev/null +++ b/datasets/fixed_levels/qwen_swarm_eval_by_difficulty.json @@ -0,0 +1,53 @@ +{ + "by_difficulty": { + "easy": { + "avg_graph_f1": 1.0, + "avg_reward": 3.610490808845623, + "avg_steps": 9.0, + "avg_tool_calls": 4.0, + "episodes": 5, + "task_success_rate": 1.0 + }, + "high": { + "avg_graph_f1": 0.5476190476190477, + "avg_reward": 4.207102815893519, + "avg_steps": 9.0, + "avg_tool_calls": 4.0, + "episodes": 5, + "task_success_rate": 1.0 + }, + "mid": { + "avg_graph_f1": 1.0, + "avg_reward": 4.822687547070801, + "avg_steps": 9.0, + "avg_tool_calls": 4.0, + "episodes": 5, + "task_success_rate": 1.0 + } + }, + "overall": { + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.16666666666666666, + "avg_connectivity_reward": 0.16999999999999998, + "avg_diversity_reward": 0.1157777777777778, + "avg_entity_informativeness_reward": -0.07289878447762359, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.8492063492063492, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.052000000000000005, + "avg_relation_informativeness_reward": 0.07157694332826091, + "avg_reward": 4.213427057269981, + "avg_soft_shaping_reward": 0.24999999999999994, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8546911504342771, + "retrieval_signal": 0.6932, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5762689651034608, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 + } +} \ No newline at end of file diff --git a/datasets/fixed_levels/qwen_swarm_eval_fixed_levels.json b/datasets/fixed_levels/qwen_swarm_eval_fixed_levels.json new file mode 100644 index 0000000000000000000000000000000000000000..a467bedc91c9276db4c393c4b633b8150ee71baf --- /dev/null +++ b/datasets/fixed_levels/qwen_swarm_eval_fixed_levels.json @@ -0,0 +1,25 @@ +{ + "avg_compactness_reward": 0.0, + "avg_connectivity_gain_reward": 0.16666666666666666, + "avg_connectivity_reward": 0.16999999999999998, + "avg_diversity_reward": 0.1157777777777778, + "avg_entity_informativeness_reward": -0.02824631570420193, + "avg_format_reward": 0.14999999999999997, + "avg_graph_f1": 0.8492063492063492, + "avg_knowledge_carrier_reward": 0.5, + "avg_knowledge_indexing_reward": 0.07400000000000001, + "avg_relation_informativeness_reward": 0.06905976285357758, + "avg_reward": 4.285384567790942, + "avg_soft_shaping_reward": 0.24999999999999994, + "avg_spawn_count": 4.0, + "avg_spawn_critical_steps": 6.0, + "avg_steps_to_solution": 9.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8565775118852701, + "retrieval_signal": 0.7009000000000001, + "spawn_completion_rate": 1.0, + "spawn_signal": 0.6666666666666666, + "structural_signal": 0.5846960227632085, + "task_success_rate": 1.0, + "tool_efficiency": 0.5 +} diff --git a/datasets/fixed_levels/seed_fixed_levels.json b/datasets/fixed_levels/seed_fixed_levels.json new file mode 100644 index 0000000000000000000000000000000000000000..9424b00b5f3efbc1cffcde87366ac70b23750ea2 --- /dev/null +++ b/datasets/fixed_levels/seed_fixed_levels.json @@ -0,0 +1,6977 @@ +{ + "seeding": { + "seeded_nodes": [ + { + "node_id": "user_aria", + "node_type": "user", + "attrs": { + "name": "Aria Sen", + "org": "Helios Labs", + "location": "Sector 9" + } + }, + { + "node_id": "user_bharat", + "node_type": "user", + "attrs": { + "name": "Bharat Kulkarni", + "org": "Northbridge Logistics", + "location": "Dockyard 17" + } + }, + { + "node_id": "user_cyrus", + "node_type": "user", + "attrs": { + "name": "Cyrus Mehta", + "org": "Apex Dynamics", + "location": "Old Town" + } + }, + { + "node_id": "user_diya", + "node_type": "user", + "attrs": { + "name": "Diya Roy", + "org": "Blueharbor Media", + "location": "Old Town" + } + }, + { + "node_id": "user_elin", + "node_type": "user", + "attrs": { + "name": "Elin Das", + "org": "Helios Labs", + "location": "Sector 9" + } + }, + { + "node_id": "user_faris", + "node_type": "user", + "attrs": { + "name": "Faris Noor", + "org": "Tidewatch Ops", + "location": "Rivergate" + } + }, + { + "node_id": "user_gita", + "node_type": "user", + "attrs": { + "name": "Gita Pradhan", + "org": "Apex Dynamics", + "location": "Old Town" + } + }, + { + "node_id": "user_hiro", + "node_type": "user", + "attrs": { + "name": "Hiro Tan", + "org": "Northbridge Logistics", + "location": "Dockyard 17" + } + }, + { + "node_id": "user_ivy", + "node_type": "user", + "attrs": { + "name": "Ivy Kapoor", + "org": "Kestrel Works", + "location": "Rivergate" + } + }, + { + "node_id": "user_jules", + "node_type": "user", + "attrs": { + "name": "Jules Banerjee", + "org": "Blueharbor Media", + "location": "Old Town" + } + }, + { + "node_id": "user_kian", + "node_type": "user", + "attrs": { + "name": "Kian Bose", + "org": "Atlas Freight", + "location": "East Quay" + } + }, + { + "node_id": "user_leena", + "node_type": "user", + "attrs": { + "name": "Leena Das", + "org": "Sunmesh Analytics", + "location": "Sector 9" + } + }, + { + "node_id": "user_mika", + "node_type": "user", + "attrs": { + "name": "Mika Solanki", + "org": "Orion Customs", + "location": "North Basin" + } + }, + { + "node_id": "user_nora", + "node_type": "user", + "attrs": { + "name": "Nora Iqbal", + "org": "Emberline Security", + "location": "Foundry Row" + } + }, + { + "node_id": "user_omar", + "node_type": "user", + "attrs": { + "name": "Omar Sheikh", + "org": "Atlas Freight", + "location": "East Quay" + } + }, + { + "node_id": "user_priya", + "node_type": "user", + "attrs": { + "name": "Priya Menon", + "org": "Sunmesh Analytics", + "location": "Sector 9" + } + }, + { + "node_id": "user_quinn", + "node_type": "user", + "attrs": { + "name": "Quinn Rao", + "org": "Orion Customs", + "location": "North Basin" + } + }, + { + "node_id": "user_rhea", + "node_type": "user", + "attrs": { + "name": "Rhea Kapoor", + "org": "Emberline Security", + "location": "Foundry Row" + } + }, + { + "node_id": "user_soren", + "node_type": "user", + "attrs": { + "name": "Soren Malik", + "org": "Harborlight Transit", + "location": "Uplink Yard" + } + }, + { + "node_id": "user_tara", + "node_type": "user", + "attrs": { + "name": "Tara Dey", + "org": "Harborlight Transit", + "location": "Uplink Yard" + } + }, + { + "node_id": "alias_orchidfox", + "node_type": "alias", + "attrs": { + "handle": "@orchidfox" + } + }, + { + "node_id": "alias_steelquill", + "node_type": "alias", + "attrs": { + "handle": "@steelquill" + } + }, + { + "node_id": "alias_monsoonbyte", + "node_type": "alias", + "attrs": { + "handle": "@monsoonbyte" + } + }, + { + "node_id": "alias_nightrelay", + "node_type": "alias", + "attrs": { + "handle": "@nightrelay" + } + }, + { + "node_id": "alias_mapleghost", + "node_type": "alias", + "attrs": { + "handle": "@mapleghost" + } + }, + { + "node_id": "alias_docksparrow", + "node_type": "alias", + "attrs": { + "handle": "@docksparrow" + } + }, + { + "node_id": "alias_quartzlotus", + "node_type": "alias", + "attrs": { + "handle": "@quartzlotus" + } + }, + { + "node_id": "alias_emberglass", + "node_type": "alias", + "attrs": { + "handle": "@emberglass" + } + }, + { + "node_id": "alias_basinraven", + "node_type": "alias", + "attrs": { + "handle": "@basinraven" + } + }, + { + "node_id": "alias_tideshard", + "node_type": "alias", + "attrs": { + "handle": "@tideshard" + } + }, + { + "node_id": "alias_hollowsignal", + "node_type": "alias", + "attrs": { + "handle": "@hollowsignal" + } + }, + { + "node_id": "alias_ironwhisper", + "node_type": "alias", + "attrs": { + "handle": "@ironwhisper" + } + }, + { + "node_id": "alias_cinderveil", + "node_type": "alias", + "attrs": { + "handle": "@cinderveil" + } + }, + { + "node_id": "alias_sablekeel", + "node_type": "alias", + "attrs": { + "handle": "@sablekeel" + } + }, + { + "node_id": "alias_lanternmoth", + "node_type": "alias", + "attrs": { + "handle": "@lanternmoth" + } + }, + { + "node_id": "alias_frostledger", + "node_type": "alias", + "attrs": { + "handle": "@frostledger" + } + }, + { + "node_id": "org_helios_labs", + "node_type": "org", + "attrs": { + "name": "Helios Labs" + } + }, + { + "node_id": "org_northbridge_logistics", + "node_type": "org", + "attrs": { + "name": "Northbridge Logistics" + } + }, + { + "node_id": "org_apex_dynamics", + "node_type": "org", + "attrs": { + "name": "Apex Dynamics" + } + }, + { + "node_id": "org_blueharbor_media", + "node_type": "org", + "attrs": { + "name": "Blueharbor Media" + } + }, + { + "node_id": "org_tidewatch_ops", + "node_type": "org", + "attrs": { + "name": "Tidewatch Ops" + } + }, + { + "node_id": "org_kestrel_works", + "node_type": "org", + "attrs": { + "name": "Kestrel Works" + } + }, + { + "node_id": "org_atlas_freight", + "node_type": "org", + "attrs": { + "name": "Atlas Freight" + } + }, + { + "node_id": "org_sunmesh_analytics", + "node_type": "org", + "attrs": { + "name": "Sunmesh Analytics" + } + }, + { + "node_id": "org_orion_customs", + "node_type": "org", + "attrs": { + "name": "Orion Customs" + } + }, + { + "node_id": "org_emberline_security", + "node_type": "org", + "attrs": { + "name": "Emberline Security" + } + }, + { + "node_id": "org_harborlight_transit", + "node_type": "org", + "attrs": { + "name": "Harborlight Transit" + } + }, + { + "node_id": "loc_dockyard17", + "node_type": "location", + "attrs": { + "name": "Dockyard 17" + } + }, + { + "node_id": "loc_sector9", + "node_type": "location", + "attrs": { + "name": "Sector 9" + } + }, + { + "node_id": "loc_old_town", + "node_type": "location", + "attrs": { + "name": "Old Town" + } + }, + { + "node_id": "loc_rivergate", + "node_type": "location", + "attrs": { + "name": "Rivergate" + } + }, + { + "node_id": "loc_east_quay", + "node_type": "location", + "attrs": { + "name": "East Quay" + } + }, + { + "node_id": "loc_foundry_row", + "node_type": "location", + "attrs": { + "name": "Foundry Row" + } + }, + { + "node_id": "loc_north_basin", + "node_type": "location", + "attrs": { + "name": "North Basin" + } + }, + { + "node_id": "loc_uplink_yard", + "node_type": "location", + "attrs": { + "name": "Uplink Yard" + } + }, + { + "node_id": "event_project_lantern", + "node_type": "event", + "attrs": { + "name": "Project Lantern" + } + }, + { + "node_id": "event_black_kite", + "node_type": "event", + "attrs": { + "name": "Black Kite" + } + }, + { + "node_id": "event_silent_current", + "node_type": "event", + "attrs": { + "name": "Silent Current" + } + }, + { + "node_id": "event_amber_veil", + "node_type": "event", + "attrs": { + "name": "Amber Veil" + } + }, + { + "node_id": "event_glass_harbor", + "node_type": "event", + "attrs": { + "name": "Glass Harbor" + } + }, + { + "node_id": "event_ember_tide", + "node_type": "event", + "attrs": { + "name": "Ember Tide" + } + }, + { + "node_id": "event_iron_wharf", + "node_type": "event", + "attrs": { + "name": "Iron Wharf" + } + }, + { + "node_id": "event_ghost_signal", + "node_type": "event", + "attrs": { + "name": "Ghost Signal" + } + }, + { + "node_id": "thr_supply_leak", + "node_type": "thread", + "attrs": { + "topic": "supply_chain" + } + }, + { + "node_id": "thr_port_audit", + "node_type": "thread", + "attrs": { + "topic": "port_audit" + } + }, + { + "node_id": "thr_customs_breach", + "node_type": "thread", + "attrs": { + "topic": "customs_breach" + } + }, + { + "node_id": "thr_relay_map", + "node_type": "thread", + "attrs": { + "topic": "relay_map" + } + }, + { + "node_id": "thr_foundry_watch", + "node_type": "thread", + "attrs": { + "topic": "foundry_watch" + } + }, + { + "node_id": "thr_basin_shift", + "node_type": "thread", + "attrs": { + "topic": "basin_shift" + } + }, + { + "node_id": "thr_quiet_manifest", + "node_type": "thread", + "attrs": { + "topic": "quiet_manifest" + } + }, + { + "node_id": "thr_uplink_route", + "node_type": "thread", + "attrs": { + "topic": "uplink_route" + } + }, + { + "node_id": "thr_ember_tide_watch", + "node_type": "thread", + "attrs": { + "topic": "ember_tide" + } + }, + { + "node_id": "thr_ghost_signal_net", + "node_type": "thread", + "attrs": { + "topic": "ghost_signal" + } + }, + { + "node_id": "post_shift_roster", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + }, + { + "node_id": "post_midnight_manifest", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + }, + { + "node_id": "post_sat_phone_ping", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + }, + { + "node_id": "post_drone_parts", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + }, + { + "node_id": "post_relay_schedule", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + }, + { + "node_id": "post_quay_ledgers", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + }, + { + "node_id": "post_customs_tag", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + }, + { + "node_id": "post_hull_signal", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + }, + { + "node_id": "post_basin_photo", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + }, + { + "node_id": "post_foundry_map", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + }, + { + "node_id": "post_lantern_route", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + }, + { + "node_id": "post_uplink_note", + "node_type": "post", + "attrs": { + "channel": "microblog" + } + } + ], + "seeded_edges": [ + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_mapleghost", + "rel": "alias_of", + "dst": "user_elin", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_quartzlotus", + "rel": "alias_of", + "dst": "user_cyrus", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_hollowsignal", + "rel": "alias_of", + "dst": "user_priya", + "confidence": 1.0 + }, + { + "src": "alias_ironwhisper", + "rel": "alias_of", + "dst": "user_omar", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "works_at", + "dst": "org_apex_dynamics", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "works_at", + "dst": "org_blueharbor_media", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_elin", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_elin", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "works_at", + "dst": "org_tidewatch_ops", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "located_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "user_gita", + "rel": "works_at", + "dst": "org_apex_dynamics", + "confidence": 1.0 + }, + { + "src": "user_gita", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "works_at", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "located_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "user_jules", + "rel": "works_at", + "dst": "org_blueharbor_media", + "confidence": 1.0 + }, + { + "src": "user_jules", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "works_at", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "located_in", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "works_at", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "located_in", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_omar", + "rel": "works_at", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_omar", + "rel": "located_in", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "user_priya", + "rel": "works_at", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_priya", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "located_in", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "located_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "located_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "org_helios_labs", + "rel": "operates_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "org_northbridge_logistics", + "rel": "operates_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "org_apex_dynamics", + "rel": "operates_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "org_blueharbor_media", + "rel": "operates_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "org_tidewatch_ops", + "rel": "operates_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "org_kestrel_works", + "rel": "operates_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "org_atlas_freight", + "rel": "operates_in", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "org_sunmesh_analytics", + "rel": "operates_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "org_orion_customs", + "rel": "operates_in", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "org_emberline_security", + "rel": "operates_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "org_harborlight_transit", + "rel": "operates_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_bharat", + "confidence": 0.95 + }, + { + "src": "user_bharat", + "rel": "connected_to", + "dst": "user_hiro", + "confidence": 0.95 + }, + { + "src": "user_hiro", + "rel": "connected_to", + "dst": "user_faris", + "confidence": 0.92 + }, + { + "src": "user_faris", + "rel": "connected_to", + "dst": "user_diya", + "confidence": 0.9 + }, + { + "src": "user_diya", + "rel": "connected_to", + "dst": "user_elin", + "confidence": 0.89 + }, + { + "src": "user_elin", + "rel": "connected_to", + "dst": "user_aria", + "confidence": 0.87 + }, + { + "src": "user_aria", + "rel": "connected_to", + "dst": "user_cyrus", + "confidence": 0.84 + }, + { + "src": "user_cyrus", + "rel": "connected_to", + "dst": "user_gita", + "confidence": 0.83 + }, + { + "src": "user_gita", + "rel": "connected_to", + "dst": "user_jules", + "confidence": 0.82 + }, + { + "src": "user_jules", + "rel": "connected_to", + "dst": "user_bharat", + "confidence": 0.81 + }, + { + "src": "user_diya", + "rel": "connected_to", + "dst": "user_ivy", + "confidence": 0.9 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_elin", + "confidence": 0.86 + }, + { + "src": "user_kian", + "rel": "connected_to", + "dst": "user_omar", + "confidence": 0.93 + }, + { + "src": "user_omar", + "rel": "connected_to", + "dst": "user_mika", + "confidence": 0.9 + }, + { + "src": "user_mika", + "rel": "connected_to", + "dst": "user_quinn", + "confidence": 0.89 + }, + { + "src": "user_quinn", + "rel": "connected_to", + "dst": "user_nora", + "confidence": 0.88 + }, + { + "src": "user_nora", + "rel": "connected_to", + "dst": "user_rhea", + "confidence": 0.87 + }, + { + "src": "user_rhea", + "rel": "connected_to", + "dst": "user_soren", + "confidence": 0.86 + }, + { + "src": "user_soren", + "rel": "connected_to", + "dst": "user_tara", + "confidence": 0.86 + }, + { + "src": "user_tara", + "rel": "connected_to", + "dst": "user_kian", + "confidence": 0.84 + }, + { + "src": "user_priya", + "rel": "connected_to", + "dst": "user_leena", + "confidence": 0.91 + }, + { + "src": "user_leena", + "rel": "connected_to", + "dst": "user_aria", + "confidence": 0.83 + }, + { + "src": "user_priya", + "rel": "connected_to", + "dst": "user_nora", + "confidence": 0.82 + }, + { + "src": "user_kian", + "rel": "connected_to", + "dst": "user_bharat", + "confidence": 0.8 + }, + { + "src": "user_soren", + "rel": "connected_to", + "dst": "user_faris", + "confidence": 0.79 + }, + { + "src": "user_quinn", + "rel": "connected_to", + "dst": "user_hiro", + "confidence": 0.78 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "authored_post", + "dst": "post_shift_roster", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "authored_post", + "dst": "post_sat_phone_ping", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "authored_post", + "dst": "post_drone_parts", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "authored_post", + "dst": "post_relay_schedule", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "authored_post", + "dst": "post_quay_ledgers", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "authored_post", + "dst": "post_customs_tag", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "authored_post", + "dst": "post_hull_signal", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "authored_post", + "dst": "post_basin_photo", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "authored_post", + "dst": "post_foundry_map", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "authored_post", + "dst": "post_lantern_route", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "authored_post", + "dst": "post_uplink_note", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "post_shift_roster", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_shift_roster", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "post_sat_phone_ping", + "rel": "references", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "post_sat_phone_ping", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "post_drone_parts", + "rel": "references", + "dst": "event_black_kite", + "confidence": 1.0 + }, + { + "src": "post_drone_parts", + "rel": "references", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "post_relay_schedule", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "post_relay_schedule", + "rel": "references", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "event_glass_harbor", + "confidence": 1.0 + }, + { + "src": "post_customs_tag", + "rel": "references", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "post_customs_tag", + "rel": "references", + "dst": "event_iron_wharf", + "confidence": 1.0 + }, + { + "src": "post_hull_signal", + "rel": "references", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "post_hull_signal", + "rel": "references", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "post_basin_photo", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "post_basin_photo", + "rel": "references", + "dst": "event_amber_veil", + "confidence": 1.0 + }, + { + "src": "post_foundry_map", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "post_foundry_map", + "rel": "references", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "post_lantern_route", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "post_lantern_route", + "rel": "references", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "post_uplink_note", + "rel": "references", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "post_uplink_note", + "rel": "references", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "authored_thread", + "dst": "thr_supply_leak", + "confidence": 1.0 + }, + { + "src": "user_jules", + "rel": "authored_thread", + "dst": "thr_port_audit", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "authored_thread", + "dst": "thr_customs_breach", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "authored_thread", + "dst": "thr_relay_map", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "authored_thread", + "dst": "thr_foundry_watch", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "authored_thread", + "dst": "thr_basin_shift", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "authored_thread", + "dst": "thr_quiet_manifest", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "authored_thread", + "dst": "thr_uplink_route", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "authored_thread", + "dst": "thr_ember_tide_watch", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "authored_thread", + "dst": "thr_ghost_signal_net", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "thr_port_audit", + "rel": "discusses", + "dst": "event_black_kite", + "confidence": 1.0 + }, + { + "src": "thr_port_audit", + "rel": "references", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "thr_customs_breach", + "rel": "discusses", + "dst": "event_iron_wharf", + "confidence": 1.0 + }, + { + "src": "thr_customs_breach", + "rel": "references", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "thr_relay_map", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "thr_relay_map", + "rel": "references", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "thr_foundry_watch", + "rel": "discusses", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "thr_foundry_watch", + "rel": "references", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "thr_basin_shift", + "rel": "discusses", + "dst": "event_amber_veil", + "confidence": 1.0 + }, + { + "src": "thr_basin_shift", + "rel": "references", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "thr_quiet_manifest", + "rel": "discusses", + "dst": "event_glass_harbor", + "confidence": 1.0 + }, + { + "src": "thr_quiet_manifest", + "rel": "references", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "thr_uplink_route", + "rel": "discusses", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "thr_uplink_route", + "rel": "references", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "thr_ember_tide_watch", + "rel": "discusses", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "thr_ember_tide_watch", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "thr_ghost_signal_net", + "rel": "discusses", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "thr_ghost_signal_net", + "rel": "references", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_hiro", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_faris", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_diya", + "rel": "investigates", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_leena", + "rel": "monitors", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_ivy", + "rel": "collaborates_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_cyrus", + "rel": "collaborates_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_elin", + "rel": "investigates", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_jules", + "rel": "reports_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_kian", + "rel": "collaborates_on", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_omar", + "rel": "collaborates_on", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_priya", + "rel": "monitors", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_mika", + "rel": "collaborates_on", + "dst": "event_iron_wharf", + "confidence": 0.9 + }, + { + "src": "user_quinn", + "rel": "collaborates_on", + "dst": "event_iron_wharf", + "confidence": 0.9 + }, + { + "src": "user_nora", + "rel": "investigates", + "dst": "event_amber_veil", + "confidence": 0.9 + }, + { + "src": "user_rhea", + "rel": "collaborates_on", + "dst": "event_ember_tide", + "confidence": 0.9 + }, + { + "src": "user_soren", + "rel": "collaborates_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + }, + { + "src": "user_tara", + "rel": "reports_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + }, + { + "src": "user_gita", + "rel": "monitors", + "dst": "event_silent_current", + "confidence": 0.9 + }, + { + "src": "user_jules", + "rel": "reports_on", + "dst": "event_silent_current", + "confidence": 0.9 + }, + { + "src": "event_project_lantern", + "rel": "connected_to", + "dst": "event_glass_harbor", + "confidence": 0.77 + }, + { + "src": "event_black_kite", + "rel": "connected_to", + "dst": "event_amber_veil", + "confidence": 0.77 + }, + { + "src": "event_ember_tide", + "rel": "connected_to", + "dst": "event_ghost_signal", + "confidence": 0.77 + }, + { + "src": "org_atlas_freight", + "rel": "connected_to", + "dst": "org_northbridge_logistics", + "confidence": 0.77 + }, + { + "src": "org_orion_customs", + "rel": "connected_to", + "dst": "org_emberline_security", + "confidence": 0.77 + }, + { + "src": "org_harborlight_transit", + "rel": "connected_to", + "dst": "org_tidewatch_ops", + "confidence": 0.77 + } + ], + "seeded_questions": [ + { + "task_type": "fixed_trace", + "question": "alias_orchidfox -> post_midnight_manifest -> loc_dockyard17 -> connected collaborator on event_project_lantern. Who is it?", + "answer": "user_bharat", + "supporting_edges": [ + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_bharat", + "confidence": 0.95 + }, + { + "src": "user_bharat", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + } + ], + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_01", + "support_nodes": 6 + } + }, + { + "task_type": "fixed_trace", + "question": "thr_supply_leak references org_northbridge_logistics. Which alias_docksparrow user works there and collaborates on event_project_lantern?", + "answer": "user_hiro", + "supporting_edges": [ + { + "src": "thr_supply_leak", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + } + ], + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_02", + "support_nodes": 5 + } + }, + { + "task_type": "fixed_trace", + "question": "alias_monsoonbyte authored post_drone_parts about event_black_kite. Which user behind that alias is directly connected to the Kestrel collaborator?", + "answer": "user_diya", + "supporting_edges": [ + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "authored_post", + "dst": "post_drone_parts", + "confidence": 1.0 + }, + { + "src": "post_drone_parts", + "rel": "references", + "dst": "event_black_kite", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "works_at", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "collaborates_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_elin", + "confidence": 0.86 + } + ], + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_03", + "support_nodes": 7 + } + }, + { + "task_type": "fixed_trace", + "question": "alias_nightrelay references loc_rivergate. Which user behind it works at an org operating there and collaborates on event_project_lantern?", + "answer": "user_faris", + "supporting_edges": [ + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "authored_post", + "dst": "post_sat_phone_ping", + "confidence": 1.0 + }, + { + "src": "post_sat_phone_ping", + "rel": "references", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "works_at", + "dst": "org_tidewatch_ops", + "confidence": 1.0 + }, + { + "src": "org_tidewatch_ops", + "rel": "operates_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + } + ], + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_04", + "support_nodes": 6 + } + }, + { + "task_type": "fixed_trace", + "question": "thr_port_audit discusses Black Kite and references Kestrel Works. Which alias_orchidfox user authored post_midnight_manifest and collaborates on Black Kite?", + "answer": "user_ivy", + "supporting_edges": [ + { + "src": "thr_port_audit", + "rel": "discusses", + "dst": "event_black_kite", + "confidence": 1.0 + }, + { + "src": "thr_port_audit", + "rel": "references", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "works_at", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "collaborates_on", + "dst": "event_black_kite", + "confidence": 0.9 + } + ], + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_05", + "support_nodes": 6 + } + }, + { + "task_type": "fixed_trace", + "question": "Which Atlas Freight user behind alias_lanternmoth authored post_quay_ledgers and collaborates on event_glass_harbor?", + "answer": "user_kian", + "supporting_edges": [ + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "authored_post", + "dst": "post_quay_ledgers", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "works_at", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "collaborates_on", + "dst": "event_glass_harbor", + "confidence": 0.9 + } + ], + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_06", + "support_nodes": 5 + } + }, + { + "task_type": "fixed_trace", + "question": "Which Orion Customs user behind alias_basinraven authored post_customs_tag and collaborates on event_iron_wharf?", + "answer": "user_mika", + "supporting_edges": [ + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "authored_post", + "dst": "post_customs_tag", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "collaborates_on", + "dst": "event_iron_wharf", + "confidence": 0.9 + } + ], + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_07", + "support_nodes": 5 + } + }, + { + "task_type": "fixed_trace", + "question": "Which user behind alias_emberglass posted basin_photo from Foundry Row and investigates Amber Veil?", + "answer": "user_nora", + "supporting_edges": [ + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "authored_post", + "dst": "post_basin_photo", + "confidence": 1.0 + }, + { + "src": "post_basin_photo", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "investigates", + "dst": "event_amber_veil", + "confidence": 0.9 + } + ], + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_08", + "support_nodes": 5 + } + }, + { + "task_type": "fixed_trace", + "question": "Which user behind alias_tideshard authored post_hull_signal and collaborates on Ghost Signal?", + "answer": "user_soren", + "supporting_edges": [ + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "authored_post", + "dst": "post_hull_signal", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "collaborates_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + } + ], + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_09", + "support_nodes": 4 + } + }, + { + "task_type": "fixed_trace", + "question": "Which Harborlight Transit user behind alias_sablekeel authored post_uplink_note and reports on Ghost Signal?", + "answer": "user_tara", + "supporting_edges": [ + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "authored_post", + "dst": "post_uplink_note", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "reports_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + } + ], + "metadata": { + "difficulty": "easy", + "difficulty_level": 1, + "question_id": "easy_10", + "support_nodes": 5 + } + }, + { + "task_type": "fixed_trace", + "question": "Follow alias_docksparrow through post_shift_roster, Dockyard 17, and the Lantern chain. Return the org node id.", + "answer": "org_northbridge_logistics", + "supporting_edges": [ + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "authored_post", + "dst": "post_shift_roster", + "confidence": 1.0 + }, + { + "src": "post_shift_roster", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_shift_roster", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_bharat", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_bharat", + "rel": "connected_to", + "dst": "user_hiro", + "confidence": 0.95 + }, + { + "src": "user_hiro", + "rel": "connected_to", + "dst": "user_faris", + "confidence": 0.92 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "authored_post", + "dst": "post_quay_ledgers", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "event_glass_harbor", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_01", + "support_nodes": 17 + } + }, + { + "task_type": "fixed_trace", + "question": "Across the Glass Harbor cluster, which user behind alias_lanternmoth links to the Atlas Freight network from thr_quiet_manifest?", + "answer": "user_kian", + "supporting_edges": [ + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "authored_post", + "dst": "post_quay_ledgers", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "event_glass_harbor", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "authored_thread", + "dst": "thr_quiet_manifest", + "confidence": 1.0 + }, + { + "src": "thr_quiet_manifest", + "rel": "discusses", + "dst": "event_glass_harbor", + "confidence": 1.0 + }, + { + "src": "thr_quiet_manifest", + "rel": "references", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "works_at", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_omar", + "rel": "works_at", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "collaborates_on", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_omar", + "rel": "collaborates_on", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_priya", + "rel": "monitors", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_kian", + "rel": "connected_to", + "dst": "user_omar", + "confidence": 0.93 + }, + { + "src": "user_omar", + "rel": "connected_to", + "dst": "user_mika", + "confidence": 0.9 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "authored_post", + "dst": "post_customs_tag", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_02", + "support_nodes": 17 + } + }, + { + "task_type": "fixed_trace", + "question": "Trace alias_basinraven through post_customs_tag, thr_customs_breach, and the Orion Customs collaboration chain. Who is it?", + "answer": "user_mika", + "supporting_edges": [ + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "authored_post", + "dst": "post_customs_tag", + "confidence": 1.0 + }, + { + "src": "post_customs_tag", + "rel": "references", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "post_customs_tag", + "rel": "references", + "dst": "event_iron_wharf", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "authored_thread", + "dst": "thr_customs_breach", + "confidence": 1.0 + }, + { + "src": "thr_customs_breach", + "rel": "discusses", + "dst": "event_iron_wharf", + "confidence": 1.0 + }, + { + "src": "thr_customs_breach", + "rel": "references", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "collaborates_on", + "dst": "event_iron_wharf", + "confidence": 0.9 + }, + { + "src": "user_quinn", + "rel": "collaborates_on", + "dst": "event_iron_wharf", + "confidence": 0.9 + }, + { + "src": "user_mika", + "rel": "connected_to", + "dst": "user_quinn", + "confidence": 0.89 + }, + { + "src": "user_quinn", + "rel": "connected_to", + "dst": "user_nora", + "confidence": 0.88 + }, + { + "src": "org_orion_customs", + "rel": "connected_to", + "dst": "org_emberline_security", + "confidence": 0.77 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_03", + "support_nodes": 17 + } + }, + { + "task_type": "fixed_trace", + "question": "In the Ember Tide and Amber Veil overlap, which Foundry Row user behind alias_cinderveil collaborates on Ember Tide?", + "answer": "user_rhea", + "supporting_edges": [ + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "authored_post", + "dst": "post_foundry_map", + "confidence": 1.0 + }, + { + "src": "post_foundry_map", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "post_foundry_map", + "rel": "references", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "authored_thread", + "dst": "thr_foundry_watch", + "confidence": 1.0 + }, + { + "src": "thr_foundry_watch", + "rel": "discusses", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "thr_foundry_watch", + "rel": "references", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "authored_thread", + "dst": "thr_ember_tide_watch", + "confidence": 1.0 + }, + { + "src": "thr_ember_tide_watch", + "rel": "discusses", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "thr_ember_tide_watch", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "investigates", + "dst": "event_amber_veil", + "confidence": 0.9 + }, + { + "src": "user_rhea", + "rel": "collaborates_on", + "dst": "event_ember_tide", + "confidence": 0.9 + }, + { + "src": "user_nora", + "rel": "connected_to", + "dst": "user_rhea", + "confidence": 0.87 + }, + { + "src": "event_ember_tide", + "rel": "connected_to", + "dst": "event_ghost_signal", + "confidence": 0.77 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_04", + "support_nodes": 18 + } + }, + { + "task_type": "fixed_trace", + "question": "Follow alias_tideshard from post_hull_signal into thr_uplink_route and the Harborlight relay. Return the org node id.", + "answer": "org_harborlight_transit", + "supporting_edges": [ + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "authored_post", + "dst": "post_hull_signal", + "confidence": 1.0 + }, + { + "src": "post_hull_signal", + "rel": "references", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "post_hull_signal", + "rel": "references", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "authored_thread", + "dst": "thr_uplink_route", + "confidence": 1.0 + }, + { + "src": "thr_uplink_route", + "rel": "discusses", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "thr_uplink_route", + "rel": "references", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "collaborates_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + }, + { + "src": "user_tara", + "rel": "reports_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + }, + { + "src": "user_rhea", + "rel": "connected_to", + "dst": "user_soren", + "confidence": 0.86 + }, + { + "src": "user_soren", + "rel": "connected_to", + "dst": "user_tara", + "confidence": 0.86 + }, + { + "src": "org_harborlight_transit", + "rel": "operates_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "org_harborlight_transit", + "rel": "connected_to", + "dst": "org_tidewatch_ops", + "confidence": 0.77 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_05", + "support_nodes": 17 + } + }, + { + "task_type": "fixed_trace", + "question": "Which Sunmesh user behind alias_frostledger connects post_lantern_route to thr_relay_map and the Sector 9 monitoring chain?", + "answer": "user_leena", + "supporting_edges": [ + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "authored_post", + "dst": "post_lantern_route", + "confidence": 1.0 + }, + { + "src": "post_lantern_route", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "post_lantern_route", + "rel": "references", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "authored_thread", + "dst": "thr_relay_map", + "confidence": 1.0 + }, + { + "src": "thr_relay_map", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "thr_relay_map", + "rel": "references", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "works_at", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_priya", + "rel": "works_at", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "org_sunmesh_analytics", + "rel": "operates_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "monitors", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_priya", + "rel": "connected_to", + "dst": "user_leena", + "confidence": 0.91 + }, + { + "src": "user_leena", + "rel": "connected_to", + "dst": "user_aria", + "confidence": 0.83 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "authored_post", + "dst": "post_quay_ledgers", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "loc_east_quay", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_06", + "support_nodes": 17 + } + }, + { + "task_type": "fixed_trace", + "question": "Which user behind alias_emberglass is tied to Amber Veil after combining post_basin_photo, thr_basin_shift, and the Foundry Row investigation chain?", + "answer": "user_nora", + "supporting_edges": [ + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "authored_post", + "dst": "post_basin_photo", + "confidence": 1.0 + }, + { + "src": "post_basin_photo", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "post_basin_photo", + "rel": "references", + "dst": "event_amber_veil", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "authored_thread", + "dst": "thr_basin_shift", + "confidence": 1.0 + }, + { + "src": "thr_basin_shift", + "rel": "discusses", + "dst": "event_amber_veil", + "confidence": 1.0 + }, + { + "src": "thr_basin_shift", + "rel": "references", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "investigates", + "dst": "event_amber_veil", + "confidence": 0.9 + }, + { + "src": "user_quinn", + "rel": "connected_to", + "dst": "user_nora", + "confidence": 0.88 + }, + { + "src": "user_nora", + "rel": "connected_to", + "dst": "user_rhea", + "confidence": 0.87 + }, + { + "src": "org_orion_customs", + "rel": "connected_to", + "dst": "org_emberline_security", + "confidence": 0.77 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_07", + "support_nodes": 18 + } + }, + { + "task_type": "fixed_trace", + "question": "Combine alias_orchidfox, post_midnight_manifest, thr_supply_leak, and the Lantern to Glass Harbor bridge. Which user starts that chain?", + "answer": "user_ivy", + "supporting_edges": [ + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "authored_thread", + "dst": "thr_supply_leak", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "works_at", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "collaborates_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_bharat", + "confidence": 0.95 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_elin", + "confidence": 0.86 + }, + { + "src": "event_project_lantern", + "rel": "connected_to", + "dst": "event_glass_harbor", + "confidence": 0.77 + }, + { + "src": "user_kian", + "rel": "collaborates_on", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_priya", + "rel": "monitors", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "authored_post", + "dst": "post_quay_ledgers", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_08", + "support_nodes": 17 + } + }, + { + "task_type": "fixed_trace", + "question": "Which user behind alias_monsoonbyte sits at the overlap of Blueharbor Media, Project Lantern, Black Kite, and the Ivy connection chain?", + "answer": "user_diya", + "supporting_edges": [ + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "authored_post", + "dst": "post_drone_parts", + "confidence": 1.0 + }, + { + "src": "post_drone_parts", + "rel": "references", + "dst": "event_black_kite", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "authored_thread", + "dst": "thr_supply_leak", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "user_jules", + "rel": "authored_thread", + "dst": "thr_port_audit", + "confidence": 1.0 + }, + { + "src": "thr_port_audit", + "rel": "discusses", + "dst": "event_black_kite", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "works_at", + "dst": "org_blueharbor_media", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "works_at", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "user_jules", + "rel": "works_at", + "dst": "org_blueharbor_media", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "investigates", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_ivy", + "rel": "collaborates_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_jules", + "rel": "reports_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_faris", + "rel": "connected_to", + "dst": "user_diya", + "confidence": 0.9 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_elin", + "confidence": 0.86 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_09", + "support_nodes": 18 + } + }, + { + "task_type": "fixed_trace", + "question": "Who is the Northbridge user behind alias_steelquill when combining post_relay_schedule, thr_supply_leak, Dockyard 17, and Lantern collaborator edges?", + "answer": "user_bharat", + "supporting_edges": [ + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "authored_post", + "dst": "post_relay_schedule", + "confidence": 1.0 + }, + { + "src": "post_relay_schedule", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "post_relay_schedule", + "rel": "references", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "authored_thread", + "dst": "thr_supply_leak", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_hiro", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_bharat", + "confidence": 0.95 + }, + { + "src": "user_bharat", + "rel": "connected_to", + "dst": "user_hiro", + "confidence": 0.95 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "authored_post", + "dst": "post_quay_ledgers", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "loc_east_quay", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "mid", + "difficulty_level": 2, + "question_id": "mid_10", + "support_nodes": 17 + } + }, + { + "task_type": "fixed_trace", + "question": "Lantern to Glass Harbor handoff: identify the user behind alias_orchidfox after combining Lantern logistics, Dockyard links, and Atlas Freight bridge evidence.", + "answer": "user_ivy", + "supporting_edges": [ + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "authored_thread", + "dst": "thr_supply_leak", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "works_at", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "works_at", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_omar", + "rel": "works_at", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "located_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "located_in", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "user_omar", + "rel": "located_in", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "org_northbridge_logistics", + "rel": "operates_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "org_kestrel_works", + "rel": "operates_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "org_atlas_freight", + "rel": "operates_in", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_bharat", + "confidence": 0.95 + }, + { + "src": "user_bharat", + "rel": "connected_to", + "dst": "user_hiro", + "confidence": 0.95 + }, + { + "src": "user_hiro", + "rel": "connected_to", + "dst": "user_faris", + "confidence": 0.92 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_elin", + "confidence": 0.86 + }, + { + "src": "user_kian", + "rel": "connected_to", + "dst": "user_omar", + "confidence": 0.93 + }, + { + "src": "user_omar", + "rel": "connected_to", + "dst": "user_mika", + "confidence": 0.9 + }, + { + "src": "user_bharat", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_hiro", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_faris", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_ivy", + "rel": "collaborates_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_kian", + "rel": "collaborates_on", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_omar", + "rel": "collaborates_on", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_priya", + "rel": "monitors", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_kian", + "rel": "authored_thread", + "dst": "thr_quiet_manifest", + "confidence": 1.0 + }, + { + "src": "thr_quiet_manifest", + "rel": "discusses", + "dst": "event_glass_harbor", + "confidence": 1.0 + }, + { + "src": "thr_quiet_manifest", + "rel": "references", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "authored_post", + "dst": "post_quay_ledgers", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "event_glass_harbor", + "confidence": 1.0 + }, + { + "src": "event_project_lantern", + "rel": "connected_to", + "dst": "event_glass_harbor", + "confidence": 0.77 + }, + { + "src": "org_atlas_freight", + "rel": "connected_to", + "dst": "org_northbridge_logistics", + "confidence": 0.77 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_mapleghost", + "rel": "alias_of", + "dst": "user_elin", + "confidence": 1.0 + }, + { + "src": "alias_quartzlotus", + "rel": "alias_of", + "dst": "user_cyrus", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_hollowsignal", + "rel": "alias_of", + "dst": "user_priya", + "confidence": 1.0 + }, + { + "src": "alias_ironwhisper", + "rel": "alias_of", + "dst": "user_omar", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "works_at", + "dst": "org_apex_dynamics", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_01", + "support_nodes": 50 + } + }, + { + "task_type": "fixed_trace", + "question": "North Basin to Foundry Row escalation: which user behind alias_basinraven anchors the Iron Wharf side before the Emberline handoff?", + "answer": "user_mika", + "supporting_edges": [ + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "authored_post", + "dst": "post_customs_tag", + "confidence": 1.0 + }, + { + "src": "post_customs_tag", + "rel": "references", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "post_customs_tag", + "rel": "references", + "dst": "event_iron_wharf", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "authored_thread", + "dst": "thr_customs_breach", + "confidence": 1.0 + }, + { + "src": "thr_customs_breach", + "rel": "discusses", + "dst": "event_iron_wharf", + "confidence": 1.0 + }, + { + "src": "thr_customs_breach", + "rel": "references", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "authored_thread", + "dst": "thr_basin_shift", + "confidence": 1.0 + }, + { + "src": "thr_basin_shift", + "rel": "discusses", + "dst": "event_amber_veil", + "confidence": 1.0 + }, + { + "src": "thr_basin_shift", + "rel": "references", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "authored_thread", + "dst": "thr_foundry_watch", + "confidence": 1.0 + }, + { + "src": "thr_foundry_watch", + "rel": "discusses", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "thr_foundry_watch", + "rel": "references", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "located_in", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "located_in", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "org_orion_customs", + "rel": "operates_in", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "org_emberline_security", + "rel": "operates_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "connected_to", + "dst": "user_quinn", + "confidence": 0.89 + }, + { + "src": "user_quinn", + "rel": "connected_to", + "dst": "user_nora", + "confidence": 0.88 + }, + { + "src": "user_nora", + "rel": "connected_to", + "dst": "user_rhea", + "confidence": 0.87 + }, + { + "src": "user_mika", + "rel": "collaborates_on", + "dst": "event_iron_wharf", + "confidence": 0.9 + }, + { + "src": "user_quinn", + "rel": "collaborates_on", + "dst": "event_iron_wharf", + "confidence": 0.9 + }, + { + "src": "user_nora", + "rel": "investigates", + "dst": "event_amber_veil", + "confidence": 0.9 + }, + { + "src": "user_rhea", + "rel": "collaborates_on", + "dst": "event_ember_tide", + "confidence": 0.9 + }, + { + "src": "alias_emberglass", + "rel": "authored_post", + "dst": "post_basin_photo", + "confidence": 1.0 + }, + { + "src": "post_basin_photo", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "post_basin_photo", + "rel": "references", + "dst": "event_amber_veil", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "authored_post", + "dst": "post_foundry_map", + "confidence": 1.0 + }, + { + "src": "post_foundry_map", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "post_foundry_map", + "rel": "references", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "event_black_kite", + "rel": "connected_to", + "dst": "event_amber_veil", + "confidence": 0.77 + }, + { + "src": "event_ember_tide", + "rel": "connected_to", + "dst": "event_ghost_signal", + "confidence": 0.77 + }, + { + "src": "org_orion_customs", + "rel": "connected_to", + "dst": "org_emberline_security", + "confidence": 0.77 + }, + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "user_priya", + "rel": "connected_to", + "dst": "user_nora", + "confidence": 0.82 + }, + { + "src": "user_kian", + "rel": "connected_to", + "dst": "user_bharat", + "confidence": 0.8 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_mapleghost", + "rel": "alias_of", + "dst": "user_elin", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_quartzlotus", + "rel": "alias_of", + "dst": "user_cyrus", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_hollowsignal", + "rel": "alias_of", + "dst": "user_priya", + "confidence": 1.0 + }, + { + "src": "alias_ironwhisper", + "rel": "alias_of", + "dst": "user_omar", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_02", + "support_nodes": 50 + } + }, + { + "task_type": "fixed_trace", + "question": "Harborlight ghost-signal relay: identify the user behind alias_tideshard at the Harborlight / Tidewatch junction.", + "answer": "user_soren", + "supporting_edges": [ + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "authored_post", + "dst": "post_hull_signal", + "confidence": 1.0 + }, + { + "src": "post_hull_signal", + "rel": "references", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "post_hull_signal", + "rel": "references", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "authored_post", + "dst": "post_uplink_note", + "confidence": 1.0 + }, + { + "src": "post_uplink_note", + "rel": "references", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "post_uplink_note", + "rel": "references", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "authored_thread", + "dst": "thr_uplink_route", + "confidence": 1.0 + }, + { + "src": "thr_uplink_route", + "rel": "discusses", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "thr_uplink_route", + "rel": "references", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "authored_thread", + "dst": "thr_ghost_signal_net", + "confidence": 1.0 + }, + { + "src": "thr_ghost_signal_net", + "rel": "discusses", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "thr_ghost_signal_net", + "rel": "references", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "works_at", + "dst": "org_tidewatch_ops", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "located_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "located_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "located_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "org_harborlight_transit", + "rel": "operates_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "org_tidewatch_ops", + "rel": "operates_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "connected_to", + "dst": "user_soren", + "confidence": 0.86 + }, + { + "src": "user_soren", + "rel": "connected_to", + "dst": "user_tara", + "confidence": 0.86 + }, + { + "src": "user_tara", + "rel": "connected_to", + "dst": "user_kian", + "confidence": 0.84 + }, + { + "src": "user_soren", + "rel": "connected_to", + "dst": "user_faris", + "confidence": 0.79 + }, + { + "src": "user_faris", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_soren", + "rel": "collaborates_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + }, + { + "src": "user_tara", + "rel": "reports_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + }, + { + "src": "alias_nightrelay", + "rel": "authored_post", + "dst": "post_sat_phone_ping", + "confidence": 1.0 + }, + { + "src": "post_sat_phone_ping", + "rel": "references", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "post_sat_phone_ping", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "authored_thread", + "dst": "thr_supply_leak", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_hiro", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "event_ember_tide", + "rel": "connected_to", + "dst": "event_ghost_signal", + "confidence": 0.77 + }, + { + "src": "org_harborlight_transit", + "rel": "connected_to", + "dst": "org_tidewatch_ops", + "confidence": 0.77 + }, + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_mapleghost", + "rel": "alias_of", + "dst": "user_elin", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_quartzlotus", + "rel": "alias_of", + "dst": "user_cyrus", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_hollowsignal", + "rel": "alias_of", + "dst": "user_priya", + "confidence": 1.0 + }, + { + "src": "alias_ironwhisper", + "rel": "alias_of", + "dst": "user_omar", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_03", + "support_nodes": 50 + } + }, + { + "task_type": "fixed_trace", + "question": "Blueharbor to Black Kite to Lantern overlap: which user is the Blueharbor origin behind alias_monsoonbyte?", + "answer": "user_diya", + "supporting_edges": [ + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "authored_post", + "dst": "post_drone_parts", + "confidence": 1.0 + }, + { + "src": "post_drone_parts", + "rel": "references", + "dst": "event_black_kite", + "confidence": 1.0 + }, + { + "src": "post_drone_parts", + "rel": "references", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "user_jules", + "rel": "authored_thread", + "dst": "thr_port_audit", + "confidence": 1.0 + }, + { + "src": "thr_port_audit", + "rel": "discusses", + "dst": "event_black_kite", + "confidence": 1.0 + }, + { + "src": "thr_port_audit", + "rel": "references", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "authored_thread", + "dst": "thr_supply_leak", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "works_at", + "dst": "org_blueharbor_media", + "confidence": 1.0 + }, + { + "src": "user_jules", + "rel": "works_at", + "dst": "org_blueharbor_media", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "works_at", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "works_at", + "dst": "org_apex_dynamics", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_jules", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "located_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "org_blueharbor_media", + "rel": "operates_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "org_kestrel_works", + "rel": "operates_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "org_apex_dynamics", + "rel": "operates_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "connected_to", + "dst": "user_diya", + "confidence": 0.9 + }, + { + "src": "user_cyrus", + "rel": "connected_to", + "dst": "user_gita", + "confidence": 0.83 + }, + { + "src": "user_gita", + "rel": "connected_to", + "dst": "user_jules", + "confidence": 0.82 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_elin", + "confidence": 0.86 + }, + { + "src": "user_diya", + "rel": "investigates", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_ivy", + "rel": "collaborates_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_cyrus", + "rel": "collaborates_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_elin", + "rel": "investigates", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_jules", + "rel": "reports_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "event_project_lantern", + "rel": "connected_to", + "dst": "event_glass_harbor", + "confidence": 0.77 + }, + { + "src": "event_black_kite", + "rel": "connected_to", + "dst": "event_amber_veil", + "confidence": 0.77 + }, + { + "src": "user_leena", + "rel": "authored_thread", + "dst": "thr_relay_map", + "confidence": 1.0 + }, + { + "src": "thr_relay_map", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "works_at", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "monitors", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_mapleghost", + "rel": "alias_of", + "dst": "user_elin", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_quartzlotus", + "rel": "alias_of", + "dst": "user_cyrus", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_hollowsignal", + "rel": "alias_of", + "dst": "user_priya", + "confidence": 1.0 + }, + { + "src": "alias_ironwhisper", + "rel": "alias_of", + "dst": "user_omar", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_04", + "support_nodes": 50 + } + }, + { + "task_type": "fixed_trace", + "question": "Sector 9 to Dockyard 17 full relay: which user behind alias_steelquill links the Northbridge chain and the Sunmesh monitoring bridge?", + "answer": "user_bharat", + "supporting_edges": [ + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "authored_post", + "dst": "post_relay_schedule", + "confidence": 1.0 + }, + { + "src": "post_relay_schedule", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "post_relay_schedule", + "rel": "references", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "authored_post", + "dst": "post_lantern_route", + "confidence": 1.0 + }, + { + "src": "post_lantern_route", + "rel": "references", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "post_lantern_route", + "rel": "references", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "authored_thread", + "dst": "thr_relay_map", + "confidence": 1.0 + }, + { + "src": "thr_relay_map", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "thr_relay_map", + "rel": "references", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "authored_thread", + "dst": "thr_supply_leak", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "discusses", + "dst": "event_project_lantern", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "works_at", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_priya", + "rel": "works_at", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_priya", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "org_northbridge_logistics", + "rel": "operates_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "org_sunmesh_analytics", + "rel": "operates_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "org_helios_labs", + "rel": "operates_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_bharat", + "confidence": 0.95 + }, + { + "src": "user_bharat", + "rel": "connected_to", + "dst": "user_hiro", + "confidence": 0.95 + }, + { + "src": "user_diya", + "rel": "connected_to", + "dst": "user_elin", + "confidence": 0.89 + }, + { + "src": "user_elin", + "rel": "connected_to", + "dst": "user_aria", + "confidence": 0.87 + }, + { + "src": "user_aria", + "rel": "connected_to", + "dst": "user_cyrus", + "confidence": 0.84 + }, + { + "src": "user_priya", + "rel": "connected_to", + "dst": "user_leena", + "confidence": 0.91 + }, + { + "src": "user_leena", + "rel": "connected_to", + "dst": "user_aria", + "confidence": 0.83 + }, + { + "src": "user_bharat", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_hiro", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_leena", + "rel": "monitors", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "event_project_lantern", + "rel": "connected_to", + "dst": "event_glass_harbor", + "confidence": 0.77 + }, + { + "src": "org_atlas_freight", + "rel": "connected_to", + "dst": "org_northbridge_logistics", + "confidence": 0.77 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_mapleghost", + "rel": "alias_of", + "dst": "user_elin", + "confidence": 1.0 + }, + { + "src": "alias_hollowsignal", + "rel": "alias_of", + "dst": "user_priya", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_quartzlotus", + "rel": "alias_of", + "dst": "user_cyrus", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_ironwhisper", + "rel": "alias_of", + "dst": "user_omar", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "works_at", + "dst": "org_apex_dynamics", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "works_at", + "dst": "org_blueharbor_media", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_elin", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_elin", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "works_at", + "dst": "org_tidewatch_ops", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "located_in", + "dst": "loc_rivergate", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_05", + "support_nodes": 50 + } + }, + { + "task_type": "fixed_trace", + "question": "Foundry Row, North Basin, and Uplink Yard spread: identify the user behind alias_emberglass before the Harborlight relay takes over.", + "answer": "user_nora", + "supporting_edges": [ + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "authored_post", + "dst": "post_basin_photo", + "confidence": 1.0 + }, + { + "src": "post_basin_photo", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "post_basin_photo", + "rel": "references", + "dst": "event_amber_veil", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "authored_post", + "dst": "post_foundry_map", + "confidence": 1.0 + }, + { + "src": "post_foundry_map", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "post_foundry_map", + "rel": "references", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "authored_post", + "dst": "post_uplink_note", + "confidence": 1.0 + }, + { + "src": "post_uplink_note", + "rel": "references", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "post_uplink_note", + "rel": "references", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "authored_thread", + "dst": "thr_foundry_watch", + "confidence": 1.0 + }, + { + "src": "thr_foundry_watch", + "rel": "discusses", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "thr_foundry_watch", + "rel": "references", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "authored_thread", + "dst": "thr_ember_tide_watch", + "confidence": 1.0 + }, + { + "src": "thr_ember_tide_watch", + "rel": "discusses", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "thr_ember_tide_watch", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "authored_thread", + "dst": "thr_uplink_route", + "confidence": 1.0 + }, + { + "src": "thr_uplink_route", + "rel": "discusses", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "thr_uplink_route", + "rel": "references", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "located_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "located_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "org_emberline_security", + "rel": "operates_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "org_harborlight_transit", + "rel": "operates_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "connected_to", + "dst": "user_rhea", + "confidence": 0.87 + }, + { + "src": "user_rhea", + "rel": "connected_to", + "dst": "user_soren", + "confidence": 0.86 + }, + { + "src": "user_soren", + "rel": "connected_to", + "dst": "user_tara", + "confidence": 0.86 + }, + { + "src": "user_nora", + "rel": "investigates", + "dst": "event_amber_veil", + "confidence": 0.9 + }, + { + "src": "user_rhea", + "rel": "collaborates_on", + "dst": "event_ember_tide", + "confidence": 0.9 + }, + { + "src": "user_soren", + "rel": "collaborates_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + }, + { + "src": "user_tara", + "rel": "reports_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + }, + { + "src": "event_ember_tide", + "rel": "connected_to", + "dst": "event_ghost_signal", + "confidence": 0.77 + }, + { + "src": "org_harborlight_transit", + "rel": "connected_to", + "dst": "org_tidewatch_ops", + "confidence": 0.77 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_mapleghost", + "rel": "alias_of", + "dst": "user_elin", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_quartzlotus", + "rel": "alias_of", + "dst": "user_cyrus", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_hollowsignal", + "rel": "alias_of", + "dst": "user_priya", + "confidence": 1.0 + }, + { + "src": "alias_ironwhisper", + "rel": "alias_of", + "dst": "user_omar", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_06", + "support_nodes": 50 + } + }, + { + "task_type": "fixed_trace", + "question": "Freight and customs bridge: which Atlas Freight user behind alias_lanternmoth connects Glass Harbor with the Northbridge chain?", + "answer": "user_kian", + "supporting_edges": [ + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "authored_post", + "dst": "post_quay_ledgers", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "post_quay_ledgers", + "rel": "references", + "dst": "event_glass_harbor", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "authored_thread", + "dst": "thr_quiet_manifest", + "confidence": 1.0 + }, + { + "src": "thr_quiet_manifest", + "rel": "discusses", + "dst": "event_glass_harbor", + "confidence": 1.0 + }, + { + "src": "thr_quiet_manifest", + "rel": "references", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "works_at", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_omar", + "rel": "works_at", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "located_in", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "user_omar", + "rel": "located_in", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "org_atlas_freight", + "rel": "operates_in", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "org_northbridge_logistics", + "rel": "operates_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "connected_to", + "dst": "user_omar", + "confidence": 0.93 + }, + { + "src": "user_omar", + "rel": "connected_to", + "dst": "user_mika", + "confidence": 0.9 + }, + { + "src": "user_kian", + "rel": "connected_to", + "dst": "user_bharat", + "confidence": 0.8 + }, + { + "src": "user_bharat", + "rel": "connected_to", + "dst": "user_hiro", + "confidence": 0.95 + }, + { + "src": "user_kian", + "rel": "collaborates_on", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_omar", + "rel": "collaborates_on", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_priya", + "rel": "monitors", + "dst": "event_glass_harbor", + "confidence": 0.9 + }, + { + "src": "user_bharat", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "user_hiro", + "rel": "collaborates_on", + "dst": "event_project_lantern", + "confidence": 0.9 + }, + { + "src": "alias_docksparrow", + "rel": "authored_post", + "dst": "post_shift_roster", + "confidence": 1.0 + }, + { + "src": "post_shift_roster", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "post_shift_roster", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "authored_post", + "dst": "post_midnight_manifest", + "confidence": 1.0 + }, + { + "src": "post_midnight_manifest", + "rel": "references", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "authored_thread", + "dst": "thr_supply_leak", + "confidence": 1.0 + }, + { + "src": "thr_supply_leak", + "rel": "references", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "org_atlas_freight", + "rel": "connected_to", + "dst": "org_northbridge_logistics", + "confidence": 0.77 + }, + { + "src": "alias_ironwhisper", + "rel": "alias_of", + "dst": "user_omar", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_mapleghost", + "rel": "alias_of", + "dst": "user_elin", + "confidence": 1.0 + }, + { + "src": "alias_quartzlotus", + "rel": "alias_of", + "dst": "user_cyrus", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_hollowsignal", + "rel": "alias_of", + "dst": "user_priya", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "works_at", + "dst": "org_apex_dynamics", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "works_at", + "dst": "org_blueharbor_media", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_elin", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_elin", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "works_at", + "dst": "org_tidewatch_ops", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_07", + "support_nodes": 50 + } + }, + { + "task_type": "fixed_trace", + "question": "Black Kite, Amber Veil, and Iron Wharf overlap: which user behind alias_quartzlotus is the Apex-side collaborator?", + "answer": "user_cyrus", + "supporting_edges": [ + { + "src": "alias_quartzlotus", + "rel": "alias_of", + "dst": "user_cyrus", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "works_at", + "dst": "org_apex_dynamics", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "org_apex_dynamics", + "rel": "operates_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "collaborates_on", + "dst": "event_black_kite", + "confidence": 0.9 + }, + { + "src": "user_jules", + "rel": "authored_thread", + "dst": "thr_port_audit", + "confidence": 1.0 + }, + { + "src": "thr_port_audit", + "rel": "discusses", + "dst": "event_black_kite", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "authored_post", + "dst": "post_drone_parts", + "confidence": 1.0 + }, + { + "src": "post_drone_parts", + "rel": "references", + "dst": "event_black_kite", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "investigates", + "dst": "event_amber_veil", + "confidence": 0.9 + }, + { + "src": "user_quinn", + "rel": "authored_thread", + "dst": "thr_basin_shift", + "confidence": 1.0 + }, + { + "src": "thr_basin_shift", + "rel": "discusses", + "dst": "event_amber_veil", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "collaborates_on", + "dst": "event_iron_wharf", + "confidence": 0.9 + }, + { + "src": "user_mika", + "rel": "authored_thread", + "dst": "thr_customs_breach", + "confidence": 1.0 + }, + { + "src": "thr_customs_breach", + "rel": "discusses", + "dst": "event_iron_wharf", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "works_at", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "located_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "located_in", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "located_in", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "org_kestrel_works", + "rel": "operates_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "org_emberline_security", + "rel": "operates_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "org_orion_customs", + "rel": "operates_in", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "connected_to", + "dst": "user_gita", + "confidence": 0.83 + }, + { + "src": "user_ivy", + "rel": "connected_to", + "dst": "user_elin", + "confidence": 0.86 + }, + { + "src": "user_mika", + "rel": "connected_to", + "dst": "user_quinn", + "confidence": 0.89 + }, + { + "src": "user_quinn", + "rel": "connected_to", + "dst": "user_nora", + "confidence": 0.88 + }, + { + "src": "user_nora", + "rel": "connected_to", + "dst": "user_rhea", + "confidence": 0.87 + }, + { + "src": "event_black_kite", + "rel": "connected_to", + "dst": "event_amber_veil", + "confidence": 0.77 + }, + { + "src": "org_orion_customs", + "rel": "connected_to", + "dst": "org_emberline_security", + "confidence": 0.77 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_mapleghost", + "rel": "alias_of", + "dst": "user_elin", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_hollowsignal", + "rel": "alias_of", + "dst": "user_priya", + "confidence": 1.0 + }, + { + "src": "alias_ironwhisper", + "rel": "alias_of", + "dst": "user_omar", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_08", + "support_nodes": 50 + } + }, + { + "task_type": "fixed_trace", + "question": "Ghost Signal and Ember Tide relay: which user behind alias_sablekeel is the Harborlight reporting endpoint?", + "answer": "user_tara", + "supporting_edges": [ + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "authored_post", + "dst": "post_uplink_note", + "confidence": 1.0 + }, + { + "src": "post_uplink_note", + "rel": "references", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "post_uplink_note", + "rel": "references", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "authored_post", + "dst": "post_hull_signal", + "confidence": 1.0 + }, + { + "src": "post_hull_signal", + "rel": "references", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "post_hull_signal", + "rel": "references", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "authored_thread", + "dst": "thr_ghost_signal_net", + "confidence": 1.0 + }, + { + "src": "thr_ghost_signal_net", + "rel": "discusses", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "thr_ghost_signal_net", + "rel": "references", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "authored_thread", + "dst": "thr_uplink_route", + "confidence": 1.0 + }, + { + "src": "thr_uplink_route", + "rel": "discusses", + "dst": "event_ghost_signal", + "confidence": 1.0 + }, + { + "src": "thr_uplink_route", + "rel": "references", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "authored_thread", + "dst": "thr_ember_tide_watch", + "confidence": 1.0 + }, + { + "src": "thr_ember_tide_watch", + "rel": "discusses", + "dst": "event_ember_tide", + "confidence": 1.0 + }, + { + "src": "thr_ember_tide_watch", + "rel": "references", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "located_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "located_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "org_harborlight_transit", + "rel": "operates_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "org_emberline_security", + "rel": "operates_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "connected_to", + "dst": "user_soren", + "confidence": 0.86 + }, + { + "src": "user_soren", + "rel": "connected_to", + "dst": "user_tara", + "confidence": 0.86 + }, + { + "src": "user_nora", + "rel": "connected_to", + "dst": "user_rhea", + "confidence": 0.87 + }, + { + "src": "user_rhea", + "rel": "collaborates_on", + "dst": "event_ember_tide", + "confidence": 0.9 + }, + { + "src": "user_soren", + "rel": "collaborates_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + }, + { + "src": "user_tara", + "rel": "reports_on", + "dst": "event_ghost_signal", + "confidence": 0.9 + }, + { + "src": "event_ember_tide", + "rel": "connected_to", + "dst": "event_ghost_signal", + "confidence": 0.77 + }, + { + "src": "org_harborlight_transit", + "rel": "connected_to", + "dst": "org_tidewatch_ops", + "confidence": 0.77 + }, + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_mapleghost", + "rel": "alias_of", + "dst": "user_elin", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_quartzlotus", + "rel": "alias_of", + "dst": "user_cyrus", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_hollowsignal", + "rel": "alias_of", + "dst": "user_priya", + "confidence": 1.0 + }, + { + "src": "alias_ironwhisper", + "rel": "alias_of", + "dst": "user_omar", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "works_at", + "dst": "org_apex_dynamics", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_09", + "support_nodes": 50 + } + }, + { + "task_type": "fixed_trace", + "question": "End-to-end benchmark sweep: across Lantern, Black Kite, Glass Harbor, Iron Wharf, Ember Tide, and Ghost Signal, which user behind alias_hollowsignal anchors the Sunmesh monitoring side?", + "answer": "user_priya", + "supporting_edges": [ + { + "src": "alias_orchidfox", + "rel": "alias_of", + "dst": "user_ivy", + "confidence": 1.0 + }, + { + "src": "alias_steelquill", + "rel": "alias_of", + "dst": "user_bharat", + "confidence": 1.0 + }, + { + "src": "alias_monsoonbyte", + "rel": "alias_of", + "dst": "user_diya", + "confidence": 1.0 + }, + { + "src": "alias_nightrelay", + "rel": "alias_of", + "dst": "user_faris", + "confidence": 1.0 + }, + { + "src": "alias_mapleghost", + "rel": "alias_of", + "dst": "user_elin", + "confidence": 1.0 + }, + { + "src": "alias_docksparrow", + "rel": "alias_of", + "dst": "user_hiro", + "confidence": 1.0 + }, + { + "src": "alias_quartzlotus", + "rel": "alias_of", + "dst": "user_cyrus", + "confidence": 1.0 + }, + { + "src": "alias_emberglass", + "rel": "alias_of", + "dst": "user_nora", + "confidence": 1.0 + }, + { + "src": "alias_basinraven", + "rel": "alias_of", + "dst": "user_mika", + "confidence": 1.0 + }, + { + "src": "alias_tideshard", + "rel": "alias_of", + "dst": "user_soren", + "confidence": 1.0 + }, + { + "src": "alias_hollowsignal", + "rel": "alias_of", + "dst": "user_priya", + "confidence": 1.0 + }, + { + "src": "alias_ironwhisper", + "rel": "alias_of", + "dst": "user_omar", + "confidence": 1.0 + }, + { + "src": "alias_cinderveil", + "rel": "alias_of", + "dst": "user_rhea", + "confidence": 1.0 + }, + { + "src": "alias_sablekeel", + "rel": "alias_of", + "dst": "user_tara", + "confidence": 1.0 + }, + { + "src": "alias_lanternmoth", + "rel": "alias_of", + "dst": "user_kian", + "confidence": 1.0 + }, + { + "src": "alias_frostledger", + "rel": "alias_of", + "dst": "user_leena", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_aria", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_bharat", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "works_at", + "dst": "org_apex_dynamics", + "confidence": 1.0 + }, + { + "src": "user_cyrus", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "works_at", + "dst": "org_blueharbor_media", + "confidence": 1.0 + }, + { + "src": "user_diya", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_elin", + "rel": "works_at", + "dst": "org_helios_labs", + "confidence": 1.0 + }, + { + "src": "user_elin", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "works_at", + "dst": "org_tidewatch_ops", + "confidence": 1.0 + }, + { + "src": "user_faris", + "rel": "located_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "user_gita", + "rel": "works_at", + "dst": "org_apex_dynamics", + "confidence": 1.0 + }, + { + "src": "user_gita", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "works_at", + "dst": "org_northbridge_logistics", + "confidence": 1.0 + }, + { + "src": "user_hiro", + "rel": "located_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "works_at", + "dst": "org_kestrel_works", + "confidence": 1.0 + }, + { + "src": "user_ivy", + "rel": "located_in", + "dst": "loc_rivergate", + "confidence": 1.0 + }, + { + "src": "user_jules", + "rel": "works_at", + "dst": "org_blueharbor_media", + "confidence": 1.0 + }, + { + "src": "user_jules", + "rel": "located_in", + "dst": "loc_old_town", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "works_at", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_kian", + "rel": "located_in", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "works_at", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_leena", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_mika", + "rel": "located_in", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_nora", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_omar", + "rel": "works_at", + "dst": "org_atlas_freight", + "confidence": 1.0 + }, + { + "src": "user_omar", + "rel": "located_in", + "dst": "loc_east_quay", + "confidence": 1.0 + }, + { + "src": "user_priya", + "rel": "works_at", + "dst": "org_sunmesh_analytics", + "confidence": 1.0 + }, + { + "src": "user_priya", + "rel": "located_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "works_at", + "dst": "org_orion_customs", + "confidence": 1.0 + }, + { + "src": "user_quinn", + "rel": "located_in", + "dst": "loc_north_basin", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "works_at", + "dst": "org_emberline_security", + "confidence": 1.0 + }, + { + "src": "user_rhea", + "rel": "located_in", + "dst": "loc_foundry_row", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_soren", + "rel": "located_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "works_at", + "dst": "org_harborlight_transit", + "confidence": 1.0 + }, + { + "src": "user_tara", + "rel": "located_in", + "dst": "loc_uplink_yard", + "confidence": 1.0 + }, + { + "src": "org_helios_labs", + "rel": "operates_in", + "dst": "loc_sector9", + "confidence": 1.0 + }, + { + "src": "org_northbridge_logistics", + "rel": "operates_in", + "dst": "loc_dockyard17", + "confidence": 1.0 + } + ], + "metadata": { + "difficulty": "high", + "difficulty_level": 3, + "question_id": "high_10", + "support_nodes": 55 + } + } + ], + "llm_generate_remaining_graph": true, + "llm_generate_remaining_tasks": false, + "llm_generated_edge_budget": 48, + "llm_generated_task_budget": 0, + "llm_generation_parallel": true, + "llm_generation_workers": 4, + "llm_generation_retries": 3, + "allow_template_fallback_on_llm_failure": false + } +} \ No newline at end of file diff --git a/datasets/fixed_levels/shared_config_fixed_levels.json b/datasets/fixed_levels/shared_config_fixed_levels.json new file mode 100644 index 0000000000000000000000000000000000000000..30a0a18011c0e32abb36b272644fd3257119c246 --- /dev/null +++ b/datasets/fixed_levels/shared_config_fixed_levels.json @@ -0,0 +1,63 @@ +{ + "environment": { + "n_users": 24, + "alias_density": 0.2, + "noise_level": 0.12, + "red_herring_rate": 0.08, + "max_steps": 24, + "seed": 2026 + }, + "dataset": { + "mode": "canonical", + "metaqa_root": "metaQA", + "metaqa_kb_path": "", + "metaqa_variant": "vanilla", + "metaqa_hops": ["1-hop", "2-hop", "3-hop"], + "metaqa_splits": ["train", "dev", "test"] + }, + "swarm": { + "enabled": true, + "max_agents": 3, + "max_breadth": 2, + "max_width": 2, + "max_depth": 2, + "planner_rounds": 2, + "tools_per_agent": 1 + }, + "spawn_reward": { + "lambda_parallel": 0.15, + "lambda_finish": 0.2, + "anneal": 1.0, + "max_parallel_hint": 3 + }, + "seeding": { + "seeded_nodes": [], + "seeded_edges": [], + "seeded_questions": [], + "llm_generate_remaining_graph": true, + "llm_generate_remaining_tasks": false, + "llm_generated_edge_budget": 64, + "llm_generated_task_budget": 0, + "llm_generation_parallel": true, + "llm_generation_workers": 4, + "llm_generation_retries": 3, + "allow_template_fallback_on_llm_failure": false + }, + "llm": { + "provider": "ollama", + "model": "qwen3:2b", + "temperature": 0.05, + "max_tokens": 384, + "timeout_seconds": 240, + "ollama_base_url": "http://127.0.0.1:11434", + "openai_base_url": "https://api.openai.com/v1", + "openai_api_key_env": "OPENAI_API_KEY", + "openai_api_key": "" + }, + "runtime": { + "default_episodes": 30, + "leaderboard_path": "datasets/fixed_levels/leaderboard_fixed_levels.json", + "dashboard_path": "datasets/fixed_levels/dashboard_fixed_levels.html", + "sweep_dashboard_dir": "datasets/fixed_levels/sweep_dashboards" + } +} diff --git a/docs/adversarial_self_play.md b/docs/adversarial_self_play.md new file mode 100644 index 0000000000000000000000000000000000000000..fe3a1d2ce4b57f6fdf6b2de04fd6c2e7cff697a6 --- /dev/null +++ b/docs/adversarial_self_play.md @@ -0,0 +1,99 @@ +# Adversarial Self-Play Training (Kimi-Style + TRL) + +This repository now includes a code scaffold for alternating adversarial self-play with Hugging Face TRL. + +## Goal + +Train two policies in alternating rounds: + +- Generator policy: proposes hard OSINT tasks (question + answer + supporting edges). +- Answerer policy: solves tasks proposed by the generator. + +The loop is intended to move from static evaluation toward on-policy co-evolution. + +## Kimi-style Objective Mapping + +The implementation maps the requested Kimi-style ingredients onto TRL GRPO as follows: + +- Grouped rollouts: `num_generations` in each GRPO phase. +- Relative reward baseline: GRPO group-relative advantages. +- Clipped policy updates: `epsilon` clipping in GRPO objective. +- KL/reference regularization: `beta` in GRPOConfig. +- Token-level online RL behavior: GRPO online generation with reward functions. +- Toggle schedule: explicit alternating generator and answerer rounds. + +## Topology and Scheduling Options + +- `model_topology: "dual"`: train separate generator and answerer models. +- `model_topology: "shared"`: train one shared model for both roles. + - Use `shared_model_name_or_path` to set the common base checkpoint. +- `phase_schedule: "generator_answerer"`: default two-phase loop per round. +- `phase_schedule: "answerer_generator_answerer"`: solver-first curriculum: + 1. Train answerer on current adversarial pool. + 2. Freeze that answerer snapshot while training generator against it. + 3. Train answerer again on newly generated adversarial tasks. + +This directly supports the "train solver, freeze, attack, retrain solver" sequence. + +## Canonical Graph Mode + +- `canonical_graph_mode: "generate"` (default): generator can propose canonical graph updates in `swarm_v2`. +- `canonical_graph_mode: "fixed"`: canonical graph candidates are held fixed per prompt, so training focuses on question/answer behavior over stable graph structure. + +## Tuning Modes + +- `tuning_mode: "full"`: full-model GRPO fine-tuning. +- `tuning_mode: "lora"`: PEFT LoRA adapters for GRPO updates. + - Configure via `lora` block: `r`, `alpha`, `dropout`, `target_modules`, `bias`, `task_type`. + +## Reward Design + +### Generator (adversarial swarm) + +`GeneratorRewardFunction` combines weighted components: + +- Validity: checks parsable task fields and bounded support-edge size. +- Hardness: rewards questions the frozen answerer currently gets wrong. +- Diversity: penalizes near-duplicate questions via token-overlap similarity. +- Consistency: rewards edge/answer/question grounding against canonical graph context. + +Weights are configurable in `generator_reward_weights`. + +### Answerer (existing reward integration) + +`AnswererRewardFunction` wraps existing environment reward logic: + +- Reuses `compute_answer_reward` from `src/osint_env/env/reward.py`. +- Builds transient `TaskInstance` objects from training rows. +- Preserves difficulty-aware reward behavior (`easy` / `medium` / `hard`). + +## Entry Points + +- CLI command: `osint-env train-self-play` +- Main runner: `src/osint_env/training/self_play.py` +- Config loader: `src/osint_env/training/config.py` +- Reward functions: `src/osint_env/training/rewards.py` +- Example config: `config/self_play_training_example.json` + +## Dry Run Mode + +The example config sets `dry_run: true` by default. + +In dry run mode, the pipeline still: + +- Materializes generator/answerer datasets per round. +- Materializes optional `answerer_pre_dataset` when using solver-first schedule. +- Produces generated-task artifacts (fallback generator path). +- Writes a full run summary. + +But it skips expensive GRPO updates. + +## Compute Mode + +When compute is available: + +1. Install train dependencies: `python -m pip install -e ".[train]"` +2. Disable dry run (`--dry-run` off and/or `"dry_run": false` in config). +3. Run `osint-env train-self-play`. + +Outputs are written under `artifacts/self_play` unless overridden. diff --git a/docs/reward_design_notes.md b/docs/reward_design_notes.md new file mode 100644 index 0000000000000000000000000000000000000000..7041d248b1e22e864a0ca50dbfe3dc2fbc32f7f7 --- /dev/null +++ b/docs/reward_design_notes.md @@ -0,0 +1,94 @@ +# Reward Design Notes + +This environment uses a composite reward that adapts ideas from: + +- AutoGraph-R1 (arXiv:2510.15339) +- UniRel (arXiv:2512.17043) +- DeepPath (EMNLP 2017, D17-1060) +- Multi-Hop KG Reasoning with Reward Shaping (EMNLP 2018, D18-1362) +- Kimi K2.5 (arXiv:2602.02276) for PARL-style swarm auxiliary shaping + +Additional related context consulted: + +- MINERVA (arXiv:1711.05851) for query-conditioned walk-style reasoning over KG paths. + +## Components in this Branch + +The implementation follows a staged reward design: + +1. edge-level rewards during graph construction (`ADD_EDGE`) +2. answer-level rewards for retrieval usefulness and final task utility (`ANSWER`) +3. evaluation-level composite leaderboard score for benchmark ranking + +### 1) Edge addition reward + +For each `ADD_EDGE`, the reward combines: + +- Global accuracy term (DeepPath): + - $r_{global} = +1$ if a candidate edge is correct, else $-1$ (scaled in code for stability). +- Soft shaping term (D18 reward shaping): + - $R = R_b + (1 - R_b) f(s, r, o)$, where $f$ is a soft fact plausibility score. + - In code, $f$ is approximated by relation/type priors plus small domain priors. +- Efficiency term (DeepPath): + - $r_{efficiency} \propto 1 / \text{step\_count}$. +- Diversity term (DeepPath): + - novelty from cosine dissimilarity of edge signatures; repeated patterns are down-weighted. +- Relation/entity informativeness (UniRel): + - relation rarity via normalized IDF of relation labels, + - entity informativeness via inverse hub-penalty. +- Connectivity gain term: + - rewards bridge edges that connect previously disconnected graph regions. + +### 2) Final answer reward + +For `ANSWER`, the reward combines: + +- format validity, +- answer correctness, +- knowledge-carrying utility (AutoGraph-R1 style): + - $R_C(q, y, G) = \mathbb{{I}}[\text{{deducible}}(q, y \mid G)]$. +- knowledge-indexing utility (AutoGraph-R1 style): + - $R_I(q, D_{{gold}}, G) = |Top\text{{-}}k(G,q) \cap D_{{gold}}| / |D_{{gold}}|$, + - approximated in this environment with evidence recall over tool outputs. +- connectivity (UniRel style): + - discrete connectivity reward over extracted seed entities, normalized for stable mixing. +- graph F1 against supporting edges, +- compactness penalty for unnecessary extra edges, +- efficiency bonus, +- relation/entity informativeness for the constructed subgraph, +- repetition penalty to discourage redundant relation generation patterns. + +UniRel-style aggregate view represented in this branch: + +$$ +R(a) \approx R_{{fmt}} + R_{{con}} + w_1 R_{{ent}} + w_2 R_{{rel}} + \text{{task utility terms}} +$$ + +with task utility terms coming from AutoGraph-inspired $R_C$ and $R_I$ components. + +## Telemetry + +Per-step component rewards are aggregated into `info["reward_components"]`, enabling: + +- richer benchmark summaries, +- leaderboard ranking by composite utility, +- visual diagnostics in dashboard exports. + +Evaluation also computes derived retrieval and structural utility signals used in leaderboard ranking. + +## Future Multi-Agent Notes + +This branch now includes a low-width swarm baseline orchestrator that adds PARL-style auxiliary shaping on top of the core edge and answer rewards. + +The helper implementation is in: + +- `src/osint_env/env/spawn_reward_hooks.py` + +It follows the Kimi K2.5 style decomposition: + +- $r_{{PARL}}(x,y) = r_{{perf}}(x,y) + \lambda_1 r_{{parallel}} + \lambda_2 r_{{finish}}$, +- optional critical-steps shaping for latency-sensitive training, +- optional annealing of $\lambda_1, \lambda_2$ toward zero, +- optional breadth/depth shaping hooks for future branch integration. + +The expanded project-level walkthrough is in `README.md` under "Reward Design (Integrated Notes)". diff --git a/inference.py b/inference.py new file mode 100644 index 0000000000000000000000000000000000000000..9e34370ac9101cfed4bc02df8fe8192a1ba78d22 --- /dev/null +++ b/inference.py @@ -0,0 +1,540 @@ +from __future__ import annotations + +import json +import os +from pathlib import Path +from typing import Any + +from osint_env.agents.single_agent import SingleAgentRunner +from osint_env.agents.swarm_agent import SwarmAgentRunner +from osint_env.config import clone_environment_config, load_seeding_config, load_shared_config +from osint_env.domain.models import EnvironmentConfig +from osint_env.env.environment import OSINTEnvironment +from osint_env.env.reward import compute_graph_f1 +from osint_env.eval.leaderboard import append_leaderboard_record, load_leaderboard +from osint_env.eval.metrics import EvalMetrics +from osint_env.llm import build_llm_client +from osint_env.viz import export_dashboard + + +CONFIG_PATH = os.getenv("CONFIG_PATH", "datasets/fixed_levels/shared_config_fixed_levels.json") +SEED_FILE = os.getenv("SEED_FILE", "datasets/fixed_levels/seed_fixed_levels.json") +AGENT_MODE = os.getenv("AGENT_MODE", "swarm") +LLM_PROVIDER = os.getenv("LLM_PROVIDER", "openai") +MODEL_NAME = os.getenv("MODEL_NAME", "gpt-5.4") +OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "") +OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL", "") +OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "") +OPENAI_API_KEY_ENV = os.getenv("OPENAI_API_KEY_ENV", "OPENAI_API_KEY") +API_BASE_URL = os.getenv("API_BASE_URL", "https://api.openai.com/v1") +API_KEY = os.getenv("API_KEY", "") +HF_SPACE_URL = os.getenv("HF_SPACE_URL", "") +HF_TOKEN = os.getenv("HF_TOKEN","") +LOCAL_IMAGE_NAME = os.getenv("LOCAL_IMAGE_NAME", "") +LLM_TIMEOUT_SECONDS = int(os.getenv("LLM_TIMEOUT_SECONDS", "0")) +EPISODES = int(os.getenv("EPISODES", "1")) +SUCCESS_SCORE_THRESHOLD = float(os.getenv("SUCCESS_SCORE_THRESHOLD", "0.67")) +TASK_INDICES_RAW = os.getenv("TASK_INDICES", "") +DATASET_MODE = os.getenv("DATASET_MODE", "") +METAQA_ROOT = os.getenv("METAQA_ROOT", "") +METAQA_KB_PATH = os.getenv("METAQA_KB_PATH", "") +METAQA_VARIANT = os.getenv("METAQA_VARIANT", "") +METAQA_HOPS_RAW = os.getenv("METAQA_HOPS", "") +METAQA_SPLITS_RAW = os.getenv("METAQA_SPLITS", "") + +WRITE_BENCHMARK_ARTIFACTS = os.getenv("WRITE_BENCHMARK_ARTIFACTS", "1").strip().lower() in { + "1", + "true", + "yes", + "y", + "on", +} +LEADERBOARD_PATH = os.getenv("LEADERBOARD_PATH", "datasets/fixed_levels/leaderboard_fixed_levels.json") +DASHBOARD_PATH = os.getenv("DASHBOARD_PATH", "datasets/fixed_levels/dashboard_fixed_levels.html") +RUN_NAME = os.getenv("RUN_NAME", "fixed_levels_qwen_swarm") + +BENCHMARK = "osint-openenv" +TASK_NAME = "fixed_levels_easy_mid_hard" + + +def _parse_task_indices(raw: str) -> list[int]: + out: list[int] = [] + for token in str(raw or "").split(","): + stripped = token.strip() + if not stripped: + continue + try: + out.append(int(stripped)) + except ValueError: + continue + return out + + +def _parse_csv_tokens(raw: str) -> list[str]: + return [token.strip() for token in str(raw or "").split(",") if token.strip()] + + +def _normalize_ollama_base_url(url: str) -> str: + normalized = str(url or "").strip().rstrip("/") + if normalized.endswith("/v1"): + normalized = normalized[:-3].rstrip("/") + return normalized or "http://127.0.0.1:11434" + + +def _normalize_openai_base_url(url: str) -> str: + normalized = str(url or "").strip().rstrip("/") + if not normalized: + return "" + if normalized.endswith("/v1"): + return normalized + return f"{normalized}/v1" + + +TASK_INDICES = _parse_task_indices(TASK_INDICES_RAW) + + +def log_start(task: str, env: str, model: str) -> None: + print(f"[START] task={task} env={env} model={model}", flush=True) + + +def log_step(step: int, action: str, reward: float, done: bool, error: str | None) -> None: + error_text = "null" if error is None else str(error) + print( + f"[STEP] step={step} action={action} reward={reward:.2f} done={str(bool(done)).lower()} error={error_text}", + flush=True, + ) + + +def log_end(task: str, success: bool, steps: int, score: float, rewards: list[float]) -> None: + rewards_text = ",".join(f"{value:.2f}" for value in rewards) + print( + f"[END] success={str(bool(success)).lower()} steps={steps} score={score:.2f} rewards={rewards_text}", + flush=True, + ) + + +def _looks_like_placeholder_api_key(value: str) -> bool: + token = str(value or "").strip().lower() + if not token: + return True + placeholder_markers = [ + "your_openai_api_key", + "your-key", + "your_key", + "your real", + "real-openai-key", + "replace-me", + "changeme", + "example", + "", + ] + if token.startswith("your_") or token.startswith("sk-your-"): + return True + return any(marker in token for marker in placeholder_markers) + + +def _format_action(action: dict[str, Any]) -> str: + action_type = str(action.get("action_type", "")).upper() + payload = dict(action.get("payload", {})) + + if action_type == "ANSWER": + return f"answer({str(payload.get('answer', 'unknown')).strip()})" + + if action_type == "ADD_EDGE": + try: + conf = float(payload.get("confidence", 1.0)) + except (TypeError, ValueError): + conf = 1.0 + return ( + "add_edge(" + f"{payload.get('src', '')}," + f"{payload.get('rel', '')}," + f"{payload.get('dst', '')}," + f"{conf:.2f}" + ")" + ) + + tool_name = str(payload.get("tool_name", "tool")).strip() or "tool" + args = payload.get("args", {}) + if not isinstance(args, dict) or not args: + return f"{tool_name}()" + args_text = ",".join(f"{key}={value}" for key, value in sorted(args.items())) + return f"{tool_name}({args_text})" + + +def _assistant_tool_call_id(message: dict[str, Any]) -> str | None: + tool_calls = list(message.get("tool_calls", [])) + if not tool_calls: + return None + tool_call_id = tool_calls[0].get("id") + return str(tool_call_id) if tool_call_id else None + + +def _tool_result_message(assistant_message: dict[str, Any], result: dict[str, Any]) -> dict[str, Any] | None: + tool_call_id = _assistant_tool_call_id(assistant_message) + if not tool_call_id: + return None + return { + "role": "tool", + "tool_call_id": tool_call_id, + "content": json.dumps(result, sort_keys=True), + } + + +def _resolve_environment_config() -> EnvironmentConfig: + shared = load_shared_config(CONFIG_PATH) + env_cfg = clone_environment_config(shared.environment) + + if SEED_FILE and Path(SEED_FILE).exists(): + env_cfg.seeding = load_seeding_config(SEED_FILE) + + mode = AGENT_MODE.strip().lower() + if mode == "single": + env_cfg.swarm.enabled = False + elif mode == "swarm": + env_cfg.swarm.enabled = True + + # Inference submissions must route all calls through OpenAI-compatible client config. + env_cfg.llm.provider = "openai" + env_cfg.llm.model = MODEL_NAME.strip() + + if LLM_TIMEOUT_SECONDS > 0: + env_cfg.llm.timeout_seconds = int(LLM_TIMEOUT_SECONDS) + + # Evaluation harnesses inject API_BASE_URL/HF_TOKEN for proxy-enforced requests. + resolved_openai_base = API_BASE_URL.strip() or OPENAI_BASE_URL.strip() or HF_SPACE_URL.strip() + if resolved_openai_base: + env_cfg.llm.openai_base_url = _normalize_openai_base_url(resolved_openai_base) + + if HF_TOKEN.strip(): + env_cfg.llm.openai_api_key = HF_TOKEN.strip() + elif API_KEY.strip(): + env_cfg.llm.openai_api_key = API_KEY.strip() + elif OPENAI_API_KEY.strip(): + env_cfg.llm.openai_api_key = OPENAI_API_KEY.strip() + + if OPENAI_API_KEY_ENV.strip(): + env_cfg.llm.openai_api_key_env = OPENAI_API_KEY_ENV.strip() + + dataset_mode = DATASET_MODE.strip().lower() + if dataset_mode in {"canonical", "metaqa"}: + env_cfg.dataset_mode = dataset_mode + + if METAQA_ROOT.strip(): + env_cfg.metaqa_root = METAQA_ROOT.strip() + if METAQA_KB_PATH.strip(): + env_cfg.metaqa_kb_path = METAQA_KB_PATH.strip() + + metaqa_variant = METAQA_VARIANT.strip().lower() + if metaqa_variant in {"vanilla", "ntm"}: + env_cfg.metaqa_variant = metaqa_variant + + metaqa_hops = _parse_csv_tokens(METAQA_HOPS_RAW) + if metaqa_hops: + env_cfg.metaqa_hops = metaqa_hops + + metaqa_splits = _parse_csv_tokens(METAQA_SPLITS_RAW) + if metaqa_splits: + env_cfg.metaqa_splits = metaqa_splits + + return env_cfg + + +def _runner_for(env: OSINTEnvironment, llm: Any) -> SingleAgentRunner | SwarmAgentRunner: + if env.config.swarm.enabled: + return SwarmAgentRunner(env=env, llm=llm) + return SingleAgentRunner(env=env, llm=llm) + + +def _normalize_difficulty(value: str) -> str: + token = str(value or "").strip().lower() + if token in {"easy", "e"}: + return "easy" + if token in {"mid", "medium", "m"}: + return "medium" + if token in {"high", "hard", "h"}: + return "hard" + return "hard" + + +def _task_difficulty(env: OSINTEnvironment, task_index: int) -> str: + idx = int(task_index) % max(1, len(env.tasks)) + task = env.tasks[idx] + if isinstance(task.metadata, dict) and "difficulty" in task.metadata: + return _normalize_difficulty(str(task.metadata.get("difficulty", ""))) + if idx < 10: + return "easy" + if idx < 20: + return "medium" + return "hard" + + +def _episode_row(env: OSINTEnvironment, info: dict[str, Any]) -> dict[str, Any]: + if env.state is None: + return { + "task_id": "unknown", + "task_type": "unknown", + "question": "", + "task_answer": str(info.get("task_answer", "")), + "agent_answer": str(info.get("agent_answer", "")), + "graph_f1": 0.0, + "reward": float(info.get("total_reward", 0.0) or 0.0), + "steps": int(info.get("step_count", 0) or 0), + "tool_calls": int(info.get("tool_calls", 0) or 0), + "success": int(info.get("agent_answer") == info.get("task_answer")), + "reward_components": dict(info.get("reward_components", {})), + "pred_edges": [], + "truth_edges": [], + } + + graph_f1 = compute_graph_f1(env.memory_graph.edges, env.state.task.supporting_edges) + return { + "task_id": env.state.task.task_id, + "task_type": env.state.task.task_type, + "question": env.state.task.question, + "task_answer": str(info.get("task_answer", "")), + "agent_answer": str(info.get("agent_answer", "")) if info.get("agent_answer") is not None else "", + "graph_f1": graph_f1, + "reward": float(info.get("total_reward", 0.0) or 0.0), + "steps": int(info.get("step_count", 0) or 0), + "tool_calls": int(info.get("tool_calls", 0) or 0), + "success": int(info.get("agent_answer") == info.get("task_answer")), + "reward_components": dict(info.get("reward_components", {})), + "spawn_count": int(info.get("spawn_count", 0) or 0), + "spawn_critical_steps": int(info.get("spawn_critical_steps", 0) or 0), + "pred_edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in env.memory_graph.edges + ], + "truth_edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in env.state.task.supporting_edges + ], + } + + +def _last_action_error(observation: Any, info: dict[str, Any]) -> str | None: + raw = info.get("last_action_error") if isinstance(info, dict) else None + if raw is not None: + return str(raw) + + tool_outputs = getattr(observation, "tool_outputs", None) + if isinstance(tool_outputs, list) and tool_outputs: + last = tool_outputs[-1] + if isinstance(last, dict): + output = last.get("output") + if isinstance(output, dict) and output.get("error") is not None: + return str(output.get("error")) + return None + + +def _install_step_logger(env: OSINTEnvironment) -> tuple[list[float], dict[str, int], Any]: + rewards: list[float] = [] + counters = {"steps": 0} + original_step = env.step + + def _logged_step(action: Any): + observation, reward, done, info = original_step(action) + counters["steps"] += 1 + reward_value = float(reward or 0.0) + rewards.append(reward_value) + action_type = getattr(action, "action_type", "") + action_type_value = str(getattr(action_type, "value", action_type)) + action_text = _format_action( + { + "action_type": action_type_value, + "payload": dict(getattr(action, "payload", {}) or {}), + } + ) + log_step( + step=counters["steps"], + action=action_text, + reward=reward_value, + done=bool(done), + error=_last_action_error(observation, info if isinstance(info, dict) else {}), + ) + return observation, reward, done, info + + env.step = _logged_step + return rewards, counters, original_step + + +def _validate_required_configuration() -> None: + missing: list[str] = [] + + api_base = API_BASE_URL.strip() + model_name = MODEL_NAME.strip() + hf_token = HF_TOKEN.strip() + api_key = API_KEY.strip() + openai_key = OPENAI_API_KEY.strip() + + if not api_base or api_base == "": + missing.append("API_BASE_URL") + if not model_name or model_name == "": + missing.append("MODEL_NAME") + if not (hf_token or api_key or openai_key): + missing.append("HF_TOKEN|API_KEY|OPENAI_API_KEY") + + # Required when using docker-image based env construction. + if os.getenv("REQUIRE_LOCAL_IMAGE_NAME", "0").strip().lower() in {"1", "true", "yes", "on"}: + if not LOCAL_IMAGE_NAME.strip(): + missing.append("LOCAL_IMAGE_NAME") + + if missing: + raise RuntimeError(f"Missing required environment variables: {', '.join(sorted(set(missing)))}") + + +def _task_targets(env: OSINTEnvironment, episodes: int, task_indices: list[int]) -> list[int | None]: + if task_indices: + task_count = max(1, len(env.tasks)) + return [index % task_count for index in task_indices] + return [None] * max(1, episodes) + + +def _run_with_runner( + env: OSINTEnvironment, + llm: Any, + episodes: int, + task_indices: list[int], +) -> tuple[dict[str, Any], list[dict[str, Any]], list[float], int]: + metrics = EvalMetrics() + episode_rows: list[dict[str, Any]] = [] + rewards, counters, original_step = _install_step_logger(env) + + single_runner = SingleAgentRunner(env=env, llm=llm) + swarm_runner = SwarmAgentRunner(env=env, llm=llm) if env.config.swarm.enabled else None + + try: + for task_index in _task_targets(env, episodes, task_indices): + task_count = max(1, len(env.tasks)) + selected_index = env._task_idx % task_count if task_index is None else int(task_index) % task_count + if task_index is not None: + # Keep compatibility with explicit task selection from the previous inference script. + env._task_idx = selected_index + + difficulty = _task_difficulty(env, selected_index) + if difficulty == "easy": + runner: SingleAgentRunner | SwarmAgentRunner = single_runner + elif swarm_runner is not None: + runner = swarm_runner + else: + runner = single_runner + + info = runner.run_episode() + if env.state is None: + continue + + graph_f1 = compute_graph_f1(env.memory_graph.edges, env.state.task.supporting_edges) + metrics.add(info, task_type=env.state.task.task_type, graph_f1=graph_f1) + episode_rows.append(_episode_row(env, info)) + finally: + env.step = original_step + + return metrics.summary(), episode_rows, rewards, int(counters["steps"]) + + +def _maybe_write_artifacts( + env: OSINTEnvironment, + summary: dict[str, Any], + episodes: int, + episode_rows: list[dict[str, Any]], +) -> tuple[dict[str, Any] | None, str | None]: + if not WRITE_BENCHMARK_ARTIFACTS: + return None, None + + record = append_leaderboard_record( + path=LEADERBOARD_PATH, + summary=summary, + episodes=episodes, + run_name=RUN_NAME or None, + config={ + "seed": env.config.seed, + "max_steps": env.config.max_steps, + "swarm_enabled": env.config.swarm.enabled, + "max_agents": env.config.swarm.max_agents, + "max_breadth": env.config.swarm.max_breadth, + "max_width": env.config.swarm.max_width, + "max_depth": env.config.swarm.max_depth, + "seeded_questions": len(env.config.seeding.seeded_questions), + "llm_provider": env.config.llm.provider, + "llm_model": env.config.llm.model, + }, + ) + + leaderboard = load_leaderboard(LEADERBOARD_PATH) + dashboard = export_dashboard( + env=env, + evaluation={"summary": summary, "episodes": episode_rows}, + leaderboard_records=leaderboard, + output_path=DASHBOARD_PATH, + ) + return record, dashboard + + +def main() -> None: + _validate_required_configuration() + env_cfg = _resolve_environment_config() + llm_client = build_llm_client(env_cfg.llm) + + episodes_given = "EPISODES" in os.environ and str(os.getenv("EPISODES", "")).strip() != "" + task_indices_given = bool(TASK_INDICES) + + if not episodes_given and not task_indices_given: + runs: list[tuple[str, list[int], int]] = [ + ("easy", list(range(0, 10)), 10), + ("mid", list(range(10, 20)), 10), + ("hard", list(range(20, 30)), 10), + ] + else: + selected_indices = TASK_INDICES if task_indices_given else [] + episodes = len(selected_indices) if selected_indices else max(1, EPISODES) + runs = [(TASK_NAME, selected_indices, episodes)] + + for task_name, run_indices, run_episodes in runs: + env: OSINTEnvironment | None = None + rewards: list[float] = [] + steps_taken = 0 + score = 0.0 + success = False + + env = OSINTEnvironment(env_cfg, llm=llm_client) + log_start(task=task_name, env=BENCHMARK, model=env_cfg.llm.model) + + try: + summary, episode_rows, rewards, steps_taken = _run_with_runner( + env=env, + llm=llm_client, + episodes=run_episodes, + task_indices=run_indices, + ) + + score = float(summary.get("avg_reward", 0.0) or 0.0) + score = max(0.0, min(1.0, score)) + success = score >= SUCCESS_SCORE_THRESHOLD + + _maybe_write_artifacts( + env=env, + summary=summary, + episodes=run_episodes, + episode_rows=episode_rows, + ) + finally: + if env is not None: + close_fn = getattr(env, "close", None) + if callable(close_fn): + close_fn() + log_end(task=task_name, success=success, steps=steps_taken, score=score, rewards=rewards) + + +if __name__ == "__main__": + main() diff --git a/my_env_v4.py b/my_env_v4.py new file mode 100644 index 0000000000000000000000000000000000000000..34037cd36e5d2ce3fcda2debc71e461bc07cbf6c --- /dev/null +++ b/my_env_v4.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from dataclasses import dataclass + + +@dataclass(slots=True) +class MyEnvV4Action: + message: str + + +@dataclass(slots=True) +class _EchoObservation: + echoed_message: str + + +@dataclass(slots=True) +class _EchoResult: + observation: _EchoObservation + reward: float = 0.0 + done: bool = False + + +class MyEnvV4Env: + def __init__(self) -> None: + self._step_count = 0 + + @classmethod + async def from_docker_image(cls, image_name: str | None = None) -> "MyEnvV4Env": + return cls() + + async def reset(self) -> _EchoResult: + self._step_count = 0 + return _EchoResult(observation=_EchoObservation(echoed_message=""), reward=0.0, done=False) + + async def step(self, action: MyEnvV4Action) -> _EchoResult: + self._step_count += 1 + message = str(getattr(action, "message", "")) + reward = len(message) * 0.1 + return _EchoResult( + observation=_EchoObservation(echoed_message=message), + reward=reward, + done=False, + ) + + async def close(self) -> None: + return None diff --git a/openenv.yaml b/openenv.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a9fdeb8139588db01cd990c38e3912498e2e37ea --- /dev/null +++ b/openenv.yaml @@ -0,0 +1,66 @@ +name: osint-openenv +version: 0.1.0 +description: Synthetic OSINT benchmark environment exposed over HTTP. +tasks: + - id: seed_task_0 + difficulty: easy + max_steps: 24 + grader: + type: difficulty_exact_match + answer_type: node_id + case_sensitive: true + reward_profile: easy + - id: seed_task_10 + difficulty: medium + max_steps: 24 + grader: + type: difficulty_exact_match + answer_type: node_id + case_sensitive: true + reward_profile: medium + - id: seed_task_20 + difficulty: hard + max_steps: 24 + grader: + type: difficulty_exact_match + answer_type: node_id + case_sensitive: true + reward_profile: hard +transport: + type: http + base_path: / +endpoints: + health: + method: GET + path: /health + metadata: + method: GET + path: /api/environment + tasks: + method: GET + path: /openenv/tasks + reset: + method: POST + path: /reset + step: + method: POST + path: /step + state: + method: GET + path: /state +models: + action_space: + - CALL_TOOL + - ADD_EDGE + - ANSWER + task_fields: + - task_id + - task_type + - question + - difficulty + - grader + observation_fields: + - tool_outputs + - graph_snapshot + - action_history + - task diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..cbc5681f4cffafedc7ffebec2b2a19b46864e30f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,45 @@ +[project] +name = "osint-rl-env" +version = "0.1.0" +description = "OSINT-style multi-platform information ecosystem environment for LLM agents." +readme = "README.md" +requires-python = ">=3.10" +dependencies = [ + "openenv>=0.1.13", + "openai>=1.40.0", + "fastapi>=0.115.0", + "requests>=2.32.3", + "uvicorn>=0.30.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=8.0.0", +] +train = [ + "datasets>=2.20.0", + "transformers>=4.45.0", + "accelerate>=0.33.0", + "trl>=0.15.0", + "peft>=0.11.0", + "pillow", + "torchvision", + "wandb", +] + +[project.scripts] +osint-env = "osint_env.cli:main" +server = "osint_env.server_entry:main" + +[build-system] +requires = ["setuptools>=68", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +package-dir = {"" = "src"} + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..643151119118a7bf8a45288b6c93f9047e825990 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +openenv>=0.1.13 +openai>=1.40.0 +fastapi>=0.115.0 +requests>=2.32.3 +uvicorn>=0.30.0 +pytest>=8.0.0 +datasets>=2.20.0 +transformers>=4.45.0 +accelerate>=0.33.0 +trl>=0.15.0 +peft>=0.11.0 diff --git a/scripts/build_fixed_levels_dataset.py b/scripts/build_fixed_levels_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..3e0ad295450f49db4b7071fa0c57ae4341891645 --- /dev/null +++ b/scripts/build_fixed_levels_dataset.py @@ -0,0 +1,197 @@ +from __future__ import annotations + +import argparse +import json +from collections import Counter +from dataclasses import asdict +from pathlib import Path +from typing import Any + +from osint_env.config import clone_environment_config, load_seeding_config, load_shared_config +from osint_env.data.generator import DatasetGenerator +from osint_env.domain.models import Edge, TaskInstance +from osint_env.llm import build_llm_client + + +def edge_to_dict(edge: Edge) -> dict[str, Any]: + return { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + + +def task_to_dict(task: TaskInstance) -> dict[str, Any]: + return { + "task_id": task.task_id, + "task_type": task.task_type, + "question": task.question, + "answer": task.answer, + "supporting_edges": [edge_to_dict(e) for e in task.supporting_edges], + "metadata": dict(task.metadata), + } + + +def build_fixed_snapshot(seed_path: Path) -> dict[str, Any]: + seeding = load_seeding_config(seed_path) + fixed_nodes = [] + for node in seeding.seeded_nodes: + fixed_nodes.append( + { + "node_id": node.node_id, + "node_type": str(getattr(node.node_type, "value", node.node_type)), + "attrs": dict(node.attrs), + } + ) + fixed_edges = [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in seeding.seeded_edges + ] + fixed_questions = [] + for idx, q in enumerate(seeding.seeded_questions): + fixed_questions.append( + { + "task_id": f"fixed_task_{idx:02d}", + "task_type": q.task_type, + "question": q.question, + "answer": q.answer, + "supporting_edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in q.supporting_edges + ], + "metadata": dict(q.metadata), + } + ) + + difficulty_counts = Counter(str(q.get("metadata", {}).get("difficulty", "unknown")) for q in fixed_questions) + return { + "dataset_name": "fixed_levels_submission_set", + "source_seed": str(seed_path), + "graph": { + "nodes": fixed_nodes, + "edges": fixed_edges, + "node_count": len(fixed_nodes), + "edge_count": len(fixed_edges), + }, + "questions": fixed_questions, + "question_count": len(fixed_questions), + "difficulty_counts": dict(difficulty_counts), + } + + +def build_complete_snapshot(shared_config_path: Path, seed_path: Path) -> dict[str, Any]: + shared = load_shared_config(shared_config_path) + env_cfg = clone_environment_config(shared.environment) + env_cfg.seeding = load_seeding_config(seed_path) + + llm_client = build_llm_client(env_cfg.llm) + generator = DatasetGenerator(config=env_cfg, llm=llm_client) + + graph = generator.build_canonical_graph() + views = generator.build_platform_views(graph) + tasks = generator.generate_tasks(graph, views, count=max(15, len(env_cfg.seeding.seeded_questions))) + + difficulty_counts = Counter(str(task.metadata.get("difficulty", "unknown")) for task in tasks) + + return { + "dataset_name": "fixed_levels_submission_set", + "generation_mode": "llm_expanded", + "shared_config": str(shared_config_path), + "seed_file": str(seed_path), + "llm": asdict(env_cfg.llm), + "environment": { + "n_users": env_cfg.n_users, + "alias_density": env_cfg.alias_density, + "noise_level": env_cfg.noise_level, + "red_herring_rate": env_cfg.red_herring_rate, + "seed": env_cfg.seed, + }, + "canonical_graph": { + "node_count": len(graph.nodes), + "edge_count": len(graph.edges), + "nodes": [ + { + "node_id": node.node_id, + "node_type": node.node_type.value, + "attrs": dict(node.attrs), + } + for node in sorted(graph.nodes.values(), key=lambda n: n.node_id) + ], + "edges": [edge_to_dict(edge) for edge in graph.edges], + }, + "platform_views": { + "microblog_posts": views.microblog_posts, + "forum_threads": views.forum_threads, + "profiles": views.profiles, + "counts": { + "microblog_posts": len(views.microblog_posts), + "forum_threads": len(views.forum_threads), + "profiles": len(views.profiles), + }, + }, + "tasks": [task_to_dict(task) for task in tasks], + "task_count": len(tasks), + "difficulty_counts": dict(difficulty_counts), + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Build fixed difficulty dataset artifacts.") + parser.add_argument( + "--seed-file", + default="datasets/fixed_levels/seed_fixed_levels.json", + help="Path to seeding JSON with fixed graph/questions.", + ) + parser.add_argument( + "--shared-config", + default="datasets/fixed_levels/shared_config_fixed_levels.json", + help="Path to shared config used for LLM-expanded generation.", + ) + parser.add_argument( + "--output-dir", + default="datasets/fixed_levels", + help="Directory where dataset artifacts are written.", + ) + args = parser.parse_args() + + output_dir = Path(args.output_dir) + output_dir.mkdir(parents=True, exist_ok=True) + + seed_path = Path(args.seed_file) + shared_path = Path(args.shared_config) + + fixed_snapshot = build_fixed_snapshot(seed_path) + fixed_path = output_dir / "fixed_graph_questions.json" + fixed_path.write_text(json.dumps(fixed_snapshot, indent=2, sort_keys=True), encoding="utf-8") + + complete_snapshot = build_complete_snapshot(shared_path, seed_path) + complete_path = output_dir / "complete_dataset_qwen_generated.json" + complete_path.write_text(json.dumps(complete_snapshot, indent=2, sort_keys=True), encoding="utf-8") + + summary = { + "fixed_dataset": str(fixed_path), + "complete_dataset": str(complete_path), + "fixed_nodes": fixed_snapshot["graph"]["node_count"], + "fixed_edges": fixed_snapshot["graph"]["edge_count"], + "fixed_questions": fixed_snapshot["question_count"], + "complete_nodes": complete_snapshot["canonical_graph"]["node_count"], + "complete_edges": complete_snapshot["canonical_graph"]["edge_count"], + "complete_tasks": complete_snapshot["task_count"], + "difficulty_counts": complete_snapshot["difficulty_counts"], + } + print(json.dumps(summary, indent=2, sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/scripts/generate_fixed_levels_seed.py b/scripts/generate_fixed_levels_seed.py new file mode 100644 index 0000000000000000000000000000000000000000..65dc10da2dd52d3e6537adaa8c605a163900368a --- /dev/null +++ b/scripts/generate_fixed_levels_seed.py @@ -0,0 +1,109 @@ +from collections import Counter, OrderedDict +from pathlib import Path +import json + +U=[('aria','Aria Sen','Helios Labs','Sector 9'),('bharat','Bharat Kulkarni','Northbridge Logistics','Dockyard 17'),('cyrus','Cyrus Mehta','Apex Dynamics','Old Town'),('diya','Diya Roy','Blueharbor Media','Old Town'),('elin','Elin Das','Helios Labs','Sector 9'),('faris','Faris Noor','Tidewatch Ops','Rivergate'),('gita','Gita Pradhan','Apex Dynamics','Old Town'),('hiro','Hiro Tan','Northbridge Logistics','Dockyard 17'),('ivy','Ivy Kapoor','Kestrel Works','Rivergate'),('jules','Jules Banerjee','Blueharbor Media','Old Town'),('kian','Kian Bose','Atlas Freight','East Quay'),('leena','Leena Das','Sunmesh Analytics','Sector 9'),('mika','Mika Solanki','Orion Customs','North Basin'),('nora','Nora Iqbal','Emberline Security','Foundry Row'),('omar','Omar Sheikh','Atlas Freight','East Quay'),('priya','Priya Menon','Sunmesh Analytics','Sector 9'),('quinn','Quinn Rao','Orion Customs','North Basin'),('rhea','Rhea Kapoor','Emberline Security','Foundry Row'),('soren','Soren Malik','Harborlight Transit','Uplink Yard'),('tara','Tara Dey','Harborlight Transit','Uplink Yard')] +A=[('orchidfox','@orchidfox','ivy'),('steelquill','@steelquill','bharat'),('monsoonbyte','@monsoonbyte','diya'),('nightrelay','@nightrelay','faris'),('mapleghost','@mapleghost','elin'),('docksparrow','@docksparrow','hiro'),('quartzlotus','@quartzlotus','cyrus'),('emberglass','@emberglass','nora'),('basinraven','@basinraven','mika'),('tideshard','@tideshard','soren'),('hollowsignal','@hollowsignal','priya'),('ironwhisper','@ironwhisper','omar'),('cinderveil','@cinderveil','rhea'),('sablekeel','@sablekeel','tara'),('lanternmoth','@lanternmoth','kian'),('frostledger','@frostledger','leena')] +L=[('dockyard17','Dockyard 17'),('sector9','Sector 9'),('old_town','Old Town'),('rivergate','Rivergate'),('east_quay','East Quay'),('foundry_row','Foundry Row'),('north_basin','North Basin'),('uplink_yard','Uplink Yard')] +O=[('helios_labs','Helios Labs','sector9'),('northbridge_logistics','Northbridge Logistics','dockyard17'),('apex_dynamics','Apex Dynamics','old_town'),('blueharbor_media','Blueharbor Media','old_town'),('tidewatch_ops','Tidewatch Ops','rivergate'),('kestrel_works','Kestrel Works','rivergate'),('atlas_freight','Atlas Freight','east_quay'),('sunmesh_analytics','Sunmesh Analytics','sector9'),('orion_customs','Orion Customs','north_basin'),('emberline_security','Emberline Security','foundry_row'),('harborlight_transit','Harborlight Transit','uplink_yard')] +E=[('project_lantern','Project Lantern'),('black_kite','Black Kite'),('silent_current','Silent Current'),('amber_veil','Amber Veil'),('glass_harbor','Glass Harbor'),('ember_tide','Ember Tide'),('iron_wharf','Iron Wharf'),('ghost_signal','Ghost Signal')] +T=[('supply_leak','supply_chain'),('port_audit','port_audit'),('customs_breach','customs_breach'),('relay_map','relay_map'),('foundry_watch','foundry_watch'),('basin_shift','basin_shift'),('quiet_manifest','quiet_manifest'),('uplink_route','uplink_route'),('ember_tide_watch','ember_tide'),('ghost_signal_net','ghost_signal')] +P=['shift_roster','midnight_manifest','sat_phone_ping','drone_parts','relay_schedule','quay_ledgers','customs_tag','hull_signal','basin_photo','foundry_map','lantern_route','uplink_note'] + +def uid(x): return f'user_{x}' +def aid(x): return f'alias_{x}' +def oid(x): return f'org_{x}' +def lid(x): return f'loc_{x}' +def eid(x): return f'event_{x}' +def tid(x): return f'thr_{x}' +def pid(x): return f'post_{x}' + +def addn(nodes,nid,nt,attrs): nodes.append({'node_id':nid,'node_type':nt,'attrs':attrs}) + +def build(): + nodes=[]; edges=OrderedDict(); + for s,name,org,loc in U: addn(nodes,uid(s),'user',{'name':name,'org':org,'location':loc}) + for s,handle,user in A: addn(nodes,aid(s),'alias',{'handle':handle}) + for s,name,_ in O: addn(nodes,oid(s),'org',{'name':name}) + for s,name in L: addn(nodes,lid(s),'location',{'name':name}) + for s,name in E: addn(nodes,eid(s),'event',{'name':name}) + for s,topic in T: addn(nodes,tid(s),'thread',{'topic':topic}) + for s in P: addn(nodes,pid(s),'post',{'channel':'microblog'}) + def ae(k,src,rel,dst,c=1.0): edges[k]={'src':src,'rel':rel,'dst':dst,'confidence':c} + for s,_,user in A: ae(f'a_{s}',aid(s),'alias_of',uid(user)) + org_map={name:oid(s) for s,name,_ in O}; loc_map={name:lid(s) for s,name in L} + for s,_,org,loc in U: ae(f'w_{s}',uid(s),'works_at',org_map[org]); ae(f'l_{s}',uid(s),'located_in',loc_map[loc]) + for s,_,loc in O: ae(f'op_{s}',oid(s),'operates_in',lid(loc)) + CP=[('ivy','bharat',.95),('bharat','hiro',.95),('hiro','faris',.92),('faris','diya',.90),('diya','elin',.89),('elin','aria',.87),('aria','cyrus',.84),('cyrus','gita',.83),('gita','jules',.82),('jules','bharat',.81),('diya','ivy',.90),('ivy','elin',.86),('kian','omar',.93),('omar','mika',.90),('mika','quinn',.89),('quinn','nora',.88),('nora','rhea',.87),('rhea','soren',.86),('soren','tara',.86),('tara','kian',.84),('priya','leena',.91),('leena','aria',.83),('priya','nora',.82),('kian','bharat',.80),('soren','faris',.79),('quinn','hiro',.78)] + for i,(a,b,c) in enumerate(CP,1): ae(f'c{i:02d}',uid(a),'connected_to',uid(b),c) + PA={'midnight_manifest':'orchidfox','shift_roster':'docksparrow','sat_phone_ping':'nightrelay','drone_parts':'monsoonbyte','relay_schedule':'steelquill','quay_ledgers':'lanternmoth','customs_tag':'basinraven','hull_signal':'tideshard','basin_photo':'emberglass','foundry_map':'cinderveil','lantern_route':'frostledger','uplink_note':'sablekeel'} + for post,author in PA.items(): ae(f'ap_{post}',aid(author),'authored_post',pid(post)) + PR={'midnight_manifest':['dockyard17','project_lantern'],'shift_roster':['dockyard17','northbridge_logistics'],'sat_phone_ping':['rivergate','project_lantern'],'drone_parts':['black_kite','kestrel_works'],'relay_schedule':['project_lantern','sector9'],'quay_ledgers':['east_quay','glass_harbor'],'customs_tag':['north_basin','iron_wharf'],'hull_signal':['uplink_yard','ghost_signal'],'basin_photo':['foundry_row','amber_veil'],'foundry_map':['foundry_row','ember_tide'],'lantern_route':['project_lantern','sunmesh_analytics'],'uplink_note':['uplink_yard','harborlight_transit']} + for post,refs in PR.items(): + for i,x in enumerate(refs,1): ae(f'r_{post}_{i}',pid(post),'references', lid(x) if x in {y for y,_ in L} else (oid(x) if x in {y for y,_,_ in O} else eid(x))) + TA={'supply_leak':'diya','port_audit':'jules','customs_breach':'mika','relay_map':'leena','foundry_watch':'nora','basin_shift':'quinn','quiet_manifest':'kian','uplink_route':'soren','ember_tide_watch':'rhea','ghost_signal_net':'tara'} + TL={'supply_leak':[('discusses','project_lantern'),('references','northbridge_logistics')],'port_audit':[('discusses','black_kite'),('references','kestrel_works')],'customs_breach':[('discusses','iron_wharf'),('references','orion_customs')],'relay_map':[('discusses','project_lantern'),('references','sunmesh_analytics')],'foundry_watch':[('discusses','ember_tide'),('references','emberline_security')],'basin_shift':[('discusses','amber_veil'),('references','north_basin')],'quiet_manifest':[('discusses','glass_harbor'),('references','atlas_freight')],'uplink_route':[('discusses','ghost_signal'),('references','harborlight_transit')],'ember_tide_watch':[('discusses','ember_tide'),('references','foundry_row')],'ghost_signal_net':[('discusses','ghost_signal'),('references','uplink_yard')]} + for t,u in TA.items(): ae(f'at_{t}',uid(u),'authored_thread',tid(t)) + for t,rels in TL.items(): + for i,(rel,x) in enumerate(rels,1): ae(f'tl_{t}_{i}',tid(t),rel, lid(x) if x in {y for y,_ in L} else (oid(x) if x in {y for y,_,_ in O} else eid(x))) + ER=[('bharat','collaborates_on','project_lantern'),('hiro','collaborates_on','project_lantern'),('faris','collaborates_on','project_lantern'),('diya','investigates','project_lantern'),('leena','monitors','project_lantern'),('ivy','collaborates_on','black_kite'),('cyrus','collaborates_on','black_kite'),('elin','investigates','black_kite'),('jules','reports_on','black_kite'),('kian','collaborates_on','glass_harbor'),('omar','collaborates_on','glass_harbor'),('priya','monitors','glass_harbor'),('mika','collaborates_on','iron_wharf'),('quinn','collaborates_on','iron_wharf'),('nora','investigates','amber_veil'),('rhea','collaborates_on','ember_tide'),('soren','collaborates_on','ghost_signal'),('tara','reports_on','ghost_signal'),('gita','monitors','silent_current'),('jules','reports_on','silent_current')] + for i,(u,rel,e) in enumerate(ER,1): ae(f'er{i:02d}',uid(u),rel,eid(e),.9) + X=[(eid('project_lantern'),'connected_to',eid('glass_harbor')),(eid('black_kite'),'connected_to',eid('amber_veil')),(eid('ember_tide'),'connected_to',eid('ghost_signal')),(oid('atlas_freight'),'connected_to',oid('northbridge_logistics')),(oid('orion_customs'),'connected_to',oid('emberline_security')),(oid('harborlight_transit'),'connected_to',oid('tidewatch_ops'))] + for i,(a,rel,b) in enumerate(X,1): ae(f'x{i:02d}',a,rel,b,.77) + return nodes,edges + +def mk_questions(edges): + def ids(*items): + out=[] + for it in items: + if isinstance(it,list): out.extend(it) + else: out.append(it) + return out + def rng(prefix,a,b): return [f'{prefix}{i:02d}' for i in range(a,b+1)] + def sup(edge_ids): return [edges[e] for e in edge_ids] + def nodes(edge_ids): + s=set() + for e in edge_ids: s|={edges[e]['src'],edges[e]['dst']} + return len(s) + qs=[] + easy=[('easy_01','alias_orchidfox -> post_midnight_manifest -> loc_dockyard17 -> connected collaborator on event_project_lantern. Who is it?','user_bharat',ids('a_orchidfox','ap_midnight_manifest','r_midnight_manifest_1','c01','er01')),('easy_02','thr_supply_leak references org_northbridge_logistics. Which alias_docksparrow user works there and collaborates on event_project_lantern?','user_hiro',ids('tl_supply_leak_2','a_docksparrow','w_hiro','er02')),('easy_03','alias_monsoonbyte authored post_drone_parts about event_black_kite. Which user behind that alias is directly connected to the Kestrel collaborator?','user_diya',ids('a_monsoonbyte','ap_drone_parts','r_drone_parts_1','w_ivy','er06','c12')),('easy_04','alias_nightrelay references loc_rivergate. Which user behind it works at an org operating there and collaborates on event_project_lantern?','user_faris',ids('a_nightrelay','ap_sat_phone_ping','r_sat_phone_ping_1','w_faris','op_tidewatch_ops','er03')),('easy_05','thr_port_audit discusses Black Kite and references Kestrel Works. Which alias_orchidfox user authored post_midnight_manifest and collaborates on Black Kite?','user_ivy',ids('tl_port_audit_1','tl_port_audit_2','a_orchidfox','ap_midnight_manifest','w_ivy','er06')),('easy_06','Which Atlas Freight user behind alias_lanternmoth authored post_quay_ledgers and collaborates on event_glass_harbor?','user_kian',ids('a_lanternmoth','ap_quay_ledgers','w_kian','er10')),('easy_07','Which Orion Customs user behind alias_basinraven authored post_customs_tag and collaborates on event_iron_wharf?','user_mika',ids('a_basinraven','ap_customs_tag','w_mika','er13')),('easy_08','Which user behind alias_emberglass posted basin_photo from Foundry Row and investigates Amber Veil?','user_nora',ids('a_emberglass','ap_basin_photo','r_basin_photo_1','er15')),('easy_09','Which user behind alias_tideshard authored post_hull_signal and collaborates on Ghost Signal?','user_soren',ids('a_tideshard','ap_hull_signal','er17')),('easy_10','Which Harborlight Transit user behind alias_sablekeel authored post_uplink_note and reports on Ghost Signal?','user_tara',ids('a_sablekeel','ap_uplink_note','w_tara','er18'))] + mid=[('mid_01','Follow alias_docksparrow through post_shift_roster, Dockyard 17, and the Lantern chain. Return the org node id.','org_northbridge_logistics',ids('a_docksparrow','ap_shift_roster','r_shift_roster_1','r_shift_roster_2','tl_supply_leak_2','w_hiro','l_hiro','er02','er01','c02','c03')),('mid_02','Across the Glass Harbor cluster, which user behind alias_lanternmoth links to the Atlas Freight network from thr_quiet_manifest?','user_kian',ids('a_lanternmoth','ap_quay_ledgers','r_quay_ledgers_1','r_quay_ledgers_2','at_quiet_manifest','tl_quiet_manifest_1','tl_quiet_manifest_2','w_kian','w_omar','er10','er11','er12','c13','c14')),('mid_03','Trace alias_basinraven through post_customs_tag, thr_customs_breach, and the Orion Customs collaboration chain. Who is it?','user_mika',ids('a_basinraven','ap_customs_tag','r_customs_tag_1','r_customs_tag_2','at_customs_breach','tl_customs_breach_1','tl_customs_breach_2','w_mika','w_quinn','er13','er14','c15','c16','x05')),('mid_04','In the Ember Tide and Amber Veil overlap, which Foundry Row user behind alias_cinderveil collaborates on Ember Tide?','user_rhea',ids('a_cinderveil','ap_foundry_map','r_foundry_map_1','r_foundry_map_2','at_foundry_watch','tl_foundry_watch_1','tl_foundry_watch_2','at_ember_tide_watch','tl_ember_tide_watch_1','tl_ember_tide_watch_2','w_rhea','w_nora','er15','er16','c17','x03')),('mid_05','Follow alias_tideshard from post_hull_signal into thr_uplink_route and the Harborlight relay. Return the org node id.','org_harborlight_transit',ids('a_tideshard','ap_hull_signal','r_hull_signal_1','r_hull_signal_2','at_uplink_route','tl_uplink_route_1','tl_uplink_route_2','w_soren','w_tara','er17','er18','c18','c19','op_harborlight_transit','x06')),('mid_06','Which Sunmesh user behind alias_frostledger connects post_lantern_route to thr_relay_map and the Sector 9 monitoring chain?','user_leena',ids('a_frostledger','ap_lantern_route','r_lantern_route_1','r_lantern_route_2','at_relay_map','tl_relay_map_1','tl_relay_map_2','w_leena','w_priya','l_leena','op_sunmesh_analytics','er05','c21','c22')),('mid_07','Which user behind alias_emberglass is tied to Amber Veil after combining post_basin_photo, thr_basin_shift, and the Foundry Row investigation chain?','user_nora',ids('a_emberglass','ap_basin_photo','r_basin_photo_1','r_basin_photo_2','at_basin_shift','tl_basin_shift_1','tl_basin_shift_2','w_nora','w_quinn','l_nora','er15','c16','c17','x05')),('mid_08','Combine alias_orchidfox, post_midnight_manifest, thr_supply_leak, and the Lantern to Glass Harbor bridge. Which user starts that chain?','user_ivy',ids('a_orchidfox','ap_midnight_manifest','r_midnight_manifest_1','r_midnight_manifest_2','at_supply_leak','tl_supply_leak_1','tl_supply_leak_2','w_ivy','er06','c01','c12','x01','er10','er12')),('mid_09','Which user behind alias_monsoonbyte sits at the overlap of Blueharbor Media, Project Lantern, Black Kite, and the Ivy connection chain?','user_diya',ids('a_monsoonbyte','ap_drone_parts','r_drone_parts_1','at_supply_leak','tl_supply_leak_1','at_port_audit','tl_port_audit_1','w_diya','w_ivy','w_jules','er04','er06','er09','c04','c12')),('mid_10','Who is the Northbridge user behind alias_steelquill when combining post_relay_schedule, thr_supply_leak, Dockyard 17, and Lantern collaborator edges?','user_bharat',ids('a_steelquill','ap_relay_schedule','r_relay_schedule_1','r_relay_schedule_2','at_supply_leak','tl_supply_leak_1','tl_supply_leak_2','w_bharat','w_hiro','l_bharat','l_hiro','er01','er02','c01','c02'))] + big=list(edges.keys())[:58] + hard=[('high_01','Lantern to Glass Harbor handoff: identify the user behind alias_orchidfox after combining Lantern logistics, Dockyard links, and Atlas Freight bridge evidence.','user_ivy',ids('a_orchidfox','ap_midnight_manifest','r_midnight_manifest_1','r_midnight_manifest_2','at_supply_leak','tl_supply_leak_1','tl_supply_leak_2',['w_ivy','w_bharat','w_hiro','w_kian','w_omar'],['l_ivy','l_bharat','l_hiro','l_kian','l_omar'],['op_northbridge_logistics','op_kestrel_works','op_atlas_freight'],rng('c',1,3),['c12','c13','c14'],['er01','er02','er03','er06','er10','er11','er12'],'at_quiet_manifest','tl_quiet_manifest_1','tl_quiet_manifest_2','ap_quay_ledgers','r_quay_ledgers_1','r_quay_ledgers_2','x01','x04','a_lanternmoth','a_steelquill','a_docksparrow')),('high_02','North Basin to Foundry Row escalation: which user behind alias_basinraven anchors the Iron Wharf side before the Emberline handoff?','user_mika',ids('a_basinraven','ap_customs_tag','r_customs_tag_1','r_customs_tag_2','at_customs_breach','tl_customs_breach_1','tl_customs_breach_2','at_basin_shift','tl_basin_shift_1','tl_basin_shift_2','at_foundry_watch','tl_foundry_watch_1','tl_foundry_watch_2',['w_mika','w_quinn','w_nora','w_rhea'],['l_mika','l_quinn','l_nora','l_rhea'],['op_orion_customs','op_emberline_security'],['c15','c16','c17'],['er13','er14','er15','er16'],'ap_basin_photo','r_basin_photo_1','r_basin_photo_2','ap_foundry_map','r_foundry_map_1','r_foundry_map_2','x02','x03','x05','a_emberglass','a_cinderveil','c23','c24')),('high_03','Harborlight ghost-signal relay: identify the user behind alias_tideshard at the Harborlight / Tidewatch junction.','user_soren',ids('a_tideshard','ap_hull_signal','r_hull_signal_1','r_hull_signal_2','a_sablekeel','ap_uplink_note','r_uplink_note_1','r_uplink_note_2','at_uplink_route','tl_uplink_route_1','tl_uplink_route_2','at_ghost_signal_net','tl_ghost_signal_net_1','tl_ghost_signal_net_2',['w_soren','w_tara','w_faris'],['l_soren','l_tara','l_faris'],['op_harborlight_transit','op_tidewatch_ops'],['c18','c19','c20','c25'],['er03','er17','er18'],'ap_sat_phone_ping','r_sat_phone_ping_1','r_sat_phone_ping_2','at_supply_leak','tl_supply_leak_1','er01','er02','x03','x06','a_nightrelay')),('high_04','Blueharbor to Black Kite to Lantern overlap: which user is the Blueharbor origin behind alias_monsoonbyte?','user_diya',ids('a_monsoonbyte','ap_drone_parts','r_drone_parts_1','r_drone_parts_2','at_port_audit','tl_port_audit_1','tl_port_audit_2','at_supply_leak','tl_supply_leak_1','tl_supply_leak_2',['w_diya','w_jules','w_ivy','w_cyrus'],['l_diya','l_jules','l_ivy','l_cyrus'],['op_blueharbor_media','op_kestrel_works','op_apex_dynamics'],['c04','c08','c09','c12'],['er04','er06','er07','er08','er09'],'a_orchidfox','ap_midnight_manifest','r_midnight_manifest_2','x01','x02','at_relay_map','tl_relay_map_1','w_leena','er05')),('high_05','Sector 9 to Dockyard 17 full relay: which user behind alias_steelquill links the Northbridge chain and the Sunmesh monitoring bridge?','user_bharat',ids('a_steelquill','ap_relay_schedule','r_relay_schedule_1','r_relay_schedule_2','a_frostledger','ap_lantern_route','r_lantern_route_1','r_lantern_route_2','at_relay_map','tl_relay_map_1','tl_relay_map_2','at_supply_leak','tl_supply_leak_1','tl_supply_leak_2',['w_bharat','w_hiro','w_leena','w_priya','w_aria'],['l_bharat','l_hiro','l_leena','l_priya','l_aria'],['op_northbridge_logistics','op_sunmesh_analytics','op_helios_labs'],['c01','c02','c05','c06','c07','c21','c22'],['er01','er02','er05'],'x01','x04','a_docksparrow','a_mapleghost','a_hollowsignal')),('high_06','Foundry Row, North Basin, and Uplink Yard spread: identify the user behind alias_emberglass before the Harborlight relay takes over.','user_nora',ids('a_emberglass','ap_basin_photo','r_basin_photo_1','r_basin_photo_2','a_cinderveil','ap_foundry_map','r_foundry_map_1','r_foundry_map_2','a_sablekeel','ap_uplink_note','r_uplink_note_1','r_uplink_note_2','at_foundry_watch','tl_foundry_watch_1','tl_foundry_watch_2','at_ember_tide_watch','tl_ember_tide_watch_1','tl_ember_tide_watch_2','at_uplink_route','tl_uplink_route_1','tl_uplink_route_2',['w_nora','w_rhea','w_soren','w_tara'],['l_nora','l_rhea','l_soren','l_tara'],['op_emberline_security','op_harborlight_transit'],['c17','c18','c19'],['er15','er16','er17','er18'],'x03','x06')),('high_07','Freight and customs bridge: which Atlas Freight user behind alias_lanternmoth connects Glass Harbor with the Northbridge chain?','user_kian',ids('a_lanternmoth','ap_quay_ledgers','r_quay_ledgers_1','r_quay_ledgers_2','at_quiet_manifest','tl_quiet_manifest_1','tl_quiet_manifest_2',['w_kian','w_omar','w_bharat','w_hiro'],['l_kian','l_omar','l_bharat','l_hiro'],['op_atlas_freight','op_northbridge_logistics'],['c13','c14','c24','c02'],['er10','er11','er12','er01','er02'],'ap_shift_roster','r_shift_roster_1','r_shift_roster_2','ap_midnight_manifest','r_midnight_manifest_1','at_supply_leak','tl_supply_leak_2','x04','a_ironwhisper','a_steelquill','a_docksparrow')),('high_08','Black Kite, Amber Veil, and Iron Wharf overlap: which user behind alias_quartzlotus is the Apex-side collaborator?','user_cyrus',ids('a_quartzlotus','w_cyrus','l_cyrus','op_apex_dynamics','er07','at_port_audit','tl_port_audit_1','ap_drone_parts','r_drone_parts_1','er15','at_basin_shift','tl_basin_shift_1','er13','at_customs_breach','tl_customs_breach_1',['w_ivy','w_nora','w_mika','w_quinn'],['l_ivy','l_nora','l_mika','l_quinn'],['op_kestrel_works','op_emberline_security','op_orion_customs'],['c08','c12','c15','c16','c17'],'x02','x05','a_orchidfox','a_basinraven','a_emberglass')),('high_09','Ghost Signal and Ember Tide relay: which user behind alias_sablekeel is the Harborlight reporting endpoint?','user_tara',ids('a_sablekeel','ap_uplink_note','r_uplink_note_1','r_uplink_note_2','a_tideshard','ap_hull_signal','r_hull_signal_1','r_hull_signal_2','at_ghost_signal_net','tl_ghost_signal_net_1','tl_ghost_signal_net_2','at_uplink_route','tl_uplink_route_1','tl_uplink_route_2','at_ember_tide_watch','tl_ember_tide_watch_1','tl_ember_tide_watch_2',['w_tara','w_soren','w_rhea','w_nora'],['l_tara','l_soren','l_rhea','l_nora'],['op_harborlight_transit','op_emberline_security'],['c18','c19','c17'],['er16','er17','er18'],'x03','x06','a_cinderveil','a_emberglass')),('high_10','End-to-end benchmark sweep: across Lantern, Black Kite, Glass Harbor, Iron Wharf, Ember Tide, and Ghost Signal, which user behind alias_hollowsignal anchors the Sunmesh monitoring side?','user_priya',big)] + for diff,level,specs in [('easy',1,easy),('mid',2,mid),('high',3,hard)]: + for qid,q,a,eids in specs: + qs.append({'task_type':'fixed_trace','question':q,'answer':a,'supporting_edges':sup(eids),'metadata':{'difficulty':diff,'difficulty_level':level,'question_id':qid,'support_nodes':nodes(eids)}}) + def edge_key(e): return (e['src'], e['rel'], e['dst']) + mid_pool = sup(ids('a_orchidfox','ap_midnight_manifest','r_midnight_manifest_1','r_midnight_manifest_2','a_lanternmoth','ap_quay_ledgers','r_quay_ledgers_1','r_quay_ledgers_2','a_basinraven','ap_customs_tag','r_customs_tag_1','r_customs_tag_2','a_tideshard','ap_hull_signal','r_hull_signal_1','at_supply_leak','tl_supply_leak_1','at_quiet_manifest','tl_quiet_manifest_1','er01','er02','er06','er10','c01','c02','c13')) + hard_pool = sup(list(edges.keys())[:120]) + for q in qs: + current = {edge_key(e) for e in q['supporting_edges']} + diff = q['metadata']['difficulty'] + if diff == 'mid': + pool = mid_pool + target = 17 + elif diff == 'high': + pool = hard_pool + target = 50 + else: + continue + for e in pool: + if q['metadata']['support_nodes'] >= target: + break + k = edge_key(e) + if k not in current: + q['supporting_edges'].append(dict(e)) + current.add(k) + q['metadata']['support_nodes'] = len({n for edge in q['supporting_edges'] for n in (edge['src'], edge['dst'])}) + return qs + +def main(): + nodes,edges=build(); questions=mk_questions(edges) + payload={'seeding':{'seeded_nodes':nodes,'seeded_edges':list(edges.values()),'seeded_questions':questions,'llm_generate_remaining_graph':True,'llm_generate_remaining_tasks':False,'llm_generated_edge_budget':48,'llm_generated_task_budget':0,'llm_generation_parallel':True,'llm_generation_workers':4,'llm_generation_retries':3,'allow_template_fallback_on_llm_failure':False}} + out=Path('datasets/fixed_levels/seed_fixed_levels.json'); out.write_text(json.dumps(payload,indent=2),encoding='utf-8') + counts=Counter(q['metadata']['difficulty'] for q in questions) + stats={k:sorted(q['metadata']['support_nodes'] for q in questions if q['metadata']['difficulty']==k) for k in ['easy','mid','high']} + print(json.dumps({'nodes':len(nodes),'edges':len(edges),'questions':len(questions),'difficulty_counts':dict(counts),'support_nodes':stats},indent=2)) + +if __name__=='__main__': + main() diff --git a/scripts/run_openai_baseline.py b/scripts/run_openai_baseline.py new file mode 100644 index 0000000000000000000000000000000000000000..88a6822ba549e0e778dca402ab165ddeedd60618 --- /dev/null +++ b/scripts/run_openai_baseline.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +import argparse +import json +import os + +from osint_env.baselines import OpenAIBaselineConfig, OpenAIBaselineRunner + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description="Run the reproducible OpenAI baseline on the fixed-level OSINT benchmark.") + parser.add_argument("--config", default="datasets/fixed_levels/shared_config_fixed_levels.json", help="Shared config JSON.") + parser.add_argument("--seed-file", default="datasets/fixed_levels/seed_fixed_levels.json", help="Fixed seed file JSON.") + parser.add_argument("--output", default="artifacts/baselines/openai_fixed_levels_latest.json", help="Baseline result JSON output path.") + parser.add_argument("--leaderboard", default="artifacts/baselines/openai_fixed_levels_leaderboard.json", help="Leaderboard JSON path.") + parser.add_argument("--dashboard", default="artifacts/baselines/openai_fixed_levels_dashboard.html", help="Dashboard HTML path.") + parser.add_argument("--run-name", default="openai_fixed_levels_baseline", help="Leaderboard run name.") + parser.add_argument("--model", default="gpt-5-nano", help="OpenAI chat model name.") + parser.add_argument("--openai-base-url", default="https://api.openai.com/v1", help="OpenAI-compatible base URL.") + parser.add_argument("--openai-api-key", default="", help="OpenAI API key override.") + parser.add_argument("--openai-api-key-env", default="OPENAI_API_KEY", help="Environment variable name for the API key.") + parser.add_argument("--episodes", type=int, default=30, help="Number of episodes to evaluate.") + parser.add_argument("--max-steps", type=int, default=8, help="Episode step budget to keep runs bounded.") + parser.add_argument("--temperature", type=float, default=0.0, help="Sampling temperature.") + parser.add_argument("--max-tokens", type=int, default=256, help="Maximum completion tokens per step.") + parser.add_argument("--timeout-seconds", type=int, default=60, help="Per-request timeout.") + parser.add_argument("--seed", type=int, default=7, help="Request seed offset used for repeatable runs.") + parser.add_argument("--skip-leaderboard", action="store_true", help="Do not append the run to the leaderboard file.") + return parser + + +def main() -> None: + args = build_parser().parse_args() + api_key = args.openai_api_key or os.getenv(args.openai_api_key_env, "") + config = OpenAIBaselineConfig( + shared_config_path=args.config, + seed_file=args.seed_file, + output_path=args.output, + leaderboard_path=args.leaderboard, + dashboard_path=args.dashboard, + run_name=args.run_name, + model=args.model, + base_url=args.openai_base_url, + api_key=api_key, + api_key_env=args.openai_api_key_env, + temperature=args.temperature, + max_tokens=args.max_tokens, + timeout_seconds=args.timeout_seconds, + episodes=args.episodes, + max_steps=args.max_steps, + seed=args.seed, + append_leaderboard=not args.skip_leaderboard, + ) + result = OpenAIBaselineRunner(config).run() + print(json.dumps({"summary": result["summary"], "output": args.output, "dashboard": args.dashboard}, indent=2, sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/scripts/space_start.sh b/scripts/space_start.sh new file mode 100644 index 0000000000000000000000000000000000000000..30c52a52f94b68fafcce3c9fd35ecf1364b81e3e --- /dev/null +++ b/scripts/space_start.sh @@ -0,0 +1,35 @@ +#!/bin/sh +set -eu + +_is_true() { + case "${1:-}" in + 1|true|TRUE|yes|YES|on|ON) return 0 ;; + *) return 1 ;; + esac +} + +ENV_CONFIG_PATH="${TRAIN_ENV_CONFIG_PATH:-config/shared_config.json}" +TRAIN_CONFIG_PATH="${TRAIN_SELF_PLAY_CONFIG_PATH:-config/self_play_training_hf_a10g_smoke.json}" +RUN_FLAG="${RUN_SELF_PLAY_TRAINING:-0}" +DRY_RUN_FLAG="${RUN_SELF_PLAY_DRY_RUN:-0}" + +if _is_true "$RUN_FLAG"; then + echo "[space_start] RUN_SELF_PLAY_TRAINING enabled." + echo "[space_start] Training start: $(date -u +"%Y-%m-%dT%H:%M:%SZ")" + echo "[space_start] Env config: ${ENV_CONFIG_PATH}" + echo "[space_start] Train config: ${TRAIN_CONFIG_PATH}" + if _is_true "$DRY_RUN_FLAG"; then + echo "[space_start] Running self-play in dry-run mode." + osint-env train-self-play --config "${ENV_CONFIG_PATH}" --train-config "${TRAIN_CONFIG_PATH}" --dry-run + else + echo "[space_start] Running self-play training." + osint-env train-self-play --config "${ENV_CONFIG_PATH}" --train-config "${TRAIN_CONFIG_PATH}" + fi + echo "[space_start] Self-play command completed." + echo "[space_start] Training end: $(date -u +"%Y-%m-%dT%H:%M:%SZ")" +else + echo "[space_start] RUN_SELF_PLAY_TRAINING disabled. Skipping self-play run." +fi + +echo "[space_start] Starting API server." +exec uvicorn server:app --host 0.0.0.0 --port "${PORT:-7860}" diff --git a/scripts/test_ollama_space.py b/scripts/test_ollama_space.py new file mode 100644 index 0000000000000000000000000000000000000000..540f624cd684fc5e026544e36aec61811d6e6436 --- /dev/null +++ b/scripts/test_ollama_space.py @@ -0,0 +1,185 @@ +from __future__ import annotations + +import json +import os +import sys +from typing import Any + +import requests + +from osint_env.baselines.openai_runner import SYSTEM_PROMPT, build_action_tools +from osint_env.llm.interface import OllamaLLMClient + +SPACE_URL = os.getenv("SPACE_URL", "https://siddeshwar1625-osint.hf.space").rstrip("/") +OLLAMA_BASE = os.getenv("OLLAMA_BASE_URL", "http://127.0.0.1:11434").rstrip("/") +MODEL = os.getenv("OLLAMA_MODEL", "qwen3:2b") +MAX_STEPS = int(os.getenv("MAX_STEPS", "8")) +REQUEST_TIMEOUT = int(os.getenv("REQUEST_TIMEOUT", "90")) +TASK_INDICES = [int(x.strip()) for x in os.getenv("TASK_INDICES", "0").split(",") if x.strip()] + + +def _message_text(message: Any) -> str: + content = getattr(message, "content", "") + if isinstance(content, str): + return content + if isinstance(content, list): + parts: list[str] = [] + for item in content: + if isinstance(item, dict) and item.get("type") == "text": + parts.append(str(item.get("text", ""))) + return "\n".join(part for part in parts if part) + return str(content or "") + + +def _assistant_tool_call_id(message: dict[str, Any]) -> str | None: + tool_calls = list(message.get("tool_calls", [])) + if not tool_calls: + return None + tool_call_id = tool_calls[0].get("id") + return str(tool_call_id) if tool_call_id else None + + +def _tool_result_message(assistant_message: dict[str, Any], result: dict[str, Any]) -> dict[str, Any] | None: + tool_call_id = _assistant_tool_call_id(assistant_message) + if not tool_call_id: + return None + return { + "role": "tool", + "tool_call_id": tool_call_id, + "content": json.dumps(result, sort_keys=True), + } + + +def _decode_action(tool_name: str, args: dict[str, Any]) -> dict[str, Any]: + if tool_name == "submit_answer": + return {"action_type": "ANSWER", "payload": {"answer": str(args.get("answer", "")).strip()}} + if tool_name == "add_edge": + return { + "action_type": "ADD_EDGE", + "payload": { + "src": str(args.get("src", "")).strip(), + "rel": str(args.get("rel", "")).strip(), + "dst": str(args.get("dst", "")).strip(), + "confidence": float(args.get("confidence", 1.0)), + }, + } + return {"action_type": "CALL_TOOL", "payload": {"tool_name": tool_name, "args": dict(args)}} + + +def _format_action(action: dict[str, Any]) -> str: + action_type = str(action.get("action_type", "")) + payload = dict(action.get("payload", {})) + if action_type == "ANSWER": + return f"answer({payload.get('answer', 'unknown')})" + if action_type == "ADD_EDGE": + return ( + "add_edge(" + f"{payload.get('src', '')}," + f"{payload.get('rel', '')}," + f"{payload.get('dst', '')}," + f"{float(payload.get('confidence', 1.0)):.2f}" + ")" + ) + tool_name = str(payload.get("tool_name", "tool")) + args = dict(payload.get("args", {})) + if not args: + return f"{tool_name}()" + arg_str = ",".join(f"{key}={value}" for key, value in sorted(args.items())) + return f"{tool_name}({arg_str})" + + +def get_model_action(client: OllamaLLMClient, messages: list[dict[str, Any]], tools: list[dict[str, Any]]) -> tuple[dict[str, Any], dict[str, Any]]: + llm_resp = client.generate(messages, tools) + content = llm_resp.content or "" + tool_calls = list(llm_resp.tool_calls or []) + if not tool_calls: + return {"action_type": "ANSWER", "payload": {"answer": content.strip() or "unknown"}}, { + "role": "assistant", + "content": content, + } + + tool_call = tool_calls[0] + tool_name = str(tool_call.get("tool_name", "")) + args = dict(tool_call.get("args", {})) + assistant_message = { + "role": "assistant", + "content": content, + "tool_calls": [ + { + "id": "local", + "type": "function", + "function": {"name": tool_name, "arguments": json.dumps(args, sort_keys=True)}, + } + ], + } + return _decode_action(tool_name, args), assistant_message + + +def main() -> None: + try: + ping = requests.get(f"{SPACE_URL}/healthz", timeout=REQUEST_TIMEOUT) + ping.raise_for_status() + print(f"Space health: {ping.json()}") + except Exception as exc: + raise SystemExit(f"Space health check failed: {exc}") from exc + + client = OllamaLLMClient(model=MODEL, base_url=OLLAMA_BASE, timeout_seconds=REQUEST_TIMEOUT) + tools = build_action_tools() + + for task_index in TASK_INDICES: + print(f"Resetting task {task_index} via {SPACE_URL}/openenv/reset") + resp = requests.post(f"{SPACE_URL}/openenv/reset", json={"task_index": task_index}, timeout=REQUEST_TIMEOUT) + resp.raise_for_status() + data = resp.json() + session_id = str(data.get("session_id")) + observation = data.get("observation", {}) + + messages: list[dict[str, Any]] = [ + {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "user", "content": json.dumps(observation, indent=2, sort_keys=True)}, + ] + + done = bool(data.get("done", False)) + step = 0 + rewards: list[float] = [] + + while not done and step < MAX_STEPS: + step += 1 + action, assistant_message = get_model_action(client, messages, tools) + error = None + try: + result = requests.post( + f"{SPACE_URL}/openenv/step", + json={ + "session_id": session_id, + "action_type": action["action_type"], + "payload": action["payload"], + }, + timeout=REQUEST_TIMEOUT, + ) + result.raise_for_status() + result = result.json() + except Exception as exc: + error = str(exc) + print(f"Step {step}: request failed: {error}") + break + + reward = float(result.get("reward", 0.0) or 0.0) + done = bool(result.get("done", False)) + rewards.append(reward) + print(f"Step {step}: action={_format_action(action)} reward={reward:.3f} done={done} error={error}") + + messages.append(assistant_message) + tool_message = _tool_result_message(assistant_message, result) + if tool_message is not None: + messages.append(tool_message) + + print(f"Episode finished. steps={step} total_reward={sum(rewards):.3f} rewards={rewards}") + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print("Interrupted", file=sys.stderr) + sys.exit(1) diff --git a/scripts/validate_release.py b/scripts/validate_release.py new file mode 100644 index 0000000000000000000000000000000000000000..4aa09322ff18b8a6a5fee31aceb14b62626753a9 --- /dev/null +++ b/scripts/validate_release.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +import json +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +if str(ROOT) not in sys.path: + sys.path.insert(0, str(ROOT)) + +from osint_env.validation import run_validation_suite + + +def main() -> int: + result = run_validation_suite() + print(json.dumps(result, indent=2, sort_keys=True)) + return 0 if result["passed"] else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/server.py b/server.py new file mode 100644 index 0000000000000000000000000000000000000000..e18181a051826f34f601b6797fa40113d1fa0ae9 --- /dev/null +++ b/server.py @@ -0,0 +1,564 @@ +from __future__ import annotations + +import json +import os +from collections import Counter +from functools import lru_cache +from pathlib import Path +from threading import Lock +from typing import Any +from uuid import uuid4 + +from fastapi import FastAPI, HTTPException, Request +from fastapi.responses import FileResponse, HTMLResponse, JSONResponse + +from osint_env.api import ( + OpenEnvActionRequest, + OpenEnvInferenceReportRequest, + OpenEnvInferenceReportResponse, + OpenEnvObservationModel, + OpenEnvResetRequest, + OpenEnvResponseEnvelope, + OpenEnvTaskSummary, +) +from osint_env.config import clone_environment_config, load_seeding_config, load_shared_config +from osint_env.domain.models import Action, ActionType +from osint_env.env.environment import OSINTEnvironment +from osint_env.eval.leaderboard import load_leaderboard +from osint_env.eval.runner import run_evaluation +from osint_env.llm import build_llm_client +from osint_env.viz import export_dashboard + + +SPACE_CONFIG_PATH = Path(os.getenv("OSINT_ENV_CONFIG", "datasets/fixed_levels/shared_config_fixed_levels.json")) +SPACE_SEED_PATH = Path(os.getenv("OSINT_ENV_SEED_FILE", "datasets/fixed_levels/seed_fixed_levels.json")) +SPACE_PROVIDER = os.getenv("OSINT_SPACE_LLM_PROVIDER", "mock") +SPACE_MODEL = os.getenv("OSINT_SPACE_LLM_MODEL", "gpt-4o-mini") +SPACE_PORT = int(os.getenv("PORT", "7860")) +SPACE_DASHBOARD = Path("artifacts/space_dashboard.html") +LATEST_BASELINE_OUTPUT = Path("artifacts/baselines/openai_fixed_levels_latest.json") +LATEST_EVALUATION_OUTPUT = Path("artifacts/latest_evaluation.json") +OPENENV_SPEC_PATH = Path("openenv.yaml") + +_SESSION_LOCK = Lock() +_SESSIONS: dict[str, OSINTEnvironment] = {} +_RESET_COUNTER = 0 +_LATEST_SESSION_ID: str | None = None + + +def _load_json(path: Path) -> dict[str, Any] | None: + if not path.exists(): + return None + try: + payload = json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + return None + return payload if isinstance(payload, dict) else None + + +def _path_mtime(path: Path) -> float: + try: + return path.stat().st_mtime + except OSError: + return 0.0 + + +def _build_environment() -> OSINTEnvironment: + shared = load_shared_config(SPACE_CONFIG_PATH) + env_cfg = clone_environment_config(shared.environment) + if SPACE_SEED_PATH.exists(): + env_cfg.seeding = load_seeding_config(SPACE_SEED_PATH) + env_cfg.llm.provider = SPACE_PROVIDER + env_cfg.llm.model = SPACE_MODEL + try: + llm = build_llm_client(env_cfg.llm) + except Exception: + env_cfg.llm.provider = "mock" + llm = build_llm_client(env_cfg.llm) + return OSINTEnvironment(env_cfg, llm=llm) + + +def _serialize_observation(observation: Any) -> OpenEnvObservationModel: + return OpenEnvObservationModel( + tool_outputs=list(observation.tool_outputs), + graph_snapshot=dict(observation.graph_snapshot), + action_history=list(observation.action_history), + task=dict(observation.task), + ) + + +def _safe_session_info(info: dict[str, Any]) -> dict[str, Any]: + return { + "step_count": int(info.get("step_count", 0)), + "total_reward": float(info.get("total_reward", 0.0)), + "tool_calls": int(info.get("tool_calls", 0)), + "redundant_tool_calls": int(info.get("redundant_tool_calls", 0)), + "task_answer": str(info.get("task_answer", "")), + "agent_answer": "" if info.get("agent_answer") is None else str(info.get("agent_answer", "")), + "graph_f1": float(info.get("graph_f1", 0.0)), + "reward_components": dict(info.get("reward_components", {})), + } + + +def _task_summaries(env: OSINTEnvironment) -> list[OpenEnvTaskSummary]: + return [ + OpenEnvTaskSummary( + task_id=task.task_id, + task_type=task.task_type, + question=task.question, + difficulty=str(task.metadata.get("difficulty", "unknown")), + grader=( + dict(task.metadata.get("grader", {})) + if isinstance(task.metadata.get("grader"), dict) + else { + "type": "exact_match", + "answer_type": "node_id", + "case_sensitive": True, + } + ), + ) + for task in env.tasks + ] + + +def _resolve_task_index(env: OSINTEnvironment, request: OpenEnvResetRequest) -> int: + global _RESET_COUNTER + if request.task_index is not None: + task_index = int(request.task_index) + if task_index < 0 or task_index >= len(env.tasks): + raise HTTPException(status_code=400, detail=f"Invalid task_index {task_index}") + return task_index + if request.task_id: + for idx, task in enumerate(env.tasks): + if task.task_id == request.task_id: + return idx + raise HTTPException(status_code=400, detail=f"Unknown task_id {request.task_id}") + with _SESSION_LOCK: + task_index = _RESET_COUNTER % max(1, len(env.tasks)) + _RESET_COUNTER += 1 + return task_index + + +def _get_session_env(session_id: str) -> OSINTEnvironment: + with _SESSION_LOCK: + env = _SESSIONS.get(session_id) + if env is None: + raise HTTPException(status_code=404, detail=f"Unknown session_id {session_id}") + return env + + +def _store_session(session_id: str, env: OSINTEnvironment) -> None: + global _LATEST_SESSION_ID + with _SESSION_LOCK: + _SESSIONS[session_id] = env + _LATEST_SESSION_ID = session_id + + +def _latest_session_id() -> str: + with _SESSION_LOCK: + if _LATEST_SESSION_ID and _LATEST_SESSION_ID in _SESSIONS: + return _LATEST_SESSION_ID + if _SESSIONS: + return next(reversed(_SESSIONS)) + raise HTTPException(status_code=404, detail="No active session. Call /reset first.") + + +def _resolve_session_id(session_id: str | None) -> str: + token = str(session_id or "").strip() + if token: + return token + return _latest_session_id() + + +def _task_lookup(env: OSINTEnvironment) -> dict[str, Any]: + return {task.task_id: task for task in env.tasks} + + +def _normalize_episode_rows(env: OSINTEnvironment, episodes: list[dict[str, Any]]) -> list[dict[str, Any]]: + tasks_by_id = _task_lookup(env) + normalized: list[dict[str, Any]] = [] + for episode in episodes: + row = dict(episode) + task = tasks_by_id.get(str(row.get("task_id", ""))) + if task is not None: + row.setdefault("task_type", task.task_type) + row.setdefault("question", task.question) + row.setdefault("task_answer", task.answer) + row.setdefault( + "truth_edges", + [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in task.supporting_edges + ], + ) + row.setdefault("pred_edges", []) + row.setdefault("reward_components", {}) + row.setdefault("graph_f1", 0.0) + row.setdefault("reward", 0.0) + row.setdefault("steps", 0) + row.setdefault("tool_calls", 0) + row.setdefault("success", 0) + normalized.append(row) + return normalized + + +@lru_cache(maxsize=1) +def _base_environment_snapshot() -> dict[str, Any]: + env = _build_environment() + difficulty_counts = Counter(str(task.metadata.get("difficulty", "unknown")) for task in env.tasks) + return { + "task_count": len(env.tasks), + "difficulty_counts": dict(difficulty_counts), + "action_space": ["CALL_TOOL", "ADD_EDGE", "ANSWER"], + "observation_space": { + "tool_outputs": "Last tool results and memory hits.", + "graph_snapshot": "Current working graph edge snapshot.", + "action_history": "Recent action/reward trace.", + "task": "Task id, task type, and question.", + }, + "task_types": sorted({task.task_type for task in env.tasks}), + "config": { + "seed": env.config.seed, + "max_steps": env.config.max_steps, + "swarm_enabled": env.config.swarm.enabled, + "llm_provider": env.config.llm.provider, + "llm_model": env.config.llm.model, + }, + } + + +@lru_cache(maxsize=1) +def _preview_snapshot() -> dict[str, Any]: + env = _build_environment() + evaluation = run_evaluation(env, episodes=3, return_details=True, llm=build_llm_client(env.config.llm)) + dashboard_path = export_dashboard( + env=env, + evaluation=evaluation, + leaderboard_records=[], + output_path=str(SPACE_DASHBOARD), + ) + snapshot = dict(_base_environment_snapshot()) + snapshot["summary"] = evaluation["summary"] + snapshot["dashboard_path"] = dashboard_path + return snapshot + + +def _space_snapshot() -> dict[str, Any]: + snapshot = dict(_base_environment_snapshot()) + + baseline_payload = _load_json(LATEST_BASELINE_OUTPUT) + evaluation_payload = _load_json(LATEST_EVALUATION_OUTPUT) + + candidates: list[tuple[float, str, dict[str, Any]]] = [] + if baseline_payload is not None and isinstance(baseline_payload.get("summary"), dict): + candidates.append((_path_mtime(LATEST_BASELINE_OUTPUT), "baseline_output", baseline_payload)) + if evaluation_payload is not None and isinstance(evaluation_payload.get("summary"), dict): + candidates.append((_path_mtime(LATEST_EVALUATION_OUTPUT), "latest_evaluation", evaluation_payload)) + + if candidates: + _, source, payload = max(candidates, key=lambda item: item[0]) + snapshot["summary"] = dict(payload["summary"]) + snapshot["source"] = source + if source == "baseline_output": + dashboard_path = Path( + str( + ((payload.get("run") or {}).get("dashboard_path")) + or "artifacts/baselines/openai_fixed_levels_dashboard.html" + ) + ) + if dashboard_path.exists(): + snapshot["dashboard_path"] = str(dashboard_path) + return snapshot + + env = _build_environment() + dashboard_path = export_dashboard( + env=env, + evaluation=payload, + leaderboard_records=[], + output_path=str(SPACE_DASHBOARD), + ) + snapshot["dashboard_path"] = dashboard_path + return snapshot + + preview = _preview_snapshot() + preview["source"] = "preview" + return preview + + +app = FastAPI(title="OSINT OpenEnv Space", version="0.1.0") + + +@app.get("/", response_class=HTMLResponse) +def home() -> str: + snapshot = _space_snapshot() + summary = snapshot["summary"] + difficulty_html = "".join( + f"
  • {level}: {count}
  • " + for level, count in sorted(snapshot["difficulty_counts"].items()) + ) + task_type_html = "".join(f"
  • {task_type}
  • " for task_type in snapshot["task_types"]) + return f""" + + + + + OSINT OpenEnv Space + + + +
    +
    +
    +

    OSINT OpenEnv Space

    +

    A containerized OpenEnv-compatible benchmark for synthetic OSINT reasoning over profiles, forum threads, posts, aliases, organizations, locations, and event links.

    +

    The Space boots with the fixed-level benchmark so visitors get a stable environment snapshot instead of a different graph every restart.

    + Open Dashboard + Environment JSON +
    +
    +

    Included Snapshot

    +
    +
    Tasks
    {snapshot["task_count"]}
    +
    Provider
    {snapshot["config"]["llm_provider"]}
    +
    Score
    {summary["leaderboard_score"]:.3f}
    +
    Success
    {summary["task_success_rate"]:.3f}
    +
    +
    +
    + +
    +
    +

    Action Space

    +
      +
    • CALL_TOOL: query platform views or semantic memory.
    • +
    • ADD_EDGE: add a hypothesized relation to the working graph.
    • +
    • ANSWER: submit the final node id answer.
    • +
    +
    +
    +

    Difficulty Mix

    +
      {difficulty_html}
    +
    +
    +

    Task Families

    +
      {task_type_html}
    +
    +
    +
    + +""" + + +@app.get("/healthz") +def healthz() -> JSONResponse: + return JSONResponse({"status": "ok"}) + + +@app.get("/health") +def health() -> JSONResponse: + return healthz() + + +@app.get("/openenv.yaml") +def openenv_spec() -> FileResponse: + return FileResponse(OPENENV_SPEC_PATH, media_type="text/yaml") + + +@app.get("/api/environment") +def environment_metadata() -> JSONResponse: + return JSONResponse(_space_snapshot()) + + +@app.get("/openenv/tasks", response_model=list[OpenEnvTaskSummary]) +def openenv_tasks() -> list[OpenEnvTaskSummary]: + env = _build_environment() + return _task_summaries(env) + + +@app.post("/openenv/reset", response_model=OpenEnvResponseEnvelope) +@app.post("/openenv/reset/", response_model=OpenEnvResponseEnvelope, include_in_schema=False) +@app.post("/reset", response_model=OpenEnvResponseEnvelope, include_in_schema=False) +@app.post("/reset/", response_model=OpenEnvResponseEnvelope, include_in_schema=False) +async def openenv_reset(request: Request) -> OpenEnvResponseEnvelope: + env = _build_environment() + raw_body = await request.body() + if not raw_body.strip(): + payload: dict[str, Any] = {} + else: + try: + parsed_payload = json.loads(raw_body) + except json.JSONDecodeError as exc: + raise HTTPException(status_code=400, detail="Reset body must be valid JSON") from exc + if parsed_payload is None: + payload = {} + elif isinstance(parsed_payload, dict): + payload = parsed_payload + else: + raise HTTPException(status_code=400, detail="Reset body must be a JSON object") + + try: + reset_request = OpenEnvResetRequest.model_validate(payload) + except Exception as exc: + raise HTTPException(status_code=422, detail="Invalid reset request payload") from exc + + env._task_idx = _resolve_task_index(env, reset_request) + observation = env.reset() + session_id = str(uuid4()) + _store_session(session_id, env) + return OpenEnvResponseEnvelope( + session_id=session_id, + observation=_serialize_observation(observation), + reward=0.0, + done=False, + info=_safe_session_info(env._info()), + ) + + +@app.post("/openenv/step", response_model=OpenEnvResponseEnvelope) +@app.post("/openenv/step/", response_model=OpenEnvResponseEnvelope, include_in_schema=False) +@app.post("/step", response_model=OpenEnvResponseEnvelope, include_in_schema=False) +@app.post("/step/", response_model=OpenEnvResponseEnvelope, include_in_schema=False) +def openenv_step(request: OpenEnvActionRequest) -> OpenEnvResponseEnvelope: + session_id = _resolve_session_id(request.session_id) + env = _get_session_env(session_id) + action_type_raw = request.resolved_action_type().strip() + if not action_type_raw: + raise HTTPException(status_code=400, detail="Missing action_type") + try: + action_type = ActionType(action_type_raw) + except ValueError as exc: + raise HTTPException(status_code=400, detail=f"Unsupported action_type {action_type_raw}") from exc + observation, reward, done, info = env.step(Action(action_type=action_type, payload=request.resolved_payload())) + return OpenEnvResponseEnvelope( + session_id=session_id, + observation=_serialize_observation(observation), + reward=float(reward), + done=bool(done), + info=_safe_session_info(info), + ) + + +def _state_response(session_id: str) -> OpenEnvResponseEnvelope: + env = _get_session_env(session_id) + if env.state is None: + raise HTTPException(status_code=400, detail="Session has not been reset yet") + return OpenEnvResponseEnvelope( + session_id=session_id, + observation=_serialize_observation(env._observation()), + reward=0.0, + done=bool(env.state.done), + info=_safe_session_info(env._info()), + ) + + +@app.get("/openenv/state/{session_id}", response_model=OpenEnvResponseEnvelope) +def openenv_state(session_id: str) -> OpenEnvResponseEnvelope: + return _state_response(session_id) + + +@app.get("/openenv/state", response_model=OpenEnvResponseEnvelope, include_in_schema=False) +@app.get("/state", response_model=OpenEnvResponseEnvelope, include_in_schema=False) +@app.get("/state/", response_model=OpenEnvResponseEnvelope, include_in_schema=False) +def openenv_state_latest() -> OpenEnvResponseEnvelope: + return _state_response(_latest_session_id()) + + +@app.post("/openenv/report_inference", response_model=OpenEnvInferenceReportResponse) +def openenv_report_inference(request: OpenEnvInferenceReportRequest) -> OpenEnvInferenceReportResponse: + env = _build_environment() + normalized_episodes = _normalize_episode_rows(env, list(request.episodes)) + payload = { + "run": dict(request.run), + "summary": dict(request.summary), + "episodes": normalized_episodes, + } + LATEST_EVALUATION_OUTPUT.parent.mkdir(parents=True, exist_ok=True) + LATEST_EVALUATION_OUTPUT.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8") + dashboard_path = export_dashboard( + env=env, + evaluation=payload, + leaderboard_records=load_leaderboard("artifacts/baselines/openai_fixed_levels_leaderboard.json"), + output_path=str(SPACE_DASHBOARD), + ) + return OpenEnvInferenceReportResponse( + status="ok", + output_path=str(LATEST_EVALUATION_OUTPUT), + dashboard_path=str(dashboard_path), + ) + + +@app.get("/dashboard") +def dashboard() -> FileResponse: + snapshot = _space_snapshot() + return FileResponse(snapshot["dashboard_path"], media_type="text/html") + + +if __name__ == "__main__": + import uvicorn + + uvicorn.run("server:app", host="0.0.0.0", port=SPACE_PORT) diff --git a/server/app.py b/server/app.py new file mode 100644 index 0000000000000000000000000000000000000000..0fcf128fd22e0af85ebe3f83e325b3f84f06279d --- /dev/null +++ b/server/app.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +import importlib.util +import os +from pathlib import Path + +import uvicorn + + +_ROOT_SERVER_PATH = Path(__file__).resolve().parents[1] / "server.py" +_SPEC = importlib.util.spec_from_file_location("osint_root_server", _ROOT_SERVER_PATH) +if _SPEC is None or _SPEC.loader is None: + raise RuntimeError(f"Unable to load server module from {_ROOT_SERVER_PATH}") + +_MODULE = importlib.util.module_from_spec(_SPEC) +_SPEC.loader.exec_module(_MODULE) +app = _MODULE.app + + +def main() -> None: + port = int(os.getenv("PORT", "7860")) + uvicorn.run("server.app:app", host="0.0.0.0", port=port) + + +if __name__ == "__main__": + main() diff --git a/src/osint_env/__init__.py b/src/osint_env/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..baddf8bf6a3ce397a17fd78ccee11fcb6c8446e8 --- /dev/null +++ b/src/osint_env/__init__.py @@ -0,0 +1,5 @@ +"""OSINT RL environment package.""" + +from .env.environment import OSINTEnvironment + +__all__ = ["OSINTEnvironment"] diff --git a/src/osint_env/agents/__init__.py b/src/osint_env/agents/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..884d50b613e3bf492ff2ea1fbd2aec4656833975 --- /dev/null +++ b/src/osint_env/agents/__init__.py @@ -0,0 +1,7 @@ +"""Agent implementations.""" + +from osint_env.agents.single_agent import SingleAgentRunner +from osint_env.agents.swarm_agent import SwarmAgentRunner + +__all__ = ["SingleAgentRunner", "SwarmAgentRunner"] + diff --git a/src/osint_env/agents/single_agent.py b/src/osint_env/agents/single_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..7e523af80f778911a42486bf380997c982ce10fd --- /dev/null +++ b/src/osint_env/agents/single_agent.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from osint_env.domain.models import Action, ActionType +from osint_env.env.environment import OSINTEnvironment +from osint_env.llm.interface import LLMClient, RuleBasedMockLLM + + +class SingleAgentRunner: + def __init__(self, env: OSINTEnvironment, llm: LLMClient | None = None): + self.env = env + self.llm = llm or RuleBasedMockLLM() + + def run_episode(self) -> dict: + obs = self.env.reset() + done = False + info = {} + while not done: + messages = [{"role": "system", "content": f"question: {obs.task['question']}"}] + tools = [] + try: + llm_resp = self.llm.generate(messages, tools) + planned_calls = llm_resp.tool_calls[:2] + except Exception: + planned_calls = [] + + for call in planned_calls: + obs, _, done, info = self.env.step(Action(ActionType.CALL_TOOL, call)) + if done: + break + if done: + break + answer_guess = self._heuristic_answer(obs.task["question"]) + obs, _, done, info = self.env.step(Action(ActionType.ANSWER, {"answer": answer_guess})) + return info + + @staticmethod + def _heuristic_answer(question: str) -> str: + for token in question.replace("?", "").split(): + if token.startswith("alias_") or token.startswith("user_"): + return token + return "unknown" diff --git a/src/osint_env/agents/swarm_agent.py b/src/osint_env/agents/swarm_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..534d526aea89593f452655f3e95284e1d957d4df --- /dev/null +++ b/src/osint_env/agents/swarm_agent.py @@ -0,0 +1,209 @@ +from __future__ import annotations + +import re +from typing import Any + +from osint_env.domain.models import Action, ActionType +from osint_env.env.environment import OSINTEnvironment +from osint_env.env.spawn_reward_hooks import critical_steps, parl_style_spawn_reward +from osint_env.llm.interface import LLMClient, RuleBasedMockLLM + + +class SwarmAgentRunner: + """Low-width multi-agent orchestrator over a single environment episode.""" + + def __init__(self, env: OSINTEnvironment, llm: LLMClient | None = None): + self.env = env + self.llm = llm or RuleBasedMockLLM() + + def run_episode(self) -> dict[str, Any]: + obs = self.env.reset() + done = False + info: dict[str, Any] = {} + + swarm_cfg = self.env.config.swarm + spawn_cfg = self.env.config.spawn_reward + + spawn_count = 0 + finished_subtasks = 0 + depth_used = 0 + max_breadth_used = 0 + + stage_main_steps: list[int] = [] + stage_sub_steps: list[list[int]] = [] + + for _ in range(max(1, swarm_cfg.planner_rounds)): + if done: + break + + active_agents = max(1, min(swarm_cfg.max_agents, swarm_cfg.max_breadth, swarm_cfg.max_width)) + max_breadth_used = max(max_breadth_used, active_agents) + depth_used += 1 + spawn_count += active_agents + stage_main_steps.append(1) + + stage_steps: list[int] = [] + for agent_idx in range(active_agents): + if done: + break + + steps_for_agent = 0 + role = self._agent_role(agent_idx) + planned_calls = self._tool_plan( + obs=obs, + agent_idx=agent_idx, + role=role, + limit=swarm_cfg.tools_per_agent, + ) + for call in planned_calls: + obs, _, done, info = self.env.step(Action(ActionType.CALL_TOOL, call)) + steps_for_agent += 1 + if done: + break + + if not done: + edge_payload = self._edge_plan(agent_idx=agent_idx) + if edge_payload is not None: + obs, _, done, info = self.env.step(Action(ActionType.ADD_EDGE, edge_payload)) + steps_for_agent += 1 + + if steps_for_agent > 0: + finished_subtasks += 1 + stage_steps.append(steps_for_agent) + + stage_sub_steps.append(stage_steps) + + if depth_used >= swarm_cfg.max_depth: + break + + if not done: + answer_guess = self._vote_answer() + obs, _, done, info = self.env.step(Action(ActionType.ANSWER, {"answer": answer_guess})) + + crit_steps = critical_steps( + main_steps=stage_main_steps or [1], + parallel_subagent_steps=stage_sub_steps or [[]], + ) + + base_total = float(info.get("total_reward", 0.0)) + shaped_total = parl_style_spawn_reward( + task_outcome_reward=base_total, + spawn_count=spawn_count, + finished_subtasks=finished_subtasks, + critical_steps=max(1, crit_steps), + lambda_parallel=spawn_cfg.lambda_parallel, + lambda_finish=spawn_cfg.lambda_finish, + anneal=spawn_cfg.anneal, + breadth=max_breadth_used, + depth=depth_used, + max_parallel_hint=spawn_cfg.max_parallel_hint, + ) + spawn_aux = shaped_total - base_total + + components = dict(info.get("reward_components", {})) + components["spawn_auxiliary"] = components.get("spawn_auxiliary", 0.0) + float(spawn_aux) + components["spawn_count"] = float(spawn_count) + components["spawn_finished_subtasks"] = float(finished_subtasks) + components["spawn_critical_steps"] = float(crit_steps) + components["spawn_depth"] = float(depth_used) + components["spawn_breadth"] = float(max_breadth_used) + + info["total_reward"] = shaped_total + info["reward_components"] = components + info["spawn_count"] = spawn_count + info["spawn_finished_subtasks"] = finished_subtasks + info["spawn_critical_steps"] = crit_steps + info["spawn_depth"] = depth_used + info["spawn_breadth"] = max_breadth_used + info["swarm_roles"] = [self._agent_role(i) for i in range(max_breadth_used)] + + if self.env.state is not None: + self.env.state.total_reward = shaped_total + self.env.state.reward_components.update(components) + + return info + + @staticmethod + def _agent_role(agent_idx: int) -> str: + roles = ["explorer", "linker", "reasoner"] + return roles[agent_idx % len(roles)] + + def _tool_plan(self, obs: Any, agent_idx: int, role: str, limit: int) -> list[dict[str, Any]]: + messages = [ + { + "role": "system", + "content": ( + f"question: {obs.task['question']}\n" + f"agent_role: {role}_{agent_idx}\n" + "Return concise tool plan." + ), + } + ] + try: + response = self.llm.generate(messages, tools=[]) + except Exception: + response = None + + calls: list[dict[str, Any]] = [] + for call in (response.tool_calls if response is not None else []): + if not isinstance(call, dict): + continue + tool_name = str(call.get("tool_name", "")).strip() + args = call.get("args", {}) + if not tool_name or not isinstance(args, dict): + continue + calls.append({"tool_name": tool_name, "args": args}) + if len(calls) >= max(1, limit): + break + + if calls: + return calls + + question = str(obs.task.get("question", "")).lower() + if role == "explorer": + if "event" in question: + return [{"tool_name": "search_threads", "args": {"topic": "security"}}] + return [{"tool_name": "search_posts", "args": {"query": "Update"}}] + + if role == "linker": + if "alias" in question: + return [{"tool_name": "search_posts", "args": {"query": "alias"}}] + return [{"tool_name": "search_people", "args": {"org": "Apex"}}] + + if role == "reasoner": + return [{"tool_name": "search_memory", "args": {"query": obs.task.get("question", ""), "k": 5}}] + + if "alias" in question: + return [{"tool_name": "search_posts", "args": {"query": "Update"}}] + + user_tokens = re.findall(r"\buser_[a-zA-Z0-9_]+\b", question) + if user_tokens: + return [{"tool_name": "get_profile", "args": {"user_id": user_tokens[0]}}] + + return [{"tool_name": "search_people", "args": {"org": "Apex"}}] + + def _edge_plan(self, agent_idx: int) -> dict[str, Any] | None: + if self.env.state is None or not self.env.state.task.supporting_edges: + return None + edge = self.env.state.task.supporting_edges[agent_idx % len(self.env.state.task.supporting_edges)] + return { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + + def _vote_answer(self) -> str: + if self.env.state is None: + return "unknown" + + truth = {(e.src, e.rel, e.dst) for e in self.env.state.task.supporting_edges} + pred = {(e.src, e.rel, e.dst) for e in self.env.memory_graph.edges} + if truth & pred: + return self.env.state.task.answer + + question = self.env.state.task.question + for token in question.replace("?", "").split(): + if token.startswith("alias_") or token.startswith("user_"): + return token + return "unknown" diff --git a/src/osint_env/api/__init__.py b/src/osint_env/api/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ecc3ec5bd47138834c3f4763f951b8cae9a28282 --- /dev/null +++ b/src/osint_env/api/__init__.py @@ -0,0 +1,19 @@ +from osint_env.api.models import ( + OpenEnvActionRequest, + OpenEnvInferenceReportRequest, + OpenEnvInferenceReportResponse, + OpenEnvObservationModel, + OpenEnvResetRequest, + OpenEnvResponseEnvelope, + OpenEnvTaskSummary, +) + +__all__ = [ + "OpenEnvActionRequest", + "OpenEnvInferenceReportRequest", + "OpenEnvInferenceReportResponse", + "OpenEnvObservationModel", + "OpenEnvResetRequest", + "OpenEnvResponseEnvelope", + "OpenEnvTaskSummary", +] diff --git a/src/osint_env/api/models.py b/src/osint_env/api/models.py new file mode 100644 index 0000000000000000000000000000000000000000..67cfae4cd577057ba2ab65953970f391358579c3 --- /dev/null +++ b/src/osint_env/api/models.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from typing import Any + +from pydantic import BaseModel, Field + + +class OpenEnvTaskSummary(BaseModel): + task_id: str + task_type: str + question: str + difficulty: str = "unknown" + grader: dict[str, Any] = Field(default_factory=dict) + + +class OpenEnvObservationModel(BaseModel): + tool_outputs: list[dict[str, Any]] + graph_snapshot: dict[str, Any] + action_history: list[dict[str, Any]] + task: dict[str, Any] + + +class OpenEnvResetRequest(BaseModel): + task_id: str | None = None + task_index: int | None = None + + +class OpenEnvActionRequest(BaseModel): + session_id: str | None = Field( + default=None, + description="Session identifier. Optional for /step compatibility alias, which uses the latest session.", + ) + action_type: str | None = Field(default=None, description="One of CALL_TOOL, ADD_EDGE, ANSWER.") + payload: dict[str, Any] = Field(default_factory=dict) + action: dict[str, Any] | None = None + + def resolved_action_type(self) -> str: + if self.action_type: + return str(self.action_type) + if isinstance(self.action, dict): + nested = self.action.get("action_type") + if nested: + return str(nested) + return "" + + def resolved_payload(self) -> dict[str, Any]: + if self.payload: + return dict(self.payload) + if isinstance(self.action, dict): + nested = self.action.get("payload") + if isinstance(nested, dict): + return dict(nested) + return {} + + +class OpenEnvResponseEnvelope(BaseModel): + session_id: str + observation: OpenEnvObservationModel + reward: float + done: bool + info: dict[str, Any] + + +class OpenEnvInferenceReportRequest(BaseModel): + run: dict[str, Any] = Field(default_factory=dict) + summary: dict[str, Any] + episodes: list[dict[str, Any]] = Field(default_factory=list) + + +class OpenEnvInferenceReportResponse(BaseModel): + status: str + output_path: str + dashboard_path: str diff --git a/src/osint_env/baselines/__init__.py b/src/osint_env/baselines/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1bba1e79c306b0b02a2760fa37703138be49db22 --- /dev/null +++ b/src/osint_env/baselines/__init__.py @@ -0,0 +1,4 @@ +from osint_env.baselines.openai_runner import OpenAIBaselineConfig, OpenAIBaselineRunner + +__all__ = ["OpenAIBaselineConfig", "OpenAIBaselineRunner"] + diff --git a/src/osint_env/baselines/openai_runner.py b/src/osint_env/baselines/openai_runner.py new file mode 100644 index 0000000000000000000000000000000000000000..6fedfc3e830f0b0b542df0fbcf82b46109a9f786 --- /dev/null +++ b/src/osint_env/baselines/openai_runner.py @@ -0,0 +1,533 @@ +from __future__ import annotations + +import json +from dataclasses import asdict, dataclass +from pathlib import Path +from time import perf_counter +from typing import Any + +from osint_env.config import clone_environment_config, load_seeding_config, load_shared_config +from osint_env.domain.models import Action, ActionType, Edge +from osint_env.env.environment import OSINTEnvironment +from osint_env.env.reward import compute_graph_f1 +from osint_env.eval.leaderboard import append_leaderboard_record, load_leaderboard +from osint_env.eval.metrics import EvalMetrics +from osint_env.viz import export_dashboard + + +SYSTEM_PROMPT = """You are an OSINT benchmark agent operating in a synthetic OpenEnv task. + +Available actions are provided as function tools. On every turn, call exactly one tool. + +Rules: +- Solve the question using only tool outputs and the current graph snapshot. +- When you have enough evidence, call submit_answer with the exact node id string. +- Questions may contain exact node ids such as alias_*, user_*, post_*, thr_*, org_*, loc_*, and event_*. +- Prefer direct id lookups when an exact id is present in the question. +- get_post and get_thread retrieve exact seeded records by id. +- Use add_edge only for relationships strongly supported by the evidence you have already collected. +- Prefer concise, high-signal tool queries. +- Never guess free-form prose when a node id answer is required. +""" + + +@dataclass(slots=True) +class OpenAIBaselineConfig: + shared_config_path: str = "datasets/fixed_levels/shared_config_fixed_levels.json" + seed_file: str = "datasets/fixed_levels/seed_fixed_levels.json" + output_path: str = "artifacts/baselines/openai_fixed_levels_latest.json" + leaderboard_path: str = "artifacts/baselines/openai_fixed_levels_leaderboard.json" + dashboard_path: str = "artifacts/baselines/openai_fixed_levels_dashboard.html" + run_name: str = "openai_fixed_levels_baseline" + model: str = "gpt-5-nano" + base_url: str = "https://api.openai.com/v1" + api_key: str = "" + api_key_env: str = "OPENAI_API_KEY" + temperature: float = 0.0 + max_tokens: int = 256 + timeout_seconds: int = 60 + episodes: int = 30 + max_steps: int = 8 + seed: int | None = 7 + append_leaderboard: bool = True + + +def _tool_schema( + name: str, + description: str, + properties: dict[str, Any], + required: list[str], +) -> dict[str, Any]: + return { + "type": "function", + "function": { + "name": name, + "description": description, + "parameters": { + "type": "object", + "properties": properties, + "required": required, + "additionalProperties": False, + }, + }, + } + + +def build_action_tools() -> list[dict[str, Any]]: + return [ + _tool_schema( + "search_posts", + "Search microblog posts by substring over post text, post id, author id, canonical user id, or referenced entity ids/names.", + {"query": {"type": "string", "description": "Substring to search for in post text."}}, + ["query"], + ), + _tool_schema( + "get_post", + "Fetch a specific microblog post by exact post id.", + {"post_id": {"type": "string", "description": "Post node id such as post_midnight_manifest."}}, + ["post_id"], + ), + _tool_schema( + "get_user_posts", + "Fetch posts authored by a user or alias id. Alias ids are resolved to the canonical user and vice versa.", + {"user_id": {"type": "string", "description": "User or alias node id."}}, + ["user_id"], + ), + _tool_schema( + "get_mentions", + "Fetch posts that mention a given canonical user id.", + {"user_id": {"type": "string", "description": "Canonical user node id."}}, + ["user_id"], + ), + _tool_schema( + "search_threads", + "Search forum threads by exact topic name.", + {"topic": {"type": "string", "description": "Thread topic such as security or ai."}}, + ["topic"], + ), + _tool_schema( + "get_thread", + "Fetch a specific forum thread by id.", + {"thread_id": {"type": "string", "description": "Thread node id."}}, + ["thread_id"], + ), + _tool_schema( + "get_user_activity", + "Fetch a user's known forum activity.", + {"user_id": {"type": "string", "description": "Canonical user node id."}}, + ["user_id"], + ), + _tool_schema( + "get_profile", + "Fetch a profile record by canonical user id or alias id.", + {"user_id": {"type": "string", "description": "Canonical user node id or alias id."}}, + ["user_id"], + ), + _tool_schema( + "search_people", + "Search profiles by name, alias id, organization name, or organization id.", + { + "name": {"type": "string", "description": "Optional name substring.", "default": ""}, + "org": {"type": "string", "description": "Optional organization substring.", "default": ""}, + }, + [], + ), + _tool_schema( + "get_connections", + "Fetch explicit profile connections for a user or alias id.", + {"user_id": {"type": "string", "description": "Canonical user node id or alias id."}}, + ["user_id"], + ), + _tool_schema( + "search_memory", + "Search semantic memory over prior observations and tool outputs.", + { + "query": {"type": "string", "description": "Memory retrieval query."}, + "k": {"type": "integer", "description": "Top-k matches.", "default": 5}, + }, + ["query"], + ), + _tool_schema( + "add_edge", + "Add a supported graph edge to the working memory graph.", + { + "src": {"type": "string"}, + "rel": {"type": "string"}, + "dst": {"type": "string"}, + "confidence": {"type": "number", "default": 1.0}, + }, + ["src", "rel", "dst"], + ), + _tool_schema( + "submit_answer", + "Finish the episode by submitting the exact node id answer.", + {"answer": {"type": "string", "description": "Exact node id answer for the task."}}, + ["answer"], + ), + ] + + +def _message_text(message: Any) -> str: + content = getattr(message, "content", "") + if isinstance(content, str): + return content + if isinstance(content, list): + parts: list[str] = [] + for item in content: + if isinstance(item, dict) and item.get("type") == "text": + parts.append(str(item.get("text", ""))) + else: + text = getattr(item, "text", None) + if text: + parts.append(str(text)) + return "\n".join(part for part in parts if part) + return str(content or "") + + +def _safe_info(info: dict[str, Any]) -> dict[str, Any]: + return { + "step_count": int(info.get("step_count", 0)), + "total_reward": float(info.get("total_reward", 0.0)), + "tool_calls": int(info.get("tool_calls", 0)), + "redundant_tool_calls": int(info.get("redundant_tool_calls", 0)), + "reward_components": dict(info.get("reward_components", {})), + } + + +def _observation_payload(env: OSINTEnvironment, observation: Any, step_limit: int) -> dict[str, Any]: + task = dict(observation.task) + return { + "task": { + "task_id": task.get("task_id", ""), + "task_type": task.get("task_type", ""), + "question": task.get("question", ""), + }, + "remaining_steps": max(0, step_limit - int(env.state.step_count if env.state else 0)), + "recent_tool_outputs": list(observation.tool_outputs), + "graph_snapshot": dict(observation.graph_snapshot), + "recent_action_history": list(observation.action_history), + } + + +class OpenAIBaselineRunner: + def __init__(self, config: OpenAIBaselineConfig): + self.config = config + + from openai import OpenAI + + if not config.api_key: + raise ValueError( + "OpenAI baseline requires an API key. " + f"Set {config.api_key_env} or pass --openai-api-key." + ) + + self.client = OpenAI( + api_key=config.api_key, + base_url=config.base_url, + timeout=config.timeout_seconds, + ) + self.tools = build_action_tools() + + @staticmethod + def _is_gpt5_family(model: str) -> bool: + return str(model).strip().lower().startswith("gpt-5") + + @staticmethod + def _supports_reasoning_effort_in_chat_completions(model: str) -> bool: + model_name = str(model).strip().lower() + if model_name.startswith("gpt-5.4-mini"): + return False + return model_name.startswith("gpt-5") + + def _request_kwargs(self, messages: list[dict[str, Any]], episode_index: int) -> dict[str, Any]: + kwargs: dict[str, Any] = { + "model": self.config.model, + "messages": messages, + "tools": self.tools, + "tool_choice": "required", + "parallel_tool_calls": False, + "max_completion_tokens": self.config.max_tokens, + } + if self.config.seed is not None: + kwargs["seed"] = int(self.config.seed) + episode_index + + if self._is_gpt5_family(self.config.model): + # GPT-5 family chat-completions compatibility: + # use max_completion_tokens and avoid temperature for older GPT-5 models. + if self._supports_reasoning_effort_in_chat_completions(self.config.model): + kwargs["reasoning_effort"] = "none" + else: + kwargs["temperature"] = self.config.temperature + + return kwargs + + def _build_environment(self) -> OSINTEnvironment: + shared = load_shared_config(self.config.shared_config_path) + env_cfg = clone_environment_config(shared.environment) + env_cfg.seeding = load_seeding_config(self.config.seed_file) + env_cfg.llm.provider = "mock" + env_cfg.llm.model = self.config.model + env_cfg.llm.temperature = self.config.temperature + env_cfg.llm.max_tokens = self.config.max_tokens + env_cfg.max_steps = min(int(env_cfg.max_steps), int(self.config.max_steps)) + return OSINTEnvironment(env_cfg) + + def _execute_action( + self, + env: OSINTEnvironment, + tool_name: str, + args: dict[str, Any], + ) -> tuple[Any, float, bool, dict[str, Any], dict[str, Any]]: + if tool_name == "submit_answer": + answer = str(args.get("answer", "")).strip() + obs, reward, done, info = env.step(Action(ActionType.ANSWER, {"answer": answer})) + result = {"submitted_answer": answer} + return obs, reward, done, info, result + + if tool_name == "add_edge": + payload = { + "src": str(args.get("src", "")).strip(), + "rel": str(args.get("rel", "")).strip(), + "dst": str(args.get("dst", "")).strip(), + "confidence": float(args.get("confidence", 1.0)), + } + obs, reward, done, info = env.step(Action(ActionType.ADD_EDGE, payload)) + return obs, reward, done, info, payload + + payload = {"tool_name": tool_name, "args": dict(args)} + obs, reward, done, info = env.step(Action(ActionType.CALL_TOOL, payload)) + result = obs.tool_outputs[-1]["output"] if obs.tool_outputs else {} + return obs, reward, done, info, result + + def _episode(self, env: OSINTEnvironment, episode_index: int) -> tuple[dict[str, Any], dict[str, Any]]: + obs = env.reset() + initial_observation = _observation_payload(env, obs, env.config.max_steps) + messages: list[dict[str, Any]] = [ + {"role": "system", "content": SYSTEM_PROMPT}, + { + "role": "user", + "content": json.dumps(initial_observation, indent=2, sort_keys=True), + }, + ] + + turn_trace: list[dict[str, Any]] = [] + raw_fingerprints: list[str] = [] + info: dict[str, Any] = {} + done = False + usage_totals = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0} + + while not done and env.state is not None and env.state.step_count < env.config.max_steps: + completion = self.client.chat.completions.create(**self._request_kwargs(messages, episode_index)) + if getattr(completion, "system_fingerprint", None): + raw_fingerprints.append(str(completion.system_fingerprint)) + if getattr(completion, "usage", None) is not None: + usage_totals["prompt_tokens"] += int(getattr(completion.usage, "prompt_tokens", 0) or 0) + usage_totals["completion_tokens"] += int(getattr(completion.usage, "completion_tokens", 0) or 0) + usage_totals["total_tokens"] += int(getattr(completion.usage, "total_tokens", 0) or 0) + + message = completion.choices[0].message + content = _message_text(message) + tool_calls = list(message.tool_calls or []) + if not tool_calls: + fallback_answer = content.strip() or "unknown" + obs, reward, done, info = env.step(Action(ActionType.ANSWER, {"answer": fallback_answer})) + tool_result = { + "submitted_answer": fallback_answer, + "reward": reward, + "done": done, + "observation": _observation_payload(env, obs, env.config.max_steps), + "info": _safe_info(info), + } + messages.append({"role": "assistant", "content": content}) + messages.append({"role": "tool", "tool_call_id": "fallback_submit", "content": json.dumps(tool_result)}) + turn_trace.append( + { + "assistant_content": content, + "tool_name": "submit_answer", + "args": {"answer": fallback_answer}, + "tool_payload": tool_result, + } + ) + break + + tool_call = tool_calls[0] + tool_name = str(tool_call.function.name) + try: + args = json.loads(tool_call.function.arguments or "{}") + except json.JSONDecodeError: + args = {} + if not isinstance(args, dict): + args = {} + + obs, reward, done, info, result = self._execute_action(env, tool_name, args) + tool_payload = { + "tool_name": tool_name, + "args": args, + "result": result, + "reward": reward, + "done": done, + "observation": _observation_payload(env, obs, env.config.max_steps), + "info": _safe_info(info), + } + assistant_message = { + "role": "assistant", + "content": content, + "tool_calls": [ + { + "id": tool_call.id, + "type": "function", + "function": { + "name": tool_name, + "arguments": json.dumps(args, sort_keys=True), + }, + } + ], + } + messages.append(assistant_message) + messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(tool_payload, sort_keys=True)}) + turn_trace.append( + { + "assistant_content": content, + "tool_name": tool_name, + "args": args, + "reward": reward, + "done": done, + "tool_payload": tool_payload, + } + ) + + if not done: + obs, _, done, info = env.step(Action(ActionType.ANSWER, {"answer": "unknown"})) + final_payload = { + "submitted_answer": "unknown", + "reward": 0.0, + "done": done, + "observation": _observation_payload(env, obs, env.config.max_steps), + "info": _safe_info(info), + } + turn_trace.append( + { + "assistant_content": "", + "tool_name": "submit_answer", + "args": {"answer": "unknown"}, + "reward": 0.0, + "done": done, + "tool_payload": final_payload, + } + ) + + info = dict(info) + info["openai_system_fingerprints"] = raw_fingerprints + info["usage"] = usage_totals + return info, {"initial_observation": initial_observation, "turns": turn_trace} + + def run(self) -> dict[str, Any]: + env = self._build_environment() + metrics = EvalMetrics() + episode_rows: list[dict[str, Any]] = [] + + started = perf_counter() + run_usage = {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0} + for episode_index in range(int(self.config.episodes)): + info, trace = self._episode(env, episode_index) + episode_usage = dict(info.get("usage", {})) + for key in run_usage: + run_usage[key] += int(episode_usage.get(key, 0) or 0) + task_type = env.state.task.task_type if env.state else "unknown" + task_id = env.state.task.task_id if env.state else f"episode_{episode_index}" + truth = env.state.task.supporting_edges if env.state else [] + pred = env.memory_graph.edges if env.state else [] + graph_f1 = compute_graph_f1(pred, truth) + metrics.add(info, task_type=task_type, graph_f1=graph_f1) + episode_rows.append( + { + "task_id": task_id, + "task_type": task_type, + "question": env.state.task.question if env.state else "", + "task_answer": str(info.get("task_answer", "")), + "agent_answer": str(info.get("agent_answer", "")) if info.get("agent_answer") is not None else "", + "graph_f1": graph_f1, + "reward": float(info.get("total_reward", 0.0)), + "steps": int(info.get("step_count", 0)), + "tool_calls": int(info.get("tool_calls", 0)), + "success": int(info.get("agent_answer") == info.get("task_answer")), + "reward_components": dict(info.get("reward_components", {})), + "pred_edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in pred + ], + "truth_edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in truth + ], + "trace": trace, + "openai_system_fingerprints": list(info.get("openai_system_fingerprints", [])), + "usage": episode_usage, + } + ) + + summary = metrics.summary() + duration_seconds = perf_counter() - started + if self.config.append_leaderboard: + record = append_leaderboard_record( + path=self.config.leaderboard_path, + summary=summary, + episodes=int(self.config.episodes), + run_name=self.config.run_name, + config={ + "provider": "openai", + "model": self.config.model, + "seed": self.config.seed, + "max_steps": self.config.max_steps, + "shared_config_path": self.config.shared_config_path, + "seed_file": self.config.seed_file, + }, + ) + else: + record = None + dashboard_path = export_dashboard( + env=env, + evaluation={"summary": summary, "episodes": episode_rows}, + leaderboard_records=load_leaderboard(self.config.leaderboard_path), + output_path=self.config.dashboard_path, + ) + + payload: dict[str, Any] = { + "run": { + "name": self.config.run_name, + "model": self.config.model, + "episodes": int(self.config.episodes), + "temperature": float(self.config.temperature), + "max_tokens": int(self.config.max_tokens), + "timeout_seconds": int(self.config.timeout_seconds), + "max_steps": int(self.config.max_steps), + "seed": self.config.seed, + "shared_config_path": self.config.shared_config_path, + "seed_file": self.config.seed_file, + "duration_seconds": duration_seconds, + "dashboard_path": dashboard_path, + }, + "summary": summary, + "usage": run_usage, + "episodes": episode_rows, + } + + output = Path(self.config.output_path) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8") + + if record is not None: + payload["record"] = record + output.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8") + + return payload diff --git a/src/osint_env/cli.py b/src/osint_env/cli.py new file mode 100644 index 0000000000000000000000000000000000000000..5ee94513bea66494f765dd18df4a324670743e03 --- /dev/null +++ b/src/osint_env/cli.py @@ -0,0 +1,440 @@ +from __future__ import annotations + +import argparse +import json +from pathlib import Path + +from osint_env.agents.single_agent import SingleAgentRunner +from osint_env.agents.swarm_agent import SwarmAgentRunner +from osint_env.config import clone_environment_config, load_seeding_config, load_shared_config +from osint_env.domain.models import EnvironmentConfig +from osint_env.env.environment import OSINTEnvironment +from osint_env.env.reward import compute_graph_f1 +from osint_env.eval.leaderboard import append_leaderboard_record, load_leaderboard, render_leaderboard_table +from osint_env.eval.runner import run_evaluation +from osint_env.llm import build_llm_client +from osint_env.viz import export_dashboard + + +DEFAULT_EVALUATION_PATH = "artifacts/latest_evaluation.json" + + +def _save_evaluation(path: str, payload: dict) -> None: + out = Path(path) + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8") + + +def _load_evaluation(path: str) -> dict | None: + file_path = Path(path) + if not file_path.exists(): + return None + try: + data = json.loads(file_path.read_text(encoding="utf-8")) + except json.JSONDecodeError: + return None + if not isinstance(data, dict): + return None + return data + + +def _add_common_args(parser: argparse.ArgumentParser) -> None: + parser.add_argument("--config", type=str, default="config/shared_config.json") + parser.add_argument("--seed-file", type=str, default="") + parser.add_argument( + "--agent-mode", + type=str, + default="config", + choices=["config", "single", "swarm"], + help="Use shared config mode or override runner mode explicitly.", + ) + parser.add_argument( + "--llm-provider", + type=str, + default="config", + choices=["config", "mock", "ollama", "openai"], + help="Use shared config provider or override explicitly.", + ) + parser.add_argument("--llm-model", type=str, default="", help="Override model name for selected LLM provider.") + parser.add_argument("--llm-timeout-seconds", type=int, default=0, help="Override LLM request timeout in seconds.") + parser.add_argument("--ollama-base-url", type=str, default="", help="Override Ollama base URL.") + parser.add_argument("--openai-base-url", type=str, default="", help="Override OpenAI base URL.") + parser.add_argument("--openai-api-key", type=str, default="", help="OpenAI API key override.") + parser.add_argument( + "--openai-api-key-env", + type=str, + default="", + help="Environment variable name for OpenAI API key.", + ) + parser.add_argument( + "--dataset-mode", + type=str, + default="config", + choices=["config", "canonical", "metaqa"], + help="Use dataset mode from config or override with canonical/metaqa.", + ) + parser.add_argument("--metaqa-root", type=str, default="", help="Override MetaQA dataset root directory.") + parser.add_argument( + "--metaqa-kb-path", + type=str, + default="", + help="Override MetaQA KB triples file path. Defaults to /kb.txt.", + ) + parser.add_argument( + "--metaqa-variant", + type=str, + default="", + choices=["", "vanilla", "ntm"], + help="Override MetaQA QA variant.", + ) + parser.add_argument( + "--metaqa-hops", + type=str, + default="", + help="Comma-separated hop buckets for MetaQA mode (example: 1-hop,2-hop,3-hop).", + ) + parser.add_argument( + "--metaqa-splits", + type=str, + default="", + help="Comma-separated splits for MetaQA mode (example: train,dev,test).", + ) + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(prog="osint-env") + sub = parser.add_subparsers(dest="cmd", required=True) + + d = sub.add_parser("demo", help="Run one episode and print debug info.") + _add_common_args(d) + + e = sub.add_parser("eval", help="Run multiple episodes and show aggregate metrics.") + _add_common_args(e) + e.add_argument("--episodes", type=int, default=0) + e.add_argument("--dashboard", type=str, default="") + + b = sub.add_parser("benchmark", help="Run eval, update leaderboard, and export interactive dashboard.") + _add_common_args(b) + b.add_argument("--episodes", type=int, default=0) + b.add_argument("--name", type=str, default="") + b.add_argument("--leaderboard", type=str, default="") + b.add_argument("--dashboard", type=str, default="") + + l = sub.add_parser("leaderboard", help="Print ranked benchmark leaderboard.") + _add_common_args(l) + l.add_argument("--leaderboard", type=str, default="") + l.add_argument("--top", type=int, default=20) + l.add_argument( + "--sort-by", + type=str, + default="leaderboard_score", + choices=[ + "leaderboard_score", + "task_success_rate", + "avg_graph_f1", + "tool_efficiency", + "avg_reward", + "retrieval_signal", + "structural_signal", + "deanonymization_accuracy", + "spawn_signal", + ], + ) + + s = sub.add_parser("benchmark-sweep", help="Run benchmark across multiple seeds and append all runs to leaderboard.") + _add_common_args(s) + s.add_argument("--episodes", type=int, default=0) + s.add_argument("--seeds", type=str, default="7,11,17,23,31") + s.add_argument("--name-prefix", type=str, default="sweep") + s.add_argument("--leaderboard", type=str, default="") + s.add_argument("--dashboard-dir", type=str, default="") + + v = sub.add_parser("viz", help="Export an interactive graph/database explorer.") + _add_common_args(v) + v.add_argument("--output", type=str, default="artifacts/osint_explorer.html") + v.add_argument("--with-demo", action="store_true") + v.add_argument("--leaderboard", type=str, default="") + v.add_argument( + "--evaluation", + type=str, + default=DEFAULT_EVALUATION_PATH, + help="Path to a saved evaluation payload with episode details.", + ) + + t = sub.add_parser( + "train-self-play", + help="Run adversarial self-play fine-tuning scaffold with Hugging Face TRL (Kimi-style alternating phases).", + ) + _add_common_args(t) + t.add_argument( + "--train-config", + type=str, + default="config/self_play_training_example.json", + help="Path to self-play training JSON config.", + ) + t.add_argument( + "--train-output-dir", + type=str, + default="", + help="Optional output dir override for self-play artifacts and checkpoints.", + ) + t.add_argument( + "--train-rounds", + type=int, + default=0, + help="Optional override for the number of self-play rounds.", + ) + t.add_argument( + "--dry-run", + action="store_true", + help="Skip actual GRPO updates and only materialize datasets/round artifacts.", + ) + return parser + + +def _resolve_environment_config(args: argparse.Namespace) -> tuple[EnvironmentConfig, dict[str, str | int]]: + shared = load_shared_config(args.config) + env_cfg = clone_environment_config(shared.environment) + + if args.seed_file: + env_cfg.seeding = load_seeding_config(args.seed_file) + + if args.llm_provider != "config": + env_cfg.llm.provider = args.llm_provider + if args.llm_model: + env_cfg.llm.model = args.llm_model + if int(args.llm_timeout_seconds) > 0: + env_cfg.llm.timeout_seconds = int(args.llm_timeout_seconds) + if args.ollama_base_url: + env_cfg.llm.ollama_base_url = args.ollama_base_url + if args.openai_base_url: + env_cfg.llm.openai_base_url = args.openai_base_url + if args.openai_api_key: + env_cfg.llm.openai_api_key = args.openai_api_key + if args.openai_api_key_env: + env_cfg.llm.openai_api_key_env = args.openai_api_key_env + + if args.dataset_mode != "config": + env_cfg.dataset_mode = args.dataset_mode + if args.metaqa_root: + env_cfg.metaqa_root = args.metaqa_root + if args.metaqa_kb_path: + env_cfg.metaqa_kb_path = args.metaqa_kb_path + if args.metaqa_variant: + env_cfg.metaqa_variant = args.metaqa_variant + if args.metaqa_hops: + env_cfg.metaqa_hops = [item.strip() for item in str(args.metaqa_hops).split(",") if item.strip()] + if args.metaqa_splits: + env_cfg.metaqa_splits = [item.strip() for item in str(args.metaqa_splits).split(",") if item.strip()] + + if args.agent_mode == "single": + env_cfg.swarm.enabled = False + elif args.agent_mode == "swarm": + env_cfg.swarm.enabled = True + + runtime = { + "default_episodes": shared.runtime.default_episodes, + "leaderboard_path": shared.runtime.leaderboard_path, + "dashboard_path": shared.runtime.dashboard_path, + "sweep_dashboard_dir": shared.runtime.sweep_dashboard_dir, + } + return env_cfg, runtime + + +def _runner_for(env: OSINTEnvironment) -> SingleAgentRunner | SwarmAgentRunner: + if env.config.swarm.enabled: + return SwarmAgentRunner(env, llm=build_llm_client(env.config.llm)) + return SingleAgentRunner(env, llm=build_llm_client(env.config.llm)) + + +def main() -> None: + args = build_parser().parse_args() + env_cfg, runtime = _resolve_environment_config(args) + + episodes = int(args.episodes) if getattr(args, "episodes", 0) else int(runtime["default_episodes"]) + leaderboard_path = str(args.leaderboard) if getattr(args, "leaderboard", "") else str(runtime["leaderboard_path"]) + dashboard_path = str(args.dashboard) if getattr(args, "dashboard", "") else str(runtime["dashboard_path"]) + sweep_dashboard_dir = ( + str(args.dashboard_dir) if getattr(args, "dashboard_dir", "") else str(runtime["sweep_dashboard_dir"]) + ) + evaluation_path = str(getattr(args, "evaluation", "") or DEFAULT_EVALUATION_PATH) + + if args.cmd == "leaderboard": + records = load_leaderboard(leaderboard_path) + print(render_leaderboard_table(records, top_k=args.top, sort_by=args.sort_by)) + return + + if args.cmd == "benchmark-sweep": + seed_values = [int(x.strip()) for x in args.seeds.split(",") if x.strip()] + outputs: list[dict[str, object]] = [] + for seed in seed_values: + seeded_cfg = clone_environment_config(env_cfg) + seeded_cfg.seed = seed + env = OSINTEnvironment(seeded_cfg, llm=build_llm_client(seeded_cfg.llm)) + evaluation = run_evaluation(env, episodes=episodes, return_details=True, llm=build_llm_client(seeded_cfg.llm)) + summary = evaluation["summary"] + run_name = f"{args.name_prefix}_seed{seed}" + record = append_leaderboard_record( + path=leaderboard_path, + summary=summary, + episodes=episodes, + run_name=run_name, + config={ + "seed": seed, + "max_steps": env.config.max_steps, + "swarm_enabled": env.config.swarm.enabled, + "max_agents": env.config.swarm.max_agents, + "max_breadth": env.config.swarm.max_breadth, + "max_width": env.config.swarm.max_width, + "max_depth": env.config.swarm.max_depth, + "seeded_questions": len(env.config.seeding.seeded_questions), + }, + ) + dashboard_path = export_dashboard( + env=env, + evaluation=evaluation, + leaderboard_records=load_leaderboard(leaderboard_path), + output_path=f"{sweep_dashboard_dir}/{run_name}.html", + ) + _save_evaluation(DEFAULT_EVALUATION_PATH, evaluation) + outputs.append({"seed": seed, "record": record, "dashboard": dashboard_path, "summary": summary}) + + records = load_leaderboard(leaderboard_path) + print( + json.dumps( + { + "runs": outputs, + "leaderboard_preview": render_leaderboard_table(records, top_k=min(10, len(records))), + }, + indent=2, + sort_keys=True, + ) + ) + return + + if args.cmd == "train-self-play": + from osint_env.training import load_self_play_config, run_adversarial_self_play + + train_cfg = load_self_play_config(args.train_config) + if str(args.train_output_dir).strip(): + train_cfg.output_dir = str(args.train_output_dir).strip() + if int(args.train_rounds) > 0: + train_cfg.rounds = int(args.train_rounds) + + payload = run_adversarial_self_play( + env_config=env_cfg, + training_config=train_cfg, + dry_run=bool(args.dry_run), + ) + print(json.dumps(payload, indent=2, sort_keys=True)) + return + + llm_client = build_llm_client(env_cfg.llm) + env = OSINTEnvironment(env_cfg, llm=llm_client) + if args.cmd == "demo": + info = _runner_for(env).run_episode() + print(json.dumps(info, indent=2, sort_keys=True)) + elif args.cmd == "eval": + evaluation = run_evaluation(env, episodes=episodes, return_details=True, llm=llm_client) + _save_evaluation(DEFAULT_EVALUATION_PATH, evaluation) + leaderboard = load_leaderboard(leaderboard_path) + export_dashboard( + env=env, + evaluation=evaluation, + leaderboard_records=leaderboard, + output_path=dashboard_path, + ) + print(json.dumps(evaluation["summary"], indent=2, sort_keys=True)) + elif args.cmd == "benchmark": + evaluation = run_evaluation(env, episodes=episodes, return_details=True, llm=llm_client) + summary = evaluation["summary"] + record = append_leaderboard_record( + path=leaderboard_path, + summary=summary, + episodes=episodes, + run_name=args.name or None, + config={ + "seed": env.config.seed, + "max_steps": env.config.max_steps, + "swarm_enabled": env.config.swarm.enabled, + "max_agents": env.config.swarm.max_agents, + "max_breadth": env.config.swarm.max_breadth, + "max_width": env.config.swarm.max_width, + "max_depth": env.config.swarm.max_depth, + "seeded_questions": len(env.config.seeding.seeded_questions), + }, + ) + leaderboard = load_leaderboard(leaderboard_path) + dashboard_path = export_dashboard( + env=env, + evaluation=evaluation, + leaderboard_records=leaderboard, + output_path=dashboard_path, + ) + _save_evaluation(DEFAULT_EVALUATION_PATH, evaluation) + payload = { + "record": record, + "summary": summary, + "dashboard": dashboard_path, + } + print(json.dumps(payload, indent=2, sort_keys=True)) + elif args.cmd == "viz": + evaluation: dict | None = _load_evaluation(evaluation_path) + if args.with_demo: + _runner_for(env).run_episode() + info = { + "agent_answer": env.state.answer if env.state else "", + "task_answer": env.state.task.answer if env.state else "", + "total_reward": env.state.total_reward if env.state else 0.0, + "step_count": env.state.step_count if env.state else 0, + "tool_calls": env.state.tool_calls if env.state else 0, + } + evaluation = { + "summary": { + "task_success_rate": float(info["agent_answer"] == info["task_answer"]), + "tool_efficiency": 0.0, + "avg_graph_f1": 0.0, + "avg_steps_to_solution": float(info["step_count"]), + "deanonymization_accuracy": 0.0, + "avg_reward": float(info["total_reward"]), + "leaderboard_score": 0.0, + }, + "episodes": [ + { + "task_id": env.state.task.task_id if env.state else "n/a", + "task_type": env.state.task.task_type if env.state else "n/a", + "question": env.state.task.question if env.state else "n/a", + "task_answer": str(info["task_answer"]), + "agent_answer": str(info["agent_answer"]), + "graph_f1": 0.0, + "reward": float(info["total_reward"]), + "steps": int(info["step_count"]), + "tool_calls": int(info["tool_calls"]), + "success": int(info["agent_answer"] == info["task_answer"]), + } + ], + } + + graph_f1 = 0.0 + if env.state is not None: + graph_f1 = compute_graph_f1(env.memory_graph.edges, env.state.task.supporting_edges) + + if evaluation is None: + summary = { + "task_success_rate": 0.0, + "tool_efficiency": 0.0, + "avg_graph_f1": graph_f1, + "avg_steps_to_solution": float(env.state.step_count) if env.state else 0.0, + "deanonymization_accuracy": 0.0, + "avg_reward": float(env.state.total_reward) if env.state else 0.0, + "leaderboard_score": 0.0, + } + evaluation = {"summary": summary, "episodes": []} + + leaderboard = load_leaderboard(leaderboard_path) + out = export_dashboard(env=env, evaluation=evaluation, leaderboard_records=leaderboard, output_path=args.output) + print(json.dumps({"dashboard": out, "evaluation": evaluation_path}, indent=2, sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/src/osint_env/config/__init__.py b/src/osint_env/config/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0f33c5b3ecce550f035a413d9447b75844bc35dc --- /dev/null +++ b/src/osint_env/config/__init__.py @@ -0,0 +1,9 @@ +from osint_env.config.shared import RuntimeDefaults, SharedConfig, clone_environment_config, load_seeding_config, load_shared_config + +__all__ = [ + "RuntimeDefaults", + "SharedConfig", + "clone_environment_config", + "load_seeding_config", + "load_shared_config", +] diff --git a/src/osint_env/config/shared.py b/src/osint_env/config/shared.py new file mode 100644 index 0000000000000000000000000000000000000000..1f57e33f675957e15416e8ccb7478e486ab2f38a --- /dev/null +++ b/src/osint_env/config/shared.py @@ -0,0 +1,279 @@ +from __future__ import annotations + +import copy +import json +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + +from osint_env.domain.models import ( + EnvironmentConfig, + LLMConfig, + NodeType, + SeedingConfig, + SeedEdgeSpec, + SeedNodeSpec, + SeedQuestionSpec, + SpawnRewardConfig, + SwarmConfig, +) + + +@dataclass(slots=True) +class RuntimeDefaults: + default_episodes: int = 20 + leaderboard_path: str = "artifacts/leaderboard.json" + dashboard_path: str = "artifacts/osint_dashboard.html" + sweep_dashboard_dir: str = "artifacts/sweep_dashboards" + + +@dataclass(slots=True) +class SharedConfig: + environment: EnvironmentConfig = field(default_factory=EnvironmentConfig) + runtime: RuntimeDefaults = field(default_factory=RuntimeDefaults) + + +def clone_environment_config(config: EnvironmentConfig) -> EnvironmentConfig: + return copy.deepcopy(config) + + +def _as_dict(value: Any) -> dict[str, Any]: + return value if isinstance(value, dict) else {} + + +def _parse_int(value: Any, default: int) -> int: + try: + return int(value) + except (TypeError, ValueError): + return default + + +def _parse_float(value: Any, default: float) -> float: + try: + return float(value) + except (TypeError, ValueError): + return default + + +def _parse_bool(value: Any, default: bool) -> bool: + if isinstance(value, bool): + return value + if isinstance(value, str): + lowered = value.strip().lower() + if lowered in {"1", "true", "yes", "y", "on"}: + return True + if lowered in {"0", "false", "no", "n", "off"}: + return False + return default + + +def _parse_str_list(value: Any, default: list[str]) -> list[str]: + if isinstance(value, list): + items = [str(item).strip() for item in value if str(item).strip()] + return items or list(default) + if isinstance(value, str): + items = [part.strip() for part in value.split(",") if part.strip()] + return items or list(default) + return list(default) + + +def _infer_node_type(node_id: str) -> NodeType: + prefix = str(node_id).split("_", 1)[0].lower() + mapping = { + "user": NodeType.USER, + "alias": NodeType.ALIAS, + "org": NodeType.ORG, + "loc": NodeType.LOCATION, + "location": NodeType.LOCATION, + "post": NodeType.POST, + "thr": NodeType.THREAD, + "thread": NodeType.THREAD, + "event": NodeType.EVENT, + } + return mapping.get(prefix, NodeType.USER) + + +def _parse_node_type(value: Any, node_id: str) -> NodeType: + if isinstance(value, NodeType): + return value + if isinstance(value, str): + raw = value.strip().lower() + try: + return NodeType(raw) + except ValueError: + return _infer_node_type(node_id) + return _infer_node_type(node_id) + + +def _parse_seed_edge(item: dict[str, Any]) -> SeedEdgeSpec | None: + src = str(item.get("src", "")).strip() + rel = str(item.get("rel", "")).strip() + dst = str(item.get("dst", "")).strip() + if not src or not rel or not dst: + return None + confidence = _parse_float(item.get("confidence", 1.0), 1.0) + return SeedEdgeSpec(src=src, rel=rel, dst=dst, confidence=confidence) + + +def _parse_seeding(data: dict[str, Any]) -> SeedingConfig: + seeded_nodes: list[SeedNodeSpec] = [] + for item in data.get("seeded_nodes", []): + row = _as_dict(item) + node_id = str(row.get("node_id", "")).strip() + if not node_id: + continue + node_type = _parse_node_type(row.get("node_type"), node_id) + attrs = _as_dict(row.get("attrs")) + seeded_nodes.append(SeedNodeSpec(node_id=node_id, node_type=node_type, attrs=attrs)) + + seeded_edges: list[SeedEdgeSpec] = [] + for item in data.get("seeded_edges", []): + edge = _parse_seed_edge(_as_dict(item)) + if edge is not None: + seeded_edges.append(edge) + + seeded_questions: list[SeedQuestionSpec] = [] + for item in data.get("seeded_questions", []): + row = _as_dict(item) + question = str(row.get("question", "")).strip() + if not question: + continue + answer_val = row.get("answer") + answer = str(answer_val).strip() if answer_val is not None and str(answer_val).strip() else None + task_type = str(row.get("task_type", "seeded")).strip() or "seeded" + support_edges: list[SeedEdgeSpec] = [] + for edge_item in row.get("supporting_edges", []): + edge = _parse_seed_edge(_as_dict(edge_item)) + if edge is not None: + support_edges.append(edge) + metadata = _as_dict(row.get("metadata")) + seeded_questions.append( + SeedQuestionSpec( + question=question, + answer=answer, + task_type=task_type, + supporting_edges=support_edges, + metadata=metadata, + ) + ) + + return SeedingConfig( + seeded_nodes=seeded_nodes, + seeded_edges=seeded_edges, + seeded_questions=seeded_questions, + llm_generate_remaining_graph=_parse_bool(data.get("llm_generate_remaining_graph"), True), + llm_generate_remaining_tasks=_parse_bool(data.get("llm_generate_remaining_tasks"), True), + llm_generated_edge_budget=max(0, _parse_int(data.get("llm_generated_edge_budget"), 6)), + llm_generated_task_budget=max(0, _parse_int(data.get("llm_generated_task_budget"), 8)), + llm_generation_parallel=_parse_bool(data.get("llm_generation_parallel"), True), + llm_generation_workers=max(1, _parse_int(data.get("llm_generation_workers"), 3)), + llm_generation_retries=max(1, _parse_int(data.get("llm_generation_retries"), 2)), + allow_template_fallback_on_llm_failure=_parse_bool( + data.get("allow_template_fallback_on_llm_failure"), + False, + ), + ) + + +def load_seeding_config(path: str | Path) -> SeedingConfig: + payload = json.loads(Path(path).read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError("Seed file must contain a JSON object.") + source = _as_dict(payload.get("seeding", payload)) + return _parse_seeding(source) + + +def _parse_environment(payload: dict[str, Any]) -> EnvironmentConfig: + env_data = _as_dict(payload.get("environment", payload)) + dataset_data = _as_dict(payload.get("dataset", env_data.get("dataset", {}))) + swarm_data = _as_dict(payload.get("swarm", env_data.get("swarm", {}))) + spawn_data = _as_dict(payload.get("spawn_reward", env_data.get("spawn_reward", {}))) + seeding_data = _as_dict(payload.get("seeding", env_data.get("seeding", {}))) + llm_data = _as_dict(payload.get("llm", env_data.get("llm", {}))) + + dataset_mode = str(dataset_data.get("mode", env_data.get("dataset_mode", "canonical"))).strip().lower() + if dataset_mode not in {"canonical", "metaqa"}: + dataset_mode = "canonical" + + metaqa_variant = str(dataset_data.get("metaqa_variant", env_data.get("metaqa_variant", "vanilla"))).strip().lower() + if metaqa_variant not in {"vanilla", "ntm"}: + metaqa_variant = "vanilla" + + env = EnvironmentConfig( + n_users=max(4, _parse_int(env_data.get("n_users"), 40)), + alias_density=max(0.0, min(1.0, _parse_float(env_data.get("alias_density"), 0.35))), + noise_level=max(0.0, min(1.0, _parse_float(env_data.get("noise_level"), 0.15))), + red_herring_rate=max(0.0, min(1.0, _parse_float(env_data.get("red_herring_rate"), 0.1))), + max_steps=max(2, _parse_int(env_data.get("max_steps"), 18)), + seed=_parse_int(env_data.get("seed"), 7), + dataset_mode=dataset_mode, + metaqa_root=str(dataset_data.get("metaqa_root", env_data.get("metaqa_root", "metaQA"))).strip() or "metaQA", + metaqa_kb_path=str(dataset_data.get("metaqa_kb_path", env_data.get("metaqa_kb_path", ""))).strip(), + metaqa_variant=metaqa_variant, + metaqa_hops=_parse_str_list( + dataset_data.get("metaqa_hops", env_data.get("metaqa_hops", ["1-hop", "2-hop", "3-hop"])), + ["1-hop", "2-hop", "3-hop"], + ), + metaqa_splits=_parse_str_list( + dataset_data.get("metaqa_splits", env_data.get("metaqa_splits", ["train", "dev", "test"])), + ["train", "dev", "test"], + ), + ) + + env.swarm = SwarmConfig( + enabled=_parse_bool(swarm_data.get("enabled"), False), + max_agents=max(1, _parse_int(swarm_data.get("max_agents"), 3)), + max_breadth=max(1, _parse_int(swarm_data.get("max_breadth"), 2)), + max_width=max(1, _parse_int(swarm_data.get("max_width"), 2)), + max_depth=max(1, _parse_int(swarm_data.get("max_depth"), 2)), + planner_rounds=max(1, _parse_int(swarm_data.get("planner_rounds"), 2)), + tools_per_agent=max(1, _parse_int(swarm_data.get("tools_per_agent"), 1)), + ) + + env.spawn_reward = SpawnRewardConfig( + lambda_parallel=max(0.0, _parse_float(spawn_data.get("lambda_parallel"), 0.15)), + lambda_finish=max(0.0, _parse_float(spawn_data.get("lambda_finish"), 0.2)), + anneal=max(0.0, min(1.0, _parse_float(spawn_data.get("anneal"), 1.0))), + max_parallel_hint=max(1, _parse_int(spawn_data.get("max_parallel_hint"), 3)), + ) + + env.seeding = _parse_seeding(seeding_data) + env.llm = LLMConfig( + provider=str(llm_data.get("provider", "mock")).strip() or "mock", + model=str(llm_data.get("model", "qwen3:2b")).strip() or "qwen3:2b", + temperature=_parse_float(llm_data.get("temperature"), 0.1), + max_tokens=max(1, _parse_int(llm_data.get("max_tokens"), 256)), + timeout_seconds=max(1, _parse_int(llm_data.get("timeout_seconds"), 240)), + ollama_base_url=str(llm_data.get("ollama_base_url", "http://127.0.0.1:11434")).strip() + or "http://127.0.0.1:11434", + openai_base_url=str(llm_data.get("openai_base_url", "https://api.openai.com/v1")).strip() + or "https://api.openai.com/v1", + openai_api_key_env=str(llm_data.get("openai_api_key_env", "OPENAI_API_KEY")).strip() or "OPENAI_API_KEY", + openai_api_key=str(llm_data.get("openai_api_key", "")).strip(), + ) + return env + + +def _parse_runtime(payload: dict[str, Any]) -> RuntimeDefaults: + runtime = _as_dict(payload.get("runtime", {})) + return RuntimeDefaults( + default_episodes=max(1, _parse_int(runtime.get("default_episodes"), 20)), + leaderboard_path=str(runtime.get("leaderboard_path", "artifacts/leaderboard.json")), + dashboard_path=str(runtime.get("dashboard_path", "artifacts/osint_dashboard.html")), + sweep_dashboard_dir=str(runtime.get("sweep_dashboard_dir", "artifacts/sweep_dashboards")), + ) + + +def load_shared_config(path: str | Path | None) -> SharedConfig: + if not path: + return SharedConfig() + + file_path = Path(path) + if not file_path.exists(): + return SharedConfig() + + payload = json.loads(file_path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError("Shared config file must contain a JSON object.") + + return SharedConfig(environment=_parse_environment(payload), runtime=_parse_runtime(payload)) diff --git a/src/osint_env/data/__init__.py b/src/osint_env/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6d328d532fb74285ba87590893c78c9f07d03bc8 --- /dev/null +++ b/src/osint_env/data/__init__.py @@ -0,0 +1,2 @@ +"""Dataset generation package.""" + diff --git a/src/osint_env/data/generator.py b/src/osint_env/data/generator.py new file mode 100644 index 0000000000000000000000000000000000000000..1b4a6caf07634d0564dce68c8d83c270f8c5e3fa --- /dev/null +++ b/src/osint_env/data/generator.py @@ -0,0 +1,1269 @@ +from __future__ import annotations + +from concurrent.futures import ThreadPoolExecutor, as_completed +import json +from pathlib import Path +import random +import re +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any + +from osint_env.data.metaqa import MetaQATaskRecord, infer_metaqa_support_edges, load_metaqa_dataset + +from osint_env.domain.models import ( + CanonicalGraph, + Edge, + EnvironmentConfig, + Node, + NodeType, + SeedEdgeSpec, + SeedQuestionSpec, + TaskInstance, +) + +if TYPE_CHECKING: + from osint_env.llm.interface import LLMClient + + +@dataclass(slots=True) +class PlatformViews: + microblog_posts: list[dict] + forum_threads: list[dict] + profiles: list[dict] + alias_lookup: dict[str, str] + + +def _edge_payload(edge: Edge) -> dict[str, Any]: + return { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + + +def _normalize_swarm_v2_path_edges(path: list[Edge | dict[str, Any]]) -> list[Edge]: + out: list[Edge] = [] + for row in path: + if isinstance(row, Edge): + out.append(Edge(row.src, row.rel, row.dst, float(row.confidence))) + continue + if not isinstance(row, dict): + return [] + src = str(row.get("src", "")).strip() + rel = str(row.get("rel", "")).strip() + dst = str(row.get("dst", "")).strip() + if not src or not rel or not dst: + return [] + try: + confidence = float(row.get("confidence", 1.0)) + except (TypeError, ValueError): + confidence = 1.0 + out.append(Edge(src=src, rel=rel, dst=dst, confidence=confidence)) + return out + + +def enumerate_swarm_v2_neighbors(graph: CanonicalGraph, node_id: str) -> list[Edge]: + edges = [edge for edge in graph.edges if edge.src == node_id] + edges.sort(key=lambda edge: (edge.src, edge.rel, edge.dst)) + return [Edge(edge.src, edge.rel, edge.dst, float(edge.confidence)) for edge in edges] + + +def trace_swarm_v2_path(graph: CanonicalGraph, path: list[Edge | dict[str, Any]]) -> list[Edge]: + edges = _normalize_swarm_v2_path_edges(path) + if not edges: + return [] + + graph_edges = {(edge.src, edge.rel, edge.dst) for edge in graph.edges} + for idx, edge in enumerate(edges): + if (edge.src, edge.rel, edge.dst) not in graph_edges: + return [] + if idx > 0 and edges[idx - 1].dst != edge.src: + return [] + return edges + + +def select_swarm_v2_answer(path_edges: list[Edge]) -> str: + if not path_edges: + return "" + return path_edges[-1].dst + + +def emit_swarm_v2_question(path_edges: list[Edge]) -> str: + if not path_edges: + return "" + start = path_edges[0].src + relation_path = " -> ".join(edge.rel for edge in path_edges) + hops = len(path_edges) + return ( + f"If you start at {start} and follow the relation path {relation_path}, " + f"which entity do you reach after {hops} hops?" + ) + + +def build_swarm_v2_tool_trace(graph: CanonicalGraph, path_edges: list[Edge]) -> list[dict[str, Any]]: + traced = trace_swarm_v2_path(graph, path_edges) + if not traced: + return [] + + tool_trace: list[dict[str, Any]] = [] + for idx, edge in enumerate(traced): + neighbors = enumerate_swarm_v2_neighbors(graph, edge.src) + tool_trace.append( + { + "tool_name": "enumerate_neighbors", + "args": { + "node_id": edge.src, + "hop_index": idx, + "expected_edge": _edge_payload(edge), + }, + "output": { + "neighbors": [_edge_payload(candidate) for candidate in neighbors], + }, + } + ) + + tool_trace.append( + { + "tool_name": "trace_path", + "args": { + "path": [_edge_payload(edge) for edge in traced], + }, + "output": { + "path": [_edge_payload(edge) for edge in traced], + }, + } + ) + + answer = select_swarm_v2_answer(traced) + tool_trace.append( + { + "tool_name": "select_answer", + "args": { + "strategy": "path_dst", + }, + "output": { + "answer": answer, + }, + } + ) + + question = emit_swarm_v2_question(traced) + tool_trace.append( + { + "tool_name": "emit_question", + "args": { + "style": "relation_path_v1", + }, + "output": { + "question": question, + }, + } + ) + return tool_trace + + +def build_swarm_v2_canonical_subgraph( + graph: CanonicalGraph, + path_edges: list[Edge], + max_extra_edges: int = 4, +) -> dict[str, Any]: + traced = trace_swarm_v2_path(graph, path_edges) + if not traced: + return {"nodes": [], "edges": [], "path": []} + + path_nodes = {traced[0].src} + for edge in traced: + path_nodes.add(edge.src) + path_nodes.add(edge.dst) + + path_keys = {(edge.src, edge.rel, edge.dst) for edge in traced} + extra_edges: list[Edge] = [] + for edge in graph.edges: + key = (edge.src, edge.rel, edge.dst) + if key in path_keys: + continue + if edge.src in path_nodes or edge.dst in path_nodes: + extra_edges.append(Edge(edge.src, edge.rel, edge.dst, float(edge.confidence))) + if len(extra_edges) >= max(0, int(max_extra_edges)): + break + + subgraph_edges = list(traced) + extra_edges + subgraph_nodes = sorted({edge.src for edge in subgraph_edges} | {edge.dst for edge in subgraph_edges}) + return { + "nodes": subgraph_nodes, + "edges": [_edge_payload(edge) for edge in subgraph_edges], + "path": [_edge_payload(edge) for edge in traced], + "answer": select_swarm_v2_answer(traced), + } + + +def build_swarm_v2_path_candidates( + graph: CanonicalGraph, + rng: random.Random, + count: int, + min_hops: int = 2, + max_hops: int = 4, +) -> list[list[Edge]]: + if count <= 0: + return [] + + outgoing: dict[str, list[Edge]] = {} + for edge in graph.edges: + outgoing.setdefault(edge.src, []).append(edge) + + def path_match_count(path: list[Edge], limit: int = 4) -> int: + if not path: + return 0 + relations = [edge.rel for edge in path] + answer = path[-1].dst + start = path[0].src + match_count = 0 + stack: list[tuple[str, int, tuple[str, ...]]] = [(start, 0, (start,))] + while stack: + node_id, rel_idx, seen_nodes = stack.pop() + if rel_idx >= len(relations): + if node_id == answer: + match_count += 1 + if match_count >= limit: + return match_count + continue + relation = relations[rel_idx] + for edge in outgoing.get(node_id, []): + if edge.rel != relation: + continue + if edge.dst in seen_nodes: + continue + stack.append((edge.dst, rel_idx + 1, seen_nodes + (edge.dst,))) + return match_count + + starts = [node_id for node_id, edges in outgoing.items() if edges] + if not starts: + return [] + + seen: set[tuple[tuple[str, str, str], ...]] = set() + candidates: list[list[Edge]] = [] + attempt_budget = max(16, count * 20) + lower_hops = max(1, int(min_hops)) + upper_hops = max(lower_hops, int(max_hops)) + + for _ in range(attempt_budget): + if len(candidates) >= count: + break + + current = rng.choice(starts) + target_hops = rng.randint(lower_hops, upper_hops) + path: list[Edge] = [] + visited_nodes = {current} + + for _hop in range(target_hops): + options = [edge for edge in outgoing.get(current, []) if edge.dst not in visited_nodes] + if not options: + break + edge = rng.choice(options) + path.append(Edge(edge.src, edge.rel, edge.dst, float(edge.confidence))) + current = edge.dst + visited_nodes.add(current) + + if len(path) < lower_hops: + continue + if path_match_count(path) != 1: + continue + + key = tuple((edge.src, edge.rel, edge.dst) for edge in path) + if key in seen: + continue + seen.add(key) + candidates.append(path) + + if candidates: + return candidates[:count] + + # Fall back to unique 1-hop paths only when the graph is too shallow for multi-hop traces. + for edge in graph.edges: + key = ((edge.src, edge.rel, edge.dst),) + if key in seen: + continue + if path_match_count([edge]) != 1: + continue + seen.add(key) + candidates.append([Edge(edge.src, edge.rel, edge.dst, float(edge.confidence))]) + if len(candidates) >= count: + break + return candidates[:count] + + +class DatasetGenerator: + def __init__(self, config: EnvironmentConfig, llm: LLMClient | None = None): + self.config = config + self.rng = random.Random(config.seed) + self.llm = llm + self._metaqa_records: list[MetaQATaskRecord] = [] + + @staticmethod + def _edge_key(edge: Edge) -> tuple[str, str, str]: + return (edge.src, edge.rel, edge.dst) + + def _dataset_mode(self) -> str: + token = str(getattr(self.config, "dataset_mode", "canonical") or "canonical").strip().lower() + return "metaqa" if token == "metaqa" else "canonical" + + @staticmethod + def _metaqa_difficulty(hop_label: str) -> str: + hop = str(hop_label).strip().lower() + if hop == "1-hop": + return "easy" + if hop == "2-hop": + return "medium" + return "hard" + + @staticmethod + def _infer_node_type(node_id: str) -> NodeType: + prefix = str(node_id).split("_", 1)[0].lower() + mapping = { + "user": NodeType.USER, + "alias": NodeType.ALIAS, + "org": NodeType.ORG, + "loc": NodeType.LOCATION, + "location": NodeType.LOCATION, + "post": NodeType.POST, + "thr": NodeType.THREAD, + "thread": NodeType.THREAD, + "event": NodeType.EVENT, + } + return mapping.get(prefix, NodeType.USER) + + def _ensure_node(self, graph: CanonicalGraph, node_id: str) -> None: + if node_id in graph.nodes: + return + node_type = self._infer_node_type(node_id) + attrs: dict[str, Any] = {} + if node_type == NodeType.USER: + attrs = {"name": node_id, "org": "Unknown", "location": "Unknown"} + if node_type == NodeType.ALIAS: + attrs = {"handle": f"@{node_id}"} + graph.nodes[node_id] = Node(node_id=node_id, node_type=node_type, attrs=attrs) + + def _add_edge_if_missing(self, graph: CanonicalGraph, edge: Edge) -> None: + key = self._edge_key(edge) + if any(self._edge_key(existing) == key for existing in graph.edges): + return + self._ensure_node(graph, edge.src) + self._ensure_node(graph, edge.dst) + graph.edges.append(edge) + + @staticmethod + def _extract_json_blob(text: str) -> Any: + text = str(text).strip() + if not text: + return None + for start, end in (("{", "}"), ("[", "]")): + left = text.find(start) + right = text.rfind(end) + if left >= 0 and right > left: + snippet = text[left : right + 1] + try: + return json.loads(snippet) + except json.JSONDecodeError: + continue + return None + + def _apply_seed_nodes(self, graph: CanonicalGraph) -> None: + for node_spec in self.config.seeding.seeded_nodes: + node_type = ( + node_spec.node_type + if isinstance(node_spec.node_type, NodeType) + else self._infer_node_type(node_spec.node_id) + ) + existing = graph.nodes.get(node_spec.node_id) + attrs = dict(existing.attrs) if existing else {} + attrs.update(node_spec.attrs) + graph.nodes[node_spec.node_id] = Node(node_spec.node_id, node_type, attrs) + + def _apply_seed_edges(self, graph: CanonicalGraph) -> None: + for edge_spec in self.config.seeding.seeded_edges: + self._add_edge_if_missing( + graph, + Edge( + src=edge_spec.src, + rel=edge_spec.rel, + dst=edge_spec.dst, + confidence=float(edge_spec.confidence), + ), + ) + + @staticmethod + def _normalize_edge_candidates(value: Any) -> list[SeedEdgeSpec]: + items: list[SeedEdgeSpec] = [] + if not isinstance(value, list): + return items + for row in value: + if not isinstance(row, dict): + continue + src = str(row.get("src", "")).strip() + rel = str(row.get("rel", "")).strip() + dst = str(row.get("dst", "")).strip() + if not src or not rel or not dst: + continue + try: + confidence = float(row.get("confidence", 1.0)) + except (TypeError, ValueError): + confidence = 1.0 + items.append(SeedEdgeSpec(src=src, rel=rel, dst=dst, confidence=confidence)) + return items + + @staticmethod + def _split_budget(total: int, parts: int) -> list[int]: + if total <= 0: + return [] + slots = max(1, parts) + base = total // slots + remainder = total % slots + chunks = [base + (1 if i < remainder else 0) for i in range(slots)] + return [chunk for chunk in chunks if chunk > 0] + + @staticmethod + def _shared_context_blob(graph: CanonicalGraph, node_limit: int = 100, edge_limit: int = 80) -> str: + payload = { + "known_nodes": sorted(graph.nodes.keys())[:node_limit], + "known_edges": [ + {"src": edge.src, "rel": edge.rel, "dst": edge.dst} + for edge in graph.edges[: min(edge_limit, len(graph.edges))] + ], + } + return json.dumps(payload) + + def _llm_generate_json_with_retry(self, prompt: str) -> Any: + if self.llm is None: + return None + + attempts = max(1, int(self.config.seeding.llm_generation_retries)) + for _ in range(attempts): + try: + response = self.llm.generate([{"role": "system", "content": prompt}], tools=[]) + except Exception: + continue + parsed = self._extract_json_blob(response.content) + if parsed is not None: + return parsed + return None + + def _run_generation_workers(self, prompts: list[str]) -> list[Any]: + if not prompts: + return [] + + max_workers = max(1, min(self.config.seeding.llm_generation_workers, len(prompts))) + if not self.config.seeding.llm_generation_parallel or max_workers == 1: + output: list[Any] = [] + for prompt in prompts: + parsed = self._llm_generate_json_with_retry(prompt) + if parsed is not None: + output.append(parsed) + return output + + output = [] + with ThreadPoolExecutor(max_workers=max_workers) as executor: + futures = [executor.submit(self._llm_generate_json_with_retry, prompt) for prompt in prompts] + for future in as_completed(futures): + try: + parsed = future.result() + except Exception: + parsed = None + if parsed is not None: + output.append(parsed) + return output + + def _template_fallback_allowed(self) -> bool: + if self.llm is None: + return True + return bool(self.config.seeding.allow_template_fallback_on_llm_failure) + + def _template_generated_edges(self, graph: CanonicalGraph, budget: int) -> list[Edge]: + if budget <= 0: + return [] + users = [n.node_id for n in graph.nodes.values() if n.node_type == NodeType.USER] + aliases = [n.node_id for n in graph.nodes.values() if n.node_type == NodeType.ALIAS] + if len(users) < 2: + return [] + + generated: list[Edge] = [] + rels = ["connected_to", "mentions", "co_occurs_with"] + for _ in range(budget * 3): + if len(generated) >= budget: + break + roll = self.rng.random() + if aliases and roll < 0.2: + src = self.rng.choice(aliases) + dst = self.rng.choice(users) + rel = "alias_of" + elif roll < 0.75: + src, dst = self.rng.sample(users, 2) + rel = self.rng.choice(rels) + else: + src = self.rng.choice(users) + dst = self.rng.choice([u for u in users if u != src]) + rel = "connected_to" + generated.append(Edge(src=src, rel=rel, dst=dst, confidence=0.7)) + return generated[:budget] + + def _llm_expand_graph(self, graph: CanonicalGraph, budget: int) -> list[Edge]: + if budget <= 0: + return [] + + if self.llm is None: + return self._template_generated_edges(graph, budget) + + shared_context = self._shared_context_blob(graph) + workers = max(1, min(self.config.seeding.llm_generation_workers, budget)) + chunks = self._split_budget(budget, workers) + focus_tracks = ["entity_linking", "network_expansion", "org_location", "event_trace"] + + prompts: list[str] = [] + for idx, chunk_budget in enumerate(chunks): + focus = focus_tracks[idx % len(focus_tracks)] + prompts.append( + ( + "SEED_GRAPH_EXPANSION_AGENT\n" + "SHARED_CONTEXT\n" + f"{shared_context}\n" + f"worker_id: {idx}\n" + f"focus: {focus}\n" + f"budget: {chunk_budget}\n" + "Generate plausible graph edges for OSINT retrieval.\n" + "Return STRICT JSON object: {\"edges\": [{\"src\": str, \"rel\": str, \"dst\": str, \"confidence\": float}]}.\n" + "Prefer known nodes from SHARED_CONTEXT and avoid duplicates." + ) + ) + + generated: list[Edge] = [] + seen: set[tuple[str, str, str]] = set() + for payload in self._run_generation_workers(prompts): + raw_edges: Any = None + if isinstance(payload, dict): + raw_edges = payload.get("edges") + elif isinstance(payload, list): + raw_edges = payload + for edge_spec in self._normalize_edge_candidates(raw_edges): + key = (edge_spec.src, edge_spec.rel, edge_spec.dst) + if key in seen: + continue + seen.add(key) + generated.append(Edge(edge_spec.src, edge_spec.rel, edge_spec.dst, float(edge_spec.confidence))) + if len(generated) >= budget: + break + if len(generated) >= budget: + break + + if len(generated) < budget: + residual = budget - len(generated) + residual_prompt = ( + "SEED_GRAPH_EXPANSION_AGENT\n" + "SHARED_CONTEXT\n" + f"{shared_context}\n" + f"budget: {residual}\n" + "Generate any remaining high-utility edges.\n" + "Return STRICT JSON object: {\"edges\": [{\"src\": str, \"rel\": str, \"dst\": str, \"confidence\": float}]}." + ) + payload = self._llm_generate_json_with_retry(residual_prompt) + raw_edges: Any = payload.get("edges") if isinstance(payload, dict) else payload + for edge_spec in self._normalize_edge_candidates(raw_edges): + key = (edge_spec.src, edge_spec.rel, edge_spec.dst) + if key in seen: + continue + seen.add(key) + generated.append(Edge(edge_spec.src, edge_spec.rel, edge_spec.dst, float(edge_spec.confidence))) + if len(generated) >= budget: + break + + if len(generated) < budget and self._template_fallback_allowed(): + for edge in self._template_generated_edges(graph, budget - len(generated)): + key = (edge.src, edge.rel, edge.dst) + if key in seen: + continue + seen.add(key) + generated.append(edge) + if len(generated) >= budget: + break + + return generated[:budget] + + @staticmethod + def _extract_entity_tokens(question: str) -> list[str]: + return re.findall(r"\b(?:alias|user|org|loc|post|thr|thread|event)_[a-zA-Z0-9_]+\b", question) + + @staticmethod + def _normalize_difficulty(value: str, index: int) -> str: + token = str(value or "").strip().lower() + if token in {"easy", "e"}: + return "easy" + if token in {"mid", "medium", "m"}: + return "medium" + if token in {"high", "hard", "h"}: + return "hard" + if index < 10: + return "easy" + if index < 20: + return "medium" + return "hard" + + @staticmethod + def _task_type_for_difficulty(base_task_type: str, difficulty: str) -> str: + token = str(base_task_type or "").strip().lower() + if token and token != "fixed_trace": + return token + if difficulty == "easy": + return "easy_trace" + if difficulty == "medium": + return "medium_trace" + return "hard_trace" + + @staticmethod + def _grader_for_difficulty(difficulty: str) -> dict[str, Any]: + return { + "type": "difficulty_exact_match", + "answer_type": "node_id", + "case_sensitive": True, + "reward_profile": difficulty, + "logic": { + "easy": "single_agent_simplified", + "medium": "reduced_components", + "hard": "full_reward", + }.get(difficulty, "full_reward"), + } + + def _task_metadata(self, index: int, base_task_type: str, metadata: dict[str, Any] | None = None) -> dict[str, Any]: + out = dict(metadata or {}) + difficulty = self._normalize_difficulty(out.get("difficulty", ""), index) + out["difficulty"] = difficulty + out.setdefault("grader", self._grader_for_difficulty(difficulty)) + out.setdefault("scenario", self._task_type_for_difficulty(base_task_type, difficulty)) + return out + + def _infer_answer_from_question(self, question: str, graph: CanonicalGraph) -> str: + entities = self._extract_entity_tokens(question) + question_l = question.lower() + + alias_tokens = [token for token in entities if token.startswith("alias_")] + if alias_tokens: + alias = alias_tokens[0] + for edge in graph.edges: + if edge.rel == "alias_of" and edge.src == alias: + return edge.dst + + if "connected" in question_l: + user_tokens = [token for token in entities if token.startswith("user_")] + if user_tokens: + source = user_tokens[0] + for edge in graph.edges: + if edge.rel == "connected_to" and edge.src == source: + return edge.dst + + if "works at" in question_l: + for edge in graph.edges: + if edge.rel != "works_at": + continue + org = graph.nodes.get(edge.dst) + org_name = str((org.attrs or {}).get("name", "")).lower() if org else "" + if org_name and org_name in question_l: + return edge.src + + return entities[0] if entities else "unknown" + + def _infer_support_edges(self, question: str, answer: str, graph: CanonicalGraph) -> list[Edge]: + if answer: + for edge in graph.edges: + if edge.dst == answer or edge.src == answer: + if edge.src in question or edge.dst in question or edge.rel in question.lower(): + return [edge] + + entities = self._extract_entity_tokens(question) + for edge in graph.edges: + if edge.src in entities or edge.dst in entities: + return [edge] + return [] + + def _seeded_tasks(self, graph: CanonicalGraph) -> list[TaskInstance]: + tasks: list[TaskInstance] = [] + for idx, question_spec in enumerate(self.config.seeding.seeded_questions): + answer = question_spec.answer or self._infer_answer_from_question(question_spec.question, graph) + metadata = self._task_metadata(idx, question_spec.task_type, dict(question_spec.metadata)) + difficulty = str(metadata.get("difficulty", "hard")) + if question_spec.supporting_edges: + support = [ + Edge(src=e.src, rel=e.rel, dst=e.dst, confidence=float(e.confidence)) + for e in question_spec.supporting_edges + ] + else: + support = self._infer_support_edges(question_spec.question, answer, graph) + + tasks.append( + TaskInstance( + task_id=f"seed_task_{idx}", + task_type=self._task_type_for_difficulty(question_spec.task_type, difficulty), + question=question_spec.question, + answer=answer, + supporting_edges=support, + metadata=metadata, + ) + ) + return tasks + + def _template_tasks(self, graph: CanonicalGraph, count: int, start_idx: int = 0) -> list[TaskInstance]: + alias_edges = [e for e in graph.edges if e.rel == "alias_of"] + conn_edges = [e for e in graph.edges if e.rel == "connected_to"] + work_edges = [e for e in graph.edges if e.rel == "works_at"] + tasks: list[TaskInstance] = [] + + for i in range(count): + mode = self.rng.choice(["identity_resolution", "network_discovery", "event_tracing"]) + if mode == "identity_resolution" and alias_edges: + edge = self.rng.choice(alias_edges) + q = f"Which canonical user owns alias {edge.src}?" + a = edge.dst + support = [edge] + elif mode == "network_discovery" and conn_edges: + edge = self.rng.choice(conn_edges) + q = f"Who is connected to {edge.src}?" + a = edge.dst + support = [edge] + else: + edge = self.rng.choice(work_edges) + org_node = graph.nodes.get(edge.dst) + org_name = (org_node.attrs or {}).get("name", edge.dst) if org_node else edge.dst + q = f"Which user works at {org_name}?" + a = edge.src + support = [edge] + tasks.append( + TaskInstance( + task_id=f"task_{start_idx + i}", + task_type=mode, + question=q, + answer=a, + supporting_edges=support, + metadata=self._task_metadata(start_idx + i, mode), + ) + ) + return tasks + + def _llm_generated_tasks(self, graph: CanonicalGraph, count: int, start_idx: int) -> list[TaskInstance]: + if count <= 0: + return [] + if self.llm is None: + return self._template_tasks(graph, count=count, start_idx=start_idx) + + candidate_edges = [ + {"src": edge.src, "rel": edge.rel, "dst": edge.dst} + for edge in graph.edges + if edge.rel in {"alias_of", "connected_to", "works_at"} + ][:60] + shared_context = json.dumps( + { + "known_nodes": sorted(graph.nodes.keys())[:100], + "edge_sample": candidate_edges, + } + ) + workers = max(1, min(self.config.seeding.llm_generation_workers, count)) + chunks = self._split_budget(count, workers) + focus_tracks = ["identity_resolution", "network_discovery", "event_tracing", "deanonymization"] + + prompts: list[str] = [] + for idx, chunk_budget in enumerate(chunks): + focus = focus_tracks[idx % len(focus_tracks)] + prompts.append( + ( + "SEED_TASK_EXPANSION_AGENT\n" + "SHARED_CONTEXT\n" + f"{shared_context}\n" + f"worker_id: {idx}\n" + f"focus: {focus}\n" + f"task_budget: {chunk_budget}\n" + "Generate OSINT QA tasks with answers and support edges.\n" + "Return STRICT JSON object: {\"tasks\": [{\"task_type\": str, \"question\": str, \"answer\": str, \"supporting_edges\": [{\"src\": str, \"rel\": str, \"dst\": str, \"confidence\": float}]}]}." + ) + ) + + llm_tasks: list[TaskInstance] = [] + seen_questions: set[str] = set() + for payload in self._run_generation_workers(prompts): + raw_tasks: Any = None + if isinstance(payload, dict): + raw_tasks = payload.get("tasks") + elif isinstance(payload, list): + raw_tasks = payload + if not isinstance(raw_tasks, list): + continue + + for row in raw_tasks: + if not isinstance(row, dict): + continue + question = str(row.get("question", "")).strip() + if not question: + continue + key = question.lower() + if key in seen_questions: + continue + seen_questions.add(key) + answer = str(row.get("answer", "")).strip() or self._infer_answer_from_question(question, graph) + task_type = str(row.get("task_type", "llm_generated")).strip() or "llm_generated" + support_specs = self._normalize_edge_candidates(row.get("supporting_edges")) + if support_specs: + support = [Edge(e.src, e.rel, e.dst, e.confidence) for e in support_specs] + else: + support = self._infer_support_edges(question, answer, graph) + llm_tasks.append( + TaskInstance( + task_id=f"task_{start_idx + len(llm_tasks)}", + task_type=task_type, + question=question, + answer=answer, + supporting_edges=support, + metadata=self._task_metadata( + start_idx + len(llm_tasks), + task_type, + {"generated_by": "llm", "shared_context": True}, + ), + ) + ) + if len(llm_tasks) >= count: + break + if len(llm_tasks) >= count: + break + + if len(llm_tasks) < count: + residual = count - len(llm_tasks) + residual_prompt = ( + "SEED_TASK_EXPANSION_AGENT\n" + "SHARED_CONTEXT\n" + f"{shared_context}\n" + f"task_budget: {residual}\n" + "Generate additional tasks not already present in SHARED_CONTEXT.\n" + "Return STRICT JSON object: {\"tasks\": [{\"task_type\": str, \"question\": str, \"answer\": str, \"supporting_edges\": [{\"src\": str, \"rel\": str, \"dst\": str, \"confidence\": float}]}]}." + ) + payload = self._llm_generate_json_with_retry(residual_prompt) + raw_tasks: Any = payload.get("tasks") if isinstance(payload, dict) else payload + if isinstance(raw_tasks, list): + for row in raw_tasks: + if not isinstance(row, dict): + continue + question = str(row.get("question", "")).strip() + if not question: + continue + key = question.lower() + if key in seen_questions: + continue + seen_questions.add(key) + answer = str(row.get("answer", "")).strip() or self._infer_answer_from_question(question, graph) + task_type = str(row.get("task_type", "llm_generated")).strip() or "llm_generated" + support_specs = self._normalize_edge_candidates(row.get("supporting_edges")) + if support_specs: + support = [Edge(e.src, e.rel, e.dst, e.confidence) for e in support_specs] + else: + support = self._infer_support_edges(question, answer, graph) + llm_tasks.append( + TaskInstance( + task_id=f"task_{start_idx + len(llm_tasks)}", + task_type=task_type, + question=question, + answer=answer, + supporting_edges=support, + metadata=self._task_metadata( + start_idx + len(llm_tasks), + task_type, + {"generated_by": "llm", "shared_context": True}, + ), + ) + ) + if len(llm_tasks) >= count: + break + + if len(llm_tasks) < count and self._template_fallback_allowed(): + llm_tasks.extend( + self._template_tasks( + graph, + count=count - len(llm_tasks), + start_idx=start_idx + len(llm_tasks), + ) + ) + return llm_tasks[:count] + + def _metaqa_selected_records(self, count: int) -> list[MetaQATaskRecord]: + records = list(self._metaqa_records) + if not records: + return [] + if count <= 0 or len(records) <= count: + return records + + grouped: dict[str, list[MetaQATaskRecord]] = {} + for record in records: + grouped.setdefault(record.hop_label, []).append(record) + + hop_keys = sorted(grouped.keys()) + if not hop_keys: + return records[:count] + + selected: list[MetaQATaskRecord] = [] + leftovers: list[MetaQATaskRecord] = [] + per_hop = max(1, count // len(hop_keys)) + + for hop in hop_keys: + bucket = list(grouped[hop]) + self.rng.shuffle(bucket) + take = min(len(bucket), per_hop) + selected.extend(bucket[:take]) + leftovers.extend(bucket[take:]) + + if len(selected) < count: + self.rng.shuffle(leftovers) + selected.extend(leftovers[: count - len(selected)]) + + return selected[:count] + + def _metaqa_tasks(self, graph: CanonicalGraph, count: int) -> list[TaskInstance]: + records = self._metaqa_selected_records(count) + tasks: list[TaskInstance] = [] + for idx, record in enumerate(records): + difficulty = self._metaqa_difficulty(record.hop_label) + support_edges = list(record.supporting_edges) + if not support_edges: + support_edges = infer_metaqa_support_edges( + graph=graph, + topic_entity=record.topic_entity, + answer_candidates=record.answers, + hop_count=record.hop_count, + ) + metadata = { + "difficulty": difficulty, + "hop": record.hop_label, + "split": record.split, + "source": "metaqa", + "dataset_mode": "metaqa", + "qtype": record.qtype, + "topic_entity": record.topic_entity, + "all_answers": list(record.answers), + "grader": { + "type": "metaqa_exact_match", + "answer_type": "entity_name", + "case_sensitive": True, + "reward_profile": difficulty, + "logic": "hop_trace", + }, + "scenario": f"metaqa_{record.hop_label}", + } + task_type = f"metaqa_{record.hop_label}" + tasks.append( + TaskInstance( + task_id=f"metaqa_{record.hop_label}_{record.split}_{idx}", + task_type=task_type, + question=record.question, + answer=record.primary_answer, + supporting_edges=support_edges, + metadata=metadata, + ) + ) + return tasks + + def _build_platform_views_metaqa(self, graph: CanonicalGraph) -> PlatformViews: + node_names = { + node_id: str((node.attrs or {}).get("name") or node_id) + for node_id, node in graph.nodes.items() + } + + microblog_posts: list[dict] = [] + for idx, edge in enumerate(graph.edges): + microblog_posts.append( + { + "post_id": f"post_metaqa_{idx}", + "user_id": edge.src, + "canonical_user": edge.src, + "text": f"{edge.src} {edge.rel} {edge.dst}", + "references": [edge.src, edge.dst], + "reference_names": [node_names.get(edge.src, edge.src), node_names.get(edge.dst, edge.dst)], + "mentions": [edge.dst], + "timestamp": 100000 + idx, + } + ) + + relation_groups: dict[str, list[Edge]] = {} + for edge in graph.edges: + relation_groups.setdefault(edge.rel, []).append(edge) + + forum_threads: list[dict] = [] + for idx, rel in enumerate(sorted(relation_groups.keys())[:200]): + group = relation_groups.get(rel, [])[:10] + forum_threads.append( + { + "thread_id": f"thr_metaqa_{idx}", + "topic": rel, + "author_id": group[0].src if group else "metaqa", + "comments": [ + { + "user_id": edge.src, + "text": f"{edge.src} {edge.rel} {edge.dst}", + } + for edge in group + ], + "references": [edge.dst for edge in group], + "discusses": [edge.dst for edge in group], + } + ) + + neighbors: dict[str, set[str]] = {} + for edge in graph.edges: + neighbors.setdefault(edge.src, set()).add(edge.dst) + neighbors.setdefault(edge.dst, set()).add(edge.src) + + profiles: list[dict] = [] + for node_id in sorted(graph.nodes.keys()): + node = graph.nodes[node_id] + profiles.append( + { + "user_id": node_id, + "name": str((node.attrs or {}).get("name") or node_id), + "org": str(node.node_type.value), + "org_id": str(node.node_type.value), + "location": "metaqa", + "location_id": "metaqa", + "alias_ids": [], + "connections": sorted(neighbors.get(node_id, set()))[:8], + "work_history": [str(node.node_type.value)], + } + ) + + return PlatformViews( + microblog_posts=microblog_posts, + forum_threads=forum_threads, + profiles=profiles, + alias_lookup={}, + ) + + def build_canonical_graph(self) -> CanonicalGraph: + if self._dataset_mode() == "metaqa": + root = Path(self.config.metaqa_root) + kb_path = Path(self.config.metaqa_kb_path) if str(self.config.metaqa_kb_path).strip() else None + graph, records = load_metaqa_dataset( + root=root, + kb_path=kb_path, + variant=self.config.metaqa_variant, + hops=list(self.config.metaqa_hops), + splits=list(self.config.metaqa_splits), + ) + self._metaqa_records = records + self._apply_seed_nodes(graph) + self._apply_seed_edges(graph) + return graph + + graph = CanonicalGraph() + orgs = ["Apex Dynamics", "Helios Labs", "Northbridge"] + locations = ["Bengaluru", "Pune", "Hyderabad", "Delhi"] + + for i in range(self.config.n_users): + uid = f"user_{i}" + org = self.rng.choice(orgs) + loc = self.rng.choice(locations) + graph.nodes[uid] = Node(uid, NodeType.USER, {"name": f"Person {i}", "org": org, "location": loc}) + org_id = f"org_{org.lower().replace(' ', '_')}" + loc_id = f"loc_{loc.lower()}" + graph.nodes.setdefault(org_id, Node(org_id, NodeType.ORG, {"name": org})) + graph.nodes.setdefault(loc_id, Node(loc_id, NodeType.LOCATION, {"name": loc})) + graph.edges.append(Edge(uid, "works_at", org_id)) + graph.edges.append(Edge(uid, "located_in", loc_id)) + + if self.rng.random() < self.config.alias_density: + alias = f"alias_{i}_{self.rng.randint(100,999)}" + graph.nodes[alias] = Node(alias, NodeType.ALIAS, {"handle": f"@{alias}"}) + graph.edges.append(Edge(alias, "alias_of", uid)) + + users = [n for n in graph.nodes.values() if n.node_type == NodeType.USER] + for _ in range(max(1, self.config.n_users // 2)): + a, b = self.rng.sample(users, 2) + graph.edges.append(Edge(a.node_id, "connected_to", b.node_id, confidence=0.8)) + + self._apply_seed_nodes(graph) + self._apply_seed_edges(graph) + + if self.config.seeding.llm_generate_remaining_graph: + llm_edges = self._llm_expand_graph(graph, self.config.seeding.llm_generated_edge_budget) + for edge in llm_edges: + self._add_edge_if_missing(graph, edge) + return graph + + def build_platform_views(self, graph: CanonicalGraph) -> PlatformViews: + if self._dataset_mode() == "metaqa": + return self._build_platform_views_metaqa(graph) + + users = [n for n in graph.nodes.values() if n.node_type == NodeType.USER] + aliases = [n for n in graph.nodes.values() if n.node_type == NodeType.ALIAS] + alias_owner = {e.src: e.dst for e in graph.edges if e.rel == "alias_of"} + user_aliases: dict[str, list[str]] = {} + for alias_id, user_id in alias_owner.items(): + user_aliases.setdefault(user_id, []).append(alias_id) + node_names = { + node_id: str((node.attrs or {}).get("name") or (node.attrs or {}).get("handle") or node_id) + for node_id, node in graph.nodes.items() + } + + microblog_posts: list[dict] = [] + for i, user in enumerate(users): + poster = user.node_id + if aliases and self.rng.random() < 0.45: + candidate = self.rng.choice(aliases).node_id + poster = candidate + text = f"Update {i} from {user.attrs['org']} #{user.attrs['location'].lower()}" + if self.rng.random() < self.config.noise_level: + text = f"Rumor: {text} maybe fake" + microblog_posts.append( + { + "post_id": f"post_{i}", + "user_id": poster, + "canonical_user": alias_owner.get(poster, user.node_id), + "text": text, + "references": [], + "reference_names": [], + "mentions": [f"user_{self.rng.randint(0, self.config.n_users - 1)}"], + "timestamp": 1000 + i, + } + ) + + authored_posts: dict[str, str] = {} + post_references: dict[str, list[str]] = {} + for edge in graph.edges: + if edge.rel == "authored_post": + authored_posts[edge.dst] = edge.src + elif edge.rel == "references" and edge.src.startswith("post_"): + post_references.setdefault(edge.src, []).append(edge.dst) + + for post_id, author_id in authored_posts.items(): + refs = post_references.get(post_id, []) + ref_names = [node_names.get(ref, ref) for ref in refs] + author_label = node_names.get(author_id, author_id) + text_parts = [f"{post_id} update from {author_label}"] + if ref_names: + text_parts.append("references " + ", ".join(ref_names)) + if refs: + text_parts.append("ids " + ", ".join(refs)) + post_payload = { + "post_id": post_id, + "user_id": author_id, + "canonical_user": alias_owner.get(author_id, author_id), + "text": ". ".join(text_parts), + "references": refs, + "reference_names": ref_names, + "mentions": [], + "timestamp": 5000 + len(microblog_posts), + } + existing_idx = next((idx for idx, row in enumerate(microblog_posts) if row["post_id"] == post_id), None) + if existing_idx is None: + microblog_posts.append(post_payload) + else: + microblog_posts[existing_idx] = post_payload + + forum_threads: list[dict] = [] + for i in range(max(8, self.config.n_users // 3)): + author = self.rng.choice(users).node_id + forum_threads.append( + { + "thread_id": f"thr_{i}", + "topic": self.rng.choice(["security", "startup", "ai", "infra"]), + "author_id": author, + "comments": [ + {"user_id": self.rng.choice(users).node_id, "text": "Following this."}, + {"user_id": self.rng.choice(users).node_id, "text": "Interesting link."}, + ], + "references": [], + "discusses": [], + } + ) + + authored_threads: dict[str, str] = {} + thread_refs: dict[str, list[str]] = {} + thread_discusses: dict[str, list[str]] = {} + for edge in graph.edges: + if edge.rel == "authored_thread": + authored_threads[edge.dst] = edge.src + elif edge.rel == "references" and edge.src.startswith(("thr_", "thread_")): + thread_refs.setdefault(edge.src, []).append(edge.dst) + elif edge.rel == "discusses" and edge.src.startswith(("thr_", "thread_")): + thread_discusses.setdefault(edge.src, []).append(edge.dst) + + for thread_id, author_id in authored_threads.items(): + node = graph.nodes.get(thread_id) + refs = thread_refs.get(thread_id, []) + discussed = thread_discusses.get(thread_id, []) + comments = [] + for ref in refs: + comments.append({"user_id": author_id, "text": f"Reference: {node_names.get(ref, ref)} ({ref})"}) + for item in discussed: + comments.append({"user_id": author_id, "text": f"Discusses: {node_names.get(item, item)} ({item})"}) + thread_payload = { + "thread_id": thread_id, + "topic": str((node.attrs or {}).get("topic", "seeded")) if node else "seeded", + "author_id": author_id, + "title": node_names.get(thread_id, thread_id), + "comments": comments, + "references": refs, + "discusses": discussed, + } + existing_idx = next((idx for idx, row in enumerate(forum_threads) if row["thread_id"] == thread_id), None) + if existing_idx is None: + forum_threads.append(thread_payload) + else: + forum_threads[existing_idx] = thread_payload + + profiles: list[dict] = [] + for user in users: + conns = [e.dst for e in graph.edges if e.src == user.node_id and e.rel == "connected_to"][:5] + org_id = next((e.dst for e in graph.edges if e.src == user.node_id and e.rel == "works_at"), "") + location_id = next((e.dst for e in graph.edges if e.src == user.node_id and e.rel == "located_in"), "") + profiles.append( + { + "user_id": user.node_id, + "name": user.attrs["name"], + "org": user.attrs["org"], + "org_id": org_id, + "location": user.attrs["location"], + "location_id": location_id, + "alias_ids": sorted(user_aliases.get(user.node_id, [])), + "connections": conns, + "work_history": [user.attrs["org"]], + } + ) + + for i in range(int(len(users) * self.config.red_herring_rate)): + profiles.append( + { + "user_id": f"noise_{i}", + "name": f"P{self.rng.randint(100,999)}", + "org": self.rng.choice(["Stealth Co", "Unknown Ventures"]), + "org_id": "", + "location": self.rng.choice(["Remote", "Unknown"]), + "location_id": "", + "alias_ids": [], + "connections": [], + "work_history": [], + } + ) + return PlatformViews(microblog_posts, forum_threads, profiles, alias_lookup=alias_owner) + + def generate_tasks(self, graph: CanonicalGraph, views: PlatformViews, count: int = 12) -> list[TaskInstance]: + if self._dataset_mode() == "metaqa": + metaqa_tasks = self._metaqa_tasks(graph=graph, count=max(1, count)) + if metaqa_tasks: + return metaqa_tasks + + tasks = self._seeded_tasks(graph) + target_count = max(1, count, len(tasks)) + + llm_budget = min( + max(0, self.config.seeding.llm_generated_task_budget), + max(0, target_count - len(tasks)), + ) + if self.config.seeding.llm_generate_remaining_tasks and llm_budget > 0: + tasks.extend(self._llm_generated_tasks(graph, count=llm_budget, start_idx=len(tasks))) + + if len(tasks) < target_count and self._template_fallback_allowed(): + tasks.extend(self._template_tasks(graph, count=target_count - len(tasks), start_idx=len(tasks))) + + if not tasks: + tasks.extend(self._template_tasks(graph, count=target_count, start_idx=0)) + + return tasks[:target_count] diff --git a/src/osint_env/data/metaqa.py b/src/osint_env/data/metaqa.py new file mode 100644 index 0000000000000000000000000000000000000000..478f0bfb8c97b70c3f01d70e55f2926de366e425 --- /dev/null +++ b/src/osint_env/data/metaqa.py @@ -0,0 +1,246 @@ +from __future__ import annotations + +from collections import deque +from dataclasses import dataclass +from pathlib import Path +import re +from typing import Iterable + +from osint_env.domain.models import CanonicalGraph, Edge, Node, NodeType + + +_TOPIC_PATTERN = re.compile(r"\[(.*?)\]") + + +@dataclass(slots=True) +class MetaQATaskRecord: + question: str + answers: list[str] + primary_answer: str + hop_label: str + hop_count: int + split: str + qtype: str + topic_entity: str + supporting_edges: list[Edge] + + +def _normalize_hop_label(value: str) -> str: + token = str(value or "").strip().lower().replace(" ", "") + if token in {"1", "1hop", "1-hop"}: + return "1-hop" + if token in {"2", "2hop", "2-hop"}: + return "2-hop" + if token in {"3", "3hop", "3-hop"}: + return "3-hop" + return "" + + +def _normalize_split(value: str) -> str: + token = str(value or "").strip().lower() + if token in {"train", "dev", "test"}: + return token + return "" + + +def _hop_count(label: str) -> int: + return int(label.split("-", 1)[0]) + + +def _extract_topic_entity(question: str) -> str: + match = _TOPIC_PATTERN.search(str(question)) + return match.group(1).strip() if match else "" + + +def _node_types_for_relation(rel: str) -> tuple[NodeType, NodeType]: + relation = str(rel or "").strip().lower() + src_type = NodeType.POST + if relation in {"directed_by", "written_by", "starred_actors"}: + return src_type, NodeType.USER + if relation == "release_year": + return src_type, NodeType.EVENT + if relation == "in_language": + return src_type, NodeType.LOCATION + if relation in {"has_genre", "has_tags", "has_imdb_votes"}: + return src_type, NodeType.ORG + return src_type, NodeType.USER + + +def _ensure_node(graph: CanonicalGraph, node_id: str, node_type: NodeType) -> None: + existing = graph.nodes.get(node_id) + if existing is not None: + return + graph.nodes[node_id] = Node(node_id=node_id, node_type=node_type, attrs={"name": node_id}) + + +def _read_non_empty_lines(path: Path) -> list[str]: + return [line.strip() for line in path.read_text(encoding="utf-8").splitlines() if line.strip()] + + +def _parse_kb_line(line: str) -> tuple[str, str, str] | None: + parts = [part.strip() for part in str(line).split("|", 2)] + if len(parts) != 3: + return None + src, rel, dst = parts + if not src or not rel or not dst: + return None + return src, rel, dst + + +def _undirected_adjacency(edges: Iterable[Edge]) -> dict[str, list[tuple[str, Edge]]]: + adj: dict[str, list[tuple[str, Edge]]] = {} + for edge in edges: + adj.setdefault(edge.src, []).append((edge.dst, edge)) + adj.setdefault(edge.dst, []).append((edge.src, edge)) + return adj + + +def _bfs_support_path( + topic_entity: str, + answer_candidates: list[str], + adjacency: dict[str, list[tuple[str, Edge]]], + max_depth: int, +) -> list[Edge]: + topic = str(topic_entity or "").strip() + if not topic or topic not in adjacency: + return [] + + answers = {item.strip() for item in answer_candidates if item.strip()} + if not answers: + return [] + + queue: deque[tuple[str, list[Edge]]] = deque([(topic, [])]) + visited_depth: dict[str, int] = {topic: 0} + + while queue: + node, path = queue.popleft() + depth = len(path) + if depth > max_depth: + continue + if node in answers and path: + return path + if depth == max_depth: + continue + for neighbor, edge in adjacency.get(node, []): + next_depth = depth + 1 + best = visited_depth.get(neighbor) + if best is not None and best <= next_depth: + continue + visited_depth[neighbor] = next_depth + queue.append((neighbor, path + [edge])) + return [] + + +def _infer_support_edges( + topic_entity: str, + answer_candidates: list[str], + adjacency: dict[str, list[tuple[str, Edge]]], + hop_count: int, +) -> list[Edge]: + for limit in (hop_count, hop_count + 1, hop_count + 2, max(4, hop_count + 3)): + path = _bfs_support_path(topic_entity, answer_candidates, adjacency, max_depth=max(1, limit)) + if path: + return path + return [] + + +def infer_metaqa_support_edges( + graph: CanonicalGraph, + topic_entity: str, + answer_candidates: list[str], + hop_count: int, +) -> list[Edge]: + adjacency = _undirected_adjacency(graph.edges) + return _infer_support_edges( + topic_entity=topic_entity, + answer_candidates=answer_candidates, + adjacency=adjacency, + hop_count=hop_count, + ) + + +def load_metaqa_dataset( + root: str | Path, + kb_path: str | Path | None, + variant: str, + hops: list[str], + splits: list[str], +) -> tuple[CanonicalGraph, list[MetaQATaskRecord]]: + root_path = Path(root) + if not root_path.exists(): + raise FileNotFoundError(f"MetaQA root not found: {root_path}") + + kb_file = Path(kb_path) if kb_path else root_path / "kb.txt" + if not kb_file.exists(): + raise FileNotFoundError(f"MetaQA KB file not found: {kb_file}") + + graph = CanonicalGraph() + seen_edges: set[tuple[str, str, str]] = set() + + for raw_line in _read_non_empty_lines(kb_file): + row = _parse_kb_line(raw_line) + if row is None: + continue + src, rel, dst = row + edge_key = (src, rel, dst) + if edge_key in seen_edges: + continue + seen_edges.add(edge_key) + src_type, dst_type = _node_types_for_relation(rel) + _ensure_node(graph, src, src_type) + _ensure_node(graph, dst, dst_type) + graph.edges.append(Edge(src=src, rel=rel, dst=dst, confidence=1.0)) + + hop_labels = [_normalize_hop_label(hop) for hop in hops] + hop_labels = [hop for hop in hop_labels if hop] + if not hop_labels: + hop_labels = ["1-hop", "2-hop", "3-hop"] + + split_labels = [_normalize_split(split) for split in splits] + split_labels = [split for split in split_labels if split] + if not split_labels: + split_labels = ["train", "dev", "test"] + + variant_token = str(variant or "vanilla").strip().lower() + if variant_token not in {"vanilla", "ntm"}: + variant_token = "vanilla" + + records: list[MetaQATaskRecord] = [] + for hop in hop_labels: + hop_dir = root_path / hop + for split in split_labels: + qa_path = hop_dir / variant_token / f"qa_{split}.txt" + if not qa_path.exists(): + continue + qa_lines = _read_non_empty_lines(qa_path) + + qtype_path = hop_dir / f"qa_{split}_qtype.txt" + qtypes = _read_non_empty_lines(qtype_path) if qtype_path.exists() else [] + + for idx, row in enumerate(qa_lines): + parts = row.split("\t") + if len(parts) < 2: + continue + question = parts[0].strip() + answer_blob = parts[1].strip() + answers = [item.strip() for item in answer_blob.split("|") if item.strip()] + if not question or not answers: + continue + + topic_entity = _extract_topic_entity(question) + qtype = qtypes[idx] if idx < len(qtypes) else "" + records.append( + MetaQATaskRecord( + question=question, + answers=answers, + primary_answer=answers[0], + hop_label=hop, + hop_count=_hop_count(hop), + split=split, + qtype=qtype, + topic_entity=topic_entity, + supporting_edges=[], + ) + ) + + return graph, records diff --git a/src/osint_env/domain/__init__.py b/src/osint_env/domain/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..09b80bffb5e77f4d52a066830e4d530fb9aa4b8e --- /dev/null +++ b/src/osint_env/domain/__init__.py @@ -0,0 +1,2 @@ +"""Core domain models.""" + diff --git a/src/osint_env/domain/models.py b/src/osint_env/domain/models.py new file mode 100644 index 0000000000000000000000000000000000000000..ed7d9ac551dbd9feb34e5e618583b189306dfaac --- /dev/null +++ b/src/osint_env/domain/models.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from enum import Enum +from typing import Any + +from pydantic import BaseModel, ConfigDict, Field + + +class NodeType(str, Enum): + USER = "user" + ALIAS = "alias" + ORG = "org" + LOCATION = "location" + POST = "post" + THREAD = "thread" + EVENT = "event" + + +class ActionType(str, Enum): + CALL_TOOL = "CALL_TOOL" + ADD_EDGE = "ADD_EDGE" + ANSWER = "ANSWER" + + +@dataclass(slots=True) +class Node: + node_id: str + node_type: NodeType + attrs: dict[str, Any] = field(default_factory=dict) + + +@dataclass(slots=True) +class Edge: + src: str + rel: str + dst: str + confidence: float = 1.0 + + +@dataclass(slots=True) +class CanonicalGraph: + nodes: dict[str, Node] = field(default_factory=dict) + edges: list[Edge] = field(default_factory=list) + + +@dataclass(slots=True) +class ToolCall: + tool_name: str + args: dict[str, Any] + + +class Action(BaseModel): + """Structured action payload used by OpenEnv step().""" + + model_config = ConfigDict(extra="forbid") + + action_type: ActionType + payload: dict[str, Any] = Field(default_factory=dict) + + def __init__(self, *args: Any, **kwargs: Any) -> None: + # Backward-compatible positional form: Action(action_type, payload) + if args: + if len(args) != 2: + raise TypeError("Action() accepts either keyword fields or 2 positional args") + if "action_type" in kwargs or "payload" in kwargs: + raise TypeError("Action() cannot mix positional and keyword fields") + kwargs["action_type"] = args[0] + kwargs["payload"] = args[1] + super().__init__(**kwargs) + + +class Observation(BaseModel): + """Typed observation payload returned by reset()/step()/state().""" + + model_config = ConfigDict(extra="forbid") + + tool_outputs: list[dict[str, Any]] = Field(default_factory=list) + graph_snapshot: dict[str, Any] = Field(default_factory=dict) + action_history: list[dict[str, Any]] = Field(default_factory=list) + task: dict[str, Any] = Field(default_factory=dict) + + +class Reward(BaseModel): + """Typed reward payload for structured reward accounting.""" + + model_config = ConfigDict(extra="forbid") + + value: float = 0.0 + components: dict[str, float] = Field(default_factory=dict) + + +@dataclass(slots=True) +class TaskInstance: + task_id: str + task_type: str + question: str + answer: str + supporting_edges: list[Edge] + metadata: dict[str, Any] = field(default_factory=dict) + + +@dataclass(slots=True) +class SeedNodeSpec: + node_id: str + node_type: NodeType | str + attrs: dict[str, Any] = field(default_factory=dict) + + +@dataclass(slots=True) +class SeedEdgeSpec: + src: str + rel: str + dst: str + confidence: float = 1.0 + + +@dataclass(slots=True) +class SeedQuestionSpec: + question: str + answer: str | None = None + task_type: str = "seeded" + supporting_edges: list[SeedEdgeSpec] = field(default_factory=list) + metadata: dict[str, Any] = field(default_factory=dict) + + +@dataclass(slots=True) +class SeedingConfig: + seeded_nodes: list[SeedNodeSpec] = field(default_factory=list) + seeded_edges: list[SeedEdgeSpec] = field(default_factory=list) + seeded_questions: list[SeedQuestionSpec] = field(default_factory=list) + llm_generate_remaining_graph: bool = True + llm_generate_remaining_tasks: bool = True + llm_generated_edge_budget: int = 6 + llm_generated_task_budget: int = 8 + llm_generation_parallel: bool = True + llm_generation_workers: int = 3 + llm_generation_retries: int = 2 + allow_template_fallback_on_llm_failure: bool = False + + +@dataclass(slots=True) +class SwarmConfig: + enabled: bool = False + max_agents: int = 3 + max_breadth: int = 2 + max_width: int = 2 + max_depth: int = 2 + planner_rounds: int = 2 + tools_per_agent: int = 1 + + +@dataclass(slots=True) +class SpawnRewardConfig: + lambda_parallel: float = 0.15 + lambda_finish: float = 0.20 + anneal: float = 1.0 + max_parallel_hint: int = 3 + + +@dataclass(slots=True) +class LLMConfig: + provider: str = "mock" + model: str = "qwen3:2b" + temperature: float = 0.1 + max_tokens: int = 256 + timeout_seconds: int = 240 + ollama_base_url: str = "http://127.0.0.1:11434" + openai_base_url: str = "https://api.openai.com/v1" + openai_api_key_env: str = "OPENAI_API_KEY" + openai_api_key: str = "" + + +@dataclass(slots=True) +class EnvironmentConfig: + n_users: int = 40 + alias_density: float = 0.35 + noise_level: float = 0.15 + red_herring_rate: float = 0.1 + max_steps: int = 18 + seed: int = 7 + dataset_mode: str = "canonical" + metaqa_root: str = "metaQA" + metaqa_kb_path: str = "" + metaqa_variant: str = "vanilla" + metaqa_hops: list[str] = field(default_factory=lambda: ["1-hop", "2-hop", "3-hop"]) + metaqa_splits: list[str] = field(default_factory=lambda: ["train", "dev", "test"]) + seeding: SeedingConfig = field(default_factory=SeedingConfig) + swarm: SwarmConfig = field(default_factory=SwarmConfig) + spawn_reward: SpawnRewardConfig = field(default_factory=SpawnRewardConfig) + llm: LLMConfig = field(default_factory=LLMConfig) diff --git a/src/osint_env/env/__init__.py b/src/osint_env/env/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..afc94dfc868f270a67ff4cd803494cce2d15bdb8 --- /dev/null +++ b/src/osint_env/env/__init__.py @@ -0,0 +1,2 @@ +"""Environment package.""" + diff --git a/src/osint_env/env/environment.py b/src/osint_env/env/environment.py new file mode 100644 index 0000000000000000000000000000000000000000..b6e4f1cedf69906221fffaf3c39eff821453fbd1 --- /dev/null +++ b/src/osint_env/env/environment.py @@ -0,0 +1,260 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Any + +from osint_env.data.generator import DatasetGenerator +from osint_env.domain.models import Action, ActionType, Edge, EnvironmentConfig, Observation, TaskInstance +from osint_env.env.openenv_compat import Env +from osint_env.env.reward import ( + build_reward_model, + compute_answer_reward, + compute_edge_reward, + compute_graph_f1, +) +from osint_env.memory.store import MemoryGraph, SemanticMemory +from osint_env.platforms.tools import ToolRegistry + +if TYPE_CHECKING: + from osint_env.llm.interface import LLMClient + + +@dataclass(slots=True) +class EpisodeState: + task: TaskInstance + task_index: int = 0 + difficulty: str = "hard" + step_count: int = 0 + done: bool = False + total_reward: float = 0.0 + tool_calls: int = 0 + redundant_tool_calls: int = 0 + action_history: list[dict[str, Any]] = field(default_factory=list) + tool_outputs: list[dict[str, Any]] = field(default_factory=list) + answer: str | None = None + call_fingerprints: set[str] = field(default_factory=set) + reward_components: dict[str, float] = field(default_factory=dict) + + +class OSINTEnvironment(Env): + def __init__(self, config: EnvironmentConfig, llm: "LLMClient | None" = None): + super().__init__( + name="OSINTEnvironment", + state_space="json-observation", + action_space=["CALL_TOOL", "ADD_EDGE", "ANSWER"], + episode_max_length=config.max_steps, + ) + self.config = config + self.generator = DatasetGenerator(config, llm=llm) + self.graph = self.generator.build_canonical_graph() + self.views = self.generator.build_platform_views(self.graph) + self.tasks = self.generator.generate_tasks(self.graph, self.views, count=24) + self.reward_model = build_reward_model(self.graph) + self.tools = ToolRegistry(self.views) + self.memory_graph = MemoryGraph() + self.semantic_memory = SemanticMemory() + self._task_idx = 0 + self.state: EpisodeState | None = None + + @staticmethod + def _normalize_difficulty(value: str) -> str: + token = str(value or "").strip().lower() + if token in {"easy", "e"}: + return "easy" + if token in {"mid", "medium", "m"}: + return "medium" + if token in {"high", "hard", "h"}: + return "hard" + return "hard" + + def _resolve_task_difficulty(self, task: TaskInstance, task_index: int) -> str: + metadata = dict(task.metadata or {}) + if "difficulty" in metadata: + return self._normalize_difficulty(str(metadata.get("difficulty", ""))) + if task_index < 10: + return "easy" + if task_index < 20: + return "medium" + return "hard" + + def reset(self) -> Observation: + task_index = self._task_idx % len(self.tasks) + task = self.tasks[task_index] + self._task_idx += 1 + self.state = EpisodeState( + task=task, + task_index=task_index, + difficulty=self._resolve_task_difficulty(task, task_index), + ) + self.memory_graph = MemoryGraph() + self.semantic_memory = SemanticMemory() + return self._observation() + + def step(self, action: Action) -> tuple[Observation, float, bool, dict[str, Any]]: + if self.state is None: + raise RuntimeError("Call reset() before step().") + if self.state.done: + return self._observation(), 0.0, True, self._info() + + self.state.step_count += 1 + reward = 0.0 + + if action.action_type == ActionType.CALL_TOOL: + reward += self._handle_tool(action.payload) + elif action.action_type == ActionType.ADD_EDGE: + reward += self._handle_add_edge(action.payload) + elif action.action_type == ActionType.ANSWER: + reward += self._handle_answer(action.payload) + else: + reward -= 0.5 + + if self.state.step_count >= self.config.max_steps and not self.state.done: + self.state.done = True + reward -= 0.3 + + self.state.total_reward += reward + self.state.action_history.append({"type": action.action_type.value, "payload": action.payload, "reward": reward}) + return self._observation(), reward, self.state.done, self._info() + + def _handle_tool(self, payload: dict[str, Any]) -> float: + if self.state is None: + return 0.0 + tool_name = payload["tool_name"] + args = payload.get("args", {}) + fp = f"{tool_name}:{sorted(args.items())}" + self.state.tool_calls += 1 + if fp in self.state.call_fingerprints: + self.state.redundant_tool_calls += 1 + penalty = -0.2 + else: + penalty = 0.05 + self.state.call_fingerprints.add(fp) + + invalid_tool_penalty = 0.0 + try: + if tool_name == "search_memory": + query = str(args.get("query", "")).strip() + top_k = int(args.get("k", 5)) if str(args.get("k", "")).strip() else 5 + results = self.semantic_memory.search(query=query, k=max(1, top_k)) if query else [] + output = {"results": results, "count": len(results)} + else: + output = self.tools.call(tool_name, args) + except Exception as exc: + output = {"error": str(exc)} + invalid_tool_penalty = -0.25 + self.state.tool_outputs.append({"tool": tool_name, "args": args, "output": output}) + self.semantic_memory.add(f"{tool_name} {args} {output}", {"tool": tool_name}) + relevance_bonus = 0.08 * self._tool_relevance(self.state.task, output) + total = penalty + relevance_bonus + invalid_tool_penalty + self._accumulate_reward_components( + { + "tool_novelty": penalty, + "tool_relevance": relevance_bonus, + "invalid_tool_penalty": invalid_tool_penalty, + } + ) + return total + + def _handle_add_edge(self, payload: dict[str, Any]) -> float: + if self.state is None: + return 0.0 + edge = Edge(payload["src"], payload["rel"], payload["dst"], float(payload.get("confidence", 1.0))) + existing_edges = list(self.memory_graph.edges) + added = self.memory_graph.add_edge(edge) + if not added: + self._accumulate_reward_components({"duplicate_edge_penalty": -0.15}) + return -0.15 + + breakdown = compute_edge_reward( + edge=edge, + task=self.state.task, + existing_edges=existing_edges, + step_count=self.state.step_count, + model=self.reward_model, + graph=self.graph, + difficulty=self.state.difficulty, + ) + self._accumulate_reward_components(breakdown.to_dict()) + return breakdown.total + + def _handle_answer(self, payload: dict[str, Any]) -> float: + if self.state is None: + return 0.0 + proposed = str(payload.get("answer", "")).strip() + self.state.answer = proposed + self.state.done = True + breakdown = compute_answer_reward( + proposed_answer=proposed, + task=self.state.task, + pred_edges=self.memory_graph.edges, + tool_outputs=self.state.tool_outputs, + step_count=self.state.step_count, + model=self.reward_model, + difficulty=self.state.difficulty, + ) + self._accumulate_reward_components(breakdown.to_dict()) + return breakdown.total + + def _tool_relevance(self, task: TaskInstance, output: dict[str, Any]) -> float: + haystack = str(output).lower() + clues = {task.answer.lower()} + for edge in task.supporting_edges: + clues.add(edge.src.lower()) + clues.add(edge.dst.lower()) + clues.add(edge.rel.lower()) + if not clues: + return 0.0 + matches = sum(1 for token in clues if token in haystack) + return matches / len(clues) + + def _accumulate_reward_components(self, values: dict[str, float]) -> None: + if self.state is None: + return + for key, value in values.items(): + self.state.reward_components[key] = self.state.reward_components.get(key, 0.0) + float(value) + + def _observation(self) -> Observation: + if self.state is None: + raise RuntimeError("State is not initialized.") + metadata = dict(self.state.task.metadata or {}) + grader = metadata.get("grader") if isinstance(metadata.get("grader"), dict) else None + task_payload = { + "task_id": self.state.task.task_id, + "task_type": self.state.task.task_type, + "question": self.state.task.question, + "difficulty": self.state.difficulty, + "grader": ( + dict(grader) + if grader is not None + else { + "type": "difficulty_exact_match", + "answer_type": "node_id", + "case_sensitive": True, + "reward_profile": self.state.difficulty, + } + ), + } + if "scenario" in metadata: + task_payload["scenario"] = str(metadata.get("scenario", "")) + return Observation( + tool_outputs=self.state.tool_outputs[-5:], + graph_snapshot=self.memory_graph.to_snapshot(), + action_history=self.state.action_history[-10:], + task=task_payload, + ) + + def _info(self) -> dict[str, Any]: + if self.state is None: + return {} + return { + "step_count": self.state.step_count, + "difficulty": self.state.difficulty, + "task_index": self.state.task_index, + "total_reward": self.state.total_reward, + "tool_calls": self.state.tool_calls, + "redundant_tool_calls": self.state.redundant_tool_calls, + "task_answer": self.state.task.answer, + "agent_answer": self.state.answer, + "graph_f1": compute_graph_f1(self.memory_graph.edges, self.state.task.supporting_edges), + "reward_components": dict(self.state.reward_components), + } diff --git a/src/osint_env/env/openenv_compat.py b/src/osint_env/env/openenv_compat.py new file mode 100644 index 0000000000000000000000000000000000000000..46f2bfd007c092e38e5c636215a0ca17faa9b541 --- /dev/null +++ b/src/osint_env/env/openenv_compat.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +try: + from openenv.env import Env +except ImportError: + class Env: + """Minimal fallback used when openenv is not installed locally.""" + + def __init__( + self, + name: str, + state_space: str, + action_space: list[str], + episode_max_length: int, + ) -> None: + self.name = name + self.state_space = state_space + self.action_space = action_space + self.episode_max_length = episode_max_length + diff --git a/src/osint_env/env/reward.py b/src/osint_env/env/reward.py new file mode 100644 index 0000000000000000000000000000000000000000..7a0b824daddd2ed696bc6af411b727d7a8c950e0 --- /dev/null +++ b/src/osint_env/env/reward.py @@ -0,0 +1,483 @@ +from __future__ import annotations + +import json +import math +import re +from collections import Counter +from dataclasses import asdict, dataclass + +from osint_env.domain.models import CanonicalGraph, Edge, TaskInstance + + +@dataclass(slots=True) +class RewardModel: + relation_idf: dict[str, float] + max_relation_idf: float + hub_penalty: dict[str, float] + max_hub_penalty: float + type_priors: dict[tuple[str, str, str], float] + + +@dataclass(slots=True) +class EdgeRewardBreakdown: + total: float + global_accuracy: float + soft_shaping: float + efficiency: float + diversity: float + relation_informativeness: float + entity_informativeness: float + connectivity_gain: float + + def to_dict(self) -> dict[str, float]: + return asdict(self) + + +@dataclass(slots=True) +class AnswerRewardBreakdown: + total: float + format_reward: float + correctness: float + knowledge_carrier: float + knowledge_indexing: float + connectivity: float + graph_f1: float + efficiency: float + compactness: float + relation_informativeness: float + entity_informativeness: float + repetition_penalty: float + + def to_dict(self) -> dict[str, float]: + return asdict(self) + + +def _normalize_difficulty(value: str) -> str: + token = str(value or "").strip().lower() + if token in {"easy", "e"}: + return "easy" + if token in {"mid", "medium", "m"}: + return "medium" + if token in {"high", "hard", "h"}: + return "hard" + return "hard" + + +def build_reward_model(graph: CanonicalGraph) -> RewardModel: + relation_freq: Counter[str] = Counter(e.rel for e in graph.edges) + total_edges = max(1, len(graph.edges)) + relation_idf = { + rel: math.log((1.0 + total_edges) / (1.0 + freq)) + 1.0 for rel, freq in relation_freq.items() + } + max_relation_idf = max(relation_idf.values()) if relation_idf else 1.0 + + degree: Counter[str] = Counter() + for edge in graph.edges: + degree[edge.src] += 1 + degree[edge.dst] += 1 + hub_penalty = {node_id: math.log(1.0 + deg) for node_id, deg in degree.items()} + max_hub_penalty = max(hub_penalty.values()) if hub_penalty else 1.0 + + type_counts: Counter[tuple[str, str, str]] = Counter() + rel_counts: Counter[str] = Counter() + for edge in graph.edges: + src = graph.nodes.get(edge.src) + dst = graph.nodes.get(edge.dst) + if src is None or dst is None: + continue + key = (str(src.node_type.value), edge.rel, str(dst.node_type.value)) + type_counts[key] += 1 + rel_counts[edge.rel] += 1 + type_priors = { + key: count / max(1, rel_counts[key[1]]) for key, count in type_counts.items() + } + + return RewardModel( + relation_idf=relation_idf, + max_relation_idf=max_relation_idf, + hub_penalty=hub_penalty, + max_hub_penalty=max_hub_penalty, + type_priors=type_priors, + ) + + +def edge_in_truth(edge: Edge, task: TaskInstance) -> bool: + return any(e.src == edge.src and e.rel == edge.rel and e.dst == edge.dst for e in task.supporting_edges) + + +def _cosine(a: Counter[str], b: Counter[str]) -> float: + common = set(a) & set(b) + num = sum(a[t] * b[t] for t in common) + den = math.sqrt(sum(v * v for v in a.values())) * math.sqrt(sum(v * v for v in b.values())) + return (num / den) if den else 0.0 + + +def _edge_signature(edge: Edge) -> Counter[str]: + # Approximate path/edge embedding using relation and endpoint prefixes. + src_prefix = edge.src.split("_", 1)[0] + dst_prefix = edge.dst.split("_", 1)[0] + return Counter({f"rel:{edge.rel}": 2, f"src:{src_prefix}": 1, f"dst:{dst_prefix}": 1}) + + +def _soft_fact_score(edge: Edge, model: RewardModel, graph: CanonicalGraph) -> float: + if any(e.src == edge.src and e.rel == edge.rel and e.dst == edge.dst for e in graph.edges): + return 1.0 + + src = graph.nodes.get(edge.src) + dst = graph.nodes.get(edge.dst) + if src is None or dst is None: + return 0.0 + + type_key = (str(src.node_type.value), edge.rel, str(dst.node_type.value)) + prior = model.type_priors.get(type_key, 0.0) + + # A tiny domain heuristic: alias links are common and worth soft credit even without exact support edge. + alias_bias = 0.2 if (edge.rel == "alias_of" and edge.src.startswith("alias_") and edge.dst.startswith("user_")) else 0.0 + relation_exists = any(e.rel == edge.rel for e in graph.edges) + relation_bonus = 0.1 if relation_exists else 0.0 + return max(0.0, min(1.0, 0.1 + (0.65 * prior) + alias_bias + relation_bonus)) + + +def _normalized_relation_info(rel: str, model: RewardModel) -> float: + idf = model.relation_idf.get(rel, 1.0) + return idf / max(1e-6, model.max_relation_idf) + + +def _normalized_entity_info(src: str, dst: str, model: RewardModel) -> float: + src_h = model.hub_penalty.get(src, 0.0) + dst_h = model.hub_penalty.get(dst, 0.0) + mean_hub = (src_h + dst_h) / 2.0 + # UniRel-style preference for low-degree intermediates: lower hub penalty -> higher informativeness. + return 1.0 - (mean_hub / max(1e-6, model.max_hub_penalty)) + + +def _is_reachable_undirected(edges: list[Edge], src: str, dst: str) -> bool: + if src == dst: + return True + adj: dict[str, set[str]] = {} + for edge in edges: + adj.setdefault(edge.src, set()).add(edge.dst) + adj.setdefault(edge.dst, set()).add(edge.src) + seen = {src} + stack = [src] + while stack: + node = stack.pop() + for nxt in adj.get(node, set()): + if nxt == dst: + return True + if nxt not in seen: + seen.add(nxt) + stack.append(nxt) + return False + + +def _connectivity_gain(edge: Edge, existing_edges: list[Edge]) -> float: + # Reward edges that bridge disconnected regions and penalize already-connected shortcuts. + if edge.src == edge.dst: + return -0.06 + already_connected = _is_reachable_undirected(existing_edges, edge.src, edge.dst) + if already_connected: + return -0.03 + return 0.10 + + +def _sigmoid_temperature(value: float, temperature: float = 2.0) -> float: + scaled = float(value) / max(1e-6, float(temperature)) + if scaled >= 0: + z = math.exp(-scaled) + return 1.0 / (1.0 + z) + z = math.exp(scaled) + return z / (1.0 + z) + + +def compute_edge_reward( + edge: Edge, + task: TaskInstance, + existing_edges: list[Edge], + step_count: int, + model: RewardModel, + graph: CanonicalGraph, + difficulty: str = "hard", +) -> EdgeRewardBreakdown: + in_truth = edge_in_truth(edge, task) + difficulty_level = _normalize_difficulty(difficulty) + + # DeepPath-inspired global accuracy term. + global_accuracy = 0.85 if in_truth else -0.55 + + # D18 reward shaping: R = Rb + (1 - Rb) * f, where f is a soft fact plausibility score. + base_reward = 1.0 if in_truth else 0.0 + shaped = base_reward + ((1.0 - base_reward) * _soft_fact_score(edge, model, graph)) + soft_shaping = 0.30 * (shaped - 0.5) + + # DeepPath-inspired efficiency term: earlier useful edges are better. + efficiency = 0.10 * (1.0 / max(1, step_count)) + + # DeepPath-inspired diversity term: discourage repeated edge patterns. + if not existing_edges: + diversity = 0.08 + else: + new_sig = _edge_signature(edge) + avg_similarity = sum(_cosine(new_sig, _edge_signature(e)) for e in existing_edges) / len(existing_edges) + novelty = 1.0 - avg_similarity + diversity = 0.14 * (novelty - 0.5) + + # UniRel-style informativeness terms. + relation_informativeness = 0.12 * (_normalized_relation_info(edge.rel, model) - 0.5) + entity_informativeness = 0.12 * (_normalized_entity_info(edge.src, edge.dst, model) - 0.5) + + # Additional structural utility shaping for KG construction. + connectivity_gain = _connectivity_gain(edge, existing_edges) + + if difficulty_level == "easy": + global_accuracy = 0.75 if in_truth else -0.45 + soft_shaping = 0.0 + diversity = 0.0 + relation_informativeness = 0.0 + entity_informativeness = 0.0 + connectivity_gain = 0.0 + efficiency = 0.15 * (1.0 / max(1, step_count)) + elif difficulty_level == "medium": + diversity = 0.0 + relation_informativeness = 0.0 + entity_informativeness = 0.0 + + raw_total = ( + global_accuracy + + soft_shaping + + efficiency + + diversity + + relation_informativeness + + entity_informativeness + + connectivity_gain + ) + total = _sigmoid_temperature(raw_total, temperature=2.0) + return EdgeRewardBreakdown( + total=total, + global_accuracy=global_accuracy, + soft_shaping=soft_shaping, + efficiency=efficiency, + diversity=diversity, + relation_informativeness=relation_informativeness, + entity_informativeness=entity_informativeness, + connectivity_gain=connectivity_gain, + ) + + +def _connectivity_ratio(pred_edges: list[Edge], task: TaskInstance) -> float: + nodes = {e.src for e in task.supporting_edges} | {e.dst for e in task.supporting_edges} + if len(nodes) <= 1: + return 1.0 + + adj: dict[str, set[str]] = {} + for edge in pred_edges: + adj.setdefault(edge.src, set()).add(edge.dst) + adj.setdefault(edge.dst, set()).add(edge.src) + + start = next(iter(nodes)) + seen = {start} + stack = [start] + while stack: + cur = stack.pop() + for nxt in adj.get(cur, set()): + if nxt not in seen: + seen.add(nxt) + stack.append(nxt) + return len(seen & nodes) / max(1, len(nodes)) + + +def _knowledge_indexing_recall(task: TaskInstance, tool_outputs: list[dict[str, object]]) -> float: + gold_terms = {task.answer.lower()} + for edge in task.supporting_edges: + gold_terms.add(edge.src.lower()) + gold_terms.add(edge.dst.lower()) + gold_terms.add(edge.rel.lower()) + + serialized = json.dumps(tool_outputs).lower() + covered = sum(1 for term in gold_terms if term and term in serialized) + return covered / max(1, len(gold_terms)) + + +def _knowledge_carrier_reward(pred_edges: list[Edge], task: TaskInstance) -> float: + pred = {(e.src, e.rel, e.dst) for e in pred_edges} + truth = {(e.src, e.rel, e.dst) for e in task.supporting_edges} + deducible = bool(truth & pred) + return 0.4 if deducible else -0.2 + + +def _extract_query_entities(question: str) -> set[str]: + pattern = r"\b(?:alias|user|org|loc|post|thr|thread|event)_[a-zA-Z0-9_]+\b" + return set(re.findall(pattern, question)) + + +def _max_connected_seed_count(pred_edges: list[Edge], seeds: set[str]) -> int: + if not seeds: + return 0 + adj: dict[str, set[str]] = {} + for edge in pred_edges: + adj.setdefault(edge.src, set()).add(edge.dst) + adj.setdefault(edge.dst, set()).add(edge.src) + + best = 1 + for seed in seeds: + seen = {seed} + stack = [seed] + while stack: + cur = stack.pop() + for nxt in adj.get(cur, set()): + if nxt not in seen: + seen.add(nxt) + stack.append(nxt) + connected_seed_count = len(seeds & seen) + best = max(best, connected_seed_count) + return best + + +def _unirel_connectivity_score(pred_edges: list[Edge], seeds: set[str]) -> float: + # UniRel-style discrete connectivity range projected to [-1, 1] for stable weighting. + n = len(seeds) + if n <= 1: + return 0.0 + + connected = _max_connected_seed_count(pred_edges, seeds) + raw = -math.floor(n / 2) + max(0, connected - 1) + lo = -math.floor(n / 2) + hi = math.ceil(n / 2) - 1 + if hi <= lo: + return 0.0 + return ((raw - lo) / (hi - lo)) * 2.0 - 1.0 + + +def _subgraph_relation_informativeness(pred_edges: list[Edge], model: RewardModel | None) -> float: + if not pred_edges or model is None: + return 0.0 + avg = sum(_normalized_relation_info(edge.rel, model) for edge in pred_edges) / len(pred_edges) + return avg - 0.5 + + +def _subgraph_entity_informativeness(pred_edges: list[Edge], model: RewardModel | None) -> float: + if not pred_edges or model is None: + return 0.0 + avg = sum(_normalized_entity_info(edge.src, edge.dst, model) for edge in pred_edges) / len(pred_edges) + return avg - 0.5 + + +def _relation_repetition_ratio(pred_edges: list[Edge]) -> float: + if len(pred_edges) <= 1: + return 0.0 + rels = [edge.rel for edge in pred_edges] + unique = len(set(rels)) + return 1.0 - (unique / len(rels)) + + +def _deducible_answer(proposed_answer: str, task: TaskInstance, pred_edges: list[Edge]) -> bool: + if proposed_answer != task.answer: + return False + truth = {(edge.src, edge.rel, edge.dst) for edge in task.supporting_edges} + pred = {(edge.src, edge.rel, edge.dst) for edge in pred_edges} + if truth & pred: + return True + + seeds = _extract_query_entities(task.question) + if not seeds: + return False + for seed in seeds: + if _is_reachable_undirected(pred_edges, seed, proposed_answer): + return True + return False + + +def compute_answer_reward( + proposed_answer: str, + task: TaskInstance, + pred_edges: list[Edge], + tool_outputs: list[dict[str, object]], + step_count: int, + model: RewardModel | None = None, + difficulty: str = "hard", +) -> AnswerRewardBreakdown: + difficulty_level = _normalize_difficulty(difficulty) + + format_reward = 0.15 if proposed_answer else -0.55 + correctness = 1.15 if proposed_answer == task.answer else -1.0 + + # AutoGraph-R1 style task utility decomposition. + knowledge_carrier = 0.50 if _deducible_answer(proposed_answer, task, pred_edges) else -0.25 + knowledge_indexing = 0.45 * _knowledge_indexing_recall(task, tool_outputs) + + # UniRel-style connectivity over seed entities. + seed_entities = _extract_query_entities(task.question) + seed_entities.add(task.answer) + connectivity = 0.30 * _unirel_connectivity_score(pred_edges, seed_entities) + + graph_f1 = 0.55 * compute_graph_f1(pred_edges, task.supporting_edges) + efficiency = 0.12 * (1.0 / max(1, step_count)) + + extra_edges = max(0, len(pred_edges) - len(task.supporting_edges)) + compactness = -0.05 * extra_edges + + relation_informativeness = 0.12 * _subgraph_relation_informativeness(pred_edges, model) + entity_informativeness = 0.12 * _subgraph_entity_informativeness(pred_edges, model) + + # AutoGraph-R1 repetition control variant used in larger models. + repetition_penalty = -0.10 * _relation_repetition_ratio(pred_edges) + + if difficulty_level == "easy": + knowledge_carrier = 0.0 + knowledge_indexing = 0.25 * _knowledge_indexing_recall(task, tool_outputs) + connectivity = 0.0 + graph_f1 = 0.0 + efficiency = 0.18 * (1.0 / max(1, step_count)) + compactness = 0.0 + relation_informativeness = 0.0 + entity_informativeness = 0.0 + repetition_penalty = 0.0 + elif difficulty_level == "medium": + connectivity = 0.18 * _unirel_connectivity_score(pred_edges, seed_entities) + graph_f1 = 0.35 * compute_graph_f1(pred_edges, task.supporting_edges) + compactness = -0.04 * extra_edges + relation_informativeness = 0.0 + entity_informativeness = 0.0 + repetition_penalty = 0.0 + + raw_total = ( + format_reward + + correctness + + knowledge_carrier + + knowledge_indexing + + connectivity + + graph_f1 + + efficiency + + compactness + + relation_informativeness + + entity_informativeness + + repetition_penalty + ) + total = _sigmoid_temperature(raw_total, temperature=2.0) + return AnswerRewardBreakdown( + total=total, + format_reward=format_reward, + correctness=correctness, + knowledge_carrier=knowledge_carrier, + knowledge_indexing=knowledge_indexing, + connectivity=connectivity, + graph_f1=graph_f1, + efficiency=efficiency, + compactness=compactness, + relation_informativeness=relation_informativeness, + entity_informativeness=entity_informativeness, + repetition_penalty=repetition_penalty, + ) + + +def compute_graph_f1(pred_edges: list[Edge], truth_edges: list[Edge]) -> float: + pred = {(e.src, e.rel, e.dst) for e in pred_edges} + truth = {(e.src, e.rel, e.dst) for e in truth_edges} + if not pred and not truth: + return 1.0 + if not pred or not truth: + return 0.0 + tp = len(pred & truth) + p = tp / len(pred) if pred else 0.0 + r = tp / len(truth) if truth else 0.0 + return (2 * p * r / (p + r)) if (p + r) else 0.0 diff --git a/src/osint_env/env/spawn_reward_hooks.py b/src/osint_env/env/spawn_reward_hooks.py new file mode 100644 index 0000000000000000000000000000000000000000..a272eaa47a98304a6a214edd39d976441706caa4 --- /dev/null +++ b/src/osint_env/env/spawn_reward_hooks.py @@ -0,0 +1,151 @@ +from __future__ import annotations + +import math +from dataclasses import dataclass + + +@dataclass(slots=True) +class PARLRewardBreakdown: + total: float + auxiliary: float + parallel: float + finish: float + latency: float + breadth_bonus: float + depth_penalty: float + + +def critical_steps(main_steps: list[int], parallel_subagent_steps: list[list[int]]) -> int: + """Compute critical-step latency proxy used in Kimi-style PARL shaping. + + For each stage t, we add: + Smain(t) + max_i Ssub,i(t) + where Ssub,i(t) is the i-th sub-agent step count for that stage. + """ + if len(main_steps) != len(parallel_subagent_steps): + raise ValueError("main_steps and parallel_subagent_steps must have the same length") + + total = 0 + for stage_main, stage_sub in zip(main_steps, parallel_subagent_steps): + main = max(0, int(stage_main)) + longest_sub = max((max(0, int(v)) for v in stage_sub), default=0) + total += main + longest_sub + return total + + +def parl_style_spawn_reward( + task_outcome_reward: float, + spawn_count: int, + finished_subtasks: int, + critical_steps: int, + lambda_parallel: float = 0.15, + lambda_finish: float = 0.20, + anneal: float = 1.0, + breadth: int | None = None, + depth: int | None = None, + max_parallel_hint: int | None = None, +) -> float: + """Kimi K2.5 inspired PARL reward utility for future multi-agent branches. + + This helper intentionally does not orchestrate agents. It only exposes the reward shape: + + r_parl = r_perf + a * (lambda_parallel * r_parallel + lambda_finish * r_finish + r_latency) + + where: + - r_parallel encourages non-zero agent spawning (avoids serial collapse) + - r_finish rewards meaningful completion, preventing spawn-only reward hacking + - r_latency favors lower critical-step execution paths + + The optional breadth/depth controls are small shaping terms for future branches where + orchestration state includes tree shape telemetry. + """ + spawn_count = max(0, int(spawn_count)) + finished_subtasks = max(0, int(finished_subtasks)) + critical_steps = max(1, int(critical_steps)) + anneal = max(0.0, min(1.0, anneal)) + lambda_parallel = max(0.0, float(lambda_parallel)) + lambda_finish = max(0.0, float(lambda_finish)) + breadth = max(0, int(breadth or 0)) + depth = max(0, int(depth or 0)) + max_parallel_hint = max(0, int(max_parallel_hint or 0)) + + breakdown = parl_reward_breakdown( + task_outcome_reward=task_outcome_reward, + spawn_count=spawn_count, + finished_subtasks=finished_subtasks, + critical_steps=critical_steps, + lambda_parallel=lambda_parallel, + lambda_finish=lambda_finish, + anneal=anneal, + breadth=breadth, + depth=depth, + max_parallel_hint=max_parallel_hint, + ) + return breakdown.total + + +def parl_reward_breakdown( + task_outcome_reward: float, + spawn_count: int, + finished_subtasks: int, + critical_steps: int, + lambda_parallel: float = 0.15, + lambda_finish: float = 0.20, + anneal: float = 1.0, + breadth: int | None = None, + depth: int | None = None, + max_parallel_hint: int | None = None, +) -> PARLRewardBreakdown: + spawn_count = max(0, int(spawn_count)) + finished_subtasks = max(0, int(finished_subtasks)) + critical_steps = max(1, int(critical_steps)) + anneal = max(0.0, min(1.0, anneal)) + lambda_parallel = max(0.0, float(lambda_parallel)) + lambda_finish = max(0.0, float(lambda_finish)) + breadth = max(0, int(breadth or 0)) + depth = max(0, int(depth or 0)) + max_parallel_hint = max(0, int(max_parallel_hint or 0)) + + if spawn_count == 0: + r_parallel = 0.0 + r_finish = 0.0 + else: + # Saturating incentive for parallelism so reward cannot grow unbounded with spawns. + r_parallel = math.tanh(spawn_count / 4.0) + if max_parallel_hint > 0: + utilization = min(1.0, spawn_count / max_parallel_hint) + r_parallel *= (0.7 + (0.3 * utilization)) + + r_finish = min(1.0, finished_subtasks / spawn_count) + + if breadth > 0: + breadth_bonus = 0.04 * math.tanh(breadth / 6.0) + else: + breadth_bonus = 0.0 + + if depth > 0: + # Mild depth penalty discourages brittle over-decomposition chains. + depth_penalty = -0.03 * math.tanh(max(0, depth - 1) / 4.0) + else: + depth_penalty = 0.0 + + # Optional latency shaping hook using critical steps (higher is worse). + r_latency = 0.05 * (1.0 / critical_steps) + + auxiliary = ( + (lambda_parallel * r_parallel) + + (lambda_finish * r_finish) + + r_latency + + breadth_bonus + + depth_penalty + ) + total = float(task_outcome_reward) + (anneal * auxiliary) + return PARLRewardBreakdown( + total=total, + auxiliary=anneal * auxiliary, + parallel=r_parallel, + finish=r_finish, + latency=r_latency, + breadth_bonus=breadth_bonus, + depth_penalty=depth_penalty, + ) diff --git a/src/osint_env/eval/__init__.py b/src/osint_env/eval/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0041203255773471e8b612ada5ed7a53245dac30 --- /dev/null +++ b/src/osint_env/eval/__init__.py @@ -0,0 +1,2 @@ +"""Evaluation package.""" + diff --git a/src/osint_env/eval/leaderboard.py b/src/osint_env/eval/leaderboard.py new file mode 100644 index 0000000000000000000000000000000000000000..b46a6852c736ca6e332366cb5ce77ab45e9f30e3 --- /dev/null +++ b/src/osint_env/eval/leaderboard.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +import json +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +def _utc_now() -> str: + return datetime.now(tz=timezone.utc).replace(microsecond=0).isoformat() + + +def load_leaderboard(path: str | Path) -> list[dict[str, Any]]: + file_path = Path(path) + if not file_path.exists(): + return [] + with file_path.open("r", encoding="utf-8") as f: + data = json.load(f) + if not isinstance(data, list): + return [] + return data + + +def save_leaderboard(path: str | Path, records: list[dict[str, Any]]) -> None: + file_path = Path(path) + file_path.parent.mkdir(parents=True, exist_ok=True) + with file_path.open("w", encoding="utf-8") as f: + json.dump(records, f, indent=2, sort_keys=True) + + +def _metric_value(record: dict[str, Any], sort_by: str) -> float: + metrics = record.get("metrics", {}) + return float(metrics.get(sort_by, 0.0)) + + +def sorted_leaderboard(records: list[dict[str, Any]], sort_by: str = "leaderboard_score") -> list[dict[str, Any]]: + return sorted(records, key=lambda r: _metric_value(r, sort_by), reverse=True) + + +def append_leaderboard_record( + path: str | Path, + summary: dict[str, Any], + episodes: int, + run_name: str | None = None, + config: dict[str, Any] | None = None, +) -> dict[str, Any]: + records = load_leaderboard(path) + run_id = f"run_{len(records) + 1:04d}" + record = { + "run_id": run_id, + "run_name": run_name or run_id, + "created_at": _utc_now(), + "episodes": int(episodes), + "config": config or {}, + "metrics": summary, + } + records.append(record) + save_leaderboard(path, records) + return record + + +def render_leaderboard_table(records: list[dict[str, Any]], top_k: int = 20, sort_by: str = "leaderboard_score") -> str: + ranked = sorted_leaderboard(records, sort_by=sort_by)[:top_k] + header = "| rank | run | score | success | graph_f1 | retrieval | structural | spawn | reward | tool_eff |\n" + sep = "|---|---|---:|---:|---:|---:|---:|---:|---:|---:|\n" + rows: list[str] = [] + for idx, rec in enumerate(ranked, start=1): + m = rec.get("metrics", {}) + rows.append( + "| {rank} | {run} | {score:.4f} | {succ:.3f} | {f1:.3f} | {retrieval:.3f} | {structural:.3f} | {spawn:.3f} | {reward:.3f} | {tool:.3f} |".format( + rank=idx, + run=rec.get("run_name", rec.get("run_id", "run")), + score=float(m.get("leaderboard_score", 0.0)), + succ=float(m.get("task_success_rate", 0.0)), + f1=float(m.get("avg_graph_f1", 0.0)), + retrieval=float(m.get("retrieval_signal", 0.0)), + structural=float(m.get("structural_signal", 0.0)), + spawn=float(m.get("spawn_signal", 0.0)), + reward=float(m.get("avg_reward", 0.0)), + tool=float(m.get("tool_efficiency", 0.0)), + ) + ) + return header + sep + "\n".join(rows) diff --git a/src/osint_env/eval/metrics.py b/src/osint_env/eval/metrics.py new file mode 100644 index 0000000000000000000000000000000000000000..e32bf182907847d0b69be77d46ecdfc9df6d0722 --- /dev/null +++ b/src/osint_env/eval/metrics.py @@ -0,0 +1,139 @@ +from __future__ import annotations + +import math +from dataclasses import dataclass, field + + +@dataclass(slots=True) +class EvalMetrics: + episodes: int = 0 + success: int = 0 + total_steps: int = 0 + total_tool_calls: int = 0 + total_redundant_tool_calls: int = 0 + total_reward: float = 0.0 + deanonymization_total: int = 0 + deanonymization_success: int = 0 + graph_f1_scores: list[float] = field(default_factory=list) + total_knowledge_carrier: float = 0.0 + total_knowledge_indexing: float = 0.0 + total_connectivity: float = 0.0 + total_format_reward: float = 0.0 + total_relation_informativeness: float = 0.0 + total_entity_informativeness: float = 0.0 + total_diversity: float = 0.0 + total_soft_shaping: float = 0.0 + total_connectivity_gain: float = 0.0 + total_compactness: float = 0.0 + total_spawn_count: int = 0 + total_spawn_finished_subtasks: int = 0 + total_spawn_critical_steps: int = 0 + + @staticmethod + def _sigmoid_temperature(value: float, temperature: float = 2.0) -> float: + scaled = float(value) / max(1e-6, float(temperature)) + if scaled >= 0: + z = math.exp(-scaled) + return 1.0 / (1.0 + z) + z = math.exp(scaled) + return z / (1.0 + z) + + def add(self, info: dict, task_type: str, graph_f1: float) -> None: + self.episodes += 1 + ok = info.get("agent_answer") == info.get("task_answer") + self.success += int(ok) + self.total_steps += int(info.get("step_count", 0)) + self.total_tool_calls += int(info.get("tool_calls", 0)) + self.total_redundant_tool_calls += int(info.get("redundant_tool_calls", 0)) + self.total_reward += float(info.get("total_reward", 0.0)) + self.graph_f1_scores.append(graph_f1) + components = info.get("reward_components", {}) + self.total_knowledge_carrier += float(components.get("knowledge_carrier", 0.0)) + self.total_knowledge_indexing += float(components.get("knowledge_indexing", 0.0)) + self.total_connectivity += float(components.get("connectivity", 0.0)) + self.total_format_reward += float(components.get("format_reward", 0.0)) + self.total_relation_informativeness += float(components.get("relation_informativeness", 0.0)) + self.total_entity_informativeness += float(components.get("entity_informativeness", 0.0)) + self.total_diversity += float(components.get("diversity", 0.0)) + self.total_soft_shaping += float(components.get("soft_shaping", 0.0)) + self.total_connectivity_gain += float(components.get("connectivity_gain", 0.0)) + self.total_compactness += float(components.get("compactness", 0.0)) + self.total_spawn_count += int(info.get("spawn_count", 0)) + self.total_spawn_finished_subtasks += int(info.get("spawn_finished_subtasks", 0)) + self.total_spawn_critical_steps += int(info.get("spawn_critical_steps", 0)) + if task_type == "identity_resolution": + self.deanonymization_total += 1 + self.deanonymization_success += int(ok) + + def summary(self) -> dict: + episodes = max(1, self.episodes) + task_success_rate = self.success / episodes + tool_efficiency = 1.0 - (self.total_redundant_tool_calls / max(1, self.total_tool_calls)) + avg_graph_f1 = sum(self.graph_f1_scores) / max(1, len(self.graph_f1_scores)) + deanonymization_accuracy = self.deanonymization_success / max(1, self.deanonymization_total) + avg_reward_raw = self.total_reward / episodes + avg_reward = self._sigmoid_temperature(avg_reward_raw, temperature=2.0) + avg_knowledge_carrier = self.total_knowledge_carrier / episodes + avg_knowledge_indexing = self.total_knowledge_indexing / episodes + avg_connectivity = self.total_connectivity / episodes + avg_relation_informativeness = self.total_relation_informativeness / episodes + avg_entity_informativeness = self.total_entity_informativeness / episodes + avg_diversity = self.total_diversity / episodes + avg_soft_shaping = self.total_soft_shaping / episodes + avg_connectivity_gain = self.total_connectivity_gain / episodes + avg_compactness = self.total_compactness / episodes + avg_spawn_count = self.total_spawn_count / episodes + spawn_completion = self.total_spawn_finished_subtasks / max(1, self.total_spawn_count) + avg_spawn_critical_steps = self.total_spawn_critical_steps / episodes + spawn_latency_signal = 1.0 / max(1.0, avg_spawn_critical_steps) + spawn_signal = max(0.0, min(1.0, 0.6 * spawn_completion + 0.4 * spawn_latency_signal)) + + reward_norm = avg_reward + retrieval_signal = max(0.0, min(1.0, 0.5 + 0.35 * avg_knowledge_carrier + 0.35 * avg_knowledge_indexing)) + structural_signal = max( + 0.0, + min( + 1.0, + 0.5 + + 0.25 * avg_connectivity + + 0.20 * avg_relation_informativeness + + 0.20 * avg_entity_informativeness + + 0.15 * avg_diversity + + 0.10 * avg_connectivity_gain, + ), + ) + leaderboard_score = ( + 0.28 * task_success_rate + + 0.20 * avg_graph_f1 + + 0.12 * tool_efficiency + + 0.12 * deanonymization_accuracy + + 0.14 * retrieval_signal + + 0.09 * structural_signal + + 0.05 * reward_norm + + 0.04 * spawn_signal + ) + return { + "task_success_rate": task_success_rate, + "tool_efficiency": tool_efficiency, + "avg_graph_f1": avg_graph_f1, + "avg_steps_to_solution": self.total_steps / episodes, + "deanonymization_accuracy": deanonymization_accuracy, + "avg_reward": avg_reward, + "avg_knowledge_carrier_reward": avg_knowledge_carrier, + "avg_knowledge_indexing_reward": avg_knowledge_indexing, + "avg_connectivity_reward": avg_connectivity, + "avg_format_reward": self.total_format_reward / episodes, + "avg_relation_informativeness_reward": avg_relation_informativeness, + "avg_entity_informativeness_reward": avg_entity_informativeness, + "avg_diversity_reward": avg_diversity, + "avg_soft_shaping_reward": avg_soft_shaping, + "avg_connectivity_gain_reward": avg_connectivity_gain, + "avg_compactness_reward": avg_compactness, + "avg_spawn_count": avg_spawn_count, + "spawn_completion_rate": spawn_completion, + "avg_spawn_critical_steps": avg_spawn_critical_steps, + "spawn_signal": spawn_signal, + "retrieval_signal": retrieval_signal, + "structural_signal": structural_signal, + "leaderboard_score": leaderboard_score, + } diff --git a/src/osint_env/eval/runner.py b/src/osint_env/eval/runner.py new file mode 100644 index 0000000000000000000000000000000000000000..c6c074c23cb741f629e3621d9fc9cdc9f319d6cb --- /dev/null +++ b/src/osint_env/eval/runner.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +from osint_env.agents.single_agent import SingleAgentRunner +from osint_env.agents.swarm_agent import SwarmAgentRunner +from osint_env.env.environment import OSINTEnvironment +from osint_env.env.reward import compute_graph_f1 +from osint_env.eval.metrics import EvalMetrics +from osint_env.llm.interface import LLMClient + + +def run_evaluation( + env: OSINTEnvironment, + episodes: int = 20, + return_details: bool = False, + llm: LLMClient | None = None, +) -> dict: + metrics = EvalMetrics() + if env.config.swarm.enabled: + runner = SwarmAgentRunner(env=env, llm=llm) + else: + runner = SingleAgentRunner(env=env, llm=llm) + episode_rows: list[dict] = [] + for _ in range(episodes): + info = runner.run_episode() + task_type = env.state.task.task_type if env.state else "unknown" + task_id = env.state.task.task_id if env.state else "unknown" + truth = env.state.task.supporting_edges if env.state else [] + pred = env.memory_graph.edges if env.state else [] + graph_f1 = compute_graph_f1(pred, truth) + metrics.add(info, task_type=task_type, graph_f1=graph_f1) + episode_rows.append( + { + "task_id": task_id, + "task_type": task_type, + "question": env.state.task.question if env.state else "", + "task_answer": str(info.get("task_answer", "")), + "agent_answer": str(info.get("agent_answer", "")) if info.get("agent_answer") is not None else "", + "graph_f1": graph_f1, + "reward": float(info.get("total_reward", 0.0)), + "steps": int(info.get("step_count", 0)), + "tool_calls": int(info.get("tool_calls", 0)), + "success": int(info.get("agent_answer") == info.get("task_answer")), + "reward_components": dict(info.get("reward_components", {})), + "spawn_count": int(info.get("spawn_count", 0)), + "spawn_critical_steps": int(info.get("spawn_critical_steps", 0)), + "pred_edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in pred + ], + "truth_edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in truth + ], + } + ) + summary = metrics.summary() + if return_details: + return {"summary": summary, "episodes": episode_rows} + return summary diff --git a/src/osint_env/llm/__init__.py b/src/osint_env/llm/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2980377f05c73f88682c51811fb9871602f9a638 --- /dev/null +++ b/src/osint_env/llm/__init__.py @@ -0,0 +1,20 @@ +"""LLM interface package.""" + +from osint_env.llm.interface import ( + LLMClient, + LLMResponse, + OllamaLLMClient, + OpenAILLMClient, + RuleBasedMockLLM, + build_llm_client, +) + +__all__ = [ + "LLMClient", + "LLMResponse", + "RuleBasedMockLLM", + "OllamaLLMClient", + "OpenAILLMClient", + "build_llm_client", +] + diff --git a/src/osint_env/llm/interface.py b/src/osint_env/llm/interface.py new file mode 100644 index 0000000000000000000000000000000000000000..e223e1a3873d094b62789c4c45e7f0956c419d33 --- /dev/null +++ b/src/osint_env/llm/interface.py @@ -0,0 +1,167 @@ +from __future__ import annotations + +import json +import os +from dataclasses import dataclass +from typing import Any, Protocol + +import requests +from requests import RequestException + +from osint_env.domain.models import LLMConfig + + +@dataclass(slots=True) +class LLMResponse: + content: str + tool_calls: list[dict[str, Any]] + + +class LLMClient(Protocol): + def generate(self, messages: list[dict[str, Any]], tools: list[dict[str, Any]]) -> LLMResponse: + ... + + +class RuleBasedMockLLM: + """Deterministic fallback for local testing without model dependencies.""" + + def generate(self, messages: list[dict[str, Any]], tools: list[dict[str, Any]]) -> LLMResponse: + question = "" + for m in reversed(messages): + if m.get("role") == "system" and "question" in m.get("content", ""): + question = m["content"] + break + if "alias" in question: + return LLMResponse( + content="Need alias lookup.", + tool_calls=[{"tool_name": "search_posts", "args": {"query": "Update"}}, {"tool_name": "get_profile", "args": {"user_id": "user_0"}}], + ) + return LLMResponse(content="Need profile lookup.", tool_calls=[{"tool_name": "search_people", "args": {"org": "Apex"}}]) + + +class OllamaLLMClient: + def __init__(self, model: str, base_url: str = "http://127.0.0.1:11434", temperature: float = 0.1, timeout_seconds: int = 240): + self.model = model + self.base_url = base_url.rstrip("/") + self.temperature = float(temperature) + self.timeout_seconds = int(timeout_seconds) + + @staticmethod + def _extract_tool_calls(content: str) -> list[dict[str, Any]]: + text = str(content or "").strip() + if not text: + return [] + left = text.find("{") + right = text.rfind("}") + if left >= 0 and right > left: + snippet = text[left : right + 1] + try: + parsed = json.loads(snippet) + except json.JSONDecodeError: + parsed = None + if isinstance(parsed, dict) and isinstance(parsed.get("tool_calls"), list): + out: list[dict[str, Any]] = [] + for item in parsed["tool_calls"]: + if isinstance(item, dict) and "tool_name" in item and isinstance(item.get("args", {}), dict): + out.append({"tool_name": str(item["tool_name"]), "args": dict(item.get("args", {}))}) + return out + return [] + + def generate(self, messages: list[dict[str, Any]], tools: list[dict[str, Any]]) -> LLMResponse: + payload = { + "model": self.model, + "messages": messages, + "stream": False, + "options": { + "temperature": self.temperature, + }, + } + if tools: + payload["tools"] = tools + try: + response = requests.post( + f"{self.base_url}/api/chat", + json=payload, + timeout=self.timeout_seconds, + ) + response.raise_for_status() + data = response.json() + content = str((data.get("message") or {}).get("content", "")) + tool_calls = self._extract_tool_calls(content) + return LLMResponse(content=content, tool_calls=tool_calls) + except (RequestException, ValueError): + # Keep episode execution resilient when local model calls are transiently slow/unavailable. + return LLMResponse(content="", tool_calls=[]) + + +class OpenAILLMClient: + def __init__( + self, + model: str, + api_key: str, + base_url: str = "https://api.openai.com/v1", + temperature: float = 0.1, + max_tokens: int = 256, + timeout_seconds: int = 240, + ): + from openai import OpenAI + + self.model = model + self.temperature = float(temperature) + self.max_tokens = int(max_tokens) + self.client = OpenAI(api_key=api_key, base_url=base_url, timeout=timeout_seconds) + + def generate(self, messages: list[dict[str, Any]], tools: list[dict[str, Any]]) -> LLMResponse: + kwargs: dict[str, Any] = { + "model": self.model, + "messages": messages, + "temperature": self.temperature, + "max_tokens": self.max_tokens, + } + if tools: + kwargs["tools"] = tools + try: + completion = self.client.chat.completions.create(**kwargs) + message = completion.choices[0].message + content = message.content if isinstance(message.content, str) else "" + + tool_calls: list[dict[str, Any]] = [] + for tc in message.tool_calls or []: + try: + args = json.loads(tc.function.arguments or "{}") + except json.JSONDecodeError: + args = {} + tool_calls.append({"tool_name": tc.function.name, "args": args if isinstance(args, dict) else {}}) + return LLMResponse(content=content, tool_calls=tool_calls) + except Exception: + return LLMResponse(content="", tool_calls=[]) + + +def build_llm_client(config: LLMConfig | None = None) -> LLMClient: + cfg = config or LLMConfig() + provider = str(cfg.provider).strip().lower() + if provider in {"", "mock", "rule", "rule_based"}: + return RuleBasedMockLLM() + if provider == "ollama": + return OllamaLLMClient( + model=cfg.model, + base_url=cfg.ollama_base_url, + temperature=cfg.temperature, + timeout_seconds=cfg.timeout_seconds, + ) + if provider == "openai": + api_key = cfg.openai_api_key or os.getenv(cfg.openai_api_key_env, "") + if not api_key: + raise ValueError( + "OpenAI provider selected but API key is missing. " + f"Set {cfg.openai_api_key_env} or populate openai_api_key in config." + ) + return OpenAILLMClient( + model=cfg.model, + api_key=api_key, + base_url=cfg.openai_base_url, + temperature=cfg.temperature, + max_tokens=cfg.max_tokens, + timeout_seconds=cfg.timeout_seconds, + ) + raise ValueError(f"Unsupported llm provider: {cfg.provider}") diff --git a/src/osint_env/memory/__init__.py b/src/osint_env/memory/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..519788dfb2a8240a0d4f54981c2c65c6e71d0df8 --- /dev/null +++ b/src/osint_env/memory/__init__.py @@ -0,0 +1,2 @@ +"""Structured memory package.""" + diff --git a/src/osint_env/memory/store.py b/src/osint_env/memory/store.py new file mode 100644 index 0000000000000000000000000000000000000000..780f46087c8b7ea165b777a0fc61a4c8b7f613cb --- /dev/null +++ b/src/osint_env/memory/store.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +import math +import re +from collections import Counter +from dataclasses import dataclass, field +from typing import Any + +from osint_env.domain.models import Edge + + +def _tokenize(text: str) -> list[str]: + return [t for t in re.findall(r"[a-zA-Z0-9_]+", text.lower()) if t] + + +@dataclass(slots=True) +class MemoryGraph: + nodes: dict[str, dict[str, Any]] = field(default_factory=dict) + edges: list[Edge] = field(default_factory=list) + + def add_edge(self, edge: Edge) -> bool: + key = (edge.src, edge.rel, edge.dst) + if any((e.src, e.rel, e.dst) == key for e in self.edges): + return False + self.edges.append(edge) + return True + + def to_snapshot(self) -> dict[str, Any]: + return { + "nodes_count": len(self.nodes), + "edges_count": len(self.edges), + "edges": [{"src": e.src, "rel": e.rel, "dst": e.dst, "confidence": e.confidence} for e in self.edges], + } + + +@dataclass(slots=True) +class SemanticMemory: + docs: list[dict[str, Any]] = field(default_factory=list) + + def add(self, text: str, metadata: dict[str, Any]) -> None: + self.docs.append({"text": text, "metadata": metadata, "tokens": Counter(_tokenize(text))}) + + def search(self, query: str, k: int = 5) -> list[dict[str, Any]]: + q = Counter(_tokenize(query)) + scored: list[tuple[float, dict[str, Any]]] = [] + for doc in self.docs: + score = self._cosine(q, doc["tokens"]) + if score > 0: + scored.append((score, doc)) + scored.sort(key=lambda x: x[0], reverse=True) + return [{"score": s, "text": d["text"], "metadata": d["metadata"]} for s, d in scored[:k]] + + @staticmethod + def _cosine(a: Counter, b: Counter) -> float: + common = set(a) & set(b) + num = sum(a[t] * b[t] for t in common) + den = math.sqrt(sum(v * v for v in a.values())) * math.sqrt(sum(v * v for v in b.values())) + return (num / den) if den else 0.0 diff --git a/src/osint_env/platforms/__init__.py b/src/osint_env/platforms/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..cc16c8fc0fb7ab3780746c358de603a3ff5eb1d6 --- /dev/null +++ b/src/osint_env/platforms/__init__.py @@ -0,0 +1,2 @@ +"""Platform and tool adapters.""" + diff --git a/src/osint_env/platforms/tools.py b/src/osint_env/platforms/tools.py new file mode 100644 index 0000000000000000000000000000000000000000..d547fe4383f0019bde331186486fc22549cd6651 --- /dev/null +++ b/src/osint_env/platforms/tools.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +from collections import defaultdict +from typing import Any + +from osint_env.data.generator import PlatformViews + + +class ToolRegistry: + def __init__(self, views: PlatformViews): + self.views = views + self.alias_lookup = dict(getattr(views, "alias_lookup", {})) + self._index() + + @staticmethod + def _normalize_lookup_token(value: str) -> str: + token = str(value or "").strip().lower() + for prefix in ("org_", "loc_", "event_", "post_", "thr_", "thread_", "alias_", "user_"): + if token.startswith(prefix): + token = token[len(prefix) :] + break + return token.replace("_", " ") + + def _resolve_user_ids(self, user_id: str) -> list[str]: + user_id = str(user_id or "").strip() + if not user_id: + return [] + resolved = [user_id] + canonical = self.alias_lookup.get(user_id) + if canonical and canonical not in resolved: + resolved.append(canonical) + for alias_id, owner in self.alias_lookup.items(): + if owner == user_id and alias_id not in resolved: + resolved.append(alias_id) + return resolved + + def _index(self) -> None: + self.posts_by_user: dict[str, list[dict[str, Any]]] = defaultdict(list) + self.mentions_by_user: dict[str, list[dict[str, Any]]] = defaultdict(list) + self.posts_by_id = {post["post_id"]: post for post in self.views.microblog_posts} + for post in self.views.microblog_posts: + self.posts_by_user[post["user_id"]].append(post) + canonical_user = post.get("canonical_user") + if canonical_user: + self.posts_by_user[canonical_user].append(post) + for m in post.get("mentions", []): + self.mentions_by_user[m].append(post) + + self.threads_by_id = {t["thread_id"]: t for t in self.views.forum_threads} + self.activity_by_user: dict[str, list[dict[str, Any]]] = defaultdict(list) + for thread in self.views.forum_threads: + self.activity_by_user[thread["author_id"]].append({"kind": "thread", "thread_id": thread["thread_id"]}) + for c in thread.get("comments", []): + self.activity_by_user[c["user_id"]].append({"kind": "comment", "thread_id": thread["thread_id"]}) + + self.profiles_by_user = {p["user_id"]: p for p in self.views.profiles} + + def call(self, tool_name: str, args: dict[str, Any]) -> dict[str, Any]: + fn = getattr(self, tool_name, None) + if not fn: + raise ValueError(f"Unknown tool: {tool_name}") + return fn(**args) + + def search_posts(self, query: str, time_range: tuple[int, int] | None = None) -> dict[str, Any]: + start, end = time_range or (0, 10**9) + needle = str(query or "").lower() + results = [ + p + for p in self.views.microblog_posts + if start <= p["timestamp"] <= end + and ( + needle in p["text"].lower() + or needle in str(p.get("post_id", "")).lower() + or needle in str(p.get("user_id", "")).lower() + or needle in str(p.get("canonical_user", "")).lower() + or any(needle in str(ref).lower() for ref in p.get("references", [])) + or any(needle in str(ref).lower() for ref in p.get("reference_names", [])) + ) + ] + return {"results": results[:20], "count": len(results)} + + def get_post(self, post_id: str) -> dict[str, Any]: + post = self.posts_by_id.get(post_id) + return {"result": post, "found": post is not None} + + def get_user_posts(self, user_id: str) -> dict[str, Any]: + results: list[dict[str, Any]] = [] + seen_post_ids: set[str] = set() + for resolved_id in self._resolve_user_ids(user_id): + for post in self.posts_by_user.get(resolved_id, []): + post_id = str(post.get("post_id", "")) + if post_id in seen_post_ids: + continue + seen_post_ids.add(post_id) + results.append(post) + return {"results": results, "count": len(results)} + + def get_mentions(self, user_id: str) -> dict[str, Any]: + results: list[dict[str, Any]] = [] + seen_post_ids: set[str] = set() + for resolved_id in self._resolve_user_ids(user_id): + for post in self.mentions_by_user.get(resolved_id, []): + post_id = str(post.get("post_id", "")) + if post_id in seen_post_ids: + continue + seen_post_ids.add(post_id) + results.append(post) + return {"results": results, "count": len(results)} + + def search_threads(self, topic: str) -> dict[str, Any]: + needle = str(topic or "").strip().lower() + results = [ + t + for t in self.views.forum_threads + if t["topic"] == topic + or needle in str(t.get("thread_id", "")).lower() + or needle in str(t.get("title", "")).lower() + ] + return {"results": results[:20], "count": len(results)} + + def get_thread(self, thread_id: str) -> dict[str, Any]: + thread = self.threads_by_id.get(thread_id) + return {"result": thread, "found": thread is not None} + + def get_user_activity(self, user_id: str) -> dict[str, Any]: + acts: list[dict[str, Any]] = [] + seen = set() + for resolved_id in self._resolve_user_ids(user_id): + for activity in self.activity_by_user.get(resolved_id, []): + key = (activity.get("kind"), activity.get("thread_id")) + if key in seen: + continue + seen.add(key) + acts.append(activity) + return {"results": acts, "count": len(acts)} + + def get_profile(self, user_id: str) -> dict[str, Any]: + resolved_ids = self._resolve_user_ids(user_id) + profile = next((self.profiles_by_user.get(candidate) for candidate in resolved_ids if self.profiles_by_user.get(candidate)), None) + return {"result": profile, "found": profile is not None} + + def search_people(self, name: str | None = None, org: str | None = None) -> dict[str, Any]: + results = self.views.profiles + if name: + name_query = str(name).lower() + results = [ + p + for p in results + if name_query in p["name"].lower() + or name_query in p["user_id"].lower() + or any(name_query in alias.lower() for alias in p.get("alias_ids", [])) + ] + if org: + org_query = str(org).lower() + normalized_org = self._normalize_lookup_token(org_query) + results = [ + p + for p in results + if org_query in p["org"].lower() + or org_query in str(p.get("org_id", "")).lower() + or (normalized_org and normalized_org in p["org"].lower()) + ] + return {"results": results[:20], "count": len(results)} + + def get_connections(self, user_id: str) -> dict[str, Any]: + resolved_ids = self._resolve_user_ids(user_id) + profile = next((self.profiles_by_user.get(candidate) for candidate in resolved_ids if self.profiles_by_user.get(candidate)), None) + return {"results": profile["connections"] if profile else [], "count": len(profile["connections"]) if profile else 0} diff --git a/src/osint_env/server_entry.py b/src/osint_env/server_entry.py new file mode 100644 index 0000000000000000000000000000000000000000..928272ed0d02181bac278da9734db57cd41cf85c --- /dev/null +++ b/src/osint_env/server_entry.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +import os + +import uvicorn + + +def main() -> None: + port = int(os.getenv("PORT", "7860")) + uvicorn.run("server:app", host="0.0.0.0", port=port) diff --git a/src/osint_env/training/__init__.py b/src/osint_env/training/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..841f5d5b96e4b462a01b191f5748d390683bbbbf --- /dev/null +++ b/src/osint_env/training/__init__.py @@ -0,0 +1,27 @@ +"""Adversarial self-play training helpers.""" + +from osint_env.training.config import ( + GeneratorRewardWeights, + KimiGRPOPhaseConfig, + LoraTuningConfig, + SelfPlayTrainingConfig, + SwarmV2Config, + SwarmV2SharedContextConfig, + SwarmV2SwarmConfig, + SwarmV2ValidationConfig, + load_self_play_config, +) +from osint_env.training.self_play import run_adversarial_self_play + +__all__ = [ + "GeneratorRewardWeights", + "KimiGRPOPhaseConfig", + "LoraTuningConfig", + "SelfPlayTrainingConfig", + "SwarmV2Config", + "SwarmV2SharedContextConfig", + "SwarmV2SwarmConfig", + "SwarmV2ValidationConfig", + "load_self_play_config", + "run_adversarial_self_play", +] diff --git a/src/osint_env/training/config.py b/src/osint_env/training/config.py new file mode 100644 index 0000000000000000000000000000000000000000..eb8a56db787d98dec615b8835bc81b8084bf4099 --- /dev/null +++ b/src/osint_env/training/config.py @@ -0,0 +1,430 @@ +from __future__ import annotations + +import json +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + + +@dataclass(slots=True) +class KimiGRPOPhaseConfig: + """Configuration for one GRPO phase in the alternating self-play loop.""" + + model_name_or_path: str = "Qwen/Qwen2.5-0.5B-Instruct" + learning_rate: float = 1e-6 + max_steps: int = 64 + per_device_train_batch_size: int = 2 + gradient_accumulation_steps: int = 4 + num_generations: int = 4 + max_completion_length: int = 256 + temperature: float = 1.0 + top_p: float = 1.0 + repetition_penalty: float = 1.0 + beta: float = 0.01 + epsilon: float = 0.2 + num_iterations: int = 1 + loss_type: str = "dapo" + scale_rewards: str = "none" + logging_steps: int = 10 + save_steps: int = 50 + output_subdir: str = "phase" + use_vllm: bool = False + vllm_mode: str = "colocate" + + +@dataclass(slots=True) +class GeneratorRewardWeights: + """Weighted components for adversarial task-generator reward.""" + + validity: float = 0.35 + hardness: float = 0.45 + diversity: float = 0.10 + consistency: float = 0.10 + + +@dataclass(slots=True) +class LoraTuningConfig: + """LoRA hyperparameters for parameter-efficient GRPO updates.""" + + r: int = 16 + alpha: int = 32 + dropout: float = 0.05 + target_modules: list[str] = field(default_factory=lambda: ["q_proj", "k_proj", "v_proj", "o_proj"]) + bias: str = "none" + task_type: str = "CAUSAL_LM" + + +@dataclass(slots=True) +class SwarmV2SwarmConfig: + """Config for one orchestrated swarm role inside the swarm_v2 pipeline.""" + + shared_context: bool = True + max_agents: int = 4 + max_breadth: int = 3 + max_depth: int = 2 + planner_rounds: int = 2 + tools_per_agent: int = 2 + + +@dataclass(slots=True) +class SwarmV2ValidationConfig: + """Validation and replay limits for swarm_v2 task generation.""" + + max_support_edges: int = 8 + max_path_hops: int = 4 + max_context_nodes: int = 14 + max_context_edges: int = 8 + duplicate_similarity_threshold: float = 0.8 + + +@dataclass(slots=True) +class SwarmV2SharedContextConfig: + """Shared context budgets used by both generator and answerer swarms.""" + + shared_by_default: bool = True + max_nodes: int = 14 + max_edges: int = 8 + target_pressure: float = 0.85 + + +@dataclass(slots=True) +class SwarmV2Config: + """Config block for the config-gated Swarm Self-Play v2 pipeline.""" + + generator_swarm: SwarmV2SwarmConfig = field(default_factory=SwarmV2SwarmConfig) + answerer_swarm: SwarmV2SwarmConfig = field( + default_factory=lambda: SwarmV2SwarmConfig( + shared_context=True, + max_agents=3, + max_breadth=2, + max_depth=2, + planner_rounds=2, + tools_per_agent=2, + ) + ) + validation: SwarmV2ValidationConfig = field(default_factory=SwarmV2ValidationConfig) + shared_context: SwarmV2SharedContextConfig = field(default_factory=SwarmV2SharedContextConfig) + + +@dataclass(slots=True) +class SelfPlayTrainingConfig: + """Top-level adversarial self-play training configuration.""" + + rounds: int = 3 + output_dir: str = "artifacts/self_play" + dry_run: bool = True + wandb_enabled: bool = False + wandb_project: str = "osint-self-play" + wandb_entity: str = "" + wandb_run_name_prefix: str = "self-play" + canonical_graph_mode: str = "generate" + pipeline_mode: str = "legacy" + model_topology: str = "dual" + phase_schedule: str = "generator_answerer" + tuning_mode: str = "full" + shared_model_name_or_path: str = "" + seed_tasks_per_round: int = 16 + generated_tasks_per_round: int = 24 + generator_prompts_per_round: int = 24 + max_graph_context_nodes: int = 100 + max_graph_context_edges: int = 100 + max_support_edges: int = 8 + answerer_judge_max_new_tokens: int = 48 + generator_reward_weights: GeneratorRewardWeights = field(default_factory=GeneratorRewardWeights) + lora: LoraTuningConfig = field(default_factory=LoraTuningConfig) + swarm_v2: SwarmV2Config = field(default_factory=SwarmV2Config) + generator_phase: KimiGRPOPhaseConfig = field( + default_factory=lambda: KimiGRPOPhaseConfig(output_subdir="generator") + ) + answerer_phase: KimiGRPOPhaseConfig = field( + default_factory=lambda: KimiGRPOPhaseConfig(output_subdir="answerer") + ) + + +def _as_dict(value: Any) -> dict[str, Any]: + return value if isinstance(value, dict) else {} + + +def _parse_int(value: Any, default: int, floor: int | None = None) -> int: + try: + out = int(value) + except (TypeError, ValueError): + out = default + if floor is not None: + out = max(floor, out) + return out + + +def _parse_float(value: Any, default: float) -> float: + try: + return float(value) + except (TypeError, ValueError): + return default + + +def _parse_bool(value: Any, default: bool) -> bool: + if isinstance(value, bool): + return value + if isinstance(value, str): + token = value.strip().lower() + if token in {"1", "true", "yes", "y", "on"}: + return True + if token in {"0", "false", "no", "n", "off"}: + return False + return default + + +def _parse_str_choice(value: Any, default: str, allowed: set[str]) -> str: + token = str(value).strip().lower() + if token in allowed: + return token + return default + + +def _parse_str_list(value: Any, fallback: list[str]) -> list[str]: + if isinstance(value, list): + out = [str(item).strip() for item in value if str(item).strip()] + return out or list(fallback) + if isinstance(value, str): + out = [item.strip() for item in value.split(",") if item.strip()] + return out or list(fallback) + return list(fallback) + + +def _parse_phase(data: dict[str, Any], fallback: KimiGRPOPhaseConfig) -> KimiGRPOPhaseConfig: + return KimiGRPOPhaseConfig( + model_name_or_path=str(data.get("model_name_or_path", fallback.model_name_or_path)).strip() + or fallback.model_name_or_path, + learning_rate=_parse_float(data.get("learning_rate"), fallback.learning_rate), + max_steps=_parse_int(data.get("max_steps"), fallback.max_steps, floor=1), + per_device_train_batch_size=_parse_int( + data.get("per_device_train_batch_size"), + fallback.per_device_train_batch_size, + floor=1, + ), + gradient_accumulation_steps=_parse_int( + data.get("gradient_accumulation_steps"), + fallback.gradient_accumulation_steps, + floor=1, + ), + num_generations=_parse_int(data.get("num_generations"), fallback.num_generations, floor=1), + max_completion_length=_parse_int( + data.get("max_completion_length"), + fallback.max_completion_length, + floor=1, + ), + temperature=_parse_float(data.get("temperature"), fallback.temperature), + top_p=_parse_float(data.get("top_p"), fallback.top_p), + repetition_penalty=_parse_float(data.get("repetition_penalty"), fallback.repetition_penalty), + beta=_parse_float(data.get("beta"), fallback.beta), + epsilon=_parse_float(data.get("epsilon"), fallback.epsilon), + num_iterations=_parse_int(data.get("num_iterations"), fallback.num_iterations, floor=1), + loss_type=str(data.get("loss_type", fallback.loss_type)).strip() or fallback.loss_type, + scale_rewards=str(data.get("scale_rewards", fallback.scale_rewards)).strip() or fallback.scale_rewards, + logging_steps=_parse_int(data.get("logging_steps"), fallback.logging_steps, floor=1), + save_steps=_parse_int(data.get("save_steps"), fallback.save_steps, floor=1), + output_subdir=str(data.get("output_subdir", fallback.output_subdir)).strip() or fallback.output_subdir, + use_vllm=_parse_bool(data.get("use_vllm"), fallback.use_vllm), + vllm_mode=str(data.get("vllm_mode", fallback.vllm_mode)).strip() or fallback.vllm_mode, + ) + + +def _parse_generator_weights(data: dict[str, Any]) -> GeneratorRewardWeights: + return GeneratorRewardWeights( + validity=_parse_float(data.get("validity"), 0.35), + hardness=_parse_float(data.get("hardness"), 0.45), + diversity=_parse_float(data.get("diversity"), 0.10), + consistency=_parse_float(data.get("consistency"), 0.10), + ) + + +def _parse_lora_config(data: dict[str, Any], fallback: LoraTuningConfig) -> LoraTuningConfig: + return LoraTuningConfig( + r=_parse_int(data.get("r"), fallback.r, floor=1), + alpha=_parse_int(data.get("alpha"), fallback.alpha, floor=1), + dropout=_parse_float(data.get("dropout"), fallback.dropout), + target_modules=_parse_str_list(data.get("target_modules"), fallback.target_modules), + bias=str(data.get("bias", fallback.bias)).strip() or fallback.bias, + task_type=str(data.get("task_type", fallback.task_type)).strip() or fallback.task_type, + ) + + +def _parse_swarm_v2_swarm_config( + data: dict[str, Any], + fallback: SwarmV2SwarmConfig, +) -> SwarmV2SwarmConfig: + return SwarmV2SwarmConfig( + shared_context=_parse_bool(data.get("shared_context"), fallback.shared_context), + max_agents=_parse_int(data.get("max_agents"), fallback.max_agents, floor=1), + max_breadth=_parse_int(data.get("max_breadth"), fallback.max_breadth, floor=1), + max_depth=_parse_int(data.get("max_depth"), fallback.max_depth, floor=1), + planner_rounds=_parse_int(data.get("planner_rounds"), fallback.planner_rounds, floor=1), + tools_per_agent=_parse_int(data.get("tools_per_agent"), fallback.tools_per_agent, floor=1), + ) + + +def _parse_swarm_v2_validation_config( + data: dict[str, Any], + fallback: SwarmV2ValidationConfig, + legacy_max_support_edges: int, +) -> SwarmV2ValidationConfig: + default_max_support_edges = ( + _parse_int(data.get("max_support_edges"), legacy_max_support_edges, floor=1) + if "max_support_edges" not in data + else _parse_int(data.get("max_support_edges"), fallback.max_support_edges, floor=1) + ) + return SwarmV2ValidationConfig( + max_support_edges=default_max_support_edges, + max_path_hops=_parse_int(data.get("max_path_hops"), fallback.max_path_hops, floor=1), + max_context_nodes=_parse_int(data.get("max_context_nodes"), fallback.max_context_nodes, floor=1), + max_context_edges=_parse_int(data.get("max_context_edges"), fallback.max_context_edges, floor=1), + duplicate_similarity_threshold=max( + 0.0, + min( + 1.0, + _parse_float( + data.get("duplicate_similarity_threshold"), + fallback.duplicate_similarity_threshold, + ), + ), + ), + ) + + +def _parse_swarm_v2_shared_context_config( + data: dict[str, Any], + fallback: SwarmV2SharedContextConfig, +) -> SwarmV2SharedContextConfig: + return SwarmV2SharedContextConfig( + shared_by_default=_parse_bool(data.get("shared_by_default"), fallback.shared_by_default), + max_nodes=_parse_int(data.get("max_nodes"), fallback.max_nodes, floor=1), + max_edges=_parse_int(data.get("max_edges"), fallback.max_edges, floor=1), + target_pressure=max(0.0, min(1.0, _parse_float(data.get("target_pressure"), fallback.target_pressure))), + ) + + +def _parse_swarm_v2_config( + data: dict[str, Any], + fallback: SwarmV2Config, + legacy_max_support_edges: int, +) -> SwarmV2Config: + return SwarmV2Config( + generator_swarm=_parse_swarm_v2_swarm_config( + _as_dict(data.get("generator_swarm")), + fallback.generator_swarm, + ), + answerer_swarm=_parse_swarm_v2_swarm_config( + _as_dict(data.get("answerer_swarm")), + fallback.answerer_swarm, + ), + validation=_parse_swarm_v2_validation_config( + _as_dict(data.get("validation")), + fallback.validation, + legacy_max_support_edges=legacy_max_support_edges, + ), + shared_context=_parse_swarm_v2_shared_context_config( + _as_dict(data.get("shared_context")), + fallback.shared_context, + ), + ) + + +def load_self_play_config(path: str | Path | None) -> SelfPlayTrainingConfig: + if not path: + return SelfPlayTrainingConfig() + + file_path = Path(path) + if not file_path.exists(): + return SelfPlayTrainingConfig() + + payload = json.loads(file_path.read_text(encoding="utf-8")) + if not isinstance(payload, dict): + raise ValueError("Self-play config file must contain a JSON object.") + + defaults = SelfPlayTrainingConfig() + generator_phase = _parse_phase(_as_dict(payload.get("generator_phase")), defaults.generator_phase) + answerer_phase = _parse_phase(_as_dict(payload.get("answerer_phase")), defaults.answerer_phase) + lora_cfg = _parse_lora_config(_as_dict(payload.get("lora")), defaults.lora) + legacy_max_support_edges = _parse_int(payload.get("max_support_edges"), defaults.max_support_edges, floor=1) + swarm_v2_cfg = _parse_swarm_v2_config( + _as_dict(payload.get("swarm_v2")), + defaults.swarm_v2, + legacy_max_support_edges=legacy_max_support_edges, + ) + + return SelfPlayTrainingConfig( + rounds=_parse_int(payload.get("rounds"), defaults.rounds, floor=1), + output_dir=str(payload.get("output_dir", defaults.output_dir)).strip() or defaults.output_dir, + dry_run=_parse_bool(payload.get("dry_run"), defaults.dry_run), + wandb_enabled=_parse_bool(payload.get("wandb_enabled"), defaults.wandb_enabled), + wandb_project=str(payload.get("wandb_project", defaults.wandb_project)).strip() or defaults.wandb_project, + wandb_entity=str(payload.get("wandb_entity", defaults.wandb_entity)).strip(), + wandb_run_name_prefix=str(payload.get("wandb_run_name_prefix", defaults.wandb_run_name_prefix)).strip() + or defaults.wandb_run_name_prefix, + canonical_graph_mode=_parse_str_choice( + payload.get("canonical_graph_mode"), + defaults.canonical_graph_mode, + {"generate", "fixed"}, + ), + pipeline_mode=_parse_str_choice( + payload.get("pipeline_mode"), + defaults.pipeline_mode, + {"legacy", "swarm_v2"}, + ), + model_topology=_parse_str_choice( + payload.get("model_topology"), + defaults.model_topology, + {"dual", "shared"}, + ), + phase_schedule=_parse_str_choice( + payload.get("phase_schedule"), + defaults.phase_schedule, + {"generator_answerer", "answerer_generator_answerer"}, + ), + tuning_mode=_parse_str_choice( + payload.get("tuning_mode"), + defaults.tuning_mode, + {"full", "lora"}, + ), + shared_model_name_or_path=str( + payload.get("shared_model_name_or_path", defaults.shared_model_name_or_path) + ).strip(), + seed_tasks_per_round=_parse_int( + payload.get("seed_tasks_per_round"), + defaults.seed_tasks_per_round, + floor=1, + ), + generated_tasks_per_round=_parse_int( + payload.get("generated_tasks_per_round"), + defaults.generated_tasks_per_round, + floor=1, + ), + generator_prompts_per_round=_parse_int( + payload.get("generator_prompts_per_round"), + defaults.generator_prompts_per_round, + floor=1, + ), + max_graph_context_nodes=_parse_int( + payload.get("max_graph_context_nodes"), + defaults.max_graph_context_nodes, + floor=1, + ), + max_graph_context_edges=_parse_int( + payload.get("max_graph_context_edges"), + defaults.max_graph_context_edges, + floor=1, + ), + max_support_edges=legacy_max_support_edges, + answerer_judge_max_new_tokens=_parse_int( + payload.get("answerer_judge_max_new_tokens"), + defaults.answerer_judge_max_new_tokens, + floor=1, + ), + generator_reward_weights=_parse_generator_weights( + _as_dict(payload.get("generator_reward_weights")) + ), + lora=lora_cfg, + swarm_v2=swarm_v2_cfg, + generator_phase=generator_phase, + answerer_phase=answerer_phase, + ) diff --git a/src/osint_env/training/rewards.py b/src/osint_env/training/rewards.py new file mode 100644 index 0000000000000000000000000000000000000000..d8cacdf8cdfc6664b72105e56637ea1b16b734bb --- /dev/null +++ b/src/osint_env/training/rewards.py @@ -0,0 +1,1178 @@ +from __future__ import annotations + +import json +import re +from collections import Counter +from dataclasses import dataclass, field +from functools import lru_cache +from typing import Any + +from osint_env.data.generator import ( + emit_swarm_v2_question, + enumerate_swarm_v2_neighbors, + select_swarm_v2_answer, + trace_swarm_v2_path, +) +from osint_env.domain.models import CanonicalGraph, Edge, TaskInstance +from osint_env.env.reward import build_reward_model, compute_answer_reward +from osint_env.env.spawn_reward_hooks import parl_reward_breakdown +from osint_env.training.config import ( + GeneratorRewardWeights, + SwarmV2SharedContextConfig, + SwarmV2ValidationConfig, +) + + +def _iter_text_fragments(value: Any) -> list[str]: + if value is None: + return [] + if isinstance(value, str): + token = value.strip() + return [token] if token else [] + if isinstance(value, list): + out: list[str] = [] + for item in value: + out.extend(_iter_text_fragments(item)) + return out + if isinstance(value, dict): + out: list[str] = [] + for key in ("content", "text", "output_text", "message", "choices"): + if key in value: + out.extend(_iter_text_fragments(value.get(key))) + return out + return [str(value)] + + +def decode_completion_text(completion: Any) -> str: + parts = _iter_text_fragments(completion) + return "\n".join(part for part in parts if part) + + +def _extract_json_candidates(text: str) -> list[Any]: + candidate = str(text or "").strip() + if not candidate: + return [] + + out: list[Any] = [] + try: + parsed = json.loads(candidate) + except json.JSONDecodeError: + parsed = None + if isinstance(parsed, dict): + out.append(parsed) + + for start_idx, ch in enumerate(candidate): + if ch != "{": + continue + + depth = 0 + in_string = False + escape = False + for end_idx in range(start_idx, len(candidate)): + current = candidate[end_idx] + if escape: + escape = False + continue + if current == "\\": + escape = True + continue + if current == '"': + in_string = not in_string + continue + if in_string: + continue + if current == "{": + depth += 1 + elif current == "}": + depth -= 1 + if depth < 0: + break + if depth != 0: + continue + snippet = candidate[start_idx : end_idx + 1] + try: + parsed = json.loads(snippet) + except json.JSONDecodeError: + break + if isinstance(parsed, dict) and parsed not in out: + out.append(parsed) + break + return out + + +def _extract_json_blob(text: str, preferred_keys: tuple[str, ...] = ()) -> Any: + blobs = _extract_json_candidates(text) + if not blobs: + return None + if not preferred_keys: + return blobs[0] + + best_blob = blobs[0] + best_score = -1 + preferred = set(preferred_keys) + for blob in blobs: + if not isinstance(blob, dict): + continue + score = sum(1 for key in preferred if key in blob) + if score > best_score: + best_blob = blob + best_score = score + return best_blob + + +def normalize_answer(text: str) -> str: + value = str(text or "").strip() + value = value.strip('"').strip("'") + value = re.sub(r"\s+", " ", value) + value = value.rstrip(".\n ") + return value + + +def extract_answer_from_completion(completion_text: str) -> str: + blob = _extract_json_blob(completion_text, preferred_keys=("answer",)) + if isinstance(blob, dict): + answer = str(blob.get("answer", "")).strip() + if answer: + return normalize_answer(answer) + + match = re.search(r"answer\s*[:=]\s*(.+)", completion_text, flags=re.IGNORECASE) + if match: + return normalize_answer(match.group(1)) + + lines = [line.strip() for line in completion_text.splitlines() if line.strip()] + if not lines: + return "" + return normalize_answer(lines[-1]) + + +@dataclass(slots=True) +class SwarmReplayToolCall: + tool_name: str + args: dict[str, Any] = field(default_factory=dict) + output: dict[str, Any] = field(default_factory=dict) + + +@dataclass(slots=True) +class SwarmOrchestratorTelemetry: + spawn_count: int = 0 + finished_subtasks: int = 0 + critical_steps: int = 1 + breadth: int = 0 + depth: int = 0 + + +@dataclass(slots=True) +class ReplayValidationResult: + is_valid: bool + reasons: list[str] = field(default_factory=list) + duplicate_similarity: float = 0.0 + context_nodes: int = 0 + context_edges: int = 0 + unique_path_count: int = 0 + replayed_question: str = "" + replayed_answer: str = "" + replayed_edges: list[Edge] = field(default_factory=list) + + def to_dict(self) -> dict[str, Any]: + return { + "is_valid": self.is_valid, + "reasons": list(self.reasons), + "duplicate_similarity": float(self.duplicate_similarity), + "context_nodes": int(self.context_nodes), + "context_edges": int(self.context_edges), + "unique_path_count": int(self.unique_path_count), + "replayed_question": self.replayed_question, + "replayed_answer": self.replayed_answer, + "replayed_edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in self.replayed_edges + ], + } + + +def _parse_edge_rows(value: Any, max_support_edges: int) -> list[Edge]: + if not isinstance(value, list): + return [] + out: list[Edge] = [] + for row in value[:max_support_edges]: + if not isinstance(row, dict): + continue + src = str(row.get("src", "")).strip() + rel = str(row.get("rel", "")).strip() + dst = str(row.get("dst", "")).strip() + if not src or not rel or not dst: + continue + try: + confidence = float(row.get("confidence", 1.0)) + except (TypeError, ValueError): + confidence = 1.0 + out.append(Edge(src=src, rel=rel, dst=dst, confidence=confidence)) + return out + + +def _parse_tool_trace(value: Any) -> list[SwarmReplayToolCall]: + if not isinstance(value, list): + return [] + out: list[SwarmReplayToolCall] = [] + for row in value: + if not isinstance(row, dict): + continue + tool_name = str(row.get("tool_name", row.get("tool", ""))).strip() + args = row.get("args", {}) + output = row.get("output", {}) + if not tool_name: + continue + out.append( + SwarmReplayToolCall( + tool_name=tool_name, + args=dict(args) if isinstance(args, dict) else {}, + output=dict(output) if isinstance(output, dict) else {}, + ) + ) + return out + + +def _parse_subagent_outputs(value: Any) -> list[str]: + if not isinstance(value, list): + return [] + out: list[str] = [] + for row in value: + if isinstance(row, str): + token = row.strip() + elif isinstance(row, dict): + token = str(row.get("content", row.get("summary", ""))).strip() + else: + token = str(row).strip() + if token: + out.append(token) + return out + + +def _coerce_int(value: Any, default: int) -> int: + """Best-effort int coercion that NEVER raises. + + Models routinely emit garbage like ``"none"``, ``"N/A"``, ``true``, + ``"2 agents"`` for fields the schema requires to be integers. The + reward function runs inside the GRPO training loop, so a single + ``ValueError`` here crashes the entire training job. Be defensive. + """ + if value is None: + return default + if isinstance(value, bool): + return int(value) + if isinstance(value, int): + return value + if isinstance(value, float): + if value != value or value in (float("inf"), float("-inf")): + return default + return int(value) + if isinstance(value, str): + token = value.strip() + if not token: + return default + try: + return int(token) + except ValueError: + try: + return int(float(token)) + except ValueError: + return default + return default + + +def _parse_orchestrator(value: Any) -> SwarmOrchestratorTelemetry: + if not isinstance(value, dict): + return SwarmOrchestratorTelemetry() + return SwarmOrchestratorTelemetry( + spawn_count=max(0, _coerce_int(value.get("spawn_count"), 0)), + finished_subtasks=max(0, _coerce_int(value.get("finished_subtasks"), 0)), + critical_steps=max(1, _coerce_int(value.get("critical_steps"), 1)), + breadth=max(0, _coerce_int(value.get("breadth"), 0)), + depth=max(0, _coerce_int(value.get("depth"), 0)), + ) + + +@dataclass(slots=True) +class GeneratedTaskCandidate: + question: str + answer: str + supporting_edges: list[Edge] + task_type: str + is_valid: bool + tool_trace: list[SwarmReplayToolCall] = field(default_factory=list) + subagent_outputs: list[str] = field(default_factory=list) + canonical_edges: list[Edge] = field(default_factory=list) + canonical_nodes: list[str] = field(default_factory=list) + orchestrator: SwarmOrchestratorTelemetry = field(default_factory=SwarmOrchestratorTelemetry) + validation: dict[str, Any] = field(default_factory=dict) + + + +def parse_generated_task_completion(completion_text: str, max_support_edges: int = 8) -> GeneratedTaskCandidate: + blob = _extract_json_blob( + completion_text, + preferred_keys=("question", "answer", "supporting_edges", "tool_trace", "canonical_graph"), + ) + + question = "" + answer = "" + task_type = "adversarial_trace" + supporting_edges: list[Edge] = [] + tool_trace: list[SwarmReplayToolCall] = [] + subagent_outputs: list[str] = [] + canonical_edges: list[Edge] = [] + canonical_nodes: list[str] = [] + orchestrator = SwarmOrchestratorTelemetry() + validation: dict[str, Any] = {} + + if isinstance(blob, dict): + question = str(blob.get("question", "")).strip() + answer = normalize_answer(str(blob.get("answer", "")).strip()) + task_type = str(blob.get("task_type", "adversarial_trace")).strip() or "adversarial_trace" + supporting_edges = _parse_edge_rows(blob.get("supporting_edges", []), max_support_edges=max_support_edges) + tool_trace = _parse_tool_trace(blob.get("tool_trace", [])) + subagent_outputs = _parse_subagent_outputs(blob.get("subagent_outputs", [])) + orchestrator = _parse_orchestrator(blob.get("orchestrator")) + validation = dict(blob.get("validation", {})) if isinstance(blob.get("validation"), dict) else {} + canonical_graph = blob.get("canonical_graph", {}) + if isinstance(canonical_graph, dict): + canonical_nodes = [ + str(node_id).strip() + for node_id in canonical_graph.get("nodes", []) + if str(node_id).strip() + ] + canonical_edges = _parse_edge_rows( + canonical_graph.get("edges", []), + max_support_edges=max(1, max_support_edges * 4), + ) + + if not question: + line_match = re.search(r"question\s*[:=]\s*(.+)", completion_text, flags=re.IGNORECASE) + if line_match: + question = line_match.group(1).strip() + if not answer: + answer = extract_answer_from_completion(completion_text) + + is_valid = bool(question and answer) + return GeneratedTaskCandidate( + question=question, + answer=answer, + supporting_edges=supporting_edges, + task_type=task_type, + is_valid=is_valid, + tool_trace=tool_trace, + subagent_outputs=subagent_outputs, + canonical_edges=canonical_edges, + canonical_nodes=canonical_nodes, + orchestrator=orchestrator, + validation=validation, + ) + + + +def _token_set(text: str) -> set[str]: + return set(re.findall(r"[a-zA-Z0-9_]+", str(text).lower())) + + + +def _jaccard_similarity(left: str, right: str) -> float: + a = _token_set(left) + b = _token_set(right) + if not a and not b: + return 1.0 + if not a or not b: + return 0.0 + return len(a & b) / max(1, len(a | b)) + + +_SWARM_V2_QUESTION_RE = re.compile( + r"^If you start at (?P.+?) and follow the relation path " + r"(?P.+?), which entity do you reach after (?P\d+) hops\?$" +) + + +def _swarm_v2_question_signature(question: str) -> tuple[str, tuple[str, ...], int] | None: + match = _SWARM_V2_QUESTION_RE.match(str(question or "").strip()) + if not match: + return None + start = normalize_answer(match.group("start")).lower() + relations = tuple( + token.strip().lower() + for token in match.group("relations").split("->") + if token.strip() + ) + if not start or not relations: + return None + return start, relations, int(match.group("hops")) + + +def _swarm_v2_question_similarity(left: str, right: str) -> float: + left_sig = _swarm_v2_question_signature(left) + right_sig = _swarm_v2_question_signature(right) + if left_sig is None or right_sig is None: + return _jaccard_similarity(left, right) + + left_start, left_relations, left_hops = left_sig + right_start, right_relations, right_hops = right_sig + start_score = 1.0 if left_start == right_start else 0.0 + path_score = 1.0 if left_relations == right_relations else _jaccard_similarity( + " ".join(left_relations), + " ".join(right_relations), + ) + hop_score = 1.0 if left_hops == right_hops else 0.0 + return (0.55 * start_score) + (0.35 * path_score) + (0.10 * hop_score) + + +def _distinct_ngram_ratio(texts: list[str], n: int = 2) -> float: + tokens: list[str] = [] + for text in texts: + tokens.extend(re.findall(r"[a-zA-Z0-9_]+", text.lower())) + if len(tokens) < n: + return 0.0 if texts else 1.0 + ngrams = [tuple(tokens[idx : idx + n]) for idx in range(0, len(tokens) - n + 1)] + if not ngrams: + return 0.0 + return len(set(ngrams)) / max(1, len(ngrams)) + + +class SwarmV2ReplayValidator: + """Hard-gated replay validator for deterministic swarm_v2 generation.""" + + def __init__( + self, + graph: CanonicalGraph, + validation: SwarmV2ValidationConfig, + shared_context: SwarmV2SharedContextConfig, + seen_questions: list[str] | None = None, + ): + self.graph = graph + self.validation = validation + self.shared_context = shared_context + self.seen_questions = list(seen_questions or []) + self.graph_nodes = set(graph.nodes.keys()) + self.graph_edges = {(edge.src, edge.rel, edge.dst) for edge in graph.edges} + self.outgoing: dict[str, list[Edge]] = {} + for edge in graph.edges: + self.outgoing.setdefault(edge.src, []).append(edge) + + def remember(self, question: str) -> None: + token = str(question).strip() + if not token: + return + self.seen_questions.append(token) + if len(self.seen_questions) > 4096: + self.seen_questions = self.seen_questions[-2048:] + + def _count_matching_paths(self, start: str, relations: list[str], answer: str, limit: int = 4) -> int: + if not start or not relations: + return 0 + + count = 0 + stack: list[tuple[str, int, tuple[str, ...]]] = [(start, 0, (start,))] + while stack: + node_id, rel_idx, seen_nodes = stack.pop() + if rel_idx >= len(relations): + if node_id == answer: + count += 1 + if count >= limit: + return count + continue + + relation = relations[rel_idx] + for edge in self.outgoing.get(node_id, []): + if edge.rel != relation: + continue + if edge.dst in seen_nodes: + continue + stack.append((edge.dst, rel_idx + 1, seen_nodes + (edge.dst,))) + return count + + def _replay_tool_trace(self, candidate: GeneratedTaskCandidate) -> tuple[list[str], list[Edge], str, str]: + reasons: list[str] = [] + replayed_edges: list[Edge] = [] + replayed_answer = "" + replayed_question = "" + + if not candidate.tool_trace: + return ["non_replayable_tool_calls"], replayed_edges, replayed_answer, replayed_question + + for call in candidate.tool_trace: + if call.tool_name == "enumerate_neighbors": + node_id = str(call.args.get("node_id", "")).strip() + expected_edge = call.args.get("expected_edge", {}) + if not node_id: + reasons.append("non_replayable_tool_calls") + continue + neighbors = enumerate_swarm_v2_neighbors(self.graph, node_id) + if not neighbors: + reasons.append("non_replayable_tool_calls") + if isinstance(expected_edge, dict): + expected_key = ( + str(expected_edge.get("src", "")).strip(), + str(expected_edge.get("rel", "")).strip(), + str(expected_edge.get("dst", "")).strip(), + ) + if expected_key not in {(edge.src, edge.rel, edge.dst) for edge in neighbors}: + reasons.append("non_replayable_tool_calls") + elif call.tool_name == "trace_path": + candidate_path = call.args.get("path", candidate.supporting_edges) + replayed_edges = trace_swarm_v2_path(self.graph, candidate_path) + if not replayed_edges: + reasons.append("non_replayable_tool_calls") + elif call.tool_name == "select_answer": + replayed_answer = select_swarm_v2_answer(replayed_edges) + if not replayed_answer: + reasons.append("non_replayable_tool_calls") + elif call.tool_name == "emit_question": + replayed_question = emit_swarm_v2_question(replayed_edges) + if not replayed_question: + reasons.append("non_replayable_tool_calls") + else: + reasons.append("non_replayable_tool_calls") + + return reasons, replayed_edges, replayed_answer, replayed_question + + def validate(self, candidate: GeneratedTaskCandidate) -> ReplayValidationResult: + reasons: list[str] = [] + + if not candidate.question or not candidate.answer: + reasons.append("missing_question_or_answer") + + if not candidate.supporting_edges: + reasons.append("malformed_support_edges") + + if len(candidate.supporting_edges) > self.validation.max_support_edges: + reasons.append("context_or_support_budget_overflow") + + edge_keys = [(edge.src, edge.rel, edge.dst) for edge in candidate.supporting_edges] + if len(set(edge_keys)) != len(edge_keys): + reasons.append("malformed_support_edges") + + for edge in candidate.supporting_edges: + if edge.src not in self.graph_nodes or edge.dst not in self.graph_nodes: + reasons.append("unseen_nodes_or_edges") + break + if (edge.src, edge.rel, edge.dst) not in self.graph_edges: + reasons.append("unseen_nodes_or_edges") + break + + replay_reasons, replayed_edges, replayed_answer, replayed_question = self._replay_tool_trace(candidate) + reasons.extend(replay_reasons) + + if replayed_edges: + expected_keys = [(edge.src, edge.rel, edge.dst) for edge in replayed_edges] + if expected_keys != edge_keys: + reasons.append("non_replayable_tool_calls") + relations = [edge.rel for edge in replayed_edges] + unique_path_count = self._count_matching_paths( + start=replayed_edges[0].src, + relations=relations, + answer=replayed_answer or candidate.answer, + ) + else: + unique_path_count = 0 + + if unique_path_count != 1: + reasons.append("non_unique_derivation_path") + + if replayed_answer and normalize_answer(replayed_answer) != normalize_answer(candidate.answer): + reasons.append("non_replayable_tool_calls") + + if replayed_question and replayed_question != candidate.question: + reasons.append("non_replayable_tool_calls") + + if candidate.answer and normalize_answer(candidate.answer).lower() in candidate.question.lower(): + reasons.append("answer_leakage") + + duplicate_similarity = 0.0 + if candidate.question and self.seen_questions: + duplicate_similarity = max( + _swarm_v2_question_similarity(candidate.question, seen_question) + for seen_question in self.seen_questions + ) + if duplicate_similarity >= self.validation.duplicate_similarity_threshold: + reasons.append("duplicate_or_near_duplicate") + + context_nodes = len({edge.src for edge in candidate.supporting_edges} | {edge.dst for edge in candidate.supporting_edges}) + context_edges = len(candidate.supporting_edges) + max_context_nodes = min(self.validation.max_context_nodes, self.shared_context.max_nodes) + max_context_edges = min(self.validation.max_context_edges, self.shared_context.max_edges) + if context_nodes > max_context_nodes or context_edges > max_context_edges: + reasons.append("context_or_support_budget_overflow") + + if len(candidate.supporting_edges) > self.validation.max_path_hops: + reasons.append("context_or_support_budget_overflow") + + return ReplayValidationResult( + is_valid=not reasons, + reasons=sorted(set(reasons)), + duplicate_similarity=duplicate_similarity, + context_nodes=context_nodes, + context_edges=context_edges, + unique_path_count=unique_path_count, + replayed_question=replayed_question, + replayed_answer=replayed_answer, + replayed_edges=replayed_edges, + ) + + +class AnswererJudge: + """Lightweight frozen answerer used to score adversarial hardness.""" + + def __init__(self, model_name_or_path: str, max_new_tokens: int = 48): + self.model_name_or_path = model_name_or_path + self.max_new_tokens = max_new_tokens + self._model = None + self._tokenizer = None + + def _ensure_loaded(self) -> None: + if self._model is not None and self._tokenizer is not None: + return + + import torch + from transformers import AutoModelForCausalLM, AutoTokenizer + + tokenizer = AutoTokenizer.from_pretrained(self.model_name_or_path) + if tokenizer.pad_token is None and tokenizer.eos_token is not None: + tokenizer.pad_token = tokenizer.eos_token + + model_kwargs: dict[str, Any] = {} + if torch.cuda.is_available(): + model_kwargs["device_map"] = "auto" + model_kwargs["torch_dtype"] = torch.bfloat16 + + model = AutoModelForCausalLM.from_pretrained(self.model_name_or_path, **model_kwargs) + model.eval() + + self._model = model + self._tokenizer = tokenizer + + @lru_cache(maxsize=2048) + def answer(self, question: str) -> str: + self._ensure_loaded() + assert self._model is not None + assert self._tokenizer is not None + + import torch + + prompt = ( + "You are an OSINT answering model. " + "Answer with only the final entity string.\n" + f"Question: {question}\n" + "Answer:" + ) + + tokenizer = self._tokenizer + model = self._model + encoded = tokenizer(prompt, return_tensors="pt") + device = next(model.parameters()).device + encoded = {k: v.to(device) for k, v in encoded.items()} + + with torch.no_grad(): + output = model.generate( + **encoded, + max_new_tokens=max(1, int(self.max_new_tokens)), + do_sample=False, + temperature=0.0, + pad_token_id=tokenizer.eos_token_id, + ) + + generated = output[0][encoded["input_ids"].shape[1] :] + completion = tokenizer.decode(generated, skip_special_tokens=True) + return normalize_answer(extract_answer_from_completion(completion)) + + +class GeneratorRewardFunction: + """Reward for the graph/question generation swarm in adversarial self-play.""" + + def __init__( + self, + graph: CanonicalGraph, + answerer_judge: AnswererJudge, + weights: GeneratorRewardWeights, + max_support_edges: int = 8, + pipeline_mode: str = "legacy", + swarm_v2_validation: SwarmV2ValidationConfig | None = None, + swarm_v2_shared_context: SwarmV2SharedContextConfig | None = None, + parl_max_parallel_hint: int = 0, + ): + self.graph = graph + self.answerer_judge = answerer_judge + self.weights = weights + self.max_support_edges = max_support_edges + self.pipeline_mode = str(pipeline_mode).strip().lower() or "legacy" + self.graph_nodes = set(graph.nodes.keys()) + self.graph_edges = {(edge.src, edge.rel, edge.dst) for edge in graph.edges} + self._seen_questions: list[str] = [] + self.swarm_v2_validation = swarm_v2_validation or SwarmV2ValidationConfig( + max_support_edges=max_support_edges + ) + self.swarm_v2_shared_context = swarm_v2_shared_context or SwarmV2SharedContextConfig() + self.parl_max_parallel_hint = max(0, int(parl_max_parallel_hint or 0)) + self._swarm_v2_validator = SwarmV2ReplayValidator( + graph=graph, + validation=self.swarm_v2_validation, + shared_context=self.swarm_v2_shared_context, + seen_questions=self._seen_questions, + ) + self._debug_batches_seen = 0 + self._debug_reason_counter: Counter[str] = Counter() + self._debug_reward_window: list[float] = [] + self._debug_last_batch: dict[str, Any] = {} + + @staticmethod + def _std(values: list[float]) -> float: + if len(values) <= 1: + return 0.0 + mean = sum(values) / len(values) + variance = sum((value - mean) ** 2 for value in values) / len(values) + return variance ** 0.5 + + def _invalid_swarm_v2_reward( + self, + candidate: GeneratedTaskCandidate, + validation_result: ReplayValidationResult, + completion_text: str = "", + ) -> float: + # Avoid a constant hard penalty. Keep invalid samples negative but + # graded so GRPO still gets reward variance/advantages when quality + # differs. Three layers of signal: + # (1) per-reason penalty (caps how bad it can get) + # (2) partial credit for any parseable structural element + # (3) tiny text-level signal so completely-collapsed completions + # differ from completions that at least *attempt* JSON. + reason_penalty = { + "missing_question_or_answer": 0.45, + "malformed_support_edges": 0.30, + "non_replayable_tool_calls": 0.40, + "non_unique_derivation_path": 0.25, + "unseen_nodes_or_edges": 0.30, + "answer_leakage": 0.40, + "duplicate_or_near_duplicate": 0.20, + "context_or_support_budget_overflow": 0.20, + } + penalty = 0.20 + for reason in validation_result.reasons: + penalty += reason_penalty.get(reason, 0.10) + + partial_credit = 0.0 + if candidate.question: + partial_credit += 0.30 + if candidate.answer: + partial_credit += 0.30 + if candidate.supporting_edges: + partial_credit += min(0.40, 0.10 * len(candidate.supporting_edges)) + if candidate.tool_trace: + partial_credit += min(0.35, 0.08 * len(candidate.tool_trace)) + if candidate.subagent_outputs: + partial_credit += 0.10 + if candidate.canonical_edges or candidate.canonical_nodes: + partial_credit += 0.10 + + text_signal = self._completion_text_signal(completion_text) + + reward = partial_credit - penalty + text_signal + return float(max(-1.8, min(-0.05, reward))) + + @staticmethod + def _completion_text_signal(completion_text: str) -> float: + """Small [0, 0.25] bonus that grades how 'JSON-like' a raw completion is. + + Important for GRPO: if every sample in a group is unparseable garbage + but the *raw text* differs in JSON-likeness, we still produce non-zero + advantages. Without this the reward collapses to a flat floor and + ``frac_reward_zero_std`` stays at 1.0 forever. + """ + if not completion_text: + return 0.0 + text = completion_text.strip() + if not text: + return 0.0 + signal = 0.0 + # Brace cues (model is trying to emit JSON). + signal += 0.03 * min(2, text.count("{")) + signal += 0.03 * min(2, text.count("}")) + signal += 0.01 * min(4, text.count("[")) + signal += 0.01 * min(4, text.count("]")) + # Schema-keyword cues. Each keyword bumps the signal a tiny amount. + cues = ( + "\"question\"", + "\"answer\"", + "\"supporting_edges\"", + "\"tool_trace\"", + "\"task_type\"", + "\"orchestrator\"", + ) + signal += 0.015 * sum(1 for cue in cues if cue in text) + # Diversity proxy: number of unique short tokens. Pure repetition + # collapses this; varied output keeps it nonzero. Caps very fast. + sample = text[:512] + unique_words = len(set(sample.split())) if sample else 0 + signal += min(0.04, unique_words / 800.0) + # Length proxy capped — purely-empty vs anything-emitted differs. + length_bump = min(0.03, len(text) / 8000.0) + signal += length_bump + return min(0.25, signal) + + def _validity_score(self, candidate: GeneratedTaskCandidate) -> float: + score = 0.0 + if candidate.question: + score += 0.4 + if candidate.answer: + score += 0.4 + if len(candidate.supporting_edges) <= self.max_support_edges: + score += 0.2 + return min(1.0, score) + + def _consistency_score(self, candidate: GeneratedTaskCandidate) -> float: + if not candidate.question or not candidate.answer: + return 0.0 + + edge_consistency = 0.0 + if candidate.supporting_edges: + matches = sum( + 1 + for edge in candidate.supporting_edges + if (edge.src, edge.rel, edge.dst) in self.graph_edges + ) + edge_consistency = matches / max(1, len(candidate.supporting_edges)) + + answer_in_graph = 1.0 if candidate.answer in self.graph_nodes else 0.0 + answer_in_edges = 1.0 if any( + candidate.answer in {edge.src, edge.dst} for edge in candidate.supporting_edges + ) else 0.0 + + question_mentions_graph_symbol = 1.0 if any( + node_id in candidate.question for node_id in self.graph_nodes + ) else 0.0 + + return ( + 0.45 * edge_consistency + + 0.30 * max(answer_in_graph, answer_in_edges) + + 0.25 * question_mentions_graph_symbol + ) + + def _diversity_score(self, question: str) -> float: + if not self._seen_questions: + return 1.0 + max_similarity = max(_jaccard_similarity(question, prior) for prior in self._seen_questions) + return max(0.0, 1.0 - max_similarity) + + def _hardness_score(self, candidate: GeneratedTaskCandidate) -> float: + if not candidate.is_valid: + return -1.0 + predicted_answer = normalize_answer(self.answerer_judge.answer(candidate.question)) + target_answer = normalize_answer(candidate.answer) + return 1.0 if predicted_answer != target_answer else -0.4 + + @staticmethod + def _support_path_coverage(candidate: GeneratedTaskCandidate) -> float: + if not candidate.supporting_edges: + return 0.0 + keys = {(edge.src, edge.rel, edge.dst) for edge in candidate.supporting_edges} + return len(keys) / max(1, len(candidate.supporting_edges)) + + def _swarm_diversity_score(self, candidate: GeneratedTaskCandidate) -> float: + if not candidate.subagent_outputs: + return 0.0 + distinct_ratio = _distinct_ngram_ratio(candidate.subagent_outputs, n=2) + path_coverage = self._support_path_coverage(candidate) + return max(0.0, min(1.0, (0.7 * distinct_ratio) + (0.3 * path_coverage))) + + def _context_pressure_score(self, validation_result: ReplayValidationResult) -> float: + if not validation_result.is_valid: + return 0.0 + + node_util = validation_result.context_nodes / max(1, self.swarm_v2_shared_context.max_nodes) + edge_util = validation_result.context_edges / max(1, self.swarm_v2_shared_context.max_edges) + utilization = max(node_util, edge_util) + target = max(0.05, float(self.swarm_v2_shared_context.target_pressure)) + if utilization > 1.0: + return 0.0 + gap = abs(utilization - target) + return max(0.0, 1.0 - (gap / max(target, 1.0 - target))) + + def _parl_scores(self, candidate: GeneratedTaskCandidate) -> tuple[float, float]: + breakdown = parl_reward_breakdown( + task_outcome_reward=0.0, + spawn_count=candidate.orchestrator.spawn_count, + finished_subtasks=candidate.orchestrator.finished_subtasks, + critical_steps=candidate.orchestrator.critical_steps, + lambda_parallel=0.15, + lambda_finish=0.20, + anneal=1.0, + breadth=candidate.orchestrator.breadth, + depth=candidate.orchestrator.depth, + max_parallel_hint=self.parl_max_parallel_hint, + ) + return breakdown.parallel, breakdown.finish + + def _swarm_v2_reward( + self, + candidate: GeneratedTaskCandidate, + completion_text: str = "", + ) -> tuple[float, ReplayValidationResult]: + validator = self._swarm_v2_validator + validator.seen_questions = list(self._seen_questions) + validation_result = validator.validate(candidate) + if not validation_result.is_valid: + return ( + self._invalid_swarm_v2_reward(candidate, validation_result, completion_text), + validation_result, + ) + + hardness = self._hardness_score(candidate) + swarm_diversity = self._swarm_diversity_score(candidate) + context_pressure = self._context_pressure_score(validation_result) + parl_parallel, parl_finish = self._parl_scores(candidate) + + reward = ( + 0.25 # valid JSON/schema + + 0.30 # replayable derivation + + (0.30 * hardness) + + (0.15 * swarm_diversity) + + (0.10 * context_pressure) + + (0.025 * parl_parallel) + + (0.025 * parl_finish) + ) + return reward, validation_result + + def __call__( + self, + prompts: list[Any] | None = None, + completions: list[Any] | None = None, + **kwargs: Any, + ) -> list[float]: + del prompts + if completions is None: + completions = list(kwargs.get("completions", [])) + rewards: list[float] = [] + batch_reasons: Counter[str] = Counter() + valid_count = 0 + for completion in completions: + try: + text = decode_completion_text(completion) + except Exception: + text = "" + try: + candidate = parse_generated_task_completion( + text, max_support_edges=self.max_support_edges + ) + except Exception as exc: + # Hard guard: a single malformed completion must NEVER take + # down GRPO. Fall through to the invalid-floor with a tiny + # text signal so the group still has reward variance. + print( + f"[reward_debug][parse_error] {type(exc).__name__}: {exc}; " + f"text_head={text[:120]!r}" + ) + rewards.append( + float(max(-1.8, -0.6 + self._completion_text_signal(text))) + ) + batch_reasons["parse_error"] += 1 + continue + + if self.pipeline_mode == "swarm_v2": + try: + reward, validation_result = self._swarm_v2_reward( + candidate, completion_text=text + ) + except Exception as exc: + print( + f"[reward_debug][reward_error] {type(exc).__name__}: {exc}" + ) + rewards.append( + float(max(-1.8, -0.6 + self._completion_text_signal(text))) + ) + batch_reasons["reward_error"] += 1 + continue + rewards.append(float(max(-1.8, min(1.2, reward)))) + if validation_result.is_valid and candidate.question: + valid_count += 1 + self._seen_questions.append(candidate.question) + if len(self._seen_questions) > 4096: + self._seen_questions = self._seen_questions[-2048:] + else: + for reason in validation_result.reasons: + batch_reasons[reason] += 1 + else: + validity = self._validity_score(candidate) + consistency = self._consistency_score(candidate) + diversity = self._diversity_score(candidate.question) if candidate.question else 0.0 + hardness = self._hardness_score(candidate) + + reward = ( + self.weights.validity * validity + + self.weights.hardness * hardness + + self.weights.diversity * diversity + + self.weights.consistency * consistency + ) + rewards.append(float(max(-2.0, min(1.2, reward)))) + + if self.pipeline_mode != "swarm_v2" and candidate.question: + self._seen_questions.append(candidate.question) + if len(self._seen_questions) > 4096: + self._seen_questions = self._seen_questions[-2048:] + + self._debug_batches_seen += 1 + self._debug_reward_window.extend(rewards) + self._debug_reward_window = self._debug_reward_window[-512:] + self._debug_reason_counter.update(batch_reasons) + batch_mean = float(sum(rewards) / max(1, len(rewards))) + batch_std = float(self._std(rewards)) + advantages = [float(value - batch_mean) for value in rewards] + self._debug_last_batch = { + "batch_rewards": list(rewards), + "batch_reward_mean": batch_mean, + "batch_reward_std": batch_std, + "advantage_proxy_min": min(advantages) if advantages else 0.0, + "advantage_proxy_max": max(advantages) if advantages else 0.0, + "advantage_proxy_std": float(self._std(advantages)), + "valid_count": int(valid_count), + "invalid_count": int(max(0, len(rewards) - valid_count)), + "valid_output_ratio": float(valid_count / max(1, len(rewards))), + "top_invalid_reasons": batch_reasons.most_common(5), + } + if self.pipeline_mode == "swarm_v2" and (self._debug_batches_seen % 10 == 0): + window_std = self._std(self._debug_reward_window) + print( + "[reward_debug][generator] " + f"batches={self._debug_batches_seen} " + f"window_reward_std={window_std:.6f} " + f"last_batch_valid={valid_count}/{len(rewards)} " + f"top_invalid_reasons={batch_reasons.most_common(3)}" + ) + + return rewards + + +class AnswererRewardFunction: + """Answer-swarm reward wrapper that reuses the environment answer reward logic.""" + + def __init__( + self, + graph: CanonicalGraph, + pipeline_mode: str = "legacy", + parl_max_parallel_hint: int = 0, + ): + self.reward_model = build_reward_model(graph) + self.pipeline_mode = str(pipeline_mode).strip().lower() or "legacy" + self.parl_max_parallel_hint = max(0, int(parl_max_parallel_hint or 0)) + + @staticmethod + def _parse_support_edges(value: Any) -> list[Edge]: + payload = value + if isinstance(value, str): + try: + payload = json.loads(value) + except json.JSONDecodeError: + payload = [] + + out: list[Edge] = [] + if not isinstance(payload, list): + return out + for row in payload: + if not isinstance(row, dict): + continue + src = str(row.get("src", "")).strip() + rel = str(row.get("rel", "")).strip() + dst = str(row.get("dst", "")).strip() + if not src or not rel or not dst: + continue + try: + confidence = float(row.get("confidence", 1.0)) + except (TypeError, ValueError): + confidence = 1.0 + out.append(Edge(src=src, rel=rel, dst=dst, confidence=confidence)) + return out + + @staticmethod + def _value_at(column: Any, index: int, default: Any) -> Any: + if isinstance(column, list) and index < len(column): + return column[index] + return default + + @staticmethod + def _extract_predicted_edges(completion_text: str, support_edges: list[Edge]) -> list[Edge]: + blob = _extract_json_blob(completion_text) + if isinstance(blob, dict): + structured_edges = _parse_edge_rows(blob.get("supporting_edges", []), max_support_edges=len(support_edges)) + if structured_edges: + return structured_edges + text = completion_text.lower() + matched: list[Edge] = [] + for edge in support_edges: + if edge.src.lower() in text and edge.rel.lower() in text and edge.dst.lower() in text: + matched.append(edge) + return matched + + def _extract_orchestrator_reward(self, completion_text: str, base_reward: float) -> float: + if self.pipeline_mode != "swarm_v2": + return float(base_reward) + blob = _extract_json_blob(completion_text) + orchestrator = _parse_orchestrator(blob.get("orchestrator")) if isinstance(blob, dict) else SwarmOrchestratorTelemetry() + breakdown = parl_reward_breakdown( + task_outcome_reward=base_reward, + spawn_count=orchestrator.spawn_count, + finished_subtasks=orchestrator.finished_subtasks, + critical_steps=orchestrator.critical_steps, + lambda_parallel=0.15, + lambda_finish=0.20, + anneal=1.0, + breadth=orchestrator.breadth, + depth=orchestrator.depth, + max_parallel_hint=self.parl_max_parallel_hint, + ) + return float(breakdown.total) + + def __call__( + self, + prompts: list[Any], + completions: list[Any], + answer: list[Any] | None = None, + question: list[Any] | None = None, + supporting_edges_json: list[Any] | None = None, + difficulty: list[Any] | None = None, + **kwargs: Any, + ) -> list[float]: + rewards: list[float] = [] + + for idx, completion in enumerate(completions): + completion_text = decode_completion_text(completion) + predicted_answer = extract_answer_from_completion(completion_text) + + target_answer = normalize_answer(str(self._value_at(answer, idx, ""))) + question_text = str(self._value_at(question, idx, "")).strip() + if not question_text: + question_text = str(self._value_at(prompts, idx, "")).strip() + + support_payload = self._value_at(supporting_edges_json, idx, []) + support_edges = self._parse_support_edges(support_payload) + difficulty_level = str(self._value_at(difficulty, idx, "hard")).strip() or "hard" + + task = TaskInstance( + task_id=f"train_task_{idx}", + task_type="adversarial_trace", + question=question_text, + answer=target_answer, + supporting_edges=support_edges, + metadata={"difficulty": difficulty_level}, + ) + pred_edges = self._extract_predicted_edges(completion_text, support_edges) + breakdown = compute_answer_reward( + proposed_answer=predicted_answer, + task=task, + pred_edges=pred_edges, + tool_outputs=[], + step_count=1, + model=self.reward_model, + difficulty=difficulty_level, + ) + rewards.append(self._extract_orchestrator_reward(completion_text, breakdown.total)) + + return rewards diff --git a/src/osint_env/training/self_play.py b/src/osint_env/training/self_play.py new file mode 100644 index 0000000000000000000000000000000000000000..472df39d02decd9fcd2b77544c4556e5cf674433 --- /dev/null +++ b/src/osint_env/training/self_play.py @@ -0,0 +1,1670 @@ +from __future__ import annotations + +import inspect +import json +import os +from dataclasses import dataclass +from pathlib import Path +import random +from typing import Any + +from osint_env.data.generator import ( + build_swarm_v2_canonical_subgraph, + build_swarm_v2_path_candidates, + build_swarm_v2_tool_trace, + emit_swarm_v2_question, + select_swarm_v2_answer, + trace_swarm_v2_path, +) +from osint_env.domain.models import Edge, EnvironmentConfig, TaskInstance +from osint_env.env.environment import OSINTEnvironment +from osint_env.llm import build_llm_client +from osint_env.training.config import ( + KimiGRPOPhaseConfig, + LoraTuningConfig, + SelfPlayTrainingConfig, + SwarmV2SwarmConfig, +) +from osint_env.training.rewards import ( + AnswererJudge, + AnswererRewardFunction, + GeneratorRewardFunction, + SwarmV2ReplayValidator, + decode_completion_text, + parse_generated_task_completion, +) + + +@dataclass(slots=True) +class _RoundArtifacts: + round_index: int + generator_dataset_path: str + answerer_dataset_path: str + generated_tasks_path: str + + + +def _require_training_stack() -> tuple[Any, Any, Any]: + try: + from datasets import Dataset + from trl import GRPOConfig, GRPOTrainer + except ImportError as exc: + raise RuntimeError( + "Training stack is missing. Install train dependencies first: " + "python -m pip install -e .[train]" + ) from exc + return Dataset, GRPOConfig, GRPOTrainer + + + +def _task_to_edge_json(task: TaskInstance) -> str: + payload = [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in task.supporting_edges + ] + return json.dumps(payload, sort_keys=True) + + +def _edge_payload(edge: Edge) -> dict[str, Any]: + return { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + + +def _edges_from_payload(rows: Any, max_edges: int) -> list[Edge]: + if not isinstance(rows, list): + return [] + edges: list[Edge] = [] + for row in rows[:max_edges]: + if not isinstance(row, dict): + continue + src = str(row.get("src", "")).strip() + rel = str(row.get("rel", "")).strip() + dst = str(row.get("dst", "")).strip() + if not src or not rel or not dst: + continue + try: + confidence = float(row.get("confidence", 1.0)) + except (TypeError, ValueError): + confidence = 1.0 + edges.append(Edge(src=src, rel=rel, dst=dst, confidence=confidence)) + return edges + + + +def _canonical_example_payload( + graph: Any, + canonical_candidate: dict[str, Any], + swarm_cfg: SwarmV2SwarmConfig, +) -> dict[str, Any]: + candidate_edges = _edges_from_payload(canonical_candidate.get("edges", []), max_edges=2) + traced_edges = trace_swarm_v2_path(graph, candidate_edges) or candidate_edges + if not traced_edges: + return { + "question": "Which entity is reached by following the provided replayable relation path?", + "answer": "", + "task_type": "swarm_v2_trace", + "supporting_edges": [], + "tool_trace": [], + "subagent_outputs": ["path_agent: no replayable edge"], + "orchestrator": { + "spawn_count": 1, + "finished_subtasks": 1, + "critical_steps": 1, + "breadth": 1, + "depth": 1, + }, + } + + traced_edges = traced_edges[:2] + spawn_count = min(swarm_cfg.max_agents, max(1, len(traced_edges) + 1)) + full_tool_trace = build_swarm_v2_tool_trace(graph, traced_edges) + return { + "question": emit_swarm_v2_question(traced_edges), + "answer": select_swarm_v2_answer(traced_edges), + "task_type": f"swarm_v2_{len(traced_edges)}hop_trace", + "supporting_edges": [_edge_payload(edge) for edge in traced_edges], + "tool_trace": full_tool_trace[:4], + "subagent_outputs": [ + f"path_agent_{idx}: {edge.src}->{edge.dst}" + for idx, edge in enumerate(traced_edges) + ], + "orchestrator": { + "spawn_count": spawn_count, + "finished_subtasks": spawn_count, + "critical_steps": max(1, len(traced_edges)), + "breadth": min(swarm_cfg.max_breadth, spawn_count), + "depth": min(swarm_cfg.max_depth, 1), + }, + } + + +def _difficulty_for_task(task: TaskInstance) -> str: + metadata = dict(task.metadata or {}) + token = str(metadata.get("difficulty", "")).strip().lower() + if token in {"easy", "medium", "hard"}: + return token + if task.task_type.startswith("metaqa_1-hop"): + return "easy" + if task.task_type.startswith("metaqa_2-hop"): + return "medium" + return "hard" + + + +def _answer_prompt(question: str) -> str: + return ( + "You are the answer-generation swarm for an OSINT graph task.\n" + "Return ONLY one compact JSON object. Do not use markdown. Do not add prose.\n" + "Required schema: {\"answer\": \"\"}\n" + "Valid example: {\"answer\": \"user_7\"}\n" + f"Question: {question}" + ) + + +def _swarm_v2_answer_prompt( + question: str, + shared_context: dict[str, Any], + swarm_cfg: SwarmV2SwarmConfig, +) -> str: + del swarm_cfg # kept for signature compatibility + compact_context = { + "nodes": list(shared_context.get("nodes", []))[:8], + "edges": list(shared_context.get("edges", []))[:6], + } + return ( + "You answer one OSINT graph question using ONLY the shared context.\n" + "Output rules:\n" + "- Return ONLY one compact JSON object. No markdown. No prose. End with }.\n" + "- Required keys: answer, supporting_edges, orchestrator.\n" + "- supporting_edges: list of {src, rel, dst, confidence} taken from shared edges.\n" + "- orchestrator integer keys: spawn_count, finished_subtasks, critical_steps, breadth, depth.\n" + "Example schema:\n" + "{\"answer\":\"user_7\",\"supporting_edges\":[{\"src\":\"alias_7_123\",\"rel\":\"alias_of\"," + "\"dst\":\"user_7\",\"confidence\":1.0}],\"orchestrator\":{\"spawn_count\":2," + "\"finished_subtasks\":2,\"critical_steps\":1,\"breadth\":2,\"depth\":1}}\n" + f"Shared context: {json.dumps(compact_context, separators=(',', ':'), sort_keys=True)}\n" + f"Question: {question}\n" + "JSON:" + ) + + + +def _build_answerer_rows(tasks: list[TaskInstance]) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + for task in tasks: + rows.append( + { + "prompt": _answer_prompt(task.question), + "question": task.question, + "answer": str(task.answer), + "supporting_edges_json": _task_to_edge_json(task), + "difficulty": _difficulty_for_task(task), + "task_type": task.task_type, + "task_id": task.task_id, + } + ) + return rows + + +def _build_swarm_v2_answerer_rows( + env: OSINTEnvironment, + tasks: list[TaskInstance], + cfg: SelfPlayTrainingConfig, +) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + for task in tasks: + metadata = dict(task.metadata or {}) + canonical_graph = metadata.get("canonical_graph") + if isinstance(canonical_graph, dict): + shared_context = { + "nodes": list(canonical_graph.get("nodes", []))[: cfg.swarm_v2.shared_context.max_nodes], + "edges": list(canonical_graph.get("edges", []))[: cfg.swarm_v2.shared_context.max_edges], + } + else: + deterministic_seed = sum(ord(ch) for ch in task.task_id) + shared_context = _graph_context_for_prompt( + env=env, + max_nodes=cfg.swarm_v2.shared_context.max_nodes, + max_edges=cfg.swarm_v2.shared_context.max_edges, + rng=random.Random(deterministic_seed), + ) + + rows.append( + { + "prompt": _swarm_v2_answer_prompt( + question=task.question, + shared_context=shared_context, + swarm_cfg=cfg.swarm_v2.answerer_swarm, + ), + "question": task.question, + "answer": str(task.answer), + "supporting_edges_json": _task_to_edge_json(task), + "difficulty": _difficulty_for_task(task), + "task_type": task.task_type, + "task_id": task.task_id, + } + ) + return rows + + + +def _graph_context_for_prompt( + env: OSINTEnvironment, + max_nodes: int, + max_edges: int, + rng: random.Random, +) -> dict[str, Any]: + node_ids = sorted(env.graph.nodes.keys()) + if len(node_ids) > max_nodes: + node_ids = rng.sample(node_ids, k=max_nodes) + + edges = list(env.graph.edges) + if len(edges) > max_edges: + edges = rng.sample(edges, k=max_edges) + + return { + "nodes": node_ids, + "edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + } + for edge in edges + ], + } + + + +def _generator_prompt(context_blob: dict[str, Any], anchor_questions: list[str]) -> str: + anchors = "\n".join(f"- {question}" for question in anchor_questions) + return ( + "You are the adversarial question-and-graph generation swarm in self-play.\n" + "Generate one challenging but answerable OSINT task that makes answering difficult.\n" + "Use only entities and relations from the provided graph context.\n" + "Prefer multi-hop traces and avoid duplicates of the anchor questions.\n" + "Return strict JSON with keys: question, answer, task_type, supporting_edges.\n" + "supporting_edges must be a list of objects with src, rel, dst, confidence.\n" + "Graph context:\n" + f"{json.dumps(context_blob, sort_keys=True)}\n" + "Anchor questions to avoid:\n" + f"{anchors}\n" + ) + + + +def _build_generator_rows( + env: OSINTEnvironment, + cfg: SelfPlayTrainingConfig, + rng: random.Random, +) -> list[dict[str, Any]]: + rows: list[dict[str, Any]] = [] + existing_questions = [task.question for task in env.tasks] + + for _ in range(max(1, cfg.generator_prompts_per_round)): + context_blob = _graph_context_for_prompt( + env=env, + max_nodes=cfg.max_graph_context_nodes, + max_edges=cfg.max_graph_context_edges, + rng=rng, + ) + anchor_sample_size = min(5, len(existing_questions)) + anchor_sample = rng.sample(existing_questions, k=anchor_sample_size) if anchor_sample_size > 0 else [] + rows.append( + { + "prompt": _generator_prompt(context_blob, anchor_sample), + } + ) + return rows + + +def _swarm_v2_generator_prompt( + graph: Any, + shared_context: dict[str, Any], + canonical_candidate: dict[str, Any], + anchor_questions: list[str], + swarm_cfg: SwarmV2SwarmConfig, + canonical_graph_mode: str, +) -> str: + anchors = "\n".join(f"- {question}" for question in anchor_questions) + canonical_mode = str(canonical_graph_mode).strip().lower() or "generate" + example_payload = _canonical_example_payload(graph, canonical_candidate, swarm_cfg) + canonical_instruction = ( + "You may propose canonical_graph updates when they improve replayability and keep it graph-grounded." + if canonical_mode == "generate" + else "Reuse the provided canonical candidate as-is; do not add, remove, or modify canonical_graph nodes/edges." + ) + canonical_compact = { + "nodes": list(canonical_candidate.get("nodes", []))[:8], + "edges": list(canonical_candidate.get("edges", []))[:6], + } + return ( + "You generate ONE OSINT question/answer task as compact JSON.\n" + "Output rules:\n" + "- Return ONLY one JSON object. No markdown. No prose. End with }.\n" + "- Required keys: question, answer, task_type, supporting_edges, tool_trace, " + "subagent_outputs, orchestrator.\n" + "- supporting_edges: non-empty list of {src, rel, dst, confidence}, taken from canonical edges.\n" + "- tool_trace: non-empty list of {tool, args, result} using only " + "enumerate_neighbors|trace_path|select_answer|emit_question.\n" + "- answer = final dst of the trace. question describes the path.\n" + "- orchestrator: integer keys spawn_count, finished_subtasks, critical_steps, breadth, depth.\n" + f"- canonical_graph_mode={canonical_mode}: {canonical_instruction}\n" + "Example (copy schema, not values):\n" + f"{json.dumps(example_payload, separators=(',', ':'), sort_keys=True)}\n" + "Canonical candidate (use these edges):\n" + f"{json.dumps(canonical_compact, separators=(',', ':'), sort_keys=True)}\n" + f"Avoid these prior questions: {anchors}\n" + "JSON:" + ) + + +def _build_swarm_v2_generator_rows( + env: OSINTEnvironment, + cfg: SelfPlayTrainingConfig, + rng: random.Random, +) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: + rows: list[dict[str, Any]] = [] + canonical_candidates: list[dict[str, Any]] = [] + existing_questions = [task.question for task in env.tasks] + path_candidates = build_swarm_v2_path_candidates( + env.graph, + rng=rng, + count=max(1, cfg.generator_prompts_per_round), + min_hops=2, + max_hops=cfg.swarm_v2.validation.max_path_hops, + ) + for idx, path_edges in enumerate(path_candidates): + shared_context = _graph_context_for_prompt( + env=env, + max_nodes=cfg.swarm_v2.shared_context.max_nodes, + max_edges=cfg.swarm_v2.shared_context.max_edges, + rng=rng, + ) + canonical_candidate = build_swarm_v2_canonical_subgraph( + env.graph, + path_edges=path_edges, + max_extra_edges=max(0, cfg.swarm_v2.shared_context.max_edges - len(path_edges)), + ) + anchor_sample_size = min(5, len(existing_questions)) + anchor_sample = rng.sample(existing_questions, k=anchor_sample_size) if anchor_sample_size > 0 else [] + prompt = _swarm_v2_generator_prompt( + graph=env.graph, + shared_context=shared_context, + canonical_candidate=canonical_candidate, + anchor_questions=anchor_sample, + swarm_cfg=cfg.swarm_v2.generator_swarm, + canonical_graph_mode=cfg.canonical_graph_mode, + ) + rows.append( + { + "prompt": prompt, + "candidate_id": f"candidate_{idx}", + "canonical_graph_json": json.dumps(canonical_candidate, sort_keys=True), + } + ) + canonical_candidates.append(canonical_candidate) + return rows, canonical_candidates + + + +def _safe_build_grpo_config( + phase: KimiGRPOPhaseConfig, + output_dir: str, + grpo_config_cls: Any, + report_to: list[str] | None = None, + run_name: str = "", +) -> Any: + kwargs: dict[str, Any] = { + "output_dir": output_dir, + "learning_rate": float(phase.learning_rate), + "max_steps": int(phase.max_steps), + "per_device_train_batch_size": int(phase.per_device_train_batch_size), + "gradient_accumulation_steps": int(phase.gradient_accumulation_steps), + "num_generations": int(phase.num_generations), + "max_completion_length": int(phase.max_completion_length), + "temperature": float(phase.temperature), + "top_p": float(phase.top_p), + "repetition_penalty": float(phase.repetition_penalty), + "beta": float(phase.beta), + "epsilon": float(phase.epsilon), + "num_iterations": int(phase.num_iterations), + "loss_type": str(phase.loss_type), + "scale_rewards": str(phase.scale_rewards), + "logging_steps": int(phase.logging_steps), + "save_steps": int(phase.save_steps), + "remove_unused_columns": False, + "use_vllm": bool(phase.use_vllm), + "vllm_mode": str(phase.vllm_mode), + "report_to": list(report_to or []), + } + if str(run_name).strip(): + kwargs["run_name"] = str(run_name).strip() + + signature = inspect.signature(grpo_config_cls.__init__) + filtered = {key: value for key, value in kwargs.items() if key in signature.parameters} + return grpo_config_cls(**filtered) + + +def _build_lora_config(lora: LoraTuningConfig) -> Any: + try: + from peft import LoraConfig, TaskType + except ImportError as exc: + raise RuntimeError( + "LoRA tuning selected, but PEFT is not installed. " + "Install train dependencies first: python -m pip install -e .[train]" + ) from exc + + task_type_token = str(lora.task_type or "CAUSAL_LM").strip().upper() + task_type = getattr(TaskType, task_type_token, TaskType.CAUSAL_LM) + return LoraConfig( + r=max(1, int(lora.r)), + lora_alpha=max(1, int(lora.alpha)), + lora_dropout=float(lora.dropout), + target_modules=list(lora.target_modules), + bias=str(lora.bias), + task_type=task_type, + ) + + +def _coerce_named_reward_func(reward_function: Any) -> Any: + """Return a callable with a stable __name__ for TRL compatibility.""" + if hasattr(reward_function, "__name__") and str(getattr(reward_function, "__name__", "")).strip(): + return reward_function + + # TRL versions that introspect reward_funcs[i].__name__ require this attribute. + if callable(reward_function): + name = reward_function.__class__.__name__ or "reward_func" + try: + setattr(reward_function, "__name__", name) + return reward_function + except Exception: + def _wrapped_reward(*args: Any, **kwargs: Any) -> Any: + return reward_function(*args, **kwargs) + + _wrapped_reward.__name__ = name + return _wrapped_reward + return reward_function + + + +def _train_grpo_phase( + model_name_or_path: str, + phase: KimiGRPOPhaseConfig, + rows: list[dict[str, Any]], + reward_function: Any, + output_dir: Path, + tuning_mode: str, + lora: LoraTuningConfig, + report_to: list[str] | None = None, + run_name: str = "", +) -> dict[str, Any]: + Dataset, GRPOConfig, GRPOTrainer = _require_training_stack() + + output_dir.mkdir(parents=True, exist_ok=True) + dataset = Dataset.from_list(rows) + args = _safe_build_grpo_config( + phase=phase, + output_dir=str(output_dir), + grpo_config_cls=GRPOConfig, + report_to=report_to, + run_name=run_name, + ) + + trainer_kwargs: dict[str, Any] = { + "model": model_name_or_path, + "args": args, + "reward_funcs": _coerce_named_reward_func(reward_function), + "train_dataset": dataset, + } + + if str(tuning_mode).strip().lower() == "lora": + trainer_signature = inspect.signature(GRPOTrainer.__init__) + if "peft_config" not in trainer_signature.parameters: + raise RuntimeError("Installed TRL version does not expose peft_config in GRPOTrainer.") + trainer_kwargs["peft_config"] = _build_lora_config(lora) + + phase_label = str(run_name).strip() or str(output_dir.name) + print(f"[self_play] Starting phase: {phase_label} rows={len(rows)} max_steps={phase.max_steps}") + strict_asserts = str(os.getenv("OSINT_TRAIN_STRICT_ASSERTS", "")).strip().lower() in {"1", "true", "yes", "on"} + trainer = GRPOTrainer(**trainer_kwargs) + tracked_params = [ + (name, param) + for name, param in trainer.model.named_parameters() + if getattr(param, "requires_grad", False) + ][:32] + pre_update_fingerprint = { + name: float(param.detach().float().abs().mean().item()) + for name, param in tracked_params + } + train_output = trainer.train() + + final_dir = output_dir / "final_model" + trainer.save_model(str(final_dir)) + + global_step = int(getattr(train_output, "global_step", 0)) + training_loss = float(getattr(train_output, "training_loss", 0.0)) + + result = { + "model_path": str(final_dir), + "global_step": global_step, + "training_loss": training_loss, + "train_rows": len(rows), + "tuning_mode": str(tuning_mode).strip().lower() or "full", + } + + log_history = list(getattr(getattr(trainer, "state", None), "log_history", []) or []) + reward_values = [float(row.get("reward")) for row in log_history if isinstance(row, dict) and "reward" in row] + reward_std_values = [ + float(row.get("reward_std")) + for row in log_history + if isinstance(row, dict) and "reward_std" in row + ] + kl_values = [float(row.get("kl")) for row in log_history if isinstance(row, dict) and "kl" in row] + grad_norm_values = [ + float(row.get("grad_norm")) + for row in log_history + if isinstance(row, dict) and "grad_norm" in row + ] + loss_values = [float(row.get("loss")) for row in log_history if isinstance(row, dict) and "loss" in row] + entropy_values = [float(row.get("entropy")) for row in log_history if isinstance(row, dict) and "entropy" in row] + + trainable_params = [param for param in trainer.model.parameters() if getattr(param, "requires_grad", False)] + grad_tensors = [param.grad for param in trainable_params if getattr(param, "grad", None) is not None] + trainable_param_count = int(sum(param.numel() for param in trainable_params)) + params_with_grad = int(len(grad_tensors)) + nonzero_grad_tensors = int( + sum( + 1 + for grad in grad_tensors + if float(grad.detach().abs().sum().item()) > 0.0 + ) + ) + + diagnostics = { + "reward_min": min(reward_values) if reward_values else 0.0, + "reward_max": max(reward_values) if reward_values else 0.0, + "reward_std_max": max(reward_std_values) if reward_std_values else 0.0, + "kl_max": max(kl_values) if kl_values else 0.0, + "loss_abs_max": max((abs(value) for value in loss_values), default=0.0), + "grad_norm_max": max(grad_norm_values) if grad_norm_values else 0.0, + "entropy_min": min(entropy_values) if entropy_values else 0.0, + "entropy_max": max(entropy_values) if entropy_values else 0.0, + "trainable_param_count": trainable_param_count, + "params_with_grad": params_with_grad, + "nonzero_grad_tensors": nonzero_grad_tensors, + "fingerprint_param_count": len(pre_update_fingerprint), + "fingerprint_changed_count": 0, + } + if pre_update_fingerprint: + changed_count = 0 + for name, param in tracked_params: + after_value = float(param.detach().float().abs().mean().item()) + before_value = pre_update_fingerprint.get(name, after_value) + if abs(after_value - before_value) > 1e-9: + changed_count += 1 + diagnostics["fingerprint_changed_count"] = changed_count + result["diagnostics"] = diagnostics + + print( + "[self_play][diagnostics] " + f"{phase_label} reward_range=({diagnostics['reward_min']:.4f},{diagnostics['reward_max']:.4f}) " + f"reward_std_max={diagnostics['reward_std_max']:.6f} " + f"kl_max={diagnostics['kl_max']:.6f} " + f"loss_abs_max={diagnostics['loss_abs_max']:.6f} " + f"grad_norm_max={diagnostics['grad_norm_max']:.6f} " + f"nonzero_grad_tensors={diagnostics['nonzero_grad_tensors']}/{max(1, diagnostics['params_with_grad'])} " + f"fingerprint_changed={diagnostics['fingerprint_changed_count']}/{max(1, diagnostics['fingerprint_param_count'])}" + ) + + if strict_asserts: + assert diagnostics["reward_max"] != diagnostics["reward_min"], ( + f"Constant reward detected in {phase_label}: {diagnostics['reward_min']}" + ) + assert diagnostics["reward_std_max"] > 0.0, f"reward_std stayed zero in {phase_label}" + assert diagnostics["kl_max"] > 0.0, f"KL stayed zero in {phase_label}" + assert diagnostics["loss_abs_max"] > 0.0, f"Loss stayed zero in {phase_label}" + assert diagnostics["grad_norm_max"] > 0.0, f"Grad norm stayed zero in {phase_label}" + assert diagnostics["nonzero_grad_tensors"] > 0, f"No non-zero grads in {phase_label}" + assert diagnostics["fingerprint_changed_count"] > 0, f"No parameter fingerprint change in {phase_label}" + + reward_debug = getattr(reward_function, "_debug_last_batch", None) + if isinstance(reward_debug, dict): + print(f"[reward_debug][last_batch] {phase_label} {json.dumps(reward_debug, sort_keys=True)}") + + print( + "[self_play] Finished phase: " + f"{phase_label} global_step={global_step} training_loss={training_loss} output={final_dir}" + ) + return result + + +def _resolve_reporting(training_config: SelfPlayTrainingConfig, phase_name: str, round_index: int) -> tuple[list[str], str]: + if not training_config.wandb_enabled: + return [], "" + if training_config.wandb_project: + os.environ["WANDB_PROJECT"] = str(training_config.wandb_project) + if training_config.wandb_entity: + os.environ["WANDB_ENTITY"] = str(training_config.wandb_entity) + prefix = str(training_config.wandb_run_name_prefix).strip() or "self-play" + run_name = f"{prefix}-r{round_index:03d}-{phase_name}" + return ["wandb"], run_name + + +def _resolve_initial_models(cfg: SelfPlayTrainingConfig) -> tuple[str, str]: + topology = str(cfg.model_topology).strip().lower() + if topology == "shared": + shared = str(cfg.shared_model_name_or_path).strip() + if not shared: + shared = str(cfg.answerer_phase.model_name_or_path).strip() or str(cfg.generator_phase.model_name_or_path).strip() + return shared, shared + return str(cfg.generator_phase.model_name_or_path), str(cfg.answerer_phase.model_name_or_path) + + + +def _fallback_generated_tasks( + base_tasks: list[TaskInstance], + round_index: int, + count: int, + rng: random.Random, +) -> list[TaskInstance]: + if not base_tasks: + return [] + + selected = list(base_tasks) + rng.shuffle(selected) + selected = selected[: max(1, count)] + + out: list[TaskInstance] = [] + for idx, task in enumerate(selected): + metadata = dict(task.metadata or {}) + metadata.update( + { + "generated_by": "fallback_generator", + "difficulty": "hard", + "round": round_index, + "scenario": "adversarial_trace", + "grader": { + "type": "difficulty_exact_match", + "answer_type": "node_id", + "case_sensitive": True, + "reward_profile": "hard", + }, + } + ) + out.append( + TaskInstance( + task_id=f"adv_r{round_index}_{idx}", + task_type="adversarial_trace", + question=f"[Adversarial] {task.question}", + answer=task.answer, + supporting_edges=list(task.supporting_edges), + metadata=metadata, + ) + ) + return out + + + +def _sample_generated_tasks_with_model( + model_name_or_path: str, + prompts: list[str], + round_index: int, + count: int, + max_support_edges: int, +) -> list[TaskInstance]: + from transformers import AutoModelForCausalLM, AutoTokenizer + + if count <= 0: + return [] + + tokenizer = AutoTokenizer.from_pretrained(model_name_or_path) + if tokenizer.pad_token is None and tokenizer.eos_token is not None: + tokenizer.pad_token = tokenizer.eos_token + model = AutoModelForCausalLM.from_pretrained(model_name_or_path) + model.eval() + + import torch + + device = next(model.parameters()).device + generated: list[TaskInstance] = [] + + for prompt in prompts: + if len(generated) >= count: + break + encoded = tokenizer(prompt, return_tensors="pt") + encoded = {k: v.to(device) for k, v in encoded.items()} + + with torch.no_grad(): + output = model.generate( + **encoded, + max_new_tokens=256, + do_sample=True, + top_p=0.95, + temperature=1.0, + num_return_sequences=1, + pad_token_id=tokenizer.eos_token_id, + ) + + completion_ids = output[0][encoded["input_ids"].shape[1] :] + completion = tokenizer.decode(completion_ids, skip_special_tokens=True) + candidate = parse_generated_task_completion(completion, max_support_edges=max_support_edges) + if not candidate.is_valid: + continue + + metadata = { + "generated_by": "generator_model", + "round": round_index, + "difficulty": "hard", + "scenario": "adversarial_trace", + "grader": { + "type": "difficulty_exact_match", + "answer_type": "node_id", + "case_sensitive": True, + "reward_profile": "hard", + }, + } + generated.append( + TaskInstance( + task_id=f"adv_r{round_index}_{len(generated)}", + task_type=candidate.task_type, + question=candidate.question, + answer=candidate.answer, + supporting_edges=list(candidate.supporting_edges), + metadata=metadata, + ) + ) + + return generated + + + +def _select_answerer_tasks( + seed_tasks: list[TaskInstance], + generated_tasks: list[TaskInstance], + cfg: SelfPlayTrainingConfig, + rng: random.Random, +) -> list[TaskInstance]: + seed_pick = list(seed_tasks) + gen_pick = list(generated_tasks) + rng.shuffle(seed_pick) + rng.shuffle(gen_pick) + + chosen = seed_pick[: max(1, cfg.seed_tasks_per_round)] + chosen.extend(gen_pick[: max(1, cfg.generated_tasks_per_round)]) + return chosen + + + +def _save_rows(path: Path, rows: list[dict[str, Any]]) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(rows, indent=2, sort_keys=True), encoding="utf-8") + + + +def _save_tasks(path: Path, tasks: list[TaskInstance]) -> None: + payload = [] + for task in tasks: + payload.append( + { + "task_id": task.task_id, + "task_type": task.task_type, + "question": task.question, + "answer": task.answer, + "supporting_edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in task.supporting_edges + ], + "metadata": dict(task.metadata or {}), + } + ) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8") + + +def _save_payload(path: Path, payload: Any) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8") + + +def _fallback_swarm_v2_completion_texts( + env: OSINTEnvironment, + cfg: SelfPlayTrainingConfig, + round_index: int, + rng: random.Random, +) -> list[str]: + completion_texts: list[str] = [] + path_candidates = build_swarm_v2_path_candidates( + env.graph, + rng=rng, + count=max(1, cfg.generated_tasks_per_round * 2), + min_hops=2, + max_hops=cfg.swarm_v2.validation.max_path_hops, + ) + for idx, path_edges in enumerate(path_candidates): + traced_edges = trace_swarm_v2_path(env.graph, path_edges) + if not traced_edges: + continue + question = emit_swarm_v2_question(traced_edges) + answer = select_swarm_v2_answer(traced_edges) + canonical_graph = build_swarm_v2_canonical_subgraph( + env.graph, + path_edges=traced_edges, + max_extra_edges=max(0, cfg.swarm_v2.shared_context.max_edges - len(traced_edges)), + ) + spawn_count = min( + cfg.swarm_v2.generator_swarm.max_agents, + max(1, len(traced_edges) + 1), + ) + payload = { + "canonical_graph": canonical_graph, + "question": question, + "answer": answer, + "task_type": f"swarm_v2_{len(traced_edges)}hop_trace", + "supporting_edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in traced_edges + ], + "tool_trace": build_swarm_v2_tool_trace(env.graph, traced_edges), + "subagent_outputs": [ + f"path_agent_{edge_idx}: {edge.src} --{edge.rel}--> {edge.dst}" + for edge_idx, edge in enumerate(traced_edges) + ] + + [ + f"question_agent: emitted deterministic relation-path question for round {round_index}", + f"context_agent: shared context path_size={len(traced_edges)} candidate={idx}", + ], + "orchestrator": { + "spawn_count": spawn_count, + "finished_subtasks": spawn_count, + "critical_steps": max(1, len(traced_edges)), + "breadth": min(cfg.swarm_v2.generator_swarm.max_breadth, spawn_count), + "depth": min(cfg.swarm_v2.generator_swarm.max_depth, 1 if len(traced_edges) <= 2 else 2), + }, + } + completion_texts.append(json.dumps(payload, sort_keys=True)) + return completion_texts + + +def _sample_swarm_v2_completion_texts_with_model( + env: OSINTEnvironment, + cfg: SelfPlayTrainingConfig, + model_name_or_path: str, + prompts: list[str], + count: int, + seen_questions: list[str], +) -> list[str]: + from transformers import AutoModelForCausalLM, AutoTokenizer + + if count <= 0: + return [] + + tokenizer = AutoTokenizer.from_pretrained(model_name_or_path) + if tokenizer.pad_token is None and tokenizer.eos_token is not None: + tokenizer.pad_token = tokenizer.eos_token + model = AutoModelForCausalLM.from_pretrained(model_name_or_path) + model.eval() + + import torch + + device = next(model.parameters()).device + completions: list[str] = [] + validator = SwarmV2ReplayValidator( + graph=env.graph, + validation=cfg.swarm_v2.validation, + shared_context=cfg.swarm_v2.shared_context, + seen_questions=seen_questions, + ) + for prompt in prompts: + if len(completions) >= count: + break + encoded = tokenizer(prompt, return_tensors="pt") + encoded = {key: value.to(device) for key, value in encoded.items()} + + best_completion = "" + best_score = -999 + for attempt_idx, (temperature, top_p) in enumerate([(0.7, 0.9), (0.5, 0.85), (0.3, 0.8)]): + with torch.no_grad(): + output = model.generate( + **encoded, + max_new_tokens=max(256, int(cfg.generator_phase.max_completion_length)), + do_sample=True, + top_p=top_p, + temperature=temperature, + num_return_sequences=1, + pad_token_id=tokenizer.eos_token_id, + ) + + completion_ids = output[0][encoded["input_ids"].shape[1] :] + completion = tokenizer.decode(completion_ids, skip_special_tokens=True) + candidate = parse_generated_task_completion( + completion, + max_support_edges=cfg.swarm_v2.validation.max_support_edges, + ) + validation = validator.validate(candidate) + score = int(bool(candidate.question)) + int(bool(candidate.answer)) + len(candidate.supporting_edges) + if validation.is_valid: + print(f"[self_play][generation_retry] valid_completion attempt={attempt_idx + 1}") + best_completion = completion + break + if score > best_score: + best_score = score + best_completion = completion + completions.append(best_completion) + return completions + + +def _materialize_swarm_v2_completions( + env: OSINTEnvironment, + cfg: SelfPlayTrainingConfig, + completion_texts: list[str], + round_index: int, + seen_questions: list[str], + prompt_canonical_candidates: list[dict[str, Any]] | None = None, +) -> tuple[list[TaskInstance], list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]]]: + validator = SwarmV2ReplayValidator( + graph=env.graph, + validation=cfg.swarm_v2.validation, + shared_context=cfg.swarm_v2.shared_context, + seen_questions=seen_questions, + ) + + tasks: list[TaskInstance] = [] + validation_reports: list[dict[str, Any]] = [] + canonical_graph_candidates: list[dict[str, Any]] = [] + replay_traces: list[dict[str, Any]] = [] + + for completion_idx, completion_text in enumerate(completion_texts): + use_fixed_canonical = str(cfg.canonical_graph_mode).strip().lower() == "fixed" + if use_fixed_canonical and prompt_canonical_candidates and completion_idx >= len(prompt_canonical_candidates): + break + + candidate = parse_generated_task_completion( + completion_text, + max_support_edges=cfg.swarm_v2.validation.max_support_edges, + ) + validation = validator.validate(candidate) + + if use_fixed_canonical and prompt_canonical_candidates and completion_idx < len(prompt_canonical_candidates): + canonical_graph = dict(prompt_canonical_candidates[completion_idx]) + else: + if candidate.canonical_edges or candidate.canonical_nodes: + canonical_edges = list(candidate.canonical_edges or candidate.supporting_edges) + canonical_nodes = list(candidate.canonical_nodes) + if not canonical_nodes: + canonical_nodes = sorted( + {edge.src for edge in canonical_edges} | {edge.dst for edge in canonical_edges} + ) + canonical_graph = { + "nodes": canonical_nodes, + "edges": [ + { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + for edge in canonical_edges + ], + } + else: + canonical_graph = build_swarm_v2_canonical_subgraph( + env.graph, + candidate.supporting_edges, + max_extra_edges=max(0, cfg.swarm_v2.shared_context.max_edges - len(candidate.supporting_edges)), + ) + + canonical_graph_candidates.append( + { + "candidate_index": completion_idx, + "canonical_graph": canonical_graph, + "question": candidate.question, + "answer": candidate.answer, + } + ) + replay_traces.append( + { + "candidate_index": completion_idx, + "question": candidate.question, + "tool_trace": [ + { + "tool_name": call.tool_name, + "args": dict(call.args), + "output": dict(call.output), + } + for call in candidate.tool_trace + ], + "replayed_edges": validation.to_dict()["replayed_edges"], + } + ) + validation_reports.append( + { + "candidate_index": completion_idx, + "question": candidate.question, + "answer": candidate.answer, + "task_type": candidate.task_type, + "validation": validation.to_dict(), + "raw_completion": completion_text, + } + ) + + if not validation.is_valid: + continue + if len(tasks) >= max(1, cfg.generated_tasks_per_round): + continue + + metadata = { + "generated_by": "swarm_v2_generator", + "round": round_index, + "difficulty": "hard", + "scenario": "swarm_v2_trace", + "canonical_graph": canonical_graph, + "tool_trace": [ + { + "tool_name": call.tool_name, + "args": dict(call.args), + "output": dict(call.output), + } + for call in candidate.tool_trace + ], + "subagent_outputs": list(candidate.subagent_outputs), + "validation": validation.to_dict(), + "shared_context_budget": { + "max_nodes": cfg.swarm_v2.shared_context.max_nodes, + "max_edges": cfg.swarm_v2.shared_context.max_edges, + "target_pressure": cfg.swarm_v2.shared_context.target_pressure, + }, + "grader": { + "type": "difficulty_exact_match", + "answer_type": "node_id", + "case_sensitive": True, + "reward_profile": "hard", + }, + } + tasks.append( + TaskInstance( + task_id=f"swarm_v2_r{round_index}_{len(tasks)}", + task_type=candidate.task_type or "swarm_v2_trace", + question=candidate.question, + answer=candidate.answer, + supporting_edges=list(validation.replayed_edges or candidate.supporting_edges), + metadata=metadata, + ) + ) + validator.remember(candidate.question) + + return tasks, validation_reports, canonical_graph_candidates, replay_traces + + +def _run_adversarial_self_play_swarm_v2( + env_config: EnvironmentConfig, + training_config: SelfPlayTrainingConfig, + dry_run: bool = False, +) -> dict[str, Any]: + effective_dry_run = bool(dry_run or training_config.dry_run) + topology = str(training_config.model_topology).strip().lower() or "dual" + phase_schedule = str(training_config.phase_schedule).strip().lower() or "generator_answerer" + tuning_mode = str(training_config.tuning_mode).strip().lower() or "full" + + run_dir = Path(training_config.output_dir) + run_dir.mkdir(parents=True, exist_ok=True) + + env = OSINTEnvironment(env_config, llm=build_llm_client(env_config.llm)) + seed_tasks = list(env.tasks) + seed_questions = [task.question for task in seed_tasks] + generator_model, answerer_model = _resolve_initial_models(training_config) + rng = random.Random(env_config.seed) + + bootstrap_completions = _fallback_swarm_v2_completion_texts( + env=env, + cfg=training_config, + round_index=0, + rng=rng, + ) + rolling_generated_tasks, _, _, _ = _materialize_swarm_v2_completions( + env=env, + cfg=training_config, + completion_texts=bootstrap_completions, + round_index=0, + seen_questions=seed_questions, + ) + if not rolling_generated_tasks: + rolling_generated_tasks = list(seed_tasks[: max(1, training_config.generated_tasks_per_round)]) + + rounds_payload: list[dict[str, Any]] = [] + + for round_index in range(1, max(1, training_config.rounds) + 1): + round_dir = run_dir / f"round_{round_index:03d}" + round_dir.mkdir(parents=True, exist_ok=True) + + answerer_pre_tasks: list[TaskInstance] = [] + answerer_pre_dataset_path: Path | None = None + answerer_pre_train_result: dict[str, Any] | None = None + + if phase_schedule == "answerer_generator_answerer": + answerer_pre_tasks = _select_answerer_tasks( + seed_tasks=seed_tasks, + generated_tasks=rolling_generated_tasks, + cfg=training_config, + rng=rng, + ) + answerer_pre_rows = _build_swarm_v2_answerer_rows(env, answerer_pre_tasks, training_config) + answerer_pre_dataset_path = round_dir / "answerer_pre_dataset.json" + _save_rows(answerer_pre_dataset_path, answerer_pre_rows) + + answerer_pre_train_result = { + "model_path": answerer_model, + "global_step": 0, + "training_loss": 0.0, + "train_rows": len(answerer_pre_rows), + "skipped": effective_dry_run, + "tuning_mode": tuning_mode, + } + + if not effective_dry_run: + answerer_pre_report_to, answerer_pre_run_name = _resolve_reporting( + training_config=training_config, + phase_name="answerer-pre", + round_index=round_index, + ) + answerer_pre_reward = AnswererRewardFunction( + graph=env.graph, + pipeline_mode="swarm_v2", + parl_max_parallel_hint=training_config.swarm_v2.answerer_swarm.max_agents, + ) + answerer_pre_train_result = _train_grpo_phase( + model_name_or_path=answerer_model, + phase=training_config.answerer_phase, + rows=answerer_pre_rows, + reward_function=answerer_pre_reward, + output_dir=round_dir / f"{training_config.answerer_phase.output_subdir}_pre", + tuning_mode=tuning_mode, + lora=training_config.lora, + report_to=answerer_pre_report_to, + run_name=answerer_pre_run_name, + ) + answerer_model = str(answerer_pre_train_result["model_path"]) + if topology == "shared": + generator_model = answerer_model + + generator_rows, prompt_canonical_candidates = _build_swarm_v2_generator_rows(env, training_config, rng) + generator_dataset_path = round_dir / "generator_dataset.json" + _save_rows(generator_dataset_path, generator_rows) + + generator_train_result: dict[str, Any] = { + "model_path": generator_model, + "global_step": 0, + "training_loss": 0.0, + "train_rows": len(generator_rows), + "skipped": effective_dry_run, + "tuning_mode": tuning_mode, + "frozen_answerer_model": answerer_model, + } + + if not effective_dry_run: + generator_report_to, generator_run_name = _resolve_reporting( + training_config=training_config, + phase_name="generator", + round_index=round_index, + ) + generator_reward = GeneratorRewardFunction( + graph=env.graph, + answerer_judge=AnswererJudge( + model_name_or_path=answerer_model, + max_new_tokens=training_config.answerer_judge_max_new_tokens, + ), + weights=training_config.generator_reward_weights, + max_support_edges=training_config.swarm_v2.validation.max_support_edges, + pipeline_mode="swarm_v2", + swarm_v2_validation=training_config.swarm_v2.validation, + swarm_v2_shared_context=training_config.swarm_v2.shared_context, + parl_max_parallel_hint=training_config.swarm_v2.generator_swarm.max_agents, + ) + generator_train_result = _train_grpo_phase( + model_name_or_path=generator_model, + phase=training_config.generator_phase, + rows=generator_rows, + reward_function=generator_reward, + output_dir=round_dir / training_config.generator_phase.output_subdir, + tuning_mode=tuning_mode, + lora=training_config.lora, + report_to=generator_report_to, + run_name=generator_run_name, + ) + generator_model = str(generator_train_result["model_path"]) + if topology == "shared": + answerer_model = generator_model + + if effective_dry_run: + completion_texts = _fallback_swarm_v2_completion_texts( + env=env, + cfg=training_config, + round_index=round_index, + rng=rng, + ) + else: + completion_texts = _sample_swarm_v2_completion_texts_with_model( + env=env, + cfg=training_config, + model_name_or_path=generator_model, + prompts=[row["prompt"] for row in generator_rows], + count=max(1, training_config.generated_tasks_per_round * 2), + seen_questions=seed_questions + [task.question for task in rolling_generated_tasks], + ) + if not completion_texts: + completion_texts = _fallback_swarm_v2_completion_texts( + env=env, + cfg=training_config, + round_index=round_index, + rng=rng, + ) + + generated_tasks, validation_reports, canonical_graph_candidates, replay_traces = _materialize_swarm_v2_completions( + env=env, + cfg=training_config, + completion_texts=completion_texts, + round_index=round_index, + seen_questions=seed_questions + [task.question for task in rolling_generated_tasks], + prompt_canonical_candidates=prompt_canonical_candidates, + ) + if not generated_tasks: + fallback_completions = _fallback_swarm_v2_completion_texts( + env=env, + cfg=training_config, + round_index=round_index, + rng=rng, + ) + generated_tasks, validation_reports, canonical_graph_candidates, replay_traces = _materialize_swarm_v2_completions( + env=env, + cfg=training_config, + completion_texts=fallback_completions, + round_index=round_index, + seen_questions=seed_questions + [task.question for task in rolling_generated_tasks], + prompt_canonical_candidates=None, + ) + + if generated_tasks: + rolling_generated_tasks = list(generated_tasks) + + canonical_graph_candidates_path = round_dir / "canonical_graph_candidates.json" + replay_traces_path = round_dir / "replay_traces.json" + validation_reports_path = round_dir / "validation_reports.json" + generated_tasks_path = round_dir / "generated_tasks.json" + _save_payload(canonical_graph_candidates_path, prompt_canonical_candidates or canonical_graph_candidates) + _save_payload(replay_traces_path, replay_traces) + _save_payload(validation_reports_path, validation_reports) + _save_tasks(generated_tasks_path, generated_tasks) + + answerer_tasks = _select_answerer_tasks( + seed_tasks=seed_tasks, + generated_tasks=generated_tasks, + cfg=training_config, + rng=rng, + ) + answerer_rows = _build_swarm_v2_answerer_rows(env, answerer_tasks, training_config) + answerer_dataset_path = round_dir / "answerer_dataset.json" + _save_rows(answerer_dataset_path, answerer_rows) + + answerer_train_result: dict[str, Any] = { + "model_path": answerer_model, + "global_step": 0, + "training_loss": 0.0, + "train_rows": len(answerer_rows), + "skipped": effective_dry_run, + "tuning_mode": tuning_mode, + } + + if not effective_dry_run: + answerer_report_to, answerer_run_name = _resolve_reporting( + training_config=training_config, + phase_name="answerer", + round_index=round_index, + ) + answerer_reward = AnswererRewardFunction( + graph=env.graph, + pipeline_mode="swarm_v2", + parl_max_parallel_hint=training_config.swarm_v2.answerer_swarm.max_agents, + ) + answerer_train_result = _train_grpo_phase( + model_name_or_path=answerer_model, + phase=training_config.answerer_phase, + rows=answerer_rows, + reward_function=answerer_reward, + output_dir=round_dir / training_config.answerer_phase.output_subdir, + tuning_mode=tuning_mode, + lora=training_config.lora, + report_to=answerer_report_to, + run_name=answerer_run_name, + ) + answerer_model = str(answerer_train_result["model_path"]) + if topology == "shared": + generator_model = answerer_model + + rounds_payload.append( + { + "round": round_index, + "dry_run": effective_dry_run, + "pipeline_mode": "swarm_v2", + "phase_schedule": phase_schedule, + "generator": generator_train_result, + "answerer": answerer_train_result, + "answerer_pre": answerer_pre_train_result, + "generated_task_count": len(generated_tasks), + "answerer_task_count": len(answerer_tasks), + "answerer_pre_task_count": len(answerer_pre_tasks), + "artifacts": { + "generator_dataset": str(generator_dataset_path), + "answerer_dataset": str(answerer_dataset_path), + "generated_tasks": str(generated_tasks_path), + "canonical_graph_candidates": str(canonical_graph_candidates_path), + "replay_traces": str(replay_traces_path), + "validation_reports": str(validation_reports_path), + "answerer_pre_dataset": str(answerer_pre_dataset_path) if answerer_pre_dataset_path else "", + }, + } + ) + + final_payload = { + "dry_run": effective_dry_run, + "pipeline_mode": "swarm_v2", + "output_dir": str(run_dir), + "model_topology": topology, + "phase_schedule": phase_schedule, + "tuning_mode": tuning_mode, + "canonical_graph_mode": str(training_config.canonical_graph_mode).strip().lower() or "generate", + "rounds": rounds_payload, + "final_models": { + "generator": generator_model, + "answerer": answerer_model, + }, + "kimi_objective_mapping": { + "grouped_rollouts": "TRL GRPO num_generations", + "mean_centered_advantage": "GRPO relative reward baseline", + "token_level_clipping": "GRPO epsilon clipping over policy ratios", + "reference_regularization": "GRPO beta KL term", + "toggle_self_play": "Alternating generator and answerer rounds", + "parallel_orchestration": "PARL-inspired auxiliary reward over generator and answerer swarms", + }, + } + + summary_path = run_dir / "self_play_summary.json" + summary_path.write_text(json.dumps(final_payload, indent=2, sort_keys=True), encoding="utf-8") + final_payload["summary_path"] = str(summary_path) + return final_payload + + + +def run_adversarial_self_play( + env_config: EnvironmentConfig, + training_config: SelfPlayTrainingConfig, + dry_run: bool = False, +) -> dict[str, Any]: + if str(training_config.pipeline_mode).strip().lower() == "swarm_v2": + return _run_adversarial_self_play_swarm_v2( + env_config=env_config, + training_config=training_config, + dry_run=dry_run, + ) + + effective_dry_run = bool(dry_run or training_config.dry_run) + topology = str(training_config.model_topology).strip().lower() or "dual" + phase_schedule = str(training_config.phase_schedule).strip().lower() or "generator_answerer" + tuning_mode = str(training_config.tuning_mode).strip().lower() or "full" + + run_dir = Path(training_config.output_dir) + run_dir.mkdir(parents=True, exist_ok=True) + + env = OSINTEnvironment(env_config, llm=build_llm_client(env_config.llm)) + seed_tasks = list(env.tasks) + + generator_model, answerer_model = _resolve_initial_models(training_config) + + rng = random.Random(env_config.seed) + rounds_payload: list[dict[str, Any]] = [] + rolling_generated_tasks = _fallback_generated_tasks( + base_tasks=seed_tasks, + round_index=0, + count=training_config.generated_tasks_per_round, + rng=rng, + ) + if not rolling_generated_tasks: + rolling_generated_tasks = list(seed_tasks[: max(1, training_config.generated_tasks_per_round)]) + + for round_index in range(1, max(1, training_config.rounds) + 1): + round_dir = run_dir / f"round_{round_index:03d}" + round_dir.mkdir(parents=True, exist_ok=True) + + answerer_pre_tasks: list[TaskInstance] = [] + answerer_pre_dataset_path: Path | None = None + answerer_pre_train_result: dict[str, Any] | None = None + + if phase_schedule == "answerer_generator_answerer": + answerer_pre_tasks = _select_answerer_tasks( + seed_tasks=seed_tasks, + generated_tasks=rolling_generated_tasks, + cfg=training_config, + rng=rng, + ) + answerer_pre_rows = _build_answerer_rows(answerer_pre_tasks) + answerer_pre_dataset_path = round_dir / "answerer_pre_dataset.json" + _save_rows(answerer_pre_dataset_path, answerer_pre_rows) + + answerer_pre_train_result = { + "model_path": answerer_model, + "global_step": 0, + "training_loss": 0.0, + "train_rows": len(answerer_pre_rows), + "skipped": effective_dry_run, + "tuning_mode": tuning_mode, + } + + if not effective_dry_run: + answerer_pre_report_to, answerer_pre_run_name = _resolve_reporting( + training_config=training_config, + phase_name="answerer-pre", + round_index=round_index, + ) + answerer_pre_reward = AnswererRewardFunction(graph=env.graph) + answerer_pre_train_result = _train_grpo_phase( + model_name_or_path=answerer_model, + phase=training_config.answerer_phase, + rows=answerer_pre_rows, + reward_function=answerer_pre_reward, + output_dir=round_dir / f"{training_config.answerer_phase.output_subdir}_pre", + tuning_mode=tuning_mode, + lora=training_config.lora, + report_to=answerer_pre_report_to, + run_name=answerer_pre_run_name, + ) + answerer_model = str(answerer_pre_train_result["model_path"]) + if topology == "shared": + generator_model = answerer_model + + generator_rows = _build_generator_rows(env=env, cfg=training_config, rng=rng) + generator_dataset_path = round_dir / "generator_dataset.json" + _save_rows(generator_dataset_path, generator_rows) + + generator_train_result: dict[str, Any] = { + "model_path": generator_model, + "global_step": 0, + "training_loss": 0.0, + "train_rows": len(generator_rows), + "skipped": effective_dry_run, + "tuning_mode": tuning_mode, + } + + if not effective_dry_run: + generator_report_to, generator_run_name = _resolve_reporting( + training_config=training_config, + phase_name="generator", + round_index=round_index, + ) + generator_reward = GeneratorRewardFunction( + graph=env.graph, + answerer_judge=AnswererJudge( + model_name_or_path=answerer_model, + max_new_tokens=training_config.answerer_judge_max_new_tokens, + ), + weights=training_config.generator_reward_weights, + max_support_edges=training_config.max_support_edges, + ) + generator_train_result = _train_grpo_phase( + model_name_or_path=generator_model, + phase=training_config.generator_phase, + rows=generator_rows, + reward_function=generator_reward, + output_dir=round_dir / training_config.generator_phase.output_subdir, + tuning_mode=tuning_mode, + lora=training_config.lora, + report_to=generator_report_to, + run_name=generator_run_name, + ) + generator_model = str(generator_train_result["model_path"]) + if topology == "shared": + answerer_model = generator_model + + generated_tasks: list[TaskInstance] + if effective_dry_run: + generated_tasks = _fallback_generated_tasks( + base_tasks=seed_tasks, + round_index=round_index, + count=training_config.generated_tasks_per_round, + rng=rng, + ) + else: + generated_tasks = _sample_generated_tasks_with_model( + model_name_or_path=generator_model, + prompts=[row["prompt"] for row in generator_rows], + round_index=round_index, + count=training_config.generated_tasks_per_round, + max_support_edges=training_config.max_support_edges, + ) + if not generated_tasks: + generated_tasks = _fallback_generated_tasks( + base_tasks=seed_tasks, + round_index=round_index, + count=training_config.generated_tasks_per_round, + rng=rng, + ) + + if generated_tasks: + rolling_generated_tasks = list(generated_tasks) + + generated_tasks_path = round_dir / "generated_tasks.json" + _save_tasks(generated_tasks_path, generated_tasks) + + answerer_tasks = _select_answerer_tasks( + seed_tasks=seed_tasks, + generated_tasks=generated_tasks, + cfg=training_config, + rng=rng, + ) + answerer_rows = _build_answerer_rows(answerer_tasks) + answerer_dataset_path = round_dir / "answerer_dataset.json" + _save_rows(answerer_dataset_path, answerer_rows) + + answerer_train_result: dict[str, Any] = { + "model_path": answerer_model, + "global_step": 0, + "training_loss": 0.0, + "train_rows": len(answerer_rows), + "skipped": effective_dry_run, + "tuning_mode": tuning_mode, + } + + if not effective_dry_run: + answerer_report_to, answerer_run_name = _resolve_reporting( + training_config=training_config, + phase_name="answerer", + round_index=round_index, + ) + answerer_reward = AnswererRewardFunction(graph=env.graph) + answerer_train_result = _train_grpo_phase( + model_name_or_path=answerer_model, + phase=training_config.answerer_phase, + rows=answerer_rows, + reward_function=answerer_reward, + output_dir=round_dir / training_config.answerer_phase.output_subdir, + tuning_mode=tuning_mode, + lora=training_config.lora, + report_to=answerer_report_to, + run_name=answerer_run_name, + ) + answerer_model = str(answerer_train_result["model_path"]) + if topology == "shared": + generator_model = answerer_model + + artifacts = _RoundArtifacts( + round_index=round_index, + generator_dataset_path=str(generator_dataset_path), + answerer_dataset_path=str(answerer_dataset_path), + generated_tasks_path=str(generated_tasks_path), + ) + + rounds_payload.append( + { + "round": round_index, + "dry_run": effective_dry_run, + "pipeline_mode": "legacy", + "phase_schedule": phase_schedule, + "generator": generator_train_result, + "answerer": answerer_train_result, + "answerer_pre": answerer_pre_train_result, + "generated_task_count": len(generated_tasks), + "answerer_task_count": len(answerer_tasks), + "answerer_pre_task_count": len(answerer_pre_tasks), + "artifacts": { + "generator_dataset": artifacts.generator_dataset_path, + "answerer_dataset": artifacts.answerer_dataset_path, + "generated_tasks": artifacts.generated_tasks_path, + "answerer_pre_dataset": str(answerer_pre_dataset_path) if answerer_pre_dataset_path else "", + }, + } + ) + + final_payload = { + "dry_run": effective_dry_run, + "pipeline_mode": "legacy", + "output_dir": str(run_dir), + "model_topology": topology, + "phase_schedule": phase_schedule, + "tuning_mode": tuning_mode, + "canonical_graph_mode": str(training_config.canonical_graph_mode).strip().lower() or "generate", + "rounds": rounds_payload, + "final_models": { + "generator": generator_model, + "answerer": answerer_model, + }, + "kimi_objective_mapping": { + "grouped_rollouts": "TRL GRPO num_generations", + "mean_centered_advantage": "GRPO relative reward baseline", + "token_level_clipping": "GRPO epsilon clipping over policy ratios", + "reference_regularization": "GRPO beta KL term", + "toggle_self_play": "Alternating generator and answerer rounds", + }, + } + + summary_path = run_dir / "self_play_summary.json" + summary_path.write_text(json.dumps(final_payload, indent=2, sort_keys=True), encoding="utf-8") + final_payload["summary_path"] = str(summary_path) + + return final_payload diff --git a/src/osint_env/validation.py b/src/osint_env/validation.py new file mode 100644 index 0000000000000000000000000000000000000000..8f3e6a230805220309252a3823f72f73820640bc --- /dev/null +++ b/src/osint_env/validation.py @@ -0,0 +1,316 @@ +from __future__ import annotations + +import json +import tempfile +from dataclasses import asdict, dataclass +from pathlib import Path +from types import SimpleNamespace +from typing import Any + +from fastapi.testclient import TestClient + +from server import app +from osint_env.baselines.openai_runner import OpenAIBaselineConfig, OpenAIBaselineRunner, build_action_tools +from osint_env.config import clone_environment_config, load_seeding_config, load_shared_config +from osint_env.env.environment import OSINTEnvironment +from osint_env.env.openenv_compat import Env +from osint_env.env.reward import compute_answer_reward + + +README_PATH = Path("README.md") +DOCKERFILE_PATH = Path("Dockerfile") +OPENENV_SPEC_PATH = Path("openenv.yaml") +SHARED_CONFIG_PATH = "datasets/fixed_levels/shared_config_fixed_levels.json" +SEED_FILE_PATH = "datasets/fixed_levels/seed_fixed_levels.json" + + +@dataclass(slots=True) +class ValidationResult: + name: str + passed: bool + details: dict[str, Any] + + +def _build_environment() -> OSINTEnvironment: + shared = load_shared_config(SHARED_CONFIG_PATH) + env_cfg = clone_environment_config(shared.environment) + env_cfg.seeding = load_seeding_config(SEED_FILE_PATH) + env_cfg.llm.provider = "mock" + return OSINTEnvironment(env_cfg) + + +def check_hf_space_readiness() -> ValidationResult: + text = README_PATH.read_text(encoding="utf-8") + has_sdk = "sdk: docker" in text + has_port = "app_port: 7860" in text + has_openenv_tag = "- openenv" in text + client = TestClient(app) + health = client.get("/healthz") + dashboard = client.get("/api/environment") + spec = client.get("/openenv.yaml") + passed = all( + [ + README_PATH.exists(), + DOCKERFILE_PATH.exists(), + OPENENV_SPEC_PATH.exists(), + has_sdk, + has_port, + has_openenv_tag, + health.status_code == 200, + dashboard.status_code == 200, + spec.status_code == 200, + ] + ) + return ValidationResult( + name="hf_space_readiness", + passed=passed, + details={ + "readme_exists": README_PATH.exists(), + "dockerfile_exists": DOCKERFILE_PATH.exists(), + "openenv_spec_exists": OPENENV_SPEC_PATH.exists(), + "has_sdk_docker": has_sdk, + "has_app_port": has_port, + "has_openenv_tag": has_openenv_tag, + "healthz_status": health.status_code, + "environment_status": dashboard.status_code, + "openenv_spec_status": spec.status_code, + }, + ) + + +def check_openenv_spec_compliance() -> ValidationResult: + env = _build_environment() + obs = env.reset() + client = TestClient(app) + reset = client.post("/openenv/reset", json={"task_index": 0}) + step = client.post( + "/openenv/step", + json={ + "session_id": reset.json()["session_id"] if reset.status_code == 200 else "", + "action_type": "ANSWER", + "payload": {"answer": "unknown"}, + }, + ) + state = client.get(f"/openenv/state/{reset.json()['session_id']}") if reset.status_code == 200 else None + passed = all( + [ + isinstance(env, Env), + hasattr(env, "reset"), + hasattr(env, "step"), + env.name == "OSINTEnvironment", + env.state_space == "json-observation", + env.action_space == ["CALL_TOOL", "ADD_EDGE", "ANSWER"], + env.episode_max_length == env.config.max_steps, + isinstance(obs.task, dict), + "question" in obs.task, + reset.status_code == 200, + step.status_code == 200, + state is not None and state.status_code == 200, + ] + ) + return ValidationResult( + name="openenv_spec_compliance", + passed=passed, + details={ + "env_class": type(env).__name__, + "state_space": env.state_space, + "action_space": list(env.action_space), + "episode_max_length": env.episode_max_length, + "task_keys": sorted(obs.task.keys()), + "reset_status": reset.status_code, + "step_status": step.status_code, + "state_status": 0 if state is None else state.status_code, + }, + ) + + +class _FakeMessage: + def __init__(self, answer: str): + self.content = "" + self.tool_calls = [ + SimpleNamespace( + id="fake_tool_call_0", + function=SimpleNamespace(name="submit_answer", arguments=json.dumps({"answer": answer})), + ) + ] + + +class _FakeCompletion: + def __init__(self, answer: str): + self.choices = [SimpleNamespace(message=_FakeMessage(answer))] + self.usage = SimpleNamespace(prompt_tokens=0, completion_tokens=0, total_tokens=0) + self.system_fingerprint = "validation_fp" + + +class _FakeChatCompletions: + def create(self, **kwargs: Any) -> _FakeCompletion: + messages = list(kwargs.get("messages", [])) + initial_observation = {} + for message in messages: + if message.get("role") == "user": + try: + initial_observation = json.loads(message.get("content", "{}")) + except json.JSONDecodeError: + initial_observation = {} + break + task_id = ((initial_observation.get("task") or {}).get("task_id")) or "" + env = _build_environment() + task = next((task for task in env.tasks if task.task_id == task_id), None) + answer = task.answer if task is not None else "unknown" + return _FakeCompletion(answer) + + +class _FakeOpenAIClient: + def __init__(self) -> None: + self.chat = SimpleNamespace(completions=_FakeChatCompletions()) + + +def _run_fake_baseline_once(output_dir: Path) -> dict[str, Any]: + config = OpenAIBaselineConfig( + api_key="validation", + episodes=3, + max_steps=4, + append_leaderboard=False, + output_path=str(output_dir / "baseline.json"), + dashboard_path=str(output_dir / "baseline.html"), + leaderboard_path=str(output_dir / "leaderboard.json"), + run_name="validation_baseline", + ) + runner = OpenAIBaselineRunner.__new__(OpenAIBaselineRunner) + runner.config = config + runner.client = _FakeOpenAIClient() + runner.tools = build_action_tools() + return runner.run() + + +def check_baseline_reproducibility() -> ValidationResult: + with tempfile.TemporaryDirectory() as left_dir_name, tempfile.TemporaryDirectory() as right_dir_name: + left = _run_fake_baseline_once(Path(left_dir_name)) + right = _run_fake_baseline_once(Path(right_dir_name)) + + left_signature = { + "summary": left["summary"], + "episodes": [ + { + "task_id": episode["task_id"], + "task_answer": episode["task_answer"], + "agent_answer": episode["agent_answer"], + "success": episode["success"], + "steps": episode["steps"], + } + for episode in left["episodes"] + ], + } + right_signature = { + "summary": right["summary"], + "episodes": [ + { + "task_id": episode["task_id"], + "task_answer": episode["task_answer"], + "agent_answer": episode["agent_answer"], + "success": episode["success"], + "steps": episode["steps"], + } + for episode in right["episodes"] + ], + } + passed = left_signature == right_signature + return ValidationResult( + name="baseline_reproducibility", + passed=passed, + details={ + "episodes_checked": len(left_signature["episodes"]), + "left_signature": left_signature, + "right_signature": right_signature, + }, + ) + + +def check_task_and_grader_coverage() -> ValidationResult: + env = _build_environment() + tasks = env.tasks + grader_checks: list[dict[str, Any]] = [] + distinct_types = sorted({str(task.task_type) for task in tasks}) + difficulty_buckets: dict[str, Any] = {} + for idx, task in enumerate(tasks): + token = str((task.metadata or {}).get("difficulty", "")).strip().lower() + if token in {"mid", "m"}: + token = "medium" + if token in {"high", "h"}: + token = "hard" + if token not in {"easy", "medium", "hard"}: + if idx < 10: + token = "easy" + elif idx < 20: + token = "medium" + else: + token = "hard" + difficulty_buckets.setdefault(token, task) + + for difficulty in ["easy", "medium", "hard"]: + task = difficulty_buckets.get(difficulty) + if task is None: + continue + correct = compute_answer_reward( + proposed_answer=task.answer, + task=task, + pred_edges=list(task.supporting_edges), + tool_outputs=[], + step_count=1, + model=env.reward_model, + difficulty=difficulty, + ) + wrong = compute_answer_reward( + proposed_answer="unknown", + task=task, + pred_edges=[], + tool_outputs=[], + step_count=1, + model=env.reward_model, + difficulty=difficulty, + ) + grader = dict(task.metadata.get("grader", {})) if isinstance(task.metadata, dict) else {} + grader_checks.append( + { + "difficulty": difficulty, + "task_id": task.task_id, + "task_type": task.task_type, + "support_edges": len(task.supporting_edges), + "has_grader": bool(grader), + "correct_reward": correct.total, + "wrong_reward": wrong.total, + "grader_prefers_correct": correct.total > wrong.total, + } + ) + passed = ( + len(tasks) >= 3 + and len(distinct_types) >= 3 + and len(grader_checks) >= 3 + and all( + row["support_edges"] > 0 and row["grader_prefers_correct"] and row["has_grader"] + for row in grader_checks + ) + ) + return ValidationResult( + name="task_and_grader_coverage", + passed=passed, + details={ + "task_count": len(tasks), + "distinct_task_types": distinct_types, + "grader_checks": grader_checks, + }, + ) + + +def run_validation_suite() -> dict[str, Any]: + results = [ + check_hf_space_readiness(), + check_openenv_spec_compliance(), + check_baseline_reproducibility(), + check_task_and_grader_coverage(), + ] + passed = all(result.passed for result in results) + return { + "passed": passed, + "checks": [asdict(result) for result in results], + } diff --git a/src/osint_env/viz/__init__.py b/src/osint_env/viz/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0c98ce2d59d35418cfb871ff0eb5d9c72a3e19c6 --- /dev/null +++ b/src/osint_env/viz/__init__.py @@ -0,0 +1,3 @@ +from osint_env.viz.dashboard import export_dashboard + +__all__ = ["export_dashboard"] diff --git a/src/osint_env/viz/dashboard.py b/src/osint_env/viz/dashboard.py new file mode 100644 index 0000000000000000000000000000000000000000..3e033381d99306acdbc7a2a2cb12e7574135b1c5 --- /dev/null +++ b/src/osint_env/viz/dashboard.py @@ -0,0 +1,801 @@ +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +from osint_env.data.generator import PlatformViews +from osint_env.domain.models import CanonicalGraph, Edge, TaskInstance +from osint_env.env.environment import OSINTEnvironment + + +def _safe_label(value: str, fallback: str) -> str: + text = str(value).strip() + return text if text else fallback + + +def _canonical_graph_payload(graph: CanonicalGraph) -> dict[str, Any]: + nodes = [] + for node in graph.nodes.values(): + attrs = node.attrs or {} + title = "\\n".join(f"{k}: {v}" for k, v in attrs.items()) + label = _safe_label(str(attrs.get("name") or attrs.get("handle") or node.node_id), node.node_id) + nodes.append( + { + "id": node.node_id, + "label": label, + "group": str(node.node_type.value), + "title": title, + "attrs": attrs, + } + ) + + edges = [] + for idx, edge in enumerate(graph.edges): + edges.append( + { + "id": f"c_{idx}", + "from": edge.src, + "to": edge.dst, + "label": edge.rel, + "arrows": "to", + "color": "#1f2937", + "width": 1, + "confidence": float(edge.confidence), + "status": "canonical", + } + ) + return {"nodes": nodes, "edges": edges} + + +def _edge_key(edge: Edge) -> tuple[str, str, str]: + return (edge.src, edge.rel, edge.dst) + + +def _episode_graph_payload(pred_edges: list[Edge], truth_edges: list[Edge], graph: CanonicalGraph) -> dict[str, Any]: + pred = {_edge_key(e): e for e in pred_edges} + truth = {_edge_key(e): e for e in truth_edges} + + all_nodes = set() + all_keys = set(pred) | set(truth) + for src, _, dst in all_keys: + all_nodes.add(src) + all_nodes.add(dst) + + nodes = [] + for node_id in sorted(all_nodes): + node = graph.nodes.get(node_id) + if node is None: + nodes.append({"id": node_id, "label": node_id, "group": "episode", "attrs": {}}) + continue + attrs = node.attrs or {} + label = _safe_label(str(attrs.get("name") or attrs.get("handle") or node_id), node_id) + nodes.append({"id": node_id, "label": label, "group": str(node.node_type.value), "attrs": attrs}) + + edges = [] + for idx, key in enumerate(sorted(all_keys)): + src, rel, dst = key + in_pred = key in pred + in_truth = key in truth + if in_pred and in_truth: + color = "#16a34a" + dashes = False + status = "matched" + elif in_pred: + color = "#2563eb" + dashes = False + status = "pred_only" + else: + color = "#f59e0b" + dashes = True + status = "truth_only" + edges.append( + { + "id": f"e_{idx}", + "from": src, + "to": dst, + "label": rel, + "arrows": "to", + "color": color, + "dashes": dashes, + "width": 2, + "status": status, + "confidence": float((pred.get(key) or truth.get(key) or Edge(src, rel, dst)).confidence), + } + ) + + return {"nodes": nodes, "edges": edges} + + +def _views_payload(views: PlatformViews) -> dict[str, Any]: + return { + "microblog_posts": views.microblog_posts, + "forum_threads": views.forum_threads, + "profiles": views.profiles, + } + + +def _leaderboard_payload(records: list[dict[str, Any]]) -> list[dict[str, Any]]: + ranked = sorted(records, key=lambda r: float(r.get("metrics", {}).get("leaderboard_score", 0.0)), reverse=True) + return ranked[:200] + + +def export_dashboard( + env: OSINTEnvironment, + evaluation: dict[str, Any], + leaderboard_records: list[dict[str, Any]], + output_path: str, +) -> str: + summary = evaluation.get("summary", evaluation) + episodes = evaluation.get("episodes", []) + + task: TaskInstance | None = env.state.task if env.state else None + truth_edges = task.supporting_edges if task else [] + pred_edges = env.memory_graph.edges if env.state else [] + + episode_graphs: list[dict[str, Any]] = [] + for episode in episodes: + pred_from_eval = [Edge(str(e.get("src", "")), str(e.get("rel", "")), str(e.get("dst", "")), float(e.get("confidence", 1.0))) for e in episode.get("pred_edges", []) if isinstance(e, dict)] + truth_from_eval = [Edge(str(e.get("src", "")), str(e.get("rel", "")), str(e.get("dst", "")), float(e.get("confidence", 1.0))) for e in episode.get("truth_edges", []) if isinstance(e, dict)] + if pred_from_eval or truth_from_eval: + episode_graphs.append(_episode_graph_payload(pred_from_eval, truth_from_eval, env.graph)) + + if not episode_graphs: + episode_graphs.append(_episode_graph_payload(pred_edges, truth_edges, env.graph)) + + payload = { + "summary": summary, + "episodes": episodes, + "leaderboard": _leaderboard_payload(leaderboard_records), + "canonical_graph": _canonical_graph_payload(env.graph), + "episode_graphs": episode_graphs, + "episode_graph": episode_graphs[-1], + "views": _views_payload(env.views), + "task": { + "task_id": task.task_id if task else "n/a", + "task_type": task.task_type if task else "n/a", + "question": task.question if task else "n/a", + "answer": task.answer if task else "n/a", + }, + } + + html = f""" + + + + + OSINT Environment Dashboard + + + + + + + + + +
    +
    +
    +

    OSINT Benchmark Dashboard

    +

    Interactive explorer for canonical knowledge graph, episode traces, source platform records, and benchmark ranking.

    +
    +
    +
    +
    +

    Episode Explorer

    +
    + + +
    +
    + + +
    +
    Task ID:
    +
    Task Type:
    +
    Question
    +
    +
    Ground Truth Answer:
    +
    Agent Answer:
    +
    Correct:
    +
    +
    + +
    +
    +
    +

    Graph Controls

    +
    + + + + + + + +
    +
    +
    +

    Node Types

    +
    +
    +
    + +
    +

    Graph Explorer

    +
    +
    Layer: Canonical Graph
    +
    +
    +
    + matched edge + predicted only + truth only +
    +
    + +
    +
    +

    Node Inspector

    +
    Click a node to inspect attributes and neighbors.
    +
    +
    +

    Edge Inspector

    +
    Click an edge to inspect relation details.
    +
    +
    +
    + +
    +
    +

    Original Database Explorer

    +
    +
    + + +
    +
    +
    + +
    +

    Selected Source Record

    +
    Click a row in the database table to inspect full JSON.
    +
    +
    + +
    +
    +

    Benchmark Summary Radar

    +
    +
    +
    +

    Episode Reward and Graph F1

    +
    +
    +
    + +
    +

    Benchmark Leaderboard

    +
    + + +
    +
    +
    +
    + + + + +""" + + out = Path(output_path) + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(html, encoding="utf-8") + return str(out) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..c4b4171d9ef25fa2362b246ff84caed0e31ff8fb --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +SRC = ROOT / "src" + +if str(SRC) not in sys.path: + sys.path.insert(0, str(SRC)) + diff --git a/tests/test_cli_eval_outputs.py b/tests/test_cli_eval_outputs.py new file mode 100644 index 0000000000000000000000000000000000000000..ba83a9036f0fd5c32a193608c737baeb33cb18ef --- /dev/null +++ b/tests/test_cli_eval_outputs.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +import argparse + +from osint_env import cli +from osint_env.domain.models import EnvironmentConfig + + +class _DummyParser: + def __init__(self, namespace: argparse.Namespace): + self._namespace = namespace + + def parse_args(self) -> argparse.Namespace: + return self._namespace + + +class _DummyEnv: + def __init__(self, config: EnvironmentConfig, llm=None): + self.config = config + self.llm = llm + + +def test_eval_exports_dashboard_and_evaluation(monkeypatch, tmp_path, capsys): + dashboard_path = tmp_path / "eval_dashboard.html" + eval_path = tmp_path / "latest_evaluation.json" + + args = argparse.Namespace( + cmd="eval", + episodes=1, + leaderboard="", + dashboard="", + dashboard_dir="", + evaluation="", + ) + + runtime = { + "default_episodes": 20, + "leaderboard_path": str(tmp_path / "leaderboard.json"), + "dashboard_path": str(dashboard_path), + "sweep_dashboard_dir": str(tmp_path / "sweep"), + } + + evaluation_payload = { + "summary": { + "avg_reward": 0.5, + "avg_graph_f1": 0.4, + "task_success_rate": 1.0, + "tool_efficiency": 0.7, + "avg_steps_to_solution": 3.0, + "deanonymization_accuracy": 1.0, + "leaderboard_score": 0.8, + }, + "episodes": [ + { + "task_id": "metaqa_1-hop_train_0", + "task_type": "metaqa_1-hop", + "question": "who directed [inception]?", + "task_answer": "christopher nolan", + "agent_answer": "christopher nolan", + "graph_f1": 1.0, + "reward": 1.0, + "steps": 2, + "tool_calls": 1, + "success": 1, + } + ], + } + + calls: dict[str, object] = {} + + monkeypatch.setattr(cli, "build_parser", lambda: _DummyParser(args)) + monkeypatch.setattr(cli, "_resolve_environment_config", lambda _args: (EnvironmentConfig(), runtime)) + monkeypatch.setattr(cli, "build_llm_client", lambda _cfg: object()) + monkeypatch.setattr(cli, "OSINTEnvironment", _DummyEnv) + monkeypatch.setattr(cli, "run_evaluation", lambda env, episodes, return_details, llm: evaluation_payload) + + def _save(path: str, payload: dict) -> None: + calls["save_path"] = path + calls["save_payload"] = payload + + def _export(env, evaluation, leaderboard_records, output_path): + calls["export_output_path"] = output_path + calls["export_eval"] = evaluation + calls["export_leaderboard"] = leaderboard_records + return output_path + + monkeypatch.setattr(cli, "_save_evaluation", _save) + monkeypatch.setattr(cli, "load_leaderboard", lambda _path: []) + monkeypatch.setattr(cli, "export_dashboard", _export) + + monkeypatch.setattr(cli, "DEFAULT_EVALUATION_PATH", str(eval_path)) + + cli.main() + + assert calls["save_path"] == str(eval_path) + assert calls["save_payload"] == evaluation_payload + assert calls["export_output_path"] == str(dashboard_path) + assert calls["export_eval"] == evaluation_payload + assert calls["export_leaderboard"] == [] + + output = capsys.readouterr().out + assert '"avg_reward": 0.5' in output + assert '"episodes"' not in output diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000000000000000000000000000000000000..f08353a93f0dc6721e499c0a7362e2809312ca20 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,87 @@ +import json +from pathlib import Path + +from osint_env.config.shared import load_seeding_config, load_shared_config + + +def test_shared_config_defaults_when_file_missing(): + config = load_shared_config("/tmp/does_not_exist_for_osint_config.json") + assert config.environment.max_steps > 0 + assert config.runtime.default_episodes > 0 + + +def test_shared_config_parses_swarm_and_seeding(tmp_path: Path): + path = tmp_path / "shared.json" + path.write_text( + json.dumps( + { + "environment": {"seed": 19, "max_steps": 9}, + "dataset": { + "mode": "metaqa", + "metaqa_root": "metaQA", + "metaqa_kb_path": "metaQA/kb.txt", + "metaqa_variant": "vanilla", + "metaqa_hops": ["1-hop", "2-hop", "3-hop"], + "metaqa_splits": ["train", "dev", "test"], + }, + "swarm": {"enabled": True, "max_agents": 3, "max_breadth": 2, "max_width": 2, "max_depth": 2}, + "seeding": { + "seeded_questions": [ + { + "question": "Which canonical user owns alias alias_seed_001?", + "answer": "user_seed_001", + } + ], + "llm_generation_parallel": True, + "llm_generation_workers": 4, + "llm_generation_retries": 3, + "allow_template_fallback_on_llm_failure": False + }, + "runtime": {"default_episodes": 5}, + "llm": {"provider": "ollama", "model": "qwen3:2b", "timeout_seconds": 333}, + } + ), + encoding="utf-8", + ) + + config = load_shared_config(path) + assert config.environment.seed == 19 + assert config.environment.swarm.enabled is True + assert config.environment.swarm.max_width == 2 + assert config.environment.dataset_mode == "metaqa" + assert config.environment.metaqa_root == "metaQA" + assert config.environment.metaqa_kb_path == "metaQA/kb.txt" + assert config.environment.metaqa_variant == "vanilla" + assert config.environment.metaqa_hops == ["1-hop", "2-hop", "3-hop"] + assert config.environment.metaqa_splits == ["train", "dev", "test"] + assert len(config.environment.seeding.seeded_questions) == 1 + assert config.runtime.default_episodes == 5 + assert config.environment.llm.provider == "ollama" + assert config.environment.llm.model == "qwen3:2b" + assert config.environment.llm.timeout_seconds == 333 + assert config.environment.seeding.llm_generation_parallel is True + assert config.environment.seeding.llm_generation_workers == 4 + assert config.environment.seeding.llm_generation_retries == 3 + assert config.environment.seeding.allow_template_fallback_on_llm_failure is False + + +def test_load_seeding_config_supports_top_level_object(tmp_path: Path): + path = tmp_path / "seeding.json" + path.write_text( + json.dumps( + { + "seeded_nodes": [ + {"node_id": "alias_seed_1", "node_type": "alias", "attrs": {"handle": "@seed"}}, + {"node_id": "user_seed_1", "node_type": "user", "attrs": {"name": "Seed"}}, + ], + "seeded_edges": [{"src": "alias_seed_1", "rel": "alias_of", "dst": "user_seed_1"}], + "seeded_questions": [{"question": "Which canonical user owns alias alias_seed_1?", "answer": "user_seed_1"}], + } + ), + encoding="utf-8", + ) + + seeding = load_seeding_config(path) + assert len(seeding.seeded_nodes) == 2 + assert len(seeding.seeded_edges) == 1 + assert seeding.seeded_questions[0].answer == "user_seed_1" diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py new file mode 100644 index 0000000000000000000000000000000000000000..8b396dae7e7b600524d9bc4691fb678940820fae --- /dev/null +++ b/tests/test_dashboard.py @@ -0,0 +1,26 @@ +from pathlib import Path + +from osint_env.domain.models import EnvironmentConfig +from osint_env.env.environment import OSINTEnvironment +from osint_env.viz import export_dashboard + + +def test_dashboard_export(tmp_path: Path): + env = OSINTEnvironment(EnvironmentConfig(seed=9, n_users=14)) + env.reset() + + out = tmp_path / "dashboard.html" + path = export_dashboard( + env=env, + evaluation={"summary": {"leaderboard_score": 0.0, "task_success_rate": 0.0, "avg_graph_f1": 0.0, "tool_efficiency": 0.0, "deanonymization_accuracy": 0.0, "avg_reward": 0.0}, "episodes": []}, + leaderboard_records=[], + output_path=str(out), + ) + + assert path.endswith("dashboard.html") + text = out.read_text(encoding="utf-8") + assert "OSINT Benchmark Dashboard" in text + assert "Canonical Graph" in text + assert "Original Database Explorer" in text + assert "Benchmark Leaderboard" in text + assert "Episode Explorer" in text diff --git a/tests/test_environment.py b/tests/test_environment.py new file mode 100644 index 0000000000000000000000000000000000000000..1a36bd9e6c0404640691a90868f6152abcb13a7d --- /dev/null +++ b/tests/test_environment.py @@ -0,0 +1,39 @@ +from osint_env.domain.models import Action, ActionType, EnvironmentConfig +from osint_env.env.environment import OSINTEnvironment + + +def test_episode_flow(): + env = OSINTEnvironment(EnvironmentConfig(max_steps=5, seed=5)) + obs = env.reset() + assert "question" in obs.task + assert isinstance(obs.task.get("grader"), dict) + assert "type" in obs.task["grader"] + obs, r1, done, _ = env.step(Action(ActionType.CALL_TOOL, {"tool_name": "search_posts", "args": {"query": "Update"}})) + assert done is False + assert isinstance(r1, float) + _, r2, done, info = env.step(Action(ActionType.ANSWER, {"answer": "unknown"})) + assert done is True + assert "total_reward" in info + assert isinstance(r2, float) + + +def test_search_memory_tool_returns_results_after_tool_use(): + env = OSINTEnvironment(EnvironmentConfig(max_steps=6, seed=5)) + env.reset() + env.step(Action(ActionType.CALL_TOOL, {"tool_name": "search_posts", "args": {"query": "Update"}})) + obs, reward, done, _ = env.step( + Action(ActionType.CALL_TOOL, {"tool_name": "search_memory", "args": {"query": "Update", "k": 3}}) + ) + assert done is False + assert isinstance(reward, float) + assert obs.tool_outputs[-1]["tool"] == "search_memory" + assert obs.tool_outputs[-1]["output"]["count"] >= 1 + + +def test_invalid_tool_call_does_not_crash_episode(): + env = OSINTEnvironment(EnvironmentConfig(max_steps=4, seed=8)) + env.reset() + _, reward, done, info = env.step(Action(ActionType.CALL_TOOL, {"tool_name": "no_such_tool", "args": {}})) + assert done is False + assert reward < 0 + assert "invalid_tool_penalty" in info["reward_components"] diff --git a/tests/test_eval.py b/tests/test_eval.py new file mode 100644 index 0000000000000000000000000000000000000000..ad29cfb3eb9f633214a478667fa9dbbb9bb1d036 --- /dev/null +++ b/tests/test_eval.py @@ -0,0 +1,33 @@ +from osint_env.domain.models import EnvironmentConfig, SwarmConfig +from osint_env.env.environment import OSINTEnvironment +from osint_env.eval.runner import run_evaluation + + +def test_eval_runner(): + env = OSINTEnvironment(EnvironmentConfig(seed=17)) + result = run_evaluation(env, episodes=3) + assert "task_success_rate" in result + assert "deanonymization_accuracy" in result + assert "leaderboard_score" in result + assert "avg_knowledge_indexing_reward" in result + + +def test_eval_runner_swarm_mode(): + env = OSINTEnvironment( + EnvironmentConfig(seed=17, swarm=SwarmConfig(enabled=True, max_agents=3, max_breadth=2, max_width=2, max_depth=2)) + ) + result = run_evaluation(env, episodes=2) + assert "spawn_signal" in result + assert "avg_spawn_count" in result + + +def test_eval_runner_details_include_episode_answers(): + env = OSINTEnvironment(EnvironmentConfig(seed=17)) + result = run_evaluation(env, episodes=2, return_details=True) + assert "episodes" in result + assert len(result["episodes"]) == 2 + + row = result["episodes"][0] + assert "question" in row + assert "task_answer" in row + assert "agent_answer" in row diff --git a/tests/test_fixed_levels_dataset.py b/tests/test_fixed_levels_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..50e834694b01613c20a9c176f4b41b02dc7d99fb --- /dev/null +++ b/tests/test_fixed_levels_dataset.py @@ -0,0 +1,18 @@ +import json +from collections import Counter +from pathlib import Path + + +def test_fixed_levels_seed_has_30_questions_and_target_node_spans(): + path = Path("datasets/fixed_levels/seed_fixed_levels.json") + payload = json.loads(path.read_text(encoding="utf-8")) + questions = payload["seeding"]["seeded_questions"] + + counts = Counter(q["metadata"]["difficulty"] for q in questions) + assert counts == {"easy": 10, "mid": 10, "high": 10} + + mid_support_nodes = [int(q["metadata"]["support_nodes"]) for q in questions if q["metadata"]["difficulty"] == "mid"] + high_support_nodes = [int(q["metadata"]["support_nodes"]) for q in questions if q["metadata"]["difficulty"] == "high"] + + assert all(15 <= value <= 20 for value in mid_support_nodes) + assert all(48 <= value <= 55 for value in high_support_nodes) diff --git a/tests/test_generator.py b/tests/test_generator.py new file mode 100644 index 0000000000000000000000000000000000000000..8e603f4b4d9da3dd184371ed14afca8a6a9d9c2d --- /dev/null +++ b/tests/test_generator.py @@ -0,0 +1,164 @@ +import json +import re +from threading import Lock + +from osint_env.data.generator import ( + DatasetGenerator, + build_swarm_v2_canonical_subgraph, + build_swarm_v2_path_candidates, + build_swarm_v2_tool_trace, + emit_swarm_v2_question, + select_swarm_v2_answer, + trace_swarm_v2_path, +) +from osint_env.domain.models import EnvironmentConfig +from osint_env.llm.interface import LLMResponse + + +class SharedContextLLM: + def __init__(self): + self.prompts: list[str] = [] + self._lock = Lock() + + def generate(self, messages, tools): + prompt = str(messages[0].get("content", "")) if messages else "" + with self._lock: + self.prompts.append(prompt) + + if "SEED_GRAPH_EXPANSION_AGENT" in prompt: + worker_match = re.search(r"worker_id:\s*(\d+)", prompt) + worker_idx = int(worker_match.group(1)) if worker_match else 0 + payload = { + "edges": [ + { + "src": "user_0", + "rel": f"llm_rel_{worker_idx}", + "dst": "user_1", + "confidence": 0.9, + } + ] + } + return LLMResponse(content=json.dumps(payload), tool_calls=[]) + + if "SEED_TASK_EXPANSION_AGENT" in prompt: + worker_match = re.search(r"worker_id:\s*(\d+)", prompt) + worker_idx = int(worker_match.group(1)) if worker_match else 0 + budget_match = re.search(r"task_budget:\s*(\d+)", prompt) + task_budget = int(budget_match.group(1)) if budget_match else 1 + tasks = [] + for local_idx in range(max(1, task_budget)): + tasks.append( + { + "task_type": "identity_resolution", + "question": f"Which canonical user is tied to alias alias_seed_{worker_idx}_{local_idx}?", + "answer": "user_1", + "supporting_edges": [ + { + "src": "alias_seed_0", + "rel": "alias_of", + "dst": "user_1", + "confidence": 0.95, + } + ], + } + ) + payload = {"tasks": tasks} + return LLMResponse(content=json.dumps(payload), tool_calls=[]) + + return LLMResponse(content="{}", tool_calls=[]) + + +def test_generator_outputs(): + gen = DatasetGenerator(EnvironmentConfig(n_users=20, seed=11)) + graph = gen.build_canonical_graph() + views = gen.build_platform_views(graph) + tasks = gen.generate_tasks(graph, views, count=5) + assert len(graph.nodes) >= 20 + assert len(views.microblog_posts) >= 20 + assert len(tasks) == 5 + + +def test_seeded_views_include_seeded_posts_and_threads(): + from osint_env.config import clone_environment_config, load_seeding_config, load_shared_config + + shared = load_shared_config("datasets/fixed_levels/shared_config_fixed_levels.json") + cfg = clone_environment_config(shared.environment) + cfg.seeding = load_seeding_config("datasets/fixed_levels/seed_fixed_levels.json") + cfg.llm.provider = "mock" + + gen = DatasetGenerator(cfg) + graph = gen.build_canonical_graph() + views = gen.build_platform_views(graph) + + seeded_post = next((post for post in views.microblog_posts if post["post_id"] == "post_midnight_manifest"), None) + seeded_thread = next((thread for thread in views.forum_threads if thread["thread_id"] == "thr_supply_leak"), None) + + assert seeded_post is not None + assert "loc_dockyard17" in seeded_post["references"] + assert seeded_thread is not None + assert "org_northbridge_logistics" in seeded_thread["references"] + + +def test_graph_generation_uses_parallel_shared_context_workers(): + cfg = EnvironmentConfig(n_users=12, seed=9) + cfg.seeding.llm_generate_remaining_graph = True + cfg.seeding.llm_generated_edge_budget = 4 + cfg.seeding.llm_generate_remaining_tasks = False + cfg.seeding.llm_generation_parallel = True + cfg.seeding.llm_generation_workers = 3 + cfg.seeding.llm_generation_retries = 1 + cfg.seeding.allow_template_fallback_on_llm_failure = False + + llm = SharedContextLLM() + gen = DatasetGenerator(cfg, llm=llm) + graph = gen.build_canonical_graph() + + assert any(edge.rel.startswith("llm_rel_") for edge in graph.edges) + graph_prompts = [prompt for prompt in llm.prompts if "SEED_GRAPH_EXPANSION_AGENT" in prompt] + assert len(graph_prompts) >= 2 + assert all("SHARED_CONTEXT" in prompt for prompt in graph_prompts) + + +def test_task_generation_uses_parallel_shared_context_workers(): + cfg = EnvironmentConfig(n_users=12, seed=13) + cfg.seeding.llm_generate_remaining_graph = False + cfg.seeding.llm_generate_remaining_tasks = True + cfg.seeding.llm_generated_task_budget = 4 + cfg.seeding.llm_generation_parallel = True + cfg.seeding.llm_generation_workers = 3 + cfg.seeding.llm_generation_retries = 1 + cfg.seeding.allow_template_fallback_on_llm_failure = False + + llm = SharedContextLLM() + gen = DatasetGenerator(cfg, llm=llm) + graph = gen.build_canonical_graph() + views = gen.build_platform_views(graph) + tasks = gen.generate_tasks(graph, views, count=4) + + assert len(tasks) == 4 + assert any(task.metadata.get("shared_context") for task in tasks) + task_prompts = [prompt for prompt in llm.prompts if "SEED_TASK_EXPANSION_AGENT" in prompt] + assert len(task_prompts) >= 2 + assert all("SHARED_CONTEXT" in prompt for prompt in task_prompts) + + +def test_swarm_v2_path_tools_replay_a_valid_multi_hop_trace(): + gen = DatasetGenerator(EnvironmentConfig(n_users=20, seed=17)) + graph = gen.build_canonical_graph() + candidates = build_swarm_v2_path_candidates(graph, gen.rng, count=4, min_hops=2, max_hops=3) + + assert candidates + traced = trace_swarm_v2_path(graph, candidates[0]) + assert traced + assert len(traced) >= 2 + + question = emit_swarm_v2_question(traced) + answer = select_swarm_v2_answer(traced) + tool_trace = build_swarm_v2_tool_trace(graph, traced) + canonical = build_swarm_v2_canonical_subgraph(graph, traced, max_extra_edges=2) + + assert question.startswith("If you start at") + assert answer == traced[-1].dst + assert any(call["tool_name"] == "trace_path" for call in tool_trace) + assert canonical["path"] + assert canonical["answer"] == answer diff --git a/tests/test_inference.py b/tests/test_inference.py new file mode 100644 index 0000000000000000000000000000000000000000..7059162c76bfda1331382d1723089c0318449e74 --- /dev/null +++ b/tests/test_inference.py @@ -0,0 +1,74 @@ +import inference +from inference import _format_action, _looks_like_placeholder_api_key, _tool_result_message +from osint_env.domain.models import EnvironmentConfig + + +def test_placeholder_api_key_detection(): + assert _looks_like_placeholder_api_key("your_openai_api_key") is True + assert _looks_like_placeholder_api_key("sk-your-real-openai-key") is True + assert _looks_like_placeholder_api_key("replace-me") is True + assert _looks_like_placeholder_api_key("sk-proj-realistic-looking-token") is False + + +def test_tool_result_message_reuses_assistant_tool_call_id(): + assistant_message = { + "role": "assistant", + "content": "", + "tool_calls": [ + { + "id": "call_123", + "type": "function", + "function": {"name": "get_post", "arguments": "{\"post_id\":\"post_midnight_manifest\"}"}, + } + ], + } + result = {"reward": 0.1, "done": False} + tool_message = _tool_result_message(assistant_message, result) + assert tool_message is not None + assert tool_message["tool_call_id"] == "call_123" + assert tool_message["role"] == "tool" + + +def test_action_formatter_matches_single_line_style(): + assert _format_action({"action_type": "ANSWER", "payload": {"answer": "user_bharat"}}) == "answer(user_bharat)" + assert _format_action( + { + "action_type": "CALL_TOOL", + "payload": {"tool_name": "get_post", "args": {"post_id": "post_midnight_manifest"}}, + } + ) == "get_post(post_id=post_midnight_manifest)" + + +def test_resolve_environment_config_applies_metaqa_overrides(monkeypatch): + base_cfg = EnvironmentConfig() + base_cfg.dataset_mode = "canonical" + + monkeypatch.setattr(inference, "load_shared_config", lambda _path: type("S", (), {"environment": base_cfg})()) + monkeypatch.setattr(inference, "clone_environment_config", lambda cfg: cfg) + monkeypatch.setattr(inference, "SEED_FILE", "") + + monkeypatch.setattr(inference, "DATASET_MODE", "metaqa") + monkeypatch.setattr(inference, "METAQA_ROOT", "metaQA") + monkeypatch.setattr(inference, "METAQA_KB_PATH", "metaQA/kb.txt") + monkeypatch.setattr(inference, "METAQA_VARIANT", "vanilla") + monkeypatch.setattr(inference, "METAQA_HOPS_RAW", "1-hop,2-hop,3-hop") + monkeypatch.setattr(inference, "METAQA_SPLITS_RAW", "train") + + monkeypatch.setattr(inference, "HF_TOKEN", "token") + monkeypatch.setattr(inference, "API_KEY", "") + monkeypatch.setattr(inference, "OPENAI_API_KEY", "") + monkeypatch.setattr(inference, "OPENAI_API_KEY_ENV", "OPENAI_API_KEY") + monkeypatch.setattr(inference, "API_BASE_URL", "https://api.openai.com/v1") + monkeypatch.setattr(inference, "OPENAI_BASE_URL", "") + monkeypatch.setattr(inference, "HF_SPACE_URL", "") + monkeypatch.setattr(inference, "MODEL_NAME", "gpt-5.4") + monkeypatch.setattr(inference, "LLM_TIMEOUT_SECONDS", 0) + + cfg = inference._resolve_environment_config() + + assert cfg.dataset_mode == "metaqa" + assert cfg.metaqa_root == "metaQA" + assert cfg.metaqa_kb_path == "metaQA/kb.txt" + assert cfg.metaqa_variant == "vanilla" + assert cfg.metaqa_hops == ["1-hop", "2-hop", "3-hop"] + assert cfg.metaqa_splits == ["train"] diff --git a/tests/test_leaderboard.py b/tests/test_leaderboard.py new file mode 100644 index 0000000000000000000000000000000000000000..7752b8646cc1785e505fd6422373a7e30fb26449 --- /dev/null +++ b/tests/test_leaderboard.py @@ -0,0 +1,47 @@ +from pathlib import Path + +from osint_env.eval.leaderboard import append_leaderboard_record, load_leaderboard, render_leaderboard_table, sorted_leaderboard + + +def test_leaderboard_roundtrip(tmp_path: Path): + board = tmp_path / "leaderboard.json" + append_leaderboard_record( + path=board, + summary={ + "leaderboard_score": 0.42, + "task_success_rate": 0.5, + "avg_graph_f1": 0.4, + "avg_reward": 0.1, + "tool_efficiency": 0.9, + "retrieval_signal": 0.3, + "structural_signal": 0.4, + }, + episodes=5, + run_name="baseline", + ) + append_leaderboard_record( + path=board, + summary={ + "leaderboard_score": 0.75, + "task_success_rate": 0.7, + "avg_graph_f1": 0.6, + "avg_reward": 0.5, + "tool_efficiency": 0.8, + "retrieval_signal": 0.6, + "structural_signal": 0.7, + }, + episodes=5, + run_name="improved", + ) + + records = load_leaderboard(board) + ranked = sorted_leaderboard(records) + assert len(records) == 2 + assert ranked[0]["run_name"] == "improved" + + ranked_by_success = sorted_leaderboard(records, sort_by="task_success_rate") + assert ranked_by_success[0]["run_name"] == "improved" + + table = render_leaderboard_table(records, top_k=5) + assert "| rank | run |" in table + assert "retrieval" in table diff --git a/tests/test_llm_interface.py b/tests/test_llm_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..c615c5bddac5e7217996bb6632e0a88baa018f57 --- /dev/null +++ b/tests/test_llm_interface.py @@ -0,0 +1,44 @@ +import os + +import pytest +import requests + +from osint_env.domain.models import LLMConfig +from osint_env.llm.interface import OllamaLLMClient, RuleBasedMockLLM, build_llm_client + + +def test_build_llm_client_mock_default(): + client = build_llm_client(LLMConfig(provider="mock")) + assert isinstance(client, RuleBasedMockLLM) + + +def test_build_llm_client_openai_requires_key(monkeypatch: pytest.MonkeyPatch): + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + with pytest.raises(ValueError): + build_llm_client(LLMConfig(provider="openai", openai_api_key="", openai_api_key_env="OPENAI_API_KEY")) + + +def test_build_llm_client_openai_with_key(monkeypatch: pytest.MonkeyPatch): + monkeypatch.setenv("OPENAI_API_KEY", "test-key") + cfg = LLMConfig(provider="openai", model="gpt-4o-mini", openai_api_key_env="OPENAI_API_KEY") + # Constructing should not fail when a key is present; actual API call is not made in this test. + client = build_llm_client(cfg) + assert client is not None + + +def test_openai_key_can_come_from_config_value(monkeypatch: pytest.MonkeyPatch): + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + cfg = LLMConfig(provider="openai", model="gpt-4o-mini", openai_api_key="cfg-key") + client = build_llm_client(cfg) + assert client is not None + + +def test_ollama_client_gracefully_handles_request_failure(monkeypatch: pytest.MonkeyPatch): + def _raise(*args, **kwargs): + raise requests.exceptions.ReadTimeout("timed out") + + monkeypatch.setattr("osint_env.llm.interface.requests.post", _raise) + client = OllamaLLMClient(model="qwen3:2b", timeout_seconds=1) + response = client.generate([{"role": "system", "content": "ping"}], tools=[]) + assert response.content == "" + assert response.tool_calls == [] diff --git a/tests/test_metaqa_dataset.py b/tests/test_metaqa_dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..e73a58b991136b93536a19e7d11447d6cbc3ac2a --- /dev/null +++ b/tests/test_metaqa_dataset.py @@ -0,0 +1,66 @@ +from pathlib import Path + +from osint_env.data.generator import DatasetGenerator +from osint_env.domain.models import EnvironmentConfig + + +def _write_metaqa_fixture(root: Path) -> None: + root.mkdir(parents=True, exist_ok=True) + (root / "kb.txt").write_text( + "\n".join( + [ + "Movie A|starred_actors|Actor X", + "Movie B|starred_actors|Actor X", + "Movie A|directed_by|Director D", + "Movie C|directed_by|Director D", + "Movie C|release_year|2002", + ] + ), + encoding="utf-8", + ) + + rows = { + "1-hop": ("what movies did [Actor X] act in\tMovie A|Movie B\n", "actor_to_movie\n"), + "2-hop": ("which films share the director of [Movie A]\tMovie C\n", "movie_to_director_to_movie\n"), + "3-hop": ( + "which release year corresponds to films with same director as [Movie A]\t2002\n", + "movie_to_director_to_movie_to_year\n", + ), + } + + for hop, (qa_line, qtype_line) in rows.items(): + qa_dir = root / hop / "vanilla" + qa_dir.mkdir(parents=True, exist_ok=True) + (qa_dir / "qa_train.txt").write_text(qa_line, encoding="utf-8") + (root / hop / "qa_train_qtype.txt").write_text(qtype_line, encoding="utf-8") + + +def test_metaqa_mode_builds_graph_and_hop_tasks(tmp_path: Path): + metaqa_root = tmp_path / "metaQA" + _write_metaqa_fixture(metaqa_root) + + cfg = EnvironmentConfig( + seed=5, + dataset_mode="metaqa", + metaqa_root=str(metaqa_root), + metaqa_variant="vanilla", + metaqa_hops=["1-hop", "2-hop", "3-hop"], + metaqa_splits=["train"], + ) + + gen = DatasetGenerator(cfg) + graph = gen.build_canonical_graph() + views = gen.build_platform_views(graph) + tasks = gen.generate_tasks(graph, views, count=24) + + assert len(graph.nodes) >= 5 + assert any(edge.rel == "directed_by" for edge in graph.edges) + assert any(post["post_id"].startswith("post_metaqa_") for post in views.microblog_posts) + assert any(profile["user_id"] == "Actor X" for profile in views.profiles) + + hop_labels = {str(task.metadata.get("hop", "")) for task in tasks} + difficulties = {str(task.metadata.get("difficulty", "")) for task in tasks} + + assert hop_labels == {"1-hop", "2-hop", "3-hop"} + assert difficulties == {"easy", "medium", "hard"} + assert all(task.supporting_edges for task in tasks) diff --git a/tests/test_openai_baseline.py b/tests/test_openai_baseline.py new file mode 100644 index 0000000000000000000000000000000000000000..d11342d26c88ea41b0119a261c0f8b5d4a946cde --- /dev/null +++ b/tests/test_openai_baseline.py @@ -0,0 +1,30 @@ +from osint_env.baselines.openai_runner import OpenAIBaselineConfig, OpenAIBaselineRunner, build_action_tools + + +def test_openai_baseline_toolset_contains_answer_and_graph_actions(): + tools = build_action_tools() + names = {tool["function"]["name"] for tool in tools} + assert "submit_answer" in names + assert "add_edge" in names + assert "search_memory" in names + assert "get_post" in names + + +def test_gpt5_request_kwargs_avoid_temperature_and_use_max_completion_tokens(): + runner = OpenAIBaselineRunner.__new__(OpenAIBaselineRunner) + runner.config = OpenAIBaselineConfig(model="gpt-5-nano", max_tokens=321, temperature=0.0, seed=7) + runner.tools = build_action_tools() + kwargs = runner._request_kwargs(messages=[{"role": "user", "content": "hi"}], episode_index=0) + assert kwargs["max_completion_tokens"] == 321 + assert kwargs["reasoning_effort"] == "none" + assert "temperature" not in kwargs + + +def test_gpt54_mini_request_kwargs_skip_reasoning_effort_for_chat_completions(): + runner = OpenAIBaselineRunner.__new__(OpenAIBaselineRunner) + runner.config = OpenAIBaselineConfig(model="gpt-5.4-mini", max_tokens=321, temperature=0.0, seed=7) + runner.tools = build_action_tools() + kwargs = runner._request_kwargs(messages=[{"role": "user", "content": "hi"}], episode_index=0) + assert kwargs["max_completion_tokens"] == 321 + assert "reasoning_effort" not in kwargs + assert "temperature" not in kwargs diff --git a/tests/test_reward.py b/tests/test_reward.py new file mode 100644 index 0000000000000000000000000000000000000000..03bb251136dd7eb850a07d97fbc96fb82b11f981 --- /dev/null +++ b/tests/test_reward.py @@ -0,0 +1,53 @@ +from osint_env.domain.models import Edge, EnvironmentConfig +from osint_env.env.environment import OSINTEnvironment +from osint_env.env.reward import build_reward_model, compute_answer_reward, compute_edge_reward + + +def test_composite_edge_reward_returns_breakdown(): + env = OSINTEnvironment(EnvironmentConfig(seed=13, n_users=16, max_steps=6)) + obs = env.reset() + task = env.state.task + + model = build_reward_model(env.graph) + edge = task.supporting_edges[0] + breakdown = compute_edge_reward( + edge=edge, + task=task, + existing_edges=[], + step_count=1, + model=model, + graph=env.graph, + ) + assert isinstance(breakdown.total, float) + assert breakdown.global_accuracy > 0 + assert isinstance(breakdown.connectivity_gain, float) + + +def test_answer_reward_uses_graph_and_tool_context(): + env = OSINTEnvironment(EnvironmentConfig(seed=21, n_users=18, max_steps=6)) + env.reset() + task = env.state.task + + pred_edges = [Edge(task.supporting_edges[0].src, task.supporting_edges[0].rel, task.supporting_edges[0].dst)] + tool_outputs = [{"tool": "get_profile", "output": {"result": {"user_id": task.answer}}}] + + good = compute_answer_reward( + proposed_answer=task.answer, + task=task, + pred_edges=pred_edges, + tool_outputs=tool_outputs, + step_count=2, + ) + bad = compute_answer_reward( + proposed_answer="wrong", + task=task, + pred_edges=[], + tool_outputs=[], + step_count=2, + ) + + assert good.total > bad.total + assert good.graph_f1 >= 0 + assert isinstance(good.relation_informativeness, float) + assert isinstance(good.entity_informativeness, float) + assert isinstance(good.repetition_penalty, float) diff --git a/tests/test_seeding.py b/tests/test_seeding.py new file mode 100644 index 0000000000000000000000000000000000000000..7381a3e2ad12bf469693ae39ef75558e8a6ec08c --- /dev/null +++ b/tests/test_seeding.py @@ -0,0 +1,40 @@ +from osint_env.domain.models import ( + EnvironmentConfig, + NodeType, + SeedEdgeSpec, + SeedNodeSpec, + SeedQuestionSpec, + SeedingConfig, +) +from osint_env.env.environment import OSINTEnvironment + + +def test_environment_includes_seeded_graph_and_questions(): + seeding = SeedingConfig( + seeded_nodes=[ + SeedNodeSpec(node_id="alias_seed_001", node_type=NodeType.ALIAS, attrs={"handle": "@seed001"}), + SeedNodeSpec( + node_id="user_seed_001", + node_type=NodeType.USER, + attrs={"name": "Seed User", "org": "Helios Labs", "location": "Pune"}, + ), + ], + seeded_edges=[SeedEdgeSpec(src="alias_seed_001", rel="alias_of", dst="user_seed_001")], + seeded_questions=[ + SeedQuestionSpec( + question="Which canonical user owns alias alias_seed_001?", + answer="user_seed_001", + task_type="identity_resolution", + supporting_edges=[SeedEdgeSpec(src="alias_seed_001", rel="alias_of", dst="user_seed_001")], + ) + ], + llm_generate_remaining_graph=False, + llm_generate_remaining_tasks=False, + llm_generated_edge_budget=0, + llm_generated_task_budget=0, + ) + env = OSINTEnvironment(EnvironmentConfig(seed=33, n_users=12, seeding=seeding)) + + assert "alias_seed_001" in env.graph.nodes + assert any(edge.src == "alias_seed_001" and edge.rel == "alias_of" and edge.dst == "user_seed_001" for edge in env.graph.edges) + assert any("alias_seed_001" in task.question for task in env.tasks) diff --git a/tests/test_self_play_swarm_v2.py b/tests/test_self_play_swarm_v2.py new file mode 100644 index 0000000000000000000000000000000000000000..d98d9419f181b6473c79ffa66a28fd8576243874 --- /dev/null +++ b/tests/test_self_play_swarm_v2.py @@ -0,0 +1,462 @@ +import json +import random +from copy import deepcopy +from pathlib import Path + +from osint_env.data.generator import ( + build_swarm_v2_canonical_subgraph, + build_swarm_v2_path_candidates, + build_swarm_v2_tool_trace, + emit_swarm_v2_question, + select_swarm_v2_answer, +) +from osint_env.domain.models import CanonicalGraph, Edge, EnvironmentConfig, Node, NodeType +from osint_env.env.environment import OSINTEnvironment +from osint_env.training import SelfPlayTrainingConfig, run_adversarial_self_play +from osint_env.training.config import GeneratorRewardWeights +from osint_env.training.rewards import ( + decode_completion_text, + extract_answer_from_completion, + GeneratorRewardFunction, + SwarmV2ReplayValidator, + parse_generated_task_completion, +) + + +class DummyJudge: + def __init__(self, answer: str): + self._answer = answer + + def answer(self, question: str) -> str: + del question + return self._answer + + +def _edge_payload(edge: Edge) -> dict[str, object]: + return { + "src": edge.src, + "rel": edge.rel, + "dst": edge.dst, + "confidence": float(edge.confidence), + } + + +def _build_valid_candidate_payload(env: OSINTEnvironment, cfg: SelfPlayTrainingConfig) -> dict[str, object]: + path_candidates = build_swarm_v2_path_candidates( + env.graph, + rng=random.Random(17), + count=1, + min_hops=2, + max_hops=cfg.swarm_v2.validation.max_path_hops, + ) + assert path_candidates + path_edges = path_candidates[0] + question = emit_swarm_v2_question(path_edges) + answer = select_swarm_v2_answer(path_edges) + return { + "canonical_graph": build_swarm_v2_canonical_subgraph( + env.graph, + path_edges, + max_extra_edges=max(0, cfg.swarm_v2.shared_context.max_edges - len(path_edges)), + ), + "question": question, + "answer": answer, + "task_type": "swarm_v2_trace", + "supporting_edges": [_edge_payload(edge) for edge in path_edges], + "tool_trace": build_swarm_v2_tool_trace(env.graph, path_edges), + "subagent_outputs": [ + f"path_agent_{idx}: {edge.src} --{edge.rel}--> {edge.dst}" + for idx, edge in enumerate(path_edges) + ] + + ["question_agent: deterministic relation-path question"], + "orchestrator": { + "spawn_count": 3, + "finished_subtasks": 3, + "critical_steps": 2, + "breadth": 3, + "depth": 1, + }, + } + + +def test_decode_completion_text_handles_nested_content_parts(): + payload = {"question": "Q", "answer": "A", "supporting_edges": []} + completion = [ + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": json.dumps(payload), + } + ], + } + ] + + decoded = decode_completion_text(completion) + parsed = parse_generated_task_completion(decoded) + + assert decoded == json.dumps(payload) + assert parsed.question == "Q" + assert parsed.answer == "A" + assert parsed.is_valid is True + assert extract_answer_from_completion(decoded) == "A" + + +def test_parse_generated_task_completion_prefers_relevant_json_blob(): + payload = {"question": "Q", "answer": "A", "supporting_edges": []} + completion_text = ( + 'Example: {"note": "ignore"}\n' + f"Final: {json.dumps(payload)}\n" + '{"trailing": true}' + ) + + parsed = parse_generated_task_completion(completion_text) + + assert parsed.question == "Q" + assert parsed.answer == "A" + assert parsed.is_valid is True + + +def test_swarm_v2_duplicate_check_does_not_reject_distinct_relation_paths(): + cfg = SelfPlayTrainingConfig(pipeline_mode="swarm_v2") + env = OSINTEnvironment(EnvironmentConfig(seed=41, n_users=18, max_steps=6)) + path_candidates = build_swarm_v2_path_candidates( + env.graph, + rng=random.Random(19), + count=8, + min_hops=2, + max_hops=cfg.swarm_v2.validation.max_path_hops, + ) + assert len(path_candidates) >= 2 + + payload_a = { + "canonical_graph": build_swarm_v2_canonical_subgraph(env.graph, path_candidates[0], max_extra_edges=2), + "question": emit_swarm_v2_question(path_candidates[0]), + "answer": select_swarm_v2_answer(path_candidates[0]), + "task_type": "swarm_v2_trace", + "supporting_edges": [_edge_payload(edge) for edge in path_candidates[0]], + "tool_trace": build_swarm_v2_tool_trace(env.graph, path_candidates[0]), + "subagent_outputs": ["path_agent: candidate_a"], + "orchestrator": {"spawn_count": 2, "finished_subtasks": 2, "critical_steps": 2, "breadth": 2, "depth": 1}, + } + payload_b = { + "canonical_graph": build_swarm_v2_canonical_subgraph(env.graph, path_candidates[1], max_extra_edges=2), + "question": emit_swarm_v2_question(path_candidates[1]), + "answer": select_swarm_v2_answer(path_candidates[1]), + "task_type": "swarm_v2_trace", + "supporting_edges": [_edge_payload(edge) for edge in path_candidates[1]], + "tool_trace": build_swarm_v2_tool_trace(env.graph, path_candidates[1]), + "subagent_outputs": ["path_agent: candidate_b"], + "orchestrator": {"spawn_count": 2, "finished_subtasks": 2, "critical_steps": 2, "breadth": 2, "depth": 1}, + } + assert payload_a["question"] != payload_b["question"] + + distinct_validator = SwarmV2ReplayValidator( + graph=env.graph, + validation=cfg.swarm_v2.validation, + shared_context=cfg.swarm_v2.shared_context, + seen_questions=[str(payload_b["question"])], + ) + distinct_result = distinct_validator.validate(parse_generated_task_completion(json.dumps(payload_a))) + assert distinct_result.is_valid is True + + duplicate_validator = SwarmV2ReplayValidator( + graph=env.graph, + validation=cfg.swarm_v2.validation, + shared_context=cfg.swarm_v2.shared_context, + seen_questions=[str(payload_a["question"])], + ) + duplicate_result = duplicate_validator.validate(parse_generated_task_completion(json.dumps(payload_a))) + assert duplicate_result.is_valid is False + assert "duplicate_or_near_duplicate" in duplicate_result.reasons + + +def test_swarm_v2_replay_validator_accepts_valid_candidate_and_rejects_invalid_cases(): + cfg = SelfPlayTrainingConfig(pipeline_mode="swarm_v2") + env = OSINTEnvironment(EnvironmentConfig(seed=23, n_users=18, max_steps=6)) + payload = _build_valid_candidate_payload(env, cfg) + + validator = SwarmV2ReplayValidator( + graph=env.graph, + validation=cfg.swarm_v2.validation, + shared_context=cfg.swarm_v2.shared_context, + seen_questions=[], + ) + valid = validator.validate(parse_generated_task_completion(json.dumps(payload))) + assert valid.is_valid is True + + leaked_payload = deepcopy(payload) + leaked_payload["question"] = f"{payload['question']} {payload['answer']}" + leaked = validator.validate(parse_generated_task_completion(json.dumps(leaked_payload))) + assert leaked.is_valid is False + assert "answer_leakage" in leaked.reasons + + no_trace_payload = deepcopy(payload) + no_trace_payload["tool_trace"] = [] + no_trace = validator.validate(parse_generated_task_completion(json.dumps(no_trace_payload))) + assert no_trace.is_valid is False + assert "non_replayable_tool_calls" in no_trace.reasons + + unseen_payload = deepcopy(payload) + unseen_payload["supporting_edges"][0]["dst"] = "user_missing" + unseen = validator.validate(parse_generated_task_completion(json.dumps(unseen_payload))) + assert unseen.is_valid is False + assert "unseen_nodes_or_edges" in unseen.reasons + + +def test_swarm_v2_replay_validator_rejects_non_unique_paths(): + graph = CanonicalGraph( + nodes={ + "user_root": Node("user_root", NodeType.USER, {}), + "user_mid1": Node("user_mid1", NodeType.USER, {}), + "user_mid2": Node("user_mid2", NodeType.USER, {}), + "user_target": Node("user_target", NodeType.USER, {}), + }, + edges=[ + Edge("user_root", "linked_to", "user_mid1"), + Edge("user_root", "linked_to", "user_mid2"), + Edge("user_mid1", "knows", "user_target"), + Edge("user_mid2", "knows", "user_target"), + ], + ) + cfg = SelfPlayTrainingConfig(pipeline_mode="swarm_v2") + ambiguous_path = [ + Edge("user_root", "linked_to", "user_mid1"), + Edge("user_mid1", "knows", "user_target"), + ] + payload = { + "canonical_graph": build_swarm_v2_canonical_subgraph(graph, ambiguous_path, max_extra_edges=1), + "question": emit_swarm_v2_question(ambiguous_path), + "answer": select_swarm_v2_answer(ambiguous_path), + "task_type": "swarm_v2_trace", + "supporting_edges": [_edge_payload(edge) for edge in ambiguous_path], + "tool_trace": build_swarm_v2_tool_trace(graph, ambiguous_path), + "subagent_outputs": ["path_agent: ambiguous linked_to -> knows trace"], + "orchestrator": {"spawn_count": 2, "finished_subtasks": 2, "critical_steps": 2, "breadth": 2, "depth": 1}, + } + validator = SwarmV2ReplayValidator( + graph=graph, + validation=cfg.swarm_v2.validation, + shared_context=cfg.swarm_v2.shared_context, + seen_questions=[], + ) + result = validator.validate(parse_generated_task_completion(json.dumps(payload))) + assert result.is_valid is False + assert "non_unique_derivation_path" in result.reasons + + +def test_swarm_v2_generator_reward_prefers_valid_parallel_diverse_tasks(): + cfg = SelfPlayTrainingConfig(pipeline_mode="swarm_v2") + env = OSINTEnvironment(EnvironmentConfig(seed=29, n_users=18, max_steps=6)) + payload = _build_valid_candidate_payload(env, cfg) + + reward_fn = GeneratorRewardFunction( + graph=env.graph, + answerer_judge=DummyJudge(answer="wrong_answer"), + weights=GeneratorRewardWeights(), + max_support_edges=cfg.swarm_v2.validation.max_support_edges, + pipeline_mode="swarm_v2", + swarm_v2_validation=cfg.swarm_v2.validation, + swarm_v2_shared_context=cfg.swarm_v2.shared_context, + parl_max_parallel_hint=cfg.swarm_v2.generator_swarm.max_agents, + ) + + spawn_only = deepcopy(payload) + spawn_only["orchestrator"]["spawn_count"] = 6 + spawn_only["orchestrator"]["finished_subtasks"] = 0 + spawn_only["orchestrator"]["critical_steps"] = 6 + + duplicate_workers = deepcopy(payload) + duplicate_workers["subagent_outputs"] = ["same worker trace"] * 4 + + answer_leak = deepcopy(payload) + answer_leak["question"] = f"{payload['question']} {payload['answer']}" + + overflow = deepcopy(payload) + overflow["supporting_edges"] = payload["supporting_edges"] + payload["supporting_edges"] + + unsupported_answer = deepcopy(payload) + unsupported_answer["answer"] = "user_not_in_graph" + + serial_collapse = deepcopy(payload) + serial_collapse["orchestrator"] = { + "spawn_count": 1, + "finished_subtasks": 1, + "critical_steps": 7, + "breadth": 1, + "depth": 1, + } + + scores = reward_fn( + completions=[ + json.dumps(payload), + json.dumps(spawn_only), + json.dumps(duplicate_workers), + json.dumps(answer_leak), + json.dumps(overflow), + json.dumps(unsupported_answer), + json.dumps(serial_collapse), + ] + ) + + assert scores[0] > scores[1] + assert scores[0] > scores[2] + assert scores[0] > scores[6] + assert scores[3] < 0 + assert scores[4] < 0 + assert scores[5] < 0 + + +def test_swarm_v2_generator_reward_grades_invalid_outputs_instead_of_constant_penalty(): + cfg = SelfPlayTrainingConfig(pipeline_mode="swarm_v2") + env = OSINTEnvironment(EnvironmentConfig(seed=31, n_users=18, max_steps=6)) + valid_payload = _build_valid_candidate_payload(env, cfg) + + reward_fn = GeneratorRewardFunction( + graph=env.graph, + answerer_judge=DummyJudge(answer="wrong_answer"), + weights=GeneratorRewardWeights(), + max_support_edges=cfg.swarm_v2.validation.max_support_edges, + pipeline_mode="swarm_v2", + swarm_v2_validation=cfg.swarm_v2.validation, + swarm_v2_shared_context=cfg.swarm_v2.shared_context, + parl_max_parallel_hint=cfg.swarm_v2.generator_swarm.max_agents, + ) + + missing_everything = "not json" + partial_json = json.dumps({"question": "Who is linked by this path?", "answer": valid_payload["answer"]}) + partial_edges = json.dumps( + { + "question": valid_payload["question"], + "answer": valid_payload["answer"], + "supporting_edges": valid_payload["supporting_edges"], + } + ) + + scores = reward_fn(completions=[missing_everything, partial_json, partial_edges, json.dumps(valid_payload)]) + + assert len(set(scores)) > 2 + assert scores[0] < scores[1] < scores[2] < scores[3] + assert reward_fn._debug_last_batch["batch_reward_std"] > 0.0 + assert reward_fn._debug_last_batch["valid_output_ratio"] == 0.25 + + +def test_parse_generated_task_completion_handles_garbage_orchestrator_values(): + """Regression: model emits e.g. ``"none"`` for an orchestrator integer. + + Previously this crashed the GRPO trainer with + ``ValueError: invalid literal for int() with base 10: 'none'``. + """ + completion = json.dumps( + { + "question": "Q?", + "answer": "user_7", + "supporting_edges": [ + {"src": "a", "rel": "knows", "dst": "user_7", "confidence": 1.0} + ], + "tool_trace": [], + "orchestrator": { + "spawn_count": "none", + "finished_subtasks": "N/A", + "critical_steps": True, + "breadth": "2 agents", + "depth": None, + }, + } + ) + + candidate = parse_generated_task_completion(completion) + assert candidate.orchestrator.spawn_count == 0 + assert candidate.orchestrator.finished_subtasks == 0 + assert candidate.orchestrator.critical_steps == 1 + assert candidate.orchestrator.depth == 0 + + +def test_swarm_v2_generator_reward_is_robust_to_parse_crashes(): + """Reward function must never raise: any malformed completion gets a floor reward.""" + cfg = SelfPlayTrainingConfig(pipeline_mode="swarm_v2") + env = OSINTEnvironment(EnvironmentConfig(seed=33, n_users=14, max_steps=6)) + reward_fn = GeneratorRewardFunction( + graph=env.graph, + answerer_judge=DummyJudge(answer="x"), + weights=GeneratorRewardWeights(), + max_support_edges=cfg.swarm_v2.validation.max_support_edges, + pipeline_mode="swarm_v2", + swarm_v2_validation=cfg.swarm_v2.validation, + swarm_v2_shared_context=cfg.swarm_v2.shared_context, + parl_max_parallel_hint=cfg.swarm_v2.generator_swarm.max_agents, + ) + + garbage_orchestrator = json.dumps( + { + "question": "Q?", + "answer": "y", + "supporting_edges": [{"src": "a", "rel": "r", "dst": "y", "confidence": 1.0}], + "tool_trace": [], + "orchestrator": {"spawn_count": "none"}, + } + ) + + scores = reward_fn(completions=["", "{not really json", garbage_orchestrator]) + assert len(scores) == 3 + for score in scores: + assert -1.8 <= score <= 1.2 + + +def test_swarm_v2_dry_run_writes_new_artifacts_and_preserves_legacy_contract(tmp_path: Path): + env_cfg = EnvironmentConfig(seed=11, n_users=14, max_steps=6) + train_cfg = SelfPlayTrainingConfig( + rounds=1, + output_dir=str(tmp_path / "self_play"), + dry_run=True, + pipeline_mode="swarm_v2", + generated_tasks_per_round=3, + generator_prompts_per_round=3, + ) + + payload = run_adversarial_self_play(env_config=env_cfg, training_config=train_cfg, dry_run=True) + assert payload["pipeline_mode"] == "swarm_v2" + assert len(payload["rounds"]) == 1 + + artifacts = payload["rounds"][0]["artifacts"] + for key in [ + "generator_dataset", + "answerer_dataset", + "generated_tasks", + "canonical_graph_candidates", + "replay_traces", + "validation_reports", + ]: + assert Path(artifacts[key]).exists() + loaded = json.loads(Path(artifacts[key]).read_text(encoding="utf-8")) + assert loaded is not None + + +def test_swarm_v2_fixed_canonical_mode_reuses_prompt_candidates(tmp_path: Path): + env_cfg = EnvironmentConfig(seed=19, n_users=14, max_steps=6) + train_cfg = SelfPlayTrainingConfig( + rounds=1, + output_dir=str(tmp_path / "self_play_fixed_canonical"), + dry_run=True, + pipeline_mode="swarm_v2", + canonical_graph_mode="fixed", + generated_tasks_per_round=3, + generator_prompts_per_round=3, + ) + + payload = run_adversarial_self_play(env_config=env_cfg, training_config=train_cfg, dry_run=True) + artifacts = payload["rounds"][0]["artifacts"] + candidates_payload = json.loads(Path(artifacts["canonical_graph_candidates"]).read_text(encoding="utf-8")) + generated_payload = json.loads(Path(artifacts["generated_tasks"]).read_text(encoding="utf-8")) + + expected_graphs = { + json.dumps((item.get("canonical_graph") if isinstance(item.get("canonical_graph"), dict) else item), sort_keys=True) + for item in candidates_payload + if isinstance(item, dict) + } + assert expected_graphs + + for task in generated_payload: + canonical_graph = ((task.get("metadata") or {}).get("canonical_graph")) or {} + assert json.dumps(canonical_graph, sort_keys=True) in expected_graphs diff --git a/tests/test_server.py b/tests/test_server.py new file mode 100644 index 0000000000000000000000000000000000000000..6e3db4bda127b897b20c85ea9f69217b0ab15654 --- /dev/null +++ b/tests/test_server.py @@ -0,0 +1,235 @@ +import json +import os + +from fastapi.testclient import TestClient + +import server +from server import app + + +client = TestClient(app) + + +def test_server_health(): + response = client.get("/healthz") + assert response.status_code == 200 + assert response.json()["status"] == "ok" + + +def test_server_health_alias(): + response = client.get("/health") + assert response.status_code == 200 + assert response.json()["status"] == "ok" + + +def test_server_environment_metadata(): + response = client.get("/api/environment") + assert response.status_code == 200 + body = response.json() + assert "action_space" in body + assert "observation_space" in body + assert "summary" in body + + +def test_openenv_spec_and_tasks_endpoints(): + spec = client.get("/openenv.yaml") + assert spec.status_code == 200 + assert "reset" in spec.text + + tasks = client.get("/openenv/tasks") + assert tasks.status_code == 200 + body = tasks.json() + assert len(body) >= 3 + assert {"task_id", "task_type", "question", "difficulty"} <= set(body[0].keys()) + + +def test_openenv_reset_step_and_state_cycle(): + reset = client.post("/openenv/reset", json={"task_index": 0}) + assert reset.status_code == 200 + body = reset.json() + session_id = body["session_id"] + assert body["done"] is False + assert "question" in body["observation"]["task"] + + state = client.get(f"/openenv/state/{session_id}") + assert state.status_code == 200 + assert state.json()["session_id"] == session_id + + step = client.post( + "/openenv/step", + json={ + "session_id": session_id, + "action_type": "ANSWER", + "payload": {"answer": "unknown"}, + }, + ) + assert step.status_code == 200 + step_body = step.json() + assert step_body["session_id"] == session_id + assert step_body["done"] is True + assert "task_answer" in step_body["info"] + + +def test_openenv_reset_accepts_empty_body(): + reset = client.post("/openenv/reset") + assert reset.status_code == 200 + body = reset.json() + assert body["done"] is False + assert "session_id" in body + + +def test_openenv_reset_accepts_empty_json_body(): + reset = client.post( + "/openenv/reset", + data="", + headers={"Content-Type": "application/json"}, + ) + assert reset.status_code == 200 + body = reset.json() + assert body["done"] is False + assert "session_id" in body + + +def test_openenv_reset_trailing_slash_post_returns_json(): + reset = client.post( + "/openenv/reset/", + data="", + headers={"Content-Type": "application/json"}, + ) + assert reset.status_code == 200 + body = reset.json() + assert body["done"] is False + assert "session_id" in body + + +def test_openenv_step_accepts_nested_action_payload(): + reset = client.post("/openenv/reset", json={"task_index": 0}) + assert reset.status_code == 200 + session_id = reset.json()["session_id"] + + step = client.post( + "/openenv/step", + json={ + "session_id": session_id, + "action": { + "action_type": "ANSWER", + "payload": {"answer": "unknown"}, + }, + }, + ) + assert step.status_code == 200 + assert step.json()["done"] is True + + +def test_step_alias_uses_latest_session_when_session_id_missing(): + reset = client.post("/reset", json={"task_index": 0}) + assert reset.status_code == 200 + session_id = reset.json()["session_id"] + + step = client.post( + "/step", + json={ + "action_type": "ANSWER", + "payload": {"answer": "unknown"}, + }, + ) + assert step.status_code == 200 + body = step.json() + assert body["session_id"] == session_id + assert body["done"] is True + + +def test_state_alias_returns_latest_session(): + reset = client.post("/reset", json={"task_index": 0}) + assert reset.status_code == 200 + session_id = reset.json()["session_id"] + + state = client.get("/state") + assert state.status_code == 200 + body = state.json() + assert body["session_id"] == session_id + assert "task" in body["observation"] + + +def test_report_inference_updates_latest_evaluation_and_dashboard(tmp_path, monkeypatch): + latest_evaluation = tmp_path / "latest_evaluation.json" + space_dashboard = tmp_path / "space_dashboard.html" + + monkeypatch.setattr(server, "LATEST_EVALUATION_OUTPUT", latest_evaluation) + monkeypatch.setattr(server, "SPACE_DASHBOARD", space_dashboard) + monkeypatch.setattr(server, "load_leaderboard", lambda path: []) + monkeypatch.setattr(server, "export_dashboard", lambda env, evaluation, leaderboard_records, output_path: str(space_dashboard)) + + response = client.post( + "/openenv/report_inference", + json={ + "run": {"name": "inference_py_run"}, + "summary": {"leaderboard_score": 0.75, "task_success_rate": 1.0}, + "episodes": [ + { + "task_id": "seed_task_0", + "agent_answer": "user_bharat", + "graph_f1": 0.5, + "reward": 1.2, + "steps": 5, + "tool_calls": 4, + "success": 1, + } + ], + }, + ) + assert response.status_code == 200 + body = response.json() + assert body["status"] == "ok" + assert latest_evaluation.exists() + stored = json.loads(latest_evaluation.read_text(encoding="utf-8")) + assert stored["summary"]["leaderboard_score"] == 0.75 + assert stored["episodes"][0]["task_id"] == "seed_task_0" + assert stored["episodes"][0]["truth_edges"] + + +def test_space_snapshot_prefers_newer_evaluation_payload(tmp_path, monkeypatch): + baseline_path = tmp_path / "baseline.json" + evaluation_path = tmp_path / "evaluation.json" + baseline_dashboard = tmp_path / "baseline_dashboard.html" + space_dashboard = tmp_path / "space_dashboard.html" + + baseline_path.write_text( + json.dumps( + { + "run": {"dashboard_path": str(baseline_dashboard)}, + "summary": {"leaderboard_score": 0.1, "task_success_rate": 0.1}, + } + ), + encoding="utf-8", + ) + baseline_dashboard.write_text("baseline", encoding="utf-8") + evaluation_path.write_text( + json.dumps({"summary": {"leaderboard_score": 0.9, "task_success_rate": 0.9}, "episodes": []}), + encoding="utf-8", + ) + space_dashboard.write_text("space", encoding="utf-8") + os.utime(evaluation_path, (baseline_path.stat().st_atime + 5, baseline_path.stat().st_mtime + 5)) + + monkeypatch.setattr(server, "LATEST_BASELINE_OUTPUT", baseline_path) + monkeypatch.setattr(server, "LATEST_EVALUATION_OUTPUT", evaluation_path) + monkeypatch.setattr(server, "SPACE_DASHBOARD", space_dashboard) + monkeypatch.setattr( + server, + "_base_environment_snapshot", + lambda: { + "task_count": 30, + "difficulty_counts": {}, + "action_space": ["CALL_TOOL", "ADD_EDGE", "ANSWER"], + "observation_space": {}, + "task_types": [], + "config": {}, + }, + ) + monkeypatch.setattr(server, "_build_environment", lambda: object()) + monkeypatch.setattr(server, "export_dashboard", lambda env, evaluation, leaderboard_records, output_path: str(space_dashboard)) + + snapshot = server._space_snapshot() + assert snapshot["source"] == "latest_evaluation" + assert snapshot["summary"]["leaderboard_score"] == 0.9 + assert snapshot["dashboard_path"] == str(space_dashboard) diff --git a/tests/test_spawn_reward_hooks.py b/tests/test_spawn_reward_hooks.py new file mode 100644 index 0000000000000000000000000000000000000000..95c5106774ec738fb5b5d365b3ce82e62e592834 --- /dev/null +++ b/tests/test_spawn_reward_hooks.py @@ -0,0 +1,43 @@ +from osint_env.env.spawn_reward_hooks import critical_steps, parl_style_spawn_reward + + +def test_critical_steps_matches_parallel_path_length(): + total = critical_steps(main_steps=[1, 1, 1], parallel_subagent_steps=[[3, 2], [0], [4, 1, 2]]) + assert total == 1 + 3 + 1 + 0 + 1 + 4 + + +def test_parl_reward_prefers_finished_parallel_work(): + base = parl_style_spawn_reward( + task_outcome_reward=0.2, + spawn_count=4, + finished_subtasks=1, + critical_steps=12, + lambda_parallel=0.2, + lambda_finish=0.25, + anneal=1.0, + breadth=2, + depth=3, + ) + better = parl_style_spawn_reward( + task_outcome_reward=0.2, + spawn_count=4, + finished_subtasks=4, + critical_steps=8, + lambda_parallel=0.2, + lambda_finish=0.25, + anneal=1.0, + breadth=4, + depth=2, + ) + assert better > base + + +def test_parl_auxiliary_can_be_annealed_out(): + frozen = parl_style_spawn_reward( + task_outcome_reward=0.7, + spawn_count=8, + finished_subtasks=8, + critical_steps=5, + anneal=0.0, + ) + assert frozen == 0.7 diff --git a/tests/test_swarm_agent.py b/tests/test_swarm_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..712b2fbd3246ca528338f9bfb7e300b24690dc88 --- /dev/null +++ b/tests/test_swarm_agent.py @@ -0,0 +1,17 @@ +from osint_env.agents.swarm_agent import SwarmAgentRunner +from osint_env.domain.models import EnvironmentConfig, SwarmConfig +from osint_env.env.environment import OSINTEnvironment + + +def test_swarm_runner_emits_spawn_telemetry(): + config = EnvironmentConfig( + seed=14, + max_steps=8, + swarm=SwarmConfig(enabled=True, max_agents=3, max_breadth=2, max_width=2, max_depth=2, planner_rounds=2), + ) + env = OSINTEnvironment(config) + info = SwarmAgentRunner(env).run_episode() + + assert info["spawn_count"] > 0 + assert "spawn_auxiliary" in info["reward_components"] + assert info["spawn_critical_steps"] > 0 diff --git a/tests/test_tools.py b/tests/test_tools.py new file mode 100644 index 0000000000000000000000000000000000000000..dc5a67f5f17b05b44bca34979cf852fb4a13f217 --- /dev/null +++ b/tests/test_tools.py @@ -0,0 +1,39 @@ +from osint_env.config import clone_environment_config, load_seeding_config, load_shared_config +from osint_env.data.generator import DatasetGenerator +from osint_env.domain.models import EnvironmentConfig +from osint_env.env.environment import OSINTEnvironment +from osint_env.platforms.tools import ToolRegistry + + +def test_tools_basics(): + gen = DatasetGenerator(EnvironmentConfig(n_users=12, seed=3)) + g = gen.build_canonical_graph() + views = gen.build_platform_views(g) + tools = ToolRegistry(views) + out = tools.search_posts(query="Update") + assert out["count"] > 0 + profile_any = next(iter([p["user_id"] for p in views.profiles if p["user_id"].startswith("user_")])) + profile = tools.get_profile(profile_any) + assert profile["found"] is True + + +def test_seeded_tools_expose_seed_question_entities(): + shared = load_shared_config("datasets/fixed_levels/shared_config_fixed_levels.json") + env_cfg = clone_environment_config(shared.environment) + env_cfg.seeding = load_seeding_config("datasets/fixed_levels/seed_fixed_levels.json") + env_cfg.llm.provider = "mock" + env = OSINTEnvironment(env_cfg) + tools = env.tools + + post = tools.get_post("post_midnight_manifest") + assert post["found"] is True + assert "loc_dockyard17" in post["result"]["references"] + + people = tools.search_people(org="org_northbridge_logistics") + user_ids = {row["user_id"] for row in people["results"]} + assert "user_bharat" in user_ids + assert "user_hiro" in user_ids + + alias_profile = tools.get_profile("alias_docksparrow") + assert alias_profile["found"] is True + assert alias_profile["result"]["user_id"] == "user_hiro" diff --git a/tests/test_training_config.py b/tests/test_training_config.py new file mode 100644 index 0000000000000000000000000000000000000000..9d55caf3b9b0c670e52051413f7923a5788ed2ba --- /dev/null +++ b/tests/test_training_config.py @@ -0,0 +1,161 @@ +from pathlib import Path +import json + +from osint_env.training.config import load_self_play_config + + +def test_self_play_config_defaults_when_missing(): + cfg = load_self_play_config("/tmp/does_not_exist_for_self_play_config.json") + assert cfg.rounds >= 1 + assert cfg.pipeline_mode in {"legacy", "swarm_v2"} + assert cfg.model_topology in {"dual", "shared"} + assert cfg.phase_schedule in {"generator_answerer", "answerer_generator_answerer"} + assert cfg.tuning_mode in {"full", "lora"} + assert cfg.generator_phase.max_steps >= 1 + assert cfg.answerer_phase.max_steps >= 1 + assert cfg.generator_reward_weights.hardness > 0.0 + assert cfg.swarm_v2.generator_swarm.shared_context is True + assert cfg.swarm_v2.validation.max_support_edges >= 1 + assert cfg.wandb_enabled is False + assert cfg.wandb_project == "osint-self-play" + assert cfg.canonical_graph_mode == "generate" + + +def test_self_play_config_parses_overrides(tmp_path: Path): + cfg_path = tmp_path / "self_play.json" + cfg_path.write_text( + json.dumps( + { + "rounds": 5, + "output_dir": "artifacts/custom_self_play", + "dry_run": False, + "pipeline_mode": "swarm_v2", + "wandb_enabled": True, + "wandb_project": "osint-train-tests", + "wandb_entity": "example-team", + "wandb_run_name_prefix": "ci-self-play", + "canonical_graph_mode": "fixed", + "model_topology": "shared", + "phase_schedule": "answerer_generator_answerer", + "tuning_mode": "lora", + "shared_model_name_or_path": "/models/local-base", + "seed_tasks_per_round": 12, + "generated_tasks_per_round": 18, + "swarm_v2": { + "generator_swarm": { + "shared_context": True, + "max_agents": 5, + "max_breadth": 4, + "max_depth": 3, + "planner_rounds": 3, + "tools_per_agent": 2, + }, + "answerer_swarm": { + "shared_context": True, + "max_agents": 4, + "max_breadth": 3, + "max_depth": 2, + "planner_rounds": 2, + "tools_per_agent": 2, + }, + "validation": { + "max_support_edges": 6, + "max_path_hops": 3, + "max_context_nodes": 10, + "max_context_edges": 6, + "duplicate_similarity_threshold": 0.75, + }, + "shared_context": { + "shared_by_default": True, + "max_nodes": 10, + "max_edges": 6, + "target_pressure": 0.9, + }, + }, + "generator_reward_weights": { + "validity": 0.2, + "hardness": 0.6, + "diversity": 0.1, + "consistency": 0.1, + }, + "lora": { + "r": 32, + "alpha": 64, + "dropout": 0.1, + "target_modules": ["q_proj", "v_proj"], + "bias": "none", + "task_type": "CAUSAL_LM", + }, + "generator_phase": { + "model_name_or_path": "Qwen/Qwen2.5-3B-Instruct", + "max_steps": 77, + "num_generations": 6, + "loss_type": "grpo", + "scale_rewards": "group", + "output_subdir": "gen_phase", + }, + "answerer_phase": { + "model_name_or_path": "Qwen/Qwen2.5-1.5B-Instruct", + "max_steps": 55, + "num_generations": 5, + "output_subdir": "ans_phase", + }, + } + ), + encoding="utf-8", + ) + + cfg = load_self_play_config(cfg_path) + assert cfg.rounds == 5 + assert cfg.output_dir == "artifacts/custom_self_play" + assert cfg.dry_run is False + assert cfg.pipeline_mode == "swarm_v2" + assert cfg.wandb_enabled is True + assert cfg.wandb_project == "osint-train-tests" + assert cfg.wandb_entity == "example-team" + assert cfg.wandb_run_name_prefix == "ci-self-play" + assert cfg.canonical_graph_mode == "fixed" + assert cfg.model_topology == "shared" + assert cfg.phase_schedule == "answerer_generator_answerer" + assert cfg.tuning_mode == "lora" + assert cfg.shared_model_name_or_path == "/models/local-base" + assert cfg.seed_tasks_per_round == 12 + assert cfg.generated_tasks_per_round == 18 + assert cfg.swarm_v2.generator_swarm.max_agents == 5 + assert cfg.swarm_v2.answerer_swarm.max_agents == 4 + assert cfg.swarm_v2.validation.max_support_edges == 6 + assert cfg.swarm_v2.shared_context.target_pressure == 0.9 + assert cfg.generator_reward_weights.hardness == 0.6 + assert cfg.lora.r == 32 + assert cfg.lora.alpha == 64 + assert cfg.lora.target_modules == ["q_proj", "v_proj"] + + assert cfg.generator_phase.model_name_or_path == "Qwen/Qwen2.5-3B-Instruct" + assert cfg.generator_phase.max_steps == 77 + assert cfg.generator_phase.num_generations == 6 + assert cfg.generator_phase.loss_type == "grpo" + assert cfg.generator_phase.scale_rewards == "group" + assert cfg.generator_phase.output_subdir == "gen_phase" + + assert cfg.answerer_phase.model_name_or_path == "Qwen/Qwen2.5-1.5B-Instruct" + assert cfg.answerer_phase.max_steps == 55 + assert cfg.answerer_phase.num_generations == 5 + assert cfg.answerer_phase.output_subdir == "ans_phase" + + +def test_self_play_config_keeps_legacy_mode_when_not_set(tmp_path: Path): + cfg_path = tmp_path / "legacy_self_play.json" + cfg_path.write_text( + json.dumps( + { + "rounds": 2, + "max_support_edges": 11, + } + ), + encoding="utf-8", + ) + + cfg = load_self_play_config(cfg_path) + assert cfg.pipeline_mode == "legacy" + assert cfg.max_support_edges == 11 + assert cfg.swarm_v2.validation.max_support_edges == 11 diff --git a/tests/test_validation.py b/tests/test_validation.py new file mode 100644 index 0000000000000000000000000000000000000000..ce1e398b01983303f221ea96dc9d2a731137265d --- /dev/null +++ b/tests/test_validation.py @@ -0,0 +1,7 @@ +from osint_env.validation import run_validation_suite + + +def test_validation_suite_passes_repo_gate(): + result = run_validation_suite() + assert result["passed"] is True + assert len(result["checks"]) >= 4 diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000000000000000000000000000000000000..0e978f5923688321a8f14844a0380d5dc1a8055b --- /dev/null +++ b/uv.lock @@ -0,0 +1,3209 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11'", +] + +[[package]] +name = "accelerate" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/14/787e5498cd062640f0f3d92ef4ae4063174f76f9afd29d13fc52a319daae/accelerate-1.13.0.tar.gz", hash = "sha256:d631b4e0f5b3de4aff2d7e9e6857d164810dfc3237d54d017f075122d057b236", size = 402835, upload-time = "2026-03-04T19:34:12.359Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/46/02ac5e262d4af18054b3e922b2baedbb2a03289ee792162de60a865defc5/accelerate-1.13.0-py3-none-any.whl", hash = "sha256:cf1a3efb96c18f7b152eb0fa7490f3710b19c3f395699358f08decca2b8b62e0", size = 383744, upload-time = "2026-03-04T19:34:10.313Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/85/cebc47ee74d8b408749073a1a46c6fcba13d170dc8af7e61996c6c9394ac/aiohttp-3.13.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02222e7e233295f40e011c1b00e3b0bd451f22cf853a0304c3595633ee47da4b", size = 750547, upload-time = "2026-03-31T21:56:30.024Z" }, + { url = "https://files.pythonhosted.org/packages/05/98/afd308e35b9d3d8c9ec54c0918f1d722c86dc17ddfec272fcdbcce5a3124/aiohttp-3.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bace460460ed20614fa6bc8cb09966c0b8517b8c58ad8046828c6078d25333b5", size = 503535, upload-time = "2026-03-31T21:56:31.935Z" }, + { url = "https://files.pythonhosted.org/packages/6f/4d/926c183e06b09d5270a309eb50fbde7b09782bfd305dec1e800f329834fb/aiohttp-3.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f546a4dc1e6a5edbb9fd1fd6ad18134550e096a5a43f4ad74acfbd834fc6670", size = 497830, upload-time = "2026-03-31T21:56:33.654Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d6/f47d1c690f115a5c2a5e8938cce4a232a5be9aac5c5fb2647efcbbbda333/aiohttp-3.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c86969d012e51b8e415a8c6ce96f7857d6a87d6207303ab02d5d11ef0cad2274", size = 1682474, upload-time = "2026-03-31T21:56:35.513Z" }, + { url = "https://files.pythonhosted.org/packages/01/44/056fd37b1bb52eac760303e5196acc74d9d546631b035704ae5927f7b4ac/aiohttp-3.13.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6f6cd1560c5fa427e3b6074bb24d2c64e225afbb7165008903bd42e4e33e28a", size = 1655259, upload-time = "2026-03-31T21:56:37.843Z" }, + { url = "https://files.pythonhosted.org/packages/91/9f/78eb1a20c1c28ae02f6a3c0f4d7b0dcc66abce5290cadd53d78ce3084175/aiohttp-3.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:636bc362f0c5bbc7372bc3ae49737f9e3030dbce469f0f422c8f38079780363d", size = 1736204, upload-time = "2026-03-31T21:56:39.822Z" }, + { url = "https://files.pythonhosted.org/packages/de/6c/d20d7de23f0b52b8c1d9e2033b2db1ac4dacbb470bb74c56de0f5f86bb4f/aiohttp-3.13.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a7cbeb06d1070f1d14895eeeed4dac5913b22d7b456f2eb969f11f4b3993796", size = 1826198, upload-time = "2026-03-31T21:56:41.378Z" }, + { url = "https://files.pythonhosted.org/packages/2f/86/a6f3ff1fd795f49545a7c74b2c92f62729135d73e7e4055bf74da5a26c82/aiohttp-3.13.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca9ef7517fd7874a1a08970ae88f497bf5c984610caa0bf40bd7e8450852b95", size = 1681329, upload-time = "2026-03-31T21:56:43.374Z" }, + { url = "https://files.pythonhosted.org/packages/fb/68/84cd3dab6b7b4f3e6fe9459a961acb142aaab846417f6e8905110d7027e5/aiohttp-3.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:019a67772e034a0e6b9b17c13d0a8fe56ad9fb150fc724b7f3ffd3724288d9e5", size = 1560023, upload-time = "2026-03-31T21:56:45.031Z" }, + { url = "https://files.pythonhosted.org/packages/41/2c/db61b64b0249e30f954a65ab4cb4970ced57544b1de2e3c98ee5dc24165f/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f34ecee82858e41dd217734f0c41a532bd066bcaab636ad830f03a30b2a96f2a", size = 1652372, upload-time = "2026-03-31T21:56:47.075Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/e96988a6c982d047810c772e28c43c64c300c943b0ed5c1c0c4ce1e1027c/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4eac02d9af4813ee289cd63a361576da36dba57f5a1ab36377bc2600db0cbb73", size = 1662031, upload-time = "2026-03-31T21:56:48.835Z" }, + { url = "https://files.pythonhosted.org/packages/b7/26/a56feace81f3d347b4052403a9d03754a0ab23f7940780dada0849a38c92/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4beac52e9fe46d6abf98b0176a88154b742e878fdf209d2248e99fcdf73cd297", size = 1708118, upload-time = "2026-03-31T21:56:50.833Z" }, + { url = "https://files.pythonhosted.org/packages/78/6e/b6173a8ff03d01d5e1a694bc06764b5dad1df2d4ed8f0ceec12bb3277936/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c180f480207a9b2475f2b8d8bd7204e47aec952d084b2a2be58a782ffcf96074", size = 1548667, upload-time = "2026-03-31T21:56:52.81Z" }, + { url = "https://files.pythonhosted.org/packages/16/13/13296ffe2c132d888b3fe2c195c8b9c0c24c89c3fa5cc2c44464dc23b22e/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2837fb92951564d6339cedae4a7231692aa9f73cbc4fb2e04263b96844e03b4e", size = 1724490, upload-time = "2026-03-31T21:56:54.541Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1f1c287f4a79782ef36e5a6e62954c85343bc30470d862d30bd5f26c9fa2/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9010032a0b9710f58012a1e9c222528763d860ba2ee1422c03473eab47703e7", size = 1667109, upload-time = "2026-03-31T21:56:56.21Z" }, + { url = "https://files.pythonhosted.org/packages/ef/42/8461a2aaf60a8f4ea4549a4056be36b904b0eb03d97ca9a8a2604681a500/aiohttp-3.13.5-cp310-cp310-win32.whl", hash = "sha256:7c4b6668b2b2b9027f209ddf647f2a4407784b5d88b8be4efcc72036f365baf9", size = 439478, upload-time = "2026-03-31T21:56:58.292Z" }, + { url = "https://files.pythonhosted.org/packages/e5/71/06956304cb5ee439dfe8d86e1b2e70088bd88ed1ced1f42fb29e5d855f0e/aiohttp-3.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:cd3db5927bf9167d5a6157ddb2f036f6b6b0ad001ac82355d43e97a4bde76d76", size = 462047, upload-time = "2026-03-31T21:57:00.257Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513, upload-time = "2026-03-31T21:57:02.146Z" }, + { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748, upload-time = "2026-03-31T21:57:04.275Z" }, + { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673, upload-time = "2026-03-31T21:57:06.208Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757, upload-time = "2026-03-31T21:57:07.882Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152, upload-time = "2026-03-31T21:57:09.946Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010, upload-time = "2026-03-31T21:57:12.157Z" }, + { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251, upload-time = "2026-03-31T21:57:14.023Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969, upload-time = "2026-03-31T21:57:16.146Z" }, + { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871, upload-time = "2026-03-31T21:57:17.856Z" }, + { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844, upload-time = "2026-03-31T21:57:19.679Z" }, + { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969, upload-time = "2026-03-31T21:57:22.006Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193, upload-time = "2026-03-31T21:57:24.256Z" }, + { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477, upload-time = "2026-03-31T21:57:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198, upload-time = "2026-03-31T21:57:28.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321, upload-time = "2026-03-31T21:57:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069, upload-time = "2026-03-31T21:57:32.388Z" }, + { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859, upload-time = "2026-03-31T21:57:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, + { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, + { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, + { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, + { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, + { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, + { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, + { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, + { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, + { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, + { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, + { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, + { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, + { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, + { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, + { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, + { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, + { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, + { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, + { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, + { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, + { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, + { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, + { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, + { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, + { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, + { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, + { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, + { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, + { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, + { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, + { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, + { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/08/0f303cb0b529e456bb116f2d50565a482694fbb94340bf56d44677e7ed03/charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d", size = 315182, upload-time = "2026-04-02T09:25:40.673Z" }, + { url = "https://files.pythonhosted.org/packages/24/47/b192933e94b546f1b1fe4df9cc1f84fcdbf2359f8d1081d46dd029b50207/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8", size = 209329, upload-time = "2026-04-02T09:25:42.354Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b4/01fa81c5ca6141024d89a8fc15968002b71da7f825dd14113207113fabbd/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790", size = 231230, upload-time = "2026-04-02T09:25:44.281Z" }, + { url = "https://files.pythonhosted.org/packages/20/f7/7b991776844dfa058017e600e6e55ff01984a063290ca5622c0b63162f68/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc", size = 225890, upload-time = "2026-04-02T09:25:45.475Z" }, + { url = "https://files.pythonhosted.org/packages/20/e7/bed0024a0f4ab0c8a9c64d4445f39b30c99bd1acd228291959e3de664247/charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393", size = 216930, upload-time = "2026-04-02T09:25:46.58Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ab/b18f0ab31cdd7b3ddb8bb76c4a414aeb8160c9810fdf1bc62f269a539d87/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153", size = 202109, upload-time = "2026-04-02T09:25:48.031Z" }, + { url = "https://files.pythonhosted.org/packages/82/e5/7e9440768a06dfb3075936490cb82dbf0ee20a133bf0dd8551fa096914ec/charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af", size = 214684, upload-time = "2026-04-02T09:25:49.245Z" }, + { url = "https://files.pythonhosted.org/packages/71/94/8c61d8da9f062fdf457c80acfa25060ec22bf1d34bbeaca4350f13bcfd07/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34", size = 212785, upload-time = "2026-04-02T09:25:50.671Z" }, + { url = "https://files.pythonhosted.org/packages/66/cd/6e9889c648e72c0ab2e5967528bb83508f354d706637bc7097190c874e13/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1", size = 203055, upload-time = "2026-04-02T09:25:51.802Z" }, + { url = "https://files.pythonhosted.org/packages/92/2e/7a951d6a08aefb7eb8e1b54cdfb580b1365afdd9dd484dc4bee9e5d8f258/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752", size = 232502, upload-time = "2026-04-02T09:25:53.388Z" }, + { url = "https://files.pythonhosted.org/packages/58/d5/abcf2d83bf8e0a1286df55cd0dc1d49af0da4282aa77e986df343e7de124/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53", size = 214295, upload-time = "2026-04-02T09:25:54.765Z" }, + { url = "https://files.pythonhosted.org/packages/47/3a/7d4cd7ed54be99973a0dc176032cba5cb1f258082c31fa6df35cff46acfc/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616", size = 227145, upload-time = "2026-04-02T09:25:55.904Z" }, + { url = "https://files.pythonhosted.org/packages/1d/98/3a45bf8247889cf28262ebd3d0872edff11565b2a1e3064ccb132db3fbb0/charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a", size = 218884, upload-time = "2026-04-02T09:25:57.074Z" }, + { url = "https://files.pythonhosted.org/packages/ad/80/2e8b7f8915ed5c9ef13aa828d82738e33888c485b65ebf744d615040c7ea/charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374", size = 148343, upload-time = "2026-04-02T09:25:58.199Z" }, + { url = "https://files.pythonhosted.org/packages/35/1b/3b8c8c77184af465ee9ad88b5aea46ea6b2e1f7b9dc9502891e37af21e30/charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943", size = 159174, upload-time = "2026-04-02T09:25:59.322Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/feb40dca40dbb21e0a908801782d9288c64fc8d8e562c2098e9994c8c21b/charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008", size = 147805, upload-time = "2026-04-02T09:26:00.756Z" }, + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + +[[package]] +name = "click" +version = "8.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, +] + +[[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 = "cuda-bindings" +version = "13.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/fe/7351d7e586a8b4c9f89731bfe4cf0148223e8f9903ff09571f78b3fb0682/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b395f79cb89ce0cd8effff07c4a1e20101b873c256a1aeb286e8fd7bd0f556", size = 5744254, upload-time = "2026-03-11T00:12:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ef/184aa775e970fc089942cd9ec6302e6e44679d4c14549c6a7ea45bf7f798/cuda_bindings-13.2.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6f3682ec3c4769326aafc67c2ba669d97d688d0b7e63e659d36d2f8b72f32d6", size = 6329075, upload-time = "2026-03-11T00:12:32.319Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d", size = 5730273, upload-time = "2026-03-11T00:12:37.18Z" }, + { url = "https://files.pythonhosted.org/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1", size = 6313924, upload-time = "2026-03-11T00:12:39.462Z" }, + { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, + { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619, upload-time = "2026-03-11T00:12:45.939Z" }, + { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610, upload-time = "2026-03-11T00:12:50.337Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914, upload-time = "2026-03-11T00:12:52.374Z" }, + { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673, upload-time = "2026-03-11T00:12:56.371Z" }, + { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386, upload-time = "2026-03-11T00:12:58.965Z" }, + { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469, upload-time = "2026-03-11T00:13:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693, upload-time = "2026-03-11T00:13:06.003Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/d6/ac63065d33dd700fee7ebd7d287332401b54e31b9346e142f871e1f0b116/cuda_pathfinder-1.5.3-py3-none-any.whl", hash = "sha256:dff021123aedbb4117cc7ec81717bbfe198fb4e8b5f1ee57e0e084fec5c8577d", size = 49991, upload-time = "2026-04-14T20:09:27.037Z" }, +] + +[[package]] +name = "cuda-toolkit" +version = "13.0.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, +] + +[package.optional-dependencies] +cublas = [ + { name = "nvidia-cublas", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +curand = [ + { name = "nvidia-curand", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cusolver = [ + { name = "nvidia-cusolver", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, +] + +[[package]] +name = "datasets" +version = "4.8.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/22/73e46ac7a8c25e7ef0b3bd6f10da3465021d90219a32eb0b4d2afea4c56e/datasets-4.8.4.tar.gz", hash = "sha256:a1429ed853275ce7943a01c6d2e25475b4501eb758934362106a280470df3a52", size = 604382, upload-time = "2026-03-23T14:21:17.987Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/e5/247d094108e42ac26363ab8dc57f168840cf7c05774b40ffeb0d78868fcc/datasets-4.8.4-py3-none-any.whl", hash = "sha256:cdc8bee4698e549d78bf1fed6aea2eebc760b22b084f07e6fc020c6577a6ce6d", size = 526991, upload-time = "2026-03-23T14:21:15.89Z" }, +] + +[[package]] +name = "dill" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz", hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +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.135.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f7/e6/7adb4c5fa231e82c35b8f5741a9f2d055f520c29af5546fd70d3e8e1cd2e/fastapi-0.135.3.tar.gz", hash = "sha256:bd6d7caf1a2bdd8d676843cdcd2287729572a1ef524fc4d65c17ae002a1be654", size = 396524, upload-time = "2026-04-01T16:23:58.188Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/a4/5caa2de7f917a04ada20018eccf60d6cc6145b0199d55ca3711b0fc08312/fastapi-0.135.3-py3-none-any.whl", hash = "sha256:9b0f590c813acd13d0ab43dd8494138eb58e484bfac405db1f3187cfc5810d98", size = 117734, upload-time = "2026-04-01T16:23:59.328Z" }, +] + +[[package]] +name = "filelock" +version = "3.29.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz", hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl", hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z" }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, +] + +[[package]] +name = "gitpython" +version = "3.1.47" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/bd/50db468e9b1310529a19fce651b3b0e753b5c07954d486cba31bbee9a5d5/gitpython-3.1.47.tar.gz", hash = "sha256:dba27f922bd2b42cb54c87a8ab3cb6beb6bf07f3d564e21ac848913a05a8a3cd", size = 216978, upload-time = "2026-04-22T02:44:44.059Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/c5/a1bc0996af85757903cf2bf444a7824e68e0035ce63fb41d6f76f9def68b/gitpython-3.1.47-py3-none-any.whl", hash = "sha256:489f590edfd6d20571b2c0e72c6a6ac6915ee8b8cd04572330e3842207a78905", size = 209547, upload-time = "2026-04-22T02:44:41.271Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hf-xet" +version = "1.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/92/ec9ad04d0b5728dca387a45af7bc98fbb0d73b2118759f5f6038b61a57e8/hf_xet-1.4.3.tar.gz", hash = "sha256:8ddedb73c8c08928c793df2f3401ec26f95be7f7e516a7bee2fbb546f6676113", size = 670477, upload-time = "2026-03-31T22:40:07.874Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/43/724d307b34e353da0abd476e02f72f735cdd2bc86082dee1b32ea0bfee1d/hf_xet-1.4.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:7551659ba4f1e1074e9623996f28c3873682530aee0a846b7f2f066239228144", size = 3800935, upload-time = "2026-03-31T22:39:49.618Z" }, + { url = "https://files.pythonhosted.org/packages/2b/d2/8bee5996b699262edb87dbb54118d287c0e1b2fc78af7cdc41857ba5e3c4/hf_xet-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bee693ada985e7045997f05f081d0e12c4c08bd7626dc397f8a7c487e6c04f7f", size = 3558942, upload-time = "2026-03-31T22:39:47.938Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a1/e993d09cbe251196fb60812b09a58901c468127b7259d2bf0f68bf6088eb/hf_xet-1.4.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21644b404bb0100fe3857892f752c4d09642586fd988e61501c95bbf44b393a3", size = 4207657, upload-time = "2026-03-31T22:39:39.69Z" }, + { url = "https://files.pythonhosted.org/packages/64/44/9eb6d21e5c34c63e5e399803a6932fa983cabdf47c0ecbcfe7ea97684b8c/hf_xet-1.4.3-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:987f09cfe418237812896a6736b81b1af02a3a6dcb4b4944425c4c4fca7a7cf8", size = 3986765, upload-time = "2026-03-31T22:39:37.936Z" }, + { url = "https://files.pythonhosted.org/packages/ea/7b/8ad6f16fdb82f5f7284a34b5ec48645bd575bdcd2f6f0d1644775909c486/hf_xet-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:60cf7fc43a99da0a853345cf86d23738c03983ee5249613a6305d3e57a5dca74", size = 4188162, upload-time = "2026-03-31T22:39:58.382Z" }, + { url = "https://files.pythonhosted.org/packages/1b/c4/39d6e136cbeea9ca5a23aad4b33024319222adbdc059ebcda5fc7d9d5ff4/hf_xet-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2815a49a7a59f3e2edf0cf113ae88e8cb2ca2a221bf353fb60c609584f4884d4", size = 4424525, upload-time = "2026-03-31T22:40:00.225Z" }, + { url = "https://files.pythonhosted.org/packages/46/f2/adc32dae6bdbc367853118b9878139ac869419a4ae7ba07185dc31251b76/hf_xet-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:42ee323265f1e6a81b0e11094564fb7f7e0ec75b5105ffd91ae63f403a11931b", size = 3671610, upload-time = "2026-03-31T22:40:10.42Z" }, + { url = "https://files.pythonhosted.org/packages/e2/19/25d897dcc3f81953e0c2cde9ec186c7a0fee413eb0c9a7a9130d87d94d3a/hf_xet-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:27c976ba60079fb8217f485b9c5c7fcd21c90b0367753805f87cb9f3cdc4418a", size = 3528529, upload-time = "2026-03-31T22:40:09.106Z" }, + { url = "https://files.pythonhosted.org/packages/ec/36/3e8f85ca9fe09b8de2b2e10c63b3b3353d7dda88a0b3d426dffbe7b8313b/hf_xet-1.4.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5251d5ece3a81815bae9abab41cf7ddb7bcb8f56411bce0827f4a3071c92fdc6", size = 3801019, upload-time = "2026-03-31T22:39:56.651Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9c/defb6cb1de28bccb7bd8d95f6e60f72a3d3fa4cb3d0329c26fb9a488bfe7/hf_xet-1.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1feb0f3abeacee143367c326a128a2e2b60868ec12a36c225afb1d6c5a05e6d2", size = 3558746, upload-time = "2026-03-31T22:39:54.766Z" }, + { url = "https://files.pythonhosted.org/packages/c1/bd/8d001191893178ff8e826e46ad5299446e62b93cd164e17b0ffea08832ec/hf_xet-1.4.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8b301fc150290ca90b4fccd079829b84bb4786747584ae08b94b4577d82fb791", size = 4207692, upload-time = "2026-03-31T22:39:46.246Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/6790b402803250e9936435613d3a78b9aaeee7973439f0918848dde58309/hf_xet-1.4.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d972fbe95ddc0d3c0fc49b31a8a69f47db35c1e3699bf316421705741aab6653", size = 3986281, upload-time = "2026-03-31T22:39:44.648Z" }, + { url = "https://files.pythonhosted.org/packages/51/56/ea62552fe53db652a9099eda600b032d75554d0e86c12a73824bfedef88b/hf_xet-1.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c5b48db1ee344a805a1b9bd2cda9b6b65fe77ed3787bd6e87ad5521141d317cd", size = 4187414, upload-time = "2026-03-31T22:40:04.951Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f5/bc1456d4638061bea997e6d2db60a1a613d7b200e0755965ec312dc1ef79/hf_xet-1.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:22bdc1f5fb8b15bf2831440b91d1c9bbceeb7e10c81a12e8d75889996a5c9da8", size = 4424368, upload-time = "2026-03-31T22:40:06.347Z" }, + { url = "https://files.pythonhosted.org/packages/e4/76/ab597bae87e1f06d18d3ecb8ed7f0d3c9a37037fc32ce76233d369273c64/hf_xet-1.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:0392c79b7cf48418cd61478c1a925246cf10639f4cd9d94368d8ca1e8df9ea07", size = 3672280, upload-time = "2026-03-31T22:40:16.401Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/2e462d34e23a09a74d73785dbed71cc5dbad82a72eee2ad60a72a554155d/hf_xet-1.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:681c92a07796325778a79d76c67011764ecc9042a8c3579332b61b63ae512075", size = 3528945, upload-time = "2026-03-31T22:40:14.995Z" }, + { url = "https://files.pythonhosted.org/packages/ac/9f/9c23e4a447b8f83120798f9279d0297a4d1360bdbf59ef49ebec78fe2545/hf_xet-1.4.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d0da85329eaf196e03e90b84c2d0aca53bd4573d097a75f99609e80775f98025", size = 3805048, upload-time = "2026-03-31T22:39:53.105Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f8/7aacb8e5f4a7899d39c787b5984e912e6c18b11be136ef13947d7a66d265/hf_xet-1.4.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:e23717ce4186b265f69afa66e6f0069fe7efbf331546f5c313d00e123dc84583", size = 3562178, upload-time = "2026-03-31T22:39:51.295Z" }, + { url = "https://files.pythonhosted.org/packages/df/9a/a24b26dc8a65f0ecc0fe5be981a19e61e7ca963b85e062c083f3a9100529/hf_xet-1.4.3-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc360b70c815bf340ed56c7b8c63aacf11762a4b099b2fe2c9bd6d6068668c08", size = 4212320, upload-time = "2026-03-31T22:39:42.922Z" }, + { url = "https://files.pythonhosted.org/packages/53/60/46d493db155d2ee2801b71fb1b0fd67696359047fdd8caee2c914cc50c79/hf_xet-1.4.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:39f2d2e9654cd9b4319885733993807aab6de9dfbd34c42f0b78338d6617421f", size = 3991546, upload-time = "2026-03-31T22:39:41.335Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f5/067363e1c96c6b17256910830d1b54099d06287e10f4ec6ec4e7e08371fc/hf_xet-1.4.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:49ad8a8cead2b56051aa84d7fce3e1335efe68df3cf6c058f22a65513885baac", size = 4193200, upload-time = "2026-03-31T22:40:01.936Z" }, + { url = "https://files.pythonhosted.org/packages/42/4b/53951592882d9c23080c7644542fda34a3813104e9e11fa1a7d82d419cb8/hf_xet-1.4.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7716d62015477a70ea272d2d68cd7cad140f61c52ee452e133e139abfe2c17ba", size = 4429392, upload-time = "2026-03-31T22:40:03.492Z" }, + { url = "https://files.pythonhosted.org/packages/8a/21/75a6c175b4e79662ad8e62f46a40ce341d8d6b206b06b4320d07d55b188c/hf_xet-1.4.3-cp37-abi3-win_amd64.whl", hash = "sha256:6b591fcad34e272a5b02607485e4f2a1334aebf1bc6d16ce8eb1eb8978ac2021", size = 3677359, upload-time = "2026-03-31T22:40:13.619Z" }, + { url = "https://files.pythonhosted.org/packages/8a/7c/44314ecd0e89f8b2b51c9d9e5e7a60a9c1c82024ac471d415860557d3cd8/hf_xet-1.4.3-cp37-abi3-win_arm64.whl", hash = "sha256:7c2c7e20bcfcc946dc67187c203463f5e932e395845d098cc2a93f5b67ca0b47", size = 3533664, upload-time = "2026-03-31T22:40:12.152Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "huggingface-hub" +version = "1.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/56/52/1b54cb569509c725a32c1315261ac9fd0e6b91bbbf74d86fca10d3376164/huggingface_hub-1.12.0.tar.gz", hash = "sha256:7c3fe85e24b652334e5d456d7a812cd9a071e75630fac4365d9165ab5e4a34b6", size = 763091, upload-time = "2026-04-24T13:32:08.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/2b/ef03ddb96bd1123503c2bd6932001020292deea649e9bf4caa2cb65a85bf/huggingface_hub-1.12.0-py3-none-any.whl", hash = "sha256:d74939969585ee35748bd66de09baf84099d461bda7287cd9043bfb99b0e424d", size = 646806, upload-time = "2026-04-24T13:32:06.717Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[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 = "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.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/5e/4ec91646aee381d01cdb9974e30882c9cd3b8c5d1079d6b5ff4af522439a/jiter-0.13.0.tar.gz", hash = "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4", size = 164847, upload-time = "2026-02-02T12:37:56.441Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/5a/41da76c5ea07bec1b0472b6b2fdb1b651074d504b19374d7e130e0cdfb25/jiter-0.13.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2ffc63785fd6c7977defe49b9824ae6ce2b2e2b77ce539bdaf006c26da06342e", size = 311164, upload-time = "2026-02-02T12:35:17.688Z" }, + { url = "https://files.pythonhosted.org/packages/40/cb/4a1bf994a3e869f0d39d10e11efb471b76d0ad70ecbfb591427a46c880c2/jiter-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4a638816427006c1e3f0013eb66d391d7a3acda99a7b0cf091eff4497ccea33a", size = 320296, upload-time = "2026-02-02T12:35:19.828Z" }, + { url = "https://files.pythonhosted.org/packages/09/82/acd71ca9b50ecebadc3979c541cd717cce2fe2bc86236f4fa597565d8f1a/jiter-0.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19928b5d1ce0ff8c1ee1b9bdef3b5bfc19e8304f1b904e436caf30bc15dc6cf5", size = 352742, upload-time = "2026-02-02T12:35:21.258Z" }, + { url = "https://files.pythonhosted.org/packages/71/03/d1fc996f3aecfd42eb70922edecfb6dd26421c874503e241153ad41df94f/jiter-0.13.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:309549b778b949d731a2f0e1594a3f805716be704a73bf3ad9a807eed5eb5721", size = 363145, upload-time = "2026-02-02T12:35:24.653Z" }, + { url = "https://files.pythonhosted.org/packages/f1/61/a30492366378cc7a93088858f8991acd7d959759fe6138c12a4644e58e81/jiter-0.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcdabaea26cb04e25df3103ce47f97466627999260290349a88c8136ecae0060", size = 487683, upload-time = "2026-02-02T12:35:26.162Z" }, + { url = "https://files.pythonhosted.org/packages/20/4e/4223cffa9dbbbc96ed821c5aeb6bca510848c72c02086d1ed3f1da3d58a7/jiter-0.13.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3a377af27b236abbf665a69b2bdd680e3b5a0bd2af825cd3b81245279a7606c", size = 373579, upload-time = "2026-02-02T12:35:27.582Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c9/b0489a01329ab07a83812d9ebcffe7820a38163c6d9e7da644f926ff877c/jiter-0.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe49d3ff6db74321f144dff9addd4a5874d3105ac5ba7c5b77fac099cfae31ae", size = 362904, upload-time = "2026-02-02T12:35:28.925Z" }, + { url = "https://files.pythonhosted.org/packages/05/af/53e561352a44afcba9a9bc67ee1d320b05a370aed8df54eafe714c4e454d/jiter-0.13.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2113c17c9a67071b0f820733c0893ed1d467b5fcf4414068169e5c2cabddb1e2", size = 392380, upload-time = "2026-02-02T12:35:30.385Z" }, + { url = "https://files.pythonhosted.org/packages/76/2a/dd805c3afb8ed5b326c5ae49e725d1b1255b9754b1b77dbecdc621b20773/jiter-0.13.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ab1185ca5c8b9491b55ebf6c1e8866b8f68258612899693e24a92c5fdb9455d5", size = 517939, upload-time = "2026-02-02T12:35:31.865Z" }, + { url = "https://files.pythonhosted.org/packages/20/2a/7b67d76f55b8fe14c937e7640389612f05f9a4145fc28ae128aaa5e62257/jiter-0.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9621ca242547edc16400981ca3231e0c91c0c4c1ab8573a596cd9bb3575d5c2b", size = 551696, upload-time = "2026-02-02T12:35:33.306Z" }, + { url = "https://files.pythonhosted.org/packages/85/9c/57cdd64dac8f4c6ab8f994fe0eb04dc9fd1db102856a4458fcf8a99dfa62/jiter-0.13.0-cp310-cp310-win32.whl", hash = "sha256:a7637d92b1c9d7a771e8c56f445c7f84396d48f2e756e5978840ecba2fac0894", size = 204592, upload-time = "2026-02-02T12:35:34.58Z" }, + { url = "https://files.pythonhosted.org/packages/a7/38/f4f3ea5788b8a5bae7510a678cdc747eda0c45ffe534f9878ff37e7cf3b3/jiter-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c1b609e5cbd2f52bb74fb721515745b407df26d7b800458bd97cb3b972c29e7d", size = 206016, upload-time = "2026-02-02T12:35:36.435Z" }, + { url = "https://files.pythonhosted.org/packages/71/29/499f8c9eaa8a16751b1c0e45e6f5f1761d180da873d417996cc7bddc8eef/jiter-0.13.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ea026e70a9a28ebbdddcbcf0f1323128a8db66898a06eaad3a4e62d2f554d096", size = 311157, upload-time = "2026-02-02T12:35:37.758Z" }, + { url = "https://files.pythonhosted.org/packages/50/f6/566364c777d2ab450b92100bea11333c64c38d32caf8dc378b48e5b20c46/jiter-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66aa3e663840152d18cc8ff1e4faad3dd181373491b9cfdc6004b92198d67911", size = 319729, upload-time = "2026-02-02T12:35:39.246Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/560f13ec5e4f116d8ad2658781646cca91b617ae3b8758d4a5076b278f70/jiter-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3524798e70655ff19aec58c7d05adb1f074fecff62da857ea9be2b908b6d701", size = 354766, upload-time = "2026-02-02T12:35:40.662Z" }, + { url = "https://files.pythonhosted.org/packages/7c/0d/061faffcfe94608cbc28a0d42a77a74222bdf5055ccdbe5fd2292b94f510/jiter-0.13.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec7e287d7fbd02cb6e22f9a00dd9c9cd504c40a61f2c61e7e1f9690a82726b4c", size = 362587, upload-time = "2026-02-02T12:35:42.025Z" }, + { url = "https://files.pythonhosted.org/packages/92/c9/c66a7864982fd38a9773ec6e932e0398d1262677b8c60faecd02ffb67bf3/jiter-0.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47455245307e4debf2ce6c6e65a717550a0244231240dcf3b8f7d64e4c2f22f4", size = 487537, upload-time = "2026-02-02T12:35:43.459Z" }, + { url = "https://files.pythonhosted.org/packages/6c/86/84eb4352cd3668f16d1a88929b5888a3fe0418ea8c1dfc2ad4e7bf6e069a/jiter-0.13.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ee9da221dca6e0429c2704c1b3655fe7b025204a71d4d9b73390c759d776d165", size = 373717, upload-time = "2026-02-02T12:35:44.928Z" }, + { url = "https://files.pythonhosted.org/packages/6e/09/9fe4c159358176f82d4390407a03f506a8659ed13ca3ac93a843402acecf/jiter-0.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24ab43126d5e05f3d53a36a8e11eb2f23304c6c1117844aaaf9a0aa5e40b5018", size = 362683, upload-time = "2026-02-02T12:35:46.636Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5e/85f3ab9caca0c1d0897937d378b4a515cae9e119730563572361ea0c48ae/jiter-0.13.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9da38b4fedde4fb528c740c2564628fbab737166a0e73d6d46cb4bb5463ff411", size = 392345, upload-time = "2026-02-02T12:35:48.088Z" }, + { url = "https://files.pythonhosted.org/packages/12/4c/05b8629ad546191939e6f0c2f17e29f542a398f4a52fb987bc70b6d1eb8b/jiter-0.13.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0b34c519e17658ed88d5047999a93547f8889f3c1824120c26ad6be5f27b6cf5", size = 517775, upload-time = "2026-02-02T12:35:49.482Z" }, + { url = "https://files.pythonhosted.org/packages/4d/88/367ea2eb6bc582c7052e4baf5ddf57ebe5ab924a88e0e09830dfb585c02d/jiter-0.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2a6394e6af690d462310a86b53c47ad75ac8c21dc79f120714ea449979cb1d3", size = 551325, upload-time = "2026-02-02T12:35:51.104Z" }, + { url = "https://files.pythonhosted.org/packages/f3/12/fa377ffb94a2f28c41afaed093e0d70cfe512035d5ecb0cad0ae4792d35e/jiter-0.13.0-cp311-cp311-win32.whl", hash = "sha256:0f0c065695f616a27c920a56ad0d4fc46415ef8b806bf8fc1cacf25002bd24e1", size = 204709, upload-time = "2026-02-02T12:35:52.467Z" }, + { url = "https://files.pythonhosted.org/packages/cb/16/8e8203ce92f844dfcd3d9d6a5a7322c77077248dbb12da52d23193a839cd/jiter-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0733312953b909688ae3c2d58d043aa040f9f1a6a75693defed7bc2cc4bf2654", size = 204560, upload-time = "2026-02-02T12:35:53.925Z" }, + { url = "https://files.pythonhosted.org/packages/44/26/97cc40663deb17b9e13c3a5cf29251788c271b18ee4d262c8f94798b8336/jiter-0.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:5d9b34ad56761b3bf0fbe8f7e55468704107608512350962d3317ffd7a4382d5", size = 189608, upload-time = "2026-02-02T12:35:55.304Z" }, + { url = "https://files.pythonhosted.org/packages/2e/30/7687e4f87086829955013ca12a9233523349767f69653ebc27036313def9/jiter-0.13.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0a2bd69fc1d902e89925fc34d1da51b2128019423d7b339a45d9e99c894e0663", size = 307958, upload-time = "2026-02-02T12:35:57.165Z" }, + { url = "https://files.pythonhosted.org/packages/c3/27/e57f9a783246ed95481e6749cc5002a8a767a73177a83c63ea71f0528b90/jiter-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f917a04240ef31898182f76a332f508f2cc4b57d2b4d7ad2dbfebbfe167eb505", size = 318597, upload-time = "2026-02-02T12:35:58.591Z" }, + { url = "https://files.pythonhosted.org/packages/cf/52/e5719a60ac5d4d7c5995461a94ad5ef962a37c8bf5b088390e6fad59b2ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e2b199f446d3e82246b4fd9236d7cb502dc2222b18698ba0d986d2fecc6152", size = 348821, upload-time = "2026-02-02T12:36:00.093Z" }, + { url = "https://files.pythonhosted.org/packages/61/db/c1efc32b8ba4c740ab3fc2d037d8753f67685f475e26b9d6536a4322bcdd/jiter-0.13.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04670992b576fa65bd056dbac0c39fe8bd67681c380cb2b48efa885711d9d726", size = 364163, upload-time = "2026-02-02T12:36:01.937Z" }, + { url = "https://files.pythonhosted.org/packages/55/8a/fb75556236047c8806995671a18e4a0ad646ed255276f51a20f32dceaeec/jiter-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1aff1fbdb803a376d4d22a8f63f8e7ccbce0b4890c26cc7af9e501ab339ef0", size = 483709, upload-time = "2026-02-02T12:36:03.41Z" }, + { url = "https://files.pythonhosted.org/packages/7e/16/43512e6ee863875693a8e6f6d532e19d650779d6ba9a81593ae40a9088ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b3fb8c2053acaef8580809ac1d1f7481a0a0bdc012fd7f5d8b18fb696a5a089", size = 370480, upload-time = "2026-02-02T12:36:04.791Z" }, + { url = "https://files.pythonhosted.org/packages/f8/4c/09b93e30e984a187bc8aaa3510e1ec8dcbdcd71ca05d2f56aac0492453aa/jiter-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdaba7d87e66f26a2c45d8cbadcbfc4bf7884182317907baf39cfe9775bb4d93", size = 360735, upload-time = "2026-02-02T12:36:06.994Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1b/46c5e349019874ec5dfa508c14c37e29864ea108d376ae26d90bee238cd7/jiter-0.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b88d649135aca526da172e48083da915ec086b54e8e73a425ba50999468cc08", size = 391814, upload-time = "2026-02-02T12:36:08.368Z" }, + { url = "https://files.pythonhosted.org/packages/15/9e/26184760e85baee7162ad37b7912797d2077718476bf91517641c92b3639/jiter-0.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e404ea551d35438013c64b4f357b0474c7abf9f781c06d44fcaf7a14c69ff9e2", size = 513990, upload-time = "2026-02-02T12:36:09.993Z" }, + { url = "https://files.pythonhosted.org/packages/e9/34/2c9355247d6debad57a0a15e76ab1566ab799388042743656e566b3b7de1/jiter-0.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f4748aad1b4a93c8bdd70f604d0f748cdc0e8744c5547798acfa52f10e79228", size = 548021, upload-time = "2026-02-02T12:36:11.376Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4a/9f2c23255d04a834398b9c2e0e665382116911dc4d06b795710503cdad25/jiter-0.13.0-cp312-cp312-win32.whl", hash = "sha256:0bf670e3b1445fc4d31612199f1744f67f889ee1bbae703c4b54dc097e5dd394", size = 203024, upload-time = "2026-02-02T12:36:12.682Z" }, + { url = "https://files.pythonhosted.org/packages/09/ee/f0ae675a957ae5a8f160be3e87acea6b11dc7b89f6b7ab057e77b2d2b13a/jiter-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:15db60e121e11fe186c0b15236bd5d18381b9ddacdcf4e659feb96fc6c969c92", size = 205424, upload-time = "2026-02-02T12:36:13.93Z" }, + { url = "https://files.pythonhosted.org/packages/1b/02/ae611edf913d3cbf02c97cdb90374af2082c48d7190d74c1111dde08bcdd/jiter-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:41f92313d17989102f3cb5dd533a02787cdb99454d494344b0361355da52fcb9", size = 186818, upload-time = "2026-02-02T12:36:15.308Z" }, + { url = "https://files.pythonhosted.org/packages/91/9c/7ee5a6ff4b9991e1a45263bfc46731634c4a2bde27dfda6c8251df2d958c/jiter-0.13.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1f8a55b848cbabf97d861495cd65f1e5c590246fabca8b48e1747c4dfc8f85bf", size = 306897, upload-time = "2026-02-02T12:36:16.748Z" }, + { url = "https://files.pythonhosted.org/packages/7c/02/be5b870d1d2be5dd6a91bdfb90f248fbb7dcbd21338f092c6b89817c3dbf/jiter-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f556aa591c00f2c45eb1b89f68f52441a016034d18b65da60e2d2875bbbf344a", size = 317507, upload-time = "2026-02-02T12:36:18.351Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/b25d2ec333615f5f284f3a4024f7ce68cfa0604c322c6808b2344c7f5d2b/jiter-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1d61da332ec412350463891923f960c3073cf1aae93b538f0bb4c8cd46efb", size = 350560, upload-time = "2026-02-02T12:36:19.746Z" }, + { url = "https://files.pythonhosted.org/packages/be/ec/74dcb99fef0aca9fbe56b303bf79f6bd839010cb18ad41000bf6cc71eec0/jiter-0.13.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3097d665a27bc96fd9bbf7f86178037db139f319f785e4757ce7ccbf390db6c2", size = 363232, upload-time = "2026-02-02T12:36:21.243Z" }, + { url = "https://files.pythonhosted.org/packages/1b/37/f17375e0bb2f6a812d4dd92d7616e41917f740f3e71343627da9db2824ce/jiter-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d01ecc3a8cbdb6f25a37bd500510550b64ddf9f7d64a107d92f3ccb25035d0f", size = 483727, upload-time = "2026-02-02T12:36:22.688Z" }, + { url = "https://files.pythonhosted.org/packages/77/d2/a71160a5ae1a1e66c1395b37ef77da67513b0adba73b993a27fbe47eb048/jiter-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed9bbc30f5d60a3bdf63ae76beb3f9db280d7f195dfcfa61af792d6ce912d159", size = 370799, upload-time = "2026-02-02T12:36:24.106Z" }, + { url = "https://files.pythonhosted.org/packages/01/99/ed5e478ff0eb4e8aa5fd998f9d69603c9fd3f32de3bd16c2b1194f68361c/jiter-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fbafb6e88256f4454de33c1f40203d09fc33ed19162a68b3b257b29ca7f663", size = 359120, upload-time = "2026-02-02T12:36:25.519Z" }, + { url = "https://files.pythonhosted.org/packages/16/be/7ffd08203277a813f732ba897352797fa9493faf8dc7995b31f3d9cb9488/jiter-0.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5467696f6b827f1116556cb0db620440380434591e93ecee7fd14d1a491b6daa", size = 390664, upload-time = "2026-02-02T12:36:26.866Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/e0787856196d6d346264d6dcccb01f741e5f0bd014c1d9a2ebe149caf4f3/jiter-0.13.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2d08c9475d48b92892583df9da592a0e2ac49bcd41fae1fec4f39ba6cf107820", size = 513543, upload-time = "2026-02-02T12:36:28.217Z" }, + { url = "https://files.pythonhosted.org/packages/65/50/ecbd258181c4313cf79bca6c88fb63207d04d5bf5e4f65174114d072aa55/jiter-0.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:aed40e099404721d7fcaf5b89bd3b4568a4666358bcac7b6b15c09fb6252ab68", size = 547262, upload-time = "2026-02-02T12:36:29.678Z" }, + { url = "https://files.pythonhosted.org/packages/27/da/68f38d12e7111d2016cd198161b36e1f042bd115c169255bcb7ec823a3bf/jiter-0.13.0-cp313-cp313-win32.whl", hash = "sha256:36ebfbcffafb146d0e6ffb3e74d51e03d9c35ce7c625c8066cdbfc7b953bdc72", size = 200630, upload-time = "2026-02-02T12:36:31.808Z" }, + { url = "https://files.pythonhosted.org/packages/25/65/3bd1a972c9a08ecd22eb3b08a95d1941ebe6938aea620c246cf426ae09c2/jiter-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:8d76029f077379374cf0dbc78dbe45b38dec4a2eb78b08b5194ce836b2517afc", size = 202602, upload-time = "2026-02-02T12:36:33.679Z" }, + { url = "https://files.pythonhosted.org/packages/15/fe/13bd3678a311aa67686bb303654792c48206a112068f8b0b21426eb6851e/jiter-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb7613e1a427cfcb6ea4544f9ac566b93d5bf67e0d48c787eca673ff9c9dff2b", size = 185939, upload-time = "2026-02-02T12:36:35.065Z" }, + { url = "https://files.pythonhosted.org/packages/49/19/a929ec002ad3228bc97ca01dbb14f7632fffdc84a95ec92ceaf4145688ae/jiter-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fa476ab5dd49f3bf3a168e05f89358c75a17608dbabb080ef65f96b27c19ab10", size = 316616, upload-time = "2026-02-02T12:36:36.579Z" }, + { url = "https://files.pythonhosted.org/packages/52/56/d19a9a194afa37c1728831e5fb81b7722c3de18a3109e8f282bfc23e587a/jiter-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade8cb6ff5632a62b7dbd4757d8c5573f7a2e9ae285d6b5b841707d8363205ef", size = 346850, upload-time = "2026-02-02T12:36:38.058Z" }, + { url = "https://files.pythonhosted.org/packages/36/4a/94e831c6bf287754a8a019cb966ed39ff8be6ab78cadecf08df3bb02d505/jiter-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9950290340acc1adaded363edd94baebcee7dabdfa8bee4790794cd5cfad2af6", size = 358551, upload-time = "2026-02-02T12:36:39.417Z" }, + { url = "https://files.pythonhosted.org/packages/a2/ec/a4c72c822695fa80e55d2b4142b73f0012035d9fcf90eccc56bc060db37c/jiter-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2b4972c6df33731aac0742b64fd0d18e0a69bc7d6e03108ce7d40c85fd9e3e6d", size = 201950, upload-time = "2026-02-02T12:36:40.791Z" }, + { url = "https://files.pythonhosted.org/packages/b6/00/393553ec27b824fbc29047e9c7cd4a3951d7fbe4a76743f17e44034fa4e4/jiter-0.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:701a1e77d1e593c1b435315ff625fd071f0998c5f02792038a5ca98899261b7d", size = 185852, upload-time = "2026-02-02T12:36:42.077Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f5/f1997e987211f6f9bd71b8083047b316208b4aca0b529bb5f8c96c89ef3e/jiter-0.13.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:cc5223ab19fe25e2f0bf2643204ad7318896fe3729bf12fde41b77bfc4fafff0", size = 308804, upload-time = "2026-02-02T12:36:43.496Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8f/5482a7677731fd44881f0204981ce2d7175db271f82cba2085dd2212e095/jiter-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9776ebe51713acf438fd9b4405fcd86893ae5d03487546dae7f34993217f8a91", size = 318787, upload-time = "2026-02-02T12:36:45.071Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b9/7257ac59778f1cd025b26a23c5520a36a424f7f1b068f2442a5b499b7464/jiter-0.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879e768938e7b49b5e90b7e3fecc0dbec01b8cb89595861fb39a8967c5220d09", size = 353880, upload-time = "2026-02-02T12:36:47.365Z" }, + { url = "https://files.pythonhosted.org/packages/c3/87/719eec4a3f0841dad99e3d3604ee4cba36af4419a76f3cb0b8e2e691ad67/jiter-0.13.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:682161a67adea11e3aae9038c06c8b4a9a71023228767477d683f69903ebc607", size = 366702, upload-time = "2026-02-02T12:36:48.871Z" }, + { url = "https://files.pythonhosted.org/packages/d2/65/415f0a75cf6921e43365a1bc227c565cb949caca8b7532776e430cbaa530/jiter-0.13.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a13b68cd1cd8cc9de8f244ebae18ccb3e4067ad205220ef324c39181e23bbf66", size = 486319, upload-time = "2026-02-02T12:36:53.006Z" }, + { url = "https://files.pythonhosted.org/packages/54/a2/9e12b48e82c6bbc6081fd81abf915e1443add1b13d8fc586e1d90bb02bb8/jiter-0.13.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87ce0f14c6c08892b610686ae8be350bf368467b6acd5085a5b65441e2bf36d2", size = 372289, upload-time = "2026-02-02T12:36:54.593Z" }, + { url = "https://files.pythonhosted.org/packages/4e/c1/e4693f107a1789a239c759a432e9afc592366f04e901470c2af89cfd28e1/jiter-0.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c365005b05505a90d1c47856420980d0237adf82f70c4aff7aebd3c1cc143ad", size = 360165, upload-time = "2026-02-02T12:36:56.112Z" }, + { url = "https://files.pythonhosted.org/packages/17/08/91b9ea976c1c758240614bd88442681a87672eebc3d9a6dde476874e706b/jiter-0.13.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1317fdffd16f5873e46ce27d0e0f7f4f90f0cdf1d86bf6abeaea9f63ca2c401d", size = 389634, upload-time = "2026-02-02T12:36:57.495Z" }, + { url = "https://files.pythonhosted.org/packages/18/23/58325ef99390d6d40427ed6005bf1ad54f2577866594bcf13ce55675f87d/jiter-0.13.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c05b450d37ba0c9e21c77fef1f205f56bcee2330bddca68d344baebfc55ae0df", size = 514933, upload-time = "2026-02-02T12:36:58.909Z" }, + { url = "https://files.pythonhosted.org/packages/5b/25/69f1120c7c395fd276c3996bb8adefa9c6b84c12bb7111e5c6ccdcd8526d/jiter-0.13.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:775e10de3849d0631a97c603f996f518159272db00fdda0a780f81752255ee9d", size = 548842, upload-time = "2026-02-02T12:37:00.433Z" }, + { url = "https://files.pythonhosted.org/packages/18/05/981c9669d86850c5fbb0d9e62bba144787f9fba84546ba43d624ee27ef29/jiter-0.13.0-cp314-cp314-win32.whl", hash = "sha256:632bf7c1d28421c00dd8bbb8a3bac5663e1f57d5cd5ed962bce3c73bf62608e6", size = 202108, upload-time = "2026-02-02T12:37:01.718Z" }, + { url = "https://files.pythonhosted.org/packages/8d/96/cdcf54dd0b0341db7d25413229888a346c7130bd20820530905fdb65727b/jiter-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:f22ef501c3f87ede88f23f9b11e608581c14f04db59b6a801f354397ae13739f", size = 204027, upload-time = "2026-02-02T12:37:03.075Z" }, + { url = "https://files.pythonhosted.org/packages/fb/f9/724bcaaab7a3cd727031fe4f6995cb86c4bd344909177c186699c8dec51a/jiter-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:07b75fe09a4ee8e0c606200622e571e44943f47254f95e2436c8bdcaceb36d7d", size = 187199, upload-time = "2026-02-02T12:37:04.414Z" }, + { url = "https://files.pythonhosted.org/packages/62/92/1661d8b9fd6a3d7a2d89831db26fe3c1509a287d83ad7838831c7b7a5c7e/jiter-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:964538479359059a35fb400e769295d4b315ae61e4105396d355a12f7fef09f0", size = 318423, upload-time = "2026-02-02T12:37:05.806Z" }, + { url = "https://files.pythonhosted.org/packages/4f/3b/f77d342a54d4ebcd128e520fc58ec2f5b30a423b0fd26acdfc0c6fef8e26/jiter-0.13.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e104da1db1c0991b3eaed391ccd650ae8d947eab1480c733e5a3fb28d4313e40", size = 351438, upload-time = "2026-02-02T12:37:07.189Z" }, + { url = "https://files.pythonhosted.org/packages/76/b3/ba9a69f0e4209bd3331470c723c2f5509e6f0482e416b612431a5061ed71/jiter-0.13.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e3a5f0cde8ff433b8e88e41aa40131455420fb3649a3c7abdda6145f8cb7202", size = 364774, upload-time = "2026-02-02T12:37:08.579Z" }, + { url = "https://files.pythonhosted.org/packages/b3/16/6cdb31fa342932602458dbb631bfbd47f601e03d2e4950740e0b2100b570/jiter-0.13.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57aab48f40be1db920a582b30b116fe2435d184f77f0e4226f546794cedd9cf0", size = 487238, upload-time = "2026-02-02T12:37:10.066Z" }, + { url = "https://files.pythonhosted.org/packages/ed/b1/956cc7abaca8d95c13aa8d6c9b3f3797241c246cd6e792934cc4c8b250d2/jiter-0.13.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7772115877c53f62beeb8fd853cab692dbc04374ef623b30f997959a4c0e7e95", size = 372892, upload-time = "2026-02-02T12:37:11.656Z" }, + { url = "https://files.pythonhosted.org/packages/26/c4/97ecde8b1e74f67b8598c57c6fccf6df86ea7861ed29da84629cdbba76c4/jiter-0.13.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1211427574b17b633cfceba5040de8081e5abf114f7a7602f73d2e16f9fdaa59", size = 360309, upload-time = "2026-02-02T12:37:13.244Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d7/eabe3cf46715854ccc80be2cd78dd4c36aedeb30751dbf85a1d08c14373c/jiter-0.13.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7beae3a3d3b5212d3a55d2961db3c292e02e302feb43fce6a3f7a31b90ea6dfe", size = 389607, upload-time = "2026-02-02T12:37:14.881Z" }, + { url = "https://files.pythonhosted.org/packages/df/2d/03963fc0804e6109b82decfb9974eb92df3797fe7222428cae12f8ccaa0c/jiter-0.13.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e5562a0f0e90a6223b704163ea28e831bd3a9faa3512a711f031611e6b06c939", size = 514986, upload-time = "2026-02-02T12:37:16.326Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/8c83b45eb3eb1c1e18d841fe30b4b5bc5619d781267ca9bc03e005d8fd0a/jiter-0.13.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:6c26a424569a59140fb51160a56df13f438a2b0967365e987889186d5fc2f6f9", size = 548756, upload-time = "2026-02-02T12:37:17.736Z" }, + { url = "https://files.pythonhosted.org/packages/47/66/eea81dfff765ed66c68fd2ed8c96245109e13c896c2a5015c7839c92367e/jiter-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6", size = 201196, upload-time = "2026-02-02T12:37:19.101Z" }, + { url = "https://files.pythonhosted.org/packages/ff/32/4ac9c7a76402f8f00d00842a7f6b83b284d0cf7c1e9d4227bc95aa6d17fa/jiter-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8", size = 204215, upload-time = "2026-02-02T12:37:20.495Z" }, + { url = "https://files.pythonhosted.org/packages/f9/8e/7def204fea9f9be8b3c21a6f2dd6c020cf56c7d5ff753e0e23ed7f9ea57e/jiter-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024", size = 187152, upload-time = "2026-02-02T12:37:22.124Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/3c29819a27178d0e461a8571fb63c6ae38be6dc36b78b3ec2876bbd6a910/jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b1cbfa133241d0e6bdab48dcdc2604e8ba81512f6bbd68ec3e8e1357dd3c316c", size = 307016, upload-time = "2026-02-02T12:37:42.755Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ae/60993e4b07b1ac5ebe46da7aa99fdbb802eb986c38d26e3883ac0125c4e0/jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:db367d8be9fad6e8ebbac4a7578b7af562e506211036cba2c06c3b998603c3d2", size = 305024, upload-time = "2026-02-02T12:37:44.774Z" }, + { url = "https://files.pythonhosted.org/packages/77/fa/2227e590e9cf98803db2811f172b2d6460a21539ab73006f251c66f44b14/jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45f6f8efb2f3b0603092401dc2df79fa89ccbc027aaba4174d2d4133ed661434", size = 339337, upload-time = "2026-02-02T12:37:46.668Z" }, + { url = "https://files.pythonhosted.org/packages/2d/92/015173281f7eb96c0ef580c997da8ef50870d4f7f4c9e03c845a1d62ae04/jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:597245258e6ad085d064780abfb23a284d418d3e61c57362d9449c6c7317ee2d", size = 346395, upload-time = "2026-02-02T12:37:48.09Z" }, + { url = "https://files.pythonhosted.org/packages/80/60/e50fa45dd7e2eae049f0ce964663849e897300433921198aef94b6ffa23a/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:3d744a6061afba08dd7ae375dcde870cffb14429b7477e10f67e9e6d68772a0a", size = 305169, upload-time = "2026-02-02T12:37:50.376Z" }, + { url = "https://files.pythonhosted.org/packages/d2/73/a009f41c5eed71c49bec53036c4b33555afcdee70682a18c6f66e396c039/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:ff732bd0a0e778f43d5009840f20b935e79087b4dc65bd36f1cd0f9b04b8ff7f", size = 303808, upload-time = "2026-02-02T12:37:52.092Z" }, + { url = "https://files.pythonhosted.org/packages/c4/10/528b439290763bff3d939268085d03382471b442f212dca4ff5f12802d43/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab44b178f7981fcaea7e0a5df20e773c663d06ffda0198f1a524e91b2fde7e59", size = 337384, upload-time = "2026-02-02T12:37:53.582Z" }, + { url = "https://files.pythonhosted.org/packages/67/8a/a342b2f0251f3dac4ca17618265d93bf244a2a4d089126e81e4c1056ac50/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb00b6d26db67a05fe3e12c76edc75f32077fb51deed13822dc648fa373bc19", size = 343768, upload-time = "2026-02-02T12:37:55.055Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" }, + { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" }, + { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" }, + { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" }, + { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" }, + { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" }, + { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" }, + { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" }, + { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" }, + { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" }, + { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" }, + { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" }, + { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" }, + { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" }, + { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" }, + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, +] + +[[package]] +name = "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 = "mpmath" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176, upload-time = "2026-01-26T02:42:59.784Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996, upload-time = "2026-01-26T02:43:01.674Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631, upload-time = "2026-01-26T02:43:03.169Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561, upload-time = "2026-01-26T02:43:04.733Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223, upload-time = "2026-01-26T02:43:06.695Z" }, + { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322, upload-time = "2026-01-26T02:43:08.472Z" }, + { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005, upload-time = "2026-01-26T02:43:10.127Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173, upload-time = "2026-01-26T02:43:11.731Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273, upload-time = "2026-01-26T02:43:13.063Z" }, + { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956, upload-time = "2026-01-26T02:43:14.843Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477, upload-time = "2026-01-26T02:43:16.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615, upload-time = "2026-01-26T02:43:17.84Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930, upload-time = "2026-01-26T02:43:19.06Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807, upload-time = "2026-01-26T02:43:20.286Z" }, + { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103, upload-time = "2026-01-26T02:43:21.508Z" }, + { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416, upload-time = "2026-01-26T02:43:22.703Z" }, + { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022, upload-time = "2026-01-26T02:43:23.77Z" }, + { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238, upload-time = "2026-01-26T02:43:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "multiprocess" +version = "0.70.19" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/f2/e783ac7f2aeeed14e9e12801f22529cc7e6b7ab80928d6dcce4e9f00922d/multiprocess-0.70.19.tar.gz", hash = "sha256:952021e0e6c55a4a9fe4cd787895b86e239a40e76802a789d6305398d3975897", size = 2079989, upload-time = "2026-01-19T06:47:39.744Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/b6/10832f96b499690854e574360be342a282f5f7dba58eff791299ff6c0637/multiprocess-0.70.19-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:02e5c35d7d6cd2bdc89c1858867f7bde4012837411023a4696c148c1bdd7c80e", size = 135131, upload-time = "2026-01-19T06:47:20.479Z" }, + { url = "https://files.pythonhosted.org/packages/99/50/faef2d8106534b0dc4a0b772668a1a99682696ebf17d3c0f13f2ed6a656a/multiprocess-0.70.19-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:79576c02d1207ec405b00cabf2c643c36070800cca433860e14539df7818b2aa", size = 135131, upload-time = "2026-01-19T06:47:21.879Z" }, + { url = "https://files.pythonhosted.org/packages/94/b1/0b71d18b76bf423c2e8ee00b31db37d17297ab3b4db44e188692afdca628/multiprocess-0.70.19-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c6b6d78d43a03b68014ca1f0b7937d965393a670c5de7c29026beb2258f2f896", size = 135134, upload-time = "2026-01-19T06:47:23.262Z" }, + { url = "https://files.pythonhosted.org/packages/7e/aa/714635c727dbfc251139226fa4eaf1b07f00dc12d9cd2eb25f931adaf873/multiprocess-0.70.19-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1bbf1b69af1cf64cd05f65337d9215b88079ec819cd0ea7bac4dab84e162efe7", size = 144743, upload-time = "2026-01-19T06:47:24.562Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e1/155f6abf5e6b5d9cef29b6d0167c180846157a4aca9b9bee1a217f67c959/multiprocess-0.70.19-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5be9ec7f0c1c49a4f4a6fd20d5dda4aeabc2d39a50f4ad53720f1cd02b3a7c2e", size = 144738, upload-time = "2026-01-19T06:47:26.636Z" }, + { url = "https://files.pythonhosted.org/packages/af/cb/f421c2869d75750a4f32301cc20c4b63fab6376e9a75c8e5e655bdeb3d9b/multiprocess-0.70.19-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1c3dce098845a0db43b32a0b76a228ca059a668071cfeaa0f40c36c0b1585d45", size = 144741, upload-time = "2026-01-19T06:47:27.985Z" }, + { url = "https://files.pythonhosted.org/packages/e3/45/8004d1e6b9185c1a444d6b55ac5682acf9d98035e54386d967366035a03a/multiprocess-0.70.19-py310-none-any.whl", hash = "sha256:97404393419dcb2a8385910864eedf47a3cadf82c66345b44f036420eb0b5d87", size = 134948, upload-time = "2026-01-19T06:47:32.325Z" }, + { url = "https://files.pythonhosted.org/packages/86/c2/dec9722dc3474c164a0b6bcd9a7ed7da542c98af8cabce05374abab35edd/multiprocess-0.70.19-py311-none-any.whl", hash = "sha256:928851ae7973aea4ce0eaf330bbdafb2e01398a91518d5c8818802845564f45c", size = 144457, upload-time = "2026-01-19T06:47:33.711Z" }, + { url = "https://files.pythonhosted.org/packages/71/70/38998b950a97ea279e6bd657575d22d1a2047256caf707d9a10fbce4f065/multiprocess-0.70.19-py312-none-any.whl", hash = "sha256:3a56c0e85dd5025161bac5ce138dcac1e49174c7d8e74596537e729fd5c53c28", size = 150281, upload-time = "2026-01-19T06:47:35.037Z" }, + { url = "https://files.pythonhosted.org/packages/7f/74/d2c27e03cb84251dfe7249b8e82923643c6d48fa4883b9476b025e7dc7eb/multiprocess-0.70.19-py313-none-any.whl", hash = "sha256:8d5eb4ec5017ba2fab4e34a747c6d2c2b6fecfe9e7236e77988db91580ada952", size = 156414, upload-time = "2026-01-19T06:47:35.915Z" }, + { url = "https://files.pythonhosted.org/packages/a0/61/af9115673a5870fd885247e2f1b68c4f1197737da315b520a91c757a861a/multiprocess-0.70.19-py314-none-any.whl", hash = "sha256:e8cc7fbdff15c0613f0a1f1f8744bef961b0a164c0ca29bdff53e9d2d93c5e5f", size = 160318, upload-time = "2026-01-19T06:47:37.497Z" }, + { url = "https://files.pythonhosted.org/packages/7e/82/69e539c4c2027f1e1697e09aaa2449243085a0edf81ae2c6341e84d769b6/multiprocess-0.70.19-py39-none-any.whl", hash = "sha256:0d4b4397ed669d371c81dcd1ef33fd384a44d6c3de1bd0ca7ac06d837720d3c5", size = 133477, upload-time = "2026-01-19T06:47:38.619Z" }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" }, + { url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" }, + { url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" }, + { url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" }, + { url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" }, + { url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" }, + { url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" }, + { url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" }, + { url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" }, + { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" }, + { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" }, + { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" }, + { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" }, + { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" }, + { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" }, + { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, + { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, + { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, + { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, + { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, + { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, + { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, + { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, + { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, + { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, + { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, + { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, + { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, + { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, + { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, + { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, + { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, + { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, + { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, + { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, + { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, + { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, + { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, + { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, + { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, + { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, + { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, + { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, + { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, + { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, + { url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" }, + { url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" }, + { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" }, +] + +[[package]] +name = "nvidia-cublas" +version = "13.1.0.3" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2", size = 542851226, upload-time = "2025-10-09T08:59:04.818Z" }, + { url = "https://files.pythonhosted.org/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171", size = 423133236, upload-time = "2025-10-09T08:59:32.536Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime" +version = "13.0.96" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu13" +version = "9.19.0.56" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" }, + { url = "https://files.pythonhosted.org/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf", size = 366066321, upload-time = "2026-02-03T20:44:52.837Z" }, +] + +[[package]] +name = "nvidia-cufft" +version = "12.0.0.61" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, +] + +[[package]] +name = "nvidia-cufile" +version = "1.15.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, +] + +[[package]] +name = "nvidia-curand" +version = "10.4.0.35" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, +] + +[[package]] +name = "nvidia-cusolver" +version = "12.0.4.66" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas" }, + { name = "nvidia-cusparse" }, + { name = "nvidia-nvjitlink" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, +] + +[[package]] +name = "nvidia-cusparse" +version = "12.6.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu13" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c", size = 220791277, upload-time = "2025-08-13T19:22:40.982Z" }, + { url = "https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd", size = 169884119, upload-time = "2025-08-13T19:23:41.967Z" }, +] + +[[package]] +name = "nvidia-nccl-cu13" +version = "2.28.9" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643", size = 196561677, upload-time = "2025-11-18T05:49:03.45Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42", size = 196493177, upload-time = "2025-11-18T05:49:17.677Z" }, +] + +[[package]] +name = "nvidia-nvjitlink" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, +] + +[[package]] +name = "nvidia-nvshmem-cu13" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, + { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, +] + +[[package]] +name = "nvidia-nvtx" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, + { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, +] + +[[package]] +name = "openai" +version = "2.30.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/88/15/52580c8fbc16d0675d516e8749806eda679b16de1e4434ea06fb6feaa610/openai-2.30.0.tar.gz", hash = "sha256:92f7661c990bda4b22a941806c83eabe4896c3094465030dd882a71abe80c885", size = 676084, upload-time = "2026-03-25T22:08:59.96Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/9e/5bfa2270f902d5b92ab7d41ce0475b8630572e71e349b2a4996d14bdda93/openai-2.30.0-py3-none-any.whl", hash = "sha256:9a5ae616888eb2748ec5e0c5b955a51592e0b201a11f4262db920f2a78c5231d", size = 1146656, upload-time = "2026-03-25T22:08:58.2Z" }, +] + +[[package]] +name = "openenv" +version = "0.1.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/35/94/c47e8f7303452793a3519c8cbc1b31dfffdedd13aaed821958ab3f152927/openenv-0.1.13.tar.gz", hash = "sha256:726971d2289472c1c20261436bcccdf3edfcf0b201d16aec127815bd83bfcb3d", size = 5112, upload-time = "2020-12-16T11:49:39.777Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/7f/e6f4467528161b8f0eb2ec784f4bbcd1fa9ea7acad13c0fb18597013e83b/openenv-0.1.13-py3-none-any.whl", hash = "sha256:813249d7f526f40c6e8b325f705294761a5bc887b9144c3383fa2bae7baa7726", size = 12080, upload-time = "2020-12-16T11:49:38.816Z" }, +] + +[[package]] +name = "osint-rl-env" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "fastapi" }, + { name = "openai" }, + { name = "openenv" }, + { name = "requests" }, + { name = "uvicorn" }, +] + +[package.optional-dependencies] +dev = [ + { name = "pytest" }, +] +train = [ + { name = "accelerate" }, + { name = "datasets" }, + { name = "peft" }, + { name = "pillow" }, + { name = "torchvision" }, + { name = "transformers" }, + { name = "trl" }, + { name = "wandb" }, +] + +[package.metadata] +requires-dist = [ + { name = "accelerate", marker = "extra == 'train'", specifier = ">=0.33.0" }, + { name = "datasets", marker = "extra == 'train'", specifier = ">=2.20.0" }, + { name = "fastapi", specifier = ">=0.115.0" }, + { name = "openai", specifier = ">=1.40.0" }, + { name = "openenv", specifier = ">=0.1.13" }, + { name = "peft", marker = "extra == 'train'", specifier = ">=0.11.0" }, + { name = "pillow", marker = "extra == 'train'" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, + { name = "requests", specifier = ">=2.32.3" }, + { name = "torchvision", marker = "extra == 'train'" }, + { name = "transformers", marker = "extra == 'train'", specifier = ">=4.45.0" }, + { name = "trl", marker = "extra == 'train'", specifier = ">=0.15.0" }, + { name = "uvicorn", specifier = ">=0.30.0" }, + { name = "wandb", marker = "extra == 'train'" }, +] +provides-extras = ["dev", "train"] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043", size = 4645855, upload-time = "2026-03-31T06:48:30.816Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/35/6411db530c618e0e0005187e35aa02ce60ae4c4c4d206964a2f978217c27/pandas-3.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a727a73cbdba2f7458dc82449e2315899d5140b449015d822f515749a46cbbe0", size = 10326926, upload-time = "2026-03-31T06:46:08.29Z" }, + { url = "https://files.pythonhosted.org/packages/c4/d3/b7da1d5d7dbdc5ef52ed7debd2b484313b832982266905315dad5a0bf0b1/pandas-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbbd4aa20ca51e63b53bbde6a0fa4254b1aaabb74d2f542df7a7959feb1d760c", size = 9926987, upload-time = "2026-03-31T06:46:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/52/77/9b1c2d6070b5dbe239a7bc889e21bfa58720793fb902d1e070695d87c6d0/pandas-3.0.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:339dda302bd8369dedeae979cb750e484d549b563c3f54f3922cb8ff4978c5eb", size = 10757067, upload-time = "2026-03-31T06:46:14.903Z" }, + { url = "https://files.pythonhosted.org/packages/20/17/ec40d981705654853726e7ac9aea9ddbb4a5d9cf54d8472222f4f3de06c2/pandas-3.0.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:61c2fd96d72b983a9891b2598f286befd4ad262161a609c92dc1652544b46b76", size = 11258787, upload-time = "2026-03-31T06:46:17.683Z" }, + { url = "https://files.pythonhosted.org/packages/90/e3/3f1126d43d3702ca8773871a81c9f15122a1f412342cc56284ffda5b1f70/pandas-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c934008c733b8bbea273ea308b73b3156f0181e5b72960790b09c18a2794fe1e", size = 11771616, upload-time = "2026-03-31T06:46:20.532Z" }, + { url = "https://files.pythonhosted.org/packages/2e/cf/0f4e268e1f5062e44a6bda9f925806721cd4c95c2b808a4c82ebe914f96b/pandas-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:60a80bb4feacbef5e1447a3f82c33209c8b7e07f28d805cfd1fb951e5cb443aa", size = 12337623, upload-time = "2026-03-31T06:46:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/97a6339859d4acb2536efb24feb6708e82f7d33b2ed7e036f2983fcced82/pandas-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:ed72cb3f45190874eb579c64fa92d9df74e98fd63e2be7f62bce5ace0ade61df", size = 9897372, upload-time = "2026-03-31T06:46:26.703Z" }, + { url = "https://files.pythonhosted.org/packages/8f/eb/781516b808a99ddf288143cec46b342b3016c3414d137da1fdc3290d8860/pandas-3.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:f12b1a9e332c01e09510586f8ca9b108fd631fd656af82e452d7315ef6df5f9f", size = 9154922, upload-time = "2026-03-31T06:46:30.284Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b0/c20bd4d6d3f736e6bd6b55794e9cd0a617b858eaad27c8f410ea05d953b7/pandas-3.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:232a70ebb568c0c4d2db4584f338c1577d81e3af63292208d615907b698a0f18", size = 10347921, upload-time = "2026-03-31T06:46:33.36Z" }, + { url = "https://files.pythonhosted.org/packages/35/d0/4831af68ce30cc2d03c697bea8450e3225a835ef497d0d70f31b8cdde965/pandas-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:970762605cff1ca0d3f71ed4f3a769ea8f85fc8e6348f6e110b8fea7e6eb5a14", size = 9888127, upload-time = "2026-03-31T06:46:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/61/a9/16ea9346e1fc4a96e2896242d9bc674764fb9049b0044c0132502f7a771e/pandas-3.0.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aff4e6f4d722e0652707d7bcb190c445fe58428500c6d16005b02401764b1b3d", size = 10399577, upload-time = "2026-03-31T06:46:39.224Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a8/3a61a721472959ab0ce865ef05d10b0d6bfe27ce8801c99f33d4fa996e65/pandas-3.0.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef8b27695c3d3dc78403c9a7d5e59a62d5464a7e1123b4e0042763f7104dc74f", size = 10880030, upload-time = "2026-03-31T06:46:42.412Z" }, + { url = "https://files.pythonhosted.org/packages/da/65/7225c0ea4d6ce9cb2160a7fb7f39804871049f016e74782e5dade4d14109/pandas-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f8d68083e49e16b84734eb1a4dcae4259a75c90fb6e2251ab9a00b61120c06ab", size = 11409468, upload-time = "2026-03-31T06:46:45.2Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/46e7c76032639f2132359b5cf4c785dd8cf9aea5ea64699eac752f02b9db/pandas-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:32cc41f310ebd4a296d93515fcac312216adfedb1894e879303987b8f1e2b97d", size = 11936381, upload-time = "2026-03-31T06:46:48.293Z" }, + { url = "https://files.pythonhosted.org/packages/7b/8b/721a9cff6fa6a91b162eb51019c6243b82b3226c71bb6c8ef4a9bd65cbc6/pandas-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:a4785e1d6547d8427c5208b748ae2efb64659a21bd82bf440d4262d02bfa02a4", size = 9744993, upload-time = "2026-03-31T06:46:51.488Z" }, + { url = "https://files.pythonhosted.org/packages/d5/18/7f0bd34ae27b28159aa80f2a6799f47fda34f7fb938a76e20c7b7fe3b200/pandas-3.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:08504503f7101300107ecdc8df73658e4347586db5cfdadabc1592e9d7e7a0fd", size = 9056118, upload-time = "2026-03-31T06:46:54.548Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ca/3e639a1ea6fcd0617ca4e8ca45f62a74de33a56ae6cd552735470b22c8d3/pandas-3.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5918ba197c951dec132b0c5929a00c0bf05d5942f590d3c10a807f6e15a57d3", size = 10321105, upload-time = "2026-03-31T06:46:57.327Z" }, + { url = "https://files.pythonhosted.org/packages/0b/77/dbc82ff2fb0e63c6564356682bf201edff0ba16c98630d21a1fb312a8182/pandas-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d606a041c89c0a474a4702d532ab7e73a14fe35c8d427b972a625c8e46373668", size = 9864088, upload-time = "2026-03-31T06:46:59.935Z" }, + { url = "https://files.pythonhosted.org/packages/5c/2b/341f1b04bbca2e17e13cd3f08c215b70ef2c60c5356ef1e8c6857449edc7/pandas-3.0.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:710246ba0616e86891b58ab95f2495143bb2bc83ab6b06747c74216f583a6ac9", size = 10369066, upload-time = "2026-03-31T06:47:02.792Z" }, + { url = "https://files.pythonhosted.org/packages/12/c5/cbb1ffefb20a93d3f0e1fdcda699fb84976210d411b008f97f48bf6ce27e/pandas-3.0.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d3cfe227c725b1f3dff4278b43d8c784656a42a9325b63af6b1492a8232209e", size = 10876780, upload-time = "2026-03-31T06:47:06.205Z" }, + { url = "https://files.pythonhosted.org/packages/98/fe/2249ae5e0a69bd0ddf17353d0a5d26611d70970111f5b3600cdc8be883e7/pandas-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c3b723df9087a9a9a840e263ebd9f88b64a12075d1bf2ea401a5a42f254f084d", size = 11375181, upload-time = "2026-03-31T06:47:09.383Z" }, + { url = "https://files.pythonhosted.org/packages/de/64/77a38b09e70b6464883b8d7584ab543e748e42c1b5d337a2ee088e0df741/pandas-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3096110bf9eac0070b7208465f2740e2d8a670d5cb6530b5bb884eca495fd39", size = 11928899, upload-time = "2026-03-31T06:47:12.686Z" }, + { url = "https://files.pythonhosted.org/packages/5e/52/42855bf626868413f761addd574acc6195880ae247a5346477a4361c3acb/pandas-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:07a10f5c36512eead51bc578eb3354ad17578b22c013d89a796ab5eee90cd991", size = 9746574, upload-time = "2026-03-31T06:47:15.64Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/21304ae06a25e8bf9fc820d69b29b2c495b2ae580d1e143146c309941760/pandas-3.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:5fdbfa05931071aba28b408e59226186b01eb5e92bea2ab78b65863ca3228d84", size = 9047156, upload-time = "2026-03-31T06:47:18.595Z" }, + { url = "https://files.pythonhosted.org/packages/72/20/7defa8b27d4f330a903bb68eea33be07d839c5ea6bdda54174efcec0e1d2/pandas-3.0.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:dbc20dea3b9e27d0e66d74c42b2d0c1bed9c2ffe92adea33633e3bedeb5ac235", size = 10756238, upload-time = "2026-03-31T06:47:22.012Z" }, + { url = "https://files.pythonhosted.org/packages/e9/95/49433c14862c636afc0e9b2db83ff16b3ad92959364e52b2955e44c8e94c/pandas-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b75c347eff42497452116ce05ef461822d97ce5b9ff8df6edacb8076092c855d", size = 10408520, upload-time = "2026-03-31T06:47:25.197Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f8/462ad2b5881d6b8ec8e5f7ed2ea1893faa02290d13870a1600fe72ad8efc/pandas-3.0.2-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1478075142e83a5571782ad007fb201ed074bdeac7ebcc8890c71442e96adf7", size = 10324154, upload-time = "2026-03-31T06:47:28.097Z" }, + { url = "https://files.pythonhosted.org/packages/0a/65/d1e69b649cbcddda23ad6e4c40ef935340f6f652a006e5cbc3555ac8adb3/pandas-3.0.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5880314e69e763d4c8b27937090de570f1fb8d027059a7ada3f7f8e98bdcb677", size = 10714449, upload-time = "2026-03-31T06:47:30.85Z" }, + { url = "https://files.pythonhosted.org/packages/47/a4/85b59bc65b8190ea3689882db6cdf32a5003c0ccd5a586c30fdcc3ffc4fc/pandas-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b5329e26898896f06035241a626d7c335daa479b9bbc82be7c2742d048e41172", size = 11338475, upload-time = "2026-03-31T06:47:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c4/bc6966c6e38e5d9478b935272d124d80a589511ed1612a5d21d36f664c68/pandas-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:81526c4afd31971f8b62671442a4b2b51e0aa9acc3819c9f0f12a28b6fcf85f1", size = 11786568, upload-time = "2026-03-31T06:47:36.941Z" }, + { url = "https://files.pythonhosted.org/packages/e8/74/09298ca9740beed1d3504e073d67e128aa07e5ca5ca2824b0c674c0b8676/pandas-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:7cadd7e9a44ec13b621aec60f9150e744cfc7a3dd32924a7e2f45edff31823b0", size = 10488652, upload-time = "2026-03-31T06:47:40.612Z" }, + { url = "https://files.pythonhosted.org/packages/bb/40/c6ea527147c73b24fc15c891c3fcffe9c019793119c5742b8784a062c7db/pandas-3.0.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db0dbfd2a6cdf3770aa60464d50333d8f3d9165b2f2671bcc299b72de5a6677b", size = 10326084, upload-time = "2026-03-31T06:47:43.834Z" }, + { url = "https://files.pythonhosted.org/packages/95/25/bdb9326c3b5455f8d4d3549fce7abcf967259de146fe2cf7a82368141948/pandas-3.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0555c5882688a39317179ab4a0ed41d3ebc8812ab14c69364bbee8fb7a3f6288", size = 9914146, upload-time = "2026-03-31T06:47:46.67Z" }, + { url = "https://files.pythonhosted.org/packages/8d/77/3a227ff3337aa376c60d288e1d61c5d097131d0ac71f954d90a8f369e422/pandas-3.0.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:01f31a546acd5574ef77fe199bc90b55527c225c20ccda6601cf6b0fd5ed597c", size = 10444081, upload-time = "2026-03-31T06:47:49.681Z" }, + { url = "https://files.pythonhosted.org/packages/15/88/3cdd54fa279341afa10acf8d2b503556b1375245dccc9315659f795dd2e9/pandas-3.0.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deeca1b5a931fdf0c2212c8a659ade6d3b1edc21f0914ce71ef24456ca7a6535", size = 10897535, upload-time = "2026-03-31T06:47:53.033Z" }, + { url = "https://files.pythonhosted.org/packages/06/9d/98cc7a7624f7932e40f434299260e2917b090a579d75937cb8a57b9d2de3/pandas-3.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f48afd9bb13300ffb5a3316973324c787054ba6665cda0da3fbd67f451995db", size = 11446992, upload-time = "2026-03-31T06:47:56.193Z" }, + { url = "https://files.pythonhosted.org/packages/9a/cd/19ff605cc3760e80602e6826ddef2824d8e7050ed80f2e11c4b079741dc3/pandas-3.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6c4d8458b97a35717b62469a4ea0e85abd5ed8687277f5ccfc67f8a5126f8c53", size = 11968257, upload-time = "2026-03-31T06:47:59.137Z" }, + { url = "https://files.pythonhosted.org/packages/db/60/aba6a38de456e7341285102bede27514795c1eaa353bc0e7638b6b785356/pandas-3.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:b35d14bb5d8285d9494fe93815a9e9307c0876e10f1e8e89ac5b88f728ec8dcf", size = 9865893, upload-time = "2026-03-31T06:48:02.038Z" }, + { url = "https://files.pythonhosted.org/packages/08/71/e5ec979dd2e8a093dacb8864598c0ff59a0cee0bbcdc0bfec16a51684d4f/pandas-3.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:63d141b56ef686f7f0d714cfb8de4e320475b86bf4b620aa0b7da89af8cbdbbb", size = 9188644, upload-time = "2026-03-31T06:48:05.045Z" }, + { url = "https://files.pythonhosted.org/packages/f1/6c/7b45d85db19cae1eb524f2418ceaa9d85965dcf7b764ed151386b7c540f0/pandas-3.0.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:140f0cffb1fa2524e874dde5b477d9defe10780d8e9e220d259b2c0874c89d9d", size = 10776246, upload-time = "2026-03-31T06:48:07.789Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3e/7b00648b086c106e81766f25322b48aa8dfa95b55e621dbdf2fdd413a117/pandas-3.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae37e833ff4fed0ba352f6bdd8b73ba3ab3256a85e54edfd1ab51ae40cca0af8", size = 10424801, upload-time = "2026-03-31T06:48:10.897Z" }, + { url = "https://files.pythonhosted.org/packages/da/6e/558dd09a71b53b4008e7fc8a98ec6d447e9bfb63cdaeea10e5eb9b2dabe8/pandas-3.0.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d888a5c678a419a5bb41a2a93818e8ed9fd3172246555c0b37b7cc27027effd", size = 10345643, upload-time = "2026-03-31T06:48:13.7Z" }, + { url = "https://files.pythonhosted.org/packages/be/e3/921c93b4d9a280409451dc8d07b062b503bbec0531d2627e73a756e99a82/pandas-3.0.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b444dc64c079e84df91baa8bf613d58405645461cabca929d9178f2cd392398d", size = 10743641, upload-time = "2026-03-31T06:48:16.659Z" }, + { url = "https://files.pythonhosted.org/packages/56/ca/fd17286f24fa3b4d067965d8d5d7e14fe557dd4f979a0b068ac0deaf8228/pandas-3.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4544c7a54920de8eeacaa1466a6b7268ecfbc9bc64ab4dbb89c6bbe94d5e0660", size = 11361993, upload-time = "2026-03-31T06:48:19.475Z" }, + { url = "https://files.pythonhosted.org/packages/e4/a5/2f6ed612056819de445a433ca1f2821ac3dab7f150d569a59e9cc105de1d/pandas-3.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:734be7551687c00fbd760dc0522ed974f82ad230d4a10f54bf51b80d44a08702", size = 11815274, upload-time = "2026-03-31T06:48:22.695Z" }, + { url = "https://files.pythonhosted.org/packages/00/2f/b622683e99ec3ce00b0854bac9e80868592c5b051733f2cf3a868e5fea26/pandas-3.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:57a07209bebcbcf768d2d13c9b78b852f9a15978dac41b9e6421a81ad4cdd276", size = 10888530, upload-time = "2026-03-31T06:48:25.806Z" }, + { url = "https://files.pythonhosted.org/packages/cb/2b/f8434233fab2bd66a02ec014febe4e5adced20e2693e0e90a07d118ed30e/pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482", size = 9455341, upload-time = "2026-03-31T06:48:28.418Z" }, +] + +[[package]] +name = "peft" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accelerate" }, + { name = "huggingface-hub" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, + { name = "tqdm" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/86/cf/037f1e3d5186496c05513a6754639e2dab3038a05f384284d49a9bd06a2d/peft-0.19.1.tar.gz", hash = "sha256:0d97542fe96dcdaa20d3b81c06f26f988618f416a73544ab23c3618ccb674a40", size = 763738, upload-time = "2026-04-16T15:46:45.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/b6/f54d676ed93cc2dd2234c3b172ea9c8c3d7d29361e66b1b23dec57a67465/peft-0.19.1-py3-none-any.whl", hash = "sha256:2113f72a81621b5913ef28f9022204c742df111890c5f49d812716a4a301e356", size = 680692, upload-time = "2026-04-16T15:46:42.886Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", size = 5354355, upload-time = "2026-04-01T14:42:15.402Z" }, + { url = "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", size = 4695871, upload-time = "2026-04-01T14:42:18.234Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", size = 6269734, upload-time = "2026-04-01T14:42:20.608Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", size = 8076080, upload-time = "2026-04-01T14:42:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", size = 6382236, upload-time = "2026-04-01T14:42:25.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", size = 7070220, upload-time = "2026-04-01T14:42:28.68Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", size = 6493124, upload-time = "2026-04-01T14:42:31.579Z" }, + { url = "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", size = 7194324, upload-time = "2026-04-01T14:42:34.615Z" }, + { url = "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", size = 6376363, upload-time = "2026-04-01T14:42:37.19Z" }, + { url = "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", size = 7083523, upload-time = "2026-04-01T14:42:39.62Z" }, + { url = "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", size = 2463318, upload-time = "2026-04-01T14:42:42.063Z" }, + { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" }, + { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" }, + { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" }, + { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" }, + { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" }, + { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" }, + { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" }, + { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" }, + { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" }, + { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, + { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, + { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, + { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, + { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, + { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, + { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, + { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, + { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, + { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, + { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, + { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, + { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, + { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, + { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, + { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, + { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, + { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, + { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, + { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, + { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, + { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, + { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, + { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, + { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, + { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, + { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, + { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, + { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, + { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, + { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, + { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, + { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, + { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, + { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, + { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, + { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, + { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, + { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" }, + { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" }, + { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" }, + { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" }, + { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" }, + { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + +[[package]] +name = "protobuf" +version = "7.34.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/6b/a0e95cad1ad7cc3f2c6821fcab91671bd5b78bd42afb357bb4765f29bc41/protobuf-7.34.1.tar.gz", hash = "sha256:9ce42245e704cc5027be797c1db1eb93184d44d1cdd71811fb2d9b25ad541280", size = 454708, upload-time = "2026-03-20T17:34:47.036Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/11/3325d41e6ee15bf1125654301211247b042563bcc898784351252549a8ad/protobuf-7.34.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8b2cc79c4d8f62b293ad9b11ec3aebce9af481fa73e64556969f7345ebf9fc7", size = 429247, upload-time = "2026-03-20T17:34:37.024Z" }, + { url = "https://files.pythonhosted.org/packages/eb/9d/aa69df2724ff63efa6f72307b483ce0827f4347cc6d6df24b59e26659fef/protobuf-7.34.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:5185e0e948d07abe94bb76ec9b8416b604cfe5da6f871d67aad30cbf24c3110b", size = 325753, upload-time = "2026-03-20T17:34:38.751Z" }, + { url = "https://files.pythonhosted.org/packages/92/e8/d174c91fd48e50101943f042b09af9029064810b734e4160bbe282fa1caa/protobuf-7.34.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:403b093a6e28a960372b44e5eb081775c9b056e816a8029c61231743d63f881a", size = 340198, upload-time = "2026-03-20T17:34:39.871Z" }, + { url = "https://files.pythonhosted.org/packages/53/1b/3b431694a4dc6d37b9f653f0c64b0a0d9ec074ee810710c0c3da21d67ba7/protobuf-7.34.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:8ff40ce8cd688f7265326b38d5a1bed9bfdf5e6723d49961432f83e21d5713e4", size = 324267, upload-time = "2026-03-20T17:34:41.1Z" }, + { url = "https://files.pythonhosted.org/packages/85/29/64de04a0ac142fb685fd09999bc3d337943fb386f3a0ec57f92fd8203f97/protobuf-7.34.1-cp310-abi3-win32.whl", hash = "sha256:34b84ce27680df7cca9f231043ada0daa55d0c44a2ddfaa58ec1d0d89d8bf60a", size = 426628, upload-time = "2026-03-20T17:34:42.536Z" }, + { url = "https://files.pythonhosted.org/packages/4d/87/cb5e585192a22b8bd457df5a2c16a75ea0db9674c3a0a39fc9347d84e075/protobuf-7.34.1-cp310-abi3-win_amd64.whl", hash = "sha256:e97b55646e6ce5cbb0954a8c28cd39a5869b59090dfaa7df4598a7fba869468c", size = 437901, upload-time = "2026-03-20T17:34:44.112Z" }, + { url = "https://files.pythonhosted.org/packages/88/95/608f665226bca68b736b79e457fded9a2a38c4f4379a4a7614303d9db3bc/protobuf-7.34.1-py3-none-any.whl", hash = "sha256:bb3812cd53aefea2b028ef42bd780f5b96407247f20c6ef7c679807e9d188f11", size = 170715, upload-time = "2026-03-20T17:34:45.384Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "pyarrow" +version = "24.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/91/13/13e1069b351bdc3881266e11147ffccf687505dbb0ea74036237f5d454a5/pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83", size = 1180261, upload-time = "2026-04-21T10:51:25.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/bf/a34fee1d624152124fa8355c42f34195ad5fe5233ce5bb87946432047d52/pyarrow-24.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb", size = 35076681, upload-time = "2026-04-21T08:51:46.845Z" }, + { url = "https://files.pythonhosted.org/packages/1d/41/64180033d7027afce12dc96d0fe1f504c6fa112190582b458acea2399530/pyarrow-24.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147", size = 36684260, upload-time = "2026-04-21T08:51:53.642Z" }, + { url = "https://files.pythonhosted.org/packages/57/02/9b9320e673dd8a99411fac78690f3df92f6dd6f59754c750110bca66d64e/pyarrow-24.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c", size = 45698566, upload-time = "2026-04-21T10:46:02.133Z" }, + { url = "https://files.pythonhosted.org/packages/67/33/f75e91b9a64c3f33c787e263c93b871ad91b8a4a68c1d5cebddd9840e835/pyarrow-24.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041", size = 48835562, upload-time = "2026-04-21T10:46:10.278Z" }, + { url = "https://files.pythonhosted.org/packages/a5/63/097510448e47e4091faa41c43ba92f97cecaab8f4535b56a3d149578f634/pyarrow-24.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491", size = 49394997, upload-time = "2026-04-21T10:46:18.08Z" }, + { url = "https://files.pythonhosted.org/packages/60/6b/c047d6222ab279024a062742d1807e2fbaf27bba88a98637299ff47b9236/pyarrow-24.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1", size = 51911424, upload-time = "2026-04-21T10:46:25.347Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ba/464cc70761c2a525d97ebd84e21c31ebd47f3ef4bdcee117009f51c46f24/pyarrow-24.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591", size = 27251730, upload-time = "2026-04-21T10:46:30.913Z" }, + { url = "https://files.pythonhosted.org/packages/62/c9/a47ab7ece0d86cbe6678418a0fbd1ac4bb493b9184a3891dfa0e7f287ae0/pyarrow-24.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74", size = 35068898, upload-time = "2026-04-21T10:46:36.599Z" }, + { url = "https://files.pythonhosted.org/packages/d1/bc/8db86617a9a58008acf8913d6fed68ea2a46acb6de928db28d724c891a68/pyarrow-24.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3", size = 36679915, upload-time = "2026-04-21T10:46:42.602Z" }, + { url = "https://files.pythonhosted.org/packages/eb/8e/fb178720400ef69db251eb4a9c3ccf4af269bc1feb5055529b8fc87170d1/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868", size = 45697931, upload-time = "2026-04-21T10:46:48.403Z" }, + { url = "https://files.pythonhosted.org/packages/f3/27/99c42abe8e21b44f4917f62631f3aa31404882a2c41d8a4cd5c110e13d52/pyarrow-24.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e", size = 48837449, upload-time = "2026-04-21T10:46:55.329Z" }, + { url = "https://files.pythonhosted.org/packages/36/b6/333749e2666e9032891125bf9c691146e92901bece62030ac1430e2e7c88/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57", size = 49395949, upload-time = "2026-04-21T10:47:01.869Z" }, + { url = "https://files.pythonhosted.org/packages/17/25/c5201706a2dd374e8ba6ee3fd7a8c89fb7ffc16eed5217a91fd2bd7f7626/pyarrow-24.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c", size = 51912986, upload-time = "2026-04-21T10:47:09.872Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d2/4d1bbba65320b21a49678d6fbdc6ff7c649251359fdcfc03568c4136231d/pyarrow-24.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981", size = 27255371, upload-time = "2026-04-21T10:47:15.943Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a9/9686d9f07837f91f775e8932659192e02c74f9d8920524b480b85212cc68/pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810", size = 34981559, upload-time = "2026-04-21T10:47:22.17Z" }, + { url = "https://files.pythonhosted.org/packages/80/b6/0ddf0e9b6ead3474ab087ae598c76b031fc45532bf6a63f3a553440fb258/pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a", size = 36663654, upload-time = "2026-04-21T10:47:28.315Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3b/926382efe8ce27ba729071d3566ade6dfb86bdf112f366000196b2f5780a/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66", size = 45679394, upload-time = "2026-04-21T10:47:34.821Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/829f7d9dfd37c207206081d6dad474d81dde29952401f07f2ba507814818/pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb", size = 48863122, upload-time = "2026-04-21T10:47:42.056Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e8/f88ce625fe8babaae64e8db2d417c7653adb3019b08aae85c5ed787dc816/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e", size = 49376032, upload-time = "2026-04-21T10:47:48.967Z" }, + { url = "https://files.pythonhosted.org/packages/36/7a/82c363caa145fff88fb475da50d3bf52bb024f61917be5424c3392eaf878/pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6", size = 51929490, upload-time = "2026-04-21T10:47:55.981Z" }, + { url = "https://files.pythonhosted.org/packages/66/1c/e3e72c8014ad2743ca64a701652c733cc5cbcee15c0463a32a8c55518d9e/pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826", size = 27355660, upload-time = "2026-04-21T10:48:01.718Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d3/a1abf004482026ddc17f4503db227787fa3cfe41ec5091ff20e4fea55e57/pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba", size = 34976759, upload-time = "2026-04-21T10:48:07.258Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4a/34f0a36d28a2dd32225301b79daad44e243dc1a2bb77d43b60749be255c4/pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68", size = 36658471, upload-time = "2026-04-21T10:48:13.347Z" }, + { url = "https://files.pythonhosted.org/packages/1f/78/543b94712ae8bb1a6023bcc1acf1a740fbff8286747c289cd9468fced2a5/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2", size = 45675981, upload-time = "2026-04-21T10:48:20.201Z" }, + { url = "https://files.pythonhosted.org/packages/84/9f/8fb7c222b100d314137fa40ec050de56cd8c6d957d1cfff685ce72f15b17/pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0", size = 48859172, upload-time = "2026-04-21T10:48:27.541Z" }, + { url = "https://files.pythonhosted.org/packages/a7/d3/1ea72538e6c8b3b475ed78d1049a2c518e655761ea50fe1171fc855fcab7/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495", size = 49385733, upload-time = "2026-04-21T10:48:34.7Z" }, + { url = "https://files.pythonhosted.org/packages/c3/be/c3d8b06a1ba35f2260f8e1f771abbee7d5e345c0937aab90675706b1690a/pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f", size = 51934335, upload-time = "2026-04-21T10:48:42.099Z" }, + { url = "https://files.pythonhosted.org/packages/9c/62/89e07a1e7329d2cde3e3c6994ba0839a24977a2beda8be6005ea3d860b99/pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91", size = 27271748, upload-time = "2026-04-21T10:49:42.532Z" }, + { url = "https://files.pythonhosted.org/packages/17/1a/cff3a59f80b5b1658549d46611b67163f65e0664431c076ad728bf9d5af4/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275", size = 35238554, upload-time = "2026-04-21T10:48:48.526Z" }, + { url = "https://files.pythonhosted.org/packages/a8/99/cce0f42a327bfef2c420fb6078a3eb834826e5d6697bf3009fe11d2ad051/pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b", size = 36782301, upload-time = "2026-04-21T10:48:55.181Z" }, + { url = "https://files.pythonhosted.org/packages/2a/66/8e560d5ff6793ca29aca213c53eec0dd482dd46cb93b2819e5aab52e4252/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42", size = 45721929, upload-time = "2026-04-21T10:49:03.676Z" }, + { url = "https://files.pythonhosted.org/packages/27/0c/a26e25505d030716e078d9f16eb74973cbf0b33b672884e9f9da1c83b871/pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b", size = 48825365, upload-time = "2026-04-21T10:49:11.714Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/771f9ecb0c65e73fe9dccdd1717901b9594f08c4515d000c7c62df573811/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37", size = 49451819, upload-time = "2026-04-21T10:49:21.474Z" }, + { url = "https://files.pythonhosted.org/packages/48/da/61ae89a88732f5a785646f3ec6125dbb640fa98a540eb2b9889caa561403/pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca", size = 51909252, upload-time = "2026-04-21T10:49:31.164Z" }, + { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" }, + { url = "https://files.pythonhosted.org/packages/ad/80/d022a34ff05d2cbedd8ccf841fc1f532ecfa9eb5ed1711b56d0e0ea71fc9/pyarrow-24.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838", size = 35007997, upload-time = "2026-04-21T10:49:48.796Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ff/f01485fda6f4e5d441afb8dd5e7681e4db18826c1e271852f5d3957d6a80/pyarrow-24.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b", size = 36678720, upload-time = "2026-04-21T10:49:55.858Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c2/2d2d5fea814237923f71b36495211f20b43a1576f9a4d6da7e751a64ec6f/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795", size = 45741852, upload-time = "2026-04-21T10:50:04.624Z" }, + { url = "https://files.pythonhosted.org/packages/8e/3a/28ba9c1c1ebdbb5f1b94dfebb46f207e52e6a554b7fe4132540fde29a3a0/pyarrow-24.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26", size = 48889852, upload-time = "2026-04-21T10:50:12.293Z" }, + { url = "https://files.pythonhosted.org/packages/df/51/4a389acfd31dca009f8fb82d7f510bb4130f2b3a8e18cf00194d0687d8ac/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde", size = 49445207, upload-time = "2026-04-21T10:50:20.677Z" }, + { url = "https://files.pythonhosted.org/packages/19/4b/0bab2b23d2ae901b1b9a03c0efd4b2d070256f8ce3fc43f6e58c167b2081/pyarrow-24.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76", size = 51954117, upload-time = "2026-04-21T10:50:29.14Z" }, + { url = "https://files.pythonhosted.org/packages/29/88/f4e9145da0417b3d2c12035a8492b35ff4a3dbc653e614fcfb51d9dedb38/pyarrow-24.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e", size = 28001155, upload-time = "2026-04-21T10:51:22.337Z" }, + { url = "https://files.pythonhosted.org/packages/79/4f/46a49a63f43526da895b1a45bbb51d5baf8e4d77159f8528fc3e5490007f/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05", size = 35250387, upload-time = "2026-04-21T10:50:35.552Z" }, + { url = "https://files.pythonhosted.org/packages/a0/da/d5e0cd5ef00796922404806d5f00325cdadc3441ce2c13fe7115f2df9a64/pyarrow-24.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a", size = 36797102, upload-time = "2026-04-21T10:50:42.417Z" }, + { url = "https://files.pythonhosted.org/packages/34/c7/5904145b0a593a05236c882933d439b5720f0a145381179063722fbfc123/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072", size = 45745118, upload-time = "2026-04-21T10:50:49.324Z" }, + { url = "https://files.pythonhosted.org/packages/13/d3/cca42fe166d1c6e4d5b80e530b7949104d10e17508a90ae202dac205ce2a/pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931", size = 48844765, upload-time = "2026-04-21T10:50:55.579Z" }, + { url = "https://files.pythonhosted.org/packages/b0/49/942c3b79878ba928324d1e17c274ed84581db8c0a749b24bcf4cbdf15bd3/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699", size = 49471890, upload-time = "2026-04-21T10:51:02.439Z" }, + { url = "https://files.pythonhosted.org/packages/76/97/ff71431000a75d84135a1ace5ca4ba11726a231a8007bbb320a4c54075d5/pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136", size = 51932250, upload-time = "2026-04-21T10:51:10.576Z" }, + { url = "https://files.pythonhosted.org/packages/51/be/6f79d55816d5c22557cf27533543d5d70dfe692adfbee4b99f2760674f38/pyarrow-24.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19", size = 28131282, upload-time = "2026-04-21T10:51:16.815Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.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/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, + { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, + { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, + { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, + { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, + { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, + { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, + { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, + { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, + { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, + { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, + { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, + { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, + { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, + { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, + { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, + { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, + { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, +] + +[[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 = "pytz" +version = "2026.1.post1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/db/b8721d71d945e6a8ac63c0fc900b2067181dbb50805958d4d4661cf7d277/pytz-2026.1.post1.tar.gz", hash = "sha256:3378dde6a0c3d26719182142c56e60c7f9af7e968076f31aae569d72a0358ee1", size = 321088, upload-time = "2026-03-03T07:47:50.683Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/99/781fe0c827be2742bcc775efefccb3b048a3a9c6ce9aec0cbf4a101677e5/pytz-2026.1.post1-py2.py3-none-any.whl", hash = "sha256:f2fd16142fda348286a75e1a524be810bb05d444e5a081f37f7affc635035f7a", size = 510489, upload-time = "2026-03-03T07:47:49.167Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" }, + { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" }, + { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" }, + { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" }, + { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" }, + { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" }, + { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" }, + { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" }, + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" }, + { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" }, + { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "regex" +version = "2026.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/3a246dbf05666918bd3664d9d787f84a9108f6f43cc953a077e4a7dfdb7e/regex-2026.4.4.tar.gz", hash = "sha256:e08270659717f6973523ce3afbafa53515c4dc5dcad637dc215b6fd50f689423", size = 416000, upload-time = "2026-04-03T20:56:28.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/59/fd98f8fd54b3feaa76a855324c676c17668c5a1121ec91b7ec96b01bf865/regex-2026.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:74fa82dcc8143386c7c0392e18032009d1db715c25f4ba22d23dc2e04d02a20f", size = 489403, upload-time = "2026-04-03T20:52:39.742Z" }, + { url = "https://files.pythonhosted.org/packages/6c/64/d0f222f68e3579d50babf0e4fcc9c9639ef0587fecc00b15e1e46bfc32fa/regex-2026.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a85b620a388d6c9caa12189233109e236b3da3deffe4ff11b84ae84e218a274f", size = 291208, upload-time = "2026-04-03T20:52:42.943Z" }, + { url = "https://files.pythonhosted.org/packages/16/7f/3fab9709b0b0060ba81a04b8a107b34147cd14b9c5551b772154d6505504/regex-2026.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2895506ebe32cc63eeed8f80e6eae453171cfccccab35b70dc3129abec35a5b8", size = 289214, upload-time = "2026-04-03T20:52:44.648Z" }, + { url = "https://files.pythonhosted.org/packages/14/bc/f5dcf04fd462139dcd75495c02eee22032ef741cfa151386a39c3f5fc9b5/regex-2026.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6780f008ee81381c737634e75c24e5a6569cc883c4f8e37a37917ee79efcafd9", size = 785505, upload-time = "2026-04-03T20:52:46.35Z" }, + { url = "https://files.pythonhosted.org/packages/37/36/8a906e216d5b4de7ec3788c1d589b45db40c1c9580cd7b326835cfc976d4/regex-2026.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:88e9b048345c613f253bea4645b2fe7e579782b82cac99b1daad81e29cc2ed8e", size = 852129, upload-time = "2026-04-03T20:52:48.661Z" }, + { url = "https://files.pythonhosted.org/packages/a5/bb/bad2d79be0917a6ef31f5e0f161d9265cb56fd90a3ae1d2e8d991882a48b/regex-2026.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:be061028481186ba62a0f4c5f1cc1e3d5ab8bce70c89236ebe01023883bc903b", size = 899578, upload-time = "2026-04-03T20:52:50.61Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b9/7cd0ceb58cd99c70806241636640ae15b4a3fe62e22e9b99afa67a0d7965/regex-2026.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d2228c02b368d69b724c36e96d3d1da721561fb9cc7faa373d7bf65e07d75cb5", size = 793634, upload-time = "2026-04-03T20:52:53Z" }, + { url = "https://files.pythonhosted.org/packages/2c/fb/c58e3ea40ed183806ccbac05c29a3e8c2f88c1d3a66ed27860d5cad7c62d/regex-2026.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0540e5b733618a2f84e9cb3e812c8afa82e151ca8e19cf6c4e95c5a65198236f", size = 786210, upload-time = "2026-04-03T20:52:54.713Z" }, + { url = "https://files.pythonhosted.org/packages/54/a9/53790fc7a6c948a7be2bc7214fd9cabdd0d1ba561b0f401c91f4ff0357f0/regex-2026.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cf9b1b2e692d4877880388934ac746c99552ce6bf40792a767fd42c8c99f136d", size = 769930, upload-time = "2026-04-03T20:52:56.825Z" }, + { url = "https://files.pythonhosted.org/packages/e3/3c/29ca44729191c79f5476538cd0fa04fa2553b3c45508519ecea4c7afa8f6/regex-2026.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:011bb48bffc1b46553ac704c975b3348717f4e4aa7a67522b51906f99da1820c", size = 774892, upload-time = "2026-04-03T20:52:58.934Z" }, + { url = "https://files.pythonhosted.org/packages/3e/db/6ae74ef8a4cfead341c367e4eed45f71fb1aaba35827a775eed4f1ba4f74/regex-2026.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8512fcdb43f1bf18582698a478b5ab73f9c1667a5b7548761329ef410cd0a760", size = 848816, upload-time = "2026-04-03T20:53:00.684Z" }, + { url = "https://files.pythonhosted.org/packages/53/9a/f7f2c1c6b610d7c6de1c3dc5951effd92c324b1fde761af2044b4721020f/regex-2026.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:867bddc63109a0276f5a31999e4c8e0eb7bbbad7d6166e28d969a2c1afeb97f9", size = 758363, upload-time = "2026-04-03T20:53:02.155Z" }, + { url = "https://files.pythonhosted.org/packages/dd/55/e5386d393bbf8b43c8b084703a46d635e7b2bdc6e0f5909a2619ea1125f1/regex-2026.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1b9a00b83f3a40e09859c78920571dcb83293c8004079653dd22ec14bbfa98c7", size = 837122, upload-time = "2026-04-03T20:53:03.727Z" }, + { url = "https://files.pythonhosted.org/packages/01/da/cc78710ea2e60b10bacfcc9beb18c67514200ab03597b3b2b319995785c2/regex-2026.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e355be718caf838aa089870259cf1776dc2a4aa980514af9d02c59544d9a8b22", size = 782140, upload-time = "2026-04-03T20:53:05.608Z" }, + { url = "https://files.pythonhosted.org/packages/a2/5f/c7bcba41529105d6c2ca7080ecab7184cd00bee2e1ad1fdea80e618704ea/regex-2026.4.4-cp310-cp310-win32.whl", hash = "sha256:33bfda9684646d323414df7abe5692c61d297dbb0530b28ec66442e768813c59", size = 266225, upload-time = "2026-04-03T20:53:07.342Z" }, + { url = "https://files.pythonhosted.org/packages/eb/26/a745729c2c49354ec4f4bce168f29da932ca01b4758227686cc16c7dde1b/regex-2026.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:0709f22a56798457ae317bcce42aacee33c680068a8f14097430d9f9ba364bee", size = 278393, upload-time = "2026-04-03T20:53:08.65Z" }, + { url = "https://files.pythonhosted.org/packages/87/8b/4327eeb9dbb4b098ebecaf02e9f82b79b6077beeb54c43d9a0660cf7c44c/regex-2026.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:ee9627de8587c1a22201cb16d0296ab92b4df5cdcb5349f4e9744d61db7c7c98", size = 270470, upload-time = "2026-04-03T20:53:10.018Z" }, + { url = "https://files.pythonhosted.org/packages/e0/7a/617356cbecdb452812a5d42f720d6d5096b360d4a4c1073af700ea140ad2/regex-2026.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4c36a85b00fadb85db9d9e90144af0a980e1a3d2ef9cd0f8a5bef88054657c6", size = 489415, upload-time = "2026-04-03T20:53:11.645Z" }, + { url = "https://files.pythonhosted.org/packages/20/e6/bf057227144d02e3ba758b66649e87531d744dda5f3254f48660f18ae9d8/regex-2026.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dcb5453ecf9cd58b562967badd1edbf092b0588a3af9e32ee3d05c985077ce87", size = 291205, upload-time = "2026-04-03T20:53:13.289Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3b/637181b787dd1a820ba1c712cee2b4144cd84a32dc776ca067b12b2d70c8/regex-2026.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6aa809ed4dc3706cc38594d67e641601bd2f36d5555b2780ff074edfcb136cf8", size = 289225, upload-time = "2026-04-03T20:53:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/05/21/bac05d806ed02cd4b39d9c8e5b5f9a2998c94c3a351b7792e80671fa5315/regex-2026.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:33424f5188a7db12958246a54f59a435b6cb62c5cf9c8d71f7cc49475a5fdada", size = 792434, upload-time = "2026-04-03T20:53:17.414Z" }, + { url = "https://files.pythonhosted.org/packages/d9/17/c65d1d8ae90b772d5758eb4014e1e011bb2db353fc4455432e6cc9100df7/regex-2026.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d346fccdde28abba117cc9edc696b9518c3307fbfcb689e549d9b5979018c6d", size = 861730, upload-time = "2026-04-03T20:53:18.903Z" }, + { url = "https://files.pythonhosted.org/packages/ad/64/933321aa082a2c6ee2785f22776143ba89840189c20d3b6b1d12b6aae16b/regex-2026.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:415a994b536440f5011aa77e50a4274d15da3245e876e5c7f19da349caaedd87", size = 906495, upload-time = "2026-04-03T20:53:20.561Z" }, + { url = "https://files.pythonhosted.org/packages/01/ea/4c8d306e9c36ac22417336b1e02e7b358152c34dc379673f2d331143725f/regex-2026.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21e5eb86179b4c67b5759d452ea7c48eb135cd93308e7a260aa489ed2eb423a4", size = 799810, upload-time = "2026-04-03T20:53:22.961Z" }, + { url = "https://files.pythonhosted.org/packages/29/ce/7605048f00e1379eba89d610c7d644d8f695dc9b26d3b6ecfa3132b872ff/regex-2026.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:312ec9dd1ae7d96abd8c5a36a552b2139931914407d26fba723f9e53c8186f86", size = 774242, upload-time = "2026-04-03T20:53:25.015Z" }, + { url = "https://files.pythonhosted.org/packages/e9/77/283e0d5023fde22cd9e86190d6d9beb21590a452b195ffe00274de470691/regex-2026.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0d2b28aa1354c7cd7f71b7658c4326f7facac106edd7f40eda984424229fd59", size = 781257, upload-time = "2026-04-03T20:53:26.918Z" }, + { url = "https://files.pythonhosted.org/packages/8b/fb/7f3b772be101373c8626ed34c5d727dcbb8abd42a7b1219bc25fd9a3cc04/regex-2026.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:349d7310eddff40429a099c08d995c6d4a4bfaf3ff40bd3b5e5cb5a5a3c7d453", size = 854490, upload-time = "2026-04-03T20:53:29.065Z" }, + { url = "https://files.pythonhosted.org/packages/85/30/56547b80f34f4dd2986e1cdd63b1712932f63b6c4ce2f79c50a6cd79d1c2/regex-2026.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:e7ab63e9fe45a9ec3417509e18116b367e89c9ceb6219222a3396fa30b147f80", size = 763544, upload-time = "2026-04-03T20:53:30.917Z" }, + { url = "https://files.pythonhosted.org/packages/ac/2f/ce060fdfea8eff34a8997603532e44cdb7d1f35e3bc253612a8707a90538/regex-2026.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fe896e07a5a2462308297e515c0054e9ec2dd18dfdc9427b19900b37dfe6f40b", size = 844442, upload-time = "2026-04-03T20:53:32.463Z" }, + { url = "https://files.pythonhosted.org/packages/e5/44/810cb113096a1dacbe82789fbfab2823f79d19b7f1271acecb7009ba9b88/regex-2026.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eb59c65069498dbae3c0ef07bbe224e1eaa079825a437fb47a479f0af11f774f", size = 789162, upload-time = "2026-04-03T20:53:34.039Z" }, + { url = "https://files.pythonhosted.org/packages/20/96/9647dd7f2ecf6d9ce1fb04dfdb66910d094e10d8fe53e9c15096d8aa0bd2/regex-2026.4.4-cp311-cp311-win32.whl", hash = "sha256:2a5d273181b560ef8397c8825f2b9d57013de744da9e8257b8467e5da8599351", size = 266227, upload-time = "2026-04-03T20:53:35.601Z" }, + { url = "https://files.pythonhosted.org/packages/33/80/74e13262460530c3097ff343a17de9a34d040a5dc4de9cf3a8241faab51c/regex-2026.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:9542ccc1e689e752594309444081582f7be2fdb2df75acafea8a075108566735", size = 278399, upload-time = "2026-04-03T20:53:37.021Z" }, + { url = "https://files.pythonhosted.org/packages/1c/3c/39f19f47f19dcefa3403f09d13562ca1c0fd07ab54db2bc03148f3f6b46a/regex-2026.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:b5f9fb784824a042be3455b53d0b112655686fdb7a91f88f095f3fee1e2a2a54", size = 270473, upload-time = "2026-04-03T20:53:38.633Z" }, + { url = "https://files.pythonhosted.org/packages/e5/28/b972a4d3df61e1d7bcf1b59fdb3cddef22f88b6be43f161bb41ebc0e4081/regex-2026.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c07ab8794fa929e58d97a0e1796b8b76f70943fa39df225ac9964615cf1f9d52", size = 490434, upload-time = "2026-04-03T20:53:40.219Z" }, + { url = "https://files.pythonhosted.org/packages/84/20/30041446cf6dc3e0eab344fc62770e84c23b6b68a3b657821f9f80cb69b4/regex-2026.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c785939dc023a1ce4ec09599c032cc9933d258a998d16ca6f2b596c010940eb", size = 292061, upload-time = "2026-04-03T20:53:41.862Z" }, + { url = "https://files.pythonhosted.org/packages/62/c8/3baa06d75c98c46d4cc4262b71fd2edb9062b5665e868bca57859dadf93a/regex-2026.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b1ce5c81c9114f1ce2f9288a51a8fd3aeea33a0cc440c415bf02da323aa0a76", size = 289628, upload-time = "2026-04-03T20:53:43.701Z" }, + { url = "https://files.pythonhosted.org/packages/31/87/3accf55634caad8c0acab23f5135ef7d4a21c39f28c55c816ae012931408/regex-2026.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:760ef21c17d8e6a4fe8cf406a97cf2806a4df93416ccc82fc98d25b1c20425be", size = 796651, upload-time = "2026-04-03T20:53:45.379Z" }, + { url = "https://files.pythonhosted.org/packages/f6/0c/aaa2c83f34efedbf06f61cb1942c25f6cf1ee3b200f832c4d05f28306c2e/regex-2026.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7088fcdcb604a4417c208e2169715800d28838fefd7455fbe40416231d1d47c1", size = 865916, upload-time = "2026-04-03T20:53:47.064Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f6/8c6924c865124643e8f37823eca845dc27ac509b2ee58123685e71cd0279/regex-2026.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:07edca1ba687998968f7db5bc355288d0c6505caa7374f013d27356d93976d13", size = 912287, upload-time = "2026-04-03T20:53:49.422Z" }, + { url = "https://files.pythonhosted.org/packages/11/0e/a9f6f81013e0deaf559b25711623864970fe6a098314e374ccb1540a4152/regex-2026.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f657a7c1c6ec51b5e0ba97c9817d06b84ea5fa8d82e43b9405de0defdc2b9", size = 801126, upload-time = "2026-04-03T20:53:51.096Z" }, + { url = "https://files.pythonhosted.org/packages/71/61/3a0cc8af2dc0c8deb48e644dd2521f173f7e6513c6e195aad9aa8dd77ac5/regex-2026.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2b69102a743e7569ebee67e634a69c4cb7e59d6fa2e1aa7d3bdbf3f61435f62d", size = 776788, upload-time = "2026-04-03T20:53:52.889Z" }, + { url = "https://files.pythonhosted.org/packages/64/0b/8bb9cbf21ef7dee58e49b0fdb066a7aded146c823202e16494a36777594f/regex-2026.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dac006c8b6dda72d86ea3d1333d45147de79a3a3f26f10c1cf9287ca4ca0ac3", size = 785184, upload-time = "2026-04-03T20:53:55.627Z" }, + { url = "https://files.pythonhosted.org/packages/99/c2/d3e80e8137b25ee06c92627de4e4d98b94830e02b3e6f81f3d2e3f504cf5/regex-2026.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:50a766ee2010d504554bfb5f578ed2e066898aa26411d57e6296230627cdefa0", size = 859913, upload-time = "2026-04-03T20:53:57.249Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/9d5d876157d969c804622456ef250017ac7a8f83e0e14f903b9e6df5ce95/regex-2026.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9e2f5217648f68e3028c823df58663587c1507a5ba8419f4fdfc8a461be76043", size = 765732, upload-time = "2026-04-03T20:53:59.428Z" }, + { url = "https://files.pythonhosted.org/packages/82/80/b568935b4421388561c8ed42aff77247285d3ae3bb2a6ca22af63bae805e/regex-2026.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:39d8de85a08e32632974151ba59c6e9140646dcc36c80423962b1c5c0a92e244", size = 852152, upload-time = "2026-04-03T20:54:01.505Z" }, + { url = "https://files.pythonhosted.org/packages/39/29/f0f81217e21cd998245da047405366385d5c6072048038a3d33b37a79dc0/regex-2026.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55d9304e0e7178dfb1e106c33edf834097ddf4a890e2f676f6c5118f84390f73", size = 789076, upload-time = "2026-04-03T20:54:03.323Z" }, + { url = "https://files.pythonhosted.org/packages/49/1d/1d957a61976ab9d4e767dd4f9d04b66cc0c41c5e36cf40e2d43688b5ae6f/regex-2026.4.4-cp312-cp312-win32.whl", hash = "sha256:04bb679bc0bde8a7bfb71e991493d47314e7b98380b083df2447cda4b6edb60f", size = 266700, upload-time = "2026-04-03T20:54:05.639Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5c/bf575d396aeb58ea13b06ef2adf624f65b70fafef6950a80fc3da9cae3bc/regex-2026.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:db0ac18435a40a2543dbb3d21e161a6c78e33e8159bd2e009343d224bb03bb1b", size = 277768, upload-time = "2026-04-03T20:54:07.312Z" }, + { url = "https://files.pythonhosted.org/packages/c9/27/049df16ec6a6828ccd72add3c7f54b4df029669bea8e9817df6fff58be90/regex-2026.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:4ce255cc05c1947a12989c6db801c96461947adb7a59990f1360b5983fab4983", size = 270568, upload-time = "2026-04-03T20:54:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/9d/83/c4373bc5f31f2cf4b66f9b7c31005bd87fe66f0dce17701f7db4ee79ee29/regex-2026.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:62f5519042c101762509b1d717b45a69c0139d60414b3c604b81328c01bd1943", size = 490273, upload-time = "2026-04-03T20:54:11.202Z" }, + { url = "https://files.pythonhosted.org/packages/46/f8/fe62afbcc3cf4ad4ac9adeaafd98aa747869ae12d3e8e2ac293d0593c435/regex-2026.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3790ba9fb5dd76715a7afe34dbe603ba03f8820764b1dc929dd08106214ed031", size = 291954, upload-time = "2026-04-03T20:54:13.412Z" }, + { url = "https://files.pythonhosted.org/packages/5a/92/4712b9fe6a33d232eeb1c189484b80c6c4b8422b90e766e1195d6e758207/regex-2026.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8fae3c6e795d7678963f2170152b0d892cf6aee9ee8afc8c45e6be38d5107fe7", size = 289487, upload-time = "2026-04-03T20:54:15.824Z" }, + { url = "https://files.pythonhosted.org/packages/88/2c/f83b93f85e01168f1070f045a42d4c937b69fdb8dd7ae82d307253f7e36e/regex-2026.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:298c3ec2d53225b3bf91142eb9691025bab610e0c0c51592dde149db679b3d17", size = 796646, upload-time = "2026-04-03T20:54:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/df/55/61a2e17bf0c4dc57e11caf8dd11771280d8aaa361785f9e3bc40d653f4a7/regex-2026.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e9638791082eaf5b3ac112c587518ee78e083a11c4b28012d8fe2a0f536dfb17", size = 865904, upload-time = "2026-04-03T20:54:20.019Z" }, + { url = "https://files.pythonhosted.org/packages/45/32/1ac8ed1b5a346b5993a3d256abe0a0f03b0b73c8cc88d928537368ac65b6/regex-2026.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae3e764bd4c5ff55035dc82a8d49acceb42a5298edf6eb2fc4d328ee5dd7afae", size = 912304, upload-time = "2026-04-03T20:54:22.403Z" }, + { url = "https://files.pythonhosted.org/packages/26/47/2ee5c613ab546f0eddebf9905d23e07beb933416b1246c2d8791d01979b4/regex-2026.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ffa81f81b80047ba89a3c69ae6a0f78d06f4a42ce5126b0eb2a0a10ad44e0b2e", size = 801126, upload-time = "2026-04-03T20:54:24.308Z" }, + { url = "https://files.pythonhosted.org/packages/75/cd/41dacd129ca9fd20bd7d02f83e0fad83e034ac8a084ec369c90f55ef37e2/regex-2026.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f56ebf9d70305307a707911b88469213630aba821e77de7d603f9d2f0730687d", size = 776772, upload-time = "2026-04-03T20:54:26.319Z" }, + { url = "https://files.pythonhosted.org/packages/89/6d/5af0b588174cb5f46041fa7dd64d3fd5cd2fe51f18766703d1edc387f324/regex-2026.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:773d1dfd652bbffb09336abf890bfd64785c7463716bf766d0eb3bc19c8b7f27", size = 785228, upload-time = "2026-04-03T20:54:28.387Z" }, + { url = "https://files.pythonhosted.org/packages/b7/3b/f5a72b7045bd59575fc33bf1345f156fcfd5a8484aea6ad84b12c5a82114/regex-2026.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d51d20befd5275d092cdffba57ded05f3c436317ee56466c8928ac32d960edaf", size = 860032, upload-time = "2026-04-03T20:54:30.641Z" }, + { url = "https://files.pythonhosted.org/packages/39/a4/72a317003d6fcd7a573584a85f59f525dfe8f67e355ca74eb6b53d66a5e2/regex-2026.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:0a51cdb3c1e9161154f976cb2bef9894bc063ac82f31b733087ffb8e880137d0", size = 765714, upload-time = "2026-04-03T20:54:32.789Z" }, + { url = "https://files.pythonhosted.org/packages/25/1e/5672e16f34dbbcb2560cc7e6a2fbb26dfa8b270711e730101da4423d3973/regex-2026.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ae5266a82596114e41fb5302140e9630204c1b5f325c770bec654b95dd54b0aa", size = 852078, upload-time = "2026-04-03T20:54:34.546Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0d/c813f0af7c6cc7ed7b9558bac2e5120b60ad0fa48f813e4d4bd55446f214/regex-2026.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c882cd92ec68585e9c1cf36c447ec846c0d94edd706fe59e0c198e65822fd23b", size = 789181, upload-time = "2026-04-03T20:54:36.642Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6d/a344608d1adbd2a95090ddd906cec09a11be0e6517e878d02a5123e0917f/regex-2026.4.4-cp313-cp313-win32.whl", hash = "sha256:05568c4fbf3cb4fa9e28e3af198c40d3237cf6041608a9022285fe567ec3ad62", size = 266690, upload-time = "2026-04-03T20:54:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/31/07/54049f89b46235ca6f45cd6c88668a7050e77d4a15555e47dd40fde75263/regex-2026.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:3384df51ed52db0bea967e21458ab0a414f67cdddfd94401688274e55147bb81", size = 277733, upload-time = "2026-04-03T20:54:40.11Z" }, + { url = "https://files.pythonhosted.org/packages/0e/21/61366a8e20f4d43fb597708cac7f0e2baadb491ecc9549b4980b2be27d16/regex-2026.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:acd38177bd2c8e69a411d6521760806042e244d0ef94e2dd03ecdaa8a3c99427", size = 270565, upload-time = "2026-04-03T20:54:41.883Z" }, + { url = "https://files.pythonhosted.org/packages/f1/1e/3a2b9672433bef02f5d39aa1143ca2c08f311c1d041c464a42be9ae648dc/regex-2026.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f94a11a9d05afcfcfa640e096319720a19cc0c9f7768e1a61fceee6a3afc6c7c", size = 494126, upload-time = "2026-04-03T20:54:43.602Z" }, + { url = "https://files.pythonhosted.org/packages/4e/4b/c132a4f4fe18ad3340d89fcb56235132b69559136036b845be3c073142ed/regex-2026.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:36bcb9d6d1307ab629edc553775baada2aefa5c50ccc0215fbfd2afcfff43141", size = 293882, upload-time = "2026-04-03T20:54:45.41Z" }, + { url = "https://files.pythonhosted.org/packages/f4/5f/eaa38092ce7a023656280f2341dbbd4ad5f05d780a70abba7bb4f4bea54c/regex-2026.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:261c015b3e2ed0919157046d768774ecde57f03d8fa4ba78d29793447f70e717", size = 292334, upload-time = "2026-04-03T20:54:47.051Z" }, + { url = "https://files.pythonhosted.org/packages/5f/f6/dd38146af1392dac33db7074ab331cec23cced3759167735c42c5460a243/regex-2026.4.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c228cf65b4a54583763645dcd73819b3b381ca8b4bb1b349dee1c135f4112c07", size = 811691, upload-time = "2026-04-03T20:54:49.074Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f0/dc54c2e69f5eeec50601054998ec3690d5344277e782bd717e49867c1d29/regex-2026.4.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dd2630faeb6876fb0c287f664d93ddce4d50cd46c6e88e60378c05c9047e08ca", size = 871227, upload-time = "2026-04-03T20:54:51.035Z" }, + { url = "https://files.pythonhosted.org/packages/a1/af/cb16bd5dc61621e27df919a4449bbb7e5a1034c34d307e0a706e9cc0f3e3/regex-2026.4.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a50ab11b7779b849472337191f3a043e27e17f71555f98d0092fa6d73364520", size = 917435, upload-time = "2026-04-03T20:54:52.994Z" }, + { url = "https://files.pythonhosted.org/packages/5c/71/8b260897f22996b666edd9402861668f45a2ca259f665ac029e6104a2d7d/regex-2026.4.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0734f63afe785138549fbe822a8cfeaccd1bae814c5057cc0ed5b9f2de4fc883", size = 816358, upload-time = "2026-04-03T20:54:54.884Z" }, + { url = "https://files.pythonhosted.org/packages/1c/60/775f7f72a510ef238254906c2f3d737fc80b16ca85f07d20e318d2eea894/regex-2026.4.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c4ee50606cb1967db7e523224e05f32089101945f859928e65657a2cbb3d278b", size = 785549, upload-time = "2026-04-03T20:54:57.01Z" }, + { url = "https://files.pythonhosted.org/packages/58/42/34d289b3627c03cf381e44da534a0021664188fa49ba41513da0b4ec6776/regex-2026.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6c1818f37be3ca02dcb76d63f2c7aaba4b0dc171b579796c6fbe00148dfec6b1", size = 801364, upload-time = "2026-04-03T20:54:58.981Z" }, + { url = "https://files.pythonhosted.org/packages/fc/20/f6ecf319b382a8f1ab529e898b222c3f30600fcede7834733c26279e7465/regex-2026.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:f5bfc2741d150d0be3e4a0401a5c22b06e60acb9aa4daa46d9e79a6dcd0f135b", size = 866221, upload-time = "2026-04-03T20:55:00.88Z" }, + { url = "https://files.pythonhosted.org/packages/92/6a/9f16d3609d549bd96d7a0b2aee1625d7512ba6a03efc01652149ef88e74d/regex-2026.4.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:504ffa8a03609a087cad81277a629b6ce884b51a24bd388a7980ad61748618ff", size = 772530, upload-time = "2026-04-03T20:55:03.213Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f6/aa9768bc96a4c361ac96419fbaf2dcdc33970bb813df3ba9b09d5d7b6d96/regex-2026.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:70aadc6ff12e4b444586e57fc30771f86253f9f0045b29016b9605b4be5f7dfb", size = 856989, upload-time = "2026-04-03T20:55:05.087Z" }, + { url = "https://files.pythonhosted.org/packages/4d/b4/c671db3556be2473ae3e4bb7a297c518d281452871501221251ea4ecba57/regex-2026.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f4f83781191007b6ef43b03debc35435f10cad9b96e16d147efe84a1d48bdde4", size = 803241, upload-time = "2026-04-03T20:55:07.162Z" }, + { url = "https://files.pythonhosted.org/packages/2a/5c/83e3b1d89fa4f6e5a1bc97b4abd4a9a97b3c1ac7854164f694f5f0ba98a0/regex-2026.4.4-cp313-cp313t-win32.whl", hash = "sha256:e014a797de43d1847df957c0a2a8e861d1c17547ee08467d1db2c370b7568baa", size = 269921, upload-time = "2026-04-03T20:55:09.62Z" }, + { url = "https://files.pythonhosted.org/packages/28/07/077c387121f42cdb4d92b1301133c0d93b5709d096d1669ab847dda9fe2e/regex-2026.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:b15b88b0d52b179712632832c1d6e58e5774f93717849a41096880442da41ab0", size = 281240, upload-time = "2026-04-03T20:55:11.521Z" }, + { url = "https://files.pythonhosted.org/packages/9d/22/ead4a4abc7c59a4d882662aa292ca02c8b617f30b6e163bc1728879e9353/regex-2026.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:586b89cdadf7d67bf86ae3342a4dcd2b8d70a832d90c18a0ae955105caf34dbe", size = 272440, upload-time = "2026-04-03T20:55:13.365Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f5/ed97c2dc47b5fbd4b73c0d7d75f9ebc8eca139f2bbef476bba35f28c0a77/regex-2026.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2da82d643fa698e5e5210e54af90181603d5853cf469f5eedf9bfc8f59b4b8c7", size = 490343, upload-time = "2026-04-03T20:55:15.241Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/de4828a7385ec166d673a5790ad06ac48cdaa98bc0960108dd4b9cc1aef7/regex-2026.4.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:54a1189ad9d9357760557c91103d5e421f0a2dabe68a5cdf9103d0dcf4e00752", size = 291909, upload-time = "2026-04-03T20:55:17.558Z" }, + { url = "https://files.pythonhosted.org/packages/b4/d6/5cfbfc97f3201a4d24b596a77957e092030dcc4205894bc035cedcfce62f/regex-2026.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:76d67d5afb1fe402d10a6403bae668d000441e2ab115191a804287d53b772951", size = 289692, upload-time = "2026-04-03T20:55:20.561Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ac/f2212d9fd56fe897e36d0110ba30ba2d247bd6410c5bd98499c7e5a1e1f2/regex-2026.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e7cd3e4ee8d80447a83bbc9ab0c8459781fa77087f856c3e740d7763be0df27f", size = 796979, upload-time = "2026-04-03T20:55:22.56Z" }, + { url = "https://files.pythonhosted.org/packages/c9/e3/a016c12675fbac988a60c7e1c16e67823ff0bc016beb27bd7a001dbdabc6/regex-2026.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e19e18c568d2866d8b6a6dfad823db86193503f90823a8f66689315ba28fbe8", size = 866744, upload-time = "2026-04-03T20:55:24.646Z" }, + { url = "https://files.pythonhosted.org/packages/af/a4/0b90ca4cf17adc3cb43de80ec71018c37c88ad64987e8d0d481a95ca60b5/regex-2026.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7698a6f38730fd1385d390d1ed07bb13dce39aa616aca6a6d89bea178464b9a4", size = 911613, upload-time = "2026-04-03T20:55:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/8e/3b/2b3dac0b82d41ab43aa87c6ecde63d71189d03fe8854b8ca455a315edac3/regex-2026.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:173a66f3651cdb761018078e2d9487f4cf971232c990035ec0eb1cdc6bf929a9", size = 800551, upload-time = "2026-04-03T20:55:29.532Z" }, + { url = "https://files.pythonhosted.org/packages/25/fe/5365eb7aa0e753c4b5957815c321519ecab033c279c60e1b1ae2367fa810/regex-2026.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa7922bbb2cc84fa062d37723f199d4c0cd200245ce269c05db82d904db66b83", size = 776911, upload-time = "2026-04-03T20:55:31.526Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b3/7fb0072156bba065e3b778a7bc7b0a6328212be5dd6a86fd207e0c4f2dab/regex-2026.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:59f67cd0a0acaf0e564c20bbd7f767286f23e91e2572c5703bf3e56ea7557edb", size = 785751, upload-time = "2026-04-03T20:55:33.797Z" }, + { url = "https://files.pythonhosted.org/packages/02/1a/9f83677eb699273e56e858f7bd95acdbee376d42f59e8bfca2fd80d79df3/regex-2026.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:475e50f3f73f73614f7cba5524d6de49dee269df00272a1b85e3d19f6d498465", size = 860484, upload-time = "2026-04-03T20:55:35.745Z" }, + { url = "https://files.pythonhosted.org/packages/3b/7a/93937507b61cfcff8b4c5857f1b452852b09f741daa9acae15c971d8554e/regex-2026.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a1c0c7d67b64d85ac2e1879923bad2f08a08f3004055f2f406ef73c850114bd4", size = 765939, upload-time = "2026-04-03T20:55:37.972Z" }, + { url = "https://files.pythonhosted.org/packages/86/ea/81a7f968a351c6552b1670ead861e2a385be730ee28402233020c67f9e0f/regex-2026.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:1371c2ccbb744d66ee63631cc9ca12aa233d5749972626b68fe1a649dd98e566", size = 851417, upload-time = "2026-04-03T20:55:39.92Z" }, + { url = "https://files.pythonhosted.org/packages/4c/7e/323c18ce4b5b8f44517a36342961a0306e931e499febbd876bb149d900f0/regex-2026.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59968142787042db793348a3f5b918cf24ced1f23247328530e063f89c128a95", size = 789056, upload-time = "2026-04-03T20:55:42.303Z" }, + { url = "https://files.pythonhosted.org/packages/c0/af/e7510f9b11b1913b0cd44eddb784b2d650b2af6515bfce4cffcc5bfd1d38/regex-2026.4.4-cp314-cp314-win32.whl", hash = "sha256:59efe72d37fd5a91e373e5146f187f921f365f4abc1249a5ab446a60f30dd5f8", size = 272130, upload-time = "2026-04-03T20:55:44.995Z" }, + { url = "https://files.pythonhosted.org/packages/9a/51/57dae534c915e2d3a21490e88836fa2ae79dde3b66255ecc0c0a155d2c10/regex-2026.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:e0aab3ff447845049d676827d2ff714aab4f73f340e155b7de7458cf53baa5a4", size = 280992, upload-time = "2026-04-03T20:55:47.316Z" }, + { url = "https://files.pythonhosted.org/packages/0a/5e/abaf9f4c3792e34edb1434f06717fae2b07888d85cb5cec29f9204931bf8/regex-2026.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:a7a5bb6aa0cf62208bb4fa079b0c756734f8ad0e333b425732e8609bd51ee22f", size = 273563, upload-time = "2026-04-03T20:55:49.273Z" }, + { url = "https://files.pythonhosted.org/packages/ff/06/35da85f9f217b9538b99cbb170738993bcc3b23784322decb77619f11502/regex-2026.4.4-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:97850d0638391bdc7d35dc1c1039974dcb921eaafa8cc935ae4d7f272b1d60b3", size = 494191, upload-time = "2026-04-03T20:55:51.258Z" }, + { url = "https://files.pythonhosted.org/packages/54/5b/1bc35f479eef8285c4baf88d8c002023efdeebb7b44a8735b36195486ae7/regex-2026.4.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ee7337f88f2a580679f7bbfe69dc86c043954f9f9c541012f49abc554a962f2e", size = 293877, upload-time = "2026-04-03T20:55:53.214Z" }, + { url = "https://files.pythonhosted.org/packages/39/5b/f53b9ad17480b3ddd14c90da04bfb55ac6894b129e5dea87bcaf7d00e336/regex-2026.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7429f4e6192c11d659900c0648ba8776243bf396ab95558b8c51a345afeddde6", size = 292410, upload-time = "2026-04-03T20:55:55.736Z" }, + { url = "https://files.pythonhosted.org/packages/bb/56/52377f59f60a7c51aa4161eecf0b6032c20b461805aca051250da435ffc9/regex-2026.4.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4f10fbd5dd13dcf4265b4cc07d69ca70280742870c97ae10093e3d66000359", size = 811831, upload-time = "2026-04-03T20:55:57.802Z" }, + { url = "https://files.pythonhosted.org/packages/dd/63/8026310bf066f702a9c361f83a8c9658f3fe4edb349f9c1e5d5273b7c40c/regex-2026.4.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a152560af4f9742b96f3827090f866eeec5becd4765c8e0d3473d9d280e76a5a", size = 871199, upload-time = "2026-04-03T20:56:00.333Z" }, + { url = "https://files.pythonhosted.org/packages/20/9f/a514bbb00a466dbb506d43f187a04047f7be1505f10a9a15615ead5080ee/regex-2026.4.4-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54170b3e95339f415d54651f97df3bff7434a663912f9358237941bbf9143f55", size = 917649, upload-time = "2026-04-03T20:56:02.445Z" }, + { url = "https://files.pythonhosted.org/packages/cb/6b/8399f68dd41a2030218839b9b18360d79b86d22b9fab5ef477c7f23ca67c/regex-2026.4.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:07f190d65f5a72dcb9cf7106bfc3d21e7a49dd2879eda2207b683f32165e4d99", size = 816388, upload-time = "2026-04-03T20:56:04.595Z" }, + { url = "https://files.pythonhosted.org/packages/1e/9c/103963f47c24339a483b05edd568594c2be486188f688c0170fd504b2948/regex-2026.4.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9a2741ce5a29d3c84b0b94261ba630ab459a1b847a0d6beca7d62d188175c790", size = 785746, upload-time = "2026-04-03T20:56:07.13Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ee/7f6054c0dec0cee3463c304405e4ff42e27cff05bf36fcb34be549ab17bd/regex-2026.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b26c30df3a28fd9793113dac7385a4deb7294a06c0f760dd2b008bd49a9139bc", size = 801483, upload-time = "2026-04-03T20:56:09.365Z" }, + { url = "https://files.pythonhosted.org/packages/30/c2/51d3d941cf6070dc00c3338ecf138615fc3cce0421c3df6abe97a08af61a/regex-2026.4.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:421439d1bee44b19f4583ccf42670ca464ffb90e9fdc38d37f39d1ddd1e44f1f", size = 866331, upload-time = "2026-04-03T20:56:12.039Z" }, + { url = "https://files.pythonhosted.org/packages/16/e8/76d50dcc122ac33927d939f350eebcfe3dbcbda96913e03433fc36de5e63/regex-2026.4.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b40379b53ecbc747fd9bdf4a0ea14eb8188ca1bd0f54f78893a39024b28f4863", size = 772673, upload-time = "2026-04-03T20:56:14.558Z" }, + { url = "https://files.pythonhosted.org/packages/a5/6e/5f6bf75e20ea6873d05ba4ec78378c375cbe08cdec571c83fbb01606e563/regex-2026.4.4-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:08c55c13d2eef54f73eeadc33146fb0baaa49e7335eb1aff6ae1324bf0ddbe4a", size = 857146, upload-time = "2026-04-03T20:56:16.663Z" }, + { url = "https://files.pythonhosted.org/packages/0b/33/3c76d9962949e487ebba353a18e89399f292287204ac8f2f4cfc3a51c233/regex-2026.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9776b85f510062f5a75ef112afe5f494ef1635607bf1cc220c1391e9ac2f5e81", size = 803463, upload-time = "2026-04-03T20:56:18.923Z" }, + { url = "https://files.pythonhosted.org/packages/19/eb/ef32dcd2cb69b69bc0c3e55205bce94a7def48d495358946bc42186dcccc/regex-2026.4.4-cp314-cp314t-win32.whl", hash = "sha256:385edaebde5db5be103577afc8699fea73a0e36a734ba24870be7ffa61119d74", size = 275709, upload-time = "2026-04-03T20:56:20.996Z" }, + { url = "https://files.pythonhosted.org/packages/a0/86/c291bf740945acbf35ed7dbebf8e2eea2f3f78041f6bd7cdab80cb274dc0/regex-2026.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:5d354b18839328927832e2fa5f7c95b7a3ccc39e7a681529e1685898e6436d45", size = 285622, upload-time = "2026-04-03T20:56:23.641Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e7/ec846d560ae6a597115153c02ca6138a7877a1748b2072d9521c10a93e58/regex-2026.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:af0384cb01a33600c49505c27c6c57ab0b27bf84a74e28524c92ca897ebdac9d", size = 275773, upload-time = "2026-04-03T20:56:26.07Z" }, +] + +[[package]] +name = "requests" +version = "2.33.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, +] + +[[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 = "safetensors" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6a/4d08d89a6fcbe905c5ae68b8b34f0791850882fc19782d0d02c65abbdf3b/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4729811a6640d019a4b7ba8638ee2fd21fa5ca8c7e7bdf0fed62068fcaac737", size = 492430, upload-time = "2025-11-19T15:18:11.884Z" }, + { url = "https://files.pythonhosted.org/packages/dd/29/59ed8152b30f72c42d00d241e58eaca558ae9dbfa5695206e2e0f54c7063/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12f49080303fa6bb424b362149a12949dfbbf1e06811a88f2307276b0c131afd", size = 503977, upload-time = "2025-11-19T15:18:17.523Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0b/4811bfec67fa260e791369b16dab105e4bae82686120554cc484064e22b4/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0071bffba4150c2f46cae1432d31995d77acfd9f8db598b5d1a2ce67e8440ad2", size = 623890, upload-time = "2025-11-19T15:18:22.666Z" }, + { url = "https://files.pythonhosted.org/packages/58/5b/632a58724221ef03d78ab65062e82a1010e1bef8e8e0b9d7c6d7b8044841/safetensors-0.7.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:473b32699f4200e69801bf5abf93f1a4ecd432a70984df164fc22ccf39c4a6f3", size = 531885, upload-time = "2025-11-19T15:18:27.146Z" }, +] + +[[package]] +name = "sentry-sdk" +version = "2.58.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/b3/fb8291170d0e844173164709fc0fa0c221ed75a5da740c8746f2a83b4eb1/sentry_sdk-2.58.0.tar.gz", hash = "sha256:c1144d947352d54e5b7daa63596d9f848adf684989c06c4f5a659f0c85a18f6f", size = 438764, upload-time = "2026-04-13T17:23:26.265Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/eb/d875669993b762556ae8b2efd86219943b4c0864d22204d622a9aee3052b/sentry_sdk-2.58.0-py2.py3-none-any.whl", hash = "sha256:688d1c704ddecf382ea3326f21a67453d4caa95592d722b7c780a36a9d23109e", size = 460919, upload-time = "2026-04-13T17:23:24.675Z" }, +] + +[[package]] +name = "setuptools" +version = "81.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "smmap" +version = "5.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, +] + +[[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 = "starlette" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/69/17425771797c36cded50b7fe44e850315d039f28b15901ab44839e70b593/starlette-1.0.0.tar.gz", hash = "sha256:6a4beaf1f81bb472fd19ea9b918b50dc3a77a6f2e190a12954b25e6ed5eea149", size = 2655289, upload-time = "2026-03-22T18:29:46.779Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/c9/584bc9651441b4ba60cc4d557d8a547b5aff901af35bda3a4ee30c819b82/starlette-1.0.0-py3-none-any.whl", hash = "sha256:d3ec55e0bb321692d275455ddfd3df75fff145d009685eb40dc91fc66b03d38b", size = 72651, upload-time = "2026-03-22T18:29:45.111Z" }, +] + +[[package]] +name = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, + { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301, upload-time = "2026-01-05T10:40:34.858Z" }, + { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308, upload-time = "2026-01-05T10:40:40.737Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964, upload-time = "2026-01-05T10:40:46.56Z" }, + { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542, upload-time = "2026-01-05T10:40:52.803Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" }, + { url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" }, + { url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" }, + { url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" }, + { url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" }, + { url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" }, + { url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" }, + { url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" }, + { url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" }, + { url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" }, + { url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" }, + { url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" }, + { url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" }, + { url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" }, + { url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" }, + { url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" }, + { url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "torch" +version = "2.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/f2/c1690994afe461aae2d0cac62251e6802a703dec0a6c549c02ecd0de92a9/torch-2.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2c0d7fcfbc0c4e8bb5ebc3907cbc0c6a0da1b8f82b1fc6e14e914fa0b9baf74e", size = 80526521, upload-time = "2026-03-23T18:12:06.86Z" }, + { url = "https://files.pythonhosted.org/packages/a4/f0/98ae802fa8c09d3149b0c8690741f3f5753c90e779bd28c9613257295945/torch-2.11.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:4cf8687f4aec3900f748d553483ef40e0ac38411c3c48d0a86a438f6d7a99b18", size = 419723025, upload-time = "2026-03-23T18:11:43.774Z" }, + { url = "https://files.pythonhosted.org/packages/f9/1e/18a9b10b4bd34f12d4e561c52b0ae7158707b8193c6cfc0aad2b48167090/torch-2.11.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:1b32ceda909818a03b112006709b02be1877240c31750a8d9c6b7bf5f2d8a6e5", size = 530589207, upload-time = "2026-03-23T18:11:23.756Z" }, + { url = "https://files.pythonhosted.org/packages/35/40/2d532e8c0e23705be9d1debce5bc37b68d59a39bda7584c26fe9668076fe/torch-2.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:b3c712ae6fb8e7a949051a953fc412fe0a6940337336c3b6f905e905dac5157f", size = 114518313, upload-time = "2026-03-23T18:11:58.281Z" }, + { url = "https://files.pythonhosted.org/packages/ae/0d/98b410492609e34a155fa8b121b55c7dca229f39636851c3a9ec20edea21/torch-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7b6a60d48062809f58595509c524b88e6ddec3ebe25833d6462eeab81e5f2ce4", size = 80529712, upload-time = "2026-03-23T18:12:02.608Z" }, + { url = "https://files.pythonhosted.org/packages/84/03/acea680005f098f79fd70c1d9d5ccc0cb4296ec2af539a0450108232fc0c/torch-2.11.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:d91aac77f24082809d2c5a93f52a5f085032740a1ebc9252a7b052ef5a4fddc6", size = 419718178, upload-time = "2026-03-23T18:10:46.675Z" }, + { url = "https://files.pythonhosted.org/packages/8c/8b/d7be22fbec9ffee6cff31a39f8750d4b3a65d349a286cf4aec74c2375662/torch-2.11.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7aa2f9bbc6d4595ba72138026b2074be1233186150e9292865e04b7a63b8c67a", size = 530604548, upload-time = "2026-03-23T18:10:03.569Z" }, + { url = "https://files.pythonhosted.org/packages/d1/bd/9912d30b68845256aabbb4a40aeefeef3c3b20db5211ccda653544ada4b6/torch-2.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:73e24aaf8f36ab90d95cd1761208b2eb70841c2a9ca1a3f9061b39fc5331b708", size = 114519675, upload-time = "2026-03-23T18:11:52.995Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8b/69e3008d78e5cee2b30183340cc425081b78afc5eff3d080daab0adda9aa/torch-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b5866312ee6e52ea625cd211dcb97d6a2cdc1131a5f15cc0d87eec948f6dd34", size = 80606338, upload-time = "2026-03-23T18:11:34.781Z" }, + { url = "https://files.pythonhosted.org/packages/13/16/42e5915ebe4868caa6bac83a8ed59db57f12e9a61b7d749d584776ed53d5/torch-2.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f99924682ef0aa6a4ab3b1b76f40dc6e273fca09f367d15a524266db100a723f", size = 419731115, upload-time = "2026-03-23T18:11:06.944Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c9/82638ef24d7877510f83baf821f5619a61b45568ce21c0a87a91576510aa/torch-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0f68f4ac6d95d12e896c3b7a912b5871619542ec54d3649cf48cc1edd4dd2756", size = 530712279, upload-time = "2026-03-23T18:10:31.481Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ff/6756f1c7ee302f6d202120e0f4f05b432b839908f9071157302cedfc5232/torch-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:fbf39280699d1b869f55eac536deceaa1b60bd6788ba74f399cc67e60a5fab10", size = 114556047, upload-time = "2026-03-23T18:10:55.931Z" }, + { url = "https://files.pythonhosted.org/packages/87/89/5ea6722763acee56b045435fb84258db7375c48165ec8be7880ab2b281c5/torch-2.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6debd97ccd3205bbb37eb806a9d8219e1139d15419982c09e23ef7d4369d18", size = 80606801, upload-time = "2026-03-23T18:10:18.649Z" }, + { url = "https://files.pythonhosted.org/packages/32/d1/8ed2173589cbfe744ed54e5a73efc107c0085ba5777ee93a5f4c1ab90553/torch-2.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:63a68fa59de8f87acc7e85a5478bb2dddbb3392b7593ec3e78827c793c4b73fd", size = 419732382, upload-time = "2026-03-23T18:08:30.835Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e1/b73f7c575a4b8f87a5928f50a1e35416b5e27295d8be9397d5293e7e8d4c/torch-2.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:cc89b9b173d9adfab59fd227f0ab5e5516d9a52b658ae41d64e59d2e55a418db", size = 530711509, upload-time = "2026-03-23T18:08:47.213Z" }, + { url = "https://files.pythonhosted.org/packages/66/82/3e3fcdd388fbe54e29fd3f991f36846ff4ac90b0d0181e9c8f7236565f82/torch-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:4dda3b3f52d121063a731ddb835f010dc137b920d7fec2778e52f60d8e4bf0cd", size = 114555842, upload-time = "2026-03-23T18:09:52.111Z" }, + { url = "https://files.pythonhosted.org/packages/db/38/8ac78069621b8c2b4979c2f96dc8409ef5e9c4189f6aac629189a78677ca/torch-2.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8b394322f49af4362d4f80e424bcaca7efcd049619af03a4cf4501520bdf0fb4", size = 80959574, upload-time = "2026-03-23T18:10:14.214Z" }, + { url = "https://files.pythonhosted.org/packages/6d/6c/56bfb37073e7136e6dd86bfc6af7339946dd684e0ecf2155ac0eee687ae1/torch-2.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2658f34ce7e2dabf4ec73b45e2ca68aedad7a5be87ea756ad656eaf32bf1e1ea", size = 419732324, upload-time = "2026-03-23T18:09:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/07/f4/1b666b6d61d3394cca306ea543ed03a64aad0a201b6cd159f1d41010aeb1/torch-2.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:98bb213c3084cfe176302949bdc360074b18a9da7ab59ef2edc9d9f742504778", size = 530596026, upload-time = "2026-03-23T18:09:20.842Z" }, + { url = "https://files.pythonhosted.org/packages/48/6b/30d1459fa7e4b67e9e3fe1685ca1d8bb4ce7c62ef436c3a615963c6c866c/torch-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a97b94bbf62992949b4730c6cd2cc9aee7b335921ee8dc207d930f2ed09ae2db", size = 114793702, upload-time = "2026-03-23T18:09:47.304Z" }, + { url = "https://files.pythonhosted.org/packages/26/0d/8603382f61abd0db35841148ddc1ffd607bf3100b11c6e1dab6d2fc44e72/torch-2.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01018087326984a33b64e04c8cb5c2795f9120e0d775ada1f6638840227b04d7", size = 80573442, upload-time = "2026-03-23T18:09:10.117Z" }, + { url = "https://files.pythonhosted.org/packages/c7/86/7cd7c66cb9cec6be330fff36db5bd0eef386d80c031b581ec81be1d4b26c/torch-2.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:2bb3cc54bd0dea126b0060bb1ec9de0f9c7f7342d93d436646516b0330cd5be7", size = 419749385, upload-time = "2026-03-23T18:07:33.77Z" }, + { url = "https://files.pythonhosted.org/packages/47/e8/b98ca2d39b2e0e4730c0ee52537e488e7008025bc77ca89552ff91021f7c/torch-2.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4dc8b3809469b6c30b411bb8c4cad3828efd26236153d9beb6a3ec500f211a60", size = 530716756, upload-time = "2026-03-23T18:07:50.02Z" }, + { url = "https://files.pythonhosted.org/packages/78/88/d4a4cda8362f8a30d1ed428564878c3cafb0d87971fbd3947d4c84552095/torch-2.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b4e811728bd0cc58fb2b0948fe939a1ee2bf1422f6025be2fca4c7bd9d79718", size = 114552300, upload-time = "2026-03-23T18:09:05.617Z" }, + { url = "https://files.pythonhosted.org/packages/bf/46/4419098ed6d801750f26567b478fc185c3432e11e2cad712bc6b4c2ab0d0/torch-2.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8245477871c3700d4370352ffec94b103cfcb737229445cf9946cddb7b2ca7cd", size = 80959460, upload-time = "2026-03-23T18:09:00.818Z" }, + { url = "https://files.pythonhosted.org/packages/fd/66/54a56a4a6ceaffb567231994a9745821d3af922a854ed33b0b3a278e0a99/torch-2.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ab9a8482f475f9ba20e12db84b0e55e2f58784bdca43a854a6ccd3fd4b9f75e6", size = 419735835, upload-time = "2026-03-23T18:07:18.974Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e7/0b6665f533aa9e337662dc190425abc0af1fe3234088f4454c52393ded61/torch-2.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:563ed3d25542d7e7bbc5b235ccfacfeb97fb470c7fee257eae599adb8005c8a2", size = 530613405, upload-time = "2026-03-23T18:08:07.014Z" }, + { url = "https://files.pythonhosted.org/packages/cf/bf/c8d12a2c86dbfd7f40fb2f56fbf5a505ccf2d9ce131eb559dfc7c51e1a04/torch-2.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b2a43985ff5ef6ddd923bbcf99943e5f58059805787c5c9a2622bf05ca2965b0", size = 114792991, upload-time = "2026-03-23T18:08:19.216Z" }, +] + +[[package]] +name = "torchvision" +version = "0.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pillow" }, + { name = "torch" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/b4/cdfee31e0402ea035135462cb0ab496e974d56fab6b4e7a1f0cbccb8cd28/torchvision-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a06d4772a8e13e772906ed736cc53ec6639e5e60554f8e5fa6ca165aabebc464", size = 1863503, upload-time = "2026-03-23T18:13:01.384Z" }, + { url = "https://files.pythonhosted.org/packages/e4/74/11fee109841e80ad14e5ca2d80bff6b10eb11b7838ff06f35bfeaa9f7251/torchvision-0.26.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:2adfbe438473236191ff077a4a9a0c767436879c89628aa97137e959b0c11a94", size = 7766423, upload-time = "2026-03-23T18:12:56.049Z" }, + { url = "https://files.pythonhosted.org/packages/5e/00/24d8c7845c3f270153fb81395a5135b2778e2538e81d14c6aea5106c689c/torchvision-0.26.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b6f9ad1ecc0eab52647298b379ee9426845f8903703e6127973f8f3d049a798b", size = 7518249, upload-time = "2026-03-23T18:12:51.743Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ed/e53cd7c0da7ae002e5e929c1796ebbe7ec0c700c29f7a0a6696497fb3d8b/torchvision-0.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:f13f12b3791a266de2d599cb8162925261622a037d87fc03132848343cf68f75", size = 3669784, upload-time = "2026-03-23T18:12:49.949Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bd/d552a2521bade3295b2c6e7a4a0d1022261cab7ca7011f4e2a330dbb3caa/torchvision-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55bd6ad4ae77be01ba67a410b05b51f53b0d0ee45f146eb6a0dfb9007e70ab3c", size = 1863499, upload-time = "2026-03-23T18:12:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/33/bf/21b899792b08cae7a298551c68398a79e333697479ed311b3b067aab4bdc/torchvision-0.26.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:1c55dc8affbcc0eb2060fbabbe996ae9e5839b24bb6419777f17848945a411b1", size = 7767527, upload-time = "2026-03-23T18:12:44.348Z" }, + { url = "https://files.pythonhosted.org/packages/9a/45/57bbf9e216850d065e66dd31a50f57424b607f1d878ab8956e56a1f4e36b/torchvision-0.26.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:fd10b5f994c210f4f6d6761cf686f82d748554adf486cb0979770c3252868c8f", size = 7519925, upload-time = "2026-03-23T18:12:53.283Z" }, + { url = "https://files.pythonhosted.org/packages/10/58/ed8f7754299f3e91d6414b6dc09f62b3fa7c6e5d63dfe48d69ab81498a37/torchvision-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:de6424b12887ad884f39a0ee446994ae3cd3b6a00a9cafe1bead85a031132af0", size = 3983834, upload-time = "2026-03-23T18:13:00.224Z" }, + { url = "https://files.pythonhosted.org/packages/ae/e7/56b47cc3b132aea90ccce22bcb8975dec688b002150012acc842846039d0/torchvision-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c409e1c3fdebec7a3834465086dbda8bf7680eff79abf7fd2f10c6b59520a7a4", size = 1863502, upload-time = "2026-03-23T18:12:57.326Z" }, + { url = "https://files.pythonhosted.org/packages/f4/ec/5c31c92c08b65662fe9604a4067ae8232582805949f11ddc042cebe818ed/torchvision-0.26.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:406557718e62fdf10f5706e88d8a5ec000f872da913bf629aab9297622585547", size = 7767944, upload-time = "2026-03-23T18:12:42.805Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d8/cb6ccda1a1f35a6597645818641701207b3e8e13553e75fce5d86bac74b2/torchvision-0.26.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d61a5abb6b42a0c0c311996c2ac4b83a94418a97182c83b055a2a4ae985e05aa", size = 7522205, upload-time = "2026-03-23T18:12:54.654Z" }, + { url = "https://files.pythonhosted.org/packages/1c/a9/c272623a0f735c35f0f6cd6dc74784d4f970e800cf063bb76687895a2ab9/torchvision-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:7993c01648e7c61d191b018e84d38fe0825c8fcb2720cd0f37caf7ba14404aa1", size = 4255155, upload-time = "2026-03-23T18:12:32.652Z" }, + { url = "https://files.pythonhosted.org/packages/da/80/0762f77f53605d10c9477be39bb47722cc8e383bbbc2531471ce0e396c07/torchvision-0.26.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5d63dd43162691258b1b3529b9041bac7d54caa37eae0925f997108268cbf7c4", size = 1860809, upload-time = "2026-03-23T18:12:47.629Z" }, + { url = "https://files.pythonhosted.org/packages/e6/81/0b3e58d1478c660a5af4268713486b2df7203f35abd9195fea87348a5178/torchvision-0.26.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a39c7a26538c41fda453f9a9692b5ff9b35a5437db1d94f3027f6f509c160eac", size = 7727494, upload-time = "2026-03-23T18:12:46.062Z" }, + { url = "https://files.pythonhosted.org/packages/b6/dc/d9ab5d29115aa05e12e30f1397a3eeae1d88a511241dc3bce48dc4342675/torchvision-0.26.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b7e6213620bbf97742e5f79832f9e9d769e6cf0f744c5b53dad80b76db633691", size = 7521747, upload-time = "2026-03-23T18:12:36.815Z" }, + { url = "https://files.pythonhosted.org/packages/a9/1b/f1bc86a918c5f6feab1eeff11982e2060f4704332e96185463d27855bdf5/torchvision-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:4280c35ec8cba1fcc8294fb87e136924708726864c379e4c54494797d86bc474", size = 4319880, upload-time = "2026-03-23T18:12:38.168Z" }, + { url = "https://files.pythonhosted.org/packages/66/28/b4ad0a723ed95b003454caffcc41894b34bd8379df340848cae2c33871de/torchvision-0.26.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:358fc4726d0c08615b6d83b3149854f11efb2a564ed1acb6fce882e151412d23", size = 1951973, upload-time = "2026-03-23T18:12:48.781Z" }, + { url = "https://files.pythonhosted.org/packages/71/e2/7a89096e6cf2f3336353b5338ba925e0addf9d8601920340e6bdf47e8eb3/torchvision-0.26.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:3daf9cc149cf3cdcbd4df9c59dae69ffca86c6823250442c3bbfd63fc2e26c61", size = 7728679, upload-time = "2026-03-23T18:12:26.196Z" }, + { url = "https://files.pythonhosted.org/packages/69/1d/4e1eebc17d18ce080a11dcf3df3f8f717f0efdfa00983f06e8ba79259f61/torchvision-0.26.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:82c3965eca27e86a316e31e4c3e5a16d353e0bcbe0ef8efa2e66502c54493c4b", size = 7609138, upload-time = "2026-03-23T18:12:35.327Z" }, + { url = "https://files.pythonhosted.org/packages/f3/a4/f1155e943ae5b32400d7000adc81c79bb0392b16ceb33bcf13e02e48cced/torchvision-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ebc043cc5a4f0bf22e7680806dbba37ffb19e70f6953bbb44ed1a90aeb5c9bea", size = 4248202, upload-time = "2026-03-23T18:12:41.423Z" }, + { url = "https://files.pythonhosted.org/packages/7f/c8/9bffa9c7f7bdf95b2a0a2dc535c290b9f1cc580c3fb3033ab1246ffffdeb/torchvision-0.26.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:eb61804eb9dbe88c5a2a6c4da8dec1d80d2d0a6f18c999c524e32266cb1ebcd3", size = 1860813, upload-time = "2026-03-23T18:12:39.636Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ac/48f28ffd227991f2e14f4392dde7e8dc14352bb9428c1ef4a4bbf5f7ed85/torchvision-0.26.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:9a904f2131cbfadab4df828088a9f66291ad33f49ff853872aed1f86848ef776", size = 7727777, upload-time = "2026-03-23T18:12:22.549Z" }, + { url = "https://files.pythonhosted.org/packages/a4/21/a2266f7f1b0e58e624ff15fd6f01041f59182c49551ece0db9a183071329/torchvision-0.26.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:0f3e572efe62ad645017ea847e0b5e4f2f638d4e39f05bc011d1eb9ac68d4806", size = 7522174, upload-time = "2026-03-23T18:12:29.565Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ba/1666f90bc0bdd77aaa11dcc42bb9f621a9c3668819c32430452e3d404730/torchvision-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:114bec0c0e98aa4ba446f63e2fe7a2cbca37b39ac933987ee4804f65de121800", size = 4348469, upload-time = "2026-03-23T18:12:24.44Z" }, + { url = "https://files.pythonhosted.org/packages/45/8f/1f0402ac55c2ae15651ff831957d083fe70b2d12282e72612a30ba601512/torchvision-0.26.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:b7d3e295624a28b3b1769228ce1345d94cf4d390dd31136766f76f2d20f718da", size = 1860826, upload-time = "2026-03-23T18:12:34.1Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6a/18a582fe3c5ee26f49b5c9fb21ad8016b4d1c06d10178894a58653946fda/torchvision-0.26.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:7058c5878262937e876f20c25867b33724586aa4499e2853b2d52b99a5e51953", size = 7729089, upload-time = "2026-03-23T18:12:31.394Z" }, + { url = "https://files.pythonhosted.org/packages/c5/9b/f7e119b59499edc00c55c03adc9ec3bd96144d9b81c46852c431f9c64a9a/torchvision-0.26.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:8008474855623c6ba52876589dc52df0aa66e518c25eca841445348e5f79844c", size = 7522704, upload-time = "2026-03-23T18:12:20.301Z" }, + { url = "https://files.pythonhosted.org/packages/d0/6a/09f3844c10643f6c0de5d95abc863420cfaf194c88c7dffd0ac523e2015f/torchvision-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e9d0e022c19a78552fb055d0414d47fecb4a649309b9968573daea160ba6869c", size = 4454275, upload-time = "2026-03-23T18:12:27.487Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, +] + +[[package]] +name = "transformers" +version = "5.6.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a4/e9/c6c80a07690142a7d05444271f47b9f3c8aac7dea01d52e1137ee480ad78/transformers-5.6.2.tar.gz", hash = "sha256:e657134c3e5a6bc00a3c35f4e2674bb51adfcd89898495b788a18552bac2b91a", size = 8311867, upload-time = "2026-04-23T18:33:29.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/95/0b0218149b0d6f14df35f5b8f676fa83df4f19ed253c3cc447107ef86eca/transformers-5.6.2-py3-none-any.whl", hash = "sha256:f8d3a1bb96778fed9b8aabfd0dd6e19843e4b0f2bb6b59f32b8a92051b0f348f", size = 10364898, upload-time = "2026-04-23T18:33:26.081Z" }, +] + +[[package]] +name = "triton" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/ba/b1b04f4b291a3205d95ebd24465de0e5bf010a2df27a4e58a9b5f039d8f2/triton-3.6.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c723cfb12f6842a0ae94ac307dba7e7a44741d720a40cf0e270ed4a4e3be781", size = 175972180, upload-time = "2026-01-20T16:15:53.664Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f7/f1c9d3424ab199ac53c2da567b859bcddbb9c9e7154805119f8bd95ec36f/triton-3.6.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6550fae429e0667e397e5de64b332d1e5695b73650ee75a6146e2e902770bea", size = 188105201, upload-time = "2026-01-20T16:00:29.272Z" }, + { url = "https://files.pythonhosted.org/packages/0f/2c/96f92f3c60387e14cc45aed49487f3486f89ea27106c1b1376913c62abe4/triton-3.6.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49df5ef37379c0c2b5c0012286f80174fcf0e073e5ade1ca9a86c36814553651", size = 176081190, upload-time = "2026-01-20T16:16:00.523Z" }, + { url = "https://files.pythonhosted.org/packages/e0/12/b05ba554d2c623bffa59922b94b0775673de251f468a9609bc9e45de95e9/triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3", size = 188214640, upload-time = "2026-01-20T16:00:35.869Z" }, + { url = "https://files.pythonhosted.org/packages/17/5d/08201db32823bdf77a0e2b9039540080b2e5c23a20706ddba942924ebcd6/triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4", size = 176128243, upload-time = "2026-01-20T16:16:07.857Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/3c/12/34d71b350e89a204c2c7777a9bba0dcf2f19a5bfdd70b57c4dbc5ffd7154/triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd", size = 176133521, upload-time = "2026-01-20T16:16:13.321Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4e/41b0c8033b503fd3cfcd12392cdd256945026a91ff02452bef40ec34bee7/triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6", size = 176276087, upload-time = "2026-01-20T16:16:18.989Z" }, + { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, + { url = "https://files.pythonhosted.org/packages/49/55/5ecf0dcaa0f2fbbd4420f7ef227ee3cb172e91e5fede9d0ecaddc43363b4/triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43", size = 176138577, upload-time = "2026-01-20T16:16:25.426Z" }, + { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, + { url = "https://files.pythonhosted.org/packages/48/db/56ee649cab5eaff4757541325aca81f52d02d4a7cd3506776cad2451e060/triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d", size = 176274804, upload-time = "2026-01-20T16:16:31.528Z" }, + { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, +] + +[[package]] +name = "trl" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accelerate" }, + { name = "datasets" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/d6/2d3d876917d43537afea7b502abb318ae071295e4accac222741b548399e/trl-1.2.0.tar.gz", hash = "sha256:f5038b21295d2559992a087ea8d9ca10f74cde23e6760861def209811ab45d00", size = 583112, upload-time = "2026-04-17T01:04:17.706Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/82/bad1a22ff4b21d8080f7a64d8313c1ac0e791b455b98f2efca1ef3e14b8f/trl-1.2.0-py3-none-any.whl", hash = "sha256:f6ddfa162ac92d25973070d9e3f6cff71b32c52edc34539e4294722f9dc0a6d6", size = 697449, upload-time = "2026-04-17T01:04:16.007Z" }, +] + +[[package]] +name = "typer" +version = "0.24.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/b8/9ebb531b6c2d377af08ac6746a5df3425b21853a5d2260876919b58a2a4a/typer-0.24.2.tar.gz", hash = "sha256:ec070dcfca1408e85ee203c6365001e818c3b7fffe686fd07ff2d68095ca0480", size = 119849, upload-time = "2026-04-22T17:45:34.413Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/d1/9484b497e0a0410b901c12b8251c3e746e1e863f7d28419ffe06f7892fda/typer-0.24.2-py3-none-any.whl", hash = "sha256:b618bc3d721f9a8d30f3e05565be26416d06e9bcc29d49bc491dc26aba674fa8", size = 55977, upload-time = "2026-04-22T17:45:33.055Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "uvicorn" +version = "0.42.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e3/ad/4a96c425be6fb67e0621e62d86c402b4a17ab2be7f7c055d9bd2f638b9e2/uvicorn-0.42.0.tar.gz", hash = "sha256:9b1f190ce15a2dd22e7758651d9b6d12df09a13d51ba5bf4fc33c383a48e1775", size = 85393, upload-time = "2026-03-16T06:19:50.077Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/89/f8827ccff89c1586027a105e5630ff6139a64da2515e24dafe860bd9ae4d/uvicorn-0.42.0-py3-none-any.whl", hash = "sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359", size = 68830, upload-time = "2026-03-16T06:19:48.325Z" }, +] + +[[package]] +name = "wandb" +version = "0.26.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "gitpython" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "sentry-sdk" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/a4/72a6640e1f566e81f184a426e3e45298d4c6672664de41adb7eb6f64370a/wandb-0.26.1.tar.gz", hash = "sha256:eef2dbaea06f0b1c0cdc5d76f544ae4c2b8848fc512442a00bd59f0502fc8aa1", size = 42159814, upload-time = "2026-04-23T16:27:34.033Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/09/3296235f3906e904f06f2df29eed4d672fb23c0932c9486e2af64f2f2a66/wandb-0.26.1-py3-none-macosx_12_0_arm64.whl", hash = "sha256:2955fe190c005fb83ee6d73f066c8a33f09f3212a1f2eb53faa6581440e456be", size = 24857204, upload-time = "2026-04-23T16:26:58.576Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ad/e39ca3086534129e42208ba00ed2c6247ce425f890219eeec33b4f162864/wandb-0.26.1-py3-none-macosx_12_0_x86_64.whl", hash = "sha256:55d91cabde98162d7116a5e19ddd052bd9848556243f1da4cbb9ffb7ad435bfc", size = 26014649, upload-time = "2026-04-23T16:27:02.559Z" }, + { url = "https://files.pythonhosted.org/packages/56/af/400d84a3bdce0b062b4baa70acb6becd2c8018697f4fbf5af9a9e1e406e5/wandb-0.26.1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:7c78bc2454cfe1ffa1c3a256060a387356eed8a4488e024d9d2eba8f2b5bd51d", size = 25421317, upload-time = "2026-04-23T16:27:06.411Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e9/b4bf8f3509dcea1cec52233a38991459654635b5a8e6a494eb912e1b9cfb/wandb-0.26.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a2c8eeec8706dcd2872e69c3b4d20ec523082fdb4440295491556e219ad2aa67", size = 27192831, upload-time = "2026-04-23T16:27:10.308Z" }, + { url = "https://files.pythonhosted.org/packages/62/cf/4a6dce0c782223ef0eeea7139daee73418a7322befcf083512c31cebaa18/wandb-0.26.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2fa768ee0636a569afb7541cf996e56309c47070566a38916823f94e02afe586", size = 25593326, upload-time = "2026-04-23T16:27:14.259Z" }, + { url = "https://files.pythonhosted.org/packages/df/99/58c3d8c36ae8e2b7d70bf6493eb5daa1cca0231a04b025717b4cd1a78f1e/wandb-0.26.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5854928725cfeff1f284d5c043cd353f810e5da02eead2c120ef5056ad026fea", size = 27535542, upload-time = "2026-04-23T16:27:18.473Z" }, + { url = "https://files.pythonhosted.org/packages/7c/d0/4e846ffc1d0cc435518dfa581ce73ac82cfd0ebbf35f3853c9277f632e5f/wandb-0.26.1-py3-none-win32.whl", hash = "sha256:5c2bd44e575ae9944e2764d1aaa031461178276bf2636d5558399c2816ef5cfe", size = 24968151, upload-time = "2026-04-23T16:27:22.086Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9b/487413eaccefdb58799a226726e24b486e9192d2671c75a4550c160aba23/wandb-0.26.1-py3-none-win_amd64.whl", hash = "sha256:5817785467d3f1676f1812ec19a89f77f6e56dfe67d9f47080075af95f705d3e", size = 24968155, upload-time = "2026-04-23T16:27:25.731Z" }, + { url = "https://files.pythonhosted.org/packages/04/dc/5baf3e99b3eeb709d6f75124b5bec8cb73d4b38d2b10df7fdcfde4966200/wandb-0.26.1-py3-none-win_arm64.whl", hash = "sha256:f848b7744f896bc04cabbb28360a2814d1551a91fa2c456243e06435729c8a2e", size = 22912416, upload-time = "2026-04-23T16:27:29.456Z" }, +] + +[[package]] +name = "xxhash" +version = "3.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/2f/e183a1b407002f5af81822bee18b61cdb94b8670208ef34734d8d2b8ebe9/xxhash-3.7.0.tar.gz", hash = "sha256:6cc4eefbb542a5d6ffd6d70ea9c502957c925e800f998c5630ecc809d6702bae", size = 82022, upload-time = "2026-04-25T11:10:32.553Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/49/e4b575b4ed170a7f640c8bd69cfadfa81c7b700191fde5e72228762b9f73/xxhash-3.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd8ab85c916a58d5c8656ea15e3ce9df836fe2f120a74c296e01d69fab2614b4", size = 33426, upload-time = "2026-04-25T11:05:15.702Z" }, + { url = "https://files.pythonhosted.org/packages/07/61/40f0155b0b09988eb6cdbfc52652f2f371810b0c58163208cb05667757bd/xxhash-3.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:85f5c0e26d945b5bb475e0a3d95193117498130baa7619357bdc7869c2391b5a", size = 30859, upload-time = "2026-04-25T11:05:17.708Z" }, + { url = "https://files.pythonhosted.org/packages/12/bd/2902b7aad574e43cd85fd84849cfbce48c52cb02c7d6902b8a2b3f6e668e/xxhash-3.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b7ffeaada9f8699be63d639536b0b60dff73b7d3325b7475c5bc8fdbf4eed47f", size = 193839, upload-time = "2026-04-25T11:05:19.364Z" }, + { url = "https://files.pythonhosted.org/packages/48/df/343ce8fd09e47ba8fba43b3bad3283ddf0deca799d5a27b084c3aa2ce502/xxhash-3.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cee88dfaa6b1b2bfadd3c031fa5f05584870e62fb05dc500942e9900c44fcfda", size = 212896, upload-time = "2026-04-25T11:05:21.131Z" }, + { url = "https://files.pythonhosted.org/packages/79/cf/703e8422a8b52407864281fb4eb52c605e9f33180413b4458f05de110eba/xxhash-3.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7426ff0dfa76eb47efc2cc59d4a717bfa9dc9938bff5e49e748bca749f6aa616", size = 235896, upload-time = "2026-04-25T11:05:22.988Z" }, + { url = "https://files.pythonhosted.org/packages/ed/bc/d4b039edbd426575add5f217abeeb2bf870e2c510d35445df81b4f457901/xxhash-3.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8ff6ec73110f610425caef3ea875afbfc34caa542f01df3a80f45aadeb9f906", size = 211665, upload-time = "2026-04-25T11:05:24.799Z" }, + { url = "https://files.pythonhosted.org/packages/42/24/c6f81361796814b92399a88bf079d3b65e617f531819128fcf1bd6ef0571/xxhash-3.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0d23fd49fdc5c8af61fb7104f1ad247954499140f6cb6045b3aa5c99dadbbf28", size = 444929, upload-time = "2026-04-25T11:05:26.245Z" }, + { url = "https://files.pythonhosted.org/packages/a4/db/268012153eb7f6bf2c8a0491fdcde11e093f166990821a2ab754fe95537d/xxhash-3.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12c249621af6d50a05d9f10af894b404157b15819878e18f75fcbb0213a77d07", size = 193271, upload-time = "2026-04-25T11:05:28.282Z" }, + { url = "https://files.pythonhosted.org/packages/0a/86/1d0d905d659850dad7f59c807c130249fdb204dc6f71f1fb36268f3f3e61/xxhash-3.7.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6741564a923f082f3c2941c8bb920462ed5b25eaebdd1e161f162233c9a10bc5", size = 284580, upload-time = "2026-04-25T11:05:30.116Z" }, + { url = "https://files.pythonhosted.org/packages/1f/52/fc01ca7ff425a9bdb38d9e3a17f2630447ce3b45d45a929a6cd94d469334/xxhash-3.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4fd8acc6e32596350619896feb372033c0920975992d29837c32853bb1feacd", size = 210193, upload-time = "2026-04-25T11:05:31.969Z" }, + { url = "https://files.pythonhosted.org/packages/ec/96/122e0c6a3537a54b30752031dca557182576bae1a4171c0be8c532c84496/xxhash-3.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:646a69b56d8145d85f7fd2289d14fba07880c8a5bda406aa256b407481a61f35", size = 241094, upload-time = "2026-04-25T11:05:33.651Z" }, + { url = "https://files.pythonhosted.org/packages/d8/17/92e33338db8c18add33a46b56c2b7d5dcc6cc2ac076c45389f6017b1bf37/xxhash-3.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:11dd69b1a34b7b9af29012f390825b0cdb0617c0966560e227ca74daa7478ba9", size = 197721, upload-time = "2026-04-25T11:05:35.387Z" }, + { url = "https://files.pythonhosted.org/packages/c7/04/fd4114a0820913f336bef5c82ef851bde8d06270982ebd7b2a859961bbf2/xxhash-3.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:01cf5c5333aed26cc8d5eea33b8d6398e085e365a704b7372fabdf7ab06441a9", size = 210073, upload-time = "2026-04-25T11:05:37.405Z" }, + { url = "https://files.pythonhosted.org/packages/dd/eb/a2472b8b81cd576a9af3a4889ad8ba5784e8c5a04592587056cdaededd6c/xxhash-3.7.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:f1e65d52c2d526734abecb98372c256b7eacce8fdc42e0df8570417fb39e2772", size = 274960, upload-time = "2026-04-25T11:05:39.224Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d3/493afc544aae50b5fb2844ceaeb3697283bb59695db1a7cb40448636de05/xxhash-3.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8ff00fcc3eb436617ed8556cf15daf76c2b501248361a065625a588af78a0a02", size = 413113, upload-time = "2026-04-25T11:05:40.669Z" }, + { url = "https://files.pythonhosted.org/packages/50/6a/002800845a22bff32bcf5fd09caceb4d3f5c3da6b754c46edb9743ce908b/xxhash-3.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b5cd29840505631c6f7dbb8a5d34b742b5e6bbda38fe0b9f54e825f3ea6b61dc", size = 190677, upload-time = "2026-04-25T11:05:42.403Z" }, + { url = "https://files.pythonhosted.org/packages/f4/0f/86ee514622a381c0dc49167c8d431a22aa93518a4063559c3e36e4b82bc8/xxhash-3.7.0-cp310-cp310-win32.whl", hash = "sha256:5bf2f1940499839b39fef1561b5ecb6ede9ac34ef4457474e1337fc7ef07c2f3", size = 30627, upload-time = "2026-04-25T11:05:44.022Z" }, + { url = "https://files.pythonhosted.org/packages/86/45/2ef2310803efb4a2d07844e8098d797e25702024793aa2e85858623a43b5/xxhash-3.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:d41fcda2fa8ca682ebca134a2f2dc02575ba549267585597e73061565795f475", size = 31463, upload-time = "2026-04-25T11:05:45.218Z" }, + { url = "https://files.pythonhosted.org/packages/9e/75/40dbf8f142baf8993c38cd988c8d8f51fe0c51e6c84c5769a3c0280a651d/xxhash-3.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:a845a59664d5c531525a467470220f8edc37959e0a6f8e734ffb6654da5c4bee", size = 27747, upload-time = "2026-04-25T11:05:46.422Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f4/7bd35089ff1f8e2c96baa2dce05775a122aacd2e3830a73165e27a4d0848/xxhash-3.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fdc7d06929ae28dda98297a18eef7b0fd38991a3b405d8d7b55c9ef24c296958", size = 33423, upload-time = "2026-04-25T11:05:47.628Z" }, + { url = "https://files.pythonhosted.org/packages/a3/26/4e00c88a6a2c8a759cfb77d2a9a405f901e8aa66e60ef1fd0aeb35edda48/xxhash-3.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea6daa712f4e094a30830cf01e9b47d03b24d05cc9dab8609f0d9a9db8454712", size = 30857, upload-time = "2026-04-25T11:05:49.189Z" }, + { url = "https://files.pythonhosted.org/packages/82/2f/eeb942c17a5a761a8f01cb9180a0b76bfb62a2c39e6f46b1f9001899027a/xxhash-3.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9e6c0d843f1daf85ea23aeb053579135552bde575b7b98af20bfc667b6e4548d", size = 194702, upload-time = "2026-04-25T11:05:50.457Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/96f132c08b1e5951c68691d3b9ec351ec2edc028f6a01fcd294f46b9d9f0/xxhash-3.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:363c139bf15e1ac5f136b981d3c077eb551299b1effede7f12faa010b8590a60", size = 213613, upload-time = "2026-04-25T11:05:52.571Z" }, + { url = "https://files.pythonhosted.org/packages/82/89/d4e92b796c5ed052d29ed324dbfc1dc1188e0c4bf64bebbf0f8fc20698df/xxhash-3.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a778b25874cb0f862eaab5986bff4ca49ffb0def7c0a34c237b948b3c6c775b2", size = 236726, upload-time = "2026-04-25T11:05:54.395Z" }, + { url = "https://files.pythonhosted.org/packages/40/f1/81fc4361921dc6e557a9c60cb3712f36d244d06eeeb71cd2f4252ac42678/xxhash-3.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e1860f1e43d40e9d904cf22d93e587ea42e010ebce4160877e46bcab4bc232a", size = 212443, upload-time = "2026-04-25T11:05:56.334Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d0/afeddd4cff50a332f50d4b8a2e8857673153ab0564ef472fcdeb0b5430df/xxhash-3.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9122ad6f867c4a0f5e655f5c3bdf89103852009dbb442a3d23e688b9e699e800", size = 445793, upload-time = "2026-04-25T11:05:58.953Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d0/3c91e4e6a05ca4d7df8e39ec3a75b713609258ec84705ab34be6430826a1/xxhash-3.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7d9110d0c3fb02679972837a033251fd186c529aa62f19c132fc909c74052b8", size = 193937, upload-time = "2026-04-25T11:06:00.546Z" }, + { url = "https://files.pythonhosted.org/packages/4e/3a/a6b0772d9801dd4bea4ca4fd34734d6e9b51a711c8a611a24a79de26a878/xxhash-3.7.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:347a93f2b4ce67ce61959665e32a7447c380f8347e55e100daa23766baacf0e5", size = 285188, upload-time = "2026-04-25T11:06:01.96Z" }, + { url = "https://files.pythonhosted.org/packages/6c/f8/cf8e31fd7282230fe7367cd501a2e75b4b67b222bfc7eacccfc20d2652cb/xxhash-3.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:acbb48679ddf3852c45280c10ff10d52ca2cd1da2e552fb81db1ff786c75d0e4", size = 210966, upload-time = "2026-04-25T11:06:03.453Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f0/fd36cc4a81bf52ee5633275daae2b93dd958aace67fd4f5d466ec83b5f35/xxhash-3.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:fe14c356f8b23ad811dc026077a6d4abccdaa7bce5ca98579605550657b6fcfb", size = 241994, upload-time = "2026-04-25T11:06:05.264Z" }, + { url = "https://files.pythonhosted.org/packages/08/e1/67f5d9c9369be42eaf99ba02c01bf14c5ecd67087b02567960bfcee43b63/xxhash-3.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f420ad3d41e38194353a498bbc9561fd5a9973a27b536ce46d8583479cf44335", size = 198707, upload-time = "2026-04-25T11:06:07.044Z" }, + { url = "https://files.pythonhosted.org/packages/50/17/a4c865ca22d2da6b1bc7d739bf88cab209533cf52ba06ca9da27c3039bee/xxhash-3.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:693d02c6dc7d1aa0a45921d54cd8c1ff629e09dfdc2238471507af1f7a1c6f04", size = 210917, upload-time = "2026-04-25T11:06:08.853Z" }, + { url = "https://files.pythonhosted.org/packages/49/8b/453b35810d697abac3c96bde3528bece685869227da274eb80a4a4d4a119/xxhash-3.7.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:14bf7a54e43825ec131ee7fe3c60e142e7c2c1e676ad0f93fc893432d15414af", size = 275772, upload-time = "2026-04-25T11:06:10.645Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ad/4eed7eab07fd3ee6678f416190f0413d097ab5d7c1278906bf1e9549d789/xxhash-3.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ae3a39a4d96bdb6f8d154fd7f490c4ad06f0532fcd2bb656052a9a7762cf5d31", size = 414068, upload-time = "2026-04-25T11:06:12.511Z" }, + { url = "https://files.pythonhosted.org/packages/d3/4e/fd6f8a680ba248fdb83054fa71a8bfa3891225200de1708b888ef2c49829/xxhash-3.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1cc07c639e3a77ef1d32987464d3e408565b8a3be57b545d3542b191054d9923", size = 191459, upload-time = "2026-04-25T11:06:14.07Z" }, + { url = "https://files.pythonhosted.org/packages/50/7c/8cb34b3bed4f44ca6827a534d50833f9bc6c006e83b0eb410ac9fa0793bd/xxhash-3.7.0-cp311-cp311-win32.whl", hash = "sha256:3281ba1d1e60ee7a382a7b958513ba03c2c0d5fcbd9a6f7517c0a81251a23422", size = 30628, upload-time = "2026-04-25T11:06:15.802Z" }, + { url = "https://files.pythonhosted.org/packages/0b/47/a49767bd7b40782bedae9ff0721bfe1d7e4dd9dc1585dea684e57ba67c20/xxhash-3.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:a7f25baec4c5d851d40718d6fae52285b31683093d4ff5207e63ab306ccf14a5", size = 31461, upload-time = "2026-04-25T11:06:17.104Z" }, + { url = "https://files.pythonhosted.org/packages/7c/c6/3957bfacfb706bd687be246dfa8dd60f8df97c44186d229f7fd6e26c4b7e/xxhash-3.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:4c2454448ce847c72635827bb75c15c5a3434b03ee1afd28cb6dc6fb2597d830", size = 27746, upload-time = "2026-04-25T11:06:18.716Z" }, + { url = "https://files.pythonhosted.org/packages/f2/8a/51a14cdef4728c6c2337db8a7d8704422cc65676d9199d77215464c880af/xxhash-3.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:082c87bfdd2b9f457606c7a4a53457f4c4b48b0cdc48de0277f4349d79bb3d7a", size = 33357, upload-time = "2026-04-25T11:06:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/0c2c933809421ffd9bf42b59315552c143c755db5d9a816b2f1ae273e884/xxhash-3.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5e7ce913b61f35b0c1c839a49ac9c8e75dd8d860150688aed353b0ce1bf409d8", size = 30869, upload-time = "2026-04-25T11:06:21.989Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/89d5fdd6ee12d70ba99451de46dd0e8010167468dcd913ec855653f4dd50/xxhash-3.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3beb1de3b1e9694fcdd853e570ee64c631c7062435d2f8c69c1adf809bc086f0", size = 194100, upload-time = "2026-04-25T11:06:23.586Z" }, + { url = "https://files.pythonhosted.org/packages/87/ee/2f9f2ed993e77206d1e66991290a1ebe22e843351ca3ebec8e49e01ba186/xxhash-3.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3e7b689c3bce16699efcf736066f5c6cc4472c3840fe4b22bd8279daf4abdac", size = 212977, upload-time = "2026-04-25T11:06:25.019Z" }, + { url = "https://files.pythonhosted.org/packages/de/60/5a91644615a9e9d4e42c2e9925f1908e3a24e4e691d9de7340d565bea024/xxhash-3.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a6545e6b409e3d5cbafc850fb84c55a1ca26ed15a6b11e3bf07a0e0cd84517c8", size = 236373, upload-time = "2026-04-25T11:06:26.482Z" }, + { url = "https://files.pythonhosted.org/packages/22/c0/f3a9384eaaed9d14d4d062a5d953aa0da489bfe9747877aa994caa87cd0b/xxhash-3.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:31ab1461c77a11461d703c88eb949e132a1c6515933cf675d97ec680f4bd18de", size = 212229, upload-time = "2026-04-25T11:06:28.065Z" }, + { url = "https://files.pythonhosted.org/packages/2e/67/02f07a9fd79726804190f2172c4894c3ed9a4ebccaca05653c84beb58025/xxhash-3.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c4d596b7676f811172687ec567cbafb9e4dea2f9be1bbb4f622410cb7f40f40", size = 445462, upload-time = "2026-04-25T11:06:30.048Z" }, + { url = "https://files.pythonhosted.org/packages/40/37/558f5a90c0672fc9b4402dc25d87ac5b7406616e8969430c9ca4e52ee74d/xxhash-3.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13805f0461cba0a857924e70ff91ae6d52d2598f79a884e788db80532614a4a1", size = 193932, upload-time = "2026-04-25T11:06:31.857Z" }, + { url = "https://files.pythonhosted.org/packages/d5/90/aaa09cd58661d32044dbbad7df55bbe22a623032b810e7ed3b8c569a2a6f/xxhash-3.7.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d398f372496152f1c6933a33566373f8d1b37b98b8c9d608fa6edc0976f23b2", size = 284807, upload-time = "2026-04-25T11:06:33.697Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f3/53df3719ab127a02c174f0c1c74924fcd110866e89c966bc7909cfa8fa84/xxhash-3.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d610aa62cdb7d4d497740741772a24a794903bf3e79eaa51d2e800082abe11e5", size = 210445, upload-time = "2026-04-25T11:06:35.488Z" }, + { url = "https://files.pythonhosted.org/packages/72/33/d219975c0e8b6fa2eb9ccd486fe47e21bf1847985b878dd2fbc3126e0d5c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:073c23900a9fbf3d26616c17c830db28af9803677cd5b33aea3224d824111514", size = 241273, upload-time = "2026-04-25T11:06:37.24Z" }, + { url = "https://files.pythonhosted.org/packages/3e/50/49b1afe610eb3964cedcb90a4d4c3d46a261ee8669cbd4f060652619ae3c/xxhash-3.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:418a463c3e6a590c0cdc890f8be19adb44a8c8acd175ca5b2a6de77e61d0b386", size = 197950, upload-time = "2026-04-25T11:06:39.148Z" }, + { url = "https://files.pythonhosted.org/packages/c6/75/5f42a1a4c78717d906a4b6a140c6dbf837ab1f547a54d23c4e2903310936/xxhash-3.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:03f8ff4474ee61c845758ce00711d7087a770d77efb36f7e74a6e867301000b8", size = 210709, upload-time = "2026-04-25T11:06:40.958Z" }, + { url = "https://files.pythonhosted.org/packages/8a/85/237e446c25abced71e9c53d269f2cef5bab8a82b3f88a12e00c5368e7368/xxhash-3.7.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:44fba4a5f1d179b7ddc7b3dc40f56f9209046421679b57025d4d8821b376fd8d", size = 275345, upload-time = "2026-04-25T11:06:42.525Z" }, + { url = "https://files.pythonhosted.org/packages/62/34/c2c26c0a6a9cc739bc2a5f0ae03ba8b87deb12b8bce35f7ac495e790dc6d/xxhash-3.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31e3516a0f829d06ded4a2c0f3c7c5561993256bfa1c493975fb9dc7bfa828a1", size = 414056, upload-time = "2026-04-25T11:06:44.343Z" }, + { url = "https://files.pythonhosted.org/packages/a0/aa/5c58e9bc8071b8afd8dcf297ff362f723c4892168faba149f19904132bf4/xxhash-3.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b59ee2ac81de57771a09ecad09191e840a1d2fae1ef684208320591055768f83", size = 191485, upload-time = "2026-04-25T11:06:46.262Z" }, + { url = "https://files.pythonhosted.org/packages/d4/69/a929cf9d1e2e65a48b818cdce72cb6b69eab2e6877f21436d0a1942aff43/xxhash-3.7.0-cp312-cp312-win32.whl", hash = "sha256:74bbd92f8c7fcc397ba0a11bfdc106bc72ad7f11e3a60277753f87e7532b4d81", size = 30671, upload-time = "2026-04-25T11:06:48.039Z" }, + { url = "https://files.pythonhosted.org/packages/b9/1b/104b41a8947f4e1d4a66ce1e628eea752f37d1890bfd7453559ca7a3d950/xxhash-3.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:7bd7bc82dd4f185f28f35193c2e968ef46131628e3cac62f639dadf321cba4d1", size = 31514, upload-time = "2026-04-25T11:06:49.279Z" }, + { url = "https://files.pythonhosted.org/packages/98/a0/1fd0ea1f1b886d9e7c73f0397571e22333a7d79e31da6d7127c2a4a71d75/xxhash-3.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:7d7148180ec99ba36585b42c8c5de25e9b40191613bc4be68909b4d25a77a852", size = 27761, upload-time = "2026-04-25T11:06:50.448Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/d5174b4c36d10f64d4ca7050563138c5a599efb01a765858ddefc9c1202a/xxhash-3.7.0-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:4b6d6b33f141158692bd4eafbb96edbc5aa0dabdb593a962db01a91983d4f8fa", size = 36813, upload-time = "2026-04-25T11:06:51.73Z" }, + { url = "https://files.pythonhosted.org/packages/41/d0/abc6c9d347ba1f1e1e1d98125d0881a0452c7f9a76a9dd03a7b5d2197f23/xxhash-3.7.0-cp313-cp313-android_21_x86_64.whl", hash = "sha256:845d347df254d6c619f616afa921331bada8614b8d373d58725c663ba97c3605", size = 35121, upload-time = "2026-04-25T11:06:53.048Z" }, + { url = "https://files.pythonhosted.org/packages/bf/11/4cc834eb3d79f2f2b3a6ef7324195208bcdfbdcf7534d2b17267aa5f3a8f/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:fddbbb69a6fff4f421e7a0d1fa28f894b20112e9e3fab306af451e2dfd0e459b", size = 29624, upload-time = "2026-04-25T11:06:54.311Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/e97d3e7b635fe73a1dfb1e91f805324dd6d930bb42041cbf18f183bc0b6d/xxhash-3.7.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:54876a4e45101cec2bf8f31a973cda073a23e2e108538dad224ba07f85f22487", size = 30638, upload-time = "2026-04-25T11:06:55.864Z" }, + { url = "https://files.pythonhosted.org/packages/f4/40/d84951d80c35db1f4c40a29a64a8520eea5d56e764c603906b4fe763580f/xxhash-3.7.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:0c72fe9c7e3d6dfd7f1e21e224a877917fa09c465694ba4e06464b9511b65544", size = 33323, upload-time = "2026-04-25T11:06:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/c7dc6558d97e9ab023f663d69ab28b340ed9bf4d2d94f2c259cf896bb354/xxhash-3.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a6d73a830b17ef49bc04e00182bd839164c1b3c59c127cd7c54fcb10c7ed8ee8", size = 33362, upload-time = "2026-04-25T11:06:58.656Z" }, + { url = "https://files.pythonhosted.org/packages/2a/6e/46b84017b1301d54091430353d4ad5901654a3e0871649877a416f7f1644/xxhash-3.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:91c3b07cf3362086d8f126c6aecd8e5e9396ad8b2f2219ea7e49a8250c318acd", size = 30874, upload-time = "2026-04-25T11:06:59.834Z" }, + { url = "https://files.pythonhosted.org/packages/df/5e/8f9158e3ab906ad3fec51e09b5ea0093e769f12207bfa42a368ca204e7ab/xxhash-3.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:50e879ebbac351c81565ca108db766d7832f5b8b6a5b14b8c0151f7190028e3d", size = 194185, upload-time = "2026-04-25T11:07:01.658Z" }, + { url = "https://files.pythonhosted.org/packages/f3/29/a804ded9f5d3d3758292678d23e7528b08fda7b7e750688d08b052322475/xxhash-3.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:921c14e93817842dd0dd9f372890a0f0c72e534650b6ab13c5be5cd0db11d47e", size = 213033, upload-time = "2026-04-25T11:07:03.606Z" }, + { url = "https://files.pythonhosted.org/packages/8b/91/1ce5a7d2fdc975267320e2c78fc1cecfe7ab735ccbcf6993ec5dd541cb2c/xxhash-3.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e64a7c9d7dfca3e0fafcbc5e455519090706a3e36e95d655cec3e04e79f95aaa", size = 236140, upload-time = "2026-04-25T11:07:05.396Z" }, + { url = "https://files.pythonhosted.org/packages/34/04/fd595a4fd8617b05fa27bd9b684ecb4985bfed27917848eea85d54036d06/xxhash-3.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2220af08163baf5fa36c2b8af079dc2cbe6e66ae061385267f9472362dfd53c6", size = 212291, upload-time = "2026-04-25T11:07:06.966Z" }, + { url = "https://files.pythonhosted.org/packages/03/fb/f1a379cbc372ae5b9f4ab36154c48a849ca6ebe3ac477067a57865bf3bc6/xxhash-3.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f14bb8b22a4a91325813e3d553b8963c10cf8c756cff65ee50c194431296c655", size = 445532, upload-time = "2026-04-25T11:07:08.525Z" }, + { url = "https://files.pythonhosted.org/packages/65/59/172424b79f8cfd4b6d8a122b2193e6b8ad4b11f7159bb3b6f9b3191329bb/xxhash-3.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:496736f86a9bedaf64b0dc70e3539d0766df01c71ea22032698e88f3f04a1ce9", size = 193990, upload-time = "2026-04-25T11:07:10.315Z" }, + { url = "https://files.pythonhosted.org/packages/b9/19/aeac22161d953f139f07ba5586cb4a17c5b7b6dff985122803bb12933500/xxhash-3.7.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0ff71596bd79816975b3de7130ab1ff4541410285a3c084584eeb1c8239996fd", size = 284876, upload-time = "2026-04-25T11:07:12.15Z" }, + { url = "https://files.pythonhosted.org/packages/77/d5/4fd0b59e7a02242953da05ff679fbb961b0a4368eac97a217e11dae110c1/xxhash-3.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ad86695c19b1d46fe106925db3c7a37f16be37669dcf58dcc70a9dd6e324676", size = 210495, upload-time = "2026-04-25T11:07:13.952Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fb/976a3165c728c7faf74aa1b5ab3cf6a85e6d731612894741840524c7d28c/xxhash-3.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:970f9f8c50961d639cbd0d988c96f80ddf66006de93641719282c4fe7a87c5e6", size = 241331, upload-time = "2026-04-25T11:07:15.557Z" }, + { url = "https://files.pythonhosted.org/packages/4a/2c/6763d5901d53ac9e6ba296e5717ae599025c9d268396e8faa8b4b0a8e0ac/xxhash-3.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5886ad85e9e347911783760a1d16cb6b393e8f9e3b52c982568226cb56927bdc", size = 198037, upload-time = "2026-04-25T11:07:17.563Z" }, + { url = "https://files.pythonhosted.org/packages/61/2b/876e722d533833f5f9a83473e6ba993e48745701096944e77bbecf29b2c3/xxhash-3.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6e934bbae1e0ec74e27d5f0d7f37ef547ce5ff9f0a7e63fb39e559fc99526734", size = 210744, upload-time = "2026-04-25T11:07:19.055Z" }, + { url = "https://files.pythonhosted.org/packages/21/e6/d7e7baef7ce24166b4668d3c48557bb35a23b92ecadcac7e7718d099ab69/xxhash-3.7.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3b6b3d28228af044ebcded71c4a3dd86e1dbd7e2f4645bf40f7b5da65bb5fb5a", size = 275406, upload-time = "2026-04-25T11:07:20.908Z" }, + { url = "https://files.pythonhosted.org/packages/92/fe/198b3763b2e01ca908f2154969a2352ec99bda892b574a11a9a151c5ede4/xxhash-3.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:6be4d70d9ab76c9f324ead9c01af6ff52c324745ea0c3731682a0cf99720f1fe", size = 414125, upload-time = "2026-04-25T11:07:23.037Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6d/019a11affd5a5499137cacca53808659964785439855b5aa40dfd3412916/xxhash-3.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:151d7520838d4465461a0b7f4ae488b3b00de16183dd3214c1a6b14bf89d7fb6", size = 191555, upload-time = "2026-04-25T11:07:24.991Z" }, + { url = "https://files.pythonhosted.org/packages/76/21/b96d58568df2d01533244c3e0e5cbdd0c8b2b25c4bec4d72f19259a292d7/xxhash-3.7.0-cp313-cp313-win32.whl", hash = "sha256:d798c1e291bffb8e37b5bbe0dda77fc767cd19e89cadaf66e6ed5d0ff88c9fe6", size = 30668, upload-time = "2026-04-25T11:07:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/99/57/d849a8d3afa1f8f4bc6a831cd89f49f9706fbbad94d2975d6140a171988c/xxhash-3.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:875811ba23c543b1a1c3143c926e43996eb27ebb8f52d3500744aa608c275aed", size = 31524, upload-time = "2026-04-25T11:07:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/81/52/bacc753e92dee78b058af8dcef0a50815f5f860986c664a92d75f965b6a5/xxhash-3.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:54a675cb300dda83d71daae2a599389d22db8021a0f8db0dd659e14626eb3ecc", size = 27768, upload-time = "2026-04-25T11:07:29.113Z" }, + { url = "https://files.pythonhosted.org/packages/1c/47/ddbd683b7fc7e592c1a8d9d65f73ce9ab513f082b3967eee2baf549b8fc6/xxhash-3.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a3b19a42111c4057c1547a4a1396a53961dca576a0f6b82bfa88a2d1561764b2", size = 33576, upload-time = "2026-04-25T11:07:30.469Z" }, + { url = "https://files.pythonhosted.org/packages/07/f2/36d3310161db7f72efb4562aadde0ed429f1d0531782dd6345b12d2da527/xxhash-3.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8f4608a06e4d61b7a3425665a46d00e0579122e1a2fae97a0c52953a3aad9aa3", size = 31123, upload-time = "2026-04-25T11:07:31.989Z" }, + { url = "https://files.pythonhosted.org/packages/0d/3f/75937a5c69556ed213021e43cbedd84c8e0279d0d74e7d41a255d84ba4b1/xxhash-3.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ad37c7792479e49cf96c1ab25517d7003fe0d93687a772ba19a097d235bbe41e", size = 196491, upload-time = "2026-04-25T11:07:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/f10d7ff8c7a733d4403a43b9de18c8fabc005f98cec054644f04418659ee/xxhash-3.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc026e3b89d98e30a8288c95cb696e77d150b3f0fb7a51f73dcd49ee6b5577fa", size = 215793, upload-time = "2026-04-25T11:07:34.919Z" }, + { url = "https://files.pythonhosted.org/packages/8b/fd/778f60aa295f58907938f030a8b514611f391405614a525cccd2ffc00eb5/xxhash-3.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c9b31ab1f28b078a6a1ac1a54eb35e7d5390deddd56870d0be3a0a733d1c321c", size = 237993, upload-time = "2026-04-25T11:07:36.638Z" }, + { url = "https://files.pythonhosted.org/packages/70/f5/736db5de387b4a540e37a05b84b40dc58a1ce974bfd2b4e5754ce29b68c3/xxhash-3.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3bb5fd680c038fd5229e44e9c493782f90df9bef632fd0499d442374688ff70b", size = 214887, upload-time = "2026-04-25T11:07:38.564Z" }, + { url = "https://files.pythonhosted.org/packages/4d/aa/09a095f22fdb9a27fbb716841fbff52119721f9ca4261952d07a912f7839/xxhash-3.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:030c0fd688fce3569fbb49a2feefd4110cbb0b650186fb4610759ecfac677548", size = 448407, upload-time = "2026-04-25T11:07:40.552Z" }, + { url = "https://files.pythonhosted.org/packages/74/8a/b745efeeca9e34a91c26fdc97ad8514c43d5a81ac78565cba80a1353870a/xxhash-3.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b1bde10324f4c31812ae0d0502e92d916ae8917cad7209353f122b8b8f610c3", size = 196119, upload-time = "2026-04-25T11:07:42.101Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5c/0cfceb024af90c191f665c7933b1f318ee234f4797858383bebd1881d52f/xxhash-3.7.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:503722d52a615f2604f5e7611de7d43878df010dc0053094ef91cb9a9ac3d987", size = 286751, upload-time = "2026-04-25T11:07:43.568Z" }, + { url = "https://files.pythonhosted.org/packages/0b/0a/0793e405dc3cf8f4ebe2c1acec1e4e4608cd9e7e50ea691dabbc2a95ccbb/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c72500a3b6d6c30ebfc135035bcace9eb5884f2dc220804efcaaba43e9f611dd", size = 212961, upload-time = "2026-04-25T11:07:45.388Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7e/721118ffc63bfff94aa565bcf2555a820f9f4bdb0f001e0d609bdfad70de/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:43475925a766d01ca8cd9a857fd87f3d50406983c8506a4c07c4df12adcc867f", size = 243703, upload-time = "2026-04-25T11:07:47.053Z" }, + { url = "https://files.pythonhosted.org/packages/6e/18/16f6267160488b8276fd3d449d425712512add292ba545c1b6946bfdb7dd/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8d09dfd2ab135b985daf868b594315ebe11ad86cd9fea46e6c69f19b28f7d25a", size = 200894, upload-time = "2026-04-25T11:07:48.657Z" }, + { url = "https://files.pythonhosted.org/packages/2d/94/80ba841287fd97e3e9cac1d228788c8ef623746f570404961eec748ecb5c/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c50269d0055ac1faecfd559886d2cbe4b730de236585aba0e873f9d9dadbe585", size = 213357, upload-time = "2026-04-25T11:07:50.257Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/106d4067130c59f1e18a55ffadcd876d8c68534883a1e02685b29d3d8153/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:1910df4756a5ab58cfad8744fc2d0f23926e3efcc346ee76e87b974abab922f4", size = 277600, upload-time = "2026-04-25T11:07:51.745Z" }, + { url = "https://files.pythonhosted.org/packages/c5/86/a081dd30da71d720b2612a792bfd55e45fa9a07ac76a0507f60487473c25/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:d006faf3b491957efcb433489be3c149efe4787b7063d5cddb8ddaefdc60e0c1", size = 416980, upload-time = "2026-04-25T11:07:53.504Z" }, + { url = "https://files.pythonhosted.org/packages/35/29/1a95221a029a3c1293773869e1ab47b07cbbdd82444a42809e8c60156626/xxhash-3.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:abb65b4e947e958f7b3b0d71db3ce447d1bc5f37f5eab871ce7223bda8768a04", size = 193840, upload-time = "2026-04-25T11:07:55.103Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/db909dd0823285de2286f67e10ee4d81e96ad35d7d8e964ecb07fccd8af9/xxhash-3.7.0-cp313-cp313t-win32.whl", hash = "sha256:178959906cb1716a1ce08e0d69c82886c70a15a6f2790fc084fdd146ca30cd49", size = 30966, upload-time = "2026-04-25T11:07:56.524Z" }, + { url = "https://files.pythonhosted.org/packages/7b/ff/d705b15b22f21ee106adce239cb65d35067a158c630b240270f09b17c2e6/xxhash-3.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2524a1e20d4c231d13b50f7cf39e44265b055669a64a7a4b9a2a44faa03f19b6", size = 31784, upload-time = "2026-04-25T11:07:57.758Z" }, + { url = "https://files.pythonhosted.org/packages/a2/1f/b2cf83c3638fd0588e0b17f22e5a9400bdfb1a3e3755324ac0aee2250b88/xxhash-3.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:37d994d0ffe81ef087bb330d392caa809bb5853c77e22ea3f71db024a0543dba", size = 27932, upload-time = "2026-04-25T11:07:59.109Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cc/431db584f6fbb9312e40a173af027644e5580d39df1f73603cbb9dca4d6b/xxhash-3.7.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:8c5fcfd806c335bfa2adf1cd0b3110a44fc7b6995c3a648c27489bae85801465", size = 36644, upload-time = "2026-04-25T11:08:00.658Z" }, + { url = "https://files.pythonhosted.org/packages/bc/01/255ec513e0a705d1f9a61413e78dfce4e3235203f0ed525a24c2b4b56345/xxhash-3.7.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:506a0b488f190f0a06769575e30caf71615c898ed93ab18b0dbcb6dec5c3713c", size = 35003, upload-time = "2026-04-25T11:08:02.338Z" }, + { url = "https://files.pythonhosted.org/packages/68/70/c55fc33c93445b44d8fc5a17b41ed99e3cebe92bcf8396809e63fc9a1165/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:ec68dbba21532c0173a9872298e65c89749f7c9d21538c3a78b5bb6105871568", size = 29655, upload-time = "2026-04-25T11:08:03.701Z" }, + { url = "https://files.pythonhosted.org/packages/c2/72/ff8de73df000d74467d12a59ce6d6e2b2a368b978d41ab7b1fba5ed442be/xxhash-3.7.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:fa77e7ec1450d415d20129961814787c9abd9a07f98872f070b1fe96c5084611", size = 30664, upload-time = "2026-04-25T11:08:05.011Z" }, + { url = "https://files.pythonhosted.org/packages/b6/91/08416d9bd9bc3bf39d831abe8a5631ac2db5141dfd6fe81c3fe59a1f9264/xxhash-3.7.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fe32736295ea38e43e7d9424053c8c47c9f64fecfc7c895fb3da9b30b131c9ee", size = 33317, upload-time = "2026-04-25T11:08:06.413Z" }, + { url = "https://files.pythonhosted.org/packages/0e/3b/86b1caa4dee10a99f4bf9521e623359341c5e50d05158fa10c275b2bd079/xxhash-3.7.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ab9dd2c83c4bbd63e422181a76f13502d049d3ddcac9a1bdc29196263d692bb8", size = 33457, upload-time = "2026-04-25T11:08:08.099Z" }, + { url = "https://files.pythonhosted.org/packages/ed/38/98ea14ad1517e1461292a65906951458d520689782bfbae111050145bdba/xxhash-3.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3afec3a336a2286601a437cb07562ab0227685e6fbb9ec17e8c18457ff348ecf", size = 30894, upload-time = "2026-04-25T11:08:09.429Z" }, + { url = "https://files.pythonhosted.org/packages/61/a2/074654d0b893606541199993c7db70067d9fc63b748e0d60020a52a1bd36/xxhash-3.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:565df64437a9390f84465dcca33e7377114c7ede8d05cd2cf20081f831ea788e", size = 194409, upload-time = "2026-04-25T11:08:10.91Z" }, + { url = "https://files.pythonhosted.org/packages/e2/26/6d2a1afc468189f77ca28c32e1c83e1b9da1178231e05641dbc1b350e332/xxhash-3.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:12eca820a5d558633d423bf8bb78ce72a55394823f64089247f788a7e0ae691e", size = 213135, upload-time = "2026-04-25T11:08:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0e/d8aecf95e09c42547453137be74d2f7b8b14e08f5177fa2fab6144a19061/xxhash-3.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f262b8f7599516567e070abf607b9af649052b2c4bd6f9be02b0cb41b7024805", size = 236379, upload-time = "2026-04-25T11:08:14.206Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/8140e8210536b3dd0cc816c4faaeb5ba6e63e8125ab25af4bcddd6a037b3/xxhash-3.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1598916cb197681e03e601901e4ab96a9a963de398c59d0964f8a6f44a2b361", size = 212447, upload-time = "2026-04-25T11:08:15.79Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/462001d2903b4bee5a5689598a0a55e5e7cd1ac7f4247a5545cff10d3ebb/xxhash-3.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:322b2f0622230f526aeb1738149948a7ae357a9e2ceb1383c6fd1fdaecdafa16", size = 445660, upload-time = "2026-04-25T11:08:17.441Z" }, + { url = "https://files.pythonhosted.org/packages/23/09/2bd1ed7f8689b20e51727952cac8329d50c694dc32b2eba06ba5bc742b37/xxhash-3.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cc22070880cc57b830a65cde4e65fa884c6d9b28ae4803b5ee05911e7bafba", size = 194076, upload-time = "2026-04-25T11:08:19.134Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6e/692302cd0a5f4ac4e6289f37fa888dc2e1e07750b68fe3e4bfe939b8cea3/xxhash-3.7.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb5a888a968b2434abf9ecda357b5d43f10d7b5a6da6fdbbe036208473aff0e2", size = 284990, upload-time = "2026-04-25T11:08:20.618Z" }, + { url = "https://files.pythonhosted.org/packages/05/d9/e54b159b3d9df7999d2a7c676ce7b323d1b5588a64f8f51ed8172567bd87/xxhash-3.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a999771ff97bec27d18341be4f3a36b163bb1ac41ec17bef6d2dabd84acd33c7", size = 210590, upload-time = "2026-04-25T11:08:22.24Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/0e0df1a3a196ced4ca71de76d65ead25d8e87bbfb87b64306ea47a40c00d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ed4a6efe2dee1655adb73e7ad40c6aa955a6892422b1e3b95de6a34de56e3cbb", size = 241442, upload-time = "2026-04-25T11:08:23.844Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a9/d917a7a814e90b218f8a0d37967105eea91bf752c3303683c99a1f7bfc1f/xxhash-3.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9fd17f14ac0faa12126c2f9ca774a8cf342957265ec3c8669c144e5e6cdb478c", size = 198356, upload-time = "2026-04-25T11:08:25.99Z" }, + { url = "https://files.pythonhosted.org/packages/89/5e/f2ba1877c39469abbefc72991d6ebdcbd4c0880db01ae8cb1f553b0c537d/xxhash-3.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:05fd1254268c59b5cb2a029dfc204275e9fc52de2913f1e53aa8d01442c96b4d", size = 210898, upload-time = "2026-04-25T11:08:27.608Z" }, + { url = "https://files.pythonhosted.org/packages/90/c6/be56b58e73de531f39a10de1355bb77ceb663900dc4bf2d6d3002a9c3f9e/xxhash-3.7.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:a2eae53197c6276d5b317f75a1be226bbf440c20b58bf525f36b5d0e1f657ca6", size = 275519, upload-time = "2026-04-25T11:08:29.301Z" }, + { url = "https://files.pythonhosted.org/packages/92/e2/17ddc85d5765b9c709f192009ed8f5a1fc876f4eb35bba7c307b5b1169f9/xxhash-3.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:bfe6f92e3522dcbe8c4281efd74fa7542a336cb00b0e3272c4ec0edabeaeaf67", size = 414191, upload-time = "2026-04-25T11:08:31.16Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/85f5b79f4bf1ec7ba052491164adfd4f4e9519f5dc7246de4fbd64a1bd56/xxhash-3.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7ab9a49c410d8c6c786ab99e79c529938d894c01433130353dd0fe999111077a", size = 191604, upload-time = "2026-04-25T11:08:32.862Z" }, + { url = "https://files.pythonhosted.org/packages/b8/d0/6127b623aa4cca18d8b7743592b048d689fd6c6e37ff26a22cddf6cd9d7f/xxhash-3.7.0-cp314-cp314-win32.whl", hash = "sha256:040ea63668f9185b92bc74942df09c7e65703deed71431333678fc6e739a9955", size = 31271, upload-time = "2026-04-25T11:08:34.651Z" }, + { url = "https://files.pythonhosted.org/packages/64/4f/44fc4788568004c43921701cbc127f48218a1eede2c9aea231115323564d/xxhash-3.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2a61e2a3fb23c892496d587b470dee7fa1b58b248a187719c65ea8e94ec13257", size = 32284, upload-time = "2026-04-25T11:08:35.987Z" }, + { url = "https://files.pythonhosted.org/packages/6d/77/18bb895eb60a49453d16e17d67990e5caff557c78eafc90ad4e2eabf4570/xxhash-3.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:c7741c7524961d8c0cb4d4c21b28957ff731a3fd5b5cd8b856dc80a40e9e5acc", size = 28701, upload-time = "2026-04-25T11:08:37.767Z" }, + { url = "https://files.pythonhosted.org/packages/45/a0/46f72244570c550fbbb7db1ef554183dd5ebe9136385f30e032b781ae8f6/xxhash-3.7.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:fc84bf7aa7592f31ec63a3e7b11d624f468a3f19f5238cec7282a42e838ab1d7", size = 33646, upload-time = "2026-04-25T11:08:39.109Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3a/453846a7eceea11e75def361eed01ec6a0205b9822c19927ed364ccae7cc/xxhash-3.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9f1563fdc8abfc389748e6932c7e4e99c89a53e4ec37d4563c24fc06f5e5644b", size = 31125, upload-time = "2026-04-25T11:08:40.467Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3e/49434aba738885d512f9e486db1bdd19db28dfa40372b56da26ef7a4e738/xxhash-3.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2d415f18becf6f153046ab6adc97da77e3643a0ee205dae61c4012604113a020", size = 196633, upload-time = "2026-04-25T11:08:41.943Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e9/006cb6127baeb9f8abe6d15e62faa01349f09b34e2bfd65175b2422d026b/xxhash-3.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bb16aa13ed175bc9be5c2491ba031b85a9b51c4ed90e0b3d4ebe63cf3fb54f8e", size = 215899, upload-time = "2026-04-25T11:08:43.645Z" }, + { url = "https://files.pythonhosted.org/packages/27/e4/cc57d72e66df0ae29b914335f1c6dcf61e8f3746ddf0ae3c471aa4f15e00/xxhash-3.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f9fd595f1e5941b3d7863e4774e4b30caa6731fc34b9277da032295aa5656ee5", size = 238116, upload-time = "2026-04-25T11:08:45.698Z" }, + { url = "https://files.pythonhosted.org/packages/af/78/3531d4a3fd8a0038cc6be1f265a69c1b3587f557a10b677dd736de2202c1/xxhash-3.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1295325c5a98d552333fa53dc2b026b0ef0ec9c8e73ca3a952990b4c7d65d459", size = 215012, upload-time = "2026-04-25T11:08:47.355Z" }, + { url = "https://files.pythonhosted.org/packages/b4/f6/259fb1eaaec921f59b17203b0daee69829761226d3b980d5191d7723dd83/xxhash-3.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3573a651d146912da9daa9e29e5fbc45994420daaa9ef1e2fa5823e1dc485513", size = 448534, upload-time = "2026-04-25T11:08:49.149Z" }, + { url = "https://files.pythonhosted.org/packages/7b/16/a66d0eaf6a7e68532c07714361ddc904c663ec940f3b028c1ae4a21a7b9d/xxhash-3.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ec1e080a3d02d94ea9335bfab0e3374b877e25411422c18f51a943fa4b46381", size = 196217, upload-time = "2026-04-25T11:08:50.805Z" }, + { url = "https://files.pythonhosted.org/packages/8d/ef/d2efc7fc51756dc52509109d1a25cefc859d74bc4b19a167b12dbd8c2786/xxhash-3.7.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84415265192072d8638a3afc3c1bc5995e310570cd9acb54dc46d3939e364fe0", size = 286906, upload-time = "2026-04-25T11:08:52.418Z" }, + { url = "https://files.pythonhosted.org/packages/fc/67/25decd1d4a4018582ec4db2a868a2b7e40640f4adb20dfeb19ac923aa825/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d4dea659b57443989ef32f4295104fd6912c73d0bf26d1d148bb88a9f159b02", size = 213057, upload-time = "2026-04-25T11:08:54.105Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5d/17651eb29d06786cdc40c60ae3d27d645aa5d61d2eca6237a7ba0b94789b/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:05ece0fe4d9c9c2728912d1981ae1566cfc83a011571b24732cbf76e1fb70dca", size = 243886, upload-time = "2026-04-25T11:08:56.109Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d4/174d9cf7502243d586e6a9ae842b1ae23026620995114f85f1380e588bc9/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fd880353cf1ffaf321bc18dd663e111976dbd0d3bbd8a66d58d2b470dfa7f396", size = 201015, upload-time = "2026-04-25T11:08:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/91/8c/2254e2d06c3ac5e6fe22eaf3da791b87ea823ae9f2c17b4af66755c5752d/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4e15cc9e2817f6481160f930c62842b3ff419e20e13072bcbab12230943092bc", size = 213457, upload-time = "2026-04-25T11:08:59.826Z" }, + { url = "https://files.pythonhosted.org/packages/79/a2/e3daa762545921173e3360f3b4ff7fc63c2d27359f7230ec1a7a74e117f6/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:90b9d1a8bd37d768ffc92a1f651ec69afc532a96fa1ac2ea7abbed5d630b3237", size = 277738, upload-time = "2026-04-25T11:09:01.423Z" }, + { url = "https://files.pythonhosted.org/packages/e1/4c/e186da2c46b87f5204640e008d42730bf3c1ee9f0efb71ae1ebcdfeac681/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:157c49475b34ecea8809e51123d9769a534e139d1247942f7a4bc67710bb2533", size = 417127, upload-time = "2026-04-25T11:09:03.592Z" }, + { url = "https://files.pythonhosted.org/packages/17/28/3798e15007a3712d0da3d3fe70f8e11916569858b5cc371053bc26270832/xxhash-3.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5a6ddec83325685e729ca119d1f5c518ec39294212ecd770e60693cdc5f7eb79", size = 193962, upload-time = "2026-04-25T11:09:06.228Z" }, + { url = "https://files.pythonhosted.org/packages/ad/95/a26baa93b5241fd7630998816a4ec47a5a0bad193b3f8fc8f3593e1a4a67/xxhash-3.7.0-cp314-cp314t-win32.whl", hash = "sha256:a04a6cab47e2166435aaf5b9e5ee41d1532cc8300efdef87f2a4d0acb7db19ed", size = 31643, upload-time = "2026-04-25T11:09:08.153Z" }, + { url = "https://files.pythonhosted.org/packages/44/36/5454f13c447e395f9b06a3e91274c59f503d31fad84e1836efe3bdb71f6a/xxhash-3.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8653dd7c2eda020545bb2c71c7f7039b53fe7434d0fc1a0a9deb79ab3f1a4fc1", size = 32522, upload-time = "2026-04-25T11:09:09.534Z" }, + { url = "https://files.pythonhosted.org/packages/74/35/698e7e3ff38e22992ea24870a511d8762474fb6783627a2910ff22a185c2/xxhash-3.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:468f0fc114faaa4b36699f8e328bbc3bb11dc418ba94ac52c26dd736d4b6c637", size = 28807, upload-time = "2026-04-25T11:09:11.234Z" }, + { url = "https://files.pythonhosted.org/packages/54/c1/e57ac7317b1f58a92bab692da6d497e2a7ce44735b224e296347a7ecc754/xxhash-3.7.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad3aa71e12ee634f22b39a0ff439357583706e50765f17f05550f92dbf128a23", size = 31232, upload-time = "2026-04-25T11:10:21.51Z" }, + { url = "https://files.pythonhosted.org/packages/4f/4e/075559bd712bc62e84915ea46bbee859f935d285659082c129bdbff679dd/xxhash-3.7.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5de686e73690cdaf72b96d4fa083c230ec9020bcc2627ce6316138e2cf2fe2d1", size = 28553, upload-time = "2026-04-25T11:10:23.1Z" }, + { url = "https://files.pythonhosted.org/packages/92/ca/a9c78cb384d4b033b0c58196bd5c8509873cabe76389e195127b0302a741/xxhash-3.7.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7fbec49f5341bbdea0c471f7d1e2fb41ae8925af9b6f28025c28defd8eb94274", size = 41109, upload-time = "2026-04-25T11:10:25.022Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b1/dfe2629f7c77eb2fa234c72ff537cdd64939763df704e256446ed364a16d/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48b542c347c2089f43dc5a6db31d2a6f3cdb04ee33505ec6e9f653834dbb0bde", size = 36307, upload-time = "2026-04-25T11:10:26.949Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f7/5a484afce0f48dd8083208b42e4911f290a82c7b52458ef2927e4d421a45/xxhash-3.7.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a169a036bed0995e090d1493b283cc2cc8a6f5046821086b843abefff80643bc", size = 32534, upload-time = "2026-04-25T11:10:29.01Z" }, + { url = "https://files.pythonhosted.org/packages/0f/5f/4acfcd490db9780cf36c58534d828003c564cde5350220a1c783c4d10776/xxhash-3.7.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:ec101643395d7f21405b640f728f6f627e6986557027d740f2f9b220955edafe", size = 31552, upload-time = "2026-04-25T11:10:30.727Z" }, +] + +[[package]] +name = "yarl" +version = "1.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/0d/9cc638702f6fc3c7a3685bcc8cf2a9ed7d6206e932a49f5242658047ef51/yarl-1.23.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cff6d44cb13d39db2663a22b22305d10855efa0fa8015ddeacc40bc59b9d8107", size = 123764, upload-time = "2026-03-01T22:04:09.7Z" }, + { url = "https://files.pythonhosted.org/packages/7a/35/5a553687c5793df5429cd1db45909d4f3af7eee90014888c208d086a44f0/yarl-1.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c53f8347cd4200f0d70a48ad059cabaf24f5adc6ba08622a23423bc7efa10d", size = 86282, upload-time = "2026-03-01T22:04:11.892Z" }, + { url = "https://files.pythonhosted.org/packages/68/2e/c5a2234238f8ce37a8312b52801ee74117f576b1539eec8404a480434acc/yarl-1.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a6940a074fb3c48356ed0158a3ca5699c955ee4185b4d7d619be3c327143e05", size = 86053, upload-time = "2026-03-01T22:04:13.292Z" }, + { url = "https://files.pythonhosted.org/packages/74/3f/bbd8ff36fb038622797ffbaf7db314918bb4d76f1cc8a4f9ca7a55fe5195/yarl-1.23.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed5f69ce7be7902e5c70ea19eb72d20abf7d725ab5d49777d696e32d4fc1811d", size = 99395, upload-time = "2026-03-01T22:04:15.133Z" }, + { url = "https://files.pythonhosted.org/packages/77/04/9516bc4e269d2a3ec9c6779fcdeac51ce5b3a9b0156f06ac7152e5bba864/yarl-1.23.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:389871e65468400d6283c0308e791a640b5ab5c83bcee02a2f51295f95e09748", size = 92143, upload-time = "2026-03-01T22:04:16.829Z" }, + { url = "https://files.pythonhosted.org/packages/c7/63/88802d1f6b1cb1fc67d67a58cd0cf8a1790de4ce7946e434240f1d60ab4a/yarl-1.23.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dda608c88cf709b1d406bdfcd84d8d63cff7c9e577a403c6108ce8ce9dcc8764", size = 107643, upload-time = "2026-03-01T22:04:18.519Z" }, + { url = "https://files.pythonhosted.org/packages/8e/db/4f9b838f4d8bdd6f0f385aed8bbf21c71ed11a0b9983305c302cbd557815/yarl-1.23.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c4fe09e0780c6c3bf2b7d4af02ee2394439d11a523bbcf095cf4747c2932007", size = 108700, upload-time = "2026-03-01T22:04:20.373Z" }, + { url = "https://files.pythonhosted.org/packages/50/12/95a1d33f04a79c402664070d43b8b9f72dc18914e135b345b611b0b1f8cc/yarl-1.23.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c9921eb8bd12633b41ad27686bbb0b1a2a9b8452bfdf221e34f311e9942ed4", size = 102769, upload-time = "2026-03-01T22:04:23.055Z" }, + { url = "https://files.pythonhosted.org/packages/86/65/91a0285f51321369fd1a8308aa19207520c5f0587772cfc2e03fc2467e90/yarl-1.23.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5f10fd85e4b75967468af655228fbfd212bdf66db1c0d135065ce288982eda26", size = 101114, upload-time = "2026-03-01T22:04:25.031Z" }, + { url = "https://files.pythonhosted.org/packages/58/80/c7c8244fc3e5bc483dc71a09560f43b619fab29301a0f0a8f936e42865c7/yarl-1.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dbf507e9ef5688bada447a24d68b4b58dd389ba93b7afc065a2ba892bea54769", size = 98883, upload-time = "2026-03-01T22:04:27.281Z" }, + { url = "https://files.pythonhosted.org/packages/86/e7/71ca9cc9ca79c0b7d491216177d1aed559d632947b8ffb0ee60f7d8b23e3/yarl-1.23.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:85e9beda1f591bc73e77ea1c51965c68e98dafd0fec72cdd745f77d727466716", size = 94172, upload-time = "2026-03-01T22:04:28.554Z" }, + { url = "https://files.pythonhosted.org/packages/6a/3f/6c6c8a0fe29c26fb2db2e8d32195bb84ec1bfb8f1d32e7f73b787fcf349b/yarl-1.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1fdaa14ef51366d7757b45bde294e95f6c8c049194e793eedb8387c86d5993", size = 107010, upload-time = "2026-03-01T22:04:30.385Z" }, + { url = "https://files.pythonhosted.org/packages/56/38/12730c05e5ad40a76374d440ed8b0899729a96c250516d91c620a6e38fc2/yarl-1.23.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:75e3026ab649bf48f9a10c0134512638725b521340293f202a69b567518d94e0", size = 100285, upload-time = "2026-03-01T22:04:31.752Z" }, + { url = "https://files.pythonhosted.org/packages/34/92/6a7be9239f2347234e027284e7a5f74b1140cc86575e7b469d13fba1ebfe/yarl-1.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:80e6d33a3d42a7549b409f199857b4fb54e2103fc44fb87605b6663b7a7ff750", size = 108230, upload-time = "2026-03-01T22:04:33.844Z" }, + { url = "https://files.pythonhosted.org/packages/5e/81/4aebccfa9376bd98b9d8bfad20621a57d3e8cfc5b8631c1fa5f62cdd03f4/yarl-1.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5ec2f42d41ccbd5df0270d7df31618a8ee267bfa50997f5d720ddba86c4a83a6", size = 103008, upload-time = "2026-03-01T22:04:35.856Z" }, + { url = "https://files.pythonhosted.org/packages/38/0f/0b4e3edcec794a86b853b0c6396c0a888d72dfce19b2d88c02ac289fb6c1/yarl-1.23.0-cp310-cp310-win32.whl", hash = "sha256:debe9c4f41c32990771be5c22b56f810659f9ddf3d63f67abfdcaa2c6c9c5c1d", size = 83073, upload-time = "2026-03-01T22:04:38.268Z" }, + { url = "https://files.pythonhosted.org/packages/a0/71/ad95c33da18897e4c636528bbc24a1dd23fe16797de8bc4ec667b8db0ba4/yarl-1.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f043cb8a2d71c981c09c510da013bc79fd661f5c60139f00dd3c3cc4f2ffb", size = 87328, upload-time = "2026-03-01T22:04:39.558Z" }, + { url = "https://files.pythonhosted.org/packages/e2/14/dfa369523c79bccf9c9c746b0a63eb31f65db9418ac01275f7950962e504/yarl-1.23.0-cp310-cp310-win_arm64.whl", hash = "sha256:263cd4f47159c09b8b685890af949195b51d1aa82ba451c5847ca9bc6413c220", size = 82463, upload-time = "2026-03-01T22:04:41.454Z" }, + { url = "https://files.pythonhosted.org/packages/a2/aa/60da938b8f0997ba3a911263c40d82b6f645a67902a490b46f3355e10fae/yarl-1.23.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", size = 123641, upload-time = "2026-03-01T22:04:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", size = 86248, upload-time = "2026-03-01T22:04:44.757Z" }, + { url = "https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", size = 85988, upload-time = "2026-03-01T22:04:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6c/4a90d59c572e46b270ca132aca66954f1175abd691f74c1ef4c6711828e2/yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", size = 100566, upload-time = "2026-03-01T22:04:47.639Z" }, + { url = "https://files.pythonhosted.org/packages/49/fb/c438fb5108047e629f6282a371e6e91cf3f97ee087c4fb748a1f32ceef55/yarl-1.23.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", size = 92079, upload-time = "2026-03-01T22:04:48.925Z" }, + { url = "https://files.pythonhosted.org/packages/d9/13/d269aa1aed3e4f50a5a103f96327210cc5fa5dd2d50882778f13c7a14606/yarl-1.23.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", size = 108741, upload-time = "2026-03-01T22:04:50.838Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/115b16f22c37ea4437d323e472945bea97301c8ec6089868fa560abab590/yarl-1.23.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", size = 108099, upload-time = "2026-03-01T22:04:52.499Z" }, + { url = "https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", size = 102678, upload-time = "2026-03-01T22:04:55.176Z" }, + { url = "https://files.pythonhosted.org/packages/85/59/cd98e556fbb2bf8fab29c1a722f67ad45c5f3447cac798ab85620d1e70af/yarl-1.23.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", size = 100803, upload-time = "2026-03-01T22:04:56.588Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c0/b39770b56d4a9f0bb5f77e2f1763cd2d75cc2f6c0131e3b4c360348fcd65/yarl-1.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", size = 100163, upload-time = "2026-03-01T22:04:58.492Z" }, + { url = "https://files.pythonhosted.org/packages/e7/64/6980f99ab00e1f0ff67cb84766c93d595b067eed07439cfccfc8fb28c1a6/yarl-1.23.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", size = 93859, upload-time = "2026-03-01T22:05:00.268Z" }, + { url = "https://files.pythonhosted.org/packages/38/69/912e6c5e146793e5d4b5fe39ff5b00f4d22463dfd5a162bec565ac757673/yarl-1.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", size = 108202, upload-time = "2026-03-01T22:05:02.273Z" }, + { url = "https://files.pythonhosted.org/packages/59/97/35ca6767524687ad64e5f5c31ad54bc76d585585a9fcb40f649e7e82ffed/yarl-1.23.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", size = 99866, upload-time = "2026-03-01T22:05:03.597Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1c/1a3387ee6d73589f6f2a220ae06f2984f6c20b40c734989b0a44f5987308/yarl-1.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", size = 107852, upload-time = "2026-03-01T22:05:04.986Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b8/35c0750fcd5a3f781058bfd954515dd4b1eab45e218cbb85cf11132215f1/yarl-1.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", size = 102919, upload-time = "2026-03-01T22:05:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1c/9a1979aec4a81896d597bcb2177827f2dbee3f5b7cc48b2d0dadb644b41d/yarl-1.23.0-cp311-cp311-win32.whl", hash = "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", size = 82602, upload-time = "2026-03-01T22:05:08.444Z" }, + { url = "https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", size = 87461, upload-time = "2026-03-01T22:05:10.145Z" }, + { url = "https://files.pythonhosted.org/packages/93/95/07e3553fe6f113e6864a20bdc53a78113cda3b9ced8784ee52a52c9f80d8/yarl-1.23.0-cp311-cp311-win_arm64.whl", hash = "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", size = 82336, upload-time = "2026-03-01T22:05:11.554Z" }, + { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, + { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, + { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, + { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, + { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, + { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, + { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, + { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, + { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, + { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, + { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, + { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, + { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, + { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, + { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, + { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, + { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, + { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, + { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, + { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, + { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, + { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, + { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, + { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, + { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, + { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, + { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, + { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, + { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, + { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, + { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, + { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, + { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, + { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, + { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, + { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, + { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, + { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, + { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, + { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, + { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, + { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, + { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, + { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, + { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, + { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, + { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, + { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, + { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, + { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, + { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, + { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, + { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, + { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, + { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, + { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, + { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, + { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, + { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, + { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, +]