Merge pull request #200 from rajamukerji/fix/bayesian-silent-skip-and-warmstart-bounds

fix(bayesian): surface skipped optimization and clamp warm start to its search space
This commit is contained in:
Joseph Magly
2026-09-07 17:16:37 -04:00
committed by GitHub
11 changed files with 743 additions and 91 deletions
+5
View File
@@ -182,6 +182,11 @@ Pick a model from the dropdown, pick a method, hit Run All. Download the result
For automation, CI pipelines, or remote servers without a display:
The normal installation includes Optuna for the `optimized` method; no separate
optimizer install is required. If optimization cannot run, its reason appears
in the pipeline log and in
`abliteration_metadata.json` under `method_config.bayesian_optimization_skipped`.
```bash
pip install -e .
+1
View File
@@ -41,6 +41,7 @@
"BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0",
"ISC License (ISCL)",
"MIT",
"MIT AND PSF-2.0",
"MIT License",
"MIT-CMU",
"MPL-2.0",
+3 -1
View File
@@ -81,6 +81,7 @@
"tests/test_qwen35_contracts.py",
"tests/test_auto_obliterate.py",
"tests/test_bayesian_optimizer_contracts.py",
"tests/test_bayesian_optimizer_optuna.py",
"tests/test_informed_pipeline.py",
"tests/test_informed_pipeline_contracts.py",
"tests/test_offline_integration.py",
@@ -777,9 +778,10 @@
{
"path": "obliteratus/bayesian_optimizer.py",
"risk_class": "conditional-runtime",
"risk": "optional optimizer trials over repeated live model mutation and evaluation",
"risk": "optimizer trials over repeated live model mutation and evaluation",
"required_tests": [
"tests/test_bayesian_optimizer_contracts.py",
"tests/test_bayesian_optimizer_optuna.py",
"tests/test_module_imports.py",
"tests/conditional/test_model_download_runtime.py"
],
+29
View File
@@ -9,6 +9,35 @@ CI uses exact Python tool versions and checksum-pinned standalone binaries.
`ci/digests.txt` records executable and GitHub Action pins; build, test, and
license-tool transitive dependencies are covered by `uv.lock`.
## Bayesian optimizer dependency
Optuna is a core dependency (`>=4.9,<5`), installed by both `pip install .`
and the documented locked development setup. The `optimized` and `heretic`
pipeline methods therefore receive their requested Bayesian optimization
without a separate manual installation. A partial environment that lacks
Optuna still reports the fallback in the pipeline log and checkpoint metadata.
The lock pins Optuna 4.9.0 and adds Alembic 1.19.2, colorlog 6.12.0,
greenlet 3.5.5, Mako 1.4.1, and SQLAlchemy 2.0.52 without upgrading existing
packages. These distributions come from PyPI, with artifact SHA-256 hashes in
`uv.lock`; the signed repository change records the reviewed lock. The package
registry and package publishers remain the source trust boundary. No hosted
Optuna service, dashboard, database connection, or integration extra is used:
the optimizer creates an in-memory study.
The package license inventory identifies MIT/MIT License for Optuna, Alembic,
colorlog, Mako, and SQLAlchemy. Optuna's bundled third-party notices cover
SciPy-derived BSD code and fdlibm's permissive notice. Greenlet's included
`LICENSE` and `LICENSE.PSF` identify MIT code and Stackless/Python-derived code
under PSF-2.0. The reviewed composite expression `MIT AND PSF-2.0` is therefore
included in the allow list; preserve both notices when redistributing it.
No vulnerability suppression is added. Dependency updates remain subject to
the same vulnerability and license gates as the rest of the lock.
`tests/test_bayesian_optimizer_optuna.py` runs real Optuna trials with tiny
local CPU tensors, verifies in-range warm starts and model restoration, and
uses no model download, network, credentials, or accelerator.
## Digest-bound release evidence
The release artifact is a deterministic ZIP snapshot of the tested repository
+22 -1
View File
@@ -1242,6 +1242,7 @@ class AbliterationPipeline:
# Float layer interpolation: continuous layer weights
self._float_layer_weights: dict[int, float] = {}
# Bayesian optimizer component-specific scales (set by optimizer)
self._bayesian_skipped: str | None = None
self._bayesian_attn_scale: float | None = None
self._bayesian_mlp_scale: float | None = None
# CoT-aware: identified reasoning-critical directions to preserve
@@ -1552,6 +1553,8 @@ class AbliterationPipeline:
def run(self) -> Path:
"""Execute the full abliteration pipeline. Returns path to saved model."""
from obliteratus.bayesian_optimizer import _reset_bayesian_state
_reset_bayesian_state(self)
try:
return self._run_pipeline()
except PipelineFailure:
@@ -4209,11 +4212,13 @@ class AbliterationPipeline:
# When enabled, run Optuna TPE to find optimal per-layer regularization
# before the standard projection loop. The found values override the
# static layer_adaptive_strength weights.
from obliteratus.bayesian_optimizer import _reset_bayesian_state
_reset_bayesian_state(self)
bayesian_regs: dict[int, float] = {}
bayesian_trials = getattr(self, "_bayesian_trials", 0) or (
METHODS.get(self.method, {}).get("bayesian_trials", 0)
)
if bayesian_trials > 0 and self._strong_layers and self.handle:
if bayesian_trials > 0:
self.log(f"Running Bayesian optimization ({bayesian_trials} trials)...")
from obliteratus.bayesian_optimizer import run_bayesian_optimization
bayesian_regs = run_bayesian_optimization(
@@ -4231,6 +4236,17 @@ class AbliterationPipeline:
f"{idx}:{reg:.3f}" for idx, reg in sorted(bayesian_regs.items())
)
self.log(f" Optimal regs: {regs_str}")
else:
# Reached whenever the optimizer no-ops for any reason. Without
# this the run logs "Running Bayesian optimization (50 trials)",
# prints nothing further, and completes as a success — so the
# only difference between an optimized and an unoptimized
# checkpoint is an absence of output.
self.log(
"WARNING: Bayesian optimization returned no layer "
f"regularizations (reason: {getattr(self, '_bayesian_skipped', 'unknown')}). "
"Continuing with method defaults — this checkpoint is NOT optimized."
)
# ── LoRA-based reversible ablation ──────────────────────────────
# When enabled, compute LoRA adapters and merge them instead of
@@ -7584,6 +7600,11 @@ class AbliterationPipeline:
"som_diversity_penalty": self.som_diversity_penalty if self.direction_method == "som" else None,
"som_min_signal_to_noise": self.som_min_signal_to_noise if self.direction_method == "som" else None,
"layer_selection": self.layer_selection,
# None when the optimizer ran (or was never requested); a reason
# string when it no-opped. Without it a checkpoint's provenance
# records the method that ASKED for an optimization, with nothing
# to say whether one happened.
"bayesian_optimization_skipped": getattr(self, "_bayesian_skipped", None),
"min_layer_fraction": self.min_layer_fraction,
"max_layer_fraction": self.max_layer_fraction,
"harmless_pc_count": self.harmless_pc_count,
+96 -13
View File
@@ -41,6 +41,63 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__)
# Kernel search space, declared once. `objective` suggests from these bounds and
# the warm-start trial is clamped to them; keeping two copies is what let the
# warm start drift outside the space it was seeding (see `_clamp_to_space`).
KERNEL_SPACE: dict[str, tuple[float, float]] = {
"attn_max_weight": (0.5, 1.0),
"attn_peak_position": (0.1, 0.9),
"attn_min_weight": (0.0, 0.3),
"attn_spread": (0.1, 0.6),
"mlp_max_weight": (0.3, 1.0),
"mlp_peak_position": (0.1, 0.9),
"mlp_min_weight": (0.0, 0.3),
"mlp_spread": (0.1, 0.6),
}
def _reset_bayesian_state(pipeline: AbliterationPipeline) -> None:
"""Clear status and component overrides from an earlier attempt."""
pipeline._bayesian_skipped = None
pipeline._bayesian_attn_scale = None
pipeline._bayesian_mlp_scale = None
def _skip(pipeline: AbliterationPipeline, reason: str, message: str) -> None:
"""Record and surface a no-op so it cannot be mistaken for a completed run.
Reported three ways because each reaches a different audience: the stdlib
logger for anyone capturing logs, the pipeline log for the console panel the
operator is watching, and an attribute the caller persists into checkpoint
metadata so the provenance of an already-written model still shows it.
"""
logger.warning(message)
log = getattr(pipeline, "log", None)
if callable(log):
log(f"WARNING: {message}")
try:
pipeline._bayesian_skipped = reason
except AttributeError: # pragma: no cover - pipeline stubs may use __slots__
pass
def _clamp_to_space(params: dict[str, float]) -> dict[str, float]:
"""Clamp warm-start values into the declared search space.
Optuna warns about an out-of-range enqueued value but still evaluates it.
Clamping keeps the informed starting point within the same bounds as later
sampled trials. This is reachable in normal use: `attn_peak_position` is
seeded from
`peak_layer / (n_layers - 1)`, which for an early peak layer falls below the
0.1 floor — a 36-layer model peaking at layer 2 seeds 0.057.
"""
out = dict(params)
for name, (lo, hi) in KERNEL_SPACE.items():
if name in out:
out[name] = min(max(float(out[name]), lo), hi)
return out
def _measure_refusal_rate(
pipeline: AbliterationPipeline,
n_prompts: int = 10,
@@ -261,17 +318,33 @@ def run_bayesian_optimization(
Returns:
Dict mapping layer_idx -> optimal regularization value.
"""
_reset_bayesian_state(pipeline)
if n_trials <= 0:
_skip(pipeline, "no-trials", "Bayesian optimization SKIPPED: no trials requested.")
return {}
try:
import optuna
from optuna.samplers import TPESampler
except ImportError:
logger.warning(
"Optuna not installed — skipping Bayesian optimization. "
"Install with: pip install optuna"
# Keep the operator log and saved provenance explicit even when a
# partial or older installation lacks the optimizer dependency.
_skip(
pipeline,
"optuna-not-installed",
"Optuna not installed — Bayesian optimization SKIPPED. Layer "
"weights fall back to method defaults; results are NOT optimized. "
"Install with: pip install optuna",
)
return {}
if not pipeline.handle or not pipeline._strong_layers:
_skip(
pipeline,
"no-strong-layers",
"Bayesian optimization SKIPPED: no model handle or no selected "
"layers to optimize over.",
)
return {}
model = pipeline.handle.model
@@ -307,7 +380,11 @@ def run_bayesian_optimization(
pipeline._free_gpu_memory()
if not reference_logits:
pipeline.log(" Failed to collect reference logits — skipping optimization")
_skip(
pipeline,
"reference-logits-unavailable",
"Bayesian optimization SKIPPED: Failed to collect reference logits.",
)
return {}
from obliteratus.strategies.utils import (
@@ -401,16 +478,16 @@ def run_bayesian_optimization(
_restore_all()
# Attention kernel: 4 params
attn_max = trial.suggest_float("attn_max_weight", 0.5, 1.0)
attn_peak = trial.suggest_float("attn_peak_position", 0.1, 0.9)
attn_min = trial.suggest_float("attn_min_weight", 0.0, 0.3)
attn_spread = trial.suggest_float("attn_spread", 0.1, 0.6)
attn_max = trial.suggest_float("attn_max_weight", *KERNEL_SPACE["attn_max_weight"])
attn_peak = trial.suggest_float("attn_peak_position", *KERNEL_SPACE["attn_peak_position"])
attn_min = trial.suggest_float("attn_min_weight", *KERNEL_SPACE["attn_min_weight"])
attn_spread = trial.suggest_float("attn_spread", *KERNEL_SPACE["attn_spread"])
# MLP kernel: 4 params (separate — can peak at a different layer)
mlp_max = trial.suggest_float("mlp_max_weight", 0.3, 1.0)
mlp_peak = trial.suggest_float("mlp_peak_position", 0.1, 0.9)
mlp_min = trial.suggest_float("mlp_min_weight", 0.0, 0.3)
mlp_spread = trial.suggest_float("mlp_spread", 0.1, 0.6)
mlp_max = trial.suggest_float("mlp_max_weight", *KERNEL_SPACE["mlp_max_weight"])
mlp_peak = trial.suggest_float("mlp_peak_position", *KERNEL_SPACE["mlp_peak_position"])
mlp_min = trial.suggest_float("mlp_min_weight", *KERNEL_SPACE["mlp_min_weight"])
mlp_spread = trial.suggest_float("mlp_spread", *KERNEL_SPACE["mlp_spread"])
# Float direction index (cross-layer interpolation, Heretic-style)
dir_idx = trial.suggest_float("dir_idx", 0.0, max(n_layers_with_dirs - 1, 0.0))
@@ -521,7 +598,7 @@ def run_bayesian_optimization(
"mlp_spread": 0.3,
"dir_idx": 0.0,
}
study.enqueue_trial(warm_params)
study.enqueue_trial(_clamp_to_space(warm_params))
pipeline.log(f"Bayesian optimization: running {n_trials} trials (parametric kernel)...")
study.optimize(objective, n_trials=n_trials, show_progress_bar=False)
@@ -585,4 +662,10 @@ def run_bayesian_optimization(
del original_params
pipeline._free_gpu_memory()
if not best_result:
_skip(
pipeline,
"no-successful-trials",
"Bayesian optimization SKIPPED: no usable trial results.",
)
return best_result
+1
View File
@@ -34,6 +34,7 @@ dependencies = [
"numpy>=1.24",
"scikit-learn>=1.3",
"tqdm>=4.64",
"optuna>=4.9,<5",
]
[project.urls]
+1
View File
@@ -12,4 +12,5 @@ pandas>=2.0
numpy>=1.24
scikit-learn>=1.3
tqdm>=4.64
optuna>=4.9,<5
bitsandbytes>=0.46.1
+236 -2
View File
@@ -253,6 +253,7 @@ def test_run_bayesian_optimization_returns_empty_when_reference_logits_fail(monk
assert bo.run_bayesian_optimization(pipeline, n_kl_prompts=2) == {}
assert pipeline.freed == 1
assert pipeline._bayesian_skipped == "reference-logits-unavailable"
assert "Failed to collect reference logits" in pipeline.logs[-1]
@@ -443,13 +444,17 @@ def test_run_bayesian_optimization_no_pareto_uses_objective_best_and_restores_af
n_kl_prompts=1,
)
# peak_position was 0.0 here before the warm start was clamped. 0.0 is
# outside the distribution `objective` declares for it (0.1..0.9), so Optuna
# warned but still evaluated a value outside the search space. Clamping
# keeps the initial trial within the same bounds as sampled trials.
assert study.enqueued == [{
"attn_max_weight": 0.9,
"attn_peak_position": 0.0,
"attn_peak_position": 0.1,
"attn_min_weight": 0.05,
"attn_spread": 0.3,
"mlp_max_weight": 0.6,
"mlp_peak_position": 0.0,
"mlp_peak_position": 0.1,
"mlp_min_weight": 0.05,
"mlp_spread": 0.3,
"dir_idx": 0.0,
@@ -460,3 +465,232 @@ def test_run_bayesian_optimization_no_pareto_uses_objective_best_and_restores_af
assert pipeline.moe_calls == []
assert torch.allclose(layers[0].self_attn.o_proj.weight, original_attn)
assert torch.allclose(layers[0].mlp.down_proj.weight, original_mlp)
def test_missing_optuna_is_surfaced_on_the_pipeline_log_and_recorded(monkeypatch):
"""A skipped optimization must not be indistinguishable from a completed one.
`logger.warning` reaches stderr only through logging's last-resort handler —
the `obliterate` command never configures logging — while the console panel
the operator watches is fed by `pipeline.log`. Before this contract the
pipeline log received nothing at all, so a run whose headline feature never
executed still read as a clean success.
"""
real_import = builtins.__import__
def fake_import(name, *args, **kwargs):
if name == "optuna" or name.startswith("optuna."):
raise ImportError("no optuna in this test")
return real_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", fake_import)
pipeline = _Pipeline()
pipeline.handle = object()
pipeline._strong_layers = [0]
assert bo.run_bayesian_optimization(pipeline) == {}
assert any("SKIPPED" in message for message in pipeline.logs)
assert any("optuna" in message.lower() for message in pipeline.logs)
assert pipeline._bayesian_skipped == "optuna-not-installed"
def test_skip_without_handle_or_layers_is_also_recorded(monkeypatch):
_install_fake_optuna(monkeypatch)
pipeline = _Pipeline()
pipeline.handle = None
pipeline._strong_layers = [0]
assert bo.run_bayesian_optimization(pipeline) == {}
assert pipeline._bayesian_skipped == "no-strong-layers"
def test_clamp_to_space_pulls_out_of_range_warm_start_into_the_distribution():
"""Reachable in normal use, not a synthetic edge case.
`attn_peak_position` is seeded from ``peak_layer / (n_layers - 1)``; a
36-layer model whose peak layer is 2 seeds 0.057, below the 0.1 floor.
Optuna warns but evaluates an enqueued value outside its distribution;
the warm-start trial should obey the same bounds as sampled trials.
"""
clamped = bo._clamp_to_space(
{
"attn_peak_position": 2 / 35, # early peak layer on a 36-layer model
"mlp_peak_position": 34 / 35, # late peak layer
"mlp_max_weight": 0.25, # max_weight 0.5 x mlp_scale 0.5
"dir_idx": 3.0, # bounds are dynamic; must pass through
}
)
for name, value in clamped.items():
if name in bo.KERNEL_SPACE:
low, high = bo.KERNEL_SPACE[name]
assert low <= value <= high, f"{name}={value} outside {low}..{high}"
assert clamped["attn_peak_position"] == pytest.approx(0.1)
assert clamped["mlp_peak_position"] == pytest.approx(0.9)
assert clamped["mlp_max_weight"] == pytest.approx(0.3)
assert clamped["dir_idx"] == 3.0
def test_objective_bounds_and_warm_start_share_one_declaration(monkeypatch):
"""The two copies of the search space are why the warm start could drift."""
study = _FakeStudy(best_trials=[_FakeTrial()])
_install_fake_optuna(monkeypatch, study=study)
recorded: dict[str, tuple[float, float]] = {}
class _RecordingTrial(_FakeTrial):
def suggest_float(self, name, low, high):
recorded[name] = (low, high)
return self.params[name]
monkeypatch.setattr(
study,
"optimize",
lambda objective, n_trials, show_progress_bar: objective(_RecordingTrial()),
)
monkeypatch.setattr(bo, "_measure_refusal_rate", lambda *_a, **_k: 0.25)
monkeypatch.setattr(bo, "_measure_kl_divergence", lambda *_a, **_k: 0.1)
bo.run_bayesian_optimization(_optimization_pipeline([_Layer()]), n_trials=1)
for name, bounds in bo.KERNEL_SPACE.items():
assert recorded.get(name) == bounds, f"{name} suggested outside KERNEL_SPACE"
def test_retry_clears_skip_reason_and_component_overrides(monkeypatch):
study = _FakeStudy(best_trials=[_FakeTrial()])
_install_fake_optuna(monkeypatch, study)
monkeypatch.setattr(bo, "_measure_refusal_rate", lambda *_a, **_k: 0.25)
monkeypatch.setattr(bo, "_measure_kl_divergence", lambda *_a, **_k: 0.1)
pipeline = _optimization_pipeline([_Layer()])
tokenizer = pipeline.handle.tokenizer
pipeline.handle.tokenizer = lambda *_a, **_k: (_ for _ in ()).throw(RuntimeError("tokenization"))
assert bo.run_bayesian_optimization(pipeline, n_trials=1) == {}
assert pipeline._bayesian_skipped == "reference-logits-unavailable"
pipeline.handle.tokenizer = tokenizer
assert bo.run_bayesian_optimization(pipeline, n_trials=1)
assert pipeline._bayesian_skipped is None
assert pipeline._bayesian_attn_scale == 0.8
pipeline._strong_layers = []
assert bo.run_bayesian_optimization(pipeline, n_trials=1) == {}
assert pipeline._bayesian_skipped == "no-strong-layers"
assert pipeline._bayesian_attn_scale is None
assert pipeline._bayesian_mlp_scale is None
@pytest.mark.parametrize("n_trials", [0, -1])
def test_nonpositive_trial_budget_records_skip(n_trials):
pipeline = _optimization_pipeline([_Layer()])
assert bo.run_bayesian_optimization(pipeline, n_trials=n_trials) == {}
assert pipeline._bayesian_skipped == "no-trials"
def test_empty_study_records_skip(monkeypatch):
study = _FakeStudy(best_trials=[])
_install_fake_optuna(monkeypatch, study)
monkeypatch.setattr(study, "optimize", lambda *_a, **_k: None)
pipeline = _optimization_pipeline([_Layer()])
assert bo.run_bayesian_optimization(pipeline, n_trials=1) == {}
assert pipeline._bayesian_skipped == "no-successful-trials"
@pytest.mark.parametrize("outcome,reason", [
("missing", "optuna-not-installed"),
("reference-failure", "reference-logits-unavailable"),
("no-layers", "no-strong-layers"),
("success", None),
("not-requested", None),
])
def test_caller_warning_and_saved_checkpoint_status(monkeypatch, tmp_path, outcome, reason):
"""Exercise EXCISE and the actual on-disk checkpoint provenance together."""
import json
from pathlib import Path
from unittest.mock import MagicMock
from transformers import LlamaConfig, LlamaForCausalLM
from obliteratus.abliterate import AbliterationPipeline
from obliteratus.models.loader import ModelHandle
config = LlamaConfig(
vocab_size=16, hidden_size=8, intermediate_size=16, num_hidden_layers=1,
num_attention_heads=1, num_key_value_heads=1, max_position_embeddings=16,
)
model = LlamaForCausalLM(config).eval()
tokenizer = MagicMock()
tokenizer.return_value = {"input_ids": torch.tensor([[1, 2]])}
tokenizer.save_pretrained.side_effect = lambda path: (
Path(path) / "tokenizer_config.json"
).write_text("{}", encoding="utf-8")
pipeline = AbliterationPipeline(
model_name="test-model", output_dir=str(tmp_path / "checkpoint"), method="basic",
)
pipeline.handle = ModelHandle(
model=model, tokenizer=tokenizer, config=config, model_name="test-model", task="causal_lm",
)
logs = []
pipeline._on_log = logs.append
pipeline._on_stage = lambda _record: None
pipeline._strong_layers = [] if outcome == "no-layers" else [0]
pipeline.refusal_directions = {0: torch.ones(8) / 8**0.5}
pipeline.refusal_subspaces = {0: pipeline.refusal_directions[0].unsqueeze(0)}
pipeline._bayesian_trials = 0 if outcome == "not-requested" else 1
# Both a previous skip and previous successful component overrides must be reset.
pipeline._bayesian_skipped = "previous-attempt"
pipeline._bayesian_attn_scale = 0.2
pipeline._bayesian_mlp_scale = 0.3
study = _FakeStudy(best_trials=[_FakeTrial(params={**_FakeTrial.params, "dir_idx": 0.0})])
_install_fake_optuna(monkeypatch, study)
monkeypatch.setattr(bo, "_measure_refusal_rate", lambda *_a, **_k: 0.25)
monkeypatch.setattr(bo, "_measure_kl_divergence", lambda *_a, **_k: 0.1)
if outcome == "missing":
real_import = builtins.__import__
def import_without_optuna(name, *args, **kwargs):
if name == "optuna" or name.startswith("optuna."):
raise ImportError("missing optuna")
return real_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", import_without_optuna)
elif outcome == "reference-failure":
tokenizer.side_effect = RuntimeError("tokenization failed")
pipeline._excise()
result = pipeline._rebirth()
metadata = json.loads((result / "abliteration_metadata.json").read_text())
assert metadata["method_config"]["bayesian_optimization_skipped"] == reason
fallback = [message for message in logs if "this checkpoint is NOT optimized" in message]
if reason:
assert len(fallback) == 1
assert reason in fallback[0]
else:
assert not fallback
if outcome != "success":
assert pipeline._bayesian_attn_scale is None
assert pipeline._bayesian_mlp_scale is None
if outcome == "success":
assert any("Bayesian optimization complete" in message for message in logs)
if outcome == "not-requested":
assert not study.enqueued
def test_run_resets_bayesian_status_before_any_stage(monkeypatch, tmp_path):
from obliteratus.abliterate import AbliterationPipeline
pipeline = AbliterationPipeline(model_name="test-model", output_dir=str(tmp_path), method="basic")
pipeline._bayesian_skipped = "previous-attempt"
pipeline._bayesian_attn_scale = 0.2
pipeline._bayesian_mlp_scale = 0.3
def stages():
assert pipeline._bayesian_skipped is None
assert pipeline._bayesian_attn_scale is None
assert pipeline._bayesian_mlp_scale is None
return tmp_path
monkeypatch.setattr(pipeline, "_run_pipeline", stages)
assert pipeline.run() == tmp_path
+95
View File
@@ -0,0 +1,95 @@
"""Real, in-memory Optuna contracts with a tiny deterministic CPU model."""
from types import SimpleNamespace
import optuna
import pytest
import torch
from torch import nn
from obliteratus import bayesian_optimizer as bo
from obliteratus.abliterate import AbliterationPipeline
class _TinyModel(nn.Module):
def __init__(self):
super().__init__()
layer = nn.Module()
layer.self_attn = nn.Module()
layer.self_attn.o_proj = nn.Linear(2, 2, bias=False)
layer.mlp = nn.Module()
layer.mlp.down_proj = nn.Linear(2, 2, bias=False)
with torch.no_grad():
layer.self_attn.o_proj.weight.copy_(torch.tensor([[1., .2], [.3, 1.]]))
layer.mlp.down_proj.weight.copy_(torch.tensor([[.7, .1], [.4, .8]]))
self.model = nn.Module()
self.model.layers = nn.ModuleList([layer])
self.forward_calls = 0
self.generation_calls = 0
def forward(self, input_ids, **_kwargs):
self.forward_calls += 1
hidden = torch.nn.functional.one_hot(input_ids, num_classes=2).float()
for layer in self.model.layers:
hidden = layer.mlp.down_proj(layer.self_attn.o_proj(hidden))
return SimpleNamespace(logits=hidden)
def generate(self, input_ids, **_kwargs):
self.generation_calls += 1
token = self(input_ids).logits[:, -1, :].argmax(dim=-1, keepdim=True)
return torch.cat([input_ids, token], dim=1)
class _TinyTokenizer:
chat_template = None
def __call__(self, *_args, **_kwargs):
return {"input_ids": torch.tensor([[0, 1]])}
def decode(self, tokens, **_kwargs):
return "I cannot assist with that request." if tokens[-1] == 0 else "Here is an explanation."
@pytest.mark.parametrize("peak", [2 / 35, 34 / 35])
def test_real_optuna_runs_bounded_warm_start_and_restores_model(monkeypatch, peak):
studies = []
create_study = optuna.create_study
def capture_study(**kwargs):
study = create_study(**kwargs)
studies.append(study)
return study
monkeypatch.setattr(optuna, "create_study", capture_study)
pipeline = AbliterationPipeline(model_name="tiny-local", method="optimized")
model = _TinyModel()
pipeline.handle = SimpleNamespace(model=model, tokenizer=_TinyTokenizer(), architecture="llama")
pipeline._strong_layers = [0]
pipeline.refusal_directions = {0: torch.tensor([1., 0.])}
pipeline.harmful_prompts = ["A local test prompt"]
pipeline._informed_warm_start = {"peak_position": peak, "max_weight": .5, "mlp_scale": .5}
# The fixture owns no accelerators or large allocations.
monkeypatch.setattr(pipeline, "_free_gpu_memory", lambda: None)
original = {name: tensor.clone() for name, tensor in model.state_dict().items()}
result = bo.run_bayesian_optimization(pipeline, n_trials=2, n_refusal_prompts=1, n_kl_prompts=1)
assert set(result) == {0}
assert 0 <= result[0] <= 1
assert len(studies) == 1
trials = studies[0].trials
assert len(trials) == 2
assert all(trial.state == optuna.trial.TrialState.COMPLETE for trial in trials)
expected_peak = .1 if peak < .1 else .9
assert trials[0].params["attn_peak_position"] == pytest.approx(expected_peak)
assert trials[0].params["mlp_peak_position"] == pytest.approx(expected_peak)
assert trials[0].params["mlp_max_weight"] == pytest.approx(.3)
for trial in trials:
assert all(bo.KERNEL_SPACE[name][0] <= trial.params[name] <= bo.KERNEL_SPACE[name][1]
for name in bo.KERNEL_SPACE)
assert trial.values is not None and len(trial.values) == 2
assert all(torch.isfinite(torch.tensor(value)) for value in trial.values)
assert model.generation_calls == 2
assert model.forward_calls >= 5 # reference, generation and KL for each trial
for name, tensor in model.state_dict().items():
torch.testing.assert_close(tensor, original[name], rtol=0, atol=0)
Generated
+254 -74
View File
@@ -3,8 +3,8 @@ revision = 3
requires-python = ">=3.10"
resolution-markers = [
"(python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version >= '3.14' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version == '3.13.*' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.12.*' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform == 'win32')",
"python_full_version == '3.12.*' and sys_platform != 'linux' and sys_platform != 'win32'",
@@ -15,7 +15,7 @@ resolution-markers = [
]
[options]
exclude-newer = "2026-08-24T05:10:12.868014219Z"
exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
exclude-newer-span = "P3D"
[manifest]
@@ -201,6 +201,21 @@ 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 = "alembic"
version = "1.19.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mako" },
{ name = "sqlalchemy" },
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/34/10/181eecdd552217d0342492bd6f3b8a96e973083379aace3d3402830ddc03/alembic-1.19.2.tar.gz", hash = "sha256:297950a8a91f6770eb82bfbce9bea55c728b90a5386c6e81430191a319d138b0", size = 2082643, upload-time = "2026-09-04T17:10:11.212Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/cb/9014784dcb0585977ae23b6f43331d0a33c51ac0d692506d12b4f5ee9f3b/alembic-1.19.2-py3-none-any.whl", hash = "sha256:32d553dcd577e6fe5c3c63e91468526d35e4dcecafe865d7db4e9b328fa93cb2", size = 267399, upload-time = "2026-09-04T17:10:12.796Z" },
]
[[package]]
name = "annotated-doc"
version = "0.0.5"
@@ -436,7 +451,7 @@ name = "cffi"
version = "2.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "(implementation_name != 'PyPy' and sys_platform == 'linux') or (implementation_name != 'PyPy' and sys_platform == 'win32')" },
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" }
wheels = [
@@ -594,6 +609,18 @@ 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 = "colorlog"
version = "6.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8c/55/ba79756cb90c8d69d599d57785398ac87bba7b19c80e87f4e8a562197c93/colorlog-6.12.0.tar.gz", hash = "sha256:2a7924c1dadf18b22a0eb8b06d1c7b01d5341707ec1641eb6fcc4fde0c3e8e5f", size = 18151, upload-time = "2026-07-23T13:40:40.71Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d4/19/0b6647bf5e331521e55d2b63bfbdc210bd9cd605189273f03614a05f702d/colorlog-6.12.0-py3-none-any.whl", hash = "sha256:30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e", size = 12239, upload-time = "2026-07-23T13:40:39.562Z" },
]
[[package]]
name = "contourpy"
version = "1.3.2"
@@ -603,7 +630,7 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" }
wheels = [
@@ -671,8 +698,8 @@ version = "1.3.3"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"(python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version >= '3.14' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version == '3.13.*' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.12.*' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform == 'win32')",
"python_full_version == '3.12.*' and sys_platform != 'linux' and sys_platform != 'win32'",
@@ -680,7 +707,7 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
@@ -896,8 +923,8 @@ name = "cryptography"
version = "50.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "(platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (platform_python_implementation != 'PyPy' and sys_platform == 'win32')" },
{ name = "typing-extensions", marker = "(python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" }
wheels = [
@@ -1002,7 +1029,7 @@ name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions" },
]
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 = [
@@ -1308,6 +1335,76 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/5d/29/80957aef2f107e5357cb3d8d5d07f40302962c9b829f9e8f970b80bec502/gradio_client-2.6.0-py3-none-any.whl", hash = "sha256:4493a6425560dd23d1eb5427a13290819135c664c5ed9f184eb517b1ea263291", size = 61654, upload-time = "2026-07-29T19:39:49.788Z" },
]
[[package]]
name = "greenlet"
version = "3.5.5"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0b/d8/7cc97c142388aef03f622e001c572c4f84e9252a439549d483f555771970/greenlet-3.5.5.tar.gz", hash = "sha256:adb4bae02e91a8e863e48b177e4014bdcac8a6b5e047ea1df687a61534b85e6c", size = 207585, upload-time = "2026-08-10T15:09:36.136Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/07/67/07ecd6d85c0f253363cbc4ac9e7dd048ca571a267fbccfea084d6009ac3b/greenlet-3.5.5-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:816230f469381ad0a43abc9fa8dda5a699e32fb78958dde32ded93213b70a667", size = 292971, upload-time = "2026-08-10T13:28:09.837Z" },
{ url = "https://files.pythonhosted.org/packages/24/35/426733bc24247ee17bb76df90f550c299ff5f8572b2bdd7cacb5c53fd994/greenlet-3.5.5-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5433cf291e0ef9114bd14d0d824db6e5e4a43033234bca48181a9597acca07b", size = 609290, upload-time = "2026-08-10T14:14:32.273Z" },
{ url = "https://files.pythonhosted.org/packages/9c/dc/17d3a5acceb2fd0bbc9682a228117b719cdfb68085e6dbb33fa325755f27/greenlet-3.5.5-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19d59f068887d8c5907fc177f27683413ace3011b6ed646c0b309266e74a6502", size = 622650, upload-time = "2026-08-10T14:27:22.004Z" },
{ url = "https://files.pythonhosted.org/packages/51/91/00b3c0566316c6f383ea16ee05388ec4f57f6e0afa49e72ae471eda8c47f/greenlet-3.5.5-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff00e12102358292087274dfb1669132387ff6e7920ebf9d85f4826ce0d3a56", size = 622819, upload-time = "2026-08-10T13:40:46.562Z" },
{ url = "https://files.pythonhosted.org/packages/3c/78/52de4f7ac9152ad1dbc8f437895c1e573d30ee1e1427e0f91a280e2417b6/greenlet-3.5.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:523bb8e27614d77101ea7a8cf59f8d91219b72d5c29f6a038c92b50828bfa8d0", size = 1582164, upload-time = "2026-08-10T14:15:02.645Z" },
{ url = "https://files.pythonhosted.org/packages/f6/ff/c3855a00c2417e8f61c7b9f0bc4f8f599c6928f5c15681ad251e78a91e05/greenlet-3.5.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f1e2db190db51c17433eee424803818cf0670bf049d9cfe0dd07be111d1aa7c4", size = 1648807, upload-time = "2026-08-10T13:40:27.471Z" },
{ url = "https://files.pythonhosted.org/packages/70/21/d29ff6a79dbd1ec1fe74c155d25d4c5a85e221d6b9ffc2cc7a709e7c8c39/greenlet-3.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:740e544169527b82695ce76af2f7ad6f030904658f2f3921a1d245771fb88cfc", size = 322832, upload-time = "2026-08-10T13:28:10.85Z" },
{ url = "https://files.pythonhosted.org/packages/4e/a3/07297917485ee2ca85bc3c8dc6ed85ad3fffcf424047fba62671dba68e97/greenlet-3.5.5-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:be63afcbbccfad3dd95a1ba12ada84dab2ef32031973d80b5b92df67fa763a61", size = 294165, upload-time = "2026-08-10T13:25:17.987Z" },
{ url = "https://files.pythonhosted.org/packages/db/51/6f732f9314cda54c5fd48a7620c7160f4f286967e8045ad94b9d66ce80b7/greenlet-3.5.5-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a268024ce2d7d2b04694bf1594058981a9fa663d1df4b762dee499211ed7c1c", size = 613610, upload-time = "2026-08-10T14:14:33.829Z" },
{ url = "https://files.pythonhosted.org/packages/d8/c0/b27589e25d220289edcd4d582b2b17b83058d1a56d53d971b6ea1a34f10d/greenlet-3.5.5-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35cbb8bf55ace57fbccb4fb8622c4521713acd8691e77f4696d416ea7ca527da", size = 625481, upload-time = "2026-08-10T14:27:23.647Z" },
{ url = "https://files.pythonhosted.org/packages/51/2d/f2c928218ac52f26d7a2c188c171d1b7e728b23782cb3347e7b4fce1493a/greenlet-3.5.5-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cc6df89ec5302337adc9cf096221cbed2510fd444b0e0f1586cf0470740864", size = 624562, upload-time = "2026-08-10T13:40:48.064Z" },
{ url = "https://files.pythonhosted.org/packages/3e/4a/92fc51d5d35912f4f06eec037ba347985defd0be47463a010a325634d9d2/greenlet-3.5.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d9b454c5fc48aeaa7c4337813dbf513a6870468e426438a04d922c6d0fe63db", size = 1584909, upload-time = "2026-08-10T14:15:04.343Z" },
{ url = "https://files.pythonhosted.org/packages/ac/58/ed98b80ac5738c149a5258544843c45601ade1fd70f61740cdaead6351b3/greenlet-3.5.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03551ed792cb1b4fc0277a0c60dfd8c343894a0ba06fe60dcd22f568b433da39", size = 1651184, upload-time = "2026-08-10T13:40:28.879Z" },
{ url = "https://files.pythonhosted.org/packages/d8/be/b582ceb80cefdf9d8da34078714e4b12b3d16f509dee0f65e40a5cc8fc7d/greenlet-3.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:ab3df3dffb58bf70564e93a5cec7941e4d9faa5a36cc4234a10d3131afe04f53", size = 323280, upload-time = "2026-08-10T13:26:07.495Z" },
{ url = "https://files.pythonhosted.org/packages/4d/18/5313c4c58598c38b0373c013e4ff2b3e6d258aaaa338f373335ebecdaddd/greenlet-3.5.5-cp311-cp311-win_arm64.whl", hash = "sha256:2b70a766135540c472ac1393d57c2e1b4a2eb85bf526a1e41e6d096173a8cee5", size = 307785, upload-time = "2026-08-10T13:28:34.874Z" },
{ url = "https://files.pythonhosted.org/packages/2e/7e/9ecd0285e3153532ae07aeb88063c43c72b4221cf0d4d123b02f3682e3ff/greenlet-3.5.5-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:49520f0c95a48b42cf55414b8e8479beb274ea70431afc33e3f79903c71f4380", size = 295809, upload-time = "2026-08-10T13:25:34.023Z" },
{ url = "https://files.pythonhosted.org/packages/35/73/60e4bbcc89252037b18087f2ec16405d5b2d5be42dde191bbf3667e96102/greenlet-3.5.5-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:55272212cbc5f43d1d723725ab931f1939969b7e9523882ca58b55061769d053", size = 611910, upload-time = "2026-08-10T14:14:35.18Z" },
{ url = "https://files.pythonhosted.org/packages/a4/17/cd5134be659cd4a443e7a61ae670dabec165a814c51162916d637b6dd38e/greenlet-3.5.5-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655bca754a2ef4efcb0eb48a94d3f4593536d0f3d48f8ed44343c01d16a92f95", size = 624198, upload-time = "2026-08-10T14:27:25.229Z" },
{ url = "https://files.pythonhosted.org/packages/78/ac/5c5b959999b6f09c3026b5dfe171575bc3121c5236ce74f495096f25b203/greenlet-3.5.5-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:147b25a42e5ca5be3d42356e8f608b37af715a1c196e9bf9d1627f3341adfe1d", size = 621439, upload-time = "2026-08-10T13:40:49.391Z" },
{ url = "https://files.pythonhosted.org/packages/c8/8b/6acf112ed8aee499f25b4d6949820fb02ac950ff9c1f3d793bd5be0599f2/greenlet-3.5.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:27493374cff1d1b7919dc8126547f2aea582737e3046147b434b1e12de56389b", size = 1581342, upload-time = "2026-08-10T14:15:05.653Z" },
{ url = "https://files.pythonhosted.org/packages/b8/d7/734e5f198888876b42d7616ff6644c075baf6b8a2412deadd6b0e1b8b20c/greenlet-3.5.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:12e2ee66c2aba86133f10fd99d6a8856c6d351ffb7be0e4d52ef2cc5fbb705b2", size = 1645744, upload-time = "2026-08-10T13:40:30.353Z" },
{ url = "https://files.pythonhosted.org/packages/de/30/1f42b88dc587b5899ee50616ad56ee40cafaf225df4fb829f10183c62a5c/greenlet-3.5.5-cp312-cp312-win_amd64.whl", hash = "sha256:49ddacd36af37735fab103846f4ee4d18a492dde72730d1699c0c8ebe30d9f18", size = 324171, upload-time = "2026-08-10T13:28:44.472Z" },
{ url = "https://files.pythonhosted.org/packages/76/e5/4dee4d8d2e603fe5fdd7b444e63219f7b9bd852c60c6214511c7157cbe88/greenlet-3.5.5-cp312-cp312-win_arm64.whl", hash = "sha256:5f1b1ff4828cdc1aba4266aff814085d04a1d07959287219af021b838b265d52", size = 308362, upload-time = "2026-08-10T13:26:46.839Z" },
{ url = "https://files.pythonhosted.org/packages/fb/3d/8cef5f724ec0d4add2af8961d504535ec60c3cca9e464f6d03bdba29d85b/greenlet-3.5.5-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:b79fd2a5bc099b5e744f34c4c9a58954a5f4cb7529fb4b6e8446057d61b6edaa", size = 294730, upload-time = "2026-08-10T13:27:51.206Z" },
{ url = "https://files.pythonhosted.org/packages/88/4b/8e7aa3f514273aecff30a16ab1bac09ff54cfc7e6860fdd8058c37ff2499/greenlet-3.5.5-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:634cf15a233a949136879dd388e25d3296e16f3f1e217d2456797b8579ebc6ed", size = 614536, upload-time = "2026-08-10T14:14:36.589Z" },
{ url = "https://files.pythonhosted.org/packages/85/48/4e95e9dd5a8a397dc6a6345dd7f1935113d0fca4f85e89d3976da9cd988d/greenlet-3.5.5-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:499adea519f748407fc6806d20eedabac2884fd73b9f38d81236e190ba20dfef", size = 626924, upload-time = "2026-08-10T14:27:27.048Z" },
{ url = "https://files.pythonhosted.org/packages/89/5d/398a1c71fa7a277deeb376c999979de6786f08fc2d5747a0b9d6e11738dd/greenlet-3.5.5-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2eabb980975cba5b93a95f6f69287d05fc05ac955bfd6a320a7c083eeb52c0b0", size = 623906, upload-time = "2026-08-10T13:40:50.501Z" },
{ url = "https://files.pythonhosted.org/packages/04/1b/745450fc5ea9e0cb17d840d248f284db3363de736d362c7d2d883e3eadba/greenlet-3.5.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:03115c2e0a371999bf8ae616aa8d653f96641d4705c457aebaa187276e9f7537", size = 1581430, upload-time = "2026-08-10T14:15:06.853Z" },
{ url = "https://files.pythonhosted.org/packages/d4/29/d51b296e3191bb15d3d81ec375af1909e4466c0f395d744ed475801798a9/greenlet-3.5.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4441153ffba21b90d3ca89fe3d31f5c093ae6c0bf0cfdfc98f54cde22f95b62e", size = 1645684, upload-time = "2026-08-10T13:40:32.133Z" },
{ url = "https://files.pythonhosted.org/packages/12/63/369f1a1625e64e9e31df3963c6044056e3fdfa3fa3fdba3c54ffefa6e987/greenlet-3.5.5-cp313-cp313-win_amd64.whl", hash = "sha256:95c5b1f4b3a193f8a0c2de4bfdcb48d119f7f1063941f1de1f2168051b3e52dd", size = 324075, upload-time = "2026-08-10T13:26:58.974Z" },
{ url = "https://files.pythonhosted.org/packages/45/78/649cb5c09d4d81f6dd1444e75474a7206784743283a21d24171562ac4899/greenlet-3.5.5-cp313-cp313-win_arm64.whl", hash = "sha256:1af90aa4bc129883b340cdd6957a3bc74f60528a4993bbd1f53aaebe1d9981cc", size = 308260, upload-time = "2026-08-10T13:27:50.795Z" },
{ url = "https://files.pythonhosted.org/packages/7f/8c/080e881fa2be95ff1ddbd6994b2bab3b1a78df3b3fcab39306011764fcc7/greenlet-3.5.5-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:d4a389a852e392a6366058651a20fa5ba40d979865aa81bea2ccbdc44805070d", size = 295309, upload-time = "2026-08-10T13:26:03.032Z" },
{ url = "https://files.pythonhosted.org/packages/25/cc/0ac614e6586c0e42d4cc281a5819150f4f43685744a4c5ff77139286409d/greenlet-3.5.5-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70b157cd319873e8b544ddc2de158f55bbd0a9b0218c8ce9332039801518e328", size = 661185, upload-time = "2026-08-10T14:14:37.867Z" },
{ url = "https://files.pythonhosted.org/packages/5e/b9/6808725354be8ad305dfe5172377664fc9642d4fc043be246b3314cf4482/greenlet-3.5.5-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8bdfd1424abcf26832961e766570cae79efdb9599d709088c9cb6ef82b194926", size = 673419, upload-time = "2026-08-10T14:27:28.652Z" },
{ url = "https://files.pythonhosted.org/packages/42/2e/40c509967da7f254680826a2fa0dd22138ec79946c70b97542d74cde8b43/greenlet-3.5.5-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:182de51c6b572a705f2fafaab2e783bcf7d2760940229dfe73086cbae037af3e", size = 670822, upload-time = "2026-08-10T13:40:51.833Z" },
{ url = "https://files.pythonhosted.org/packages/2d/22/c3c2eee4a8fe191d6d1d183086c56133d646024e3d70bfd414829f64560b/greenlet-3.5.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8fec3f165dfe332e490c3247c0f6c23b0bfc45f06496ad7f00ddb00e3d35e4dc", size = 1628469, upload-time = "2026-08-10T14:15:08.11Z" },
{ url = "https://files.pythonhosted.org/packages/f7/87/25babd09b94cb1f03e71db815fde463f0262e40cfbd953d58a8d77311351/greenlet-3.5.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c6ce25fee6cabc8bf22cb8b52e642cbb821be5b9aec8094d07ff03378141b8e9", size = 1691952, upload-time = "2026-08-10T13:40:33.502Z" },
{ url = "https://files.pythonhosted.org/packages/2e/3d/5cc9701117ea4dc0eb7bf1f4f9b7888a6e2e5277ddfae095805ace50f2b6/greenlet-3.5.5-cp314-cp314-win_amd64.whl", hash = "sha256:7dffc5c859fe6059974df1e37d7923d654a83e2ae18fdd616994270e001115e1", size = 327458, upload-time = "2026-08-10T13:27:02.868Z" },
{ url = "https://files.pythonhosted.org/packages/a7/6b/594fa2de7fae7629168a404a4305d7d7e31a5742c50a801b1839543cb93d/greenlet-3.5.5-cp314-cp314-win_arm64.whl", hash = "sha256:5e2afcfc4d4305dd715809b03da5cbe437c8984f61d8917751eb5fe4aefa3e07", size = 311146, upload-time = "2026-08-10T13:27:25.046Z" },
{ url = "https://files.pythonhosted.org/packages/24/e0/50cd600b469e5734c72709b6b1838b6bc63f307b573c772c3132d6ecfe92/greenlet-3.5.5-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:0e5a7de979d764aea1f5b6e95cf92b5b37741b9823702041f34b126e7f690277", size = 305471, upload-time = "2026-08-10T13:26:20.568Z" },
{ url = "https://files.pythonhosted.org/packages/75/a3/77acd66dfc6387b5219b2080806c0cabb73c10eb1bb44b413c40a62015ba/greenlet-3.5.5-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fef01bd457f11fc158b130ca0027a3c365693280e8e231b65bdaf57999f39f5b", size = 672470, upload-time = "2026-08-10T14:14:39.058Z" },
{ url = "https://files.pythonhosted.org/packages/b9/71/0d178142dca3ec19f46fb2212ae73d30ad53b9d548dc64804086033a7089/greenlet-3.5.5-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5173a72310725a74afc82c164f0e52cb8ad0de62f2bb623f24f6c0cc07d80272", size = 679973, upload-time = "2026-08-10T14:27:30.072Z" },
{ url = "https://files.pythonhosted.org/packages/6e/31/46eb8567302eaf787abf88d09df014e14ae3baf460af1b8b0efdbd3efcd5/greenlet-3.5.5-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44f08341873200ba8a60a8bc14ace3d91f1754f7fa7bc66157714a8cd420a476", size = 676634, upload-time = "2026-08-10T13:40:53.004Z" },
{ url = "https://files.pythonhosted.org/packages/a3/e9/b88bbf5b29970cb84172dc2c32aa3e5e579ceb94c808e81c826454138850/greenlet-3.5.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d246c0db9a2513cd45f019ba178ea4d4d4705bd210ee465e2c15d76a1ab13874", size = 1637320, upload-time = "2026-08-10T14:15:09.317Z" },
{ url = "https://files.pythonhosted.org/packages/6d/8c/7631ed29cc6f0392f11830076e172ce4885e70b0bc2c1bce1731176d4b4e/greenlet-3.5.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:72507285b5caa1d17904a3f7c322ca780823a54170a0e04ec3f37bcc60d4db71", size = 1697412, upload-time = "2026-08-10T13:40:34.924Z" },
{ url = "https://files.pythonhosted.org/packages/da/0f/f7dd935f9c4cb1be49098770587f54d8a78518e55c89bce86c4fb4109057/greenlet-3.5.5-cp314-cp314t-win_amd64.whl", hash = "sha256:7805655781fb8f28a55d05fe57ed61f5f10f1892fb587673e3bb5264f28041f0", size = 331514, upload-time = "2026-08-10T13:29:20.611Z" },
{ url = "https://files.pythonhosted.org/packages/b7/e5/681b01f8fbc1b55232822f99e8f8afeb78a55a7c76a7bf9dbdc7ccb03a6d/greenlet-3.5.5-cp315-cp315-macosx_11_0_universal2.whl", hash = "sha256:c0db80fcd5b8aece93f66c64f78a786bbb6b96c5fe63ef5a5a4581ecf8bab206", size = 295975, upload-time = "2026-08-10T13:28:45.985Z" },
{ url = "https://files.pythonhosted.org/packages/11/f2/69b488cd9e7267bf4b0fe8cdebf25d8d6df680d21bdf41150d23e23d6652/greenlet-3.5.5-cp315-cp315-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b241c32f912ada659808d68e308c568baf577eebf757d15471472de0c18cfad", size = 666823, upload-time = "2026-08-10T14:14:40.222Z" },
{ url = "https://files.pythonhosted.org/packages/84/d4/d5bc2fdebbdda0c94555925ba79948b8395d75a7f6a36cc85dce5bab9f11/greenlet-3.5.5-cp315-cp315-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ef6a08349401d8eaf3cb12688ac8557de95788556b8631ef17555a4a173022c0", size = 677613, upload-time = "2026-08-10T14:27:31.543Z" },
{ url = "https://files.pythonhosted.org/packages/bd/93/542d8a3a90f3b35c6ad8bf7e56a03010287f2cafa289a5b7985b5207db39/greenlet-3.5.5-cp315-cp315-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f2e3d061b8e13aec2f0441689b3c71b244a20e5d274a52cb0f7e31bd1d139552", size = 675930, upload-time = "2026-08-10T13:40:54.205Z" },
{ url = "https://files.pythonhosted.org/packages/52/b5/89c9f2e8460d71101037d47a1feed11928615a5edd42370be290e0657eeb/greenlet-3.5.5-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:9ab5f5b93655e77fe0d6c2dfd22b5eac751bb1f876d8ec21761b7c1fb9266007", size = 1633878, upload-time = "2026-08-10T14:15:10.693Z" },
{ url = "https://files.pythonhosted.org/packages/b8/60/297de93f3b02ac78a5e04d32bb8bbe3080f4a73d8ed95016561463b70618/greenlet-3.5.5-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:f0e5a21bd4452a88cf032fc43c4a5b307ab1380eacb63b5988f9c0317885e773", size = 1696597, upload-time = "2026-08-10T13:40:36.252Z" },
{ url = "https://files.pythonhosted.org/packages/18/25/54c6eaff4f337fb670215e89eb2d00d9499487b658e709d4b477be4a342e/greenlet-3.5.5-cp315-cp315-win_amd64.whl", hash = "sha256:469dbb0a78625642f4a626cfd0c6e8bccc0385b5e49189b6308bbe849ec88a8e", size = 327700, upload-time = "2026-08-10T13:28:06.752Z" },
{ url = "https://files.pythonhosted.org/packages/67/67/857e88a36301caa0e029870132c2478bd55d896630321432afab03a3115f/greenlet-3.5.5-cp315-cp315-win_arm64.whl", hash = "sha256:2d57406c3efd32d7a81e17a674314e8bd00792cdab49ea3228a49aa1bfb2e769", size = 311750, upload-time = "2026-08-10T13:34:08.815Z" },
{ url = "https://files.pythonhosted.org/packages/10/e2/3144c0a116067ac1e30457b0139a94d60d1d36a86e015de68e9ac87cb3bc/greenlet-3.5.5-cp315-cp315t-macosx_11_0_universal2.whl", hash = "sha256:68184dfcf50ccaa8e864770fe0633a7e27250ea9329f8192ef47ee9ecfd78e1c", size = 306387, upload-time = "2026-08-10T13:27:00.897Z" },
{ url = "https://files.pythonhosted.org/packages/5c/a1/cb4223a7e9b9f43b8807e8eb212358bfe2dfaa174a9ea2889eb1714dcba2/greenlet-3.5.5-cp315-cp315t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ec0dc0e59dc9c61af5c47348365ccbbd7addfafe0a93b00336ff3da2907bdc6", size = 676472, upload-time = "2026-08-10T14:14:41.417Z" },
{ url = "https://files.pythonhosted.org/packages/9e/cd/a154b4498e5d8f12ada291cfb3b8d596eadde2177f5bf09a9be699d2a446/greenlet-3.5.5-cp315-cp315t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e604f58e35833fc46ef20302bcb314dddbfd3fcf33a4f936216d51dd678d63ae", size = 684238, upload-time = "2026-08-10T14:27:32.946Z" },
{ url = "https://files.pythonhosted.org/packages/bf/bb/b0031d260c2968a3c87deebc51d80c64e499377f993aafe06ee3b7488cc2/greenlet-3.5.5-cp315-cp315t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:40239b5384f96da3963585cc6d7eaa9b56f8ae67e8d92cc82dd9e202fc847de3", size = 681246, upload-time = "2026-08-10T13:40:55.402Z" },
{ url = "https://files.pythonhosted.org/packages/9a/07/da554b71ab88e649da146e1065d86a48a5c5d92e50ab74ef41b504aa7f56/greenlet-3.5.5-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:a1eaccf5c3a1d3e46dead602c72e6836731e8e245c9de6a27764567b6b62d4c0", size = 1642735, upload-time = "2026-08-10T14:15:11.92Z" },
{ url = "https://files.pythonhosted.org/packages/78/76/26a3782a051677668af9d92beaa47cd87ba9dd5072f762961144a03dd4c6/greenlet-3.5.5-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:19e4e026fe20691f333b8eb1a3bc9625eceba8c3f9d62ec5a6f8581afbc6b5a5", size = 1700925, upload-time = "2026-08-10T13:40:37.656Z" },
{ url = "https://files.pythonhosted.org/packages/28/d9/fe7baf4190c2ae71f267efb9de21b3172bb35bc0ed1ef53dd6027d658e33/greenlet-3.5.5-cp315-cp315t-win_amd64.whl", hash = "sha256:712aee154f648bde84634654bb38bb78c69ac640c37a45c9effed800735049d8", size = 331829, upload-time = "2026-08-10T13:26:48.851Z" },
{ url = "https://files.pythonhosted.org/packages/df/af/419a4e383bd600858a9b67e9b280a60fdc383ee3f2fe5b6c0c1ef04e74d1/greenlet-3.5.5-cp315-cp315t-win_arm64.whl", hash = "sha256:7f049911ee81a16a03c33d5450d8d5867d27f596ca5fb201b86f4524e874468b", size = 315093, upload-time = "2026-08-10T13:29:34.949Z" },
]
[[package]]
name = "groovy"
version = "0.1.2"
@@ -1510,7 +1607,7 @@ name = "importlib-metadata"
version = "9.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "zipp", marker = "python_full_version < '3.12'" },
{ name = "zipp" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a9/01/15bb152d77b21318514a96f43af312635eb2500c96b55398d020c93d86ea/importlib_metadata-9.0.0.tar.gz", hash = "sha256:a4f57ab599e6a2e3016d7595cfd72eb4661a5106e787a95bcc90c7105b831efc", size = 56405, upload-time = "2026-03-20T06:42:56.999Z" }
wheels = [
@@ -1842,6 +1939,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b4/de/88b3be5c31b22333b3ca2f6ff1de4e863d8fe45aaea7485f591970ec1d3e/linkify_it_py-2.1.0-py3-none-any.whl", hash = "sha256:0d252c1594ecba2ecedc444053db5d3a9b7ec1b0dd929c8f1d74dce89f86c05e", size = 19878, upload-time = "2026-03-01T07:48:46.098Z" },
]
[[package]]
name = "mako"
version = "1.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markupsafe" },
]
sdist = { url = "https://files.pythonhosted.org/packages/2a/12/b5fa2353e2754cd67fb9f83793fa48ff42c213a5da7e719869d2301f6ab8/mako-1.4.1.tar.gz", hash = "sha256:d7904710b662996425a21627710c4777c45053146942cf8a7aebf757c92b8c27", size = 410165, upload-time = "2026-08-05T06:10:56.611Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a5/54/12ed58d458474aaab5c3d180173e745a4fe131bb330370596876d19ff60f/mako-1.4.1-py3-none-any.whl", hash = "sha256:a359d9a94a541213958742b2698d0a7757bb83551767bc468a74b9905aba9617", size = 80010, upload-time = "2026-08-05T06:10:58.248Z" },
]
[[package]]
name = "markdown-it-py"
version = "4.2.0"
@@ -1906,15 +2015,15 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "cycler", marker = "python_full_version < '3.11'" },
{ name = "fonttools", marker = "python_full_version < '3.11'" },
{ name = "kiwisolver", marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "packaging", marker = "python_full_version < '3.11'" },
{ name = "pillow", marker = "python_full_version < '3.11'" },
{ name = "pyparsing", marker = "python_full_version < '3.11'" },
{ name = "python-dateutil", marker = "python_full_version < '3.11'" },
{ name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" } },
{ name = "cycler" },
{ name = "fonttools" },
{ name = "kiwisolver" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
{ name = "packaging" },
{ name = "pillow" },
{ name = "pyparsing" },
{ name = "python-dateutil" },
]
sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" }
wheels = [
@@ -1980,8 +2089,8 @@ version = "3.11.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"(python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version >= '3.14' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version == '3.13.*' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.12.*' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform == 'win32')",
"python_full_version == '3.12.*' and sys_platform != 'linux' and sys_platform != 'win32'",
@@ -1989,16 +2098,16 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "cycler", marker = "python_full_version >= '3.11'" },
{ name = "fonttools", marker = "python_full_version >= '3.11'" },
{ name = "kiwisolver", marker = "python_full_version >= '3.11'" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" } },
{ name = "cycler" },
{ name = "fonttools" },
{ name = "kiwisolver" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "packaging", marker = "python_full_version >= '3.11'" },
{ name = "pillow", marker = "python_full_version >= '3.11'" },
{ name = "pyparsing", marker = "python_full_version >= '3.11'" },
{ name = "python-dateutil", marker = "python_full_version >= '3.11'" },
{ name = "packaging" },
{ name = "pillow" },
{ name = "pyparsing" },
{ name = "python-dateutil" },
]
sdist = { url = "https://files.pythonhosted.org/packages/49/64/f9a391af28f518b11ad45a8a712353c94a0aefce09d3703200e5c54b610a/matplotlib-3.11.1.tar.gz", hash = "sha256:69647db5746941c793d6e445a4cd349323ffb87d9cc958c2ad84a659b4832d30", size = 32612045, upload-time = "2026-07-18T03:39:46.63Z" }
wheels = [
@@ -2075,7 +2184,7 @@ name = "mlx"
version = "0.32.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mlx-metal", marker = "sys_platform == 'darwin'" },
{ name = "mlx-metal" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/df/38/a034159df4d21ef7b5721225a2c46092e20a2c627724f57be3e89fa7dbce/mlx-0.32.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:c6feb17e32160b70c7634aab925cf3f8c5c7bebbf99f227c48450478e1008af2", size = 562899, upload-time = "2026-07-07T17:55:25.157Z" },
@@ -2100,15 +2209,15 @@ name = "mlx-lm"
version = "0.31.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jinja2", marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "mlx", marker = "sys_platform == 'darwin'" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "protobuf", marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "pyyaml", marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "sentencepiece", marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "transformers", marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "jinja2" },
{ name = "mlx" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "protobuf" },
{ name = "pyyaml" },
{ name = "sentencepiece" },
{ name = "transformers" },
]
sdist = { url = "https://files.pythonhosted.org/packages/84/94/9a38d6b0c6fcca995b9136c94eb7da1e9c5165652edf228b96b29960fa7a/mlx_lm-0.31.3.tar.gz", hash = "sha256:61eb0e3ba09444f77f874aff295401d7ccd20b39495cbbce0c782a15474ce733", size = 304318, upload-time = "2026-04-22T07:37:27.922Z" }
wheels = [
@@ -2393,8 +2502,8 @@ version = "3.6.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"(python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version >= '3.14' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version == '3.13.*' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.12.*' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform == 'win32')",
"python_full_version == '3.12.*' and sys_platform != 'linux' and sys_platform != 'win32'",
@@ -2621,8 +2730,8 @@ version = "2.5.2"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"(python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version >= '3.14' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version == '3.13.*' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.12.*' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform == 'win32')",
"python_full_version == '3.12.*' and sys_platform != 'linux' and sys_platform != 'win32'",
@@ -2708,6 +2817,7 @@ dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "optuna" },
{ name = "pandas" },
{ name = "pyyaml" },
{ name = "rich" },
@@ -2771,6 +2881,7 @@ requires-dist = [
{ name = "matplotlib", specifier = ">=3.7" },
{ name = "mypy", marker = "extra == 'dev'", specifier = "==1.13.0" },
{ name = "numpy", specifier = ">=1.24" },
{ name = "optuna", specifier = ">=4.9,<5" },
{ name = "pandas", specifier = ">=2.0" },
{ name = "pytest", marker = "extra == 'dev'", specifier = "==9.1.1" },
{ name = "pytest-cov", marker = "extra == 'dev'", specifier = "==7.1.0" },
@@ -2800,6 +2911,26 @@ mlx = [
]
quality = [{ name = "mutmut", specifier = "==3.7.0" }]
[[package]]
name = "optuna"
version = "4.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "alembic" },
{ name = "colorlog" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "packaging" },
{ name = "pyyaml" },
{ name = "sqlalchemy" },
{ name = "tqdm" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f4/aa/05f5e3f662cc96a4c478fc3446b8ed6359825a2b504ecb614a9ac84e4a4d/optuna-4.9.0.tar.gz", hash = "sha256:b322e5cbdf1655fb84c37646c4a7a1f391de1b47806bbe222e015825d0a82b87", size = 485834, upload-time = "2026-06-01T06:23:30.424Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ab/f3/e5fcd5d9b15771ed6dc10e3a7eeddc672e418f4f4c4653d216cc1d857e2d/optuna-4.9.0-py3-none-any.whl", hash = "sha256:f52f3be6148654850c92a5860d398fd88ec6b2c84ab68d9c3d07dcff02e7afee", size = 425553, upload-time = "2026-06-01T06:23:28.804Z" },
]
[[package]]
name = "orjson"
version = "3.11.9"
@@ -3972,8 +4103,8 @@ version = "2026.6.3"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"(python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version >= '3.14' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version == '3.13.*' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.12.*' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform == 'win32')",
"python_full_version == '3.12.*' and sys_platform != 'linux' and sys_platform != 'win32'",
@@ -4169,10 +4300,10 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "joblib", marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "threadpoolctl", marker = "python_full_version < '3.11'" },
{ name = "joblib" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
{ name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" } },
{ name = "threadpoolctl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" }
wheels = [
@@ -4214,8 +4345,8 @@ version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"(python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version >= '3.14' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version == '3.13.*' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.12.*' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform == 'win32')",
"python_full_version == '3.12.*' and sys_platform != 'linux' and sys_platform != 'win32'",
@@ -4223,13 +4354,13 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "joblib", marker = "python_full_version >= '3.11'" },
{ name = "narwhals", marker = "python_full_version >= '3.11'" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "joblib" },
{ name = "narwhals" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
{ name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "threadpoolctl", marker = "python_full_version >= '3.11'" },
{ name = "threadpoolctl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fa/6f/37092bdb25f712817231799fc5674d8e704066a8a70c1d2d40517e18b4ab/scikit_learn-1.9.0.tar.gz", hash = "sha256:8833266989d3a5110178a9fae30783675460724d0e1efb13b14901d2c660c557", size = 7750767, upload-time = "2026-06-02T11:54:32.706Z" }
wheels = [
@@ -4274,7 +4405,7 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" }
wheels = [
@@ -4334,7 +4465,7 @@ resolution-markers = [
"python_full_version == '3.11.*' and sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
wheels = [
@@ -4406,14 +4537,14 @@ version = "1.18.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"(python_full_version >= '3.14' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform == 'win32')",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version >= '3.14' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.13.*' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform == 'win32')",
"python_full_version == '3.13.*' and sys_platform != 'linux' and sys_platform != 'win32'",
"(python_full_version == '3.12.*' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform == 'win32')",
"python_full_version == '3.12.*' and sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
{ name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" } },
]
sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447, upload-time = "2026-06-19T15:01:43.456Z" }
wheels = [
@@ -4481,8 +4612,8 @@ name = "secretstorage"
version = "3.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "jeepney", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "cryptography" },
{ name = "jeepney" },
]
sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" }
wheels = [
@@ -4640,6 +4771,55 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" },
]
[[package]]
name = "sqlalchemy"
version = "2.0.52"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3b/21/77b4c147963073040dc3c3a5cb7a8c3001a1893c0209432cb77f9df836aa/sqlalchemy-2.0.52.tar.gz", hash = "sha256:5e2d46356ac2ccb7d268ab6c2319ac6a2b42f1b8d5fd8bd3d46855cd82abee97", size = 9945637, upload-time = "2026-08-11T19:07:09.829Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a6/d7/e0354e7334d33ea2795db3ecbe2977026c05a1ecf8ba4b5953c329872453/sqlalchemy-2.0.52-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a7438774e1091192fc50a2bd8ceff5c596912d00ecd46587e88effdea7826101", size = 2172616, upload-time = "2026-08-11T20:58:21.078Z" },
{ url = "https://files.pythonhosted.org/packages/5b/64/98eef682e6946eb1b4195a9a2393db4662ebfcc89f823ae78b938765c3c0/sqlalchemy-2.0.52-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c1b7ed45bf87b214e0a9def9c2313949067efe6269db5ef18d542ee13250af7", size = 3279798, upload-time = "2026-08-11T21:00:03.535Z" },
{ url = "https://files.pythonhosted.org/packages/20/05/5b96afc1407c314347ad006b72bb251fb68ef84d05505ebf8a39bc47fcde/sqlalchemy-2.0.52-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:309cc8ba50fc5d2174189dfcd49cdf7aa711f8346afcff19f2642ae4fc449c14", size = 3277555, upload-time = "2026-08-11T21:05:47.932Z" },
{ url = "https://files.pythonhosted.org/packages/50/69/ce6776724511d1b5dd40477b08d6a5f0953a45375e092dfa852b1857732c/sqlalchemy-2.0.52-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2f9eccf8793c8c3f8dd2dfd11b9e400cb27d1d19370ef732b66017e212107822", size = 3231246, upload-time = "2026-08-11T21:00:05.184Z" },
{ url = "https://files.pythonhosted.org/packages/72/19/ab0cb9ccdafa2419c796ae62f8740aedb903f1e93bb326064b1a0147e458/sqlalchemy-2.0.52-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9255ceb65a80c1b001129060b63ee776a2e9c288be3b662be36dfbb888fffdcd", size = 3250763, upload-time = "2026-08-11T21:05:49.664Z" },
{ url = "https://files.pythonhosted.org/packages/41/f2/3c9b54b61bec4f493c0007dc9e2700c963c5b32667e21a506f1aca7a8115/sqlalchemy-2.0.52-cp310-cp310-win32.whl", hash = "sha256:2e15b1d1116a64fc399b8c2694a83f3e792fdc58df28514a81e1dc4f8cf22729", size = 2132169, upload-time = "2026-08-11T21:09:48.108Z" },
{ url = "https://files.pythonhosted.org/packages/b3/a1/934bb6cf543a398c72784d1fc777eb530559c16ebe1549fa7611e5989ce9/sqlalchemy-2.0.52-cp310-cp310-win_amd64.whl", hash = "sha256:11560064cc4696e772298b6221ede59e646386d9f2a85d549365473b972f7850", size = 2156289, upload-time = "2026-08-11T21:09:49.429Z" },
{ url = "https://files.pythonhosted.org/packages/6b/08/cc5f7627b92f1456bc0b5fb7e98af4600248abe422a44da0d17a3fe6a448/sqlalchemy-2.0.52-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0c3ce43907374889f3352bdcc6195c970148a2cb71574cd0237a5071a37fb6c", size = 2172460, upload-time = "2026-08-11T20:58:22.429Z" },
{ url = "https://files.pythonhosted.org/packages/ed/dc/9a2abad8bfc8fdcd38c64adc056aeefab7aaa96ecd32f5e8c140e6375f17/sqlalchemy-2.0.52-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a0d48c4b80717c61385b4e966e087c839a66cfd7b780641dcb428f4dba65608", size = 3355720, upload-time = "2026-08-11T21:00:06.746Z" },
{ url = "https://files.pythonhosted.org/packages/a8/73/e75597b5841043e3c74055d00d4feb53d9a49a5c89ba2450d2d9aab53597/sqlalchemy-2.0.52-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:938325a5373267afc53bfbe72983b20fbd64ca47842aac62433c3da1137ecff1", size = 3354394, upload-time = "2026-08-11T21:05:51.454Z" },
{ url = "https://files.pythonhosted.org/packages/12/25/410fbc6c2f1fa8310f4ef1b6847d47d0ac1c042c7b4e81eaaca063d030a9/sqlalchemy-2.0.52-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f8438a98d49424acf69d0d53c0a522951dfe49a6f2d86417fbb37ad3066ab43", size = 3306991, upload-time = "2026-08-11T21:00:08.603Z" },
{ url = "https://files.pythonhosted.org/packages/b2/ba/25ffd5c24681ea4b46e62c80ceca8200ce204de1773366321306cf3f608a/sqlalchemy-2.0.52-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4699dbb8d396d199e7e78fd4d525e3ad3d6008a9c8c0160b87e74c606c2c3736", size = 3327454, upload-time = "2026-08-11T21:05:53.368Z" },
{ url = "https://files.pythonhosted.org/packages/c2/f1/0f1b1d4800e51218e736a06ed55a3b2a59c257600bbaca7673bf13d2dbec/sqlalchemy-2.0.52-cp311-cp311-win32.whl", hash = "sha256:cef328349452ae152637df4d11ce5a0919ecdf0a363e16c830c3518ee33bde72", size = 2131248, upload-time = "2026-08-11T21:09:50.765Z" },
{ url = "https://files.pythonhosted.org/packages/7a/f0/04d2ac5ad66f3d31278f37064ed5f5ef3fe653f7bdaa67036663f223d186/sqlalchemy-2.0.52-cp311-cp311-win_amd64.whl", hash = "sha256:f1c850792a3b25a3ad74dade3f05e4f402cdebfea27438bcadafaa1617f77bcc", size = 2156943, upload-time = "2026-08-11T21:09:51.979Z" },
{ url = "https://files.pythonhosted.org/packages/e0/d5/1b77a026d161f98a08f11af1a5f6c47b98ee7c7e2648af525a1004826c78/sqlalchemy-2.0.52-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be8c49131665dfe2cc74c498aa1240ffb548d0fd901325dd11c2c7a18956f727", size = 2170940, upload-time = "2026-08-11T20:58:11.25Z" },
{ url = "https://files.pythonhosted.org/packages/54/bd/f444444adb37b5d53753fb1730ee7a421628e2e3b756c4da461af7e6394a/sqlalchemy-2.0.52-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b2d9e507a458832adcfbd8af6e2036ddf069b7710b799448542ebccae2dceee", size = 3383415, upload-time = "2026-08-11T21:02:38.534Z" },
{ url = "https://files.pythonhosted.org/packages/be/57/2eadf93a552568c57e8680b7e58bb5e9770d80942a1bdbaf4f2f63f0d7c8/sqlalchemy-2.0.52-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8738008376d22f30f411ea3efecf39b51110b6996d80bb73786f30bcfdd5fd3b", size = 3398577, upload-time = "2026-08-11T21:16:59.092Z" },
{ url = "https://files.pythonhosted.org/packages/15/c3/2887cf9dd111d1fbf05d22165b404c221ef43e029f7a2695e7302f27a7cc/sqlalchemy-2.0.52-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37a4d548327b6cab9c7d8cdb4e0e82feabee0110c4d150059068e2d1cfbd99ee", size = 3328225, upload-time = "2026-08-11T21:02:40.183Z" },
{ url = "https://files.pythonhosted.org/packages/02/0f/466bdf9e1feeeef5587f868c187d8687e21ff8c85b1775e9041130181132/sqlalchemy-2.0.52-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e49f51a5d59857a7a0dcaf9469febf7197d9394bd88f00d69c2c4e848112cdbf", size = 3357374, upload-time = "2026-08-11T21:17:01.076Z" },
{ url = "https://files.pythonhosted.org/packages/22/20/5c2b4583904af4173076dda1c9e53c9e2ffc7a702d2efde0216bbacbf7cb/sqlalchemy-2.0.52-cp312-cp312-win32.whl", hash = "sha256:afda3ec521d0517d0de783fc70030775841900896d832de5bbd066549290470e", size = 2129366, upload-time = "2026-08-11T21:14:50.991Z" },
{ url = "https://files.pythonhosted.org/packages/ed/06/543dab8ef62d4e9fb96fb31a30c2b8b14a8763bccf48d428294d6b3041c0/sqlalchemy-2.0.52-cp312-cp312-win_amd64.whl", hash = "sha256:2d5e53e36e37129fe0be8b9d08b6e4052c10a963ee6cda56c8c10dcc194b99ca", size = 2157344, upload-time = "2026-08-11T21:14:52.453Z" },
{ url = "https://files.pythonhosted.org/packages/7f/18/e30c6fe1eca1bf34a39fbdd6066121cc9974c850faf6f349eac563697a26/sqlalchemy-2.0.52-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2eb3c6a64b1bfe6704777cfd504e7b8ad093a5f3e03ce67663a5e6742f294e43", size = 2167724, upload-time = "2026-08-11T20:58:12.679Z" },
{ url = "https://files.pythonhosted.org/packages/d0/56/2e17d161a4f7ecc1c2ffb93e607b4e1898bb551b451b283235acb8f6ce47/sqlalchemy-2.0.52-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:923bb183c1dc64fdf7b717965e3d59938ec4f8b8710b419a21ce403e5da9a9e1", size = 3321189, upload-time = "2026-08-11T21:02:41.932Z" },
{ url = "https://files.pythonhosted.org/packages/cf/b8/8490916e893f3f8d74dc9cc54c078619364999dee37047a188e73abbc852/sqlalchemy-2.0.52-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:651d6d8782e80679e6151707c7b490834d46ada526328895abf567f25e63d29c", size = 3338185, upload-time = "2026-08-11T21:17:02.597Z" },
{ url = "https://files.pythonhosted.org/packages/8b/f7/752cc8ee453da222829b3f5c4613614bf750d97429363b70414fa10478e4/sqlalchemy-2.0.52-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b08cddb8989775e3c88799d86704bdfc3ee6e9846118201aa5997f16f27e3a15", size = 3271698, upload-time = "2026-08-11T21:02:43.963Z" },
{ url = "https://files.pythonhosted.org/packages/51/e6/074ade0c07b9e4c8e8bca46820320ed94df9702afdb6f2af06623068d2e6/sqlalchemy-2.0.52-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab66fa9618269390d4dfa222f2f2f88f7bc4bf5da13905131b818217db7e8057", size = 3308936, upload-time = "2026-08-11T21:17:04.172Z" },
{ url = "https://files.pythonhosted.org/packages/66/07/557c0d04716705599227945ac14e0a17ad0338e899f37d8c2ddff4dcc663/sqlalchemy-2.0.52-cp313-cp313-win32.whl", hash = "sha256:c63bda077685c85ca513286547a531ba57e7a68cf0a7ed3bafcc2bbd18896f4d", size = 2127308, upload-time = "2026-08-11T21:14:53.879Z" },
{ url = "https://files.pythonhosted.org/packages/96/4e/226eda27654318ce525d043025221f689abef883da2c7126f9065121618c/sqlalchemy-2.0.52-cp313-cp313-win_amd64.whl", hash = "sha256:9876b09b9f1ce7398b0ffece585c0a911244c53191187341f6bcae640e133751", size = 2153876, upload-time = "2026-08-11T21:14:55.527Z" },
{ url = "https://files.pythonhosted.org/packages/d5/f5/71cb30af58c9b80a4e1fac0b73bb48f86d497a774a6a2eb6d2f1e657bb73/sqlalchemy-2.0.52-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:410d52be41d17f1a236d19520fbe776257dc16516ed06bd16d433311842aefd9", size = 2169537, upload-time = "2026-08-11T20:58:13.855Z" },
{ url = "https://files.pythonhosted.org/packages/4c/93/d07ebd645d1b07b6b5ed63450a70f063a346a7e0f2c8810daf2e532400cb/sqlalchemy-2.0.52-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dfe9ce533dbe4d0a2ae1486546619bd30b76bcd670539a44d910361376175f5e", size = 3319606, upload-time = "2026-08-11T21:02:45.829Z" },
{ url = "https://files.pythonhosted.org/packages/ae/5c/290c84c7c2566ecd3b65baaae0fddec9bc33b033b398a06123bb86fbfc6e/sqlalchemy-2.0.52-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:812bae5138bfc0aa46fb0686da0fc7f581f68e2bbb05bc24c3713bebaedd1437", size = 3323642, upload-time = "2026-08-11T21:17:05.675Z" },
{ url = "https://files.pythonhosted.org/packages/13/f5/2cc160590ca49173359557880b92a0572293ccb899e8f6cedf150c5a3ddf/sqlalchemy-2.0.52-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:50bff43b632a56fbf5ed9afdd76307e1512b62051bcd5afb341ae67205bbb6c8", size = 3268125, upload-time = "2026-08-11T21:02:47.649Z" },
{ url = "https://files.pythonhosted.org/packages/35/f3/ea8933fc9f7d1353e9c2ff9965eae687c4cef181120574591ed2fa0633e1/sqlalchemy-2.0.52-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:49565daf5af554f538e23aef1fc81a95a4e49658f152285e45c02f5fc44f04cd", size = 3289516, upload-time = "2026-08-11T21:17:07.267Z" },
{ url = "https://files.pythonhosted.org/packages/45/67/05cf86541c1e1716fca1e4a996954a439cd74501707cda607fb7cb02ef50/sqlalchemy-2.0.52-cp314-cp314-win32.whl", hash = "sha256:ab9da41e61b9979b910499d633b241df20c51ee5037e5405b11c2faac3cbe1a2", size = 2130249, upload-time = "2026-08-11T21:14:57.273Z" },
{ url = "https://files.pythonhosted.org/packages/96/d7/8ac6ffa1e36169e762ef65bd835046abb2251b1bc17f8f6708e14ed8d31f/sqlalchemy-2.0.52-cp314-cp314-win_amd64.whl", hash = "sha256:a593db51b3bae75db17a5738ad5f992244b3a03863f83c28117ee482c6a3f76d", size = 2156718, upload-time = "2026-08-11T21:14:58.667Z" },
{ url = "https://files.pythonhosted.org/packages/dc/4b/e01a737eef378e734cc6394a82248a6ce13b167dfa36c731075ce9fc9c64/sqlalchemy-2.0.52-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1e61d08bdf4ee2f41024569e3400de7d6734ba498144766b11260936ccfa582", size = 2190344, upload-time = "2026-08-11T19:53:21.393Z" },
{ url = "https://files.pythonhosted.org/packages/b3/3f/3582293d1e185e71d19d7c731c3e2ee20ba21981c4a1115c0806c1f62120/sqlalchemy-2.0.52-py3-none-any.whl", hash = "sha256:3b81b8363a919ce53453591cdb93702e6bd54ade6c4fa2f468fc053baee5ed89", size = 1950700, upload-time = "2026-08-11T20:47:21.603Z" },
]
[[package]]
name = "starlette"
version = "1.6.0"
@@ -4805,14 +4985,14 @@ resolution-markers = [
"python_full_version < '3.11' and sys_platform != 'linux' and sys_platform != 'win32'",
]
dependencies = [
{ name = "filelock", marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "fsspec", marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "jinja2", marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "setuptools", marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "sympy", marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ name = "typing-extensions", marker = "sys_platform != 'linux' and sys_platform != 'win32'" },
{ 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 = "setuptools" },
{ name = "sympy" },
{ name = "typing-extensions" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/e7/19894fdb51c7dbaf94f5a79bb0871da0992e8e4241e579cb006da46d2e58/torch-2.13.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:94f0de129916f77b8dc2c7a8eff644cfeddfe59e39c9f55e9f6e17543410281d", size = 111178962, upload-time = "2026-07-08T16:05:49.855Z" },
@@ -4835,14 +5015,14 @@ resolution-markers = [
"(python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')",
]
dependencies = [
{ name = "filelock", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "fsspec", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "jinja2", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform == 'win32')" },
{ name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and sys_platform == 'linux') or (python_full_version >= '3.11' and sys_platform == 'win32')" },
{ name = "setuptools", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "sympy", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ name = "typing-extensions", marker = "sys_platform == 'linux' or sys_platform == 'win32'" },
{ 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 = "setuptools" },
{ name = "sympy" },
{ name = "typing-extensions" },
]
wheels = [
{ url = "https://download-r2.pytorch.org/whl/cpu/torch-2.13.0%2Bcpu-cp310-cp310-linux_s390x.whl", hash = "sha256:0555fde6108ca90247ae33d4e1237cbae475c86a223bb2f0f91d9addf1f611bd", upload-time = "2026-07-08T19:27:11Z" },