From a5f10f29e481f4f359b118ef61e2757203016ebd Mon Sep 17 00:00:00 2001 From: katari Date: Fri, 7 Aug 2026 10:12:56 +0900 Subject: [PATCH 01/17] [fix] reject unsupported quant_methods (MDBF/OneBit) in CPU/GGUF export MDBF fell through to the dequantize fallback and exported randomly initialised weights without error; an explicit mode= bypassed the unsupported guard, including OneBit. Add mdbf to the unsupported set and move it to checkpoint.py as UNSUPPORTED_METHODS so dequantize_to_hf rejects it too, guard on plan["path"] instead of the resolved mode, and raise before save_pretrained when load_state_dict leaves any .weight/.bias unsourced. --- onecomp/cpu/export/auto.py | 29 +++++--- onecomp/cpu/export/checkpoint.py | 9 +++ onecomp/cpu/export/dequantize.py | 60 +++++++++++++++- tests/onecomp/cpu/test_export_routing.py | 92 +++++++++++++++++++++++- 4 files changed, 175 insertions(+), 15 deletions(-) diff --git a/onecomp/cpu/export/auto.py b/onecomp/cpu/export/auto.py index 7cbde7c2..314d6d40 100644 --- a/onecomp/cpu/export/auto.py +++ b/onecomp/cpu/export/auto.py @@ -21,7 +21,8 @@ (see :mod:`onecomp.cpu.export.rotation`) so the GGUF runs correctly with no online operation. -OneBit (``quant_method == "onebit"``) is intentionally unsupported. +OneBit and MDBF are rejected up-front, for every ``mode``; see +``UNSUPPORTED_METHODS`` in :mod:`onecomp.cpu.export.checkpoint` for why. Copyright 2025-2026 Fujitsu Ltd. @@ -35,6 +36,7 @@ from typing import Dict, Optional from onecomp.cpu.export.checkpoint import ( + UNSUPPORTED_METHODS, configured_bit_widths, load_quant_config, needs_mixed_export, @@ -43,18 +45,15 @@ logger = getLogger(__name__) -# Methods we will not export (no faithful GGUF representation / out of scope). -_UNSUPPORTED = {"onebit"} - def plan_export(quantized_dir: str) -> Dict[str, object]: """Decide which export path to use for ``quantized_dir`` (no side effects). - Returns a dict with ``method`` (direct / mixed / fallback / unsupported), - plus the parsed :class:`QuantMeta` fields, and a human-readable ``reason``. + Returns a dict with ``path`` (direct / mixed / fallback / unsupported), the + parsed :class:`QuantMeta` under ``meta``, and a human-readable ``reason``. """ meta = read_quant_meta(quantized_dir) - if meta.quant_method in _UNSUPPORTED: + if meta.quant_method in UNSUPPORTED_METHODS: return { "path": "unsupported", "meta": meta, @@ -108,24 +107,34 @@ def export_to_gguf( quantized_dir: OneComp quantized checkpoint directory. out_gguf: Output ``.gguf`` path. mode: ``auto`` (route by quant_method/rotation) or force a path with - ``direct`` / ``mixed`` / ``fallback``. + ``direct`` / ``mixed`` / ``fallback``. Forcing a path does not + override support: an unsupported ``quant_method`` is rejected for + every ``mode``. qtype: target type for the fallback (dequantize) path, e.g. ``Q4_K_M``. original_model: optional original FP model dir for skeleton metadata. work_dir: scratch directory. Returns: Summary dict including the chosen ``path`` and per-path details. + + Raises: + ValueError: If ``quant_method`` is unsupported (any ``mode``), or if + ``mode`` is not one of auto/direct/mixed/fallback. """ plan = plan_export(quantized_dir) - chosen = mode if mode != "auto" else plan["path"] meta = plan["meta"] - if chosen == "unsupported": + # Test the *plan*, not the resolved mode: an explicit ``mode=`` names a path, + # not a capability, so forcing one must not route an unsupported method into + # an exporter that cannot represent it. + if plan["path"] == "unsupported": raise ValueError( f"quant_method={meta.quant_method!r} is not supported for CPU/GGUF export. " "Supported: gptq, mixed_gptq, jointq, rtn, dbf, autobit (and rotated variants)." ) + chosen = mode if mode != "auto" else plan["path"] + logger.info( "export_to_gguf: %s -> %s | method=%s rotated=%s | path=%s (%s)", quantized_dir, diff --git a/onecomp/cpu/export/checkpoint.py b/onecomp/cpu/export/checkpoint.py index 02da98f4..8ed00ffe 100644 --- a/onecomp/cpu/export/checkpoint.py +++ b/onecomp/cpu/export/checkpoint.py @@ -86,6 +86,15 @@ def supports_direct(self) -> bool: # (so iter_gptq_layers can read them): GPTQ, QEP (same codes), JointQ, RTN, mixed. _GPTQ_FAMILY = {"gptq", "mixed_gptq", "jointq", "rtn"} +# quant_method values with no GGUF export at all. ``onebit`` is out of scope by +# request; ``mdbf`` has no GPTQ layout and no dequantize reconstruction *yet* +# (implementable via MultipathMDBFLinear.get_weight), so no exporter can +# represent it today -- the fallback path would silently ship randomly +# initialised weights. Lives here (not in ``auto``) so the low-level +# ``dequantize_to_hf`` entry point can reject them too, not just the +# ``export_to_gguf`` router. +UNSUPPORTED_METHODS = {"onebit", "mdbf"} + def configured_bit_widths(quant_config: dict) -> set: """All weight bit-widths in a checkpoint (default + per-layer ``quantization_bits``).""" diff --git a/onecomp/cpu/export/dequantize.py b/onecomp/cpu/export/dequantize.py index 6a47ea4b..22d75943 100644 --- a/onecomp/cpu/export/dequantize.py +++ b/onecomp/cpu/export/dequantize.py @@ -19,7 +19,12 @@ import torch -from onecomp.cpu.export.checkpoint import dequantize_layer, iter_gptq_layers, read_quant_meta +from onecomp.cpu.export.checkpoint import ( + UNSUPPORTED_METHODS, + dequantize_layer, + iter_gptq_layers, + read_quant_meta, +) logger = getLogger(__name__) @@ -68,6 +73,38 @@ def _dequantize_dbf_layers(model, state, torch_dtype): return dense, consumed +def _reject_unfilled_weights( + missing: list[str], retied: set[str], save_directory: str, quant_method: str +) -> None: + """Fail when ``load_state_dict(strict=False)`` left a weight at its random init. + + ``strict=False`` is needed because the checkpoint legitimately lacks + non-persistent buffers, but it equally swallows a whole quantizer's worth of + unreconstructed weights. Only ``.weight`` / ``.bias`` keys are checked, so + buffers stay exempt while any dense tensor that found no source is loud. + + Args: + missing: ``missing_keys`` from ``load_state_dict``. + retied: Keys since restored by ``tie_weights()``. + save_directory: Checkpoint directory, for the error message. + quant_method: Checkpoint's ``quant_method``, for the error message. + + Raises: + RuntimeError: If any weight/bias key had no source. + """ + unfilled = sorted( + key for key in missing if key.endswith((".weight", ".bias")) and key not in retied + ) + if not unfilled: + return + raise RuntimeError( + f"{len(unfilled)} tensor(s) in {save_directory} (quant_method={quant_method!r}) " + f"had no source and would be exported as random init: {unfilled[:8]}" + f"{' ...' if len(unfilled) > 8 else ''}. This layout has no dense " + "reconstruction implemented in onecomp.cpu.export.dequantize." + ) + + def dequantize_to_hf( save_directory: str, output_directory: str, @@ -82,10 +119,26 @@ def dequantize_to_hf( Returns: ``output_directory``. + + Raises: + ValueError: If the checkpoint's ``quant_method`` has no dense + reconstruction implemented here (see ``UNSUPPORTED_METHODS``). + RuntimeError: If any weight/bias tensor ends up with no source in the + checkpoint, which would ship the model's random init. """ from safetensors.torch import load_file from transformers import AutoConfig, AutoModelForCausalLM + # Guard here as well as in ``plan_export``: this is a public entry point and + # is also reached via ``export_via_dequantize`` / the skeleton builder. + meta = read_quant_meta(save_directory) + if meta.quant_method in UNSUPPORTED_METHODS: + raise ValueError( + f"quant_method={meta.quant_method!r} has no dense reconstruction " + "implemented in dequantize_to_hf; its tensors would be dropped and the " + "result would carry randomly initialised weights." + ) + os.makedirs(output_directory, exist_ok=True) config = AutoConfig.from_pretrained(save_directory) @@ -107,7 +160,6 @@ def dequantize_to_hf( dense_state: Dict[str, torch.Tensor] = {} quant_keys = set() n_layers = 0 - meta = read_quant_meta(save_directory) if meta.is_gptq_family: for layer in iter_gptq_layers(save_directory): dense_state[layer.weight_key] = dequantize_layer(layer).to(torch_dtype) @@ -144,12 +196,16 @@ def dequantize_to_hf( # Gemma) rely on. Without re-tying, lm_head keeps its random init and the # exported model emits garbage. Re-establish the tie when the checkpoint did # not carry a separate lm_head weight. + retied = set() if getattr(model.config, "tie_word_embeddings", False) and not any( k.endswith("lm_head.weight") for k in dense_state ): model.tie_weights() + retied = {k for k in missing if k.endswith("lm_head.weight")} logger.info("Re-tied lm_head to embed_tokens (tie_word_embeddings=True)") + _reject_unfilled_weights(missing, retied, save_directory, meta.quant_method) + model.save_pretrained(output_directory, safe_serialization=True) _copy_tokenizer(save_directory, output_directory) logger.info("Wrote dense HF model to %s", output_directory) diff --git a/tests/onecomp/cpu/test_export_routing.py b/tests/onecomp/cpu/test_export_routing.py index 66a71542..9e360444 100644 --- a/tests/onecomp/cpu/test_export_routing.py +++ b/tests/onecomp/cpu/test_export_routing.py @@ -30,6 +30,9 @@ def _write_quant_config(tmp_path, quant_method, **extra): ("dbf", {}, "fallback", False), ("autobit", {}, "fallback", False), ("onebit", {}, "unsupported", False), + # MDBF is rejected up-front; the rotated row pins guard-before-rotation. + ("mdbf", {}, "unsupported", False), + ("mdbf", {"rotated": True}, "unsupported", False), ("gptq", {"rotated": True}, "fallback", True), ("mixed_gptq", {"rotated": True}, "fallback", True), # act-order uniform GPTQ must go to mixed (direct packing isn't block-aligned) @@ -78,12 +81,95 @@ def test_needs_mixed_export_helpers(): ) == {4, 2} -def test_export_to_gguf_rejects_unsupported(tmp_path): +@pytest.mark.parametrize("method", ["onebit", "mdbf"]) +@pytest.mark.parametrize("mode", ["auto", "direct", "mixed", "fallback"]) +def test_export_to_gguf_rejects_unsupported(tmp_path, method, mode): + """An explicit ``mode`` names a path, not a capability: it must not bypass the guard.""" from onecomp.cpu.export.auto import export_to_gguf - d = _write_quant_config(tmp_path, "onebit") + d = _write_quant_config(tmp_path, method) with pytest.raises(ValueError, match="not supported"): - export_to_gguf(d, str(tmp_path / "out.gguf")) + export_to_gguf(d, str(tmp_path / "out.gguf"), mode=mode) + + +@pytest.mark.parametrize("method", ["onebit", "mdbf"]) +def test_dequantize_to_hf_rejects_unsupported(tmp_path, method): + """The low-level entry point guards too; it is public and reached via other paths.""" + from onecomp.cpu.export.dequantize import dequantize_to_hf + + d = _write_quant_config(tmp_path, method) + with pytest.raises(ValueError, match="no dense reconstruction"): + dequantize_to_hf(d, str(tmp_path / "dense")) + + +def test_reject_unfilled_weights_flags_random_init_tensors(): + """An unknown layout leaves dense weights unsourced; that must raise, not warn.""" + from onecomp.cpu.export.dequantize import _reject_unfilled_weights + + missing = [ + "model.layers.0.mlp.down_proj.weight", + "model.layers.0.mlp.down_proj.bias", + "model.rotary_emb.inv_freq", # a buffer, legitimately absent + ] + with pytest.raises(RuntimeError, match="random init"): + _reject_unfilled_weights(missing, set(), "/ckpt", "future_method") + + +def test_reject_unfilled_weights_ignores_buffers_and_retied_lm_head(): + from onecomp.cpu.export.dequantize import _reject_unfilled_weights + + _reject_unfilled_weights(["model.rotary_emb.inv_freq"], set(), "/ckpt", "gptq") + _reject_unfilled_weights( + ["lm_head.weight"], {"lm_head.weight"}, "/ckpt", "gptq" + ) # restored by tie_weights() + + +def test_dequantize_to_hf_rejects_unknown_layout_end_to_end(tmp_path): + """``UNSUPPORTED_METHODS`` is an allow-list of *known* gaps; this pins the net. + + A quant_method nobody listed (a future quantizer, or MDBF children hidden + inside an ``autobit`` checkpoint) reaches the dequantize body, drops its + tensors and leaves the dense weights at ``from_config`` random init. Only an + end-to-end call proves ``_reject_unfilled_weights`` is actually wired into + ``dequantize_to_hf``; the unit tests above pass even if the call is deleted. + """ + from safetensors.torch import save_file + from transformers import LlamaConfig + + from onecomp.cpu.export.dequantize import dequantize_to_hf + + config = LlamaConfig( + hidden_size=16, + num_attention_heads=4, + num_hidden_layers=1, + num_key_value_heads=4, + intermediate_size=32, + max_position_embeddings=16, + vocab_size=32, + tie_word_embeddings=False, + ) + # Keep the output outside the checkpoint so the shard glob cannot see it. + ckpt = tmp_path / "ckpt" + out = tmp_path / "dense" + ckpt.mkdir() + + cfg_dict = config.to_dict() + cfg_dict["quantization_config"] = {"quant_method": "future_method", "bits": 2} + (ckpt / "config.json").write_text(json.dumps(cfg_dict), encoding="utf-8") + + # A layer stored in some unknown factorized form: no ``.weight``, and keys + # neither the GPTQ nor the DBF reader recognises. + save_file( + { + "model.layers.0.self_attn.q_proj.factor_a": torch.zeros(16, 4), + "model.layers.0.self_attn.q_proj.factor_b": torch.zeros(4, 16), + }, + str(ckpt / "model.safetensors"), + ) + + with pytest.raises(RuntimeError, match="random init"): + dequantize_to_hf(str(ckpt), str(out)) + assert not (out / "model.safetensors").exists(), "must not write a broken model" def test_dbf_dequantize_matches_forward(): From d730cc9941906a488b2dad1baf4af2d015758191 Mon Sep 17 00:00:00 2001 From: katari Date: Wed, 12 Aug 2026 09:39:20 +0900 Subject: [PATCH 02/17] [fix] reject incompatible forced GGUF export modes --- onecomp/cpu/export/auto.py | 7 +++++++ tests/onecomp/cpu/test_export_routing.py | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/onecomp/cpu/export/auto.py b/onecomp/cpu/export/auto.py index 314d6d40..7d13a5f8 100644 --- a/onecomp/cpu/export/auto.py +++ b/onecomp/cpu/export/auto.py @@ -133,6 +133,13 @@ def export_to_gguf( "Supported: gptq, mixed_gptq, jointq, rtn, dbf, autobit (and rotated variants)." ) + if mode in ("direct", "mixed") and not meta.supports_direct: + raise ValueError( + f"mode={mode!r} needs the AutoGPTQ block layout and no online Hadamard " + f"(quant_method={meta.quant_method!r}, rotated={meta.rotated}); " + "use mode='fallback'." + ) + chosen = mode if mode != "auto" else plan["path"] logger.info( diff --git a/tests/onecomp/cpu/test_export_routing.py b/tests/onecomp/cpu/test_export_routing.py index 9e360444..fadd8511 100644 --- a/tests/onecomp/cpu/test_export_routing.py +++ b/tests/onecomp/cpu/test_export_routing.py @@ -92,6 +92,26 @@ def test_export_to_gguf_rejects_unsupported(tmp_path, method, mode): export_to_gguf(d, str(tmp_path / "out.gguf"), mode=mode) +@pytest.mark.parametrize( + "method,extra", + [ + ("dbf", {}), + ("autobit", {}), + ("gptq", {"rotated": True}), + ], +) +@pytest.mark.parametrize("mode", ["direct", "mixed"]) +def test_export_to_gguf_rejects_incompatible_forced_mode( + tmp_path, method: str, extra: dict[str, bool], mode: str +) -> None: + """A forced packed path must reject checkpoints without that capability.""" + from onecomp.cpu.export.auto import export_to_gguf + + d = _write_quant_config(tmp_path, method, **extra) + with pytest.raises(ValueError, match="needs the AutoGPTQ block layout"): + export_to_gguf(d, str(tmp_path / "out.gguf"), mode=mode) + + @pytest.mark.parametrize("method", ["onebit", "mdbf"]) def test_dequantize_to_hf_rejects_unsupported(tmp_path, method): """The low-level entry point guards too; it is public and reached via other paths.""" From d781031f91bc40dc98cabbab14936887c7f77aa9 Mon Sep 17 00:00:00 2001 From: katari Date: Wed, 12 Aug 2026 09:44:31 +0900 Subject: [PATCH 03/17] [refactor] share MDBF path index parsing --- onecomp/quantizer/mdbf/mdbf_layer.py | 31 ++++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/onecomp/quantizer/mdbf/mdbf_layer.py b/onecomp/quantizer/mdbf/mdbf_layer.py index 455a0e51..e7ebc1dc 100644 --- a/onecomp/quantizer/mdbf/mdbf_layer.py +++ b/onecomp/quantizer/mdbf/mdbf_layer.py @@ -21,6 +21,7 @@ """ +from collections.abc import Mapping from typing import List, Optional, Tuple import torch @@ -42,6 +43,23 @@ # ============================================================================= +def mdbf_path_indices(layer_state_dict: Mapping[str, torch.Tensor]) -> set[int]: + """Return MDBF path indices found in a layer state dict. + + Args: + layer_state_dict: State dict whose nested keys use ``paths.{p}.*``. + + Returns: + Non-negative path indices present in the state dict. + """ + path_indices = set() + for key in layer_state_dict: + parts = key.split(".") + if parts[0] == "paths" and len(parts) >= 2 and parts[1].isdigit(): + path_indices.add(int(parts[1])) + return path_indices + + def pack_binary(x: torch.Tensor) -> Tuple[torch.Tensor, Tuple[int, ...]]: """ Convert ±1 to {0,1} and pack into uint8 with 8:1 ratio. Pad the end with +1. @@ -472,11 +490,7 @@ def validate_saved_state( ValueError: If a path is missing, or bias presence disagrees with the model being loaded into. """ - path_indices = set() - for key in layer_state_dict: - parts = key.split(".") - if parts[0] == "paths" and len(parts) >= 2 and parts[1].isdigit(): - path_indices.add(int(parts[1])) + path_indices = mdbf_path_indices(layer_state_dict) # Compared without building range(expected_paths): a corrupt config can # record an absurd P, and materializing it would exhaust memory before @@ -542,12 +556,7 @@ def _t(k): return torch.zeros_like(t) if empty else t # Detect P from state_dict keys - path_indices = set() - for key in layer_state_dict: - if key.startswith("paths."): - parts = key.split(".") - if len(parts) >= 2 and parts[1].isdigit(): - path_indices.add(int(parts[1])) + path_indices = mdbf_path_indices(layer_state_dict) if not path_indices: raise ValueError( "MultipathMDBFLinear.from_saved_state: no `paths.{p}.*` keys " From 1e211e46788ca9c04698a449619c230a97c5d34d Mon Sep 17 00:00:00 2001 From: katari Date: Wed, 12 Aug 2026 09:55:59 +0900 Subject: [PATCH 04/17] [feat] reconstruct dense MDBF export weights --- onecomp/cpu/export/dequantize.py | 154 +++++++++++++++++++++++ tests/onecomp/cpu/test_export_routing.py | 68 ++++++++++ 2 files changed, 222 insertions(+) diff --git a/onecomp/cpu/export/dequantize.py b/onecomp/cpu/export/dequantize.py index 22d75943..be7531b9 100644 --- a/onecomp/cpu/export/dequantize.py +++ b/onecomp/cpu/export/dequantize.py @@ -13,6 +13,7 @@ from __future__ import annotations import os +from collections.abc import Mapping from glob import glob from logging import getLogger from typing import Dict @@ -23,6 +24,7 @@ UNSUPPORTED_METHODS, dequantize_layer, iter_gptq_layers, + load_quant_config, read_quant_meta, ) @@ -31,6 +33,8 @@ _QUANT_SUFFIXES = (".qweight", ".scales", ".qzeros", ".g_idx", ".perm") # DBF (DoubleBinaryLinear) tensors; the dense weight is reconstructed by a forward. _DBF_SUFFIXES = (".scaling0", ".scaling2", ".scaling4", ".bp1", ".bp3") +# MDBF tensors are nested under one submodule per path. +_MDBF_MARKER = ".paths.0.A_sign_packed" def _dequantize_dbf_layers(model, state, torch_dtype): @@ -73,6 +77,156 @@ def _dequantize_dbf_layers(model, state, torch_dtype): return dense, consumed +def _check_mdbf_shapes( + layer_state_dict: Mapping[str, torch.Tensor], + layer_name: str, + in_features: int, + out_features: int, +) -> None: + """Reject MDBF shapes that can silently reconstruct an invalid weight. + + Args: + layer_state_dict: Checkpoint tensors for one MDBF layer. + layer_name: Layer name used in error messages. + in_features: Dense layer input width. + out_features: Dense layer output width. + + Raises: + KeyError: If a required MDBF tensor is absent. + ValueError: If factor shapes do not match the dense layer. + """ + from onecomp.quantizer.mdbf.mdbf_layer import mdbf_path_indices + + def _raise_shape(path_index: int, tensor_name: str, actual: object, expected: object) -> None: + """Raise a consistent shape validation error.""" + raise ValueError( + f"Invalid MDBF shape for {layer_name}.paths.{path_index}.{tensor_name}: " + f"expected {expected}, got {actual}." + ) + + for path_index in sorted(mdbf_path_indices(layer_state_dict)): + prefix = f"paths.{path_index}." + q_u = layer_state_dict[prefix + "Q_U_amp"] + if q_u.ndim != 2: + _raise_shape(path_index, "Q_U_amp", tuple(q_u.shape), "a 2-D tensor") + + rank, amplitude_rank = (int(dim) for dim in q_u.shape) + if rank <= 0 or amplitude_rank <= 0: + _raise_shape( + path_index, + "Q_U_amp", + tuple(q_u.shape), + "positive rank and amplitude dimensions", + ) + + # Packed byte counts pin down rank for production widths of at least 8. + expected_packed_sizes = { + "A_sign_packed": (out_features * rank + 7) // 8, + "B_sign_packed": (rank * in_features + 7) // 8, + } + for tensor_name, expected_size in expected_packed_sizes.items(): + actual_size = layer_state_dict[prefix + tensor_name].numel() + if actual_size != expected_size: + _raise_shape(path_index, tensor_name, actual_size, expected_size) + + expected_shapes = { + "A_amp": (out_features, amplitude_rank), + "B_amp": (in_features, amplitude_rank), + "Q_V_amp": (rank, amplitude_rank), + } + for tensor_name, expected_shape in expected_shapes.items(): + actual_shape = tuple(layer_state_dict[prefix + tensor_name].shape) + if actual_shape != expected_shape: + _raise_shape(path_index, tensor_name, actual_shape, expected_shape) + + # from_saved_state rebuilds these buffers from the dense layer widths; + # checkpoint values are validation inputs, not reconstruction inputs. + expected_sign_shapes = { + "_A_sign_shape": (out_features, rank), + "_B_sign_shape": (rank, in_features), + } + for tensor_name, expected_shape in expected_sign_shapes.items(): + tensor = layer_state_dict.get(prefix + tensor_name) + if tensor is None: + continue + actual_shape = tuple(int(dim) for dim in tensor.reshape(-1).tolist()) + if tuple(tensor.shape) != (2,) or actual_shape != expected_shape: + _raise_shape(path_index, tensor_name, actual_shape, expected_shape) + + +def _dequantize_mdbf_layers( + model: torch.nn.Module, + state: Mapping[str, torch.Tensor], + torch_dtype: torch.dtype, + save_directory: str, +) -> tuple[dict[str, torch.Tensor], set[str]]: + """Reconstruct dense weights for every MDBF layer. + + Args: + model: Dense model exposing the target linear modules. + state: Flat checkpoint state dict. + torch_dtype: Output weight dtype. + save_directory: Checkpoint directory containing quantization metadata. + + Returns: + Dense tensors and checkpoint keys consumed during reconstruction. + + Raises: + KeyError: If a required MDBF tensor is absent. + ValueError: If the checkpoint is incomplete or has invalid shapes. + RuntimeError: If an MDBF layer has no matching dense module. + """ + marker_keys = sorted(key for key in state if key.endswith(_MDBF_MARKER)) + if not marker_keys: + return {}, set() + + from onecomp.quantizer.mdbf.config import resolve_mdbf_paths + from onecomp.quantizer.mdbf.mdbf_layer import MultipathMDBFLinear + + modules = dict(model.named_modules()) + expected_paths = resolve_mdbf_paths(load_quant_config(save_directory)) + dense: dict[str, torch.Tensor] = {} + consumed: set[str] = set() + + for marker_key in marker_keys: + name = marker_key[: -len(_MDBF_MARKER)] + target = modules.get(name) + if target is None or not hasattr(target, "in_features"): + raise RuntimeError( + f"MDBF layer {name!r} from {save_directory} has no matching " + "nn.Linear in the dense model; its weight cannot be exported." + ) + + in_features = int(target.in_features) + out_features = int(target.out_features) + prefix = name + "." + layer_state_dict = { + key[len(prefix) :]: tensor for key, tensor in state.items() if key.startswith(prefix) + } + + MultipathMDBFLinear.validate_saved_state( + layer_state_dict, + layer_name=name, + expected_paths=expected_paths, + expects_bias=getattr(target, "bias", None) is not None, + ) + _check_mdbf_shapes(layer_state_dict, name, in_features, out_features) + + layer = MultipathMDBFLinear.from_saved_state( + layer_state_dict, in_features, out_features + ).eval() + with torch.no_grad(): + weight = layer.get_weight(torch.float32) + dense[f"{name}.weight"] = weight.to(torch_dtype) + bias = layer_state_dict.get("bias") + if bias is not None: + dense[f"{name}.bias"] = bias.to(torch_dtype) + consumed.update(key for key in state if key.startswith(prefix)) + + logger.info("Dequantized %d MDBF layers", len(marker_keys)) + return dense, consumed + + def _reject_unfilled_weights( missing: list[str], retied: set[str], save_directory: str, quant_method: str ) -> None: diff --git a/tests/onecomp/cpu/test_export_routing.py b/tests/onecomp/cpu/test_export_routing.py index fadd8511..9892c8c3 100644 --- a/tests/onecomp/cpu/test_export_routing.py +++ b/tests/onecomp/cpu/test_export_routing.py @@ -9,6 +9,7 @@ """ import json +from pathlib import Path import pytest import torch @@ -238,6 +239,73 @@ def __init__(self): assert torch.allclose(expected, got, atol=1e-2, rtol=1e-2) +@pytest.mark.parametrize("path_count", [1, 2]) +@pytest.mark.parametrize("amplitude_rank", [1, 2]) +@pytest.mark.parametrize("with_bias", [False, True]) +def test_mdbf_dequantize_matches_forward( + tmp_path: Path, path_count: int, amplitude_rank: int, with_bias: bool +) -> None: + """Dense MDBF reconstruction matches its factorized fp32 forward.""" + from onecomp.cpu.export.dequantize import _dequantize_mdbf_layers + from onecomp.quantizer.mdbf.initialize import MDBFParams + from onecomp.quantizer.mdbf.mdbf_layer import MultipathMDBFLinear + + in_features, out_features, rank = 16, 8, 6 + + def _make_params(seed: int) -> MDBFParams: + """Build deterministic MDBF parameters for one path.""" + generator = torch.Generator().manual_seed(seed) + + def _sign(*shape: int) -> torch.Tensor: + """Build a deterministic sign tensor.""" + values = torch.randint(0, 2, shape, generator=generator) + return (values * 2 - 1).to(torch.float32) + + def _amplitude(*shape: int) -> torch.Tensor: + """Build a deterministic positive amplitude tensor.""" + return torch.rand(*shape, generator=generator) + 0.5 + + return MDBFParams( + A_sign=_sign(out_features, rank), + B_sign=_sign(rank, in_features), + A_amp=_amplitude(out_features, amplitude_rank), + B_amp=_amplitude(in_features, amplitude_rank), + Q_U_amp=_amplitude(rank, amplitude_rank), + Q_V_amp=_amplitude(rank, amplitude_rank), + ) + + bias = torch.randn(out_features) if with_bias else None + reference = MultipathMDBFLinear( + [_make_params(seed) for seed in range(path_count)], + bias=bias, + use_gemlite=False, + ).eval() + state = {f"lin.{key}": tensor for key, tensor in reference.state_dict().items()} + save_directory = _write_quant_config(tmp_path, "mdbf", P=path_count) + + class _Stub(torch.nn.Module): + """Dense model exposing the MDBF target layer.""" + + def __init__(self) -> None: + """Create the target dense linear.""" + super().__init__() + self.lin = torch.nn.Linear(in_features, out_features, bias=with_bias) + + dense, consumed = _dequantize_mdbf_layers(_Stub(), state, torch.float32, save_directory) + + assert consumed == set(state) + assert set(dense) == ({"lin.weight", "lin.bias"} if with_bias else {"lin.weight"}) + + generator = torch.Generator().manual_seed(100) + inputs = torch.randn(5, in_features, generator=generator) + with torch.no_grad(): + expected = reference(inputs.float()) + actual = inputs.float() @ dense["lin.weight"].t() + if with_bias: + actual += dense["lin.bias"].float() + torch.testing.assert_close(actual, expected, rtol=1e-4, atol=1e-3) + + def test_hadamard_defold_roundtrip(): """De-fold inverts the online down_proj Hadamard applied during rotation.""" from onecomp.cpu.export.rotation import defold_down_proj_hadamard From 13cd4c1c75acf4e3cafebecd235792886b5f2144 Mon Sep 17 00:00:00 2001 From: katari Date: Wed, 12 Aug 2026 10:30:06 +0900 Subject: [PATCH 05/17] [feat] route MDBF GGUF export through fallback --- onecomp/cpu/export/auto.py | 3 ++- onecomp/cpu/export/checkpoint.py | 13 ++++--------- onecomp/cpu/export/dequantize.py | 11 +++++++++-- tests/onecomp/cpu/test_export_routing.py | 11 ++++++----- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/onecomp/cpu/export/auto.py b/onecomp/cpu/export/auto.py index 7d13a5f8..bdffcc7d 100644 --- a/onecomp/cpu/export/auto.py +++ b/onecomp/cpu/export/auto.py @@ -130,7 +130,8 @@ def export_to_gguf( if plan["path"] == "unsupported": raise ValueError( f"quant_method={meta.quant_method!r} is not supported for CPU/GGUF export. " - "Supported: gptq, mixed_gptq, jointq, rtn, dbf, autobit (and rotated variants)." + "Supported: gptq, mixed_gptq, jointq, rtn, dbf, mdbf, autobit " + "(and rotated variants)." ) if mode in ("direct", "mixed") and not meta.supports_direct: diff --git a/onecomp/cpu/export/checkpoint.py b/onecomp/cpu/export/checkpoint.py index 8ed00ffe..037d9caa 100644 --- a/onecomp/cpu/export/checkpoint.py +++ b/onecomp/cpu/export/checkpoint.py @@ -86,14 +86,9 @@ def supports_direct(self) -> bool: # (so iter_gptq_layers can read them): GPTQ, QEP (same codes), JointQ, RTN, mixed. _GPTQ_FAMILY = {"gptq", "mixed_gptq", "jointq", "rtn"} -# quant_method values with no GGUF export at all. ``onebit`` is out of scope by -# request; ``mdbf`` has no GPTQ layout and no dequantize reconstruction *yet* -# (implementable via MultipathMDBFLinear.get_weight), so no exporter can -# represent it today -- the fallback path would silently ship randomly -# initialised weights. Lives here (not in ``auto``) so the low-level -# ``dequantize_to_hf`` entry point can reject them too, not just the -# ``export_to_gguf`` router. -UNSUPPORTED_METHODS = {"onebit", "mdbf"} +# quant_method values with no GGUF export at all. Lives here (not in ``auto``) +# so the low-level ``dequantize_to_hf`` entry point rejects OneBit too. +UNSUPPORTED_METHODS = {"onebit"} def configured_bit_widths(quant_config: dict) -> set: @@ -192,7 +187,7 @@ def iter_gptq_layers(save_directory: str) -> Iterator[GPTQLayer]: """Yield every GPTQ-quantized linear in a saved OneComp model, fully unpacked. Only ``gptq`` / ``mixed_gptq`` checkpoints expose ``qweight`` tensors; other - methods (dbf/onebit) are skipped here and must use the dequantize path. + methods (dbf/mdbf/onebit) are skipped here and must use the dequantize path. """ quant_config = load_quant_config(save_directory) state = _load_state_dict(save_directory) diff --git a/onecomp/cpu/export/dequantize.py b/onecomp/cpu/export/dequantize.py index be7531b9..5d80913f 100644 --- a/onecomp/cpu/export/dequantize.py +++ b/onecomp/cpu/export/dequantize.py @@ -277,8 +277,8 @@ def dequantize_to_hf( Raises: ValueError: If the checkpoint's ``quant_method`` has no dense reconstruction implemented here (see ``UNSUPPORTED_METHODS``). - RuntimeError: If any weight/bias tensor ends up with no source in the - checkpoint, which would ship the model's random init. + RuntimeError: If an MDBF layer cannot be mapped to the dense model, or + any weight/bias tensor ends up with no checkpoint source. """ from safetensors.torch import load_file from transformers import AutoConfig, AutoModelForCausalLM @@ -327,6 +327,13 @@ def dequantize_to_hf( dense_state.update(dbf_dense) quant_keys |= dbf_consumed + # MDBF layers use a nested paths.{p}.* layout. + mdbf_dense, mdbf_consumed = _dequantize_mdbf_layers( + model, state, torch_dtype, save_directory + ) + dense_state.update(mdbf_dense) + quant_keys |= mdbf_consumed + for key, tensor in state.items(): if key in quant_keys: continue diff --git a/tests/onecomp/cpu/test_export_routing.py b/tests/onecomp/cpu/test_export_routing.py index 9892c8c3..272acc04 100644 --- a/tests/onecomp/cpu/test_export_routing.py +++ b/tests/onecomp/cpu/test_export_routing.py @@ -31,9 +31,9 @@ def _write_quant_config(tmp_path, quant_method, **extra): ("dbf", {}, "fallback", False), ("autobit", {}, "fallback", False), ("onebit", {}, "unsupported", False), - # MDBF is rejected up-front; the rotated row pins guard-before-rotation. - ("mdbf", {}, "unsupported", False), - ("mdbf", {"rotated": True}, "unsupported", False), + # MDBF has no lossless layout; rotated MDBF uses the same fallback. + ("mdbf", {}, "fallback", False), + ("mdbf", {"rotated": True}, "fallback", False), ("gptq", {"rotated": True}, "fallback", True), ("mixed_gptq", {"rotated": True}, "fallback", True), # act-order uniform GPTQ must go to mixed (direct packing isn't block-aligned) @@ -82,7 +82,7 @@ def test_needs_mixed_export_helpers(): ) == {4, 2} -@pytest.mark.parametrize("method", ["onebit", "mdbf"]) +@pytest.mark.parametrize("method", ["onebit"]) @pytest.mark.parametrize("mode", ["auto", "direct", "mixed", "fallback"]) def test_export_to_gguf_rejects_unsupported(tmp_path, method, mode): """An explicit ``mode`` names a path, not a capability: it must not bypass the guard.""" @@ -97,6 +97,7 @@ def test_export_to_gguf_rejects_unsupported(tmp_path, method, mode): "method,extra", [ ("dbf", {}), + ("mdbf", {}), ("autobit", {}), ("gptq", {"rotated": True}), ], @@ -113,7 +114,7 @@ def test_export_to_gguf_rejects_incompatible_forced_mode( export_to_gguf(d, str(tmp_path / "out.gguf"), mode=mode) -@pytest.mark.parametrize("method", ["onebit", "mdbf"]) +@pytest.mark.parametrize("method", ["onebit"]) def test_dequantize_to_hf_rejects_unsupported(tmp_path, method): """The low-level entry point guards too; it is public and reached via other paths.""" from onecomp.cpu.export.dequantize import dequantize_to_hf From 1d576e897138abc9b4b118d40761b95c6db0d357 Mon Sep 17 00:00:00 2001 From: katari Date: Wed, 12 Aug 2026 11:18:22 +0900 Subject: [PATCH 06/17] [test] cover MDBF fallback validation --- tests/onecomp/cpu/test_export_routing.py | 221 ++++++++++++++++++----- 1 file changed, 177 insertions(+), 44 deletions(-) diff --git a/tests/onecomp/cpu/test_export_routing.py b/tests/onecomp/cpu/test_export_routing.py index 272acc04..b646877a 100644 --- a/tests/onecomp/cpu/test_export_routing.py +++ b/tests/onecomp/cpu/test_export_routing.py @@ -10,10 +10,15 @@ import json from pathlib import Path +from unittest.mock import patch import pytest import torch +_MDBF_IN_FEATURES = 16 +_MDBF_OUT_FEATURES = 8 +_MDBF_RANK = 6 + def _write_quant_config(tmp_path, quant_method, **extra): cfg = {"model_type": "llama", "quantization_config": {"quant_method": quant_method, **extra}} @@ -21,6 +26,64 @@ def _write_quant_config(tmp_path, quant_method, **extra): return str(tmp_path) +class _MDBFDenseStub(torch.nn.Module): + """Dense model exposing one MDBF target layer.""" + + def __init__(self, *, with_bias: bool) -> None: + """Create the target dense linear.""" + super().__init__() + self.lin = torch.nn.Linear(_MDBF_IN_FEATURES, _MDBF_OUT_FEATURES, bias=with_bias) + + +def _build_mdbf_state( + path_count: int, amplitude_rank: int, with_bias: bool +) -> tuple[torch.nn.Module, dict[str, torch.Tensor]]: + """Build a deterministic MDBF reference layer and flat checkpoint state. + + Args: + path_count: Number of MDBF paths. + amplitude_rank: Multi-scale amplitude rank. + with_bias: Whether the layer carries bias. + + Returns: + Reference MDBF layer and its state dict under the ``lin`` prefix. + """ + from onecomp.quantizer.mdbf.initialize import MDBFParams + from onecomp.quantizer.mdbf.mdbf_layer import MultipathMDBFLinear + + def _make_params(seed: int) -> MDBFParams: + """Build deterministic MDBF parameters for one path.""" + generator = torch.Generator().manual_seed(seed) + + def _sign(*shape: int) -> torch.Tensor: + """Build a deterministic sign tensor.""" + values = torch.randint(0, 2, shape, generator=generator) + return (values * 2 - 1).to(torch.float32) + + def _amplitude(*shape: int) -> torch.Tensor: + """Build a deterministic positive amplitude tensor.""" + return torch.rand(*shape, generator=generator) + 0.5 + + return MDBFParams( + A_sign=_sign(_MDBF_OUT_FEATURES, _MDBF_RANK), + B_sign=_sign(_MDBF_RANK, _MDBF_IN_FEATURES), + A_amp=_amplitude(_MDBF_OUT_FEATURES, amplitude_rank), + B_amp=_amplitude(_MDBF_IN_FEATURES, amplitude_rank), + Q_U_amp=_amplitude(_MDBF_RANK, amplitude_rank), + Q_V_amp=_amplitude(_MDBF_RANK, amplitude_rank), + ) + + bias_generator = torch.Generator().manual_seed(999) + bias = torch.randn(_MDBF_OUT_FEATURES, generator=bias_generator) if with_bias else None + reference = MultipathMDBFLinear( + [_make_params(seed) for seed in range(path_count)], + bias=bias, + use_gemlite=False, + ).eval() + state = {f"lin.{key}": tensor for key, tensor in reference.state_dict().items()} + return reference, state + + @pytest.mark.parametrize( "method,extra,expected_path,is_family", [ @@ -114,6 +177,20 @@ def test_export_to_gguf_rejects_incompatible_forced_mode( export_to_gguf(d, str(tmp_path / "out.gguf"), mode=mode) +@pytest.mark.parametrize("mode", ["auto", "fallback"]) +def test_export_to_gguf_mdbf_dispatches_fallback(tmp_path: Path, mode: str) -> None: + """The public exporter dispatches supported MDBF modes to fallback.""" + from onecomp.cpu.export.auto import export_to_gguf + + quantized_dir = _write_quant_config(tmp_path, "mdbf") + out_gguf = str(tmp_path / "out.gguf") + with patch("onecomp.cpu.export.fallback.export_via_dequantize") as export_mock: + result = export_to_gguf(quantized_dir, out_gguf, mode=mode) + + export_mock.assert_called_once_with(quantized_dir, out_gguf, qtype="Q4_K_M", work_dir=None) + assert result["path"] == "fallback" + + @pytest.mark.parametrize("method", ["onebit"]) def test_dequantize_to_hf_rejects_unsupported(tmp_path, method): """The low-level entry point guards too; it is public and reached via other paths.""" @@ -248,57 +325,18 @@ def test_mdbf_dequantize_matches_forward( ) -> None: """Dense MDBF reconstruction matches its factorized fp32 forward.""" from onecomp.cpu.export.dequantize import _dequantize_mdbf_layers - from onecomp.quantizer.mdbf.initialize import MDBFParams - from onecomp.quantizer.mdbf.mdbf_layer import MultipathMDBFLinear - - in_features, out_features, rank = 16, 8, 6 - def _make_params(seed: int) -> MDBFParams: - """Build deterministic MDBF parameters for one path.""" - generator = torch.Generator().manual_seed(seed) - - def _sign(*shape: int) -> torch.Tensor: - """Build a deterministic sign tensor.""" - values = torch.randint(0, 2, shape, generator=generator) - return (values * 2 - 1).to(torch.float32) - - def _amplitude(*shape: int) -> torch.Tensor: - """Build a deterministic positive amplitude tensor.""" - return torch.rand(*shape, generator=generator) + 0.5 - - return MDBFParams( - A_sign=_sign(out_features, rank), - B_sign=_sign(rank, in_features), - A_amp=_amplitude(out_features, amplitude_rank), - B_amp=_amplitude(in_features, amplitude_rank), - Q_U_amp=_amplitude(rank, amplitude_rank), - Q_V_amp=_amplitude(rank, amplitude_rank), - ) - - bias = torch.randn(out_features) if with_bias else None - reference = MultipathMDBFLinear( - [_make_params(seed) for seed in range(path_count)], - bias=bias, - use_gemlite=False, - ).eval() - state = {f"lin.{key}": tensor for key, tensor in reference.state_dict().items()} + reference, state = _build_mdbf_state(path_count, amplitude_rank, with_bias) save_directory = _write_quant_config(tmp_path, "mdbf", P=path_count) - - class _Stub(torch.nn.Module): - """Dense model exposing the MDBF target layer.""" - - def __init__(self) -> None: - """Create the target dense linear.""" - super().__init__() - self.lin = torch.nn.Linear(in_features, out_features, bias=with_bias) - - dense, consumed = _dequantize_mdbf_layers(_Stub(), state, torch.float32, save_directory) + dense, consumed = _dequantize_mdbf_layers( + _MDBFDenseStub(with_bias=with_bias), state, torch.float32, save_directory + ) assert consumed == set(state) assert set(dense) == ({"lin.weight", "lin.bias"} if with_bias else {"lin.weight"}) generator = torch.Generator().manual_seed(100) - inputs = torch.randn(5, in_features, generator=generator) + inputs = torch.randn(5, _MDBF_IN_FEATURES, generator=generator) with torch.no_grad(): expected = reference(inputs.float()) actual = inputs.float() @ dense["lin.weight"].t() @@ -307,6 +345,101 @@ def __init__(self) -> None: torch.testing.assert_close(actual, expected, rtol=1e-4, atol=1e-3) +def test_mdbf_dequantize_rejects_missing_path(tmp_path: Path) -> None: + """A path missing from the checkpoint must not silently reduce P.""" + from onecomp.cpu.export.dequantize import _dequantize_mdbf_layers + + _, state = _build_mdbf_state(path_count=2, amplitude_rank=1, with_bias=False) + damaged = {key: tensor for key, tensor in state.items() if not key.startswith("lin.paths.1.")} + save_directory = _write_quant_config(tmp_path, "mdbf", P=2) + + with pytest.raises(ValueError, match="Incomplete MDBF checkpoint"): + _dequantize_mdbf_layers( + _MDBFDenseStub(with_bias=False), damaged, torch.float32, save_directory + ) + + +def test_mdbf_dequantize_rejects_rank_mismatch(tmp_path: Path) -> None: + """A silent factor-rank mismatch is rejected using packed byte counts.""" + from onecomp.cpu.export.dequantize import _dequantize_mdbf_layers + + _, state = _build_mdbf_state(path_count=1, amplitude_rank=2, with_bias=False) + state["lin.paths.0.Q_U_amp"] = state["lin.paths.0.Q_U_amp"][:-1] + state["lin.paths.0.Q_V_amp"] = state["lin.paths.0.Q_V_amp"][:-1] + save_directory = _write_quant_config(tmp_path, "mdbf", P=1) + + with pytest.raises(ValueError, match="A_sign_packed"): + _dequantize_mdbf_layers( + _MDBFDenseStub(with_bias=False), state, torch.float32, save_directory + ) + + +@pytest.mark.parametrize("tensor_name", ["A_amp", "B_amp", "Q_V_amp"]) +def test_mdbf_dequantize_rejects_amp_shape_mismatch(tmp_path: Path, tensor_name: str) -> None: + """Singleton amplitude axes must not broadcast into a wrong weight.""" + from onecomp.cpu.export.dequantize import _dequantize_mdbf_layers + + _, state = _build_mdbf_state(path_count=1, amplitude_rank=2, with_bias=False) + key = f"lin.paths.0.{tensor_name}" + state[key] = state[key][:1] + save_directory = _write_quant_config(tmp_path, "mdbf", P=1) + + with pytest.raises(ValueError, match=tensor_name): + _dequantize_mdbf_layers( + _MDBFDenseStub(with_bias=False), state, torch.float32, save_directory + ) + + +@pytest.mark.parametrize("factor_dimension", ["rank", "amplitude_rank"]) +def test_mdbf_dequantize_rejects_empty_factor_dimension( + tmp_path: Path, factor_dimension: str +) -> None: + """Empty factor dimensions must not reconstruct an all-zero weight.""" + from onecomp.cpu.export.dequantize import _dequantize_mdbf_layers + + _, state = _build_mdbf_state(path_count=1, amplitude_rank=2, with_bias=False) + prefix = "lin.paths.0." + if factor_dimension == "rank": + for tensor_name in ("Q_U_amp", "Q_V_amp"): + state[prefix + tensor_name] = state[prefix + tensor_name][:0] + else: + for tensor_name in ("A_amp", "B_amp", "Q_U_amp", "Q_V_amp"): + state[prefix + tensor_name] = state[prefix + tensor_name][:, :0] + save_directory = _write_quant_config(tmp_path, "mdbf", P=1) + + with pytest.raises(ValueError, match="positive rank and amplitude dimensions"): + _dequantize_mdbf_layers( + _MDBFDenseStub(with_bias=False), state, torch.float32, save_directory + ) + + +@pytest.mark.parametrize("tensor_name", ["_A_sign_shape", "_B_sign_shape"]) +def test_mdbf_dequantize_rejects_sign_shape_mismatch(tmp_path: Path, tensor_name: str) -> None: + """Persisted sign shapes must agree with reconstruction dimensions.""" + from onecomp.cpu.export.dequantize import _dequantize_mdbf_layers + + _, state = _build_mdbf_state(path_count=1, amplitude_rank=1, with_bias=False) + key = f"lin.paths.0.{tensor_name}" + state[key] = state[key] + 1 + save_directory = _write_quant_config(tmp_path, "mdbf", P=1) + + with pytest.raises(ValueError, match=tensor_name): + _dequantize_mdbf_layers( + _MDBFDenseStub(with_bias=False), state, torch.float32, save_directory + ) + + +def test_mdbf_layer_absent_from_dense_model_raises(tmp_path: Path) -> None: + """A checkpoint MDBF layer without a dense target is a mapping error.""" + from onecomp.cpu.export.dequantize import _dequantize_mdbf_layers + + _, state = _build_mdbf_state(path_count=1, amplitude_rank=1, with_bias=False) + save_directory = _write_quant_config(tmp_path, "mdbf", P=1) + + with pytest.raises(RuntimeError, match="no matching nn.Linear"): + _dequantize_mdbf_layers(torch.nn.Module(), state, torch.float32, save_directory) + + def test_hadamard_defold_roundtrip(): """De-fold inverts the online down_proj Hadamard applied during rotation.""" from onecomp.cpu.export.rotation import defold_down_proj_hadamard From b4a89a8af92e05a4833ed3da107f0a92405525ed Mon Sep 17 00:00:00 2001 From: katari Date: Wed, 12 Aug 2026 13:06:57 +0900 Subject: [PATCH 07/17] [test] share MDBF checkpoint fixtures --- tests/onecomp/cpu/test_export_routing.py | 35 ++-- tests/onecomp/fixtures/mdbf_checkpoint.py | 145 ++++++++++++++ .../runner/test_mdbf_save_load_roundtrip.py | 186 ++++-------------- 3 files changed, 189 insertions(+), 177 deletions(-) create mode 100644 tests/onecomp/fixtures/mdbf_checkpoint.py diff --git a/tests/onecomp/cpu/test_export_routing.py b/tests/onecomp/cpu/test_export_routing.py index b646877a..92e7f762 100644 --- a/tests/onecomp/cpu/test_export_routing.py +++ b/tests/onecomp/cpu/test_export_routing.py @@ -48,35 +48,22 @@ def _build_mdbf_state( Returns: Reference MDBF layer and its state dict under the ``lin`` prefix. """ - from onecomp.quantizer.mdbf.initialize import MDBFParams from onecomp.quantizer.mdbf.mdbf_layer import MultipathMDBFLinear - - def _make_params(seed: int) -> MDBFParams: - """Build deterministic MDBF parameters for one path.""" - generator = torch.Generator().manual_seed(seed) - - def _sign(*shape: int) -> torch.Tensor: - """Build a deterministic sign tensor.""" - values = torch.randint(0, 2, shape, generator=generator) - return (values * 2 - 1).to(torch.float32) - - def _amplitude(*shape: int) -> torch.Tensor: - """Build a deterministic positive amplitude tensor.""" - return torch.rand(*shape, generator=generator) + 0.5 - - return MDBFParams( - A_sign=_sign(_MDBF_OUT_FEATURES, _MDBF_RANK), - B_sign=_sign(_MDBF_RANK, _MDBF_IN_FEATURES), - A_amp=_amplitude(_MDBF_OUT_FEATURES, amplitude_rank), - B_amp=_amplitude(_MDBF_IN_FEATURES, amplitude_rank), - Q_U_amp=_amplitude(_MDBF_RANK, amplitude_rank), - Q_V_amp=_amplitude(_MDBF_RANK, amplitude_rank), - ) + from tests.onecomp.fixtures.mdbf_checkpoint import make_mdbf_params bias_generator = torch.Generator().manual_seed(999) bias = torch.randn(_MDBF_OUT_FEATURES, generator=bias_generator) if with_bias else None reference = MultipathMDBFLinear( - [_make_params(seed) for seed in range(path_count)], + [ + make_mdbf_params( + _MDBF_OUT_FEATURES, + _MDBF_IN_FEATURES, + _MDBF_RANK, + amplitude_rank, + seed, + ) + for seed in range(path_count) + ], bias=bias, use_gemlite=False, ).eval() diff --git a/tests/onecomp/fixtures/mdbf_checkpoint.py b/tests/onecomp/fixtures/mdbf_checkpoint.py new file mode 100644 index 00000000..97da8587 --- /dev/null +++ b/tests/onecomp/fixtures/mdbf_checkpoint.py @@ -0,0 +1,145 @@ +"""Shared builders for tiny MDBF checkpoint tests. + +Copyright 2025-2026 Fujitsu Ltd. +""" + +import json +from pathlib import Path +from typing import Any + +import torch +from safetensors.torch import save_file + +from onecomp.quantizer.mdbf.initialize import MDBFParams +from onecomp.quantizer.mdbf.mdbf_layer import MultipathMDBFLinear + +TARGET_SUFFIXES = ("self_attn.q_proj", "mlp.down_proj") +MDBF_RANK = 8 +MDBF_PATHS = 2 + + +def make_mdbf_params(n: int, m: int, r: int, l: int, seed: int) -> MDBFParams: + """Build deterministic, non-degenerate parameters for one MDBF path. + + Args: + n: Output features. + m: Input features. + r: Decomposition rank. + l: Multi-scale amplitude rank. + seed: RNG seed. + + Returns: + MDBF parameters with sign matrices and positive amplitudes. + """ + generator = torch.Generator().manual_seed(seed) + + def _sign(*shape: int) -> torch.Tensor: + """Build a deterministic sign tensor.""" + return torch.where(torch.randn(*shape, generator=generator) > 0, 1.0, -1.0) + + def _amplitude(*shape: int) -> torch.Tensor: + """Build a deterministic positive amplitude tensor.""" + return torch.rand(*shape, generator=generator) + 0.5 + + return MDBFParams( + A_sign=_sign(n, r), + B_sign=_sign(r, m), + A_amp=_amplitude(n, l), + B_amp=_amplitude(m, l), + Q_U_amp=_amplitude(r, l), + Q_V_amp=_amplitude(r, l), + ) + + +def build_mdbf_model(*, with_bias: bool) -> tuple[torch.nn.Module, Any, list[str]]: + """Build a tiny Llama with selected linears replaced by MDBF layers. + + Args: + with_bias: Whether replaced linears carry bias. + + Returns: + Model, config, and quantized layer names. + """ + from transformers import LlamaConfig, LlamaForCausalLM + + config = LlamaConfig( + hidden_size=16, + num_attention_heads=4, + num_hidden_layers=2, + num_key_value_heads=4, + intermediate_size=32, + max_position_embeddings=16, + vocab_size=32, + tie_word_embeddings=False, + attention_bias=with_bias, + mlp_bias=with_bias, + ) + config.torch_dtype = torch.float16 + model = LlamaForCausalLM(config).to(torch.float16).eval() + + name_to_module = dict(model.named_modules()) + quantized_names: list[str] = [] + for layer_index in range(config.num_hidden_layers): + for suffix in TARGET_SUFFIXES: + name = f"model.layers.{layer_index}.{suffix}" + quantized_names.append(name) + parent_name, _, child_name = name.rpartition(".") + parent = name_to_module[parent_name] + linear = getattr(parent, child_name) + bias = linear.bias.detach().clone() if linear.bias is not None else None + params_list = [ + make_mdbf_params( + linear.out_features, + linear.in_features, + MDBF_RANK, + 1, + seed=1000 * layer_index + 7 * path_index + len(suffix), + ) + for path_index in range(MDBF_PATHS) + ] + setattr( + parent, + child_name, + MultipathMDBFLinear(params_list, bias=bias, use_gemlite=False), + ) + + return model, config, quantized_names + + +def write_mdbf_save_dir( + save_dir: Path, + config: Any, + state_dict: dict[str, torch.Tensor], + quantized_names: list[str], + *, + record_paths: bool = True, + rotated: bool = False, +) -> None: + """Persist a tiny MDBF checkpoint. + + Args: + save_dir: Destination directory. + config: Model configuration. + state_dict: Tensors to save. + quantized_names: MDBF layer names. + record_paths: Whether to record the configured path count. + rotated: Whether to mark the checkpoint as rotated. + """ + save_dir.mkdir(parents=True, exist_ok=True) + + config_dict = config.to_dict() + config_dict["torch_dtype"] = "float16" + config_dict["quantization_config"] = { + "quant_method": "mdbf", + "bits": 2.0, + "l": 1, + "modules_in_block_to_quantize": quantized_names, + "rotated": rotated, + } + if record_paths: + config_dict["quantization_config"]["P"] = MDBF_PATHS + (save_dir / "config.json").write_text(json.dumps(config_dict, indent=2), encoding="utf-8") + save_file( + {key: tensor.contiguous() for key, tensor in state_dict.items()}, + str(save_dir / "model.safetensors"), + ) diff --git a/tests/onecomp/runner/test_mdbf_save_load_roundtrip.py b/tests/onecomp/runner/test_mdbf_save_load_roundtrip.py index 8860f29c..3ba51e24 100644 --- a/tests/onecomp/runner/test_mdbf_save_load_roundtrip.py +++ b/tests/onecomp/runner/test_mdbf_save_load_roundtrip.py @@ -22,147 +22,17 @@ import pytest import torch -from safetensors.torch import save_file from onecomp.pre_process.hadamard_utils import get_hadK, matmul_hadU_cuda from onecomp.quantized_model_loader import QuantizedModelLoader from onecomp.quantizer.mdbf.config import resolve_mdbf_paths -from onecomp.quantizer.mdbf.initialize import MDBFParams from onecomp.quantizer.mdbf.mdbf_layer import MDBFLinear, MultipathMDBFLinear - -# Layers replaced by MDBF in the tiny test model (one attention, one MLP -# projection) - enough to cover both square and rectangular weight shapes. -TARGET_SUFFIXES = ("self_attn.q_proj", "mlp.down_proj") -MDBF_RANK = 8 -MDBF_PATHS = 2 - - -def _make_params(n: int, m: int, r: int, l: int, seed: int) -> MDBFParams: - """Build deterministic, non-degenerate MDBF parameters for one path. - - Args: - n: Output features. - m: Input features. - r: Decomposition rank. - l: Multi-scale amplitude rank. - seed: RNG seed making the tensors reproducible across runs. - - Returns: - MDBFParams with +-1 sign matrices and strictly positive amplitudes. - """ - g = torch.Generator().manual_seed(seed) - - def _sign(*shape: int) -> torch.Tensor: - return torch.where(torch.randn(*shape, generator=g) > 0, 1.0, -1.0) - - def _amp(*shape: int) -> torch.Tensor: - # Offset away from 0 so an "all-zero buffer" check cannot pass by luck. - return torch.rand(*shape, generator=g) + 0.5 - - return MDBFParams( - A_sign=_sign(n, r), - B_sign=_sign(r, m), - A_amp=_amp(n, l), - B_amp=_amp(m, l), - Q_U_amp=_amp(r, l), - Q_V_amp=_amp(r, l), - ) - - -def _build_mdbf_model(*, with_bias: bool) -> tuple[torch.nn.Module, Any, list[str]]: - """Build a tiny Llama whose target linears are MultipathMDBFLinear. - - Args: - with_bias: Whether the replaced linears carry a bias buffer. - - Returns: - (model, config, quantized_layer_names) - """ - from transformers import LlamaConfig, LlamaForCausalLM - - config = LlamaConfig( - hidden_size=16, - num_attention_heads=4, - num_hidden_layers=2, - num_key_value_heads=4, - intermediate_size=32, - max_position_embeddings=16, - vocab_size=32, - tie_word_embeddings=False, - attention_bias=with_bias, - mlp_bias=with_bias, - ) - config.torch_dtype = torch.float16 - model = LlamaForCausalLM(config).to(torch.float16).eval() - - name_to_module = dict(model.named_modules()) - quantized_names: list[str] = [] - for layer_idx in range(config.num_hidden_layers): - for suffix in TARGET_SUFFIXES: - name = f"model.layers.{layer_idx}.{suffix}" - quantized_names.append(name) - parent_name, _, child_name = name.rpartition(".") - parent = name_to_module[parent_name] - linear = getattr(parent, child_name) - bias = linear.bias.detach().clone() if linear.bias is not None else None - params_list = [ - _make_params( - linear.out_features, - linear.in_features, - MDBF_RANK, - 1, - seed=1000 * layer_idx + 7 * p + len(suffix), - ) - for p in range(MDBF_PATHS) - ] - setattr( - parent, - child_name, - MultipathMDBFLinear(params_list, bias=bias, use_gemlite=False), - ) - - return model, config, quantized_names - - -def _write_save_dir( - save_dir: Path, - config: Any, - state_dict: dict, - quantized_names: list[str], - *, - record_paths: bool = True, - rotated: bool = False, -) -> None: - """Persist an MDBF checkpoint the loader can consume. - - Args: - save_dir: Directory to write config.json and model.safetensors into. - config: The model's ``PretrainedConfig``. - state_dict: Tensors to save. - quantized_names: Layers recorded as MDBF-quantized. - record_paths: Whether to record ``P`` the way the quantizer does. - Set False to emulate a hand-written or partial config that omits it. - rotated: Mark the checkpoint as rotation-preprocessed, which makes the - loader register online Hadamard hooks on ``down_proj``. - """ - save_dir.mkdir(parents=True, exist_ok=True) - - cfg_dict = config.to_dict() - cfg_dict["torch_dtype"] = "float16" - cfg_dict["quantization_config"] = { - "quant_method": "mdbf", - "bits": 2.0, - "l": 1, - "modules_in_block_to_quantize": quantized_names, - "rotated": rotated, - } - if record_paths: - cfg_dict["quantization_config"]["P"] = MDBF_PATHS - (save_dir / "config.json").write_text(json.dumps(cfg_dict, indent=2), encoding="utf-8") - save_file( - {k: v.contiguous() for k, v in state_dict.items()}, - str(save_dir / "model.safetensors"), - ) +from tests.onecomp.fixtures.mdbf_checkpoint import ( + MDBF_PATHS, + build_mdbf_model, + make_mdbf_params, + write_mdbf_save_dir, +) def _load(save_dir: Path) -> tuple[torch.nn.Module, Any]: @@ -187,11 +57,12 @@ def test_mdbf_checkpoint_round_trips_through_loader(tmp_path: Path, with_bias: b nested tensor set, otherwise ``from_saved_state`` rebuilds an empty layer (or the loader fails outright). """ - reference, config, quantized_names = _build_mdbf_model(with_bias=with_bias) + reference, config, quantized_names = build_mdbf_model(with_bias=with_bias) save_dir = tmp_path / "mdbf_model" - _write_save_dir(save_dir, config, reference.state_dict(), quantized_names) + write_mdbf_save_dir(save_dir, config, reference.state_dict(), quantized_names) - input_ids = torch.randint(0, config.vocab_size, (1, 8)) + generator = torch.Generator().manual_seed(100) + input_ids = torch.randint(0, config.vocab_size, (1, 8), generator=generator) with torch.no_grad(): expected_logits = reference(input_ids).logits.float() @@ -233,9 +104,9 @@ def test_rotated_mdbf_checkpoint_loads_with_working_hadamard_hooks(tmp_path: Pat ``nn.ModuleList`` into ``layers_cls`` and *zero* hooks were registered, with no error, so only an assertion on the live model catches it. """ - reference, config, quantized_names = _build_mdbf_model(with_bias=False) + reference, config, quantized_names = build_mdbf_model(with_bias=False) save_dir = tmp_path / "rotated_mdbf_model" - _write_save_dir(save_dir, config, reference.state_dict(), quantized_names, rotated=True) + write_mdbf_save_dir(save_dir, config, reference.state_dict(), quantized_names, rotated=True) model, _ = _load(save_dir) loaded_modules = dict(model.named_modules()) @@ -253,7 +124,8 @@ def test_rotated_mdbf_checkpoint_loads_with_working_hadamard_hooks(tmp_path: Pat # rotated weights were built against. ``forward`` is called unbound to # bypass the hook and obtain the untransformed reference. down_proj = down_projs[0] - x = torch.randn(2, config.intermediate_size) + generator = torch.Generator().manual_seed(101) + x = torch.randn(2, config.intermediate_size, generator=generator) had_K, K = get_hadK(down_proj.in_features) y_hooked = down_proj(x) assert not torch.allclose(y_hooked, MultipathMDBFLinear.forward(down_proj, x)) @@ -271,7 +143,7 @@ def test_load_rejects_checkpoint_missing_a_whole_path(tmp_path: Path) -> None: with fewer passes - every remaining buffer is correctly populated, so no post-load buffer check can notice. Only the config's recorded P can. """ - reference, config, quantized_names = _build_mdbf_model(with_bias=False) + reference, config, quantized_names = build_mdbf_model(with_bias=False) victim = quantized_names[0] state_dict = { key: tensor @@ -279,7 +151,7 @@ def test_load_rejects_checkpoint_missing_a_whole_path(tmp_path: Path) -> None: if not key.startswith(f"{victim}.paths.{MDBF_PATHS - 1}.") } save_dir = tmp_path / "mdbf_model" - _write_save_dir(save_dir, config, state_dict, quantized_names) + write_mdbf_save_dir(save_dir, config, state_dict, quantized_names) with pytest.raises(ValueError, match="Incomplete MDBF checkpoint"): _load(save_dir) @@ -311,9 +183,15 @@ def test_load_accepts_complete_checkpoint_when_config_omits_p(tmp_path: Path) -> checkpoint in that case is deliberately not pinned here - the skip is a back-compat concession, not a promise to accept damaged tensors. """ - reference, config, quantized_names = _build_mdbf_model(with_bias=False) + reference, config, quantized_names = build_mdbf_model(with_bias=False) save_dir = tmp_path / "mdbf_model" - _write_save_dir(save_dir, config, reference.state_dict(), quantized_names, record_paths=False) + write_mdbf_save_dir( + save_dir, + config, + reference.state_dict(), + quantized_names, + record_paths=False, + ) model, _ = _load(save_dir) @@ -329,13 +207,13 @@ def test_load_rejects_checkpoint_missing_bias(tmp_path: Path) -> None: bias" to ``from_saved_state``; the model's own ``nn.Linear`` is the only source of truth for which one it is. """ - reference, config, quantized_names = _build_mdbf_model(with_bias=True) + reference, config, quantized_names = build_mdbf_model(with_bias=True) victim = quantized_names[0] state_dict = { key: tensor for key, tensor in reference.state_dict().items() if key != f"{victim}.bias" } save_dir = tmp_path / "mdbf_model" - _write_save_dir(save_dir, config, state_dict, quantized_names) + write_mdbf_save_dir(save_dir, config, state_dict, quantized_names) with pytest.raises(ValueError, match="bias mismatch"): _load(save_dir) @@ -343,14 +221,14 @@ def test_load_rejects_checkpoint_missing_bias(tmp_path: Path) -> None: def test_load_rejects_unexpected_bias_in_checkpoint(tmp_path: Path) -> None: """A bias the model has no place for is a mismatch too, not a silent drop.""" - reference, config, quantized_names = _build_mdbf_model(with_bias=False) + reference, config, quantized_names = build_mdbf_model(with_bias=False) victim = quantized_names[0] state_dict = dict(reference.state_dict()) state_dict[f"{victim}.bias"] = torch.zeros( dict(reference.named_modules())[victim].n, dtype=torch.float16 ) save_dir = tmp_path / "mdbf_model" - _write_save_dir(save_dir, config, state_dict, quantized_names) + write_mdbf_save_dir(save_dir, config, state_dict, quantized_names) with pytest.raises(ValueError, match="bias mismatch"): _load(save_dir) @@ -417,7 +295,9 @@ def _wrap_in_module(layer: torch.nn.Module) -> torch.nn.Module: def _saved_layer_state() -> dict: """Build the per-layer state_dict of a small MultipathMDBFLinear.""" - params_list = [_make_params(6, 4, 3, 1, seed=10 + p) for p in range(MDBF_PATHS)] + params_list = [ + make_mdbf_params(6, 4, 3, 1, seed=10 + path_index) for path_index in range(MDBF_PATHS) + ] return MultipathMDBFLinear(params_list, use_gemlite=False).state_dict() @@ -464,7 +344,7 @@ def test_resolve_mdbf_layer_bits_uses_saved_layer_name(tmp_path: Path) -> None: reaches ``resolve_mdbf_layer_bits`` - the sibling GPTQ/DBF branches pass the same one. """ - reference, config, model_names = _build_mdbf_model(with_bias=False) + reference, config, model_names = build_mdbf_model(with_bias=False) saved_names = { name: name.replace("model.layers.", "model.decoder.layers.") for name in model_names } @@ -478,7 +358,7 @@ def test_resolve_mdbf_layer_bits_uses_saved_layer_name(tmp_path: Path) -> None: state_dict[key] = tensor save_dir = tmp_path / "mdbf_model" - _write_save_dir(save_dir, config, state_dict, sorted(saved_names.values())) + write_mdbf_save_dir(save_dir, config, state_dict, sorted(saved_names.values())) cfg_path = save_dir / "config.json" cfg_dict = json.loads(cfg_path.read_text(encoding="utf-8")) From 0629497d3f4b19b8da59806092abaeb45521c20c Mon Sep 17 00:00:00 2001 From: katari Date: Thu, 13 Aug 2026 09:54:38 +0900 Subject: [PATCH 08/17] [test] verify MDBF dense export end to end --- tests/onecomp/cpu/test_export_routing.py | 66 +++++++++++++++++++++++ tests/onecomp/fixtures/mdbf_checkpoint.py | 4 +- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/tests/onecomp/cpu/test_export_routing.py b/tests/onecomp/cpu/test_export_routing.py index 92e7f762..3e140ffe 100644 --- a/tests/onecomp/cpu/test_export_routing.py +++ b/tests/onecomp/cpu/test_export_routing.py @@ -427,6 +427,72 @@ def test_mdbf_layer_absent_from_dense_model_raises(tmp_path: Path) -> None: _dequantize_mdbf_layers(torch.nn.Module(), state, torch.float32, save_directory) +@pytest.mark.parametrize( + "rotated,torch_dtype,rtol,atol", + [ + pytest.param(False, torch.float32, 1e-4, 1e-3, id="plain-fp32"), + pytest.param(True, torch.float32, 1e-4, 1e-3, id="rotated-fp32"), + pytest.param(False, torch.float16, 0.0, 5e-3, id="plain-fp16"), + ], +) +def test_mdbf_dequantize_to_hf_matches_loader_logits( + tmp_path: Path, + rotated: bool, + torch_dtype: torch.dtype, + rtol: float, + atol: float, +) -> None: + """Dense export matches MDBF loader logits for plain and rotated models. + + FP16 uses absolute tolerance because prototype relative error reached 15.7% + near zero while the worst absolute error was one ULP (9.766e-4). + """ + from transformers import AutoModelForCausalLM + + from onecomp.cpu.export.dequantize import dequantize_to_hf + from onecomp.quantized_model_loader import QuantizedModelLoader + from tests.onecomp.fixtures.mdbf_checkpoint import ( + build_mdbf_model, + write_mdbf_save_dir, + ) + + reference, config, quantized_names = build_mdbf_model(with_bias=False) + checkpoint_dir = tmp_path / "checkpoint" + dense_dir = tmp_path / "dense" + write_mdbf_save_dir( + checkpoint_dir, + config, + reference.state_dict(), + quantized_names, + rotated=rotated, + ) + + with patch( + "onecomp.quantized_model_loader.AutoTokenizer.from_pretrained", + return_value=object(), + ): + quantized_model, _ = QuantizedModelLoader.load_quantized_model( + str(checkpoint_dir), + device_map="", + local_files_only=True, + ) + quantized_model.to(dtype=torch_dtype).eval() + + dequantize_to_hf(str(checkpoint_dir), str(dense_dir), torch_dtype=torch_dtype) + dense_model = AutoModelForCausalLM.from_pretrained( + dense_dir, + torch_dtype=torch_dtype, + local_files_only=True, + ).eval() + + generator = torch.Generator().manual_seed(102) + input_ids = torch.randint(0, config.vocab_size, (2, 8), generator=generator) + with torch.no_grad(): + expected = quantized_model(input_ids).logits.float() + actual = dense_model(input_ids).logits.float() + torch.testing.assert_close(actual, expected, rtol=rtol, atol=atol) + + def test_hadamard_defold_roundtrip(): """De-fold inverts the online down_proj Hadamard applied during rotation.""" from onecomp.cpu.export.rotation import defold_down_proj_hadamard diff --git a/tests/onecomp/fixtures/mdbf_checkpoint.py b/tests/onecomp/fixtures/mdbf_checkpoint.py index 97da8587..6f372f39 100644 --- a/tests/onecomp/fixtures/mdbf_checkpoint.py +++ b/tests/onecomp/fixtures/mdbf_checkpoint.py @@ -75,7 +75,9 @@ def build_mdbf_model(*, with_bias: bool) -> tuple[torch.nn.Module, Any, list[str mlp_bias=with_bias, ) config.torch_dtype = torch.float16 - model = LlamaForCausalLM(config).to(torch.float16).eval() + with torch.random.fork_rng(devices=[]): + torch.manual_seed(0) + model = LlamaForCausalLM(config).to(torch.float16).eval() name_to_module = dict(model.named_modules()) quantized_names: list[str] = [] From 9bff77a995f9c112a33f29edc8e07f825f15361c Mon Sep 17 00:00:00 2001 From: katari Date: Thu, 13 Aug 2026 11:25:39 +0900 Subject: [PATCH 09/17] [docs] document MDBF GGUF fallback export --- CHANGELOG.md | 10 ++++++++++ docs/user-guide/cpu-inference.md | 14 ++++++++++++-- onecomp/cpu/README.md | 13 +++++++++---- onecomp/cpu/__init__.py | 2 +- onecomp/cpu/export/__init__.py | 4 ++-- onecomp/cpu/export/auto.py | 10 +++++----- onecomp/cpu/export/dequantize.py | 10 ++++------ onecomp/cpu/export/fallback.py | 7 +++---- 8 files changed, 46 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 556995d1..c61a882a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Change log +## [v1.3.2(WIP)+feature/mdbf-llamacpp-export] 2026-08-12 + +### New Features + +- Added GGUF fallback export for plain and rotated MDBF checkpoints by reconstructing dense weights before llama.cpp conversion. + +### Bug Fixes + +- Reject forced `direct` and `mixed` GGUF modes for non-GPTQ layouts and rotated checkpoints instead of entering an incompatible exporter. + ## [v1.3.1] 2026-08-06 ### Bug Fix diff --git a/docs/user-guide/cpu-inference.md b/docs/user-guide/cpu-inference.md index 860a32d9..448d0026 100644 --- a/docs/user-guide/cpu-inference.md +++ b/docs/user-guide/cpu-inference.md @@ -37,9 +37,9 @@ export_to_gguf("./model", "./model.gguf") # mode="auto" by default | `jointq`, `rtn` | same AutoGPTQ layout | direct | yes | | `mixed_gptq` | per-layer bit-widths | mixed | 4/8-bit yes, 2/3-bit no | | `dbf`, `autobit` | binary factorization / mixed | fallback | no (re-quantized) | -| `gptq`/`mixed_gptq` + `rotated=true` | online Hadamard on down_proj | fallback | no (re-quantized) | +| `mdbf` | multi-path binary factorization | fallback | no (re-quantized) | +| supported method + `rotated=true` | online Hadamard on down_proj | fallback | no (re-quantized) | | `onebit` | — | unsupported (by request) | -| `mdbf` | — | unsupported (not implemented yet) | **QEP** only changes the GPTQ *integer codes* (via pre-quantization weight adjustment), so QEP-corrected checkpoints export through the very same lossless @@ -139,6 +139,16 @@ This **re-quantizes** the weights, so the GPTQ/QEP error correction is lost and quality is comparable to a stock `Q4_K_M` GGUF. It requires the `llama-quantize` binary (set `$LLAMA_QUANTIZE_BIN` or put it on `PATH`). +MDBF uses only this fallback path, including rotated checkpoints. The exporter +reconstructs dense weights and folds any online `down_proj` Hadamard into them, +so stock llama.cpp needs no MDBF-specific kernel. The resulting GGUF does not +retain MDBF's 1–2-bit compression. + +Re-quantizing MDBF weights to the default `Q4_K_M` adds another quantization +step while producing a larger file than the original MDBF checkpoint. To carry +the reconstructed weights with less added error, use `qtype=None` for f16 +(Python API only), or `Q8_0` with either Python or the CLI. + ## Running inference ```python diff --git a/onecomp/cpu/README.md b/onecomp/cpu/README.md index 67ab2fa7..29f1a5e0 100644 --- a/onecomp/cpu/README.md +++ b/onecomp/cpu/README.md @@ -11,7 +11,7 @@ onecomp/cpu/ ├── export/ # GGUF export │ ├── blocks.py lossless GPTQ-code -> GGUF legacy-block packing │ ├── checkpoint.py read an OneComp GPTQ checkpoint -> GPTQLayer -│ ├── dequantize.py GPTQ checkpoint -> dense fp16 HF model +│ ├── dequantize.py GPTQ/DBF/MDBF checkpoint -> dense fp16 HF model │ ├── skeleton.py build metadata/tokenizer skeleton GGUF + stitch tensors │ ├── direct.py direct, lossless GPTQ -> GGUF (preferred) │ └── fallback.py dequantize -> llama-quantize (universal, re-quantizes) @@ -73,9 +73,14 @@ convert_gptq_to_gguf("./model-gptq-4bit", "./model.gguf") ### 2. Dequantize → llama-quantize (fallback; re-quantizes) `export_via_dequantize` reconstructs fp16 weights and uses -`convert_hf_to_gguf.py` + `llama-quantize`. Works for any GPTQ checkpoint but -discards the GPTQ error correction (quality ≈ stock `Q4_K_M`). Needs the -`llama-quantize` binary (`$LLAMA_QUANTIZE_BIN` / PATH). +`convert_hf_to_gguf.py` + `llama-quantize`. It supports GPTQ, DBF, MDBF, +AutoBit, and rotated checkpoints, but re-quantization does not preserve their +original quantization. It needs the `llama-quantize` binary +(`$LLAMA_QUANTIZE_BIN` / PATH). + +MDBF supports only this fallback path. Its 1–2-bit compression is not retained +in GGUF; prefer `qtype=None` in Python to keep f16, or `Q8_0` to limit additional +quantization error. ```python from onecomp.cpu import export_via_dequantize diff --git a/onecomp/cpu/__init__.py b/onecomp/cpu/__init__.py index 9645bfd9..5a2a6026 100644 --- a/onecomp/cpu/__init__.py +++ b/onecomp/cpu/__init__.py @@ -11,7 +11,7 @@ export_to_gguf -- single entry: routes any supported checkpoint to GGUF convert_gptq_to_gguf -- direct, lossless GPTQ -> GGUF (preserves QEP codes) export_via_dequantize -- fallback: dequantize -> convert -> llama-quantize - dequantize_to_hf -- reconstruct a dense fp16 HF model (GPTQ/DBF/rotated) + dequantize_to_hf -- reconstruct a dense HF model (GPTQ/DBF/MDBF/rotated) LlamaCppModel -- CPU text generation on a GGUF model inspect_gguf -- per-tensor quant types / size / effective bit-width perplexity -- CPU perplexity of a GGUF model on text diff --git a/onecomp/cpu/export/__init__.py b/onecomp/cpu/export/__init__.py index 6bd0d0e8..9a8e41f5 100644 --- a/onecomp/cpu/export/__init__.py +++ b/onecomp/cpu/export/__init__.py @@ -1,9 +1,9 @@ -"""GGUF export for OneComp GPTQ checkpoints. +"""GGUF export for OneComp quantized checkpoints. Modules: blocks -- lossless packing of GPTQ codes into GGUF legacy blocks checkpoint -- read an OneComp GPTQ checkpoint into ``GPTQLayer`` objects - dequantize -- reconstruct a dense fp16 HF model from a GPTQ checkpoint + dequantize -- reconstruct a dense HF model from GPTQ/DBF/MDBF weights skeleton -- build a metadata/tokenizer skeleton GGUF and stitch tensors direct -- direct, lossless GPTQ -> GGUF export (preferred) fallback -- dequantize -> llama-quantize export (re-quantizes; universal) diff --git a/onecomp/cpu/export/auto.py b/onecomp/cpu/export/auto.py index bdffcc7d..64929570 100644 --- a/onecomp/cpu/export/auto.py +++ b/onecomp/cpu/export/auto.py @@ -9,6 +9,7 @@ mixed_gptq same, per-layer bitwidths mixed (lossless + K-quant) jointq / rtn same AutoGPTQ layout direct (lossless) dbf DoubleBinaryLinear (binary factors) fallback (dequantize) +mdbf multi-path binary factors fallback (dequantize) autobit mix of gptq/dbf children fallback (dequantize) ============ ==================================== ========================= @@ -21,7 +22,7 @@ (see :mod:`onecomp.cpu.export.rotation`) so the GGUF runs correctly with no online operation. -OneBit and MDBF are rejected up-front, for every ``mode``; see +OneBit is rejected up-front, for every ``mode``; see ``UNSUPPORTED_METHODS`` in :mod:`onecomp.cpu.export.checkpoint` for why. Copyright 2025-2026 Fujitsu Ltd. @@ -108,8 +109,7 @@ def export_to_gguf( out_gguf: Output ``.gguf`` path. mode: ``auto`` (route by quant_method/rotation) or force a path with ``direct`` / ``mixed`` / ``fallback``. Forcing a path does not - override support: an unsupported ``quant_method`` is rejected for - every ``mode``. + override support or layout requirements. qtype: target type for the fallback (dequantize) path, e.g. ``Q4_K_M``. original_model: optional original FP model dir for skeleton metadata. work_dir: scratch directory. @@ -118,8 +118,8 @@ def export_to_gguf( Summary dict including the chosen ``path`` and per-path details. Raises: - ValueError: If ``quant_method`` is unsupported (any ``mode``), or if - ``mode`` is not one of auto/direct/mixed/fallback. + ValueError: If the method or forced mode is incompatible, or ``mode`` + is not one of auto/direct/mixed/fallback. """ plan = plan_export(quantized_dir) meta = plan["meta"] diff --git a/onecomp/cpu/export/dequantize.py b/onecomp/cpu/export/dequantize.py index 5d80913f..7b803c2d 100644 --- a/onecomp/cpu/export/dequantize.py +++ b/onecomp/cpu/export/dequantize.py @@ -1,4 +1,4 @@ -"""Reconstruct a dense (fp16) Hugging Face model from an OneComp GPTQ checkpoint. +"""Reconstruct a dense Hugging Face model from an OneComp checkpoint. Used for (a) the dequantize -> convert_hf_to_gguf -> llama-quantize fallback path and (b) building a metadata/tokenizer "skeleton" GGUF when the original @@ -264,10 +264,10 @@ def dequantize_to_hf( output_directory: str, torch_dtype: torch.dtype = torch.float16, ) -> str: - """Write a dense HF model (dequantized GPTQ weights) to ``output_directory``. + """Write a dense HF model to ``output_directory``. Args: - save_directory: An OneComp quantized model directory (gptq/mixed_gptq). + save_directory: A supported OneComp quantized model directory. output_directory: Destination directory for the dense HF model. torch_dtype: dtype of the reconstructed dense weights. @@ -328,9 +328,7 @@ def dequantize_to_hf( quant_keys |= dbf_consumed # MDBF layers use a nested paths.{p}.* layout. - mdbf_dense, mdbf_consumed = _dequantize_mdbf_layers( - model, state, torch_dtype, save_directory - ) + mdbf_dense, mdbf_consumed = _dequantize_mdbf_layers(model, state, torch_dtype, save_directory) dense_state.update(mdbf_dense) quant_keys |= mdbf_consumed diff --git a/onecomp/cpu/export/fallback.py b/onecomp/cpu/export/fallback.py index 683f4071..8a9a75cc 100644 --- a/onecomp/cpu/export/fallback.py +++ b/onecomp/cpu/export/fallback.py @@ -1,8 +1,7 @@ """Fallback GGUF export via dequantization + llama-quantize. -This path works for any OneComp GPTQ checkpoint (including 2/3-bit, actorder, -and mixed bitwidths) but re-quantizes the weights, so it does not preserve the -GPTQ/QEP error correction. Prefer ``onecomp.cpu.export.direct.convert_gptq_to_gguf`` +This path reconstructs dense weights for supported checkpoints and then +re-quantizes them. Prefer ``onecomp.cpu.export.direct.convert_gptq_to_gguf`` when its constraints are met. Copyright 2025-2026 Fujitsu Ltd. @@ -33,7 +32,7 @@ def export_via_dequantize( """Dequantize -> f16 GGUF, then optionally quantize to ``qtype`` (e.g. Q4_K_M). Args: - quantized_dir: OneComp GPTQ checkpoint. + quantized_dir: Supported OneComp quantized checkpoint. out_gguf: Output GGUF path. qtype: If given, run llama-quantize to this type; otherwise keep f16. work_dir: Scratch dir. From 5b6c69c067145f2d0407ab7186d7b51c2785a8ed Mon Sep 17 00:00:00 2001 From: katari Date: Thu, 13 Aug 2026 12:18:01 +0900 Subject: [PATCH 10/17] [docs] refine MDBF export documentation and comments --- docs/user-guide/cpu-inference.md | 2 +- onecomp/cpu/export/dequantize.py | 4 ++-- tests/onecomp/cpu/test_export_routing.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/user-guide/cpu-inference.md b/docs/user-guide/cpu-inference.md index 448d0026..b6971486 100644 --- a/docs/user-guide/cpu-inference.md +++ b/docs/user-guide/cpu-inference.md @@ -39,7 +39,7 @@ export_to_gguf("./model", "./model.gguf") # mode="auto" by default | `dbf`, `autobit` | binary factorization / mixed | fallback | no (re-quantized) | | `mdbf` | multi-path binary factorization | fallback | no (re-quantized) | | supported method + `rotated=true` | online Hadamard on down_proj | fallback | no (re-quantized) | -| `onebit` | — | unsupported (by request) | +| `onebit` | — | unsupported (by request) | — | **QEP** only changes the GPTQ *integer codes* (via pre-quantization weight adjustment), so QEP-corrected checkpoints export through the very same lossless diff --git a/onecomp/cpu/export/dequantize.py b/onecomp/cpu/export/dequantize.py index 7b803c2d..fc73e5c9 100644 --- a/onecomp/cpu/export/dequantize.py +++ b/onecomp/cpu/export/dequantize.py @@ -275,8 +275,8 @@ def dequantize_to_hf( ``output_directory``. Raises: - ValueError: If the checkpoint's ``quant_method`` has no dense - reconstruction implemented here (see ``UNSUPPORTED_METHODS``). + ValueError: If the checkpoint's ``quant_method`` is unsupported, or an + MDBF path set or factor shape is invalid. RuntimeError: If an MDBF layer cannot be mapped to the dense model, or any weight/bias tensor ends up with no checkpoint source. """ diff --git a/tests/onecomp/cpu/test_export_routing.py b/tests/onecomp/cpu/test_export_routing.py index 3e140ffe..b69fbd1f 100644 --- a/tests/onecomp/cpu/test_export_routing.py +++ b/tests/onecomp/cpu/test_export_routing.py @@ -154,7 +154,7 @@ def test_export_to_gguf_rejects_unsupported(tmp_path, method, mode): ) @pytest.mark.parametrize("mode", ["direct", "mixed"]) def test_export_to_gguf_rejects_incompatible_forced_mode( - tmp_path, method: str, extra: dict[str, bool], mode: str + tmp_path: Path, method: str, extra: dict[str, bool], mode: str ) -> None: """A forced packed path must reject checkpoints without that capability.""" from onecomp.cpu.export.auto import export_to_gguf From 66430e916931746870e7ce055d69cc49474ca5a8 Mon Sep 17 00:00:00 2001 From: katari Date: Tue, 18 Aug 2026 14:41:05 +0900 Subject: [PATCH 11/17] [docs] correct CPU/GGUF export documentation --- CHANGELOG.md | 2 +- onecomp/cpu/README.md | 2 +- onecomp/cpu/export/__init__.py | 2 +- onecomp/cpu/export/checkpoint.py | 7 ++++--- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c61a882a..61294447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change log -## [v1.3.2(WIP)+feature/mdbf-llamacpp-export] 2026-08-12 +## [v1.3.2(WIP)+feature/mdbf-llamacpp-export] 2026-08-18 ### New Features diff --git a/onecomp/cpu/README.md b/onecomp/cpu/README.md index 29f1a5e0..2ff3a2f8 100644 --- a/onecomp/cpu/README.md +++ b/onecomp/cpu/README.md @@ -14,7 +14,7 @@ onecomp/cpu/ │ ├── dequantize.py GPTQ/DBF/MDBF checkpoint -> dense fp16 HF model │ ├── skeleton.py build metadata/tokenizer skeleton GGUF + stitch tensors │ ├── direct.py direct, lossless GPTQ -> GGUF (preferred) -│ └── fallback.py dequantize -> llama-quantize (universal, re-quantizes) +│ └── fallback.py dequantize -> llama-quantize (re-quantizes) ├── eval/ # CPU-side evaluation │ ├── inspect_gguf.py per-tensor quant types / size / effective bit-width │ ├── perplexity.py CPU perplexity on text diff --git a/onecomp/cpu/export/__init__.py b/onecomp/cpu/export/__init__.py index 9a8e41f5..a0e5827a 100644 --- a/onecomp/cpu/export/__init__.py +++ b/onecomp/cpu/export/__init__.py @@ -6,7 +6,7 @@ dequantize -- reconstruct a dense HF model from GPTQ/DBF/MDBF weights skeleton -- build a metadata/tokenizer skeleton GGUF and stitch tensors direct -- direct, lossless GPTQ -> GGUF export (preferred) - fallback -- dequantize -> llama-quantize export (re-quantizes; universal) + fallback -- dequantize -> llama-quantize export (re-quantizes) rotation -- fold a rotated model's online down_proj Hadamard into weights auto -- single entry point that routes by quant_method / rotation diff --git a/onecomp/cpu/export/checkpoint.py b/onecomp/cpu/export/checkpoint.py index 037d9caa..aa2b8d9f 100644 --- a/onecomp/cpu/export/checkpoint.py +++ b/onecomp/cpu/export/checkpoint.py @@ -184,10 +184,11 @@ def _per_layer_overrides(quant_config: dict) -> Dict[str, Dict[str, int]]: def iter_gptq_layers(save_directory: str) -> Iterator[GPTQLayer]: - """Yield every GPTQ-quantized linear in a saved OneComp model, fully unpacked. + """Yield every AutoGPTQ-layout linear in a saved OneComp model, fully unpacked. - Only ``gptq`` / ``mixed_gptq`` checkpoints expose ``qweight`` tensors; other - methods (dbf/mdbf/onebit) are skipped here and must use the dequantize path. + GPTQ-family checkpoints use ``qweight`` / ``qzeros`` / ``scales`` tensors. + Checkpoints without this layout yield no layers here. DBF and MDBF instead + use dedicated dense reconstruction helpers; OneBit is unsupported. """ quant_config = load_quant_config(save_directory) state = _load_state_dict(save_directory) From 9b2e325aff9de4603941e50caf108fe8ad3ca62d Mon Sep 17 00:00:00 2001 From: katari Date: Tue, 18 Aug 2026 14:41:37 +0900 Subject: [PATCH 12/17] [test] fix inaccurate docstrings and add type hints in CPU export tests --- tests/onecomp/cpu/test_export_routing.py | 41 +++++++++--------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/tests/onecomp/cpu/test_export_routing.py b/tests/onecomp/cpu/test_export_routing.py index b69fbd1f..02d7e2fa 100644 --- a/tests/onecomp/cpu/test_export_routing.py +++ b/tests/onecomp/cpu/test_export_routing.py @@ -1,9 +1,6 @@ -"""Unit tests for CPU/GGUF export routing, DBF dequant and rotation de-folding. +"""Tests for CPU/GGUF export routing, dense reconstruction, and rotation de-folding. -These run without any model download or llama.cpp build: - * ``read_quant_meta`` / ``plan_export`` route each quant_method correctly, - * the DBF dequantize matches ``DoubleBinaryLinear`` forward, and - * the rotation Hadamard de-fold inverts the online transform exactly. +These tests require no model download or llama.cpp build. Copyright 2025-2026 Fujitsu Ltd. """ @@ -132,13 +129,12 @@ def test_needs_mixed_export_helpers(): ) == {4, 2} -@pytest.mark.parametrize("method", ["onebit"]) @pytest.mark.parametrize("mode", ["auto", "direct", "mixed", "fallback"]) -def test_export_to_gguf_rejects_unsupported(tmp_path, method, mode): +def test_export_to_gguf_rejects_unsupported(tmp_path: Path, mode: str) -> None: """An explicit ``mode`` names a path, not a capability: it must not bypass the guard.""" from onecomp.cpu.export.auto import export_to_gguf - d = _write_quant_config(tmp_path, method) + d = _write_quant_config(tmp_path, "onebit") with pytest.raises(ValueError, match="not supported"): export_to_gguf(d, str(tmp_path / "out.gguf"), mode=mode) @@ -178,17 +174,16 @@ def test_export_to_gguf_mdbf_dispatches_fallback(tmp_path: Path, mode: str) -> N assert result["path"] == "fallback" -@pytest.mark.parametrize("method", ["onebit"]) -def test_dequantize_to_hf_rejects_unsupported(tmp_path, method): +def test_dequantize_to_hf_rejects_unsupported(tmp_path: Path) -> None: """The low-level entry point guards too; it is public and reached via other paths.""" from onecomp.cpu.export.dequantize import dequantize_to_hf - d = _write_quant_config(tmp_path, method) + d = _write_quant_config(tmp_path, "onebit") with pytest.raises(ValueError, match="no dense reconstruction"): dequantize_to_hf(d, str(tmp_path / "dense")) -def test_reject_unfilled_weights_flags_random_init_tensors(): +def test_reject_unfilled_weights_flags_random_init_tensors() -> None: """An unknown layout leaves dense weights unsourced; that must raise, not warn.""" from onecomp.cpu.export.dequantize import _reject_unfilled_weights @@ -201,23 +196,19 @@ def test_reject_unfilled_weights_flags_random_init_tensors(): _reject_unfilled_weights(missing, set(), "/ckpt", "future_method") -def test_reject_unfilled_weights_ignores_buffers_and_retied_lm_head(): +def test_reject_unfilled_weights_ignores_buffers_and_retied_lm_head() -> None: + """Buffers and weights restored by tying are not unfilled parameters.""" from onecomp.cpu.export.dequantize import _reject_unfilled_weights _reject_unfilled_weights(["model.rotary_emb.inv_freq"], set(), "/ckpt", "gptq") - _reject_unfilled_weights( - ["lm_head.weight"], {"lm_head.weight"}, "/ckpt", "gptq" - ) # restored by tie_weights() + _reject_unfilled_weights(["lm_head.weight"], {"lm_head.weight"}, "/ckpt", "gptq") -def test_dequantize_to_hf_rejects_unknown_layout_end_to_end(tmp_path): - """``UNSUPPORTED_METHODS`` is an allow-list of *known* gaps; this pins the net. +def test_dequantize_to_hf_rejects_unknown_layout_end_to_end(tmp_path: Path) -> None: + """An unrecognized tensor layout must fail before saving a random-init model. - A quant_method nobody listed (a future quantizer, or MDBF children hidden - inside an ``autobit`` checkpoint) reaches the dequantize body, drops its - tensors and leaves the dense weights at ``from_config`` random init. Only an - end-to-end call proves ``_reject_unfilled_weights`` is actually wired into - ``dequantize_to_hf``; the unit tests above pass even if the call is deleted. + This end-to-end call proves ``_reject_unfilled_weights`` remains wired into + ``dequantize_to_hf`` for layouts no supported reconstructor recognizes. """ from safetensors.torch import save_file from transformers import LlamaConfig @@ -243,8 +234,8 @@ def test_dequantize_to_hf_rejects_unknown_layout_end_to_end(tmp_path): cfg_dict["quantization_config"] = {"quant_method": "future_method", "bits": 2} (ckpt / "config.json").write_text(json.dumps(cfg_dict), encoding="utf-8") - # A layer stored in some unknown factorized form: no ``.weight``, and keys - # neither the GPTQ nor the DBF reader recognises. + # This unknown factorization has no ``.weight``, and none of the supported + # reconstructors recognizes its keys. save_file( { "model.layers.0.self_attn.q_proj.factor_a": torch.zeros(16, 4), From 48156e437647c3cbbfb3c864841af838ce02f4be Mon Sep 17 00:00:00 2001 From: katari Date: Wed, 19 Aug 2026 11:27:54 +0900 Subject: [PATCH 13/17] [fix] harden MDBF export validation --- onecomp/cpu/export/dequantize.py | 26 +++++++++++++++++------- tests/onecomp/cpu/test_export_routing.py | 16 +++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/onecomp/cpu/export/dequantize.py b/onecomp/cpu/export/dequantize.py index fc73e5c9..a9cad42b 100644 --- a/onecomp/cpu/export/dequantize.py +++ b/onecomp/cpu/export/dequantize.py @@ -149,8 +149,12 @@ def _raise_shape(path_index: int, tensor_name: str, actual: object, expected: ob tensor = layer_state_dict.get(prefix + tensor_name) if tensor is None: continue - actual_shape = tuple(int(dim) for dim in tensor.reshape(-1).tolist()) - if tuple(tensor.shape) != (2,) or actual_shape != expected_shape: + tensor_shape = tuple(tensor.shape) + if tensor_shape != (2,): + _raise_shape(path_index, tensor_name, tensor_shape, (2,)) + + actual_shape = tuple(int(dim) for dim in tensor.tolist()) + if actual_shape != expected_shape: _raise_shape(path_index, tensor_name, actual_shape, expected_shape) @@ -173,7 +177,8 @@ def _dequantize_mdbf_layers( Raises: KeyError: If a required MDBF tensor is absent. - ValueError: If the checkpoint is incomplete or has invalid shapes. + ValueError: If MDBF metadata, path or bias presence, or factor shapes + are inconsistent with the dense model. RuntimeError: If an MDBF layer has no matching dense module. """ marker_keys = sorted(key for key in state if key.endswith(_MDBF_MARKER)) @@ -191,6 +196,9 @@ def _dequantize_mdbf_layers( for marker_key in marker_keys: name = marker_key[: -len(_MDBF_MARKER)] target = modules.get(name) + # Treat either an absent target or one without Linear-compatible input + # dimensions as a mapping failure. Only the absent case is invisible to + # _reject_unfilled_weights(), but neither can be reconstructed safely. if target is None or not hasattr(target, "in_features"): raise RuntimeError( f"MDBF layer {name!r} from {save_directory} has no matching " @@ -200,6 +208,8 @@ def _dequantize_mdbf_layers( in_features = int(target.in_features) out_features = int(target.out_features) prefix = name + "." + # Preserve paths.{p} in relative keys so tensors from different paths + # do not collide as they would if only the final component were kept. layer_state_dict = { key[len(prefix) :]: tensor for key, tensor in state.items() if key.startswith(prefix) } @@ -216,6 +226,7 @@ def _dequantize_mdbf_layers( layer_state_dict, in_features, out_features ).eval() with torch.no_grad(): + # Accumulate over the factor rank and across paths in fp32, then cast. weight = layer.get_weight(torch.float32) dense[f"{name}.weight"] = weight.to(torch_dtype) bias = layer_state_dict.get("bias") @@ -275,10 +286,11 @@ def dequantize_to_hf( ``output_directory``. Raises: - ValueError: If the checkpoint's ``quant_method`` is unsupported, or an - MDBF path set or factor shape is invalid. - RuntimeError: If an MDBF layer cannot be mapped to the dense model, or - any weight/bias tensor ends up with no checkpoint source. + KeyError: If a required quantized tensor is absent. + ValueError: If the quantization method is unsupported, or MDBF + metadata, paths, bias presence, or factor shapes are invalid. + RuntimeError: If a quantized layer cannot be mapped to the dense model, + or a weight or bias has no checkpoint source. """ from safetensors.torch import load_file from transformers import AutoConfig, AutoModelForCausalLM diff --git a/tests/onecomp/cpu/test_export_routing.py b/tests/onecomp/cpu/test_export_routing.py index 02d7e2fa..0c4013c8 100644 --- a/tests/onecomp/cpu/test_export_routing.py +++ b/tests/onecomp/cpu/test_export_routing.py @@ -407,6 +407,22 @@ def test_mdbf_dequantize_rejects_sign_shape_mismatch(tmp_path: Path, tensor_name ) +@pytest.mark.parametrize("tensor_name", ["_A_sign_shape", "_B_sign_shape"]) +def test_mdbf_dequantize_rejects_sign_shape_buffer_shape(tmp_path: Path, tensor_name: str) -> None: + """Malformed sign-shape buffers report shape without expanding values.""" + from onecomp.cpu.export.dequantize import _dequantize_mdbf_layers + + _, state = _build_mdbf_state(path_count=1, amplitude_rank=1, with_bias=False) + key = f"lin.paths.0.{tensor_name}" + state[key] = state[key].unsqueeze(0) + save_directory = _write_quant_config(tmp_path, "mdbf", P=1) + + with pytest.raises(ValueError, match=r"expected \(2,\), got \(1, 2\)"): + _dequantize_mdbf_layers( + _MDBFDenseStub(with_bias=False), state, torch.float32, save_directory + ) + + def test_mdbf_layer_absent_from_dense_model_raises(tmp_path: Path) -> None: """A checkpoint MDBF layer without a dense target is a mapping error.""" from onecomp.cpu.export.dequantize import _dequantize_mdbf_layers From a8b83964f47015749a172cfe7737364815336be7 Mon Sep 17 00:00:00 2001 From: katari Date: Fri, 28 Aug 2026 15:23:47 +0900 Subject: [PATCH 14/17] [feat] Add isolated OpenVINO 2026.3.1 environment and OneComp GPTQ 4-bit export example --- CHANGELOG.md | 8 + envs/openvino/README.md | 92 ++ envs/openvino/example_export_openvino.py | 66 + envs/openvino/openvino_export_utils.py | 115 ++ envs/openvino/pyproject.toml | 51 + envs/openvino/uv.lock | 1633 ++++++++++++++++++++++ 6 files changed, 1965 insertions(+) create mode 100644 envs/openvino/README.md create mode 100644 envs/openvino/example_export_openvino.py create mode 100644 envs/openvino/openvino_export_utils.py create mode 100644 envs/openvino/pyproject.toml create mode 100644 envs/openvino/uv.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 904685f3..9a67ba72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Change log +## [v1.3.3(WIP)+feature/openvino-2026.03] 2026-08-28 + +### Environment + +- Add an isolated OpenVINO 2026.3.1 environment with locked conversion dependencies and a + OneComp GPTQ 4-bit export example for text-generation models. Flat checkpoint + metadata is normalized in a temporary copy without modifying the source checkpoint. + ## [v1.3.2] 2026-08-24 ### Bug Fix diff --git a/envs/openvino/README.md b/envs/openvino/README.md new file mode 100644 index 00000000..31051f28 --- /dev/null +++ b/envs/openvino/README.md @@ -0,0 +1,92 @@ +# Export OneComp GPTQ Models to OpenVINO 2026.3 + +This directory provides an isolated Python 3.12 environment and a conversion example for +exporting local OneComp GPTQ 4-bit checkpoints to OpenVINO IR. + +- `example_export_openvino.py`: GPTQ 4-bit export for text-generation models + +Run the following commands from the repository root. + +## 1. Create the isolated environment + +uv creates the project environment at `envs/openvino/.venv`. The lock file pins OpenVINO +2026.3.1 and the matching conversion dependencies. An explicit sync is optional because the +first `uv run --project envs/openvino ...` command also creates and synchronizes `.venv`. + +```bash +uv sync --project envs/openvino --locked + +uv lock --check --project envs/openvino +``` + +## 2. Prepare a OneComp GPTQ 4-bit checkpoint + +Start from an existing local OneComp GPTQ 4-bit checkpoint containing model weights and a +`quantization_config` in `config.json`. Its architecture must be supported by Transformers, +Optimum Intel, and OpenVINO. + +When `modules_in_block_to_quantize` uses the flat `List[str]` shape, the exporter +copies the checkpoint to a temporary directory and normalizes only the copied config to the +`List[List[str]]` shape that current Transformers and Optimum require. The source checkpoint +is not modified. Set `TMPDIR` to a large local filesystem when exporting a large checkpoint +and the default temporary directory does not have enough capacity. Create the target +directory before running the exporter; otherwise Python silently falls back to a different +temporary directory such as `/tmp`. + +## 3. Update the model path in the export example + +Open `envs/openvino/example_export_openvino.py` and edit the two constants at the top of the +file: set `MODEL_PATH` to the OneComp GPTQ 4-bit checkpoint from step 2, and `OUT_DIR` to the +directory that should receive the OpenVINO IR. + +```python +# Replace this placeholder with the path to your local OneComp GPTQ 4-bit model. +MODEL_PATH = "CHANGE_TO_ONECOMP_GPTQ_MODEL_PATH" + +# Directory that receives the OpenVINO IR and tokenizer files. +OUT_DIR = Path("ov_gptq_int4_model_from_onecomp") +``` + +## 4. Run the export + +```bash +uv run --project envs/openvino --locked \ + python envs/openvino/example_export_openvino.py +``` + +The example keeps the checkpoint's GPTQ 4-bit weights, so it passes neither +`OVWeightQuantizationConfig` nor another OpenVINO weight-compression option. It writes the +model IR, the Hugging Face tokenizer files, and the OpenVINO tokenizer and detokenizer IR +required by OpenVINO GenAI, then parses every generated model IR as a check. + +## 5. VLM checkpoints + +The example uses `OVModelForCausalLM`, which does not handle multimodal models. For a VLM, +use the model-specific Optimum class such as `OVModelForVisualCausalLM` and keep the rest of +the flow, including the `modules_in_block_to_quantize` normalization from step 2. Text input +and output need only the tokenizer files that the checkpoint already contains. Processor +metadata such as `processor_config.json` is required for image or audio input, and can be +taken from a separately pinned upstream revision when the quantized checkpoint omits it. + +A VLM export produces several component IR files, such as the language model, text +embeddings, per-layer embeddings, and vision embeddings. The GPTQ weights cover only the +language model, so the embedding components stay uncompressed. Those submodels alone can be +reduced with NNCF weight compression (`nncf.compress_weights` with +`CompressWeightsMode.INT8_ASYM`), leaving the GPTQ language model untouched. NNCF is already +part of this locked environment. + +## 6. Run inference on an NPU machine + +Copy the exported directory to an NPU machine with the same environment, then run inference +with OpenVINO GenAI. For a text-generation export: + +```python +import openvino_genai as ov_genai + +pipe = ov_genai.LLMPipeline("COPIED_MODEL_DIR", "NPU") +result = pipe.generate(["YOUR_PROMPT"], max_new_tokens=100) +print(result.texts[0]) +``` + +For VLM inference, use `ov_genai.VLMPipeline` instead. Pass a plain prompt string because the +pipeline applies the model's chat template; do not apply it yourself. diff --git a/envs/openvino/example_export_openvino.py b/envs/openvino/example_export_openvino.py new file mode 100644 index 00000000..ca0a1f7f --- /dev/null +++ b/envs/openvino/example_export_openvino.py @@ -0,0 +1,66 @@ +"""Export a local OneComp GPTQ 4-bit checkpoint to OpenVINO IR. + +Copyright 2025-2026 Fujitsu Ltd. +""" + +from __future__ import annotations + +from pathlib import Path + +from openvino_export_utils import ( + modules_shape, + read_checkpoint_quantization, + save_openvino_tokenizer, + temporary_model_path_for_openvino_export, + validate_ir_files, +) + +# Replace this placeholder with the path to your local OneComp GPTQ 4-bit model. +MODEL_PATH = "CHANGE_TO_ONECOMP_GPTQ_MODEL_PATH" + +# Directory that receives the OpenVINO IR and tokenizer files. +OUT_DIR = Path("ov_gptq_int4_model_from_onecomp") + + +def main() -> None: + from optimum.intel.openvino import OVModelForCausalLM + from transformers import AutoTokenizer + + model_path = Path(MODEL_PATH) + quantization = read_checkpoint_quantization(model_path) + print( + "[INFO] checkpoint modules_in_block_to_quantize shape: " + f"{modules_shape(quantization.get('modules_in_block_to_quantize'))}" + ) + + OUT_DIR.mkdir(parents=True, exist_ok=True) + with temporary_model_path_for_openvino_export(model_path) as prepared_model_path: + # For an already-GPTQ-quantized model, do not pass OVWeightQuantizationConfig + # here. Keep the existing GPTQ 4-bit weights when exporting to OpenVINO IR. + model = OVModelForCausalLM.from_pretrained( + prepared_model_path, + export=True, + compile=False, + local_files_only=True, + trust_remote_code=True, + load_in_8bit=False, + ) + model.save_pretrained(OUT_DIR) + + # Save the Hugging Face tokenizer, plus the OpenVINO tokenizer and + # detokenizer required by OpenVINO GenAI. + tokenizer = AutoTokenizer.from_pretrained( + prepared_model_path, + local_files_only=True, + trust_remote_code=True, + ) + tokenizer.save_pretrained(OUT_DIR) + save_openvino_tokenizer(tokenizer, OUT_DIR) + + parsed_models = validate_ir_files(OUT_DIR) + print(f"[INFO] Parsed OpenVINO IR files: {', '.join(parsed_models)}") + print(f"[INFO] Export completed: {OUT_DIR.resolve()}") + + +if __name__ == "__main__": + main() diff --git a/envs/openvino/openvino_export_utils.py b/envs/openvino/openvino_export_utils.py new file mode 100644 index 00000000..f0c9ad4d --- /dev/null +++ b/envs/openvino/openvino_export_utils.py @@ -0,0 +1,115 @@ +"""Shared helpers for exporting OneComp GPTQ checkpoints to OpenVINO IR. + +Copyright 2025-2026 Fujitsu Ltd. +""" + +from __future__ import annotations + +import json +import shutil +import tempfile +from contextlib import contextmanager +from pathlib import Path +from typing import Any, Iterator + + +def read_checkpoint_quantization(model_path: Path) -> dict[str, Any]: + """Return the checkpoint quantization metadata without modifying it.""" + config_path = model_path / "config.json" + if not config_path.is_file(): + raise FileNotFoundError(f"Checkpoint config was not found: {config_path}") + + with config_path.open(encoding="utf-8") as config_file: + config = json.load(config_file) + + quantization = config.get("quantization_config") + if not isinstance(quantization, dict): + raise ValueError(f"quantization_config is missing from {config_path}") + return quantization + + +def modules_shape(modules: Any) -> str: + """Describe modules_in_block_to_quantize as flat, nested, empty, or invalid.""" + if not isinstance(modules, list): + return "invalid" + if not modules: + return "empty" + if all(isinstance(name, str) for name in modules): + return "flat" + if all( + isinstance(group, list) and group and all(isinstance(name, str) for name in group) + for group in modules + ): + return "nested" + return "invalid" + + +@contextmanager +def temporary_model_path_for_openvino_export(model_path: Path) -> Iterator[Path]: + """Yield a model path with flat GPTQ module metadata normalized. + + Some OneComp checkpoints store modules_in_block_to_quantize as List[str], + while current Transformers and Optimum expect List[List[str]]. For that + shape, a temporary checkpoint copy is patched and removed after use. + The source checkpoint is never changed. + """ + source = model_path.expanduser().resolve() + if not source.is_dir(): + raise NotADirectoryError(f"Checkpoint directory was not found: {source}") + + quantization = read_checkpoint_quantization(source) + modules_key = "modules_in_block_to_quantize" + if modules_key not in quantization: + raise ValueError(f"{modules_key} is missing from {source / 'config.json'}") + + modules = quantization[modules_key] + shape = modules_shape(modules) + if shape == "invalid": + raise ValueError("modules_in_block_to_quantize has an unsupported shape") + if shape != "flat": + yield source + return + + with tempfile.TemporaryDirectory(prefix="onecomp_ov_export_compat_") as temp_root: + temporary_model = Path(temp_root) / source.name + shutil.copytree(source, temporary_model, symlinks=True) + + temporary_config_path = temporary_model / "config.json" + with temporary_config_path.open(encoding="utf-8") as config_file: + temporary_config = json.load(config_file) + temporary_config["quantization_config"]["modules_in_block_to_quantize"] = [modules] + with temporary_config_path.open("w", encoding="utf-8") as config_file: + json.dump(temporary_config, config_file, indent=2, ensure_ascii=False) + config_file.write("\n") + + print( + "[INFO] Using a temporary checkpoint copy with " + "modules_in_block_to_quantize normalized from flat to nested." + ) + print(f"[INFO] Temporary checkpoint: {temporary_model}") + yield temporary_model + + +def save_openvino_tokenizer(tokenizer: Any, output_dir: Path) -> None: + """Save OpenVINO tokenizer and detokenizer models for OpenVINO GenAI.""" + import openvino as ov + from openvino_tokenizers import convert_tokenizer + + ov_tokenizer, ov_detokenizer = convert_tokenizer(tokenizer, with_detokenizer=True) + ov.save_model(ov_tokenizer, output_dir / "openvino_tokenizer.xml") + ov.save_model(ov_detokenizer, output_dir / "openvino_detokenizer.xml") + + +def validate_ir_files(output_dir: Path) -> list[str]: + """Parse every exported non-tokenizer IR with OpenVINO Core.""" + import openvino as ov + + excluded = {"openvino_tokenizer.xml", "openvino_detokenizer.xml"} + model_files = sorted(path for path in output_dir.glob("*.xml") if path.name not in excluded) + if not model_files: + raise FileNotFoundError(f"No model IR XML was generated in {output_dir}") + + core = ov.Core() + for model_file in model_files: + core.read_model(model_file) + return [path.name for path in model_files] diff --git a/envs/openvino/pyproject.toml b/envs/openvino/pyproject.toml new file mode 100644 index 00000000..cc7d4ed9 --- /dev/null +++ b/envs/openvino/pyproject.toml @@ -0,0 +1,51 @@ +[project] +name = "onecomp-gptq-openvino-converter" +version = "2026.3.0.1" +description = "Isolated OpenVINO conversion environment for OneCompression GPTQ models." +readme = "README.md" +requires-python = ">=3.12,<3.13" +dependencies = [ + # OpenVINO / Optimum Intel + "openvino==2026.3.1", + "openvino-genai==2026.3.1.0", + "openvino-tokenizers[transformers]==2026.3.1.0", + "optimum-intel[openvino]==2.1.0", + + # Hugging Face / conversion runtime + "transformers==5.5.0", + "accelerate==1.14.0", + "sentencepiece==0.2.2", + "protobuf==7.36.0", + + # Keep HF compatibility packages such as safetensors and huggingface-hub + # resolver-managed. optimum-intel constrains them, and uv.lock records the + # exact compatible versions used by this export environment. + + # PyTorch CUDA 13.0 + "torch==2.13.0", + "torchvision==0.28.0", + "torchaudio==2.11.0", + "torchao==0.18.0", + + # OneComp GPTQ checkpoint loading + "gptqmodel==7.0.0", + + # Workflow pins for reproducibility + "numpy==2.2.6", + "kernels==0.12.3", +] + +# Keep this environment package-less so installing root onecomp does not pull it in. +# Dependency resolution should treat onecomp and this package as fully independent. +[tool.uv] +package = false + +[tool.uv.sources] +torch = { index = "pytorch-cu130" } +torchvision = { index = "pytorch-cu130" } +torchaudio = { index = "pytorch-cu130" } + +[[tool.uv.index]] +name = "pytorch-cu130" +url = "https://download.pytorch.org/whl/cu130" +explicit = true diff --git a/envs/openvino/uv.lock b/envs/openvino/uv.lock new file mode 100644 index 00000000..9e43b5bb --- /dev/null +++ b/envs/openvino/uv.lock @@ -0,0 +1,1633 @@ +version = 1 +revision = 3 +requires-python = "==3.12.*" +resolution-markers = [ + "sys_platform == 'win32'", + "sys_platform == 'emscripten'", + "sys_platform != 'emscripten' and sys_platform != 'win32'", +] + +[[package]] +name = "accelerate" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8d/75/94cd5d389649578aca399e5aa822637eec18319a1dadc400ffe2f9a7493f/accelerate-1.14.0.tar.gz", hash = "sha256:41b9c4377a54e0b460a959b0defa1b736e4ca0a2373252d9a539964c2afe3c8d", size = 412167, upload-time = "2026-06-11T13:45:52.326Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/db/253133d7e7cb40d3af384bb2f5c0b4a2b7fdcffbc95c688cc67a20a3c103/accelerate-1.14.0-py3-none-any.whl", hash = "sha256:e94390c2863b873be18f623f9df48a0d8fe5eff13ea7f1a00092b0a7904888c6", size = 389246, upload-time = "2026-06-11T13:45:50.477Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.7.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757, upload-time = "2026-07-01T17:11:55.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038, upload-time = "2026-07-01T17:11:54.055Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache" }, + { name = "typing-extensions" }, + { name = "yarl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/d9/22ce5786ac0c1653ae8b6c23bded02c1686d11f0dbb45b31ce128e0df985/aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc", size = 7971213, upload-time = "2026-07-23T01:57:27.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/d4/eb96299230e20acf2efae207cb8d69051f1f68e357e5ea5e479bf6fb097a/aiohttp-3.14.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:39aded8c7f3b935b54aab1d8d73c70ec0ee2d3ec3b943e0e86611bc150ba47f5", size = 754690, upload-time = "2026-07-23T01:53:47.332Z" }, + { url = "https://files.pythonhosted.org/packages/88/11/e7a70a209eb9a067c0d3212b518a0134e3484f5178c7533878b6b514d469/aiohttp-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5bcb6ff3fdab1258a192679ff1a05d44f59626430aa05cd1a9d2447423599228", size = 509484, upload-time = "2026-07-23T01:53:51.159Z" }, + { url = "https://files.pythonhosted.org/packages/30/07/4bbc222cc8dbe31d4c3e8a5baad2286e4d42026ac0c570027b89afce6344/aiohttp-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:617105e2c3018ee38d0c8ce5ee3c84f621a6d8b9f723202aacaff28449ca91ee", size = 511949, upload-time = "2026-07-23T01:53:55.083Z" }, + { url = "https://files.pythonhosted.org/packages/54/b9/42e74c46b7b7c794b995bbc1f573fb48950c38b19d8600c62a6804ee2d67/aiohttp-3.14.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f631fe87a6f30df5fbe6d79640b25e4cffb38c31c7fb6f10871517b84b0f8c1a", size = 1765282, upload-time = "2026-07-23T01:53:59.662Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ed/62bc4d74363ad346d518e0720363a949f63e2e23439a79eb5813d4d29bb3/aiohttp-3.14.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a94dbaae5ae27bd849c93570669bff91e0510f33a80805738e3de72a7be0447b", size = 1741511, upload-time = "2026-07-23T01:54:04.063Z" }, + { url = "https://files.pythonhosted.org/packages/d0/9f/181e8a8bc79e47d13c7fc4540bd7a3b729d9505609c61f392a8dd2fbfe55/aiohttp-3.14.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8f2f1c4c032c7cedd7d8da6f54c97b70266c6570c3108d3fdffee7188bb70529", size = 1810680, upload-time = "2026-07-23T01:54:09.882Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9a/dec94d6ad694552fe3424e3f1928d7a606a5d9d9433a04e7ecdd9d38ae7f/aiohttp-3.14.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea05e1f97ceea523942d9b2a7d7c0359d781d683d6b043f5943a602b14da4787", size = 1905646, upload-time = "2026-07-23T01:54:13.475Z" }, + { url = "https://files.pythonhosted.org/packages/52/b7/7cd31f29d6055bd711ae6e669367fba6f5ae9de463910a793e30556a8db7/aiohttp-3.14.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42", size = 1792122, upload-time = "2026-07-23T01:54:15.752Z" }, + { url = "https://files.pythonhosted.org/packages/66/73/10b1ef93afa61f4963c746257b70ced619cf31a4798671de5fdb2608501d/aiohttp-3.14.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0a5ff2dfbb9ce645fa5b8ef3e02c6c0b9cc3f6030ff863d0c51fffc50cb5541b", size = 1591127, upload-time = "2026-07-23T01:54:19.489Z" }, + { url = "https://files.pythonhosted.org/packages/49/ed/3b203fa6de1b338c14acdc06bf6ca9b043b7944f005966958c2ced932cde/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:041badb8f84396357c4d3ad26de6afd7a32b112f43d3c63045c0c8278cfd2043", size = 1725210, upload-time = "2026-07-23T01:54:24.129Z" }, + { url = "https://files.pythonhosted.org/packages/28/b7/1c2aab8c706436dcc28598452488ac9cd7c409da815237c28c27d58993e6/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:530125ee1163c4219af35dc3aa1206e541e7b31b6efc1a3f93b70a136f65d427", size = 1764848, upload-time = "2026-07-23T01:54:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/54/50/94c28f08b131c4bf10984ea2c7a536c9920608bb2d6e7f95642c30cc87b7/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8653fd547c93a61aadc612007790f5555cdd18946fa48cf45e26d8ea4ea473d", size = 1777102, upload-time = "2026-07-23T01:54:31.775Z" }, + { url = "https://files.pythonhosted.org/packages/13/d4/e7d09ba7d345fb2d74440fd2fa033c5e079fac05552927705986f41a364f/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:89176250f686cb9853c0fb7ead90e639e915b84a6f43eedc2a4e7ec21f1037f0", size = 1580205, upload-time = "2026-07-23T01:54:34.518Z" }, + { url = "https://files.pythonhosted.org/packages/a3/84/072a91d68e1e1eb587985b54baab94221277f877e8ef274fc213a0ceae28/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3a26434dafe408229ff3403458ca58de24fb51936504decac49ce6755f77e59d", size = 1797219, upload-time = "2026-07-23T01:54:36.995Z" }, + { url = "https://files.pythonhosted.org/packages/e0/eb/aad34e897e668424d6e995da5dff8a4a09af93363d3392488772957a63aa/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19", size = 1768629, upload-time = "2026-07-23T01:54:40.103Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2b/6bb88ddba0fecd9122aa3ebcad25996cf6c083a4a7040dbb3a4f97972af6/aiohttp-3.14.3-cp312-cp312-win32.whl", hash = "sha256:16100ad3ab8d649fdfbee87602d9d2dcdca9df0b9eda8a1b5fdc0d41f96da559", size = 451481, upload-time = "2026-07-23T01:54:42.547Z" }, + { url = "https://files.pythonhosted.org/packages/76/9b/f2f8f108da17ecef2cc3efc424e8b7ad3782b1a8360f7b8eae8ced84f6ea/aiohttp-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:33a2d7c28d33797a2e99923dffa63f83d908a19b6bf26cfe80fa790aa5e1a75a", size = 476845, upload-time = "2026-07-23T01:54:44.853Z" }, + { url = "https://files.pythonhosted.org/packages/3e/44/28dac80a8941b604f4da10ce21097614ca1bf905ce93dca28d8d7de9c1e7/aiohttp-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:362a3fd481769cac1a824514bcd86fda51c65e8fe6e051099e008fddde6db17c", size = 448050, upload-time = "2026-07-23T01:54:47.087Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-doc" +version = "0.0.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/8e/38aa427ed5402449e226975b649c5dc73ccadfefeb95e6aecb8f8ea4b6b6/annotated_doc-0.0.5.tar.gz", hash = "sha256:c7e58ce09192557605d8bbd92836d7e1d520ac9580096042c0bfd197efacf1bb", size = 10758, upload-time = "2026-07-28T13:50:58.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/30/e900b21425a860e195f32e37657aa1f7c7f2b1bfb26f03ca209b90933c06/annotated_doc-0.0.5-py3-none-any.whl", hash = "sha256:117bac03a25ede5df5440e855b32d556049ca169ead221505badf432fed4b101", size = 5302, upload-time = "2026-07-28T13:50:57.239Z" }, +] + +[[package]] +name = "anyio" +version = "4.14.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, +] + +[[package]] +name = "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.7.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/3f/143b048436775b0f76ac3eec145c019e8173ccc2885c8f20319b996d5e83/charset_normalizer-3.5.1.tar.gz", hash = "sha256:6117b84ea48435e5356dc737f5121485c30920ba43375fa7b434fd753df0eac3", size = 171764, upload-time = "2026-08-15T08:20:44.807Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/27/78873dc8b6a56357517b74b6bb9568b80450e7bb4f6ef7e3fa9d22aa0bd7/charset_normalizer-3.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:5b6d1386bf0096d26d3a863dc0a487a5b4eb9aa93cf5ba69683d29dde6b9d60f", size = 344456, upload-time = "2026-08-15T08:17:10.072Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4c/be49ada26b1f0232d57aa89bbebf997a5cc2332a5616b6eca26ff680044d/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4582c27e8c889d64811987b5967fbd3ae0c823fe1fd933b543d55ac20bb475fa", size = 238530, upload-time = "2026-08-15T08:17:11.563Z" }, + { url = "https://files.pythonhosted.org/packages/76/84/6f1290fa07ae6978d3960caa3eb1b8019bf9284ab7c2297b00c099ef4250/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1d1c7a53a6c2103925cdd6d7229f8c567379f211c869793df679f2e9f738c369", size = 230200, upload-time = "2026-08-15T08:17:12.919Z" }, + { url = "https://files.pythonhosted.org/packages/e7/a0/47b18adeed31c8f16ba9700f32c1b18594cfa09f47eb672a488c273c22bf/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e6621fb2a4988d6e53eedc455e5903e2679f3967b8acb3d639f1b63c14a2e893", size = 262222, upload-time = "2026-08-15T08:17:14.571Z" }, + { url = "https://files.pythonhosted.org/packages/38/fe/341861ac118dae06f3ec0eb487488af52128f2ef2faf0b11003944d22259/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7c0c10730342b0c9b35dd1d619beb8214e520bd96a1f870f452680b238aab3e0", size = 258951, upload-time = "2026-08-15T08:17:16.158Z" }, + { url = "https://files.pythonhosted.org/packages/6f/89/bb5108dc6c3651dca963f2b0a3ba19bbcb370c94e1b6d3e0e844a58e6dca/charset_normalizer-3.5.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b9af956078716df40d985fb0dfeb2c2120c5ca92ba4ff4b388acfd01cdc14d08", size = 248801, upload-time = "2026-08-15T08:17:17.683Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ba/ef83ae3aca816393decfa3530976f38a79812d707b80b580ac33b83f9877/charset_normalizer-3.5.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9f8405c2c758532c74fed975dbee57be1f31a6e865c031870c79a6ed3212ada", size = 244070, upload-time = "2026-08-15T08:17:19.191Z" }, + { url = "https://files.pythonhosted.org/packages/f6/0b/c5292a2462d69b7378ea89793bbb5b2b6fcf6f7dd6d1667f9619094ad553/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:96fef3e886d6a9874b14f27fc193fbdc69d5d8035783d86aa4e1cea594e695f9", size = 240110, upload-time = "2026-08-15T08:17:20.547Z" }, + { url = "https://files.pythonhosted.org/packages/46/22/111e5be3b740d5c2a5bfcedb3d237b6591e5c2e82ae9d6ffcb121fe0909c/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5d8531a6569d025f68e2321e7638fb7978f23db58e5f69f56913837aae03816e", size = 232836, upload-time = "2026-08-15T08:17:21.895Z" }, + { url = "https://files.pythonhosted.org/packages/f9/d2/d2aad6fe0dbb44b194bf3becb60f5a0ac48446ade999a47fe7bb41eb09a7/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:aae2ee51122d3ae968a3837d97dc24a0aeebb0dea23694422cd172bd30017cd6", size = 262712, upload-time = "2026-08-15T08:17:23.727Z" }, + { url = "https://files.pythonhosted.org/packages/35/5a/337e4663a5eae6de99db940ee8066d4145caafb61327db62deda15313cce/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7235dc28fc6dd9d832ac7c7bce95367dedb85929f17368a0c2bee1e080b9acbf", size = 242977, upload-time = "2026-08-15T08:17:25.157Z" }, + { url = "https://files.pythonhosted.org/packages/ca/85/f82f8a92e31c7519410e2e1afdc630f28ec47490ce2c09a11c1a43cbb459/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4abdc5f9ad448c1ecbfae2974b820535d6bc6e7eef63babbab3d81cf46968c71", size = 260207, upload-time = "2026-08-15T08:17:26.602Z" }, + { url = "https://files.pythonhosted.org/packages/b7/52/643d11ffd60e9ac2fd1fb87e167a19285b9eefeff4a40e63c87cbfbeab36/charset_normalizer-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba501e667c17d8411f98e67a022d9604ef179aff0e459b7e292c796837c13573", size = 250562, upload-time = "2026-08-15T08:17:27.971Z" }, + { url = "https://files.pythonhosted.org/packages/62/16/46556278c2168d12df9da7fede5dc6fc70e60301b26a82bbeec238c9cfe3/charset_normalizer-3.5.1-cp312-cp312-win32.whl", hash = "sha256:cfa1c0cc3a8f9f53f1243a5a99ac36fd003880199383b37672e86ddda9cb07e2", size = 178507, upload-time = "2026-08-15T08:17:29.277Z" }, + { url = "https://files.pythonhosted.org/packages/9d/7a/4c6c298171e6b3e745633180ff59350fc0ca0db1ffd28df1e369e0579f71/charset_normalizer-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3617ac3cfd8b9888f145ad89dd6e692285834b0201c6074a5eeaad3fd4d668c2", size = 200551, upload-time = "2026-08-15T08:17:30.668Z" }, + { url = "https://files.pythonhosted.org/packages/cd/d7/eb95a042f0dd22e304b0b6472b154f3546a1a039a9ee89ccb2a7f61591fc/charset_normalizer-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:88e85ab89cb822c1e635f51d6d32e488f94e002e70e2f492bdb8b945543f345a", size = 180700, upload-time = "2026-08-15T08:17:32.028Z" }, + { url = "https://files.pythonhosted.org/packages/5b/97/fb4e82231aba271ffd775a1b4993b0defc4e3059f286ae41d9433409fe85/charset_normalizer-3.5.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:41876ee62a3dddf48ff1121ad8f0798032aa03f2fd35f21f34a4cab14f18d8d2", size = 331467, upload-time = "2026-08-15T08:19:50.959Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2f/fe3f187327aac18e2d54e9d2b08e15d27bf9b642d9e51c219f130fc34d1a/charset_normalizer-3.5.1-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6dac12ff6b846103483683f60c5f8fee205121adc58ffd87e90a90a3af69e99", size = 253057, upload-time = "2026-08-15T08:19:52.654Z" }, + { url = "https://files.pythonhosted.org/packages/d7/c7/9e48cee5c161fe24da823b61bf381921d77cb994a0a4de148e95018c1984/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cee5dd7c6fb5dd52a0fe2a740f9bc6e3593f5f8b1788bde49de02086f30182b2", size = 240930, upload-time = "2026-08-15T08:19:54.163Z" }, + { url = "https://files.pythonhosted.org/packages/49/e0/716601f3cc69be7b198951150c75ead1ece33c3c8036ff6ffa46029659a0/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:343fb4f2821043bd87095f7b08a1a181febc8e36ac64212143bbfd0a0e1bc235", size = 230822, upload-time = "2026-08-15T08:19:55.807Z" }, + { url = "https://files.pythonhosted.org/packages/d3/05/71bfc5caa0abcc45aea1f6a4d50ac68e59605ddc7666fe8494f4cd229665/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ae4a097991662cd4fff0ddc74e0fe7874f82e00042fa0ea00855645ed0c79598", size = 260037, upload-time = "2026-08-15T08:19:57.312Z" }, + { url = "https://files.pythonhosted.org/packages/c3/92/de7e32ed05341e7a9c4c877c318418197b7f2d66a3b68d561bf2ac57ca3e/charset_normalizer-3.5.1-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b599739b93b2cbeded49645ae3c8d1405c29ddfbceac1545c87a3f9580a9e96", size = 255097, upload-time = "2026-08-15T08:19:59.056Z" }, + { url = "https://files.pythonhosted.org/packages/f5/7b/ade0a122600319dfa0b1000ab0f9731c94a817904cf3c5de408c73a4ede7/charset_normalizer-3.5.1-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b39b69b347e5e47a3b5b8cfc005c68c1ba347474e3960236c4944a8ecd174962", size = 250166, upload-time = "2026-08-15T08:20:00.612Z" }, + { url = "https://files.pythonhosted.org/packages/75/9c/019fbb9f4834491a160951349b1a3714439376f66e5f7cf18b4f18f0c7aa/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a2028475ba855475b8b4d3cfeb4994269c967aea8b9892dfba907f4263a863a3", size = 241821, upload-time = "2026-08-15T08:20:02.321Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b8/11d4840bfc99330cc7fbcc2681ee5a044553a6e77655508d8f9b2bff7b34/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:36047af20e17097c3bb9476c2b7655f2f7aa51322c0ba58c07695bedf755a950", size = 232529, upload-time = "2026-08-15T08:20:04.008Z" }, + { url = "https://files.pythonhosted.org/packages/18/96/2b3a21492d9f65171ac75d872f5018260013d00bfa0ff70ec9f179148cbd/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:4c4fb141a727957c93edfe5c32a26ceb6b5f6461d67146e2d39f51e16170bea8", size = 260348, upload-time = "2026-08-15T08:20:05.877Z" }, + { url = "https://files.pythonhosted.org/packages/d6/aa/a69a2028e8bd052476c245460ab19d7de595de084dd968f2d75cd50c3e25/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:2f293479cce755c75f1697e87c409b7ae4c555c7dfecb6e988ad13abba943031", size = 247234, upload-time = "2026-08-15T08:20:07.487Z" }, + { url = "https://files.pythonhosted.org/packages/35/8a/3d130aeabcaf3d2466af76b7b141c08d9e89c9016ab4b7cdd0f7dc2d1c62/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:3588e376b3ea2eea84976f67273d679f229e24c66dce7b82ae45aef04ff6e072", size = 256917, upload-time = "2026-08-15T08:20:09.142Z" }, + { url = "https://files.pythonhosted.org/packages/80/c2/a7379b840292d0c1ab9fbd17d1f3967aa81794dc95bc74be8999d7fedcf7/charset_normalizer-3.5.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e199fb99720074809a7720f1c0b4d919eea8b87e88713e0f8f602f7bef543d9d", size = 254846, upload-time = "2026-08-15T08:20:10.727Z" }, + { url = "https://files.pythonhosted.org/packages/01/65/d43b714731bb2f40d4053dfa00ecfc1c5a301f8e3316c5db3a09af59fe94/charset_normalizer-3.5.1-cp37-abi3-win32.whl", hash = "sha256:dd732602a7009217f658d5863d12d79d373a4de0eebc111094bcdd3bb8e0a6cc", size = 174216, upload-time = "2026-08-15T08:20:12.334Z" }, + { url = "https://files.pythonhosted.org/packages/35/4f/b911ed898b26a09789eba9c9200c999aff6c61b4bafaf4838e56d1a1e1a3/charset_normalizer-3.5.1-cp37-abi3-win_amd64.whl", hash = "sha256:70055ff39b97c99e7ae40ea3e393fb62aa2e44dbd9b29f8d14f42fb0025c3959", size = 199764, upload-time = "2026-08-15T08:20:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a7/920baf467bfd9bf689f3b318340f37aee4572a71f162bd8db51da55ba4fa/charset_normalizer-3.5.1-cp37-abi3-win_arm64.whl", hash = "sha256:87e4f41d375c0b9be2fb5251aee4b8a689169e134535aed81bf085c3b647451e", size = 287318, upload-time = "2026-08-15T08:20:15.551Z" }, + { url = "https://files.pythonhosted.org/packages/cc/61/d01fc49b8dea277640b55a9e15960dbca9fdc8c9fde18e572d39c59f4019/charset_normalizer-3.5.1-py3-none-any.whl", hash = "sha256:6df0ec430f9a831772c23ca5a224cba36517a58a84bb32c32bb59a9fa67c47f6", size = 68658, upload-time = "2026-08-15T08:20:43.306Z" }, +] + +[[package]] +name = "click" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/0e/7fa0ef50764b67090eca4114772a2abf8b6148198475e54c660b97caeee6/click-8.5.0.tar.gz", hash = "sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34", size = 382235, upload-time = "2026-08-26T13:33:14.56Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", hash = "sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", size = 125251, upload-time = "2026-08-26T13:33:12.928Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "cuda-bindings" +version = "13.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cuda-pathfinder", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/67/5e7dba1ba576dd73da5dee894ca076ca5e959450dfff66d6d510a255d1f7/cuda_bindings-13.3.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7855c4868aabc0cfae28abbe83d56734bdfbd08f08fc234ac1912a12858bf49", size = 6025351, upload-time = "2026-05-29T23:11:49.685Z" }, + { url = "https://files.pythonhosted.org/packages/39/2a/6d2e9047d1fb243dbaa364b01e0297534b9ed7fd27dba1c9f361519cf69b/cuda_bindings-13.3.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e32d08f71ebcdf00f0f41eab2eb37e8da94c8ed411cc9f7f7a019ce6b34abe3a", size = 6657965, upload-time = "2026-05-29T23:11:52.227Z" }, +] + +[[package]] +name = "cuda-pathfinder" +version = "1.7.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/01/a7171c5e2e8755597bd8f1c1eb228a0876f502afdf25f936061f5dbe2880/cuda_pathfinder-1.7.0-py3-none-any.whl", hash = "sha256:e9d67e950f3d5992b854dfd25917c3719d0c21d3057b11abe86ba6feec526138", size = 63091, upload-time = "2026-08-24T04:13:56.054Z" }, +] + +[[package]] +name = "cuda-toolkit" +version = "13.0.3.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/c7/a79086a62c98befcdb8349656c6f114e2db3b8b2422f6e25c97a7f2a9a3c/cuda_toolkit-13.0.3.0-py2.py3-none-any.whl", hash = "sha256:d693caaa261214ddd7dbb60d68e71cbed884e68c2be7509778f3051da0b91c3f", size = 2512, upload-time = "2026-04-14T00:50:08.173Z" }, +] + +[package.optional-dependencies] +cublas = [ + { name = "nvidia-cublas", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cuda-nvrtc", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +cudart = [ + { name = "nvidia-cuda-runtime", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +cufft = [ + { name = "nvidia-cufft", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +cufile = [ + { name = "nvidia-cufile", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +cupti = [ + { name = "nvidia-cuda-cupti", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +curand = [ + { name = "nvidia-curand", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +cusolver = [ + { name = "nvidia-cublas", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cusolver", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-cusparse", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +cusparse = [ + { name = "nvidia-cusparse", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +nvjitlink = [ + { name = "nvidia-nvjitlink", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +nvrtc = [ + { name = "nvidia-cuda-nvrtc", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +nvtx = [ + { name = "nvidia-nvtx", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] + +[[package]] +name = "datasets" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, + { name = "filelock" }, + { name = "fsspec", extra = ["http"] }, + { name = "httpx" }, + { name = "huggingface-hub" }, + { name = "multiprocess" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "xxhash" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0a/5b/836516269d4f618efe621661cfb6f9acc57e6f95265db3efaee48a5ffe04/datasets-5.0.1.tar.gz", hash = "sha256:ce22bb851efd7494f08aad33b940803784434f6e77763d00679a0dc45fcf686a", size = 641498, upload-time = "2026-07-28T11:09:12.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/0b/98fc6eb83333508ca5f44c52b3e287ea8137a0ad582714e2cbc67a02154b/datasets-5.0.1-py3-none-any.whl", hash = "sha256:9fbf73688f8c18f7529b4fe592abd04015f81d1e58001e4bac73ffb2b39d7cc4", size = 559079, upload-time = "2026-07-28T11:09:10.266Z" }, +] + +[[package]] +name = "defuser" +version = "0.0.25" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pypcre" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d3/be/8e44425db207728d173ce2c64c632290c00e5f3da7e2733edd7fed5eb039/defuser-0.0.25.tar.gz", hash = "sha256:d490f701d6ff3dac65c08288c670fe9bb939f37e1db19af49a38f9085140c301", size = 63973, upload-time = "2026-08-04T17:27:12.988Z" } + +[[package]] +name = "device-smi" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/c8/3b2eeea99cc1136aa7006cebc2f17ce0220de9f3688c98742baa242fb952/device_smi-0.5.7.tar.gz", hash = "sha256:0e46ce3379e5e879d6a04b9266b9896cbbbfae3d62b22bba2ae2c9299b5a6469", size = 26499, upload-time = "2026-08-23T01:14:19.142Z" } + +[[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 = "filelock" +version = "3.32.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/30/03b03951873a1a0ffc7e8ca0e10c15597b59e8d0e39260704cd2ea087bc4/filelock-3.32.4.tar.gz", hash = "sha256:2bde2e4cf732e0153406d8a7bc80620ecf5e621fe0d25e41143c4e3b4733ff30", size = 222126, upload-time = "2026-08-23T17:37:55.363Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/a4/9b63d595d748e3aff8812b65eacc1a2c4bd90b7c2012e08e72373b4835eb/filelock-3.32.4-py3-none-any.whl", hash = "sha256:22e58ca3b1ae3b98993b762d7338367ae64fe50252bf78d59da3bfebcdf1cedd", size = 99864, upload-time = "2026-08-23T17:37:53.913Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "fsspec" +version = "2026.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/a1/ae4e3e5003468d6391d2c77b6fa1cd73bd5d13511d81c642d7b28ac90ed4/fsspec-2026.6.0.tar.gz", hash = "sha256:f5bac145310fe30e16e1471bd6840b2d990d609e872251d7e674241822abf01a", size = 313646, upload-time = "2026-06-16T01:57:28.105Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/22/4222d7ddf3da30f363edaa98e329c2bce6c65497c9cb2810931c8b2c0fbc/fsspec-2026.6.0-py3-none-any.whl", hash = "sha256:02e0b71817df9b2169dc30a16832045764def1191b43dcff5bb85bdee212d2a1", size = 203949, upload-time = "2026-06-16T01:57:26.358Z" }, +] + +[package.optional-dependencies] +http = [ + { name = "aiohttp" }, +] + +[[package]] +name = "gptqmodel" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "accelerate" }, + { name = "datasets" }, + { name = "defuser" }, + { name = "device-smi" }, + { name = "dill" }, + { name = "jinja2" }, + { name = "logbar" }, + { name = "maturin" }, + { name = "ninja" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "protobuf" }, + { name = "pyarrow" }, + { name = "pypcre" }, + { name = "safetensors" }, + { name = "threadpoolctl" }, + { name = "tokenicer" }, + { name = "torch" }, + { name = "torchao" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/ae/880723dd420d2931614a03f76a6c038e9729606aa7a242fe136db1d78665/gptqmodel-7.0.0.tar.gz", hash = "sha256:af8a01391695b2ec2fed72418fdfb20f5cf0371b6942cf0866110ac6a83a57b4", size = 979121, upload-time = "2026-04-28T20:40:49.731Z" } + +[[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.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/ab/522a2ab67f27971a9d48ca666d4fca85ef7d5282d142e31fd087e27b1bbe/hf_xet-1.6.0.tar.gz", hash = "sha256:2e58454a340b3556dfa4972d5451aff4fba8dd42a236600ba1a1d2b1514f0fef", size = 920527, upload-time = "2026-08-03T22:33:13.243Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/50/7afa2c9c787405864fc47a0d1bbc02c62e9101947ed43c1f43899fc7d91d/hf_xet-1.6.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:633dc0cd71d32da58ab8c03ad38e2fac452c15c2b0a2866ebf6ededfe0a5061d", size = 4071729, upload-time = "2026-08-03T22:33:00.721Z" }, + { url = "https://files.pythonhosted.org/packages/4b/69/55b8dcf636142ae660fec1869fcac14c4da2e8412e14d6eee1523be77e9f/hf_xet-1.6.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:f0906082d9932ae0c0057fa194041c22b4e2cdb46b2592ef3b91f020d62a081a", size = 3876287, upload-time = "2026-08-03T22:33:02.251Z" }, + { url = "https://files.pythonhosted.org/packages/67/4e/a28359bf1c1ecf11eba22123168c138698f7cb576ac678f5a2e16cd5da08/hf_xet-1.6.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d62671bb130879cef0ee4c9ebe47a14af6c66ec53e6d84dc15936e5ffdfac82f", size = 4464663, upload-time = "2026-08-03T22:33:03.802Z" }, + { url = "https://files.pythonhosted.org/packages/9a/69/1f0cbc2fb22ae6082d094f743d1b8945a3f36f6089cb95f42b7ee348cda7/hf_xet-1.6.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0e6e21fa3cdfcdcd76748564bf593870a5e013f47d97cf10aed63aa222cff5b7", size = 4262538, upload-time = "2026-08-03T22:33:05.287Z" }, + { url = "https://files.pythonhosted.org/packages/d1/3a/4f4f2301ade26e404462d3336fa11f7958d914cabbabdd6e03c3c5d5658c/hf_xet-1.6.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4fc74352a17015bd0ee90038bc9efe38db894cde45f268b6712b04fce8cd0acb", size = 4460520, upload-time = "2026-08-03T22:33:06.81Z" }, + { url = "https://files.pythonhosted.org/packages/ab/5f/311725e2a905534dfee2dcb5b08414f249147f1f12252bfc2bd24caa075c/hf_xet-1.6.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4f71cba6129110c3374a33f919001ff130488fc23553698e34cc1c2a1198c", size = 4675937, upload-time = "2026-08-03T22:33:08.616Z" }, + { url = "https://files.pythonhosted.org/packages/98/b7/8c59a66d15205024662f1d66968136f13893f96df1ddc5087e2e281fc95f/hf_xet-1.6.0-cp38-abi3-win_amd64.whl", hash = "sha256:fb4fadde1b2b70bf4c0c14a6dccbe7194b1c28947fefd5bbe3fed9d940676c3b", size = 4033128, upload-time = "2026-08-03T22:33:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/73/63/ca511b6f802f28cf3489b280fe77475bcca8de85e81a6299d7916b5b5555/hf_xet-1.6.0-cp38-abi3-win_arm64.whl", hash = "sha256:3dc3e35441ba395006af5aaacc40ef2e603c51ef46c3530b9156185f00935ea3", size = 3859359, upload-time = "2026-08-03T22:33:11.725Z" }, +] + +[[package]] +name = "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.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, + { name = "httpx" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/77/ce3331f40cb2d021fe9b24c46c41e72faf74493621138e5eddac12bf5e1c/huggingface_hub-1.21.0.tar.gz", hash = "sha256:a44f222cd8f2f7c2eade30b5e7a04cac984a3235fa61ea87a0a5a31db77d561f", size = 861572, upload-time = "2026-06-25T13:09:26.356Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/85/b505a99a133d9f99d21af182af416e9baef70bdeef019983479651e494c2/huggingface_hub-1.21.0-py3-none-any.whl", hash = "sha256:eadaa3678c512c82aea69e8675d90a184861e68de32f1105668628b4dce0e7cd", size = 721078, upload-time = "2026-06-25T13:09:24.402Z" }, +] + +[[package]] +name = "idna" +version = "3.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/f7/abb373e5757eaec4b922b92f97ec8d6d7e057cf06778247604fbc4e7c3f3/idna-3.19.tar.gz", hash = "sha256:5e0811a4383b21dc5838069f801c4fb62113b7447663d2530d2bd6e77b49bf15", size = 215237, upload-time = "2026-08-18T05:14:24.27Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl", hash = "sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", size = 68550, upload-time = "2026-08-18T05:14:22.343Z" }, +] + +[[package]] +name = "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 = "joblib" +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, +] + +[[package]] +name = "kernels" +version = "0.12.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "packaging" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/84/9f68f355f6ce99e977872021fbdbafadcf2820f51d3f7bd697ec3801cb7a/kernels-0.12.3.tar.gz", hash = "sha256:87e29716578e7e71dc5a7578e0132bfdae305bedaeb602698f87c88ca6c60e32", size = 57407, upload-time = "2026-03-20T10:20:42.166Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/3e/778e4a86830e9139df2d16d86c4488fce426ec19daa83cbd2854ef389030/kernels-0.12.3-py3-none-any.whl", hash = "sha256:5d1d33fcb774e03bb7f0688ac24d91ef6b963692f80f0a85ddd2286e69f3cf2f", size = 55501, upload-time = "2026-03-20T10:20:40.643Z" }, +] + +[[package]] +name = "logbar" +version = "0.4.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/38/48/d201291ec19eb2e91a2ebfa146aef62ecbfd4b1703d3d9ca744983b18996/logbar-0.4.13.tar.gz", hash = "sha256:1bcc7b3fb5c87e8af735ecccec6317d5654d841f0c532dc8322ea096506cae8d", size = 113545, upload-time = "2026-08-23T02:01:04.883Z" } + +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, +] + +[[package]] +name = "maturin" +version = "1.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/c8/22e5e21b2679c9bce6415ca578034ca2cc9316be0642ae21e051a2d5198c/maturin-1.15.0.tar.gz", hash = "sha256:94b26cc8e8aba61a5f2099715fe640e18c5f678e9a500408b38761263954228a", size = 385504, upload-time = "2026-08-24T12:11:22.665Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/69/5c01b461044eb1f45ddcce006706eb88110c793cdb11c7ae0b5e08492e94/maturin-1.15.0-py3-none-linux_armv6l.whl", hash = "sha256:6bf6dc62e22d4dcfd5a51244ff0d58975fa4979c48209fe84159617648956d82", size = 10206220, upload-time = "2026-08-24T12:10:53.327Z" }, + { url = "https://files.pythonhosted.org/packages/eb/1f/2b431554e11687cdb1077e0cdadcc118c53f611086b3af00c8545a67c6a5/maturin-1.15.0-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:cd35772633f489841132bc8e71d6fc7f842df30b9c05cd5cdf1ee1ddcb744cc7", size = 19416513, upload-time = "2026-08-24T12:10:56.126Z" }, + { url = "https://files.pythonhosted.org/packages/51/36/e23a21cb34a648b711036b9b2fe1d4f3f4ee24f8db54215d73f1a9a3a3ec/maturin-1.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c40b4eae7bf5ef1f4b1af8d623fe4105016f93578fb15b764e741d08ec3b92dd", size = 10014962, upload-time = "2026-08-24T12:10:58.486Z" }, + { url = "https://files.pythonhosted.org/packages/8c/80/33b15cb2d8f30f12c807955e8f2fd775027692904e30ec0784744ce8cd83/maturin-1.15.0-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:7eb066372f541f8eb4909c79c5d9bd0b9e8125980bdf1ec9e8aba23c6c8d6c55", size = 10196223, upload-time = "2026-08-24T12:11:00.696Z" }, + { url = "https://files.pythonhosted.org/packages/fe/91/b495e19e2f5c503b540452b2039115e7b2363867e8c5ad4179eb752fa92c/maturin-1.15.0-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:653020a63525bb224e5ab0adf02e17a2e08bc86dbea7fc1399c9a56d7529b99e", size = 10541186, upload-time = "2026-08-24T12:11:02.857Z" }, + { url = "https://files.pythonhosted.org/packages/5d/2b/2abff58037188d852b124871b1f0d720e1c2bfb3d4f1b03d87c52cd66488/maturin-1.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:0ebf9767892725083138e671c34482c660317a2f3d6a29fc0e0f34e9d8c99136", size = 10083468, upload-time = "2026-08-24T12:11:05.012Z" }, + { url = "https://files.pythonhosted.org/packages/3f/07/b7e9f8be99a6627849e81ac7b6694876bce8f50a92995fe17e3cf2610f0a/maturin-1.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:7ab7eebffd7b8debca2265985de4eaeb332141276d24b9560b5ad484d4b3add1", size = 10047786, upload-time = "2026-08-24T12:11:07.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ab/167e3cb7accee11b507dbe53e0e87aeccb376d44ae66284c96ee4df3a9fd/maturin-1.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:126e12e618b4db42f68c779a56d41f82a390145ba36ac3f621d057eb34f5ad9d", size = 13315332, upload-time = "2026-08-24T12:11:09.433Z" }, + { url = "https://files.pythonhosted.org/packages/14/4d/801379f646cbc6b00998e5289b0630a886be3a4ee4c75b6bc9b87478a7f1/maturin-1.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4f9d33e6c3f9615c8caceecbbbd440f8eb25a3ddeb687077682cd5eca2e9ae15", size = 10807183, upload-time = "2026-08-24T12:11:11.73Z" }, + { url = "https://files.pythonhosted.org/packages/89/27/2e612e1cbd1580e9e94d4722c227b5180dca27b32b955a34b79918aa1292/maturin-1.15.0-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:bf29beddd0c6708f112db51d5275fc28b28b9e9c9c5faae387eaef662918b176", size = 10413274, upload-time = "2026-08-24T12:11:14.04Z" }, + { url = "https://files.pythonhosted.org/packages/70/d8/202a7b4d75a51f20f84ec9ce3b7345b12b822207072164e2b1c6ef665125/maturin-1.15.0-py3-none-win32.whl", hash = "sha256:da649988be98e87e009e51b1bf0d301b6a301bc0cecbdd60d40d8ba60748d1ca", size = 8928744, upload-time = "2026-08-24T12:11:16.269Z" }, + { url = "https://files.pythonhosted.org/packages/40/dc/4e90da594986ba78dd3bc8a5921ecdcdb11085b22b02a412caab3b225601/maturin-1.15.0-py3-none-win_amd64.whl", hash = "sha256:552c2be4afd43fe8d5c9f3ec8d4c4756d973b8dcbe94c14084390301f50243e1", size = 10335085, upload-time = "2026-08-24T12:11:18.326Z" }, + { url = "https://files.pythonhosted.org/packages/8b/10/15d4314edf130955edf2dc237aa393a8a7c10f2b9b57b89fa2f61f915659/maturin-1.15.0-py3-none-win_arm64.whl", hash = "sha256:c7dc0c66c78d3debdd9c5aa807e861fbcbf07f3505d34b125df74c03986b0f48", size = 9713795, upload-time = "2026-08-24T12:11:20.83Z" }, +] + +[[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" } +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "multiprocess" +version = "0.70.19" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dill" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a2/f2/e783ac7f2aeeed14e9e12801f22529cc7e6b7ab80928d6dcce4e9f00922d/multiprocess-0.70.19.tar.gz", hash = "sha256:952021e0e6c55a4a9fe4cd787895b86e239a40e76802a789d6305398d3975897", size = 2079989, upload-time = "2026-01-19T06:47:39.744Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/45/8004d1e6b9185c1a444d6b55ac5682acf9d98035e54386d967366035a03a/multiprocess-0.70.19-py310-none-any.whl", hash = "sha256:97404393419dcb2a8385910864eedf47a3cadf82c66345b44f036420eb0b5d87", size = 134948, upload-time = "2026-01-19T06:47:32.325Z" }, + { url = "https://files.pythonhosted.org/packages/86/c2/dec9722dc3474c164a0b6bcd9a7ed7da542c98af8cabce05374abab35edd/multiprocess-0.70.19-py311-none-any.whl", hash = "sha256:928851ae7973aea4ce0eaf330bbdafb2e01398a91518d5c8818802845564f45c", size = 144457, upload-time = "2026-01-19T06:47:33.711Z" }, + { url = "https://files.pythonhosted.org/packages/71/70/38998b950a97ea279e6bd657575d22d1a2047256caf707d9a10fbce4f065/multiprocess-0.70.19-py312-none-any.whl", hash = "sha256:3a56c0e85dd5025161bac5ce138dcac1e49174c7d8e74596537e729fd5c53c28", size = 150281, upload-time = "2026-01-19T06:47:35.037Z" }, + { url = "https://files.pythonhosted.org/packages/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 = "narwhals" +version = "2.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/7b/6248dada39781db1ab3ebf08943080df0796098515a87f6f8696d14ec744/narwhals-2.25.0.tar.gz", hash = "sha256:62c036c810662bf7820b7737077176313bc59350eeeefb808510f388c743e4b2", size = 677076, upload-time = "2026-08-20T18:10:15.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/dc/55481808fd70ef1567cf13540ffd4702af3f74b112e35427564b03f79c2d/narwhals-2.25.0-py3-none-any.whl", hash = "sha256:1f0f403e8c7e4463cde9bfe78b12fdd809e3ae3dda6d9b2f802934fb9c7a6a8f", size = 467373, upload-time = "2026-08-20T18:10:13.834Z" }, +] + +[[package]] +name = "networkx" +version = "3.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, +] + +[[package]] +name = "ninja" +version = "1.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/73/79a0b22fc731989c708068427579e840a6cf4e937fe7ae5c5d0b7356ac22/ninja-1.13.0.tar.gz", hash = "sha256:4a40ce995ded54d9dc24f8ea37ff3bf62ad192b547f6c7126e7e25045e76f978", size = 242558, upload-time = "2025-08-11T15:10:19.421Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/74/d02409ed2aa865e051b7edda22ad416a39d81a84980f544f8de717cab133/ninja-1.13.0-py3-none-macosx_10_9_universal2.whl", hash = "sha256:fa2a8bfc62e31b08f83127d1613d10821775a0eb334197154c4d6067b7068ff1", size = 310125, upload-time = "2025-08-11T15:09:50.971Z" }, + { url = "https://files.pythonhosted.org/packages/8e/de/6e1cd6b84b412ac1ef327b76f0641aeb5dcc01e9d3f9eee0286d0c34fd93/ninja-1.13.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3d00c692fb717fd511abeb44b8c5d00340c36938c12d6538ba989fe764e79630", size = 177467, upload-time = "2025-08-11T15:09:52.767Z" }, + { url = "https://files.pythonhosted.org/packages/c8/83/49320fb6e58ae3c079381e333575fdbcf1cca3506ee160a2dcce775046fa/ninja-1.13.0-py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:be7f478ff9f96a128b599a964fc60a6a87b9fa332ee1bd44fa243ac88d50291c", size = 187834, upload-time = "2025-08-11T15:09:54.115Z" }, + { url = "https://files.pythonhosted.org/packages/56/c7/ba22748fb59f7f896b609cd3e568d28a0a367a6d953c24c461fe04fc4433/ninja-1.13.0-py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:60056592cf495e9a6a4bea3cd178903056ecb0943e4de45a2ea825edb6dc8d3e", size = 202736, upload-time = "2025-08-11T15:09:55.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/22/d1de07632b78ac8e6b785f41fa9aad7a978ec8c0a1bf15772def36d77aac/ninja-1.13.0-py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1c97223cdda0417f414bf864cfb73b72d8777e57ebb279c5f6de368de0062988", size = 179034, upload-time = "2025-08-11T15:09:57.394Z" }, + { url = "https://files.pythonhosted.org/packages/ed/de/0e6edf44d6a04dabd0318a519125ed0415ce437ad5a1ec9b9be03d9048cf/ninja-1.13.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fb46acf6b93b8dd0322adc3a4945452a4e774b75b91293bafcc7b7f8e6517dfa", size = 180716, upload-time = "2025-08-11T15:09:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/54/28/938b562f9057aaa4d6bfbeaa05e81899a47aebb3ba6751e36c027a7f5ff7/ninja-1.13.0-py3-none-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4be9c1b082d244b1ad7ef41eb8ab088aae8c109a9f3f0b3e56a252d3e00f42c1", size = 146843, upload-time = "2025-08-11T15:10:00.046Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fb/d06a3838de4f8ab866e44ee52a797b5491df823901c54943b2adb0389fbb/ninja-1.13.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:6739d3352073341ad284246f81339a384eec091d9851a886dfa5b00a6d48b3e2", size = 154402, upload-time = "2025-08-11T15:10:01.657Z" }, + { url = "https://files.pythonhosted.org/packages/31/bf/0d7808af695ceddc763cf251b84a9892cd7f51622dc8b4c89d5012779f06/ninja-1.13.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:11be2d22027bde06f14c343f01d31446747dbb51e72d00decca2eb99be911e2f", size = 552388, upload-time = "2025-08-11T15:10:03.349Z" }, + { url = "https://files.pythonhosted.org/packages/9d/70/c99d0c2c809f992752453cce312848abb3b1607e56d4cd1b6cded317351a/ninja-1.13.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:aa45b4037b313c2f698bc13306239b8b93b4680eb47e287773156ac9e9304714", size = 472501, upload-time = "2025-08-11T15:10:04.735Z" }, + { url = "https://files.pythonhosted.org/packages/9f/43/c217b1153f0e499652f5e0766da8523ce3480f0a951039c7af115e224d55/ninja-1.13.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f8e1e8a1a30835eeb51db05cf5a67151ad37542f5a4af2a438e9490915e5b72", size = 638280, upload-time = "2025-08-11T15:10:06.512Z" }, + { url = "https://files.pythonhosted.org/packages/8c/45/9151bba2c8d0ae2b6260f71696330590de5850e5574b7b5694dce6023e20/ninja-1.13.0-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:3d7d7779d12cb20c6d054c61b702139fd23a7a964ec8f2c823f1ab1b084150db", size = 642420, upload-time = "2025-08-11T15:10:08.35Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/95752eb635bb8ad27d101d71bef15bc63049de23f299e312878fc21cb2da/ninja-1.13.0-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:d741a5e6754e0bda767e3274a0f0deeef4807f1fec6c0d7921a0244018926ae5", size = 585106, upload-time = "2025-08-11T15:10:09.818Z" }, + { url = "https://files.pythonhosted.org/packages/c1/31/aa56a1a286703800c0cbe39fb4e82811c277772dc8cd084f442dd8e2938a/ninja-1.13.0-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:e8bad11f8a00b64137e9b315b137d8bb6cbf3086fbdc43bf1f90fd33324d2e96", size = 707138, upload-time = "2025-08-11T15:10:11.366Z" }, + { url = "https://files.pythonhosted.org/packages/34/6f/5f5a54a1041af945130abdb2b8529cbef0cdcbbf9bcf3f4195378319d29a/ninja-1.13.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b4f2a072db3c0f944c32793e91532d8948d20d9ab83da9c0c7c15b5768072200", size = 581758, upload-time = "2025-08-11T15:10:13.295Z" }, + { url = "https://files.pythonhosted.org/packages/95/97/51359c77527d45943fe7a94d00a3843b81162e6c4244b3579fe8fc54cb9c/ninja-1.13.0-py3-none-win32.whl", hash = "sha256:8cfbb80b4a53456ae8a39f90ae3d7a2129f45ea164f43fadfa15dc38c4aef1c9", size = 267201, upload-time = "2025-08-11T15:10:15.158Z" }, + { url = "https://files.pythonhosted.org/packages/29/45/c0adfbfb0b5895aa18cec400c535b4f7ff3e52536e0403602fc1a23f7de9/ninja-1.13.0-py3-none-win_amd64.whl", hash = "sha256:fb8ee8719f8af47fed145cced4a85f0755dd55d45b2bddaf7431fa89803c5f3e", size = 309975, upload-time = "2025-08-11T15:10:16.697Z" }, + { url = "https://files.pythonhosted.org/packages/df/93/a7b983643d1253bb223234b5b226e69de6cda02b76cdca7770f684b795f5/ninja-1.13.0-py3-none-win_arm64.whl", hash = "sha256:3c0b40b1f0bba764644385319028650087b4c1b18cdfa6f45cb39a3669b81aa9", size = 290806, upload-time = "2025-08-11T15:10:18.018Z" }, +] + +[[package]] +name = "nncf" +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "networkx" }, + { name = "ninja" }, + { name = "numpy" }, + { name = "openvino-telemetry" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pydot" }, + { name = "rich" }, + { name = "safetensors" }, + { name = "scikit-learn" }, + { name = "scipy" }, + { name = "tabulate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/7d/3749a03e3efd2dd355be985b634a3163f9a9b9e2c0495160b6362cb4be97/nncf-3.3.0.tar.gz", hash = "sha256:3dbcbc1ad4f399deed139041fae1fedd4b8aa5749e30af522fcc5a6c2af73e42", size = 556438, upload-time = "2026-08-05T13:49:34.999Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/b3/fe048343262827570d9e4383c90a9eb5d4b71e3378947d3f64c53d7df4f5/nncf-3.3.0-py3-none-any.whl", hash = "sha256:855f1e7099f01ff3a9868cf6d56850e1149691ab0db5ac76ab812ae8b0fba338", size = 794974, upload-time = "2026-08-05T13:49:33.587Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +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/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" }, +] + +[[package]] +name = "nvidia-cublas" +version = "13.1.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cuda-nvrtc", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/a1/0bd24ee8c8d03adac032fd2909426a00c88f8c57961b1277ded97f91119f/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b7a210458267ac818974c53038fbec2e969d5c99f305ab15c72522fa9f001dd5", size = 542848918, upload-time = "2026-04-08T18:46:22.985Z" }, + { url = "https://files.pythonhosted.org/packages/3b/cd/154ca20c38269e05eff77c1464e6c1da89f50a6390b565e9d82e06bc11e1/nvidia_cublas-13.1.1.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:37936a16db8fe4ac1f065c2139360608a543a09275cb1a1af612e08cfa065436", size = 423138758, upload-time = "2026-04-08T18:46:58.655Z" }, +] + +[[package]] +name = "nvidia-cuda-cupti" +version = "13.0.85" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/2a/80353b103fc20ce05ef51e928daed4b6015db4aaa9162ed0997090fe2250/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_aarch64.whl", hash = "sha256:796bd679890ee55fb14a94629b698b6db54bcfd833d391d5e94017dd9d7d3151", size = 10310827, upload-time = "2025-09-04T08:26:42.012Z" }, + { url = "https://files.pythonhosted.org/packages/33/6d/737d164b4837a9bbd202f5ae3078975f0525a55730fe871d8ed4e3b952b0/nvidia_cuda_cupti-13.0.85-py3-none-manylinux_2_25_x86_64.whl", hash = "sha256:4eb01c08e859bf924d222250d2e8f8b8ff6d3db4721288cf35d14252a4d933c8", size = 10715597, upload-time = "2025-09-04T08:26:51.312Z" }, +] + +[[package]] +name = "nvidia-cuda-nvrtc" +version = "13.0.88" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/68/483a78f5e8f31b08fb1bb671559968c0ca3a065ac7acabfc7cee55214fd6/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:ad9b6d2ead2435f11cbb6868809d2adeeee302e9bb94bcf0539c7a40d80e8575", size = 90215200, upload-time = "2025-09-04T08:28:44.204Z" }, + { url = "https://files.pythonhosted.org/packages/b7/dc/6bb80850e0b7edd6588d560758f17e0550893a1feaf436807d64d2da040f/nvidia_cuda_nvrtc-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d27f20a0ca67a4bb34268a5e951033496c5b74870b868bacd046b1b8e0c3267b", size = 43015449, upload-time = "2025-09-04T08:28:20.239Z" }, +] + +[[package]] +name = "nvidia-cuda-runtime" +version = "13.0.96" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/4f/17d7b9b8e285199c58ce28e31b5c5bbaa4d8271af06a89b6405258245de2/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef9bcbe90493a2b9d810e43d249adb3d02e98dd30200d86607d8d02687c43f55", size = 2261060, upload-time = "2025-10-09T08:55:15.78Z" }, + { url = "https://files.pythonhosted.org/packages/2e/24/d1558f3b68b1d26e706813b1d10aa1d785e4698c425af8db8edc3dced472/nvidia_cuda_runtime-13.0.96-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f82250d7782aa23b6cfe765ecc7db554bd3c2870c43f3d1821f1d18aebf0548", size = 2243632, upload-time = "2025-10-09T08:55:36.117Z" }, +] + +[[package]] +name = "nvidia-cudnn-cu13" +version = "9.20.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/c5/83384d846b2fd17c44bd499b36c75a45ed4f095fbbb2252294e89cea5c5c/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:e31454ae00094b0c55319d9d15b6fa2fc50a9e1c0f5c8c80fb75258234e731e1", size = 444574296, upload-time = "2026-03-09T19:28:27.751Z" }, + { url = "https://files.pythonhosted.org/packages/6e/5e/edb9c0ae051602c3ccaffe424256463636d639e27d7f302dde9975ef9e7a/nvidia_cudnn_cu13-9.20.0.48-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0c45dd8eeb50b603f07995b1b300c62ffe6a1980482b82b3bcf94a4ca9d49304", size = 366173588, upload-time = "2026-03-09T19:29:34.474Z" }, +] + +[[package]] +name = "nvidia-cufft" +version = "12.0.0.61" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2f/7b57e29836ea8714f81e9898409196f47d772d5ddedddf1592eadb8ab743/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6c44f692dce8fd5ffd3e3df134b6cdb9c2f72d99cf40b62c32dde45eea9ddad3", size = 214085489, upload-time = "2025-09-04T08:31:56.044Z" }, +] + +[[package]] +name = "nvidia-cufile" +version = "1.15.1.6" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/70/4f193de89a48b71714e74602ee14d04e4019ad36a5a9f20c425776e72cd6/nvidia_cufile-1.15.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08a3ecefae5a01c7f5117351c64f17c7c62efa5fffdbe24fc7d298da19cd0b44", size = 1223672, upload-time = "2025-09-04T08:32:22.779Z" }, + { url = "https://files.pythonhosted.org/packages/ab/73/cc4a14c9813a8a0d509417cf5f4bdaba76e924d58beb9864f5a7baceefbf/nvidia_cufile-1.15.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:bdc0deedc61f548bddf7733bdc216456c2fdb101d020e1ab4b88d232d5e2f6d1", size = 1136992, upload-time = "2025-09-04T08:32:14.119Z" }, +] + +[[package]] +name = "nvidia-curand" +version = "10.4.0.35" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/72/7c2ae24fb6b63a32e6ae5d241cc65263ea18d08802aaae087d9f013335a2/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:133df5a7509c3e292aaa2b477afd0194f06ce4ea24d714d616ff36439cee349a", size = 61962106, upload-time = "2025-08-04T10:21:41.128Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9f/be0a41ca4a4917abf5cb9ae0daff1a6060cc5de950aec0396de9f3b52bc5/nvidia_curand-10.4.0.35-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1aee33a5da6e1db083fe2b90082def8915f30f3248d5896bcec36a579d941bfc", size = 59544258, upload-time = "2025-08-04T10:22:03.992Z" }, +] + +[[package]] +name = "nvidia-cusolver" +version = "12.0.4.66" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-cublas", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-cusparse", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/5f/67/cba3777620cdacb99102da4042883709c41c709f4b6323c10781a9c3aa34/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:0a759da5dea5c0ea10fd307de75cdeb59e7ea4fcb8add0924859b944babf1112", size = 200941980, upload-time = "2025-09-04T08:33:22.767Z" }, +] + +[[package]] +name = "nvidia-cusparse" +version = "12.6.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nvidia-nvjitlink", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, + { url = "https://files.pythonhosted.org/packages/fa/18/623c77619c31d62efd55302939756966f3ecc8d724a14dab2b75f1508850/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b3c89c88d01ee0e477cb7f82ef60a11a4bcd57b6b87c33f789350b59759360b", size = 145942937, upload-time = "2025-09-04T08:33:58.029Z" }, +] + +[[package]] +name = "nvidia-cusparselt-cu13" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/e1/cdc1797eadf82d3a9a575a19b33fdc871a97edbec42c00b5b5e914f4aff4/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:4dca476c50bf4780d46cd0bfbd82e2bc10a08e4fef7950917ce8d7578d22a23f", size = 221051344, upload-time = "2025-09-05T18:49:51.289Z" }, + { url = "https://files.pythonhosted.org/packages/34/7d/2661f2fb3ac4302f3a246f5fc030213ac60c1fe0bce84f9783dbd831dbb7/nvidia_cusparselt_cu13-0.8.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:786ce87568c303fadb5afcc7102d454cd3040d75f6f8626f5db460d1871f4dd0", size = 170148586, upload-time = "2025-09-05T18:50:50.248Z" }, +] + +[[package]] +name = "nvidia-nccl-cu13" +version = "2.29.7" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/0d/daf50d44177ee0cbc7ff0a0c91eb5ff676c82be42f9a970bc7597f440c3a/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:674a12383e3c38a1bcccae7d4f3633b37852230b6047883cb2f4c2d1b36d9bf5", size = 206014712, upload-time = "2026-03-03T05:34:20.843Z" }, + { url = "https://files.pythonhosted.org/packages/67/f4/58e4e91b6919367c7aafb8e36fce9aad1a3047e536bf7e2fd560927d3a4c/nvidia_nccl_cu13-2.29.7-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:edd81538446786ec3b73972543e53bb43bcaf0bfc8ef76cb679fcc390ffe136d", size = 205976000, upload-time = "2026-03-03T05:36:24.472Z" }, +] + +[[package]] +name = "nvidia-nvjitlink" +version = "13.3.33" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/ee/580ca6f29dcab0221db8706badca1bbbb084f1975c4d4e83329c3a7e31f0/nvidia_nvjitlink-13.3.33-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:26a6de7fb4c8fdaa7703d3dad720d6d427ddfea5c48a528fd97c11733ad830e5", size = 40742423, upload-time = "2026-05-26T16:54:51.613Z" }, + { url = "https://files.pythonhosted.org/packages/69/30/45414e35ff2eee7db3da037e5707037ccf9d2b5218ffbdb055ea4d5aa98a/nvidia_nvjitlink-13.3.33-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ce48b37dfeb3cb1eae4cf85adacb47d7a6539ea2272870c9a3628ce275c2037e", size = 39168635, upload-time = "2026-05-26T16:54:13.906Z" }, +] + +[[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 = "onecomp-gptq-openvino-converter" +version = "2026.3.0.1" +source = { virtual = "." } +dependencies = [ + { name = "accelerate" }, + { name = "gptqmodel" }, + { name = "kernels" }, + { name = "numpy" }, + { name = "openvino" }, + { name = "openvino-genai" }, + { name = "openvino-tokenizers", extra = ["transformers"] }, + { name = "optimum-intel", extra = ["openvino"] }, + { name = "protobuf" }, + { name = "sentencepiece" }, + { name = "torch" }, + { name = "torchao" }, + { name = "torchaudio" }, + { name = "torchvision" }, + { name = "transformers" }, +] + +[package.metadata] +requires-dist = [ + { name = "accelerate", specifier = "==1.14.0" }, + { name = "gptqmodel", specifier = "==7.0.0" }, + { name = "kernels", specifier = "==0.12.3" }, + { name = "numpy", specifier = "==2.2.6" }, + { name = "openvino", specifier = "==2026.3.1" }, + { name = "openvino-genai", specifier = "==2026.3.1.0" }, + { name = "openvino-tokenizers", extras = ["transformers"], specifier = "==2026.3.1.0" }, + { name = "optimum-intel", extras = ["openvino"], specifier = "==2.1.0" }, + { name = "protobuf", specifier = "==7.36.0" }, + { name = "sentencepiece", specifier = "==0.2.2" }, + { name = "torch", specifier = "==2.13.0", index = "https://download.pytorch.org/whl/cu130" }, + { name = "torchao", specifier = "==0.18.0" }, + { name = "torchaudio", specifier = "==2.11.0", index = "https://download.pytorch.org/whl/cu130" }, + { name = "torchvision", specifier = "==0.28.0", index = "https://download.pytorch.org/whl/cu130" }, + { name = "transformers", specifier = "==5.5.0" }, +] + +[[package]] +name = "openvino" +version = "2026.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "openvino-telemetry" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/f1/21f1503b5d4a6671730d75e4d0006e9483dfd21ed9899b1ea956a1e683b8/openvino-2026.3.1-22476-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b23fc1669e63b22bdeb3f7cbfcd59b6d6e39ede6e68310ab74a8aaaf3912e70", size = 31612248, upload-time = "2026-08-26T09:17:23.804Z" }, + { url = "https://files.pythonhosted.org/packages/37/91/bb4cee4b1cbc244d6090fdb7625a0b3e1fe4f368bd2075bff67455451fbe/openvino-2026.3.1-22476-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:40bf3724ec70bce0fce4f9547cd1f19a576ef299172ce4ce2552249d10064acc", size = 57510470, upload-time = "2026-08-26T09:17:27.775Z" }, + { url = "https://files.pythonhosted.org/packages/88/c1/4291be34f2677af39933516356d805177effcd7457bf5cf99d289ecbb849/openvino-2026.3.1-22476-cp312-cp312-manylinux_2_35_aarch64.whl", hash = "sha256:e6b78918aef61ee4650bd6bbd6b17e5b8cd90f55f145adff5d7f6def907f33be", size = 28778134, upload-time = "2026-08-26T09:17:30.917Z" }, + { url = "https://files.pythonhosted.org/packages/27/79/77c474c3d8600185792bcb830638198ff3d15fe702028902bd07f4581026/openvino-2026.3.1-22476-cp312-cp312-win_amd64.whl", hash = "sha256:b686302a47abf7c87b48cb265b3a894f787a1b6380f4ab9f5a511de838ba3c64", size = 75818370, upload-time = "2026-08-26T09:17:35.803Z" }, +] + +[[package]] +name = "openvino-genai" +version = "2026.3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "openvino-tokenizers" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/5b/8bf09b86867b3de3428c7ff15cd513d63c4e129bd22307f28bc09e5a1217/openvino_genai-2026.3.1.0-2499-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0f859fc864a3f6b7e19fa6814c7c437804b8d50c70d1e85deeadcd2076dea997", size = 4242920, upload-time = "2026-08-26T09:26:29.289Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e7/36418a3bdbce5a93ee7aafba69b7f5bbcafdda5300b4e2bcda39bace7419/openvino_genai-2026.3.1.0-2499-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:a85b8545a035506b6910a0ef1fa921375d44281969ff827f4e2b95f9c5ecb18c", size = 6246025, upload-time = "2026-08-26T09:26:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/30/c1/6d3eeabb1184b76083d4ea8e48618de0a05edbe7b901a2940d585ff9ec8f/openvino_genai-2026.3.1.0-2499-cp312-cp312-manylinux_2_31_aarch64.whl", hash = "sha256:b87d2ffc51997c5c84f7e8b9bc7e60ac64690a1c99a5663ee317f214671e18d9", size = 5464592, upload-time = "2026-08-26T09:26:32.674Z" }, + { url = "https://files.pythonhosted.org/packages/bd/26/f89400b7403cbd9d85c613ca8e7d868f1858002bc31bc219a3d90987291d/openvino_genai-2026.3.1.0-2499-cp312-cp312-win_amd64.whl", hash = "sha256:e1eddd432240368e89fd2578c6b347b3324899f028087c20b3cf046736ce5a79", size = 3684545, upload-time = "2026-08-26T09:26:34.457Z" }, +] + +[[package]] +name = "openvino-telemetry" +version = "2025.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/8a/89d82f1a9d913fb266c2e6dc2f6030935db24b7152963a8db6c4f039787f/openvino_telemetry-2025.2.0.tar.gz", hash = "sha256:8bf8127218e51e99547bf38b8fb85a8b31c9bf96e6f3a82eb0b3b6a34155977c", size = 18894, upload-time = "2025-07-07T10:29:51.159Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ac/5ab0ca0aa269ad3c73f7bfc3801b10e5f56f75a31bf68c1ae8bd51cf70a4/openvino_telemetry-2025.2.0-py3-none-any.whl", hash = "sha256:bcb667e83a44f202ecf4cfa49281715c6d7e21499daec04ff853b7f964833599", size = 25227, upload-time = "2025-07-07T10:29:50.189Z" }, +] + +[[package]] +name = "openvino-tokenizers" +version = "2026.3.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "openvino" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/ee/43aae7a36b1ce8b4e525cddee0e3d01115ecb8a4b742eeece35d03c1d372/openvino_tokenizers-2026.3.1.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:881950692961f5fdc5628437210777fee9b97952737ba361830e5bb0a5a17539", size = 1750191, upload-time = "2026-08-26T09:20:55.183Z" }, + { url = "https://files.pythonhosted.org/packages/b5/2a/e50406892a96b98bb1adeaf5197adf7a7ef6ead6638837e67ea0f2d8259e/openvino_tokenizers-2026.3.1.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:4dc45f6b62e31b1a9d7ae0a84c1897d6f385688e89a6c87398c43e23944e6edd", size = 1833713, upload-time = "2026-08-26T09:20:56.732Z" }, + { url = "https://files.pythonhosted.org/packages/76/70/4192b1ecfd06d5af9da63df513c02143940a14dc8931fa28291e40baf89a/openvino_tokenizers-2026.3.1.0-py3-none-manylinux_2_31_aarch64.whl", hash = "sha256:596c686cf92b942b48e94e071fa13ef3f3e53c3be097419a08240f57656e0d32", size = 1745649, upload-time = "2026-08-26T09:20:58.393Z" }, + { url = "https://files.pythonhosted.org/packages/13/d0/fdc569ef7dea6b679154a13ff30412733b04c4b9fc0018b4f23818e486ca/openvino_tokenizers-2026.3.1.0-py3-none-win_amd64.whl", hash = "sha256:9a18ac537abc02b8a8cc0d7165cffe79fe16e5087e3fe6625d40d0b84bccb1b9", size = 1541935, upload-time = "2026-08-26T09:20:59.758Z" }, +] + +[package.optional-dependencies] +transformers = [ + { name = "tiktoken" }, + { name = "transformers", extra = ["sentencepiece"] }, +] + +[[package]] +name = "optimum" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "torch" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/76/e4ac0c4b398ed3fe2d41e0058002d276896b9a15a54be16889d8e0d3ee92/optimum-2.3.0.tar.gz", hash = "sha256:aa96ad535a5cec68d12c6372574125452284632fe13699633a61e8bbfb09c4df", size = 124929, upload-time = "2026-08-04T15:35:18.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/f9/a16609b4e4fc592653d9f2a0413689da686a94d0040f3a2fabfff5b5894c/optimum-2.3.0-py3-none-any.whl", hash = "sha256:3e9b217b4ab21fd4cf894a987002ee7d3626114e009592babf084c2f1a0f3b5f", size = 160922, upload-time = "2026-08-04T15:35:17.411Z" }, +] + +[[package]] +name = "optimum-intel" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "nncf" }, + { name = "openvino" }, + { name = "openvino-tokenizers" }, + { name = "optimum" }, + { name = "requests" }, + { name = "safetensors" }, + { name = "setuptools" }, + { name = "torch" }, + { name = "transformers" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9d/df/498acf3432bdf4b7e4bf2b5d66a9c6d87275eded2b6d0d2258c7c8490f31/optimum_intel-2.1.0.tar.gz", hash = "sha256:8d4c0c80af19c9048bc2e58f36f510fa7a66c051089fcf253ffc0ba2b6f1386f", size = 385587, upload-time = "2026-08-05T14:14:02.762Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/a4/d969866a78e854dcf231f2007f6bca9947f684b83bf72ed8ff5825a21c65/optimum_intel-2.1.0-py3-none-any.whl", hash = "sha256:e028c39b66552c7c977968e0df3c160b695fe5cc82d7143f0939576ecb4bb483", size = 414989, upload-time = "2026-08-05T14:14:01.259Z" }, +] + +[package.optional-dependencies] +openvino = [ + { name = "nncf" }, + { name = "openvino" }, + { name = "openvino-tokenizers" }, +] + +[[package]] +name = "packaging" +version = "26.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/4f/5f3422a2afec5ffc46308b79e53291365a93748b498ac2e58bead0197916/pandas-3.0.5.tar.gz", hash = "sha256:dca3734d6ab7c906e6730f0788b0a1dbb9f2467731f9711f77995c8e9d62d712", size = 4658219, upload-time = "2026-07-22T22:19:28.819Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/54/1dc810ea558d1320b597aa140a514f2fdf1d2ea09c38cf556f13ea712ec9/pandas-3.0.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa290c16964d4963fbfbc358928239cf3bd755b20e988ce944877def2f44471d", size = 10411717, upload-time = "2026-07-22T22:18:08.307Z" }, + { url = "https://files.pythonhosted.org/packages/68/56/fbe81c09195924d8b7b8d4461a20458fe80a6a5ed6b24f0314da684277e1/pandas-3.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2e26bb46934b8a2ca0c3de1d3d606fc5f6746584791b2db264d58cf370e08dc", size = 9957095, upload-time = "2026-07-22T22:18:10.6Z" }, + { url = "https://files.pythonhosted.org/packages/e0/51/fac252f4a913ed5eabf3c11b880a9e8d5a6c10f0b2129d0462212d238b4d/pandas-3.0.5-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73fa87b08a7ef706f8aafda39ddaccf2a99047bea62d8c88a0361bcafb2237bc", size = 10485458, upload-time = "2026-07-22T22:18:12.834Z" }, + { url = "https://files.pythonhosted.org/packages/12/98/e976540c1addf70442be7842a18cf70884a964abbf69442504f4d2939989/pandas-3.0.5-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d373ce03ffd84010ed9839fa73672a9c8256990532e158440c0085db7d914b34", size = 10998091, upload-time = "2026-07-22T22:18:15.209Z" }, + { url = "https://files.pythonhosted.org/packages/a4/8c/1f29b5be8d3fc47dd7567eb167fabba2085879b31e0287ce7cba6d3d2ff4/pandas-3.0.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2a29c53d85ea98c5e792c59ef82ee9fbe6ca902c0d0adb6b23f45ef894cd7bf6", size = 11499501, upload-time = "2026-07-22T22:18:17.689Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e2/bd9c98ad2df7b38bde002adde4cdf353519da51881634323b126c55997f9/pandas-3.0.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a5ad3b02ed6bc7d7ae9b70804b2c6aa31827489d150f8e623ce82491b82085d7", size = 12060559, upload-time = "2026-07-22T22:18:20.147Z" }, + { url = "https://files.pythonhosted.org/packages/f3/9a/ffbd852d58bd74a617fe2f8ee6a58a96982271ce41cf981eab22190b4a4b/pandas-3.0.5-cp312-cp312-pyemscripten_2024_0_wasm32.whl", hash = "sha256:b2acb4650527eec6822c3dadb2b771277b65e7dae7a267d4bccf65fd1bb3fbce", size = 7197652, upload-time = "2026-07-22T22:18:22.502Z" }, + { url = "https://files.pythonhosted.org/packages/70/b5/d2d3e9ae73362ba4229651b0ee1455cf78073a1ce585f6ff693782ce263e/pandas-3.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:80a611068e8a3ac23f7398c6c14eb46dc974e5cc9997f653e2dcfd1da74edd41", size = 9831691, upload-time = "2026-07-22T22:18:24.534Z" }, + { url = "https://files.pythonhosted.org/packages/52/51/dea1e89d6a6796b9c43f85a09b484ee03edb8a4c4842e73e200a8c11301c/pandas-3.0.5-cp312-cp312-win_arm64.whl", hash = "sha256:25ff585b972a18ef1fe9ffa3ac6544d9950508aa76832e5147640b6022821e49", size = 9105796, upload-time = "2026-07-22T22:18:27.064Z" }, +] + +[[package]] +name = "pillow" +version = "12.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/bf/fb3ebff8ddcb76aac5a01389251bbbb9519922a9b520d8247c1ca864a25d/pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965", size = 5345969, upload-time = "2026-07-01T11:54:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/d8/66/9a386a92561f402389a4fc70c18838bf6d35eb5eb5c6850b4b2dc64f5048/pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7", size = 4780323, upload-time = "2026-07-01T11:54:09.351Z" }, + { url = "https://files.pythonhosted.org/packages/25/27/ac8f99618ffd3dde21db0f4d4b1d2ab00c0880595bfd17df103f7f39fd0c/pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9", size = 6266838, upload-time = "2026-07-01T11:54:11.71Z" }, + { url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/51/8b08617af3ad95e33ce6d7dd2c99ed6c8298f7fb131636303956be022e25/pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c", size = 6344383, upload-time = "2026-07-01T11:54:15.756Z" }, + { url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" }, + { url = "https://files.pythonhosted.org/packages/20/20/25e0f4dc178a6bc0696793720055519a0de89e7661dae886992decbd2f81/pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f", size = 6472684, upload-time = "2026-07-01T11:54:19.839Z" }, + { url = "https://files.pythonhosted.org/packages/45/89/da2f7971a317f83d807fdd4065c0af40208e59e692cc43d315a71a0e96d1/pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09", size = 7227137, upload-time = "2026-07-01T11:54:22.025Z" }, + { url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887, upload-time = "2026-05-08T21:00:11.277Z" }, + { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654, upload-time = "2026-05-08T21:00:12.604Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190, upload-time = "2026-05-08T21:00:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995, upload-time = "2026-05-08T21:00:15.526Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422, upload-time = "2026-05-08T21:00:16.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342, upload-time = "2026-05-08T21:00:18.362Z" }, + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, + { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588, upload-time = "2026-05-08T21:00:21.155Z" }, + { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029, upload-time = "2026-05-08T21:00:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774, upload-time = "2026-05-08T21:00:24.001Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532, upload-time = "2026-05-08T21:00:25.545Z" }, + { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592, upload-time = "2026-05-08T21:00:27.186Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788, upload-time = "2026-05-08T21:00:28.8Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018, upload-time = "2026-05-08T21:00:31.622Z" }, + { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322, upload-time = "2026-05-08T21:00:32.918Z" }, + { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172, upload-time = "2026-05-08T21:00:35.124Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +] + +[[package]] +name = "protobuf" +version = "7.36.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/e7/0553e21d25ca4d9f573135775348a372c3ec34a93a71d5f297c3bac38341/protobuf-7.36.0.tar.gz", hash = "sha256:e8e09cb0d794c6687926fa558a8a6e72aa10edb997d5ca61da0765f12a3e00ea", size = 510034, upload-time = "2026-08-20T16:34:01.071Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/ae/58e3ca96cb2e118cc546b677359b3c6659f79a140935c08dec94c7998585/protobuf-7.36.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:9103532dffd80c6fab7e50c65a31007680a06eb57537d437bb1b35812c138a37", size = 453256, upload-time = "2026-08-20T16:33:53.945Z" }, + { url = "https://files.pythonhosted.org/packages/f0/15/5162230af4912697f0fe406f6800f80760945babcff0e2c2fe6c84ef2d5d/protobuf-7.36.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:bf94a5917c71058262de683669bc0a797a7669d3de71f0b36d058e3194f47b44", size = 341436, upload-time = "2026-08-20T16:33:55.134Z" }, + { url = "https://files.pythonhosted.org/packages/d7/09/1670b2bfc9a45e807e520c3e9be36524db9ccc7dc05ea17af7681cabdc61/protobuf-7.36.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:3297e60abdff301e5f74393d87f6cc59dacab5f024a89548a6e8de1d26576b16", size = 354440, upload-time = "2026-08-20T16:33:56.077Z" }, + { url = "https://files.pythonhosted.org/packages/c7/f8/bd5804695ba400e423c33fd4d9f58c28d86633d5ba1945c36ff3967d98cb/protobuf-7.36.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:70f5ec8eb0da81a44360c0dc0beac99a0d78071d21956a7076bae8bd2051841b", size = 340439, upload-time = "2026-08-20T16:33:56.992Z" }, + { url = "https://files.pythonhosted.org/packages/ef/9f/acd02338235a3e7d03168c4303478347b7624fc8189ff4e7f0d2654bbe86/protobuf-7.36.0-cp310-abi3-win32.whl", hash = "sha256:7326fd717bdc419162a735938d89d4032332bcc3408804012b24ff3a37086071", size = 440216, upload-time = "2026-08-20T16:33:57.99Z" }, + { url = "https://files.pythonhosted.org/packages/0e/4e/12cb93270967a2affff5b3f720694700d4d87712a67afd05c8cb3f6fa52c/protobuf-7.36.0-cp310-abi3-win_amd64.whl", hash = "sha256:1781cc1de61249b750848029bca452c0a8b7e990080316b9bbc2518b2117b488", size = 453731, upload-time = "2026-08-20T16:33:58.951Z" }, + { url = "https://files.pythonhosted.org/packages/01/c3/629999e78d46c1115c11886d51c6bd68c17ce4a944f1ea3e153a91316a33/protobuf-7.36.0-py3-none-any.whl", hash = "sha256:53374d53fc29a67f7dbbf0ade47d7526a0f0137bf0f9c90e48d8a60790ef748c", size = 177024, upload-time = "2026-08-20T16:34:00.053Z" }, +] + +[[package]] +name = "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/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 = "25.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/e3/27f57f80141379d60defe6703eb50a707325706f07fedfd1312c7a751995/pyarrow-25.0.1.tar.gz", hash = "sha256:9150a83248bfed9813ea3c3af74c3856c1984d444aa28e58bf7733b9750ddf6a", size = 1201653, upload-time = "2026-08-10T12:40:53.904Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/e2/9ab15b88cbfac28e16419ce5439ec29234c5172cb8259301b4ba639bdec0/pyarrow-25.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:df961f2e7ae9cf496459259d798652c70625f6c080650d6952f8c04053c58ee9", size = 35861559, upload-time = "2026-08-10T12:38:02.567Z" }, + { url = "https://files.pythonhosted.org/packages/58/79/a0036dbe1eabe1f73127427342f1d99982584c4a2cde2651d6c93499c6f6/pyarrow-25.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:cc4aa407fde9fc660be3939e49ea31f50f3e9fec17c0ec63159f7711edd3efc9", size = 37628383, upload-time = "2026-08-10T12:38:09.083Z" }, + { url = "https://files.pythonhosted.org/packages/13/49/d93a57d375f4bf0cf82913dd6bb54acafde83dd993be2282c81ac5616cad/pyarrow-25.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:4340f0ba6c1d2e13f21658de1d7c662ca2545018568d0030a1e9afca159d87e3", size = 46820190, upload-time = "2026-08-10T12:38:15.458Z" }, + { url = "https://files.pythonhosted.org/packages/60/c9/711ca85d79f1ec98f29a5eae2b051e25b4ecec5de3e3c0e2d5c5dcb15664/pyarrow-25.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:5389cdf79447ed1515c9e31620e6e1e2302249564d603f2ad727d4f6d313e4c3", size = 50102437, upload-time = "2026-08-10T12:38:22.487Z" }, + { url = "https://files.pythonhosted.org/packages/80/53/8fb8359ff17cfb6263a1cf3ebf7caec9fe197de118719e84fcb1d0618026/pyarrow-25.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d51592cb7561e87877c506113e7adbf1342ab579e6c21f0ef44b8ba41cb74c80", size = 49942424, upload-time = "2026-08-10T12:38:28.755Z" }, + { url = "https://files.pythonhosted.org/packages/e8/83/4e5ae02a9341571b18a6fca380ac7a58ce6ddae7ab3c060208c0a1e79f02/pyarrow-25.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6109c94d8b9f3b17a041daca16cacb2f651ad8f1ef70a4232c2c0f37a23da2a8", size = 53144206, upload-time = "2026-08-10T12:38:34.862Z" }, + { url = "https://files.pythonhosted.org/packages/65/ee/197cbf47e49f83e6ebeb946a5259a48a638dea27ac774db42fe78022179d/pyarrow-25.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:8858d7bfc22e3f51529aeaa4077225029724623e4595dc9eff8c793935c34140", size = 27953934, upload-time = "2026-08-10T12:38:39.808Z" }, +] + +[[package]] +name = "pydot" +version = "3.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyparsing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/dd/e0e6a4fb84c22050f6a9701ad9fd6a67ef82faa7ba97b97eb6fdc6b49b34/pydot-3.0.4.tar.gz", hash = "sha256:3ce88b2558f3808b0376f22bfa6c263909e1c3981e2a7b629b65b451eee4a25d", size = 168167, upload-time = "2025-01-05T16:18:45.763Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/5f/1ebfd430df05c4f9e438dd3313c4456eab937d976f6ab8ce81a98f9fb381/pydot-3.0.4-py3-none-any.whl", hash = "sha256:bfa9c3fc0c44ba1d132adce131802d7df00429d1a79cc0346b0a5cd374dbe9c6", size = 35776, upload-time = "2025-01-05T16:18:42.836Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "pypcre" +version = "0.6.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/29/bc74abc6a568add54b0adf90fe032cb93f22a13c11a2a109cc109dde2d7b/pypcre-0.6.2.tar.gz", hash = "sha256:c6925d4a0af2fa27a24656bf02cac4d75e0a00fdf2c70a1b8d4cc99c32d0c57c", size = 214981, upload-time = "2026-08-24T19:02:47.255Z" } + +[[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 = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" }, + { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" }, + { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" }, + { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, + { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" }, + { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" }, + { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" }, +] + +[[package]] +name = "regex" +version = "2026.7.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/98/04b13f1ddfb63158025291c02e03eb42fbb7acb51d091d541050eb4e35e8/regex-2026.7.19.tar.gz", hash = "sha256:7e77b324909c1617cbb4c668677e2c6ae13f44d7c1de0d4f15f2e3c10f3315b5", size = 416440, upload-time = "2026-07-19T00:19:48.923Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/b9/d11d7e501ac8fd7d617684423ebb9561e0b998481c1e4cbc0cb212c5d74a/regex-2026.7.19-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2cc3460cedf7579948486eab03bc9ad7089df4d7281c0f47f4afe03e8d13f02d", size = 496778, upload-time = "2026-07-19T00:17:05.677Z" }, + { url = "https://files.pythonhosted.org/packages/3f/a9/a5ab6f312f24318019170dc485d5421fe4f89e43a98640da50d95a8a7041/regex-2026.7.19-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0e9554c8785eac5cffe6300f69a91f58ba72bc88a5f8d661235ad7c6aa5b8ccd", size = 297122, upload-time = "2026-07-19T00:17:07.59Z" }, + { url = "https://files.pythonhosted.org/packages/b3/63/4cab4d7f2d384a144d420b763d97674cb70619c878ea6fcd7640d0e62143/regex-2026.7.19-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d7da47a0f248977f08e2cb659ff3c17ddc13a4d39b3a7baa0a81bf5b415430f6", size = 292009, upload-time = "2026-07-19T00:17:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/22/85/102a81b218298957d4ea7d2f084fae537a71add9d6ff93c8e67284c5f45e/regex-2026.7.19-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93db40c8de0815baab96a06e08a984bac71f989d13bab789e382158c5d426797", size = 796708, upload-time = "2026-07-19T00:17:11.542Z" }, + { url = "https://files.pythonhosted.org/packages/78/b5/dc136af5629938a037cd2b304c12240e132ec92f38be8ff9cc89af2a1f2d/regex-2026.7.19-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:66bd62c59a5427746e8c44becae1d9b99d22fb13f30f492083dfb9ad7c45cc18", size = 865651, upload-time = "2026-07-19T00:17:13.312Z" }, + { url = "https://files.pythonhosted.org/packages/e0/75/67402ae3cd9c8c988a4c805d15ee3eef015e7ca4cb112cf3e640fc1f4153/regex-2026.7.19-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1649eb39fcc9ea80c4d2f110fde2b8ab2aef3877b98f02ab9b14e961f418c511", size = 911756, upload-time = "2026-07-19T00:17:15.015Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8e/096d00c7c480ef2ff4265349b14e2261d4ab787ba1f74e2e80d1c58079c3/regex-2026.7.19-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9dce8ec9695f531a1b8a6f314fd4b393adcccf2ea861db480cdf97a301d01a68", size = 801798, upload-time = "2026-07-19T00:17:17.208Z" }, + { url = "https://files.pythonhosted.org/packages/f0/41/e7ecac6edb5722417f85cc67eaf386322fbe8acf6918ec2fdc37c20dd9d0/regex-2026.7.19-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3080a7fd38ef049bd489e01c970c97dd84ff446a885b0f1f6b26d9b1ad13ce11", size = 776933, upload-time = "2026-07-19T00:17:19.347Z" }, + { url = "https://files.pythonhosted.org/packages/6f/69/03c9b3f058d66403e0ca2c938696e81d51cd4c6d47ec5265f02f96948d9a/regex-2026.7.19-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d793a7988e04fcb1e2e135567443d82173225d657419ec09414a9b5a145b986", size = 784338, upload-time = "2026-07-19T00:17:21.057Z" }, + { url = "https://files.pythonhosted.org/packages/f6/f7/b38ab3d43f284afbb618fcd15d0e77eb786ae461ce1f6bc7494619ddc0f2/regex-2026.7.19-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e8b0abe7d870f53ca5143895fef7d1041a0c831a140d3dc2c760dd7ba25d4a8b", size = 860452, upload-time = "2026-07-19T00:17:23.119Z" }, + { url = "https://files.pythonhosted.org/packages/15/5c/ff60ef0571121714f3cf9920bc183071e384a10b556d042e0fdb06cc07a5/regex-2026.7.19-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:4e5413bd5f13d3a4e3539ca98f70f75e7fca92518dd7f117f030ebedd10b60cb", size = 765958, upload-time = "2026-07-19T00:17:24.81Z" }, + { url = "https://files.pythonhosted.org/packages/aa/0f/bd34021162c0ab47f9a315bd56cd5642e920c8e5668a75ef6c6a6fca590d/regex-2026.7.19-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:73b133a9e6fb512858e7f065e96f1180aa46646bc74a83aea62f1d314f3dd035", size = 851765, upload-time = "2026-07-19T00:17:26.993Z" }, + { url = "https://files.pythonhosted.org/packages/2a/20/a2ca43edade0595cccfdc98636739f536d9e26898e7dbddc2b9e98898953/regex-2026.7.19-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dbe6493fbd27321b1d1f2dd4f5c7e5bd4d8b1d7cab7f32fd67db3d0b2ed8248a", size = 789714, upload-time = "2026-07-19T00:17:28.699Z" }, + { url = "https://files.pythonhosted.org/packages/5d/47/e02db4015d424fc83c00ea0ac8c5e5ec14397943de9abf909d5ce3a25931/regex-2026.7.19-cp312-cp312-win32.whl", hash = "sha256:ddd67571c10869f65a5d7dde536d1e066e306cc90de57d7de4d5f34802428bb5", size = 267157, upload-time = "2026-07-19T00:17:31.051Z" }, + { url = "https://files.pythonhosted.org/packages/08/8e/c780c131f79b42ed22d1bd7da4096c2c35f813e835acd02ef0f018bd892c/regex-2026.7.19-cp312-cp312-win_amd64.whl", hash = "sha256:e30d40268a28d54ce0437031750497004c22602b8e3ab891f759b795a003b312", size = 277777, upload-time = "2026-07-19T00:17:32.848Z" }, + { url = "https://files.pythonhosted.org/packages/3e/4c/e4d7e086449bdf379d89774bf1f89dc4a41943f3c5a6125a03905b34b5fb/regex-2026.7.19-cp312-cp312-win_arm64.whl", hash = "sha256:de9208bb427130c82a5dbfd104f92c8876fc9559278c880b3002755bbbe9c83d", size = 277136, upload-time = "2026-07-19T00:17:34.803Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + +[[package]] +name = "safetensors" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" }, + { url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" }, + { url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" }, + { url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" }, + { url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" }, + { url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" }, + { url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "joblib" }, + { name = "narwhals" }, + { name = "numpy" }, + { name = "scipy" }, + { 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 = [ + { url = "https://files.pythonhosted.org/packages/ac/20/75f915ff375d6249e6550ac740fdbbd66159a068fd3af1400ff62036b07a/scikit_learn-1.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2bd41b0d201bc81575531b96b713d3eb5e5f50fb0b82101ff0f92294fdc236ac", size = 8741122, upload-time = "2026-06-02T11:53:24.08Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d5/2b5148f2279196775e1db2aeb85d14b70ac80e7e32b3b28e7ebeafb0901d/scikit_learn-1.9.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5be45aa4a42a68a533913a6ed736cf309de2226411c79ef8d609a5456f1939b1", size = 8261512, upload-time = "2026-06-02T11:53:27.183Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ee/5adbc77656b71f9456a2f5a7a9fdb4bcf9207a6b962889f1c2f9323afa4e/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e50ed4da51974e86e940690e9a3d82e729b62b5a49f7c9bac534d515d39d86f", size = 8837603, upload-time = "2026-06-02T11:53:30.328Z" }, + { url = "https://files.pythonhosted.org/packages/6c/c2/63fdda36c56437eeb44aaf9493c8bcd62ce230ab1598924fc626ffbfa943/scikit_learn-1.9.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:056c92bb67ad4c28463c2f2653d9701449201e7e7a9e94e321be0f71c4fef2b8", size = 9132097, upload-time = "2026-06-02T11:53:33.456Z" }, + { url = "https://files.pythonhosted.org/packages/83/a4/c8e67227c680e2259c8864ae72ff48b06e16a6f51253a22167aa02a8aa4e/scikit_learn-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:4306775fad04cc4b472a1b15af1ae9cede1540fbfcc17fbce3767cd8dc7ae283", size = 8211173, upload-time = "2026-06-02T11:53:36.602Z" }, + { url = "https://files.pythonhosted.org/packages/cf/fd/3c0863792e98e67e9184aa4029288a175935eb65443afcd30d4f143450cf/scikit_learn-1.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:26e22435f63bcdcf396b574273f29f13dd531f5ea035801f5be10ba1540a4e60", size = 7867451, upload-time = "2026-06-02T11:53:39.075Z" }, +] + +[[package]] +name = "scipy" +version = "1.18.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7e/74/66de6258867beb2ef08f35f9f2ac017a52cacd5081714d239ff1a442d458/scipy-1.18.1.tar.gz", hash = "sha256:52c4b7422442aba924d03ad4019852b08a92e64ea187b933135687bfe2747307", size = 30781235, upload-time = "2026-08-21T23:28:50.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/f7/240c110c08693826b4513a52f5717d62ec7c7af72f2920821247c03b17b3/scipy-1.18.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:457fd7a2a8edeb044ab6ffbc0aa03ff6cd18491356e5e0c834d76ce621b916d1", size = 31111061, upload-time = "2026-08-21T23:23:44.522Z" }, + { url = "https://files.pythonhosted.org/packages/05/4a/78c6285577c375e7cf27277ea8ee6961224327f1e1a0c44af5f17f23635c/scipy-1.18.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:e708533e8b2ae2497d65346538a7dcc92814410b25b81432eac66de0f2af8265", size = 28733332, upload-time = "2026-08-21T23:23:50.015Z" }, + { url = "https://files.pythonhosted.org/packages/a5/f6/a5b82f8abbe14d134691b8b903696f701d25a081353a29dc655c364d9e62/scipy-1.18.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:7bbf207c4453ce1ad2e00b17313852b33310b83090c2311bdaf97f93c0380d12", size = 20475078, upload-time = "2026-08-21T23:23:54.138Z" }, + { url = "https://files.pythonhosted.org/packages/23/22/0858a0bbd6b3e825ceb8cd9baf9eaf3b2f2b1d77727eb6be40500bcdc92f/scipy-1.18.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:78c0665edead396b1abb4897c41a5c1d9bf090c8a637a4c20a61678e0a264e66", size = 23108904, upload-time = "2026-08-21T23:23:57.824Z" }, + { url = "https://files.pythonhosted.org/packages/75/9a/2e71719f31eaefe0e3a1706c4a1ded94e664bfd95ffca2b219a671faee01/scipy-1.18.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c085faa2cfa879c5141df483f836f4d691045a078224a670fa570fa01612d89", size = 34025113, upload-time = "2026-08-21T23:24:02.209Z" }, + { url = "https://files.pythonhosted.org/packages/df/64/ff35eb9e54894cf471ff4716abd3c81eb0a0626869217ce3e6ba4ccf17d7/scipy-1.18.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f55fa87b6c612ecd6b058f167c53231b1d14e412efe361d3d6e38b3631c73218", size = 35344199, upload-time = "2026-08-21T23:24:07.844Z" }, + { url = "https://files.pythonhosted.org/packages/d3/af/c5538be1792f7034c12c7db6ee67cace58253c7b87b122d68253eaf5de89/scipy-1.18.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c35d74ce0e193ff740c2f2be2ac913ddc232fe6c1ff40b26cfecb9c670c63314", size = 35639587, upload-time = "2026-08-21T23:24:13.05Z" }, + { url = "https://files.pythonhosted.org/packages/91/4c/075e4f66471bac101141ac739e9e135549be1bae584571bd03a530c056e1/scipy-1.18.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2924a03db38dc2e848bca2fe9f077dafb891480b91a00a0963a8cf86dfc31c1", size = 37480330, upload-time = "2026-08-21T23:24:19.608Z" }, + { url = "https://files.pythonhosted.org/packages/39/e7/979fd14e75008623df31ba70d6bb144700f68feadcea042021c06a05bf82/scipy-1.18.1-cp312-cp312-win_amd64.whl", hash = "sha256:5e4d44984abc0020154ea81b247adeddcc3ac5527b975ff798bd1ba0adc513c2", size = 36658278, upload-time = "2026-08-21T23:24:25.463Z" }, + { url = "https://files.pythonhosted.org/packages/c7/0b/e1525354ff9d7d5feb6d1b31af6d14072e5c91e9607b421fa1ec889660b3/scipy-1.18.1-cp312-cp312-win_arm64.whl", hash = "sha256:d65d448389b8436493abcf629cc94ad0cf32aecaf06e1acca1de53cc795f2f12", size = 24400588, upload-time = "2026-08-21T23:24:30.579Z" }, +] + +[[package]] +name = "sentencepiece" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/33/ea3cb3839607eb175da835244a798f797f478c5ddf0e8ecdf57ea85a4c70/sentencepiece-0.2.2.tar.gz", hash = "sha256:3d2b5e824b5622038dc7b490897efe05ebbbb9e7350fc142f3ecc8789ef9bdf6", size = 8218435, upload-time = "2026-07-12T08:39:34.701Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/13/7a562289c8d5b49ebdf3f9c1e8ab67cf14a8743b1d90c8f406bfdec36b72/sentencepiece-0.2.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1edb10e520e4bddf74d85b0f5ae74cc2d60c2b448885080bfb618bc2b3a49f6b", size = 2188384, upload-time = "2026-07-12T08:38:28.486Z" }, + { url = "https://files.pythonhosted.org/packages/85/d1/912f14fd5eae168aba726ffb6a9a2dc1c71fe7676c53da6f5c442b886d4a/sentencepiece-0.2.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7c06c751c19d923435a54bff4f7e66e728fad160e8da28254f133abc9725820", size = 1441553, upload-time = "2026-07-12T08:38:30.552Z" }, + { url = "https://files.pythonhosted.org/packages/bd/44/caa9cab5f261a019e2808bc5046152775dc57352ba9cbae7525e9e7a1ed4/sentencepiece-0.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38111ed1f79268f399c505028023d5eaaf0ab4e5eafceb709468b0d3323e7838", size = 1347176, upload-time = "2026-07-12T08:38:32.211Z" }, + { url = "https://files.pythonhosted.org/packages/19/90/cd798935668cff71d309d8ff10385844ecf216b1fe454f1993ed8bf2cb91/sentencepiece-0.2.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbce24284f51f71d10a42b7b9c964dcb9048b28f1c8e5db40bcbcb6f428cba6a", size = 1325200, upload-time = "2026-07-12T08:38:33.689Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2d/37e3da037318a70066ded0d51bc2a7f35491ae6338dd993d5eb1503fc3b5/sentencepiece-0.2.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c8a168b040bc61681293f79a949b5d911c8e25086f4260285b8d97ab5f1195da", size = 1397736, upload-time = "2026-07-12T08:38:35.771Z" }, + { url = "https://files.pythonhosted.org/packages/8d/11/753fca2e6b109be3ab7867abf357dfe48677fe726ae5a5363d0b54ca9450/sentencepiece-0.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:7c6e7bf684dc12145bfa685d3060beaea55139134ba848289bee514ed42e7383", size = 1248030, upload-time = "2026-07-12T08:38:37.604Z" }, + { url = "https://files.pythonhosted.org/packages/e2/0a/70efbe861ca182d7d4b6e1a20f58e043400848fa9f2915229f082e221648/sentencepiece-0.2.2-cp312-cp312-win_arm64.whl", hash = "sha256:76ff5814db72e7462dece042d7593cdf102b8ec82c2b1cc201a2add34ee3050d", size = 1187325, upload-time = "2026-07-12T08:38:39.348Z" }, +] + +[[package]] +name = "setuptools" +version = "84.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/44/f5da03a8ef95d369145c5bb53050e7877c9f3d312e128605fd9504829143/setuptools-84.0.0.tar.gz", hash = "sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73", size = 1168449, upload-time = "2026-08-08T18:27:58.365Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl", hash = "sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", size = 818216, upload-time = "2026-08-08T18:27:56.719Z" }, +] + +[[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 = "sympy" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mpmath" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, +] + +[[package]] +name = "tabulate" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754, upload-time = "2026-03-04T18:55:34.402Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814, upload-time = "2026-03-04T18:55:31.284Z" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, +] + +[[package]] +name = "tiktoken" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/62/167a842aa0429d45f5e797354fd4343a96f6043d67d0513c675c7b8d36e6/tiktoken-0.14.0.tar.gz", hash = "sha256:231dec90efcdccf1b565a1416107736f1e09b1a08fe736ef9d6363e626d03874", size = 38898, upload-time = "2026-08-17T19:49:49.514Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/da/e273746b9d24a63c776bc60fba914351573ad9c575b52601eb5e60632564/tiktoken-0.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8e947aefe98ef74cce94923f90e48c98fe34eb1ec0a6bfdfadfc5a96359bfc36", size = 1094408, upload-time = "2026-08-17T19:48:49.269Z" }, + { url = "https://files.pythonhosted.org/packages/69/9f/fe6b1aca23331aa5271df5a4bd07bf68a7059254d47faee1b8272592a777/tiktoken-0.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cebe67765569df3dafac8474e4eccf5c19d24140492567a5e58a11445732a4", size = 1038499, upload-time = "2026-08-17T19:48:50.666Z" }, + { url = "https://files.pythonhosted.org/packages/0b/35/e9f47647c9e163bd1de30fe1a491669b7248cfc67b7404c35c009a701e1a/tiktoken-0.14.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:7db45b98e94adf4173a5cd7422b150999a7ee11ff847783a14f6e1b80cc38cb6", size = 1186355, upload-time = "2026-08-17T19:48:51.93Z" }, + { url = "https://files.pythonhosted.org/packages/51/11/9976ad86980a00cdef05e730a0127a2578a1bc6d11644d8d47246de2eb26/tiktoken-0.14.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7896eea257fe497a2b7134474d909156c6744ce8da35bce88011a960e008aa0d", size = 1204197, upload-time = "2026-08-17T19:48:53.18Z" }, + { url = "https://files.pythonhosted.org/packages/d4/9c/7035b0bcfaa68d1ee4803fc5be5214ad865669b05bd20e7105ae8a18afc6/tiktoken-0.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b950248272f1b303dc32986396e2dccfa10cf6d1e83ec8f0bba1776660305482", size = 1250635, upload-time = "2026-08-17T19:48:54.392Z" }, + { url = "https://files.pythonhosted.org/packages/bc/1d/69cabf18bed7f4366da076735816abce0d4db3fae491ae338a6612128777/tiktoken-0.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3de75343041a1c57333b1e707ac8a9769738241d7d6a55d39e12cf84548337c6", size = 1316085, upload-time = "2026-08-17T19:48:55.525Z" }, + { url = "https://files.pythonhosted.org/packages/bd/bd/a2e884fb1402cba5be08836590320012b2d8ada0e2eef9911a64df4bcd2d/tiktoken-0.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:087538c080e5ff421abd3a0785ed63c5111d06af98e6cd0d374dbe5969147ca3", size = 941208, upload-time = "2026-08-17T19:48:56.938Z" }, +] + +[[package]] +name = "tokenicer" +version = "0.0.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/71/afb973aca6ca27412a6a20775075e086ca022d9da7034d081051f20bc186/tokenicer-0.0.14.tar.gz", hash = "sha256:ef4d8346e1cc747f2c7854eb6510b6fe95d09941ed33b4a89ea1f1bbb7b849d0", size = 14281, upload-time = "2026-07-22T05:10:52.766Z" } + +[[package]] +name = "tokenizers" +version = "0.22.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, +] + +[[package]] +name = "torch" +version = "2.13.0+cu130" +source = { registry = "https://download.pytorch.org/whl/cu130" } +dependencies = [ + { name = "cuda-bindings", marker = "sys_platform == 'linux'" }, + { name = "cuda-toolkit", extra = ["cublas", "cudart", "cufft", "cufile", "cupti", "curand", "cusolver", "cusparse", "nvjitlink", "nvrtc", "nvtx"], marker = "sys_platform == 'linux'" }, + { name = "filelock" }, + { name = "fsspec" }, + { name = "jinja2" }, + { name = "networkx" }, + { name = "nvidia-cudnn-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparselt-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu13", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvshmem-cu13", marker = "sys_platform == 'linux'" }, + { name = "setuptools" }, + { name = "sympy" }, + { name = "triton", marker = "sys_platform == 'linux'" }, + { name = "typing-extensions" }, +] +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.13.0%2Bcu130-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5ebd552c887e707c8e64927aceb8377ca2e81588c4e7494bcd23cb8ac0aca14d", upload-time = "2026-07-08T20:24:19Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.13.0%2Bcu130-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:8db7338e6895c3d4bd89a02ff4209507d1f0cf2ffeb3b898538b5a07d1ea8c1e", upload-time = "2026-07-08T20:24:52Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torch-2.13.0%2Bcu130-cp312-cp312-win_amd64.whl", hash = "sha256:2efab1e83604ca628c6d85b9e188c153690980498d1297081a9dad704919303c", upload-time = "2026-07-08T20:26:27Z" }, +] + +[[package]] +name = "torchao" +version = "0.18.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/55/ed9ad98f0f09d5a1124d09830043d13a39e63539f9590d2bdb6d71cbc4a4/torchao-0.18.0-cp310-abi3-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6540b148e40ba81cbd4de86392225a076a1591146e9cebb099b3b234ba9feebe", size = 3372585, upload-time = "2026-08-03T19:43:10.993Z" }, + { url = "https://files.pythonhosted.org/packages/c4/4d/485477bb8f05bd501016059c6d8abd742f830cb1b24ab7704e086c7cc35a/torchao-0.18.0-py3-none-any.whl", hash = "sha256:5c2b4485341bf28b7fed2c4fc95b9f298e209f41685350f067de85527a05585e", size = 1369798, upload-time = "2026-08-03T19:43:12.649Z" }, +] + +[[package]] +name = "torchaudio" +version = "2.11.0+cu130" +source = { registry = "https://download.pytorch.org/whl/cu130" } +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu130/torchaudio-2.11.0%2Bcu130-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:7171f810887e7cd1a4763974d5a1f2e1466692404315bb70705e0f49fb3a28e0", upload-time = "2026-03-23T15:50:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchaudio-2.11.0%2Bcu130-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3fba988f4301fe13547fe5e99c76d9ae36a27e19ded82eeffed9d2456e12edef", upload-time = "2026-03-23T15:50:26Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchaudio-2.11.0%2Bcu130-cp312-cp312-win_amd64.whl", hash = "sha256:f74949f9ace1e4a6cf9468bdb3211b9cfa0af6ea348125471ac71c8621d6c77d", upload-time = "2026-03-23T15:50:26Z" }, +] + +[[package]] +name = "torchvision" +version = "0.28.0+cu130" +source = { registry = "https://download.pytorch.org/whl/cu130" } +dependencies = [ + { name = "numpy" }, + { name = "pillow" }, + { name = "torch" }, +] +wheels = [ + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.28.0%2Bcu130-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:ecf72161734b0cf75aabeb2a83101ee021ba8a9cfea57b40050deed7accd4615", upload-time = "2026-07-08T12:26:52Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.28.0%2Bcu130-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:8a0008d34ccc4e81066b97ff0ae5a34c676bfdf3464baf40c01b320dc9a45ce0", upload-time = "2026-07-08T12:26:52Z" }, + { url = "https://download-r2.pytorch.org/whl/cu130/torchvision-0.28.0%2Bcu130-cp312-cp312-win_amd64.whl", upload-time = "2026-07-08T12:26:52Z" }, +] + +[[package]] +name = "tqdm" +version = "4.70.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/3b/6c24bec5be5e743ffd99576daa5cc077722fc7d5bbc00bd133fa0c698dc6/tqdm-4.70.0.tar.gz", hash = "sha256:55b0b0dbd97462d06ebee91e4dac24ed4d4702be82b24f07e6c1d27e08cea220", size = 795438, upload-time = "2026-07-27T11:33:15.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/1c/01bfd571a64e7f270e6bab5e33777debe0edc56759233ce84f27dec92d14/tqdm-4.70.0-py3-none-any.whl", hash = "sha256:7f585706bfddbdebf89daac705b2dfcc16890130727d3197ca62c732b4310953", size = 80184, upload-time = "2026-07-27T11:33:13.167Z" }, +] + +[[package]] +name = "transformers" +version = "5.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "safetensors" }, + { name = "tokenizers" }, + { name = "tqdm" }, + { name = "typer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/9d/fb46e729b461985f41a5740167688b924a4019141e5c164bea77548d3d9e/transformers-5.5.0.tar.gz", hash = "sha256:c8db656cf51c600cd8c75f06b20ef85c72e8b8ff9abc880c5d3e8bc70e0ddcbd", size = 8237745, upload-time = "2026-04-02T16:13:08.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/28/35f7411ff80a3640c1f4fc907dcbb6a65061ebb82f66950e38bfc9f7f740/transformers-5.5.0-py3-none-any.whl", hash = "sha256:821a9ff0961abbb29eb1eb686d78df1c85929fdf213a3fe49dc6bd94f9efa944", size = 10245591, upload-time = "2026-04-02T16:13:03.462Z" }, +] + +[package.optional-dependencies] +sentencepiece = [ + { name = "protobuf" }, + { name = "sentencepiece" }, +] + +[[package]] +name = "triton" +version = "3.7.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/fa/f856e24deb462d5f18bd4b5a746957862ab9b6ee5834bda60605ec348366/triton-3.7.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9497f2e696ee368862a181a90b2dcc03ca978cc4f602abd67c7d81022a6988e1", size = 184692359, upload-time = "2026-06-17T20:03:48.288Z" }, + { url = "https://files.pythonhosted.org/packages/c4/6f/fb96d15db6f36d6eae4cafb998c2e0353bf59d7c4ea1662d7497f269134a/triton-3.7.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7e40869937a68206ec70d7f25bb7ec6433cb083f9135e1f36dbd318dc449a728", size = 197719725, upload-time = "2026-06-17T19:53:20.419Z" }, +] + +[[package]] +name = "typer" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ff/5a28bdfd8c3ebec42564ac7d0e54ca3db65044a9314a97f9564fa7a1e926/tzdata-2026.3.tar.gz", hash = "sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415", size = 198674, upload-time = "2026-07-10T08:50:37.887Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/6d/b53b99a9f2766d095985947a5782f1702cabb129a34f7a802d7197af832f/tzdata-2026.3-py2.py3-none-any.whl", hash = "sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931", size = 348168, upload-time = "2026-07-10T08:50:36.46Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + +[[package]] +name = "xxhash" +version = "4.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/a5/1386f35da1475fcaeef42581deae73417c6d2a6a0b2d2e8914de18844dcd/xxhash-4.0.1.tar.gz", hash = "sha256:d55bf4ef10eb09b8b6866790e083d26d087d84caa3cc0946ba87c3ca7ecaf7b7", size = 101513, upload-time = "2026-08-17T08:24:08.557Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/6c/dc7cffeadd06336cd934947187cd38abb263103bbc552ca0f55fe4ff595a/xxhash-4.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1ee523f51718e41753f04f7102bb4dc55a18d2ea5cbaceef8ec7ca08571bd428", size = 38444, upload-time = "2026-08-17T08:21:54.332Z" }, + { url = "https://files.pythonhosted.org/packages/75/c9/cf736f6db8c3273af18925061572db0d4357818a9ce425f4b5fb0021918e/xxhash-4.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:515a822c73abbf6a0b7c70976d9662be342835c9d78b8dc7c023411f39c35dbc", size = 36195, upload-time = "2026-08-17T08:35:13.004Z" }, + { url = "https://files.pythonhosted.org/packages/da/a2/ca1929354b6851529d0148f7f335b5e2b0281f83bab3e19f0896dc579796/xxhash-4.0.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f5d031f35962e5483a613214e61f09fe24ab523062c3646d592dc16c4a217451", size = 253113, upload-time = "2026-08-17T08:20:52.152Z" }, + { url = "https://files.pythonhosted.org/packages/de/bb/542005206af59518bc8d78a210f1e0172217bc53beb32f64a5b632e72b6b/xxhash-4.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da0264844a09b538c894e5eff25313d941deb4dedec2131b98418a71a3c9944e", size = 276525, upload-time = "2026-08-17T08:21:01.886Z" }, + { url = "https://files.pythonhosted.org/packages/1b/df/607cff25dcb0f1d35c3b04493f6ad8471edb03fd4eacbdcc5ceddef1f3e9/xxhash-4.0.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1642907941ee4b75aacc3db688af52ea02ca2305ab22af7ee686ed726b332684", size = 297703, upload-time = "2026-08-17T08:21:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/15/ba/9d2275eea0b9d9c6b02921be23f7588356c60df95c763b25f0e045894d43/xxhash-4.0.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4af350bc3f329970c0e3a59af84a8a30998bf8a9167eb50cd48e59baaa1d7bec", size = 280252, upload-time = "2026-08-17T08:20:47.299Z" }, + { url = "https://files.pythonhosted.org/packages/1d/aa/2299d9f6369e550aef2abb64945e39daa34412725aa46a20d99b74d76f67/xxhash-4.0.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8ba782ca3bf1e81492611152b9a0d5264971339e95e34d69de0ac2c926be496d", size = 511041, upload-time = "2026-08-17T08:20:36.771Z" }, + { url = "https://files.pythonhosted.org/packages/83/97/31bd8b8279e6935a0719f6910ced15e9d5a2cd554b253f6027ce1b5a1c2c/xxhash-4.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:237b8f63a2a0fcfb1ffc06e21dad23add44e6d354b2b014364a1d41e419a4dee", size = 261812, upload-time = "2026-08-17T08:22:00.469Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c1/d180a2da23c105d8e0b02d54f9f5841013fc81c233010ec781e31f1aee4c/xxhash-4.0.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:81507a68ba84c55241fb61cce1469f473a5da4205fc8ef6f698e5948eea8dd88", size = 339878, upload-time = "2026-08-17T08:35:17.626Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3d/f584cd3172fe934f0f5a0a3917d0d7ce781f74d794fd43bb72be71c3ef6f/xxhash-4.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1ea31d61bcd2cd2f3ec4ca80a64187bbd7948f490b63cf0dcbc6e717b4c1e9", size = 272871, upload-time = "2026-08-17T08:20:56.067Z" }, + { url = "https://files.pythonhosted.org/packages/34/50/2c7956b2b551682e00b9aebce9ceb0a991a131d65f9850c09f5f9760be2e/xxhash-4.0.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:06713a5aaf1d0905c5579416c020c02e42b3ceb931e86c7d3b7fb85403dee3f3", size = 301440, upload-time = "2026-08-17T08:21:35.911Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a2/0739f6482184a8026f4b022718f5f815d352059312e80696825433f0a8e7/xxhash-4.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e8cda075b10bb3917b002c74a04f9e02b7d13b5bf732571404d51c52b11c7329", size = 260157, upload-time = "2026-08-17T08:22:01.416Z" }, + { url = "https://files.pythonhosted.org/packages/a1/25/b31a7bcf1d7d116842812e54f9b944843b4236ea4fa85634e8259f342212/xxhash-4.0.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c10b9206753b64aa791b35b201485477525b26fdec5bf86e8364c388a03e2592", size = 278233, upload-time = "2026-08-17T08:21:15.674Z" }, + { url = "https://files.pythonhosted.org/packages/db/e8/5293bae090fc6119dbc5fcf5c4cc0e1536394b52d73b7904d033836c73db/xxhash-4.0.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:f3e1a44af01b6692de0ec6caba5f0bf93ceb36896e02b7fc00952c6ea7ef39e1", size = 330270, upload-time = "2026-08-17T08:20:51.128Z" }, + { url = "https://files.pythonhosted.org/packages/72/9e/e2ab12d40921f3f34c9317637d65e011aeababf8288356ea8d527de2c1d0/xxhash-4.0.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:c6fc415b5568bd9accc7187f1729a99707330c0a67a8b9f93c1149ed573ed75d", size = 478555, upload-time = "2026-08-17T08:22:04.183Z" }, + { url = "https://files.pythonhosted.org/packages/6d/32/c6148d39a49efa95f39b4cf0d41ef35a487f3b30f6fb1fc8fe8d8eab577e/xxhash-4.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96d8de55029d42251945531f6aa7590c32b48163c66a43bf29d8657d7446a377", size = 258174, upload-time = "2026-08-17T08:35:21.18Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fb/0b04b68d6c5bc71c7a2c344f1287327b67e607f28fbcfd937697caca64b6/xxhash-4.0.1-cp312-cp312-pyemscripten_2024_0_wasm32.whl", hash = "sha256:0163b5d259de23ae9e07b7eabf435ce4704f6f205589a2b154e6af4be985ce1b", size = 20767, upload-time = "2026-08-17T08:21:00.806Z" }, + { url = "https://files.pythonhosted.org/packages/a6/be/476092aba34d1fcd313e1613a3bb3bc692f253d167b54bc90049043b5034/xxhash-4.0.1-cp312-cp312-win32.whl", hash = "sha256:1216f7ba5683f17a89eb7dcb4bc50a0b743dfe1902278d7b3d0786f538118433", size = 34669, upload-time = "2026-08-17T08:21:49.486Z" }, + { url = "https://files.pythonhosted.org/packages/aa/02/f9413d94fae43cec6d1a74c4f12156c6f4a7f5fd50e1d34defebdee3dec9/xxhash-4.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5c2d525a3afabcd8e3549d85fc7e111fde6bc302d06a1893fe73adb79823415e", size = 37073, upload-time = "2026-08-17T08:22:04.886Z" }, + { url = "https://files.pythonhosted.org/packages/c1/83/6fe93c1b95acf962bc61a246df09dc2dcce895ccfc1080c9f48d0b652b92/xxhash-4.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:86b2b12bec60c678ed8f5cca0258ad93a8928ebddb6ca7732f0875afe1451d1a", size = 33299, upload-time = "2026-08-17T08:35:12.708Z" }, + { url = "https://files.pythonhosted.org/packages/86/79/9127ff42a887a348dc4ce3211cf1a962836887adee6f57078132bfba78b4/xxhash-4.0.1-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:ff48915bf1871a1f19f74c11834c6329443d306cedc0c05fe7fe617810422a80", size = 31836, upload-time = "2026-08-17T08:36:28.261Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e6/f238693bfdd642adb59c99683964d46d9947fe721ff44d3bd850ae675407/xxhash-4.0.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a76345f5aceb4ec404918edf9c7f2b5507db864dc0d7455982009ac0890b57b", size = 34453, upload-time = "2026-08-17T08:23:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/40/4b/796ace33cdfb75c91ba6d11615c3bd436355b9f3103e05865bbee9abce57/xxhash-4.0.1-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:31d86f9e81f3e84e00131ac7c54caf5119ae4ddd82c09c31cff597c813ce1ee2", size = 38488, upload-time = "2026-08-17T08:23:59.901Z" }, + { url = "https://files.pythonhosted.org/packages/ad/23/2d549e5d5d7759eaf9ac2d2d2ab81ff60f1bb2b52cdaae8e5ec5c6524354/xxhash-4.0.1-graalpy312-graalpy250_312_native-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deca2a30d983d240b8375ec2ee0a4288e72042827fc61df2f7671f8467e4cb2f", size = 38206, upload-time = "2026-08-17T08:36:32.193Z" }, + { url = "https://files.pythonhosted.org/packages/79/98/1ee576b27f78e6107ee4ea8ac03e8a52888dff256e57d560f8282c195563/xxhash-4.0.1-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:7c343ee174d417a44d0c3355602c0cbbfa52a04d1bbbf1723378c7d2c8f60626", size = 37127, upload-time = "2026-08-17T08:23:42.705Z" }, +] + +[[package]] +name = "yarl" +version = "1.24.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/31/33/ebe9e3d1f86c7a0b51094c0a146392045ca1631d2664889539dec8088a33/yarl-1.24.5.tar.gz", hash = "sha256:e81b83143bee16329c23db3c1b2d82b29892fcbcb849186d2f6e98a5abe9a57f", size = 228679, upload-time = "2026-07-20T02:07:45.435Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/84/71d051c850b5af41d168c679d9eb67eb7c55283ac4ee131673edf134bc4e/yarl-1.24.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d693396e5aea78db03decd60aec9ece16c9b40ba00a587f089615ff4e718a81d", size = 136035, upload-time = "2026-07-20T02:05:25.489Z" }, + { url = "https://files.pythonhosted.org/packages/03/4d/8ad27f9a1b7e69313cca5d695b925b48efe51208d3490e0844bae97cabc0/yarl-1.24.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3363fcc96e665878946ad7a106b9a13eac0541766a690ef287c0232ac768b6ec", size = 97642, upload-time = "2026-07-20T02:05:27.429Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b4/05b4131c407006cd1e410e9c6539f16a0945724677e5364447313c15ea3e/yarl-1.24.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9d399bdcfb4a0f659b9b3788bbc89babe63d9a6a65aacdf4d4e7065ff2e6316c", size = 97323, upload-time = "2026-07-20T02:05:29.441Z" }, + { url = "https://files.pythonhosted.org/packages/20/16/e618c875c73e0e39611f20a581b3d5e8d59b8857bf001bee3263044c6deb/yarl-1.24.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90333fd89b43c0d08ac85f3f1447593fc2c66de18c3d6378d7125ea118dc7a54", size = 107741, upload-time = "2026-07-20T02:05:31.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c4defeaf3ed33fcb346aacf9c6e971a8d4e2bde04a0310e79abb208e7965/yarl-1.24.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:665b0a2c463cc9423dd647e0bfd9f4ccc9b50f768c55304d5e9f80b177c1de12", size = 103570, upload-time = "2026-07-20T02:05:33.303Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e7/0e0e0de5865ebd5914537ef486f36c727a59865c3ac0cf5ff1b32aececbf/yarl-1.24.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e006d3a974c4ee19512e5f058abedb6eef36a5e553c14812bdeba1758d812e6d", size = 115815, upload-time = "2026-07-20T02:05:35.292Z" }, + { url = "https://files.pythonhosted.org/packages/2b/27/ca56b700cb170aba25a3893b75355b213935657dc5714d2383354a270e62/yarl-1.24.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e7d42c531243450ef0d4d9c172e7ed6ef052640f195629065041b5add4e058d1", size = 116025, upload-time = "2026-07-20T02:05:37.503Z" }, + { url = "https://files.pythonhosted.org/packages/d6/d0/d56c859b8222116f5d68459199f48359e0bf121b6f65a69bf329b3602ba0/yarl-1.24.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f08c7513ecef5aad65687bfdf6bc601ae9fccd04a42904501f8f7141abad9eb9", size = 109835, upload-time = "2026-07-20T02:05:39.506Z" }, + { url = "https://files.pythonhosted.org/packages/70/a2/3a35557e4d1a79425040eba202ccaf08bdc8717680fc77e2498a1ad2e0a5/yarl-1.24.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c95b17fe34ed802f17e205112e6e10db92275c34fee290aa9bdc55a9c724027", size = 108884, upload-time = "2026-07-20T02:05:41.584Z" }, + { url = "https://files.pythonhosted.org/packages/e4/35/ef4c26356b7913c68983bac2d72a4212b3347af551cb8d250b99b5ed7b7f/yarl-1.24.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:56b149b22de33b23b0c6077ab9518c6dcb538ad462e1830e68d06591ccf6e38b", size = 107308, upload-time = "2026-07-20T02:05:43.697Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/ff0dc66c2ccf3e0153ab97ff61eabab4400e6a5264af427ab30cd69f1857/yarl-1.24.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a8fe66b8f300da93798025a785a5b90b42f3810dc2b72283ff84a41aaaebc293", size = 103646, upload-time = "2026-07-20T02:05:45.895Z" }, + { url = "https://files.pythonhosted.org/packages/74/f0/33b9271c7f881766359d58266fa0811d2e5210ed860e28da7dc6d7786344/yarl-1.24.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:377fe3732edbaf78ee74efdf2c9f49f6e99f20e7f9d2649fda3eb4badd77d76e", size = 115305, upload-time = "2026-07-20T02:05:47.832Z" }, + { url = "https://files.pythonhosted.org/packages/ef/65/fd79fb1868c4a80db8661091de525bf430f63c3bea1b20e8b6a84fc7d359/yarl-1.24.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e8ffa78582120024f476a611d7befc123cee59e47e8309d470cf667d806e613b", size = 108404, upload-time = "2026-07-20T02:05:49.604Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ba/dbabe6b262f17a816c70cfc09558dbf03ece3ec76684d02f911a3d3a189c/yarl-1.24.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:daba5e594f06114e37db186efd2dd916609071e59daca901a0a2e71f02b142ce", size = 115940, upload-time = "2026-07-20T02:05:51.741Z" }, + { url = "https://files.pythonhosted.org/packages/a5/43/fab2d1dad9d340a268cdde63756a123d069723efff6a372d123fa74a9517/yarl-1.24.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:65be18ec59496c13908f02a2472751d9ef840b4f3fb5726f129306bf6a2a7bba", size = 110006, upload-time = "2026-07-20T02:05:53.554Z" }, + { url = "https://files.pythonhosted.org/packages/c4/27/41eb51bbd1b8d89546b83897cfb0164f1e109304fd408dbb151b639eec0f/yarl-1.24.5-cp312-cp312-win_amd64.whl", hash = "sha256:a929d878fec099030c292803b31e5d5540a7b6a31e6a3cc76cb4685fc2a2f51b", size = 97618, upload-time = "2026-07-20T02:05:55.57Z" }, + { url = "https://files.pythonhosted.org/packages/3c/25/b2553764b3d65db711d8f45416351ec4f420847558eb669edcbcaadf5780/yarl-1.24.5-cp312-cp312-win_arm64.whl", hash = "sha256:7ce27823052e2013b597e0c738b13e7e36b8ccb9400df8959417b052ab0fd92c", size = 93018, upload-time = "2026-07-20T02:05:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/61/02/962c1cbfc401a30c1d034dc67ff395f64b52302c6d62de556c1fca99acc0/yarl-1.24.5-py3-none-any.whl", hash = "sha256:a33700d13d9b7d84fd10947b09ff69fb9a792e519c8cb9764a3ca70baa6c23a7", size = 58612, upload-time = "2026-07-20T02:07:43.461Z" }, +] From 951a51ac839d732f80112e402a387076c8cbc1b3 Mon Sep 17 00:00:00 2001 From: kimura-keiji Date: Mon, 31 Aug 2026 16:18:05 +0900 Subject: [PATCH 15/17] Define v1-4-0 --- CHANGELOG.md | 2 +- onecomp/__version__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d115f455..eff6d91c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change log -## [v1.3.3] 2026-08-dd +## [v1.4.0] 2026-08-dd ### Enhancement diff --git a/onecomp/__version__.py b/onecomp/__version__.py index 022c0c7e..27899122 100644 --- a/onecomp/__version__.py +++ b/onecomp/__version__.py @@ -6,4 +6,4 @@ """ -__version__ = "1.3.3" +__version__ = "1.4.0" From 767cf75af2a81e2f58927168b8c96608628a8756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoshida=2C=20Akihiro/=E5=90=89=E7=94=B0=20=E6=98=8E?= =?UTF-8?q?=E5=BA=83?= Date: Fri, 4 Sep 2026 07:27:24 +0000 Subject: [PATCH 16/17] support router finetuning for MoE --- CHANGELOG.md | 2 + README.md | 1 + docs/api/post_process.md | 6 + docs/user-guide/post-process.md | 54 ++- .../example_router_fine_tuning.py | 76 ++++ onecomp/post_process/__init__.py | 2 + onecomp/post_process/router_fine_tuning.py | 398 ++++++++++++++++++ .../post_process/test_router_fine_tuning.py | 193 +++++++++ 8 files changed, 730 insertions(+), 2 deletions(-) create mode 100644 example/post_process/example_router_fine_tuning.py create mode 100644 onecomp/post_process/router_fine_tuning.py create mode 100644 tests/onecomp/post_process/test_router_fine_tuning.py diff --git a/CHANGELOG.md b/CHANGELOG.md index eff6d91c..efd21891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Enhancement +- Support fine-tuning router after quantizing expert's of MoE, which can be used as the postprocess. see [GEMQ](https://arxiv.org/abs/2605.23078). + - Add device mode synchronization between ModelConfig and QEPConfig. ## [v1.3.2] 2026-08-24 diff --git a/README.md b/README.md index cd58c9de..f398c374 100644 --- a/README.md +++ b/README.md @@ -308,6 +308,7 @@ See [`notebook/README.md`](./notebook/README.md) for local setup, or the | | [example_global_ptq.py](./example/post_process/example_global_ptq.py) | Global PTQ with packed buffers by default and HF-compatible safetensors output | | | [example_global_ptq_dbf.py](./example/post_process/example_global_ptq_dbf.py) | Global PTQ with the DBF backend and HF-compatible safetensors output | | | [example_global_ptq_distributed.py](./example/post_process/example_global_ptq_distributed.py) | Multi-GPU Global PTQ with DeepSpeed / torchrun and safetensors output | +| | [example_router_fine_tuning.py](./example/post_process/example_router_fine_tuning.py) | Router-only next-token fine-tuning for a quantized MoE model | | | [example_lora_sft.py](./example/post_process/example_lora_sft.py) | LoRA SFT post-quantization fine-tuning | | | [example_lora_sft_knowledge.py](./example/post_process/example_lora_sft_knowledge.py) | LoRA SFT knowledge injection | | | [example_lora_sft_knowledge_jointq.py](./example/post_process/example_lora_sft_knowledge_jointq.py) | LoRA SFT knowledge injection on a JointQ-quantized model | diff --git a/docs/api/post_process.md b/docs/api/post_process.md index c3bc4e0d..ebbc3578 100644 --- a/docs/api/post_process.md +++ b/docs/api/post_process.md @@ -24,6 +24,12 @@ Post-quantization process classes for improving quantized model accuracy. options: show_source: false +## Router Fine-Tuning + +::: onecomp.post_process.RouterFineTuning + options: + show_source: false + ## LoRA SFT ::: onecomp.post_process.PostProcessLoraSFT diff --git a/docs/user-guide/post-process.md b/docs/user-guide/post-process.md index 4dca6376..fe3a5825 100644 --- a/docs/user-guide/post-process.md +++ b/docs/user-guide/post-process.md @@ -1,9 +1,10 @@ -# Post-Process (Global PTQ / Block-wise PTQ / LoRA SFT) +# Post-Process (Global PTQ / Block-wise PTQ / Router Fine-Tuning / LoRA SFT) -OneComp supports **post-quantization processing** — additional steps applied to a quantized model to improve accuracy or inject domain-specific knowledge. Three implementations are available: +OneComp supports **post-quantization processing** — additional steps applied to a quantized model to improve accuracy or inject domain-specific knowledge. Four implementations are available: - **Global PTQ** — Globally optimises quantization parameters (scales, zeros, scaling factors) via KL distillation from a full-precision teacher model - **Block-wise PTQ** — Minimises intermediate-representation MSE against an FP16 teacher model at Transformer-block granularity. No training data labelling required. +- **Router Fine-Tuning** — Recovers quantized MoE quality by training only router parameters with next-token prediction loss while experts and all other weights remain frozen. - **LoRA SFT** — Fine-tunes quantized models using Low-Rank Adaptation (LoRA) adapters with SFT loss, optional teacher distillation, and intermediate block alignment. ## Overview @@ -338,6 +339,55 @@ See the [API Reference](../api/post_process.md) for the full parameter list. --- +## Router Fine-Tuning for Quantized MoE Models + +Quantization changes expert outputs even when the router itself remains in full +precision. `RouterFineTuning` adapts routing decisions to those quantized expert +outputs using standard shifted next-token prediction loss. Before training, all +parameters are frozen and only parameters below exact module-name components +`router`, `gate`, and `shared_expert_gate` are enabled. Exact matching means +expert layers such as `gate_proj` remain frozen. + +```python +from onecomp import GPTQ, CalibrationConfig, ModelConfig, RouterFineTuning, Runner + +model_config = ModelConfig(model_id="Qwen/Qwen3-30B-A3B", device="cuda:0") +runner = Runner( + model_config=model_config, + quantizer=GPTQ(wbits=4, groupsize=128), + calibration_config=CalibrationConfig(max_length=512, num_calibration_samples=128), + post_processes=[ + RouterFineTuning( + dataset_name="Salesforce/wikitext", + dataset_config_name="wikitext-2-raw-v1", + max_train_samples=512, + max_length=512, + epochs=1, + batch_size=1, + gradient_accumulation_steps=8, + lr=1e-5, + ) + ], +) +runner.run() +``` + +For architectures with another router name, pass exact path components via +`router_modules=("custom_router",)`. Local `.json`, `.jsonl`, `.csv`, `.txt`, +and `.parquet` files are accepted through `data_files`; set `text_column` when +the text field is not named `text`. + +During training, packed GPTQ layers are temporarily unpacked so gradients can +flow through quantized experts to routing scores. Their incoming packed state is +restored afterward. The process introduces no custom module type, so the result +uses the normal `save_quantized_model()` and `load_quantized_model()` workflow. + +!!! tip + A complete baseline-versus-fine-tuned perplexity example is available at + [`example/post_process/example_router_fine_tuning.py`](https://github.com/FujitsuResearch/OneCompression/blob/main/example/post_process/example_router_fine_tuning.py). + +--- + ## LoRA SFT: Accuracy Recovery The most common use case is recovering accuracy lost during quantization. Provide a general-purpose dataset (e.g., WikiText-2) to fine-tune the quantized model: diff --git a/example/post_process/example_router_fine_tuning.py b/example/post_process/example_router_fine_tuning.py new file mode 100644 index 00000000..c3b899de --- /dev/null +++ b/example/post_process/example_router_fine_tuning.py @@ -0,0 +1,76 @@ +"""Example: MoE quantization + router-only fine-tuning. + +End-to-end demonstration of the RouterFineTuning post-process workflow: + 1. Quantize a mixture-of-experts model with GPTQ + 2. Fine-tune only its routers with next-token prediction loss + 3. Evaluate PPL (original vs quantized + router fine-tuning) + 4. Save the fine-tuned model to HF-compatible safetensors + +Copyright 2025-2026 Fujitsu Ltd. + +Usage: + python example/post_process/example_router_fine_tuning.py +""" + +from onecomp import ( + GPTQ, + CalibrationConfig, + ModelConfig, + RouterFineTuning, + Runner, + setup_logger, +) + + +def main(): + setup_logger() + + save_dir = "./gpt-oss-20b-mixed_gptq_router_ft" + + model_config = ModelConfig( + model_id="openai/gpt-oss-20b", + ) + quantizer = GPTQ(wbits=4, groupsize=128) + print( + f"Quantizer: {type(quantizer).__name__} (wbits={quantizer.wbits}, groupsize={quantizer.groupsize})" + ) + + router_fine_tuning = RouterFineTuning( + dataset_name="Salesforce/wikitext", + dataset_config_name="wikitext-2-raw-v1", + train_split="train", + text_column="text", + max_train_samples=512, + max_length=512, + epochs=1, + batch_size=1, + gradient_accumulation_steps=8, + lr=1e-5, + logging_steps=10, + ) + + runner = Runner( + model_config=model_config, + quantizer=quantizer, + calibration_config=CalibrationConfig( + max_length=512, + num_calibration_samples=128, + ), + moe_quant_experts=True, + post_processes=[router_fine_tuning], + ) + runner.run() + + original_ppl, _, fine_tuned_ppl = runner.calculate_perplexity( + original_model=True, + quantized_model=True, + ) + print(f"\nOriginal MoE PPL: {original_ppl:.4f}") + print(f"Quantized MoE + router FT PPL: {fine_tuned_ppl:.4f}") + + runner.save_quantized_model(save_dir) + print(f"\nModel saved (safetensors) to {save_dir}") + + +if __name__ == "__main__": + main() diff --git a/onecomp/post_process/__init__.py b/onecomp/post_process/__init__.py index 143f2f53..7751364e 100644 --- a/onecomp/post_process/__init__.py +++ b/onecomp/post_process/__init__.py @@ -16,6 +16,7 @@ PostProcessLoraTeacherOnlySFT, PostProcessLoraTeacherSFT, ) +from .router_fine_tuning import RouterFineTuning __all__ = [ "PostQuantizationProcess", @@ -25,4 +26,5 @@ "PostProcessLoraSFT", "PostProcessLoraTeacherOnlySFT", "PostProcessLoraTeacherSFT", + "RouterFineTuning", ] diff --git a/onecomp/post_process/router_fine_tuning.py b/onecomp/post_process/router_fine_tuning.py new file mode 100644 index 00000000..4f5b79f6 --- /dev/null +++ b/onecomp/post_process/router_fine_tuning.py @@ -0,0 +1,398 @@ +"""Router-only fine-tuning for quantized mixture-of-experts models. + +Copyright 2025-2026 Fujitsu Ltd. + +""" + +from __future__ import annotations + +import math +from contextlib import nullcontext +from dataclasses import dataclass +from logging import getLogger +from typing import Any + +import torch +import torch.nn as nn +import torch.nn.functional as F +from datasets import Dataset, DatasetDict, load_dataset +from torch.utils.data import DataLoader + +from ..model_config import ModelConfig +from ._base import PostQuantizationProcess +from .post_process_lora_sft import ( + _capture_gptq_pack_state, + _infer_dataset_loader, + _restore_gptq_pack_state, + _unpack_gptq_linears_in_place, +) + +logger = getLogger(__name__) + +_DEFAULT_ROUTER_MODULES = ("router", "gate", "shared_expert_gate") + + +def _extract_logits(outputs: Any) -> torch.Tensor: + """Extract logits from common causal-LM output formats.""" + if hasattr(outputs, "logits"): + return outputs.logits + if isinstance(outputs, dict) and "logits" in outputs: + return outputs["logits"] + if isinstance(outputs, (tuple, list)) and outputs: + return outputs[0] + raise TypeError("The model forward result does not contain `logits`.") + + +def _compute_next_token_loss( + logits: torch.Tensor, + labels: torch.Tensor, +) -> torch.Tensor: + """Compute shifted causal-LM cross entropy, ignoring padded labels.""" + shift_logits = logits[..., :-1, :].contiguous().float() + shift_labels = labels[..., 1:].contiguous() + valid_targets = shift_labels.ne(-100).sum() + loss_sum = F.cross_entropy( + shift_logits.view(-1, shift_logits.size(-1)), + shift_labels.view(-1), + ignore_index=-100, + reduction="sum", + ) + return loss_sum / valid_targets.clamp_min(1) + + +@dataclass +class RouterFineTuning(PostQuantizationProcess): + """Fine-tune only MoE routers after quantization. + + All model parameters are frozen before modules named ``router``, ``gate``, + or ``shared_expert_gate`` are made trainable. Exact path-component matching + avoids selecting expert projections such as ``gate_proj``. Training uses + standard shifted next-token prediction loss and modifies the model in + place. + + Args: + dataset_name: Hugging Face dataset identifier. + dataset_config_name: Optional Hugging Face dataset configuration. + data_files: Local JSON/JSONL/CSV/TXT/Parquet training files. + train_split: Dataset split used for training. + text_column: Column containing training text. + router_modules: Exact module-name components considered routers. + max_train_samples: Optional maximum number of training examples. + max_length: Tokenized sequence length. + lr: AdamW learning rate. + epochs: Number of training epochs. + batch_size: Per-step batch size. + gradient_accumulation_steps: Number of backward passes per update. + weight_decay: AdamW weight decay. + warmup_ratio: Fraction of optimizer updates used for linear warmup. + max_grad_norm: Gradient clipping norm. Set to ``None`` to disable. + use_bf16: Use bfloat16 autocast on CUDA. Auto-detected when ``None``. + + Examples: + >>> from onecomp import GPTQ, ModelConfig, RouterFineTuning, Runner + >>> runner = Runner( + ... model_config=ModelConfig(model_id="Qwen/Qwen3-30B-A3B"), + ... quantizer=GPTQ(wbits=4, groupsize=128), + ... post_processes=[ + ... RouterFineTuning(data_files="train.jsonl", epochs=1) + ... ], + ... ) + >>> runner.run() + """ + + dataset_name: str | None = None + dataset_config_name: str | None = None + data_files: str | list[str] | dict[str, str] | None = None + train_split: str = "train" + text_column: str = "text" + router_modules: tuple[str, ...] = _DEFAULT_ROUTER_MODULES + max_train_samples: int | None = None + max_length: int = 1024 + shuffle_seed: int = 42 + + lr: float = 1e-5 + epochs: int = 1 + batch_size: int = 1 + gradient_accumulation_steps: int = 1 + weight_decay: float = 0.0 + warmup_ratio: float = 0.0 + max_grad_norm: float | None = 1.0 + logging_steps: int = 10 + use_bf16: bool | None = None + + def _validate_config(self) -> None: + if self.dataset_name is None and self.data_files is None: + raise ValueError("Either `dataset_name` or `data_files` must be specified.") + if not self.router_modules: + raise ValueError("`router_modules` must contain at least one module name.") + for field_name in ("epochs", "batch_size", "gradient_accumulation_steps", "max_length"): + value = getattr(self, field_name) + if value <= 0: + raise ValueError(f"`{field_name}` must be > 0, but got {value}.") + if self.lr <= 0.0: + raise ValueError(f"`lr` must be > 0, but got {self.lr}.") + if not 0.0 <= self.warmup_ratio <= 1.0: + raise ValueError(f"`warmup_ratio` must be in [0, 1], but got {self.warmup_ratio}.") + if self.max_grad_norm is not None and self.max_grad_norm <= 0.0: + raise ValueError(f"`max_grad_norm` must be > 0 or None, but got {self.max_grad_norm}.") + + def _resolve_train_device(self, model_config: ModelConfig) -> torch.device: + requested = model_config.device if model_config.device not in (None, "auto") else None + if requested is None: + requested = "cuda" if torch.cuda.is_available() else "cpu" + if str(requested).startswith("cuda") and not torch.cuda.is_available(): + logger.warning("CUDA is unavailable; falling back to CPU for router fine-tuning.") + return torch.device("cpu") + return torch.device(requested) + + def _load_train_dataset(self) -> Dataset: + if self.dataset_name is not None: + dataset_or_dict = load_dataset( + path=self.dataset_name, + name=self.dataset_config_name, + data_files=self.data_files, + ) + else: + dataset_or_dict = load_dataset( + _infer_dataset_loader(self.data_files), + data_files=self.data_files, + ) + + if isinstance(dataset_or_dict, DatasetDict): + if self.train_split not in dataset_or_dict: + raise ValueError( + f"`train_split`={self.train_split!r} was not found. " + f"Available splits: {sorted(dataset_or_dict.keys())}." + ) + dataset = dataset_or_dict[self.train_split] + else: + dataset = dataset_or_dict + + if self.text_column not in dataset.column_names: + raise ValueError( + f"`text_column`={self.text_column!r} was not found in " + f"dataset columns {dataset.column_names}." + ) + if self.max_train_samples is not None: + if self.max_train_samples <= 0: + raise ValueError( + "`max_train_samples` must be > 0, " f"but got {self.max_train_samples}." + ) + dataset = dataset.shuffle(seed=self.shuffle_seed).select( + range(min(self.max_train_samples, len(dataset))) + ) + else: + dataset = dataset.shuffle(seed=self.shuffle_seed) + if len(dataset) == 0: + raise ValueError("Training dataset is empty.") + return dataset + + def _tokenize_dataset(self, dataset: Dataset, tokenizer) -> Dataset: + if tokenizer.pad_token is None: + if tokenizer.eos_token is None: + raise ValueError("Tokenizer has neither pad_token nor eos_token.") + tokenizer.pad_token = tokenizer.eos_token + + def tokenize_batch(batch: dict[str, Any]) -> dict[str, Any]: + texts = [ + text if isinstance(text, str) else str(text) for text in batch[self.text_column] + ] + tokenized = tokenizer( + texts, + max_length=self.max_length, + truncation=True, + padding="max_length", + return_attention_mask=True, + ) + tokenized["labels"] = [ + [token_id if mask else -100 for token_id, mask in zip(input_ids, attention_mask)] + for input_ids, attention_mask in zip( + tokenized["input_ids"], tokenized["attention_mask"] + ) + ] + return tokenized + + tokenized = dataset.map( + tokenize_batch, + batched=True, + remove_columns=list(dataset.column_names), + desc="Tokenizing router fine-tuning dataset", + ) + tokenized.set_format(type="torch", columns=["input_ids", "attention_mask", "labels"]) + return tokenized + + @staticmethod + def _collate_batch(batch: list[dict[str, torch.Tensor]]) -> dict[str, torch.Tensor]: + return { + key: torch.stack([item[key] for item in batch], dim=0) + for key in ("input_ids", "attention_mask", "labels") + } + + def _select_router_parameters(self, model: nn.Module) -> list[tuple[str, nn.Parameter]]: + targets = set(self.router_modules) + for parameter in model.parameters(): + parameter.requires_grad_(False) + + selected = [] + for name, parameter in model.named_parameters(): + if any(component in targets for component in name.split(".")): + parameter.requires_grad_(True) + selected.append((name, parameter)) + + if not selected: + raise ValueError( + "No MoE router parameters matched " f"router_modules={self.router_modules!r}." + ) + return selected + + def _run(self, quantized_model: nn.Module, model_config: ModelConfig) -> dict: + self._validate_config() + tokenizer = model_config.load_tokenizer() + train_dataset = self._tokenize_dataset(self._load_train_dataset(), tokenizer) + train_loader = DataLoader( + train_dataset, + batch_size=self.batch_size, + shuffle=False, + collate_fn=self._collate_batch, + ) + trainable = self._select_router_parameters(quantized_model) + trainable_parameters = [parameter for _, parameter in trainable] + train_device = self._resolve_train_device(model_config) + use_bf16 = self.use_bf16 + if use_bf16 is None: + use_bf16 = bool(train_device.type == "cuda" and torch.cuda.is_bf16_supported()) + + total_updates = max( + 1, + math.ceil(len(train_loader) * self.epochs / self.gradient_accumulation_steps), + ) + warmup_steps = int(total_updates * self.warmup_ratio) + optimizer = torch.optim.AdamW( + trainable_parameters, + lr=self.lr, + weight_decay=self.weight_decay, + ) + + def lr_lambda(step: int) -> float: + if warmup_steps > 0 and step < warmup_steps: + return float(step + 1) / float(warmup_steps) + return max( + 0.0, + float(total_updates - step) / float(max(1, total_updates - warmup_steps)), + ) + + scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda) + pack_state = None + original_use_cache = None + optimizer_step = 0 + accumulation_step = 0 + skipped_batches = 0 + try: + pack_state = _capture_gptq_pack_state(quantized_model) + unpacked_count = _unpack_gptq_linears_in_place(quantized_model) + if hasattr(quantized_model, "config") and hasattr(quantized_model.config, "use_cache"): + original_use_cache = quantized_model.config.use_cache + quantized_model.config.use_cache = False + + logger.info( + "RouterFineTuning started: parameters=%d, samples=%d, epochs=%d, " + "device=%s, unpacked_layers=%d", + len(trainable), + len(train_dataset), + self.epochs, + train_device, + unpacked_count, + ) + quantized_model.to(train_device) + optimizer.zero_grad(set_to_none=True) + quantized_model.train() + autocast_context = ( + torch.autocast(device_type="cuda", dtype=torch.bfloat16) + if train_device.type == "cuda" and use_bf16 + else nullcontext() + ) + for epoch in range(self.epochs): + for batch_index, batch in enumerate(train_loader): + batch = {key: value.to(train_device) for key, value in batch.items()} + if not batch["labels"][:, 1:].ne(-100).any(): + skipped_batches += 1 + continue + with autocast_context: + outputs = quantized_model( + input_ids=batch["input_ids"], + attention_mask=batch["attention_mask"], + ) + loss = _compute_next_token_loss(_extract_logits(outputs), batch["labels"]) + if not torch.isfinite(loss): + raise FloatingPointError( + "Router fine-tuning produced a non-finite loss at " + f"epoch={epoch + 1}, batch={batch_index + 1}." + ) + (loss / self.gradient_accumulation_steps).backward() + accumulation_step += 1 + + is_update = accumulation_step % self.gradient_accumulation_steps == 0 + if is_update: + if not all( + parameter.grad is None or torch.isfinite(parameter.grad).all() + for parameter in trainable_parameters + ): + raise FloatingPointError( + "Router fine-tuning produced non-finite gradients at " + f"epoch={epoch + 1}, batch={batch_index + 1}." + ) + if self.max_grad_norm is not None: + torch.nn.utils.clip_grad_norm_( + trainable_parameters, + self.max_grad_norm, + ) + optimizer.step() + scheduler.step() + optimizer.zero_grad(set_to_none=True) + optimizer_step += 1 + if self.logging_steps > 0 and optimizer_step % self.logging_steps == 0: + logger.info( + "RouterFineTuning epoch=%d step=%d loss=%.6f", + epoch + 1, + optimizer_step, + loss.item(), + ) + if accumulation_step % self.gradient_accumulation_steps != 0: + if not all( + parameter.grad is None or torch.isfinite(parameter.grad).all() + for parameter in trainable_parameters + ): + raise FloatingPointError("Router fine-tuning produced non-finite gradients.") + if self.max_grad_norm is not None: + torch.nn.utils.clip_grad_norm_(trainable_parameters, self.max_grad_norm) + optimizer.step() + scheduler.step() + optimizer.zero_grad(set_to_none=True) + optimizer_step += 1 + finally: + if original_use_cache is not None: + quantized_model.config.use_cache = original_use_cache + try: + quantized_model.to("cpu") + finally: + if pack_state is not None: + _restore_gptq_pack_state(quantized_model, pack_state) + + if skipped_batches: + logger.info( + "RouterFineTuning skipped %d batch(es) without next-token targets.", + skipped_batches, + ) + + if optimizer_step == 0: + return { + "executed": False, + "reason": "no_valid_next_token_targets", + "optimizer_steps": 0, + } + + return { + "executed": True, + "trainable_parameters": [name for name, _ in trainable], + "optimizer_steps": optimizer_step, + } diff --git a/tests/onecomp/post_process/test_router_fine_tuning.py b/tests/onecomp/post_process/test_router_fine_tuning.py new file mode 100644 index 00000000..fa4377b1 --- /dev/null +++ b/tests/onecomp/post_process/test_router_fine_tuning.py @@ -0,0 +1,193 @@ +"""Tests for router-only post-quantization fine-tuning. + +Copyright 2025-2026 Fujitsu Ltd. + +""" + +import importlib +from types import SimpleNamespace + +import pytest +import torch +import torch.nn as nn +import torch.nn.functional as F +from datasets import Dataset + +from onecomp.post_process.router_fine_tuning import ( + RouterFineTuning, + _compute_next_token_loss, +) + + +class _Tokenizer: + pad_token = "" + eos_token = "" + + def __call__(self, texts, max_length, **_kwargs): + input_ids = [] + attention_masks = [] + for index, _text in enumerate(texts): + tokens = [] if not _text else [1, 2 + index % 2, 3, 4][:max_length] + padding = max_length - len(tokens) + input_ids.append(tokens + [0] * padding) + attention_masks.append([1] * len(tokens) + [0] * padding) + return {"input_ids": input_ids, "attention_mask": attention_masks} + + +class _TinyMoE(nn.Module): + def __init__(self): + super().__init__() + torch.manual_seed(7) + self.embed_tokens = nn.Embedding(8, 6) + self.router = nn.Linear(6, 2, bias=False) + self.experts = nn.ModuleList([nn.Linear(6, 6), nn.Linear(6, 6)]) + self.gate_proj = nn.Linear(6, 6, bias=False) + self.lm_head = nn.Linear(6, 8, bias=False) + self.config = SimpleNamespace( + use_cache=True, + quantization_config={ + "quant_method": "gptq", + "modules_in_block_to_quantize": [], + }, + ) + + def forward(self, input_ids, attention_mask=None): # noqa: ARG002 + hidden = self.embed_tokens(input_ids) + router_weights = self.router(hidden).softmax(dim=-1) + expert_outputs = torch.stack([expert(hidden) for expert in self.experts], dim=-2) + hidden = (expert_outputs * router_weights.unsqueeze(-1)).sum(dim=-2) + hidden = hidden + 0.1 * self.gate_proj(hidden) + return SimpleNamespace(logits=self.lm_head(F.silu(hidden))) + + +def _model_config(): + return SimpleNamespace(device="cpu", load_tokenizer=lambda: _Tokenizer()) + + +def test_next_token_loss_shifts_labels_and_ignores_padding(): + logits = torch.tensor( + [[[8.0, 0.0], [0.0, 8.0], [8.0, 0.0], [0.0, 8.0]]], + requires_grad=True, + ) + labels = torch.tensor([[0, 0, 1, -100]]) + + loss = _compute_next_token_loss(logits, labels) + expected = F.cross_entropy(logits[:, :2].reshape(-1, 2), labels[:, 1:3].reshape(-1)) + + torch.testing.assert_close(loss, expected) + loss.backward() + assert logits.grad is not None + + +def test_next_token_loss_is_finite_without_valid_targets(): + logits = torch.randn(1, 4, 8, requires_grad=True) + labels = torch.full((1, 4), -100) + + loss = _compute_next_token_loss(logits, labels) + + assert torch.isfinite(loss) + assert loss.item() == 0.0 + loss.backward() + assert torch.isfinite(logits.grad).all() + + +def test_router_selection_uses_exact_path_components(): + model = _TinyMoE() + process = RouterFineTuning(data_files="unused.jsonl") + + selected = {name for name, _ in process._select_router_parameters(model)} + + assert selected == {"router.weight"} + assert model.router.weight.requires_grad + assert not model.gate_proj.weight.requires_grad + assert all( + not parameter.requires_grad + for expert in model.experts + for parameter in expert.parameters() + ) + + +def test_run_updates_only_router_and_records_metadata(monkeypatch): + model = _TinyMoE() + process = RouterFineTuning( + data_files="unused.jsonl", + max_length=4, + epochs=2, + batch_size=2, + lr=0.1, + logging_steps=0, + ) + dataset = Dataset.from_dict({"text": ["a", "", "b", ""]}) + monkeypatch.setattr(process, "_load_train_dataset", lambda: dataset) + before = {name: parameter.detach().clone() for name, parameter in model.named_parameters()} + + process.run(model, _model_config()) + + after = dict(model.named_parameters()) + assert not torch.equal(before["router.weight"], after["router.weight"]) + for name, old_parameter in before.items(): + if name != "router.weight": + torch.testing.assert_close(after[name], old_parameter, rtol=0.0, atol=0.0) + + assert not model.training + assert model.config.use_cache is True + assert {parameter.device.type for parameter in model.parameters()} == {"cpu"} + metadata = model.config.quantization_config["onecomp_post_processes"][-1] + assert metadata["class"] == "RouterFineTuning" + assert metadata["executed"] is True + + +def test_run_marks_all_skipped_batches_as_not_executed(monkeypatch): + model = _TinyMoE() + process = RouterFineTuning( + data_files="unused.jsonl", + max_length=4, + batch_size=2, + logging_steps=0, + ) + dataset = Dataset.from_dict({"text": ["", ""]}) + monkeypatch.setattr(process, "_load_train_dataset", lambda: dataset) + before = {name: parameter.detach().clone() for name, parameter in model.named_parameters()} + + process.run(model, _model_config()) + + for name, old_parameter in before.items(): + torch.testing.assert_close(model.get_parameter(name), old_parameter, rtol=0.0, atol=0.0) + metadata = model.config.quantization_config["onecomp_post_processes"][-1] + assert metadata["executed"] is False + assert metadata["reason"] == "no_valid_next_token_targets" + + +def test_run_restores_state_when_device_move_fails(monkeypatch): + router_module = importlib.import_module("onecomp.post_process.router_fine_tuning") + model = _TinyMoE() + process = RouterFineTuning(data_files="unused.jsonl", max_length=4, logging_steps=0) + dataset = Dataset.from_dict({"text": ["valid"]}) + monkeypatch.setattr(process, "_load_train_dataset", lambda: dataset) + monkeypatch.setattr(process, "_resolve_train_device", lambda _config: torch.device("meta")) + monkeypatch.setattr( + router_module, + "_capture_gptq_pack_state", + lambda _model: {"router": True}, + ) + monkeypatch.setattr(router_module, "_unpack_gptq_linears_in_place", lambda _model: 1) + restored_states = [] + monkeypatch.setattr( + router_module, + "_restore_gptq_pack_state", + lambda _model, state: restored_states.append(state), + ) + original_to = model.to + + def fail_on_meta(device): + if torch.device(device).type == "meta": + raise RuntimeError("simulated device allocation failure") + return original_to(device) + + monkeypatch.setattr(model, "to", fail_on_meta) + + with pytest.raises(RuntimeError, match="simulated device allocation failure"): + process.run(model, _model_config()) + + assert model.config.use_cache is True + assert restored_states == [{"router": True}] From eeda04ba306c233ff107d49f87103fdc928fd4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kimura=2C=20Keiji/=E6=9C=A8=E6=9D=91=20=E5=9C=AD=E5=85=90?= Date: Tue, 8 Sep 2026 15:52:04 +0000 Subject: [PATCH 17/17] Remove Runner multi-GPU quantization --- CHANGELOG.md | 5 + README.md | 6 +- docs/getting-started/installation.md | 4 +- docs/getting-started/quickstart.md | 2 +- docs/user-guide/configuration.md | 22 +- docs/user-guide/examples.md | 22 - docs/user-guide/mps.md | 2 - onecomp/runner.py | 109 +---- .../runner_methods/multi_gpu_quantization.py | 406 ------------------ onecomp/utils/quantization_progress.py | 15 +- 10 files changed, 37 insertions(+), 556 deletions(-) delete mode 100644 onecomp/runner_methods/multi_gpu_quantization.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a67477d..db386e34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## [v1.4.0] 2026-08-dd +### Breaking Changes + +- Remove Runner's layer-wise multi-GPU quantization feature. The `multi_gpu` and + `gpu_ids` options and the associated implementation have been removed. + ### Enhancement - Support fine-tuning router after quantizing expert's of MoE, which can be used as the postprocess. see [GEMQ](https://arxiv.org/abs/2605.23078). diff --git a/README.md b/README.md index f398c374..84a7edb5 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ Then install OneComp from PyPI (see step 2 below). GPTQ quantization and Hugging > - GPTQ (`run_gptq`): Hessian and weights are moved to CPU for the full column-wise loop (including inverse-Hessian Cholesky). If that loop stayed on MPS, `quantize()` would call `maxq.item()` once per column; each call triggers **per-column host sync** (wait for pending MPS ops, then read one scalar—not a full Hessian/weight copy every column)—often several times slower than CPU on Apple Silicon (e.g. ~4× in internal benchmarks with PyTorch 2.12). Keeping GPTQ on CPU avoids that overhead. With `mse=True`, `find_params` also calls `quantize()` in a grid loop and benefits from the same CPU placement. > - QEP weight correction (`adjust_weight`, when QEP correction runs—typically `qep=True` with error propagation enabled): Per-layer work stays on MPS (e.g. `weight @ delta_hatX`, diagonal damping). Only the Cholesky solve uses CPU via `_safe_cholesky_and_solve` (one solve per layer, not per column); moving all of QEP to CPU does not materially improve speed. The subsequent GPTQ step still uses the CPU path above. > -> DBF-based AutoBit fallback and multi-GPU quantization are not supported on MPS. +> DBF-based AutoBit fallback is not supported on MPS. #### 2. Install `onecomp` @@ -136,7 +136,7 @@ Once PyTorch is installed, you can install `onecomp`: pip install onecomp ``` -To enable multi-GPU training features (DeepSpeed), install with the `distributed` extra: +To enable multi-GPU training for Global PTQ (DeepSpeed), install with the `distributed` extra: ```bash pip install "onecomp[distributed]" @@ -182,7 +182,7 @@ See the **MPS device placement (GPTQ vs QEP)** note under [macOS (MPS)](#macos-m Adding `--extra dev` installs development tools (black, pre-commit, pytest, pylint). Adding `--extra visualize` installs matplotlib for visualization features. -Adding `--extra distributed` installs DeepSpeed for multi-GPU training. +Adding `--extra distributed` installs DeepSpeed for Global PTQ multi-GPU training. Adding `--extra hydra` installs `hydra-core` for the example scripts and `model_validation/` runners that use Hydra-based configuration. To use vLLM for serving quantized models on Linux, add `--extra vllm` together with `--extra cu130`: diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 5807bc79..1049d23a 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -107,7 +107,7 @@ To enable visualization features (matplotlib), install with the `visualize` extr pip install onecomp[visualize] ``` -To enable multi-GPU training features (DeepSpeed), install with the `distributed` extra: +To enable multi-GPU training for Global PTQ (DeepSpeed), install with the `distributed` extra: ```bash pip install "onecomp[distributed]" @@ -152,7 +152,7 @@ See the [macOS / MPS guide](../user-guide/mps.md) for device placement and usage Adding `--extra dev` installs development tools (black, pytest, pylint). Adding `--extra visualize` installs matplotlib for visualization features. -Adding `--extra distributed` installs DeepSpeed for multi-GPU training. +Adding `--extra distributed` installs DeepSpeed for Global PTQ multi-GPU training. To use vLLM for serving quantized models on Linux, add `--extra vllm` together with `--extra cu130`: diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index 83bf2520..e6074ef9 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -190,7 +190,7 @@ model, tokenizer = load_quantized_model("./output/quantized_model") - [CLI Reference](../user-guide/cli.md) -- full CLI options and usage - [Configuration](../user-guide/configuration.md) -- detailed explanation of `ModelConfig`, `QEPConfig`, `LPCDConfig`, and `Runner` parameters -- [Examples](../user-guide/examples.md) -- more usage patterns including multi-GPU and chunked calibration +- [Examples](../user-guide/examples.md) -- more usage patterns including chunked calibration - [Evaluation](../user-guide/evaluation.md) -- `onecomp-eval` for MT-Bench and throughput on vLLM-served models - [Algorithms](../algorithms/overview.md) -- learn about the quantization algorithms available in OneComp - [macOS / MPS](../user-guide/mps.md) -- Apple Silicon setup, limitations, and inference diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md index b960b4ee..f95bd704 100644 --- a/docs/user-guide/configuration.md +++ b/docs/user-guide/configuration.md @@ -28,7 +28,7 @@ model_config = ModelConfig( !!! note "macOS (MPS)" On Apple Silicon, set `device="mps"` for GPTQ / AutoBit (GPTQ-only) quantization. - Only GPTQ quantizers are supported on MPS; DBF fallback and multi-GPU are not. + Only GPTQ quantizers are supported on MPS; DBF fallback is not. See the [macOS / MPS guide](mps.md) for details. ## Runner @@ -65,13 +65,6 @@ runner = Runner( | `lpcd` | `bool` | Enable LPCD | `False` | | `lpcd_config` | `LPCDConfig` | LPCD configuration | `None` | -### Advanced Parameters - -| Parameter | Type | Description | Default | -|---------------|-------------|--------------------------------------------------|----------| -| `multi_gpu` | `bool` | Enable multi-GPU layer-wise parallel quantization| `False` | -| `gpu_ids` | `list[int]` | Specific GPU IDs to use | `None` | - !!! note When `calibration_config` is `None`, a `CalibrationConfig()` with default values is created automatically. @@ -124,13 +117,12 @@ calib_config = CalibrationConfig( ### Valid Parameter Combinations -| `quantizers` | `qep` | `multi_gpu` | `calibration_config.batch_size` | -|:------------:|:------:|:-----------:|:-------------------------------:| -| Specified | False | False | Specified | -| None | True | False | None | -| None | False | True | None | -| None | False | False | Specified | -| None | False | False | None | +| `quantizers` | `qep` | `calibration_config.batch_size` | +|:------------:|:------:|:-------------------------------:| +| Specified | False | Specified | +| None | True | None | +| None | False | Specified | +| None | False | None | ## QEPConfig diff --git a/docs/user-guide/examples.md b/docs/user-guide/examples.md index cc7aee89..f83e365b 100644 --- a/docs/user-guide/examples.md +++ b/docs/user-guide/examples.md @@ -220,28 +220,6 @@ runner.run() !!! info Chunked calibration is mathematically exact -- it accumulates \(X^T X\) across batches without approximation. -## Multi-GPU Quantization - -Distribute layer-wise quantization across multiple GPUs: - -```python -runner = Runner( - model_config=model_config, - quantizer=gptq, - multi_gpu=True, -) -runner.run() - -# Or specify particular GPUs -runner = Runner( - model_config=model_config, - quantizer=gptq, - multi_gpu=True, - gpu_ids=[0, 2, 3], -) -runner.run() -``` - ## Comparing Multiple Quantizers Run multiple quantizers in a single session with shared calibration data: diff --git a/docs/user-guide/mps.md b/docs/user-guide/mps.md index 2282e5c5..a254e2ad 100644 --- a/docs/user-guide/mps.md +++ b/docs/user-guide/mps.md @@ -22,7 +22,6 @@ quantization steps intentionally run on CPU for performance. See | vLLM / GemLite serving | No | Linux + CUDA only | | DBF, RTN, JointQ, and other quantizers | No | — | | AutoBit DBF fallback | No | — | -| Multi-GPU quantization | No | — | ## Installation @@ -126,7 +125,6 @@ with an NVIDIA GPU. See the [vLLM Inference guide](vllm-inference.md). - Only **GPTQ** quantizers are allowed (or **AutoBitQuantizer** whose candidates are all GPTQ). - **AutoBit DBF fallback** is rejected when the target bitwidth would require DBF-only assignment. -- **`multi_gpu=True`** is not supported. To avoid DBF fallback on MPS, either set an explicit `wbits` within the GPTQ candidate range or ensure VRAM estimation yields a bitwidth that does not trigger DBF-only paths. diff --git a/onecomp/runner.py b/onecomp/runner.py index 60767146..2dcd6e38 100644 --- a/onecomp/runner.py +++ b/onecomp/runner.py @@ -61,7 +61,7 @@ class Runner: """Runner class for model quantization Runner class for executing quantization. - Supports quantization using calibration data and parallel quantization on multiple GPUs. + Supports quantization using calibration data. Examples: Single GPU quantization (default): @@ -76,27 +76,6 @@ class Runner: ... ) >>> runner.run() - Multi-GPU quantization (layer-wise parallel): - - >>> from onecomp.quantizer.jointq import JointQ - >>> quantizer = JointQ(bits=4, group_size=128) - >>> # Use all available GPUs - >>> runner = Runner( - ... model_config=model_config, - ... quantizer=quantizer, - ... multi_gpu=True, - ... ) - >>> runner.run() - - >>> # Use specific GPUs (e.g., GPU 0, 2, 3) - >>> runner = Runner( - ... model_config=model_config, - ... quantizer=quantizer, - ... multi_gpu=True, - ... gpu_ids=[0, 2, 3], - ... ) - >>> runner.run() - """ def __init__( @@ -109,8 +88,6 @@ def __init__( qep_config=None, lpcd=False, lpcd_config=None, - multi_gpu=False, - gpu_ids=None, post_processes=None, report_progress=True, moe_quant_experts=False, @@ -149,12 +126,6 @@ def __init__( lpcd_config (LPCDConfig or None): Configuration for LPCD. If None and ``lpcd=True``, a default ``LPCDConfig()`` is used. - multi_gpu (bool): - Whether to use multi-GPU for layer-wise parallel quantization. - Default is False. - gpu_ids (list[int]): - List of GPU IDs to use for multi-GPU quantization. - If None and multi_gpu is True, all available GPUs will be used. post_processes (list[PostQuantizationProcess] or None): Optional list of post-quantization processes to execute after the main quantization step. Each process receives @@ -167,8 +138,8 @@ def __init__( report_progress (bool): When ``True`` (default), emit ``[progress]`` log lines with completed steps, elapsed time, and a linear ETA estimate - during long quantization (calibration, chunked, multi-GPU, - QEP). Set to ``False`` for quiet runs (e.g. CI). + during long quantization (calibration, chunked, QEP). Set to + ``False`` for quiet runs (e.g. CI). moe_quant_experts (bool): When ``True``, MoE experts are kept as per-expert GPTQ INT4 tensors (``...experts.{i}.{gate,up,down}_proj.{qweight,...}``) @@ -253,8 +224,6 @@ def __init__( self.calibration_config = calibration_config self.qep = qep - self.multi_gpu = multi_gpu - self.gpu_ids = gpu_ids self.post_processes = post_processes or [] self.moe_quant_experts = moe_quant_experts self.quantized_model = None @@ -280,23 +249,19 @@ def check(self): 3. Type check for ``quantizer`` / ``quantizers`` (must be ``Quantizer`` instances) 4. At least one of them must be specified 5. Parameter combination consistency check (see table below) - 6. When ``multi_gpu=True``, ``quantizer.flag_calibration=True`` must hold Valid parameter combinations: - =========== ==== ========== ================================ - quantizers qep multi_gpu calibration_config.batch_size - =========== ==== ========== ================================ - Specified False False Specified - None True False None - None False True None - None False False Specified - None False False None - =========== ==== ========== ================================ + =========== ==== ================================ + quantizers qep calibration_config.batch_size + =========== ==== ================================ + Specified False Specified + None True None + None False Specified + None False None + =========== ==== ================================ Note: - ``multi_gpu=True`` requires a quantizer with ``flag_calibration=True``. - This method is intended to be called from the ``run()`` flow only. It is *not* designed to be used in the ``load_quantized_model() -> Runner.run_post_processes()`` flow, and @@ -329,27 +294,17 @@ def check(self): # Parameter combination check batch_size = self.calibration_config.batch_size if self.quantizers is not None: - # quantizers mode: qep=False, multi_gpu=False, batch_size required + # quantizers mode: qep=False, batch_size required if self.qep: raise ValueError("'quantizers' cannot be used with qep=True.") - if self.multi_gpu: - raise ValueError("'quantizers' cannot be used with multi_gpu=True.") if batch_size is None: raise ValueError( "'quantizers' requires 'calibration_config.batch_size' to be set." ) else: # Single quantizer mode: combination check - if self.qep and self.multi_gpu: - raise ValueError("'qep' and 'multi_gpu' cannot be used together.") if self.qep and batch_size is not None: raise ValueError("'qep' cannot be used with 'calibration_config.batch_size'.") - if self.multi_gpu and batch_size is not None: - raise ValueError( - "'multi_gpu' cannot be used with 'calibration_config.batch_size'." - ) - if self.multi_gpu and not self.quantizer.flag_calibration: - raise ValueError("'multi_gpu' requires a quantizer with flag_calibration=True.") if self.qep and not self.quantizer.flag_qep_supported: raise ValueError( f"Quantizer '{type(self.quantizer).__name__}' " @@ -377,8 +332,6 @@ def check(self): # candidates are all GPTQ, without DBF fallback) is supported on MPS device = self.model_config.get_device() if is_mps_device(device): - if self.multi_gpu: - raise ValueError("multi_gpu is not supported on MPS device.") all_quantizers = self.quantizers if self.quantizers is not None else [self.quantizer] for i, q in enumerate(all_quantizers): label = f"quantizers[{i}]" if self.quantizers else "quantizer" @@ -658,9 +611,6 @@ def quantize(self): if self.quantizers is not None: # Multiple quantizers mode (chunked quantization) self.quantize_with_calibration_chunked() - elif self.multi_gpu: - # Multi-GPU quantization (flag_calibration=True is guaranteed by check()) - self.quantize_with_calibration_on_multi_gpu() elif self.calibration_config.batch_size is not None: # Chunked quantization (single quantizer) self.quantize_with_calibration_chunked() @@ -747,41 +697,6 @@ def quantize_with_calibration_chunked(self): report_progress=self.report_progress, ) - def quantize_with_calibration_on_multi_gpu(self): - """Quantize the model with calibration using multiple GPUs - - Quantizes each linear layer in parallel across multiple GPUs. - - Processing flow: - 1. Load the model and prepare calibration data - 2. Capture input activations for all layers and save to CPU - 3. Distribute layers to each GPU and execute quantization in parallel - 4. Aggregate results - - Note: - - Called from quantize() when multi_gpu=True - - Uses all available GPUs when gpu_ids is None - - """ - # Lazy import: load submodule only when needed - # pylint: disable-next=import-outside-toplevel - from .runner_methods.multi_gpu_quantization import run_multi_gpu_quantization - - # Execute multi-GPU quantization - result = run_multi_gpu_quantization( - model_config=self.model_config, - quantizer=self.quantizer, - calibration_config=self.calibration_config, - gpu_ids=self.gpu_ids, - report_progress=self.report_progress, - ) - - # Store results in quantizer.results - self.quantizer.results = result["results"] - - # Post-processing - self.quantizer.execute_post_processing() - def quantize_without_calibration(self): """Quantize the model without calibration diff --git a/onecomp/runner_methods/multi_gpu_quantization.py b/onecomp/runner_methods/multi_gpu_quantization.py deleted file mode 100644 index bba13108..00000000 --- a/onecomp/runner_methods/multi_gpu_quantization.py +++ /dev/null @@ -1,406 +0,0 @@ -""" -Multi-GPU Quantization Module (Multi-threaded version) - -Copyright 2025-2026 Fujitsu Ltd. - -Author: Keiji Kimura - -Phase 1: Capture - Capture activations for all layers in a single thread -Phase 2: Quantize - Parallel quantization using multiple threads - -""" - -import queue -import threading -import time -from concurrent.futures import ThreadPoolExecutor -from dataclasses import asdict -from logging import getLogger -from typing import Any, Dict, List, Optional - -import torch - -from onecomp.calibration import CalibrationConfig -from onecomp.quantizer._quantizer import QuantizationResult -from onecomp.utils import check_activations -from onecomp.utils.quantization_progress import QuantizationProgressTracker - -logger = getLogger(__name__) - - -# ============================================================================= -# Serialization helpers -# ============================================================================= - - -def get_model_config_dict(model_config) -> Dict[str, Any]: - """Convert ModelConfig to dict (for future multi-process support).""" - return { - "model_id": model_config.model_id, - "path": model_config.path, - "dtype": model_config.dtype, - "device": model_config.device, - } - - -def get_quantizer_config_dict(quantizer) -> Dict[str, Any]: - """Convert Quantizer settings to dict (for future multi-process support).""" - config = asdict(quantizer) - # Exclude internal state - config.pop("module_to_name", None) - config.pop("results", None) - return config - - -def get_calibration_config_dict(calibration_config: CalibrationConfig) -> Dict[str, Any]: - """Convert CalibrationConfig to dict (for future multi-process support).""" - return asdict(calibration_config) - - -# ============================================================================= -# Phase 1: Capture Phase -# ============================================================================= - - -def run_capture_phase( - model_config, - quantizer, - calibration_config: CalibrationConfig, -) -> Dict[str, Any]: - """Phase 1: Capture input activations and weights for all layers. - - Args: - model_config: Model configuration. - quantizer: Quantizer instance. - calibration_config (CalibrationConfig): Calibration parameters. - - Returns: - Dict containing: - - "layer_data": Dict[layer_name, {"weight": Tensor, "input_activation": Tensor}] - - "layer_names": List of layer names in order - """ - from onecomp.calibration import prepare_calibration_dataset - - logger.info("=== Phase 1: Capture ===") - start_time = time.time() - - # Load model (follows model_config.device settings) - model = model_config.load_model() - tokenizer = model_config.load_tokenizer() - - # Get the device for placing input data - input_device = next(model.parameters()).device - - # Prepare calibration data - inputs = prepare_calibration_dataset( - tokenizer=tokenizer, - device=input_device, - calibration_config=calibration_config, - model=model, - logger=logger, - ) - - # Set up quantizer and get target layers - quantizer.setup(model) - - # Store data for each layer - layer_data = {} - layer_names = [] - - def capture_hook(module, input, output): # pylint: disable=redefined-builtin - """Forward hook: Capture input activations and weights, save to CPU.""" - name = quantizer.module_to_name[module] - logger.info("Capturing layer: %s", name) - - # Get input activation - if isinstance(input, tuple): - input_activation = input[0].detach().cpu() - else: - input_activation = input.detach().cpu() - - # Get weight - weight = module.weight.data.detach().cpu() - - layer_data[name] = { - "weight": weight, - "input_activation": input_activation, - } - layer_names.append(name) - - # Register hooks - handles = [] - for module in quantizer.module_to_name.keys(): - handle = module.register_forward_hook(capture_hook) - handles.append(handle) - - # Run forward pass - logger.info("Running forward pass to capture activations...") - with torch.no_grad(): - model(**inputs) - - # Remove hooks - for handle in handles: - handle.remove() - - # ============================================================= - # Check phase: Abort if any captured activation is all-zeros - # When ModelConfig.device is "auto", captured activations may be all-zeros. - # The following function raises RuntimeError if any activation is all-zeros. - # ============================================================= - try: - check_activations( - {name: data["input_activation"] for name, data in layer_data.items()}, - ) - except RuntimeError as e: - logger.error("Capture failed: %s", e) - raise - - # Release model - del model - torch.cuda.empty_cache() - - elapsed = time.time() - start_time - logger.info("Capture phase completed: %d layers in %.2f sec", len(layer_data), elapsed) - - return { - "layer_data": layer_data, - "layer_names": layer_names, - } - - -# ============================================================================= -# Phase 2: Quantization Phase -# ============================================================================= - - -def run_quantization_phase( - layer_data: Dict[str, Dict], - layer_names: List[str], - quantizer, - gpu_ids: List[int], - *, - report_progress: bool = True, -) -> Dict[str, Dict]: - """Phase 2: Parallel quantization using multiple threads. - - Args: - layer_data: Layer data (containing weight and input_activation). - layer_names: List of layer names (order preserved). - quantizer: Quantizer instance. - gpu_ids: List of GPU IDs to use. - - Returns: - Dict of quantization results. - """ - logger.info("=== Phase 2: Quantization (multi-threaded) ===") - logger.info("Using GPUs: %s for %d layers", gpu_ids, len(layer_names)) - start_time = time.time() - - progress = None - if report_progress: - progress = QuantizationProgressTracker( - logger, - len(layer_names), - "Multi-GPU layer quantization", - thread_safe=True, - ) - - # Force PyTorch lazy initialization upfront (avoid multi-thread race conditions) - # torch.linalg.solve's internal initialization can cause - # "lazy wrapper should be called at most once" errors when called from multiple threads simultaneously - # Note: Not required for quantizers other than JointQ, but the overhead is negligible (a few ms), - # so it is always executed without conditional branching - for gpu_id in gpu_ids: - device = torch.device(f"cuda:{gpu_id}") - dummy_A = torch.randn(2, 2, device=device, dtype=torch.float64) - dummy_b = torch.randn(2, 1, device=device, dtype=torch.float64) - _ = torch.linalg.solve(dummy_A, dummy_b) - del dummy_A, dummy_b - torch.cuda.synchronize() - logger.info("Initialized torch.linalg on all GPUs") - - results = {} - lock = threading.Lock() - - def quantize_single_layer(layer_name: str, device: torch.device, gpu_id: int): - """Quantize a single layer on the specified GPU.""" - - weight = layer_data[layer_name]["weight"] - activation = layer_data[layer_name]["input_activation"] - - # Create a dummy module - # weight.device is used to specify the computation device: - # - GPTQ: Device for Hessian computation and quantization - # - JointQ: Fallback for the device parameter - dummy_module = torch.nn.Linear(weight.shape[1], weight.shape[0], bias=False) - dummy_module.weight.data = weight.to(device) - - # activation is passed as-is on CPU - # - GPTQ: Moved to GPU as needed within calculate_hessian - # - JointQ: Moved to CPU within quantize_layer - - # Hessian computation + quantization - layer_start = time.time() - hessian, nsamples = None, None - if quantizer.flag_hessian: - hessian, nsamples = quantizer.calculate_hessian(dummy_module, activation) - extra_kwargs = {} - if quantizer.flag_nsamples: - extra_kwargs["nsamples"] = nsamples - quant_result = quantizer.quantize_layer( - dummy_module, activation, hessian=hessian, **extra_kwargs - ) - layer_elapsed = time.time() - layer_start - - # Backward compatibility: convert Tensor to QuantizationResult if needed - if isinstance(quant_result, torch.Tensor): - quant_result = QuantizationResult(dequantized_weight=quant_result) - - # Set quantization time - quant_result.quantization_time = layer_elapsed - - # Move dequantized_weight to CPU (if still on GPU) - if quant_result.dequantized_weight is not None and quant_result.dequantized_weight.is_cuda: - quant_result.dequantized_weight = quant_result.dequantized_weight.cpu() - - # Compute quantization error (if calc_quant_error=True) - if quantizer.calc_quant_error: - # TODO: Cache the result to avoid recomputing dequantized weight twice. - # Output quantization error - ( - quant_result.output_squared_error, - quant_result.mean_output_squared_error, - quant_result.relative_output_squared_error, - ) = quantizer.calculate_output_quantization_error( - dummy_module, activation, quant_result.compute_dequantized_weight() - ) - - # Weight quantization error - ( - quant_result.weight_squared_error, - quant_result.mean_weight_squared_error, - quant_result.relative_weight_squared_error, - ) = quantizer.calculate_weight_quantization_error( - dummy_module, quant_result.compute_dequantized_weight() - ) - - with lock: - results[layer_name] = quant_result - logger.info(" %s on GPU %d: %.2f sec", layer_name, gpu_id, layer_elapsed) - if progress is not None: - progress.step_complete(layer_name) - - # Free memory - del dummy_module - if hessian is not None: - del hessian - torch.cuda.empty_cache() - - # Shared task queue (dynamic work-stealing approach) - # Process heavier layers first (LPT: Longest Processing Time first) - # Layers with more elements take longer to process - sorted_layer_names = sorted( - layer_names, - key=lambda name: layer_data[name]["weight"].numel(), - reverse=True, # Descending order (heaviest layers first) - ) - - task_queue: queue.Queue = queue.Queue() - for layer_name in sorted_layer_names: - task_queue.put(layer_name) - - def gpu_worker(gpu_id: int): - """GPU worker: Fetch tasks from queue and process (idle GPUs pick up next task).""" - # Set current device at the start of the thread - # Required when JointQ etc. implicitly use the current device - torch.cuda.set_device(gpu_id) - device = torch.device(f"cuda:{gpu_id}") - logger.info("GPU %d worker started", gpu_id) - task_count = 0 - while True: - try: - layer_name = task_queue.get_nowait() - logger.info("GPU %d got task: %s", gpu_id, layer_name) - except queue.Empty: - break # Exit when queue is empty - try: - quantize_single_layer(layer_name, device, gpu_id) - except Exception as e: - logger.error("GPU %d failed on task %s: %s", gpu_id, layer_name, e) - import traceback - - logger.error("Traceback:\n%s", traceback.format_exc()) - raise # Re-raise exception to stop the job - task_queue.task_done() - task_count += 1 - logger.info("GPU %d worker finished (%d tasks completed)", gpu_id, task_count) - - # One thread per GPU, process until queue is empty - with ThreadPoolExecutor(max_workers=len(gpu_ids)) as executor: - futures = [executor.submit(gpu_worker, gpu_id) for gpu_id in gpu_ids] - # Wait for all tasks to complete (exceptions will be raised here) - for f in futures: - f.result() - - elapsed = time.time() - start_time - logger.info("Quantization phase completed in %.2f sec", elapsed) - - # Reorder results according to layer_names order - ordered_results = {name: results[name] for name in layer_names} - - return ordered_results - - -# ============================================================================= -# Main Entry Point -# ============================================================================= - - -def run_multi_gpu_quantization( - model_config, - quantizer, - calibration_config: CalibrationConfig, - gpu_ids: Optional[List[int]] = None, - *, - report_progress: bool = True, -) -> Dict[str, Any]: - """Main entry point for multi-GPU quantization. - - Args: - model_config: Model configuration. - quantizer: Quantizer instance. - calibration_config (CalibrationConfig): Calibration parameters. - gpu_ids: List of GPU IDs to use (all GPUs if None). - report_progress: When True, log ``[progress]`` with ETA per completed layer. - - Returns: - Dict containing "results" with quantization results for each layer - """ - total_start = time.time() - - # Set GPU ID list - if gpu_ids is None: - gpu_ids = list(range(torch.cuda.device_count())) - - logger.info("Multi-GPU quantization started with GPUs: %s", gpu_ids) - - # Phase 1: Capture - capture_result = run_capture_phase( - model_config=model_config, - quantizer=quantizer, - calibration_config=calibration_config, - ) - - # Phase 2: Parallel quantization - results = run_quantization_phase( - layer_data=capture_result["layer_data"], - layer_names=capture_result["layer_names"], - quantizer=quantizer, - gpu_ids=gpu_ids, - report_progress=report_progress, - ) - - total_elapsed = time.time() - total_start - logger.info("Total time: %.2f sec", total_elapsed) - - return {"results": results} diff --git a/onecomp/utils/quantization_progress.py b/onecomp/utils/quantization_progress.py index a5652f0e..2ef54094 100644 --- a/onecomp/utils/quantization_progress.py +++ b/onecomp/utils/quantization_progress.py @@ -2,7 +2,7 @@ This module exposes :class:`QuantizationProgressTracker`, a small helper used by :class:`onecomp.runner.Runner` and the underlying quantization -entry points (calibration / chunked / multi-GPU / QEP) to emit a single +entry points (calibration / chunked / QEP) to emit a single ``[progress]`` INFO line per completed step with done/total counts, percentage, wall-clock elapsed time, and a linear ETA estimate. @@ -82,11 +82,11 @@ class QuantizationProgressTracker: Thread safety: With ``thread_safe=True`` an internal :class:`threading.Lock` guards both the counter update and the log emission, so multiple - worker threads (e.g. multi-GPU quantization workers) can call - ``step_complete`` concurrently without producing torn counts or - interleaved log lines. The :attr:`done` property also takes the - lock when present. With ``thread_safe=False`` (default) no - locking is performed and callers must serialise their access. + worker threads can call ``step_complete`` concurrently without + producing torn counts or interleaved log lines. The :attr:`done` + property also takes the lock when present. With + ``thread_safe=False`` (default) no locking is performed and + callers must serialise their access. Example: >>> import logging @@ -116,8 +116,7 @@ def __init__( calls. Values ``<= 0`` disable logging entirely (the tracker becomes a no-op). label (str): Short human-readable label that appears at the - start of every log line (e.g. ``"GPTQ layers"`` or - ``"Multi-GPU layer quantization"``). + start of every log line (e.g. ``"GPTQ layers"``). thread_safe (bool): If ``True``, guard the counter and log emission with a :class:`threading.Lock` so the tracker can be safely shared across worker threads. Default