diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..73c715a Binary files /dev/null and b/.DS_Store differ diff --git a/.ipynb_checkpoints/pyproject-checkpoint.toml b/.ipynb_checkpoints/pyproject-checkpoint.toml new file mode 100644 index 0000000..edb3398 --- /dev/null +++ b/.ipynb_checkpoints/pyproject-checkpoint.toml @@ -0,0 +1,58 @@ +[build-system] +requires = ["setuptools>=68", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "libucks" +version = "0.1.0" +requires-python = ">=3.11" +dependencies = [ + "pydantic>=2.0", + "sentence-transformers>=3.0", + "numpy>=1.26", + "anthropic>=0.40", + "httpx>=0.27", + "pyyaml>=6.0", + "tree-sitter>=0.22", + "gitpython>=3.1", + "scipy>=1.13", + "scikit-learn>=1.5", + "watchdog>=4.0", + "unidiff>=0.7", + "click>=8.1", + "rich>=13.0", + "mcp>=1.0", + "structlog>=24.0", +] + +[project.optional-dependencies] +latent = [ + "torch==2.4.1; sys_platform == 'linux' or (sys_platform == 'darwin' and platform_machine == 'arm64')", + "torch>=2.2,<=2.2.2; sys_platform == 'darwin' and platform_machine == 'x86_64' and python_version < '3.13'", + "bitsandbytes>=0.43; sys_platform == 'linux'", + "bitsandbytes>=0.43; sys_platform == 'darwin' and platform_machine == 'arm64'", + "transformers>=4.40", + "accelerate>=0.28", +] +dev = [ + "pytest>=8.0", + "pytest-asyncio>=0.23", + "pytest-timeout>=2.3", + "respx>=0.21", + "jsonschema>=4.22", +] + +[project.scripts] +libucks = "libucks._cli:cli" + +[tool.pytest.ini_options] +asyncio_mode = "auto" +testpaths = ["tests"] +markers = [ + "slow: marks tests that load the embedding model (deselect with '-m not slow')", + "gpu: marks tests that require a real model and GPU (deselect with '-m not gpu')", +] + +[tool.setuptools.packages.find] +where = ["."] +include = ["libucks*"] diff --git a/libucks/.ipynb_checkpoints/_cli-checkpoint.py b/libucks/.ipynb_checkpoints/_cli-checkpoint.py new file mode 100644 index 0000000..0013890 --- /dev/null +++ b/libucks/.ipynb_checkpoints/_cli-checkpoint.py @@ -0,0 +1,365 @@ +"""CLI entry point — lives inside the package so the console script works from any directory.""" +import asyncio +import json +import socket +import subprocess +from pathlib import Path + +import click + + +def _find_repo_root() -> Path: + """Return the git repo root for cwd, or cwd itself if not in a repo.""" + try: + result = subprocess.run( + ["git", "rev-parse", "--show-toplevel"], + capture_output=True, + text=True, + timeout=5, + ) + if result.returncode == 0: + return Path(result.stdout.strip()) + except Exception: + pass + return Path.cwd() + + +@click.group() +@click.version_option(version="0.1.0", prog_name="libucks") +def cli(): + """libucks — Librarian Buckets, local AI memory server for coding agents.""" + + +@cli.command("init") +@click.option("--local", "local_path", type=click.Path(exists=True, file_okay=False, path_type=Path), + required=True, help="Path to a local repository to index.") +def init_cmd(local_path: Path): + """Seed libucks buckets from a local repository.""" + from libucks.config import Config + from libucks.init_orchestrator import InitOrchestrator + from libucks.thinking import create_strategy + + cfg = Config.load(local_path) + strategy = create_strategy(cfg) + orchestrator = InitOrchestrator(local_path, strategy=strategy) + asyncio.run(orchestrator.run()) + + +@cli.command("serve") +def serve_cmd(): + """Start the libucks MCP server over stdio.""" + from libucks.mcp_bridge import serve + asyncio.run(serve()) + + +@cli.command("install-hooks") +@click.option("--repo", "repo_path", type=click.Path(exists=True, file_okay=False, path_type=Path), + default=None, help="Path to repository (defaults to git repo containing cwd).") +def install_hooks_cmd(repo_path: Path | None): + """Append libucks git hook triggers to .git/hooks/ (never overwrites).""" + from libucks.git_hook_receiver import install_hooks + + target = repo_path or _find_repo_root() + modified = install_hooks(target) + if modified: + click.echo(f"Installed hooks: {', '.join(modified)}") + else: + click.echo("All hooks already installed — nothing changed.") + + +@cli.command("train-adapter") +@click.option("--repo", "repo_path", type=click.Path(exists=True, file_okay=False, path_type=Path), + default=None, help="Path to repository (defaults to git repo containing cwd).") +@click.option("--creative", is_flag=True, default=False, + help="Use contrastive training with multi-perspective triplets and hard negatives.") +@click.option("--epochs", default=1, show_default=True, help="Number of training epochs.") +def train_adapter_cmd(repo_path: Path | None, creative: bool, epochs: int): + """Train the CommunicationAdapter to align Librarian latents with teacher targets. + + With --creative: generates multi-perspective triplets (Summary, Logic Flow, + Dependency Map) and mines hard negatives for InfoNCE contrastive training. + + Without --creative: uses basic MSE alignment between adapter output and + teacher target latents. + + Saves trained weights to /.libucks/adapter.pt. + """ + target = repo_path or _find_repo_root() + asyncio.run(_run_train_adapter(target, creative=creative, epochs=epochs)) + + +async def _run_train_adapter(repo_path: Path, creative: bool, epochs: int) -> None: + from libucks.config import Config + from libucks.thinking import create_strategy + from libucks.thinking.communication_adapter import CommunicationAdapter + from libucks.storage.bucket_store import BucketStore + from libucks.storage.bucket_registry import BucketRegistry + + cfg = Config.load(repo_path) + + if cfg.model.strategy != "latent": + raise click.ClickException( + "train-adapter requires strategy='latent' in .libucks/config.toml.\n" + "The CommunicationAdapter operates on torch.Tensor hidden states; " + "TextStrategy returns strings and cannot be used for training.\n\n" + "Set the following in your config and re-run:\n\n" + " [model]\n" + " strategy = \"latent\"\n" + " local_model = \"Qwen/Qwen2.5-0.5B-Instruct\"\n" + " device = \"mps\" # or cuda / cpu" + ) + + registry_path = repo_path / cfg.paths.registry_file + bucket_dir = repo_path / ".libucks" + + registry = BucketRegistry(registry_path) + registry.load() + store = BucketStore(repo_path / cfg.paths.bucket_dir) + + bucket_ids = list(registry.get_all_centroids().keys()) + if not bucket_ids: + click.echo("No buckets found — run `libucks init` first.", err=True) + return + + from transformers import AutoConfig as _AutoConfig + _hidden_dim = _AutoConfig.from_pretrained(cfg.model.local_model).hidden_size + adapter = CommunicationAdapter(hidden_dim=_hidden_dim) + adapter.load_saved_weights(bucket_dir / "adapter.pt") + + from libucks.thinking.model_manager import ModelManager as _MM + _training_device = _MM._resolve_device(cfg.model.device) + adapter = adapter.to(_training_device) + + if creative: + click.echo(f"[libucks] Creative contrastive training on {len(bucket_ids)} buckets " + f"for {epochs} epoch(s)...") + await _train_creative(cfg, registry, store, bucket_ids, adapter, epochs, bucket_dir) + else: + click.echo(f"[libucks] Basic MSE training on {len(bucket_ids)} buckets " + f"for {epochs} epoch(s)...") + await _train_basic(cfg, registry, store, bucket_ids, adapter, epochs, bucket_dir) + + +async def _train_creative(cfg, registry, store, bucket_ids, adapter, epochs, bucket_dir): + """Creative mode: multi-perspective + hard negatives + InfoNCE.""" + import os + from libucks.thinking import create_strategy + from libucks.thinking.latent_strategy import LatentStrategy + from libucks.thinking.text_strategy import TextStrategy + from libucks.thinking.training.data_generator import MultiPerspectiveDataGenerator + from libucks.thinking.training.train_adapter import ContrastiveAdapterTrainer + + text_strategy = TextStrategy.from_env(cfg.model.anthropic_model) + + # Use latent strategy only if configured; otherwise encode via text (fallback) + if cfg.model.strategy == "latent": + latent_strategy = create_strategy(cfg) + else: + click.echo("[libucks] Warning: strategy='text' — encoding via TextStrategy passthrough.", + err=True) + latent_strategy = TextStrategy.from_env(cfg.model.anthropic_model) + + generator = MultiPerspectiveDataGenerator( + text_strategy=text_strategy, + latent_strategy=latent_strategy, + registry=registry, + store=store, + ) + trainer = ContrastiveAdapterTrainer(adapter, temperature=0.07, lr=1e-4) + + samples = [] + for i, bucket_id in enumerate(bucket_ids, 1): + click.echo(f" Generating sample {i}/{len(bucket_ids)}: {bucket_id}") + try: + sample = await generator.generate(bucket_id) + samples.append(sample) + except Exception as exc: + click.echo(f" Skipped {bucket_id}: {exc}", err=True) + + if not samples: + click.echo("No training samples generated.", err=True) + return + + losses = trainer.train(samples, num_epochs=epochs) + trainer.save(bucket_dir / "adapter.pt") + + first = sum(losses[:5]) / min(5, len(losses)) + last = sum(losses[-5:]) / min(5, len(losses)) + click.echo(f"Training complete. Loss: {first:.4f} → {last:.4f}. " + f"Saved to {bucket_dir / 'adapter.pt'}") + + +async def _train_basic(cfg, registry, store, bucket_ids, adapter, epochs, bucket_dir): + """Basic mode: MSE between adapter mean-pooled output and target latent.""" + import torch + import torch.nn.functional as F + from torch.optim import AdamW + from libucks.thinking import create_strategy + from libucks.thinking.text_strategy import TextStrategy + from libucks.thinking.training.data_generator import ( + MultiPerspectiveDataGenerator, PERSPECTIVE_PROMPTS + ) + + text_strategy = TextStrategy.from_env(cfg.model.anthropic_model) + + if cfg.model.strategy == "latent": + latent_strategy = create_strategy(cfg) + else: + latent_strategy = TextStrategy.from_env(cfg.model.anthropic_model) + + generator = MultiPerspectiveDataGenerator( + text_strategy=text_strategy, + latent_strategy=latent_strategy, + registry=registry, + store=store, + ) + optimizer = AdamW(adapter.parameters(), lr=1e-4) + _device = next(adapter.parameters()).device + + for epoch in range(epochs): + total_loss = 0.0 + for i, bucket_id in enumerate(bucket_ids, 1): + try: + sample = await generator.generate(bucket_id) + except Exception as exc: + click.echo(f" Skipped {bucket_id}: {exc}", err=True) + continue + + latents = [t.clone().detach().to(_device, torch.float32) for t in sample.librarian_latents] + optimizer.zero_grad() + output = adapter(latents) + anchor = F.normalize(output.mean(dim=0), dim=0) + target = F.normalize(sample.target_latent.clone().detach().to(_device, torch.float32).mean(dim=0), dim=0) + loss = 1.0 - torch.dot(anchor, target) + loss.backward() + optimizer.step() + total_loss += loss.item() + click.echo(f" Epoch {epoch+1} [{i}/{len(bucket_ids)}] loss={loss.item():.4f}") + + torch.save(adapter.state_dict(), bucket_dir / "adapter.pt") + click.echo(f"Basic training complete. Saved to {bucket_dir / 'adapter.pt'}") + + +@cli.command("query") +@click.argument("query_text") +@click.option("--repo", "repo_path", type=click.Path(exists=True, file_okay=False, path_type=Path), + default=None, help="Path to repository (defaults to git repo containing cwd).") +@click.option("--top-k", default=3, show_default=True, help="Number of buckets to consult.") +def query_cmd(query_text: str, repo_path: Path | None, top_k: int): + """Run a single query against the local memory engine and print the answer. + + Bypasses the MCP server entirely — no 60-second timeout. Useful for + validating the full inference pipeline from the terminal. + + Example: + libucks query "How does the authentication module work?" + """ + target = repo_path or _find_repo_root() + asyncio.run(_run_query(target, query_text, top_k)) + + +async def _run_query(repo_path: Path, query_text: str, top_k: int) -> None: + import sys + from libucks.config import Config + from libucks.thinking import create_strategy + from libucks.thinking.communication_adapter import CommunicationAdapter + from libucks.embeddings.embedding_service import EmbeddingService + from libucks.storage.bucket_registry import BucketRegistry + from libucks.storage.bucket_store import BucketStore + from libucks.central_agent import CentralAgent + from libucks.librarian import Librarian + from libucks.query_orchestrator import QueryOrchestrator + from libucks.translator import Translator + + cfg = Config.load(repo_path) + registry_path = repo_path / cfg.paths.registry_file + bucket_dir = repo_path / ".libucks" + bucket_store_dir = repo_path / cfg.paths.bucket_dir + + click.echo(f"[libucks] repo={repo_path} strategy={cfg.model.strategy}", err=True) + + registry = BucketRegistry(registry_path) + registry.load() + store = BucketStore(bucket_store_dir) + + bucket_ids = list(registry.get_all_centroids().keys()) + if not bucket_ids: + click.echo("No buckets found — run `libucks init` first.", err=True) + return + + click.echo(f"[libucks] {len(bucket_ids)} buckets loaded", err=True) + + # Load embedding model (suppress stdout during model loading) + _real_stdout = sys.stdout + sys.stdout = sys.stderr + try: + embedder = EmbeddingService.get_instance(cfg.model.embedding_model) + finally: + sys.stdout = _real_stdout + + click.echo("[libucks] embedding model ready, loading strategy...", err=True) + strategy = create_strategy(cfg) + click.echo("[libucks] strategy ready", err=True) + + agent = CentralAgent(registry, cfg, embed_fn=embedder.embed) + librarians: dict[str, Librarian] = {} + for bucket_id in bucket_ids: + lib = Librarian( + bucket_id=bucket_id, + store=store, + registry=registry, + strategy=strategy, + embedder=embedder, + mitosis_threshold=cfg.routing.mitosis_threshold, + ) + librarians[bucket_id] = lib + agent.register_librarian(bucket_id, lib) + + adapter = None + if cfg.model.strategy == "latent": + import torch + resolved_device = cfg.model.device if cfg.model.device != "auto" else "mps" + adapter = CommunicationAdapter(hidden_dim=strategy.hidden_dim) + adapter.load_saved_weights(bucket_dir / "adapter.pt") + # dtype must match the model's output dtype. ModelManager loads Qwen in + # float16 on MPS; float32 adapter parameters cause an MPS broadcast error. + adapter_dtype = torch.float16 if resolved_device == "mps" else None + adapter = adapter.to(device=resolved_device, dtype=adapter_dtype) + + translator = Translator(strategy, adapter=adapter) + + orchestrator = QueryOrchestrator( + central_agent=agent, + librarians=librarians, + embed_fn=embedder.embed, + top_k=top_k, + ) + + click.echo(f"[libucks] routing: \"{query_text}\"", err=True) + representations = await orchestrator.query(query_text) + click.echo(f"[libucks] {len(representations)} representations, synthesizing...", err=True) + + answer = await translator.synthesize(query_text, representations) + + # Answer goes to stdout so it can be piped / captured cleanly. + click.echo(answer) + + +@cli.command("hook") +@click.argument("event") +@click.argument("args", nargs=-1) +def hook_cmd(event: str, args: tuple): + """Send a git hook event to the running libucks server (called by git hooks).""" + repo_path = _find_repo_root() + sock_path = repo_path / ".libucks" / "server.sock" + if not sock_path.exists(): + return # server not running — silent exit so git is never blocked + + payload = json.dumps({"event": event, "args": list(args)}).encode() + try: + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s: + s.settimeout(3) + s.connect(str(sock_path)) + s.sendall(payload) + except Exception: + pass # never block git diff --git a/libucks/.ipynb_checkpoints/config-checkpoint.py b/libucks/.ipynb_checkpoints/config-checkpoint.py new file mode 100644 index 0000000..5501623 --- /dev/null +++ b/libucks/.ipynb_checkpoints/config-checkpoint.py @@ -0,0 +1,142 @@ +"""Config — typed configuration dataclasses loaded from .libucks/config.toml. + +Uses Python 3.11's built-in tomllib (read-only, binary-mode). +All sub-dataclasses validate their own fields in __post_init__ so that +invalid values are caught at construction time regardless of whether +the Config came from a TOML file or was built directly in code. + +Defaults are chosen to be sensible for a mid-sized repository: + novelty_threshold 0.35 — cosine distance; tighter = fewer new buckets + top_k 3 — buckets queried per request + mitosis_threshold 20000 — tokens before a bucket splits +""" + +from __future__ import annotations + +import tomllib +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + + +# --------------------------------------------------------------------------- +# Sub-dataclasses +# --------------------------------------------------------------------------- + +@dataclass +class ModelConfig: + """AI model identifiers.""" + + anthropic_model: str = "claude-haiku-4-5-20251001" + embedding_model: str = "all-MiniLM-L6-v2" + local_model: str = "Qwen/Qwen2.5-0.5B-Instruct" + quantization: str = "none" + device: str = "auto" + strategy: str = "text" + compression_steps: int = 8 + + def __post_init__(self) -> None: + if self.strategy not in ("text", "latent"): + raise ValueError( + f"strategy must be 'text' or 'latent', got {self.strategy!r}" + ) + if self.quantization not in ("none", "4bit", "8bit"): + raise ValueError( + f"quantization must be 'none', '4bit', or '8bit', " + f"got {self.quantization!r}" + ) + + +@dataclass +class RoutingConfig: + """Embedding-based routing parameters.""" + + novelty_threshold: float = 0.35 + """Cosine distance. A diff embedding farther than this from all + existing centroids triggers CreateBucketEvent. Range: (0, 1).""" + + top_k: int = 3 + """Number of buckets consulted per query. Must be >= 1.""" + + mitosis_threshold: int = 20_000 + """Token count at which a bucket is eligible for manual mitosis.""" + + init_bucket_size: int = 2_000 + """Target raw-token count per bucket during INIT clustering. + Controls how many buckets are seeded: n_clusters = total_tokens // init_bucket_size. + Kept separate from mitosis_threshold so INIT density can be tuned independently + of runtime splitting behaviour.""" + + def __post_init__(self) -> None: + self.novelty_threshold = float(self.novelty_threshold) + self.top_k = int(self.top_k) + self.mitosis_threshold = int(self.mitosis_threshold) + self.init_bucket_size = int(self.init_bucket_size) + + if not (0.0 < self.novelty_threshold < 1.0): + raise ValueError( + f"novelty_threshold must be in the open interval (0, 1), " + f"got {self.novelty_threshold}" + ) + if self.top_k < 1: + raise ValueError(f"top_k must be >= 1, got {self.top_k}") + if self.mitosis_threshold < 1: + raise ValueError( + f"mitosis_threshold must be >= 1, got {self.mitosis_threshold}" + ) + if self.init_bucket_size < 1: + raise ValueError( + f"init_bucket_size must be >= 1, got {self.init_bucket_size}" + ) + + +@dataclass +class PathsConfig: + """File-system paths used by libucks (relative to the target repo root + unless prefixed with ~/).""" + + bucket_dir: str = ".libucks/buckets" + registry_file: str = ".libucks/registry.json" + pending_events: str = ".libucks/pending_events.jsonl" + log_file: str = ".libucks/libucks.log" + grammar_cache: str = "~/.libucks/grammars" + repo_cache: str = "~/.libucks/repos" + + +# --------------------------------------------------------------------------- +# Root config +# --------------------------------------------------------------------------- + +def _merge(cls: type, data: dict[str, Any]) -> object: + """Construct a dataclass from a dict, ignoring unknown keys and letting + unrecognised fields fall back to their declared defaults.""" + import dataclasses + known = {f.name for f in dataclasses.fields(cls)} + return cls(**{k: v for k, v in data.items() if k in known}) + + +@dataclass +class Config: + model: ModelConfig = field(default_factory=ModelConfig) + routing: RoutingConfig = field(default_factory=RoutingConfig) + paths: PathsConfig = field(default_factory=PathsConfig) + + @classmethod + def load(cls, repo_path: Path) -> "Config": + """Load config from /.libucks/config.toml. + + Returns a Config with all defaults if the file does not exist. + Sections or keys absent from the file are filled in with defaults. + """ + config_file = repo_path / ".libucks" / "config.toml" + if not config_file.exists(): + return cls() + + with open(config_file, "rb") as fh: + data = tomllib.load(fh) + + return cls( + model=_merge(ModelConfig, data.get("model", {})), # type: ignore[arg-type] + routing=_merge(RoutingConfig, data.get("routing", {})), # type: ignore[arg-type] + paths=_merge(PathsConfig, data.get("paths", {})), # type: ignore[arg-type] + ) diff --git a/libucks/.ipynb_checkpoints/mcp_bridge-checkpoint.py b/libucks/.ipynb_checkpoints/mcp_bridge-checkpoint.py new file mode 100644 index 0000000..9b3c560 --- /dev/null +++ b/libucks/.ipynb_checkpoints/mcp_bridge-checkpoint.py @@ -0,0 +1,290 @@ +"""MCP Bridge — exposes libucks tools over the Model Context Protocol (stdio). + +Tools: + libucks_query(query, top_k=3) — query the memory store, returns synthesized answer + libucks_status() — bucket count and token totals +""" +from __future__ import annotations + +# Must be set before any native extension (tokenizers Rust runtime, ObjC) is +# imported — placing them here, at module load, guarantees that. +import os +os.environ.setdefault("OBJC_DISABLE_INITIALIZE_FORK_SAFETY", "YES") # prevents SIGABRT on Apple Silicon +os.environ.setdefault("TOKENIZERS_PARALLELISM", "false") # prevents HF tokenizer deadlock warning +os.environ.setdefault("TRANSFORMERS_VERBOSITY", "error") # suppresses "BertModel report" etc. +os.environ.setdefault("TQDM_DISABLE", "1") # suppresses "Loading weights" progress bars +os.environ.setdefault("PYTORCH_MPS_HIGH_WATERMARK_RATIO", "0.0") # disables MPS pool pre-reservation; does not + # fix peak single-op allocation (see model_manager.py + # attn_implementation="eager") but reduces background + # fragmentation pressure on unified memory + +import asyncio +import logging +import sys +import tomllib +from pathlib import Path +from typing import Any + +import mcp.server.stdio +import mcp.types as types +from mcp.server import Server + +from libucks.central_agent import CentralAgent +from libucks.config import Config +from libucks.diff.diff_extractor import DiffExtractor +from libucks.embeddings.embedding_service import EmbeddingService +from libucks.git_hook_receiver import serve_socket +from libucks.health_monitor import HealthMonitor +from libucks.librarian import Librarian +from libucks.merging_service import MergingService +from libucks.mitosis import MitosisService +from libucks.query_orchestrator import QueryOrchestrator +from libucks.stale_checker import StaleChecker +from libucks.startup_recovery import StartupRecovery +from libucks.storage.bucket_registry import BucketRegistry +from libucks.storage.bucket_store import BucketStore +from libucks.thinking import create_strategy +from libucks.translator import Translator + + +def _load_repo_path() -> Path: + """Return the repository root. + + Resolution order: + 1. LIBUCKS_REPO_PATH env var — set this in claude_desktop_config.json "env" + to point the server at any repo you want. + 2. Project root inferred from __file__ — reliable fallback that is never + the filesystem root, even when Claude Desktop launches with cwd='/'. + """ + env_path = os.environ.get("LIBUCKS_REPO_PATH") + if env_path: + return Path(env_path).expanduser().resolve() + # __file__ = libucks/mcp_bridge.py → .parent = libucks/ → .parent = project root + return Path(__file__).parent.parent.resolve() + + +async def serve() -> None: + # Route ALL logging to stderr — stdout is reserved for MCP JSON-RPC. + logging.basicConfig(stream=sys.stderr, level=logging.INFO, force=True) + + import structlog + structlog.configure( + logger_factory=structlog.PrintLoggerFactory(file=sys.stderr), + ) + + try: + import transformers + transformers.logging.set_verbosity_error() + except Exception: + pass + + repo_path = _load_repo_path() + cfg = Config.load(repo_path) + registry_path = repo_path / cfg.paths.registry_file + bucket_dir = repo_path / ".libucks" + bucket_store_dir = repo_path / cfg.paths.bucket_dir + print(f"[libucks] repo={repo_path} registry={registry_path} buckets={bucket_store_dir}", file=sys.stderr) + + registry = BucketRegistry(registry_path) + registry.load() + + store = BucketStore(bucket_store_dir) + + # Pre-load the embedding model BEFORE the MCP stdio server starts. + # Temporarily redirect sys.stdout → sys.stderr so any remaining direct + # prints from model loading never reach the MCP pipe. + _real_stdout = sys.stdout + sys.stdout = sys.stderr + try: + embedder = EmbeddingService.get_instance(cfg.model.embedding_model) + finally: + sys.stdout = _real_stdout # restore BEFORE mcp.server.stdio.stdio_server() captures it + strategy = create_strategy(cfg) + + agent = CentralAgent(registry, cfg, embed_fn=embedder.embed) + + librarians: dict[str, Librarian] = {} + for bucket_id in registry.get_all_centroids(): + lib = Librarian( + bucket_id=bucket_id, + store=store, + registry=registry, + strategy=strategy, + embedder=embedder, + mitosis_threshold=cfg.routing.mitosis_threshold, + ) + librarians[bucket_id] = lib + agent.register_librarian(bucket_id, lib) + + adapter = None + if cfg.model.strategy == "latent": + from libucks.thinking.communication_adapter import CommunicationAdapter + import torch + adapter = CommunicationAdapter() + adapter.load_saved_weights(bucket_dir / "adapter.pt") + # dtype must match the model's output dtype. ModelManager loads Qwen in + # float16 on MPS; float32 adapter parameters cause an MPS broadcast error. + adapter = adapter.to(device="mps", dtype=torch.float16) + + translator = Translator(strategy, adapter=adapter) + + # ------------------------------------------------------------------ + # Startup recovery: replay commits that arrived while server was offline. + # ------------------------------------------------------------------ + recovery: StartupRecovery | None = None + try: + extractor = DiffExtractor(repo_path) + recovery = StartupRecovery( + repo_path=repo_path, + registry=registry, + store=store, + librarians=librarians, + extractor=extractor, + ) + current_head = await recovery.run() + if current_head is not None: + registry._meta["last_indexed_head"] = current_head + registry._meta["watcher_pid"] = os.getpid() + registry.save() + print(f"[libucks] startup recovery complete, HEAD={current_head[:8]}", file=sys.stderr) + except Exception as exc: + # Recovery is best-effort — never block server startup. + print(f"[libucks] startup recovery skipped: {exc}", file=sys.stderr) + + # ------------------------------------------------------------------ + # Git hook socket listener (Phase 6-D). + # ------------------------------------------------------------------ + sock_path = bucket_dir / "server.sock" + + async def _on_hook_event(payload: dict) -> None: + """Handle a JSON event from a git hook and trigger background re-index.""" + if recovery is None: + return + try: + new_head = await recovery.run() + if new_head is not None: + registry._meta["last_indexed_head"] = new_head + registry.save() + print(f"[libucks] hook event '{payload.get('event')}' → re-indexed HEAD={new_head[:8]}", file=sys.stderr) + except Exception as exc: + print(f"[libucks] hook event error: {exc}", file=sys.stderr) + + asyncio.ensure_future(serve_socket(sock_path, _on_hook_event)) + + # ------------------------------------------------------------------ + # HealthMonitor (Phase 6-E/6-F): autonomous quality guardian. + # ------------------------------------------------------------------ + mitosis_svc = MitosisService( + store=store, + registry=registry, + embedder=embedder, + agent=agent, + strategy=strategy, + mitosis_threshold=cfg.routing.mitosis_threshold, + ) + merging_svc = MergingService( + registry=registry, + store=store, + agent=agent, + embedder=embedder, + strategy=strategy, + ) + health_monitor = HealthMonitor( + registry=registry, + store=store, + mitosis_service=mitosis_svc, + merging_service=merging_svc, + embedder=embedder, + mitosis_threshold=cfg.routing.mitosis_threshold, + ) + asyncio.ensure_future(health_monitor.run()) + + # ------------------------------------------------------------------ + # StaleChecker + reindex callback (Phase 6-C JIT invalidation). + # ------------------------------------------------------------------ + stale_checker = StaleChecker(registry=registry, store=store, repo_path=repo_path) + + async def _reindex_stale(stale_bucket_ids: list[str]) -> None: + """Background re-index triggered by stale query results.""" + if recovery is None: + return + try: + new_head = await recovery.run() + if new_head is not None: + registry._meta["last_indexed_head"] = new_head + registry.save() + except Exception as exc: + print(f"[libucks] background reindex error: {exc}", file=sys.stderr) + + orchestrator = QueryOrchestrator( + central_agent=agent, + librarians=librarians, + embed_fn=embedder.embed, + top_k=cfg.routing.top_k, + stale_checker=stale_checker, + reindex_fn=_reindex_stale, + ) + + server = Server("libucks") + + @server.list_tools() + async def list_tools() -> list[types.Tool]: + return [ + types.Tool( + name="libucks_query", + description="Query the libucks memory store for context about the repository.", + inputSchema={ + "type": "object", + "properties": { + "query": {"type": "string", "description": "Natural language question"}, + "top_k": {"type": "integer", "description": "Number of buckets to consult", "default": 3}, + }, + "required": ["query"], + }, + ), + types.Tool( + name="libucks_status", + description="Return system health: bucket count and token totals.", + inputSchema={"type": "object", "properties": {}}, + ), + ] + + @server.call_tool() + async def call_tool(name: str, arguments: dict[str, Any]) -> list[types.TextContent]: + if name == "libucks_query": + query_text = arguments["query"] + top_k = int(arguments.get("top_k", cfg.routing.top_k)) + orchestrator._top_k = top_k + + print(f"[libucks] query: routing '{query_text[:60]}' (top_k={top_k})", file=sys.stderr, flush=True) + representations = await orchestrator.query(query_text) + print(f"[libucks] query: got {len(representations)} representations", file=sys.stderr, flush=True) + answer = await translator.synthesize(query_text, representations) + print(f"[libucks] query: synthesis complete ({len(answer)} chars)", file=sys.stderr, flush=True) + return [types.TextContent(type="text", text=answer)] + + if name == "libucks_status": + centroids = registry.get_all_centroids() + bucket_ids = list(centroids.keys()) + total_tokens = sum( + registry.get_token_count(bid) for bid in bucket_ids + ) + status = { + "bucket_count": len(bucket_ids), + "total_tokens": total_tokens, + "buckets": { + bid: {"token_count": registry.get_token_count(bid)} + for bid in bucket_ids + }, + } + import json + return [types.TextContent(type="text", text=json.dumps(status, indent=2))] + + raise ValueError(f"Unknown tool: {name!r}") + + async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): + await server.run( + read_stream, + write_stream, + server.create_initialization_options(), + ) diff --git a/libucks/.ipynb_checkpoints/translator-checkpoint.py b/libucks/.ipynb_checkpoints/translator-checkpoint.py new file mode 100644 index 0000000..8ce4d88 --- /dev/null +++ b/libucks/.ipynb_checkpoints/translator-checkpoint.py @@ -0,0 +1,286 @@ +"""Translator — the ONLY component permitted to call ThinkingStrategy.decode(). + +Receives N Representations from N Librarians plus the original query string. +Selects the appropriate synthesis path based on representation type: + + V1 (str): joins partial answers, calls strategy.reason() to synthesize, + then strategy.decode() to produce the final string. + V2 (tensor): passes the list of hidden-state tensors through the + CommunicationAdapter to produce a single soft-prompt, then + calls strategy.decode() exactly once. + +In both paths, strategy.decode() is called exactly once and its return value +is the sole natural-language output returned to the MCP Bridge. +""" +from __future__ import annotations + +import sys +from typing import List, Optional + +import torch + +from libucks.thinking.base import Representation, ThinkingStrategy + + +def _log(msg: str) -> None: + print(f"[libucks:translator] {msg}", file=sys.stderr, flush=True) + + +class Translator: + def __init__( + self, + strategy: ThinkingStrategy, + adapter: Optional[object] = None, + ) -> None: + self._strategy = strategy + if adapter is not None: + try: + _device = strategy._mgr.device + self._adapter = adapter.to(_device) + except Exception: + self._adapter = adapter + else: + self._adapter = None + + async def synthesize(self, query: str, representations: List[Representation]) -> str: + if not representations: + _log("synthesize: no representations — returning fallback message") + return "No relevant context found in the memory store." + + _log(f"synthesize: {len(representations)} representations, " + f"type={'latent' if isinstance(representations[0], torch.Tensor) else 'text'}") + + if isinstance(representations[0], torch.Tensor): + return await self._synthesize_latent(representations) + + return await self._synthesize_text(query, representations) + + # ------------------------------------------------------------------ + # V1 text path (unchanged) + # ------------------------------------------------------------------ + + async def _synthesize_text( + self, query: str, representations: List[Representation] + ) -> str: + parts = "\n\n---\n\n".join(str(r) for r in representations) + synthesis_prompt = ( + "You are synthesizing partial answers from multiple domain-specific memory buckets " + "into a single, coherent response. Do not mention bucket IDs, internal metadata, " + "or implementation details of the memory system. Answer directly and concisely.\n\n" + f"Partial answers:\n{parts}" + ) + combined: Representation = await self._strategy.reason(synthesis_prompt, query) + # This is the ONLY authorised call to decode() in the entire system. + return await self._strategy.decode(combined) + + # ------------------------------------------------------------------ + # V2 latent path + # ------------------------------------------------------------------ + + async def _synthesize_latent( + self, representations: List[Representation] + ) -> str: + shapes = [tuple(r.shape) for r in representations] + _log(f"_synthesize_latent: adapter forward, representations={shapes}") + # .contiguous() before the adapter: MultiheadAttention on MPS hangs + # on non-contiguous tensors produced by prior squeeze/expand operations. + contiguous_reps = [r.contiguous() for r in representations] + contiguous_reps = [t.to(torch.float32) for t in contiguous_reps] + with torch.no_grad(): + synthesized: torch.Tensor = self._adapter(contiguous_reps) + _log(f"_synthesize_latent: adapter complete, output={tuple(synthesized.shape)}") + # This is the ONLY authorised call to decode() in the entire system. + _log("_synthesize_latent: calling decode()") + result = await self._strategy.decode(synthesized) + _log(f"_synthesize_latent: decode complete ({len(result)} chars)") + return result +"""Translator — the ONLY component permitted to call ThinkingStrategy.decode(). + +Receives N Representations from N Librarians plus the original query string. +Selects the appropriate synthesis path based on representation type: + + V1 (str): joins partial answers, calls strategy.reason() to synthesize, + then strategy.decode() to produce the final string. + V2 (tensor): passes the list of hidden-state tensors through the + CommunicationAdapter to produce a single soft-prompt, then + calls strategy.decode() exactly once. + +In both paths, strategy.decode() is called exactly once and its return value +is the sole natural-language output returned to the MCP Bridge. +""" + +import sys +from typing import List, Optional + +import torch + +from libucks.thinking.base import Representation, ThinkingStrategy + + +def _log(msg: str) -> None: + print(f"[libucks:translator] {msg}", file=sys.stderr, flush=True) + + +class Translator: + def __init__( + self, + strategy: ThinkingStrategy, + adapter: Optional[object] = None, + ) -> None: + self._strategy = strategy + if adapter is not None: + try: + _device = strategy._mgr.device + self._adapter = adapter.to(_device) + except Exception: + self._adapter = adapter + else: + self._adapter = None + + async def synthesize(self, query: str, representations: List[Representation]) -> str: + if not representations: + _log("synthesize: no representations — returning fallback message") + return "No relevant context found in the memory store." + + _log(f"synthesize: {len(representations)} representations, " + f"type={'latent' if isinstance(representations[0], torch.Tensor) else 'text'}") + + if isinstance(representations[0], torch.Tensor): + return await self._synthesize_latent(representations) + + return await self._synthesize_text(query, representations) + + # ------------------------------------------------------------------ + # V1 text path (unchanged) + # ------------------------------------------------------------------ + + async def _synthesize_text( + self, query: str, representations: List[Representation] + ) -> str: + parts = "\n\n---\n\n".join(str(r) for r in representations) + synthesis_prompt = ( + "You are synthesizing partial answers from multiple domain-specific memory buckets " + "into a single, coherent response. Do not mention bucket IDs, internal metadata, " + "or implementation details of the memory system. Answer directly and concisely.\n\n" + f"Partial answers:\n{parts}" + ) + combined: Representation = await self._strategy.reason(synthesis_prompt, query) + # This is the ONLY authorised call to decode() in the entire system. + return await self._strategy.decode(combined) + + # ------------------------------------------------------------------ + # V2 latent path + # ------------------------------------------------------------------ + + async def _synthesize_latent( + self, representations: List[Representation] + ) -> str: + shapes = [tuple(r.shape) for r in representations] + _log(f"_synthesize_latent: adapter forward, representations={shapes}") + # .contiguous() before the adapter: MultiheadAttention on MPS hangs + # on non-contiguous tensors produced by prior squeeze/expand operations. + contiguous_reps = [r.contiguous() for r in representations] + contiguous_reps = [t.to(torch.float32) for t in contiguous_reps] + with torch.no_grad(): + synthesized: torch.Tensor = self._adapter(contiguous_reps) + _log(f"_synthesize_latent: adapter complete, output={tuple(synthesized.shape)}") + # This is the ONLY authorised call to decode() in the entire system. + _log("_synthesize_latent: calling decode()") + result = await self._strategy.decode(synthesized) + _log(f"_synthesize_latent: decode complete ({len(result)} chars)") + return result +"""Translator — the ONLY component permitted to call ThinkingStrategy.decode(). + +Receives N Representations from N Librarians plus the original query string. +Selects the appropriate synthesis path based on representation type: + + V1 (str): joins partial answers, calls strategy.reason() to synthesize, + then strategy.decode() to produce the final string. + V2 (tensor): passes the list of hidden-state tensors through the + CommunicationAdapter to produce a single soft-prompt, then + calls strategy.decode() exactly once. + +In both paths, strategy.decode() is called exactly once and its return value +is the sole natural-language output returned to the MCP Bridge. +""" + +import sys +from typing import List, Optional + +import torch + +from libucks.thinking.base import Representation, ThinkingStrategy + + +def _log(msg: str) -> None: + print(f"[libucks:translator] {msg}", file=sys.stderr, flush=True) + + +class Translator: + def __init__( + self, + strategy: ThinkingStrategy, + adapter: Optional[object] = None, + ) -> None: + self._strategy = strategy + if adapter is not None: + try: + _device = strategy._mgr.device + self._adapter = adapter.to(_device) + except Exception: + self._adapter = adapter + else: + self._adapter = None + + async def synthesize(self, query: str, representations: List[Representation]) -> str: + if not representations: + _log("synthesize: no representations — returning fallback message") + return "No relevant context found in the memory store." + + _log(f"synthesize: {len(representations)} representations, " + f"type={'latent' if isinstance(representations[0], torch.Tensor) else 'text'}") + + if isinstance(representations[0], torch.Tensor): + return await self._synthesize_latent(representations) + + return await self._synthesize_text(query, representations) + + # ------------------------------------------------------------------ + # V1 text path (unchanged) + # ------------------------------------------------------------------ + + async def _synthesize_text( + self, query: str, representations: List[Representation] + ) -> str: + parts = "\n\n---\n\n".join(str(r) for r in representations) + synthesis_prompt = ( + "You are synthesizing partial answers from multiple domain-specific memory buckets " + "into a single, coherent response. Do not mention bucket IDs, internal metadata, " + "or implementation details of the memory system. Answer directly and concisely.\n\n" + f"Partial answers:\n{parts}" + ) + combined: Representation = await self._strategy.reason(synthesis_prompt, query) + # This is the ONLY authorised call to decode() in the entire system. + return await self._strategy.decode(combined) + + # ------------------------------------------------------------------ + # V2 latent path + # ------------------------------------------------------------------ + + async def _synthesize_latent( + self, representations: List[Representation] + ) -> str: + shapes = [tuple(r.shape) for r in representations] + _log(f"_synthesize_latent: adapter forward, representations={shapes}") + # .contiguous() before the adapter: MultiheadAttention on MPS hangs + # on non-contiguous tensors produced by prior squeeze/expand operations. + contiguous_reps = [r.contiguous() for r in representations] + contiguous_reps = [t.to(torch.float32) for t in contiguous_reps] + with torch.no_grad(): + synthesized: torch.Tensor = self._adapter(contiguous_reps) + _log(f"_synthesize_latent: adapter complete, output={tuple(synthesized.shape)}") + # This is the ONLY authorised call to decode() in the entire system. + _log("_synthesize_latent: calling decode()") + result = await self._strategy.decode(synthesized) + _log(f"_synthesize_latent: decode complete ({len(result)} chars)") + return result diff --git a/libucks/_cli.py b/libucks/_cli.py index aacbc3c..c1f4bcb 100644 --- a/libucks/_cli.py +++ b/libucks/_cli.py @@ -126,6 +126,10 @@ async def _run_train_adapter(repo_path: Path, creative: bool, epochs: int) -> No adapter = CommunicationAdapter(hidden_dim=_hidden_dim) adapter.load_saved_weights(bucket_dir / "adapter.pt") + from libucks.thinking.model_manager import ModelManager as _MM + _training_device = _MM._resolve_device(cfg.model.device) + adapter = adapter.to(_training_device) + if creative: click.echo(f"[libucks] Creative contrastive training on {len(bucket_ids)} buckets " f"for {epochs} epoch(s)...") @@ -210,6 +214,7 @@ async def _train_basic(cfg, registry, store, bucket_ids, adapter, epochs, bucket store=store, ) optimizer = AdamW(adapter.parameters(), lr=1e-4) + _device = next(adapter.parameters()).device for epoch in range(epochs): total_loss = 0.0 @@ -220,10 +225,12 @@ async def _train_basic(cfg, registry, store, bucket_ids, adapter, epochs, bucket click.echo(f" Skipped {bucket_id}: {exc}", err=True) continue + latents = [t.clone().detach().to(_device, torch.float32) for t in sample.librarian_latents] + latents = [t.to(torch.float32) for t in latents] optimizer.zero_grad() - output = adapter(sample.librarian_latents) + output = adapter(latents) anchor = F.normalize(output.mean(dim=0), dim=0) - target = F.normalize(sample.target_latent.mean(dim=0), dim=0) + target = F.normalize(sample.target_latent.clone().detach().to(_device, torch.float32).mean(dim=0), dim=0) loss = 1.0 - torch.dot(anchor, target) loss.backward() optimizer.step() diff --git a/libucks/thinking/.ipynb_checkpoints/latent_strategy-checkpoint.py b/libucks/thinking/.ipynb_checkpoints/latent_strategy-checkpoint.py new file mode 100644 index 0000000..05a591d --- /dev/null +++ b/libucks/thinking/.ipynb_checkpoints/latent_strategy-checkpoint.py @@ -0,0 +1,461 @@ +"""LatentStrategy — V2 implementation using local HuggingFace transformers. + +Librarians call encode() and reason() — both return torch.Tensor hidden states +from the model's last layer. No text is generated at these call sites. + +ONLY the Translator calls decode() to produce natural-language output by +projecting hidden states through the LM head and running autoregressive generation. + +See ARCHITECTURE.md §4 for the architectural constraints. +""" +from __future__ import annotations + +import sys + +from libucks.thinking.base import Representation, ThinkingStrategy + + +_ANCHOR_PROMPT = "<|im_start|>assistant\n" + + +def _log(msg: str) -> None: + print(f"[libucks:latent] {msg}", file=sys.stderr, flush=True) + + +class LatentStrategy(ThinkingStrategy): + def __init__( + self, + model_manager: object | None = None, + compressor: object | None = None, + injection_gate: float = 0.3, + temperature: float = 0.7, + top_p: float = 0.9, + repetition_penalty: float = 1.3, + receive_temperature: float = 0.6, + receive_top_p: float = 0.9, + receive_top_k: int = 50, + receive_repetition_penalty: float = 1.2, + ) -> None: + self._mgr = model_manager + self._compressor = compressor + # Residual injection gate g: x_soft = dummy + g*(hidden_matched - dummy). + # Starting at 0.1 keeps 90% of the output on the native embedding manifold + # and injects only 10% adapter signal, preventing generation collapse from + # off-manifold perturbations. + self._injection_gate = injection_gate + # Sampling parameters to break greedy (argmax) attractor loops. + # Argmax at T=0 creates deterministic 3-token cycles ("1. 1. 1.") because + # the soft-prompt primes a peaked distribution. Multinomial sampling with + # temperature > 0 breaks the cycle; repetition_penalty further suppresses + # tokens already in the generated sequence. + self._temperature = temperature + self._top_p = top_p + self._repetition_penalty = repetition_penalty + # Separate sampling params for receive() — the Base model (not Instruct) + # has a much flatter logit distribution. top_k=50 is applied first as a + # hard candidate cap before top_p, preventing tail bleed on a 150k vocab. + # repetition_penalty=1.2 breaks degenerate loops (~2 cycles) without + # pushing probability mass into multilingual tokens (which 1.3+ does). + self._receive_temperature = receive_temperature + self._receive_top_p = receive_top_p + self._receive_top_k = receive_top_k + self._receive_repetition_penalty = receive_repetition_penalty + # MPS has a single Metal command queue. Concurrent submissions from + # multiple asyncio coroutines (e.g. 3 Librarians via asyncio.gather) + # deadlock against each other. This lock serialises all device inference. + import asyncio + self._device_lock = asyncio.Lock() + + @property + def hidden_dim(self) -> int: + """Return the hidden_size of the encoder model.""" + return self._mgr.hidden_dim + + def _sample_next_token( + self, + logits: "torch.Tensor", + generated_ids: list[int], + temperature: float | None = None, + top_p: float | None = None, + top_k: int | None = None, + repetition_penalty: float | None = None, + ) -> "torch.Tensor": + """Sample the next token with repetition penalty, temperature, top-k, and top-p. + + top_k is applied before top_p: it first hard-caps the candidate set to + the k highest-logit tokens, then top_p further narrows within that set. + This prevents tail bleed on flat 150k-vocab Base model distributions where + top_p alone can include thousands of candidates. + + Args: + logits: shape (vocab_size,) — raw logits for the next-token position. + generated_ids: token IDs produced so far (used for repetition penalty). + + Returns: + torch.Tensor of shape (1,) — the sampled token ID. + """ + import torch + import torch.nn.functional as F + + _rep_penalty = repetition_penalty if repetition_penalty is not None else self._repetition_penalty + _temperature = temperature if temperature is not None else self._temperature + _top_p = top_p if top_p is not None else self._top_p + _top_k = top_k # None means no top-k filtering + + logits = logits.float().clone() + + # Repetition penalty: HuggingFace convention — + # positive logit → divide by penalty (reduces) + # negative logit → multiply by penalty (makes more negative) + if _rep_penalty != 1.0 and generated_ids: + for token_id in set(generated_ids): + if logits[token_id] > 0: + logits[token_id] /= _rep_penalty + else: + logits[token_id] *= _rep_penalty + + # Temperature scaling + logits = logits / max(_temperature, 1e-8) + + # Top-k filtering — zero out everything outside the k highest logits. + # Applied before top-p so nucleus filtering operates on a bounded set. + if _top_k is not None and _top_k > 0: + top_k_vals = torch.topk(logits, min(_top_k, logits.size(-1)))[0] + logits[logits < top_k_vals[-1]] = float("-inf") + + # Top-p (nucleus) filtering — zero out the tail below the probability mass + if _top_p < 1.0: + sorted_logits, sorted_indices = torch.sort(logits, descending=True) + cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1) + # Keep the first token that pushes cumulative mass above top_p; + # remove everything after it. + sorted_indices_to_remove = cumulative_probs > _top_p + sorted_indices_to_remove[1:] = sorted_indices_to_remove[:-1].clone() + sorted_indices_to_remove[0] = False # always keep the top token + logits[sorted_indices[sorted_indices_to_remove]] = float("-inf") + + probs = F.softmax(logits, dim=-1) + return torch.multinomial(probs, num_samples=1) + + async def encode(self, text: str) -> "torch.Tensor": + """Encode text into last-layer hidden states via a single forward pass. + + Returns: + torch.Tensor of shape (seq_len, hidden_dim). + """ + import torch + + model = self._mgr.get_model() + tokenizer = self._mgr.get_tokenizer() + device = self._mgr.device + + async with self._device_lock: + # no_grad (not inference_mode): prevents gradient computation in the + # encoder while keeping returned tensors as normal (non-inference) + # tensors. inference_mode marks outputs as inference tensors, which + # cannot be saved for backward when they later flow into the adapter + # forward pass during LoRA receiver training. + with torch.no_grad(): + inputs = tokenizer(text, return_tensors="pt") + inputs = {k: v.to(device) for k, v in inputs.items()} + output = model(**inputs, output_hidden_states=True) + # hidden_states[-1]: (1, seq_len, hidden_dim) → (seq_len, hidden_dim) + hidden = output.hidden_states[-1].squeeze(0).contiguous() + del output # release full 36-layer hidden_states tuple + return hidden + + async def reason(self, query: str, context: str) -> "torch.Tensor": + """Produce a hidden-state Representation for a query given context. + + Constructs the standard prompt template, runs a single forward pass, + and returns the last-layer hidden states. model.generate() is never + called here — only the Translator is permitted to decode. + + Returns: + torch.Tensor of shape (seq_len, hidden_dim). + """ + import torch + + model = self._mgr.get_model() + tokenizer = self._mgr.get_tokenizer() + device = self._mgr.device + + prompt = f"{context}\n\n{query}" + + _log(f"reason: tokenizing ({len(prompt)} chars, device={device})") + async with self._device_lock: + with torch.no_grad(): # see encode() comment: no_grad not inference_mode + # Truncate to 256 tokens — Qwen forward-pass latency on MPS + # scales with sequence length; 485-token prompts push 3 serial + # Librarian calls past the 60-second MCP timeout. + inputs = tokenizer( + prompt, + return_tensors="pt", + max_length=256, + truncation=True, + ) + seq_len = inputs["input_ids"].shape[-1] + inputs = {k: v.to(device) for k, v in inputs.items()} + _log(f"reason: forward pass (seq_len={seq_len})") + output = model(**inputs, output_hidden_states=True) + hidden = output.hidden_states[-1].squeeze(0).contiguous() + del output # release full 36-layer hidden_states tuple + _log(f"reason: forward pass complete, hidden={tuple(hidden.shape)}") + + if self._compressor is not None: + _log(f"reason: compressing ({hidden.shape[0]} → {self._compressor.compression_steps} steps)") + with torch.inference_mode(): + hidden = self._compressor(hidden).contiguous() + _log(f"reason: compressed to {tuple(hidden.shape)}") + + return hidden + + async def decode(self, result: "torch.Tensor") -> str: + """Convert hidden-state tensor to natural language via Residual Anchoring. + + Three-stage injection (Vision Wormhole, Eq. 2 + Eq. 9): + + 1. NormMatch (Eq. 9): rescale adapter output per-row to match the + embedding layer's mean norm — places hidden_matched at embed_scale. + + 2. Residual Anchoring (Eq. 2): blend with a dummy baseline of K EOS-token + embeddings that are guaranteed to be on Qwen's native manifold: + x_soft = dummy_embeds + g * (hidden_matched - dummy_embeds) + At g=0.1 (default), 90% of the signal comes from the real embedding + manifold and only 10% from the adapter output. A final re-normalisation + to embed_scale enforces the manifold norm constraint regardless of g. + + 3. Position ID Alignment: pass explicit position_ids on every model() call + so RoPE offsets are correct for the KV cache even when inputs_embeds is + used instead of input_ids on the first call. + + This is the ONLY authorised call site for generative inference in the system. + + Args: + result: torch.Tensor of shape (seq_len, hidden_dim) or + (1, seq_len, hidden_dim). + + Returns: + Decoded natural-language string. + """ + import torch + from transformers import DynamicCache + + model = self._mgr.get_model() + tokenizer = self._mgr.get_tokenizer() + device = self._mgr.device + + _log(f"decode: received tensor {tuple(result.shape)}, device={device}") + + async with self._device_lock: + with torch.no_grad(): + # --- Normalise input shape --- + hidden = result.to(device) + if hidden.dim() == 2: + hidden = hidden.unsqueeze(0) # (1, K, d) + hidden = hidden.contiguous() # MPS: avoids silent hang on non-contiguous strides + K = hidden.shape[1] + + # --- Step 1: NormMatch (Vision Wormhole, Eq. 9) --- + # Rescale each adapter output row to embed_scale so hidden_matched + # and the dummy baseline are at the same magnitude. + embed_scale = model.model.embed_tokens.weight.norm(dim=-1).mean() + adapter_norms = hidden.norm(dim=-1, keepdim=True).clamp(min=1e-8) + hidden_matched = hidden * (embed_scale / adapter_norms) # (1, K, d) + _log(f"decode: isnan={hidden_matched.isnan().any().item()}, isinf={hidden_matched.isinf().any().item()}") + _log( + f"decode: NormMatch embed_scale={embed_scale.item():.3f}, " + f"mean_adapter_norm={adapter_norms.mean().item():.3f}" + ) + + + # --- Step 2: Residual Anchoring (Vision Wormhole, Eq. 2) --- + # Embed K copies of eos_token_id as the dummy baseline. EOS is always + # available and its embedding sits firmly on the native manifold. + space_id = int(tokenizer(" ", return_tensors="pt", add_special_tokens=False)["input_ids"].flatten()[-1].item()) + dummy_ids = torch.full( + (1, K), space_id, dtype=torch.long, device=device + ) + dummy_embeds = model.model.embed_tokens(dummy_ids) # (1, K, d) + + # Blend: x_soft = dummy + gate * (hidden_matched - dummy) + delta = hidden_matched - dummy_embeds + x_soft = dummy_embeds + self._injection_gate * delta # (1, K, d) + + # Re-normalise to embed_scale: enforces manifold norm constraint after + # blending (the convex combination of two unit-scale vectors has norm + # ≈ 0.9 × embed_scale at gate=0.1 for orthogonal random vectors). + x_soft_norms = x_soft.norm(dim=-1, keepdim=True).clamp(min=1e-8) + x_soft = x_soft * (embed_scale / x_soft_norms) + _log( + f"decode: residual gate={self._injection_gate}, " + f"mean_delta_norm={delta.norm(dim=-1).mean().item():.3f}" + ) + + # --- Step 3: Embed anchor prompt for semantic texture --- + anchor_ids = tokenizer( + _ANCHOR_PROMPT, return_tensors="pt", add_special_tokens=False + )["input_ids"].to(device) # (1, L) + anchor_embeds = model.model.embed_tokens(anchor_ids) # (1, L, d) + + # --- Step 4: Concatenate and build position IDs --- + inputs_embeds = torch.cat( + [x_soft, anchor_embeds], dim=1 + ) # (1, K+L, d) + prefix_len = inputs_embeds.shape[1] + + # Explicit position_ids ensure RoPE is applied at the correct offsets + # even when the model receives inputs_embeds instead of input_ids. + # Without this, some Qwen2.5 versions revert to position 0 for the + # first token of a DynamicCache continuation. + prefix_pos = torch.arange( + prefix_len, dtype=torch.long, device=device + ).unsqueeze(0) # (1, K+L) + # Cast to the model's dtype (float16 on CUDA/MPS) so the lm_head + # matmul doesn't hit a Half/Float mismatch from the float32 + # adapter output flowing through the embedding concatenation. + inputs_embeds = inputs_embeds.to(model.dtype) + _log(f"decode: inputs_embeds shape={tuple(inputs_embeds.shape)}, dtype={inputs_embeds.dtype}") + + # --- Step 5: Prefix pass --- + # + # WHY NOT model.generate(): + # model.generate() calls _get_cache() which constructs a StaticCache + # regardless of past_key_values or generation_config patches. For + # Qwen2.5-3B on MPS: + # 36 layers × 2 × 8 kv_heads × 32768 × 128 × float16 ≈ 4.8 GB + # Metal rejects any single NDArray > 2^32 bytes — instant SIGABRT. + # + # DynamicCache grows ~9 MB per 128 tokens and cannot trigger the + # MPSTemporaryNDArray hard limit on Apple Silicon. + past_kv: DynamicCache = DynamicCache() + out = model( + inputs_embeds=inputs_embeds, + position_ids=prefix_pos, + past_key_values=past_kv, + use_cache=True, + ) + generated_ids: list[int] = [] + next_id = self._sample_next_token(out.logits[0, -1, :], generated_ids) + past_kv = out.past_key_values + + # --- Step 6: Autoregressive generation loop (≤ 128 new tokens) --- + # curr_pos starts immediately after the prefix so each token gets its + # correct RoPE position independent of cache state queries. + curr_pos = prefix_len + _log("decode: starting manual generation loop (max_new_tokens=128)") + for _step in range(128): + if next_id.item() == tokenizer.eos_token_id: + break + generated_ids.append(next_id.item()) + loop_pos = torch.tensor( + [[curr_pos]], dtype=torch.long, device=device + ) + out = model( + input_ids=next_id.unsqueeze(0), # (1, 1) + position_ids=loop_pos, + past_key_values=past_kv, + use_cache=True, + ) + next_id = self._sample_next_token(out.logits[0, -1, :], generated_ids) + past_kv = out.past_key_values + curr_pos += 1 + + _log(f"decode: generation complete ({len(generated_ids)} tokens)") + + decoded = tokenizer.decode(generated_ids, skip_special_tokens=True) + _log(f"decode: tokenizer.decode complete ({len(decoded)} chars)") + return decoded + + async def receive(self, framed_latent: "torch.Tensor") -> str: + """Decode a framed latent sequence using the trained Base receiver. + + This is the Interlat-Lite replacement for decode(). It uses the Base + model (LoRA fine-tuned to interpret latent injections) and does NOT + apply NormMatch or Residual Anchoring — the trained model handles the + manifold gap directly. + + Args: + framed_latent: Tensor of shape (K+2, hidden_dim) — adapter output + already framed with / boundary embeddings. + + Returns: + Decoded natural-language string. + """ + import torch + from transformers import DynamicCache + + model = self._mgr.get_base_model() + tokenizer = self._mgr.get_base_tokenizer() + device = self._mgr.device + + _log(f"receive: framed_latent {tuple(framed_latent.shape)}, device={device}") + + async with self._device_lock: + with torch.no_grad(): + # Add batch dim: (1, K+2, D) + embeds = framed_latent.to(device) + if embeds.dim() == 2: + embeds = embeds.unsqueeze(0) + + # Prefix pass — inject framed latent directly, no preprocessing + past_kv: DynamicCache = DynamicCache() + prefix_len = embeds.shape[1] + prefix_pos = torch.arange( + prefix_len, dtype=torch.long, device=device + ).unsqueeze(0) + + out = model( + inputs_embeds=embeds, + position_ids=prefix_pos, + past_key_values=past_kv, + use_cache=True, + ) + generated_ids: list[int] = [] + eos_id = tokenizer.eos_token_id + min_new_tokens = 8 # suppress EOS until at least this many tokens + + def _suppress_eos(logits: "torch.Tensor", n_generated: int) -> "torch.Tensor": + if n_generated < min_new_tokens and eos_id is not None: + logits = logits.clone() + logits[eos_id] = float("-inf") + return logits + + first_logits = _suppress_eos(out.logits[0, -1, :], 0) + next_id = self._sample_next_token( + first_logits, generated_ids, + temperature=self._receive_temperature, + top_p=self._receive_top_p, + top_k=self._receive_top_k, + repetition_penalty=self._receive_repetition_penalty, + ) + past_kv = out.past_key_values + + # Autoregressive generation loop (≤ 512 new tokens) + curr_pos = prefix_len + for _step in range(512): + if next_id.item() == eos_id: + break + generated_ids.append(next_id.item()) + loop_pos = torch.tensor([[curr_pos]], dtype=torch.long, device=device) + out = model( + input_ids=next_id.unsqueeze(0), + position_ids=loop_pos, + past_key_values=past_kv, + use_cache=True, + ) + step_logits = _suppress_eos(out.logits[0, -1, :], len(generated_ids)) + next_id = self._sample_next_token( + step_logits, generated_ids, + temperature=self._receive_temperature, + top_p=self._receive_top_p, + repetition_penalty=self._receive_repetition_penalty, + ) + past_kv = out.past_key_values + curr_pos += 1 + + _log(f"receive: generated {len(generated_ids)} tokens") + + decoded = tokenizer.decode(generated_ids, skip_special_tokens=True) + _log(f"receive: decode complete ({len(decoded)} chars)") + return decoded diff --git a/libucks/thinking/latent_strategy.py b/libucks/thinking/latent_strategy.py index 9f5d633..05a591d 100644 --- a/libucks/thinking/latent_strategy.py +++ b/libucks/thinking/latent_strategy.py @@ -28,7 +28,7 @@ def __init__( model_manager: object | None = None, compressor: object | None = None, injection_gate: float = 0.3, - temperature: float = 0.8, + temperature: float = 0.7, top_p: float = 0.9, repetition_penalty: float = 1.3, receive_temperature: float = 0.6, @@ -311,7 +311,11 @@ async def decode(self, result: "torch.Tensor") -> str: prefix_pos = torch.arange( prefix_len, dtype=torch.long, device=device ).unsqueeze(0) # (1, K+L) - _log(f"decode: inputs_embeds shape={tuple(inputs_embeds.shape)}") + # Cast to the model's dtype (float16 on CUDA/MPS) so the lm_head + # matmul doesn't hit a Half/Float mismatch from the float32 + # adapter output flowing through the embedding concatenation. + inputs_embeds = inputs_embeds.to(model.dtype) + _log(f"decode: inputs_embeds shape={tuple(inputs_embeds.shape)}, dtype={inputs_embeds.dtype}") # --- Step 5: Prefix pass --- # diff --git a/libucks/thinking/model_manager.py b/libucks/thinking/model_manager.py index b36ee10..588abbb 100644 --- a/libucks/thinking/model_manager.py +++ b/libucks/thinking/model_manager.py @@ -25,6 +25,7 @@ def load( self, model_id: str, quantization: str = "none", + bnb_4bit_compute_dtype: str = "float32", device: str = "auto", ) -> None: """Load model and tokenizer from HuggingFace hub or local cache.""" @@ -32,11 +33,17 @@ def load( import torch + _DTYPE_MAP = {"float16": torch.float16, "float32": torch.float32, "bfloat16": torch.bfloat16} + _compute_dtype = _DTYPE_MAP.get(bnb_4bit_compute_dtype, torch.float32) + kwargs: dict = {"device_map": resolved_device} if quantization == "4bit": from transformers import BitsAndBytesConfig - kwargs["quantization_config"] = BitsAndBytesConfig(load_in_4bit=True) + kwargs["quantization_config"] = BitsAndBytesConfig( + load_in_4bit=True, + bnb_4bit_compute_dtype=_compute_dtype, + ) elif quantization == "8bit": from transformers import BitsAndBytesConfig kwargs["quantization_config"] = BitsAndBytesConfig(load_in_8bit=True) @@ -93,6 +100,7 @@ def load_base_model( self, model_id: str, quantization: str = "none", + bnb_4bit_compute_dtype: str = "float32", device: str = "auto", ) -> None: """Load the Base receiver model for latent injection (Interlat-Lite decoder). @@ -104,11 +112,17 @@ def load_base_model( import torch + _DTYPE_MAP = {"float16": torch.float16, "float32": torch.float32, "bfloat16": torch.bfloat16} + _compute_dtype = _DTYPE_MAP.get(bnb_4bit_compute_dtype, torch.float32) + kwargs: dict = {"device_map": resolved_device} if quantization == "4bit": from transformers import BitsAndBytesConfig - kwargs["quantization_config"] = BitsAndBytesConfig(load_in_4bit=True) + kwargs["quantization_config"] = BitsAndBytesConfig( + load_in_4bit=True, + bnb_4bit_compute_dtype=_compute_dtype, + ) elif quantization == "8bit": from transformers import BitsAndBytesConfig kwargs["quantization_config"] = BitsAndBytesConfig(load_in_8bit=True) diff --git a/libucks/thinking/training/data_generator.py b/libucks/thinking/training/data_generator.py index d460287..d303582 100644 --- a/libucks/thinking/training/data_generator.py +++ b/libucks/thinking/training/data_generator.py @@ -38,6 +38,8 @@ def _read_chunk_content(meta) -> str: def _collect_source_text(front_matter, max_chars: int = 3000) -> str: """Concatenate actual code content from ChunkMetadata, up to max_chars.""" + if not hasattr(front_matter, "chunks"): + return "" parts = [] total = 0 for meta in front_matter.chunks: diff --git a/libucks/translator.py b/libucks/translator.py index 644639f..8ce4d88 100644 --- a/libucks/translator.py +++ b/libucks/translator.py @@ -33,7 +33,14 @@ def __init__( adapter: Optional[object] = None, ) -> None: self._strategy = strategy - self._adapter = adapter + if adapter is not None: + try: + _device = strategy._mgr.device + self._adapter = adapter.to(_device) + except Exception: + self._adapter = adapter + else: + self._adapter = None async def synthesize(self, query: str, representations: List[Representation]) -> str: if not representations: @@ -78,6 +85,197 @@ async def _synthesize_latent( # .contiguous() before the adapter: MultiheadAttention on MPS hangs # on non-contiguous tensors produced by prior squeeze/expand operations. contiguous_reps = [r.contiguous() for r in representations] + contiguous_reps = [t.to(torch.float32) for t in contiguous_reps] + with torch.no_grad(): + synthesized: torch.Tensor = self._adapter(contiguous_reps) + _log(f"_synthesize_latent: adapter complete, output={tuple(synthesized.shape)}") + # This is the ONLY authorised call to decode() in the entire system. + _log("_synthesize_latent: calling decode()") + result = await self._strategy.decode(synthesized) + _log(f"_synthesize_latent: decode complete ({len(result)} chars)") + return result +"""Translator — the ONLY component permitted to call ThinkingStrategy.decode(). + +Receives N Representations from N Librarians plus the original query string. +Selects the appropriate synthesis path based on representation type: + + V1 (str): joins partial answers, calls strategy.reason() to synthesize, + then strategy.decode() to produce the final string. + V2 (tensor): passes the list of hidden-state tensors through the + CommunicationAdapter to produce a single soft-prompt, then + calls strategy.decode() exactly once. + +In both paths, strategy.decode() is called exactly once and its return value +is the sole natural-language output returned to the MCP Bridge. +""" + +import sys +from typing import List, Optional + +import torch + +from libucks.thinking.base import Representation, ThinkingStrategy + + +def _log(msg: str) -> None: + print(f"[libucks:translator] {msg}", file=sys.stderr, flush=True) + + +class Translator: + def __init__( + self, + strategy: ThinkingStrategy, + adapter: Optional[object] = None, + ) -> None: + self._strategy = strategy + if adapter is not None: + try: + _device = strategy._mgr.device + self._adapter = adapter.to(_device) + except Exception: + self._adapter = adapter + else: + self._adapter = None + + async def synthesize(self, query: str, representations: List[Representation]) -> str: + if not representations: + _log("synthesize: no representations — returning fallback message") + return "No relevant context found in the memory store." + + _log(f"synthesize: {len(representations)} representations, " + f"type={'latent' if isinstance(representations[0], torch.Tensor) else 'text'}") + + if isinstance(representations[0], torch.Tensor): + return await self._synthesize_latent(representations) + + return await self._synthesize_text(query, representations) + + # ------------------------------------------------------------------ + # V1 text path (unchanged) + # ------------------------------------------------------------------ + + async def _synthesize_text( + self, query: str, representations: List[Representation] + ) -> str: + parts = "\n\n---\n\n".join(str(r) for r in representations) + synthesis_prompt = ( + "You are synthesizing partial answers from multiple domain-specific memory buckets " + "into a single, coherent response. Do not mention bucket IDs, internal metadata, " + "or implementation details of the memory system. Answer directly and concisely.\n\n" + f"Partial answers:\n{parts}" + ) + combined: Representation = await self._strategy.reason(synthesis_prompt, query) + # This is the ONLY authorised call to decode() in the entire system. + return await self._strategy.decode(combined) + + # ------------------------------------------------------------------ + # V2 latent path + # ------------------------------------------------------------------ + + async def _synthesize_latent( + self, representations: List[Representation] + ) -> str: + shapes = [tuple(r.shape) for r in representations] + _log(f"_synthesize_latent: adapter forward, representations={shapes}") + # .contiguous() before the adapter: MultiheadAttention on MPS hangs + # on non-contiguous tensors produced by prior squeeze/expand operations. + contiguous_reps = [r.contiguous() for r in representations] + contiguous_reps = [t.to(torch.float32) for t in contiguous_reps] + with torch.no_grad(): + synthesized: torch.Tensor = self._adapter(contiguous_reps) + _log(f"_synthesize_latent: adapter complete, output={tuple(synthesized.shape)}") + # This is the ONLY authorised call to decode() in the entire system. + _log("_synthesize_latent: calling decode()") + result = await self._strategy.decode(synthesized) + _log(f"_synthesize_latent: decode complete ({len(result)} chars)") + return result +"""Translator — the ONLY component permitted to call ThinkingStrategy.decode(). + +Receives N Representations from N Librarians plus the original query string. +Selects the appropriate synthesis path based on representation type: + + V1 (str): joins partial answers, calls strategy.reason() to synthesize, + then strategy.decode() to produce the final string. + V2 (tensor): passes the list of hidden-state tensors through the + CommunicationAdapter to produce a single soft-prompt, then + calls strategy.decode() exactly once. + +In both paths, strategy.decode() is called exactly once and its return value +is the sole natural-language output returned to the MCP Bridge. +""" + +import sys +from typing import List, Optional + +import torch + +from libucks.thinking.base import Representation, ThinkingStrategy + + +def _log(msg: str) -> None: + print(f"[libucks:translator] {msg}", file=sys.stderr, flush=True) + + +class Translator: + def __init__( + self, + strategy: ThinkingStrategy, + adapter: Optional[object] = None, + ) -> None: + self._strategy = strategy + if adapter is not None: + try: + _device = strategy._mgr.device + self._adapter = adapter.to(_device) + except Exception: + self._adapter = adapter + else: + self._adapter = None + + async def synthesize(self, query: str, representations: List[Representation]) -> str: + if not representations: + _log("synthesize: no representations — returning fallback message") + return "No relevant context found in the memory store." + + _log(f"synthesize: {len(representations)} representations, " + f"type={'latent' if isinstance(representations[0], torch.Tensor) else 'text'}") + + if isinstance(representations[0], torch.Tensor): + return await self._synthesize_latent(representations) + + return await self._synthesize_text(query, representations) + + # ------------------------------------------------------------------ + # V1 text path (unchanged) + # ------------------------------------------------------------------ + + async def _synthesize_text( + self, query: str, representations: List[Representation] + ) -> str: + parts = "\n\n---\n\n".join(str(r) for r in representations) + synthesis_prompt = ( + "You are synthesizing partial answers from multiple domain-specific memory buckets " + "into a single, coherent response. Do not mention bucket IDs, internal metadata, " + "or implementation details of the memory system. Answer directly and concisely.\n\n" + f"Partial answers:\n{parts}" + ) + combined: Representation = await self._strategy.reason(synthesis_prompt, query) + # This is the ONLY authorised call to decode() in the entire system. + return await self._strategy.decode(combined) + + # ------------------------------------------------------------------ + # V2 latent path + # ------------------------------------------------------------------ + + async def _synthesize_latent( + self, representations: List[Representation] + ) -> str: + shapes = [tuple(r.shape) for r in representations] + _log(f"_synthesize_latent: adapter forward, representations={shapes}") + # .contiguous() before the adapter: MultiheadAttention on MPS hangs + # on non-contiguous tensors produced by prior squeeze/expand operations. + contiguous_reps = [r.contiguous() for r in representations] + contiguous_reps = [t.to(torch.float32) for t in contiguous_reps] with torch.no_grad(): synthesized: torch.Tensor = self._adapter(contiguous_reps) _log(f"_synthesize_latent: adapter complete, output={tuple(synthesized.shape)}") diff --git a/pyproject.toml b/pyproject.toml index bb741af..e00202d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,11 +23,16 @@ dependencies = [ "rich>=13.0", "mcp>=1.0", "structlog>=24.0", + "bitsandbytes>=0.46.1", + "transformers==4.48.2", ] [project.optional-dependencies] latent = [ - "torch>=2.2", + "torch==2.4.1; sys_platform == 'linux' or (sys_platform == 'darwin' and platform_machine == 'arm64')", + "torch>=2.2,<=2.2.2; sys_platform == 'darwin' and platform_machine == 'x86_64' and python_version < '3.13'", + "bitsandbytes>=0.43; sys_platform == 'linux'", + "bitsandbytes>=0.43; sys_platform == 'darwin' and platform_machine == 'arm64'", "transformers>=4.40", "accelerate>=0.28", ] diff --git a/tests/unit/.ipynb_checkpoints/test_curriculum-checkpoint.py b/tests/unit/.ipynb_checkpoints/test_curriculum-checkpoint.py new file mode 100644 index 0000000..903c1f1 --- /dev/null +++ b/tests/unit/.ipynb_checkpoints/test_curriculum-checkpoint.py @@ -0,0 +1,97 @@ +"""Tests for CurriculumMixer. + +Formula (from Interlat §3.3): + H^(r) = [e_1, ..., e_{floor(r·K)}] ⊕ [h_{floor(r·K)+1}, ..., h_K] + ← token embeddings → ← latents → + +So: + r=0 → all latents (0 token positions) + r=1 → all tokens (K token positions) + +Training samples r ~ U[0,1] uniformly each step, forcing the model to handle +any mixture — this is the curriculum (not a fixed schedule). +""" +import math +import torch +import pytest +from libucks.thinking.curriculum import CurriculumMixer + + +K = 8 +D = 64 + + +@pytest.fixture +def latents(): + torch.manual_seed(0) + return torch.randn(K, D) + + +@pytest.fixture +def tok_embeds(): + torch.manual_seed(1) + return torch.randn(K, D) + + +def test_mix_r0_returns_all_latents(latents, tok_embeds): + """r=0 → floor(0*K)=0 token positions → output equals latents entirely.""" + out = CurriculumMixer.mix(latents, tok_embeds, r=0.0) + assert torch.allclose(out, latents) + + +def test_mix_r1_returns_all_tokens(latents, tok_embeds): + """r=1 → floor(1*K)=K token positions → output equals tok_embeds entirely.""" + out = CurriculumMixer.mix(latents, tok_embeds, r=1.0) + assert torch.allclose(out, tok_embeds) + + +def test_mix_midpoint(latents, tok_embeds): + """r=0.5 → first floor(0.5*K) rows are tok_embeds, rest are latents.""" + r = 0.5 + split = math.floor(r * K) # 4 + out = CurriculumMixer.mix(latents, tok_embeds, r=r) + assert torch.allclose(out[:split], tok_embeds[:split]) + assert torch.allclose(out[split:], latents[split:]) + + +def test_mix_shape_preserved(latents, tok_embeds): + """Output shape (K, D) is invariant to r.""" + for r in [0.0, 0.25, 0.5, 0.75, 1.0]: + out = CurriculumMixer.mix(latents, tok_embeds, r=r) + assert out.shape == (K, D), f"shape wrong for r={r}" + + +def test_mix_dtype_preserved(latents, tok_embeds): + """Output dtype matches input dtype.""" + out = CurriculumMixer.mix(latents, tok_embeds, r=0.5) + assert out.dtype == latents.dtype + + +def test_mix_device_preserved(latents, tok_embeds): + """Output stays on CPU when inputs are on CPU.""" + out = CurriculumMixer.mix(latents, tok_embeds, r=0.5) + assert out.device == latents.device + + +def test_mix_does_not_mutate_inputs(latents, tok_embeds): + """mix() must not modify the input tensors in-place.""" + lat_copy = latents.clone() + tok_copy = tok_embeds.clone() + CurriculumMixer.mix(latents, tok_embeds, r=0.5) + assert torch.allclose(latents, lat_copy) + assert torch.allclose(tok_embeds, tok_copy) + + +def test_mix_r_boundary_values(latents, tok_embeds): + """r outside [0,1] raises ValueError.""" + with pytest.raises(ValueError): + CurriculumMixer.mix(latents, tok_embeds, r=-0.1) + with pytest.raises(ValueError): + CurriculumMixer.mix(latents, tok_embeds, r=1.1) + + +def test_mix_shape_mismatch_raises(latents): + """Mismatched shape raises ValueError.""" + bad = torch.randn(K + 2, D) + with pytest.raises(ValueError): + CurriculumMixer.mix(latents, bad, r=0.5) diff --git a/tests/unit/.ipynb_checkpoints/test_curriculum_data-checkpoint.py b/tests/unit/.ipynb_checkpoints/test_curriculum_data-checkpoint.py new file mode 100644 index 0000000..01cde78 --- /dev/null +++ b/tests/unit/.ipynb_checkpoints/test_curriculum_data-checkpoint.py @@ -0,0 +1,172 @@ +"""Tests for MultiPerspectiveDataGenerator.generate_curriculum_batch(). + +A curriculum batch item contains: + - mixed_input: Tensor of shape (K, D) — CurriculumMixer.mix() output + - target_ids: LongTensor of token IDs — the target text tokenized (integer IDs) + - r: float in [0, 1] — the mixing rate used for this item + +The batch is used by LoRAReceiverTrainer to fine-tune the Base model with +cross-entropy loss (teacher forcing) + L_sep. +""" +import math +import torch +import pytest +from unittest.mock import AsyncMock, MagicMock, patch + + +K = 8 +D = 64 +VOCAB = 256 + + +@pytest.fixture +def mock_latent_strategy(): + strategy = MagicMock() + # encode() returns a (5, D) tensor + strategy.encode = AsyncMock(return_value=torch.randn(5, D)) + # reason() is also awaited in generate_curriculum_batch() + strategy.reason = AsyncMock(return_value=torch.randn(5, D)) + return strategy + + +@pytest.fixture +def mock_adapter(): + adapter = MagicMock() + # forward() returns (K, D) + adapter.return_value = torch.randn(K, D) + return adapter + + +@pytest.fixture +def mock_tokenizer(): + tok = MagicMock() + # tokenize returns some fake ids + tok.encode = MagicMock(return_value=[1, 2, 3, 4, 5]) + tok.return_value = {"input_ids": torch.tensor([[1, 2, 3, 4, 5]])} + return tok + + +@pytest.fixture +def mock_embedding(): + """Fake embedding layer — maps token ID to D-dim vector.""" + emb = MagicMock() + emb.return_value = torch.randn(K, D) # returns (K, D) for any input + mock_param = MagicMock() + mock_param.device = torch.device("cpu") + emb.parameters.side_effect = lambda: iter([mock_param]) + return emb + + +@pytest.fixture +def mock_text_strategy(): + ts = MagicMock() + ts.reason = AsyncMock(return_value="This module handles routing logic.") + return ts + + +@pytest.fixture +def generator(mock_latent_strategy, mock_adapter, mock_tokenizer, mock_embedding, mock_text_strategy): + from libucks.thinking.training.data_generator import MultiPerspectiveDataGenerator + gen = MultiPerspectiveDataGenerator( + text_strategy=mock_text_strategy, + latent_strategy=mock_latent_strategy, + registry=MagicMock(), + store=MagicMock(), + ) + gen._store.read.return_value = ("id", "some prose about code") + gen._registry.get_all_centroids.return_value = {} + return gen + + +# ── generate_curriculum_batch() contract ──────────────────────────────────── + + +@pytest.mark.asyncio +async def test_batch_returns_mixed_input_and_target_ids( + generator, mock_adapter, mock_tokenizer, mock_embedding +): + """generate_curriculum_batch() returns a dict with 'mixed_input' and 'target_ids'.""" + result = await generator.generate_curriculum_batch( + bucket_id="test_bucket", + adapter=mock_adapter, + tokenizer=mock_tokenizer, + embedding=mock_embedding, + output_len=K, + hidden_dim=D, + ) + assert "mixed_input" in result + assert "target_ids" in result + + +@pytest.mark.asyncio +async def test_mixed_input_shape( + generator, mock_adapter, mock_tokenizer, mock_embedding +): + """mixed_input has shape (K, D).""" + result = await generator.generate_curriculum_batch( + bucket_id="test_bucket", + adapter=mock_adapter, + tokenizer=mock_tokenizer, + embedding=mock_embedding, + output_len=K, + hidden_dim=D, + ) + assert result["mixed_input"].shape == (K, D) + + +@pytest.mark.asyncio +async def test_target_ids_are_integer_tensor( + generator, mock_adapter, mock_tokenizer, mock_embedding +): + """target_ids must be a LongTensor (integer IDs, not floats).""" + result = await generator.generate_curriculum_batch( + bucket_id="test_bucket", + adapter=mock_adapter, + tokenizer=mock_tokenizer, + embedding=mock_embedding, + output_len=K, + hidden_dim=D, + ) + ids = result["target_ids"] + assert isinstance(ids, torch.Tensor) + assert ids.dtype in (torch.long, torch.int32, torch.int64) + + +@pytest.mark.asyncio +async def test_r_in_result_is_float_in_range( + generator, mock_adapter, mock_tokenizer, mock_embedding +): + """Result must include 'r' — a float in [0, 1].""" + result = await generator.generate_curriculum_batch( + bucket_id="test_bucket", + adapter=mock_adapter, + tokenizer=mock_tokenizer, + embedding=mock_embedding, + output_len=K, + hidden_dim=D, + ) + assert "r" in result + r = result["r"] + assert isinstance(r, float) + assert 0.0 <= r <= 1.0 + + +@pytest.mark.asyncio +async def test_r_sampled_uniformly_across_calls( + generator, mock_adapter, mock_tokenizer, mock_embedding +): + """Across many calls, r values should span the range [0, 1] (not all same).""" + rs = [] + for _ in range(50): + result = await generator.generate_curriculum_batch( + bucket_id="test_bucket", + adapter=mock_adapter, + tokenizer=mock_tokenizer, + embedding=mock_embedding, + output_len=K, + hidden_dim=D, + ) + rs.append(result["r"]) + + # With 50 samples from U[0,1], max - min should be > 0.3 with overwhelming probability + assert max(rs) - min(rs) > 0.3, "r values appear to not be sampled uniformly" diff --git a/tests/unit/test_curriculum_data.py b/tests/unit/test_curriculum_data.py index 70fd301..01cde78 100644 --- a/tests/unit/test_curriculum_data.py +++ b/tests/unit/test_curriculum_data.py @@ -24,6 +24,8 @@ def mock_latent_strategy(): strategy = MagicMock() # encode() returns a (5, D) tensor strategy.encode = AsyncMock(return_value=torch.randn(5, D)) + # reason() is also awaited in generate_curriculum_batch() + strategy.reason = AsyncMock(return_value=torch.randn(5, D)) return strategy @@ -49,6 +51,9 @@ def mock_embedding(): """Fake embedding layer — maps token ID to D-dim vector.""" emb = MagicMock() emb.return_value = torch.randn(K, D) # returns (K, D) for any input + mock_param = MagicMock() + mock_param.device = torch.device("cpu") + emb.parameters.side_effect = lambda: iter([mock_param]) return emb diff --git a/training.log b/training.log new file mode 100644 index 0000000..0bf830c --- /dev/null +++ b/training.log @@ -0,0 +1,63 @@ +nohup: ignoring input + Building libucks @ file:///workspace/libucks + Built libucks @ file:///workspace/libucks +Uninstalled 4 packages in 1.93s +Installed 4 packages in 1.72s +Warning: You are sending unauthenticated requests to the HF Hub. Please set a HF_TOKEN to enable higher rate limits and faster downloads. +[libucks] Basic MSE training on 148 buckets for 5 epoch(s)... +Traceback (most recent call last): + File "/workspace/libucks/.venv/bin/libucks", line 10, in + sys.exit(cli()) + ^^^^^ + File "/workspace/libucks/.venv/lib/python3.12/site-packages/click/core.py", line 1485, in __call__ + return self.main(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/workspace/libucks/.venv/lib/python3.12/site-packages/click/core.py", line 1406, in main + rv = self.invoke(ctx) + ^^^^^^^^^^^^^^^^ + File "/workspace/libucks/.venv/lib/python3.12/site-packages/click/core.py", line 1873, in invoke + return _process_result(sub_ctx.command.invoke(sub_ctx)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/workspace/libucks/.venv/lib/python3.12/site-packages/click/core.py", line 1269, in invoke + return ctx.invoke(self.callback, **ctx.params) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/workspace/libucks/.venv/lib/python3.12/site-packages/click/core.py", line 824, in invoke + return callback(*args, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/workspace/libucks/libucks/_cli.py", line 88, in train_adapter_cmd + asyncio.run(_run_train_adapter(target, creative=creative, epochs=epochs)) + File "/root/.local/share/uv/python/cpython-3.12.13-linux-x86_64-gnu/lib/python3.12/asyncio/runners.py", line 195, in run + return runner.run(main) + ^^^^^^^^^^^^^^^^ + File "/root/.local/share/uv/python/cpython-3.12.13-linux-x86_64-gnu/lib/python3.12/asyncio/runners.py", line 118, in run + return self._loop.run_until_complete(task) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/root/.local/share/uv/python/cpython-3.12.13-linux-x86_64-gnu/lib/python3.12/asyncio/base_events.py", line 691, in run_until_complete + return future.result() + ^^^^^^^^^^^^^^^ + File "/workspace/libucks/libucks/_cli.py", line 140, in _run_train_adapter + await _train_basic(cfg, registry, store, bucket_ids, adapter, epochs, bucket_dir) + File "/workspace/libucks/libucks/_cli.py", line 206, in _train_basic + latent_strategy = create_strategy(cfg) + ^^^^^^^^^^^^^^^^^^^^ + File "/workspace/libucks/libucks/thinking/__init__.py", line 18, in create_strategy + mgr.load( + File "/workspace/libucks/libucks/thinking/model_manager.py", line 71, in load + self._model = AutoModelForCausalLM.from_pretrained(model_id, **kwargs) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/workspace/libucks/.venv/lib/python3.12/site-packages/transformers/models/auto/auto_factory.py", line 381, in from_pretrained + return model_class.from_pretrained( + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/workspace/libucks/.venv/lib/python3.12/site-packages/transformers/modeling_utils.py", line 4091, in from_pretrained + hf_quantizer.preprocess_model( + File "/workspace/libucks/.venv/lib/python3.12/site-packages/transformers/quantizers/base.py", line 171, in preprocess_model + self._process_model_before_weight_loading(model, **kwargs) + File "/workspace/libucks/.venv/lib/python3.12/site-packages/transformers/quantizers/quantizer_bnb_4bit.py", line 138, in _process_model_before_weight_loading + model = replace_with_bnb_linear( + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "/workspace/libucks/.venv/lib/python3.12/site-packages/transformers/integrations/bitsandbytes.py", line 222, in replace_with_bnb_linear + model.set_submodule(module_name, new_module) + ^^^^^^^^^^^^^^^^^^^ + File "/workspace/libucks/.venv/lib/python3.12/site-packages/torch/nn/modules/module.py", line 1729, in __getattr__ + raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") +AttributeError: 'Qwen2ForCausalLM' object has no attribute 'set_submodule'. Did you mean: 'get_submodule'? diff --git a/uv.lock b/uv.lock index 44cd575..53bb91c 100644 --- a/uv.lock +++ b/uv.lock @@ -1,14 +1,31 @@ version = 1 revision = 3 requires-python = ">=3.11" +resolution-markers = [ + "sys_platform == 'linux'", + "platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')", +] [[package]] -name = "annotated-doc" -version = "0.0.4" +name = "accelerate" +version = "1.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +dependencies = [ + { name = "huggingface-hub" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyyaml" }, + { name = "safetensors" }, + { name = "torch", version = "2.2.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, + { name = "torch", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "torch", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/14/787e5498cd062640f0f3d92ef4ae4063174f76f9afd29d13fc52a319daae/accelerate-1.13.0.tar.gz", hash = "sha256:d631b4e0f5b3de4aff2d7e9e6857d164810dfc3237d54d017f075122d057b236", size = 402835, upload-time = "2026-03-04T19:34:12.359Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, + { url = "https://files.pythonhosted.org/packages/7e/46/02ac5e262d4af18054b3e922b2baedbb2a03289ee792162de60a865defc5/accelerate-1.13.0-py3-none-any.whl", hash = "sha256:cf1a3efb96c18f7b152eb0fa7490f3710b19c3f395699358f08decca2b8b62e0", size = 383744, upload-time = "2026-03-04T19:34:10.313Z" }, ] [[package]] @@ -61,6 +78,40 @@ 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 = "bitsandbytes" +version = "0.47.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", +] +dependencies = [ + { name = "numpy", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, + { name = "torch", version = "2.2.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, +] + +[[package]] +name = "bitsandbytes" +version = "0.49.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "sys_platform == 'linux'", + "platform_machine == 'arm64' and sys_platform == 'darwin'", + "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "numpy", marker = "python_full_version >= '3.13' or platform_machine != 'x86_64' or sys_platform != 'darwin'" }, + { name = "packaging", marker = "python_full_version >= '3.13' or platform_machine != 'x86_64' or sys_platform != 'darwin'" }, + { name = "torch", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "torch", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/7d/f1fe0992334b18cd8494f89aeec1dcc674635584fcd9f115784fea3a1d05/bitsandbytes-0.49.2-py3-none-macosx_14_0_arm64.whl", hash = "sha256:87be5975edeac5396d699ecbc39dfc47cf2c026daaf2d5852a94368611a6823f", size = 131940, upload-time = "2026-02-16T21:26:04.572Z" }, + { url = "https://files.pythonhosted.org/packages/29/71/acff7af06c818664aa87ff73e17a52c7788ad746b72aea09d3cb8e424348/bitsandbytes-0.49.2-py3-none-manylinux_2_24_aarch64.whl", hash = "sha256:2fc0830c5f7169be36e60e11f2be067c8f812dfcb829801a8703735842450750", size = 31442815, upload-time = "2026-02-16T21:26:06.783Z" }, + { url = "https://files.pythonhosted.org/packages/19/57/3443d6f183436fbdaf5000aac332c4d5ddb056665d459244a5608e98ae92/bitsandbytes-0.49.2-py3-none-manylinux_2_24_x86_64.whl", hash = "sha256:54b771f06e1a3c73af5c7f16ccf0fc23a846052813d4b008d10cb6e017dd1c8c", size = 60651714, upload-time = "2026-02-16T21:26:11.579Z" }, + { url = "https://files.pythonhosted.org/packages/b6/d4/501655842ad6771fb077f576d78cbedb5445d15b1c3c91343ed58ca46f0e/bitsandbytes-0.49.2-py3-none-win_amd64.whl", hash = "sha256:2e0ddd09cd778155388023cbe81f00afbb7c000c214caef3ce83386e7144df7d", size = 55372289, upload-time = "2026-02-16T21:26:16.267Z" }, +] + [[package]] name = "certifi" version = "2026.2.25" @@ -140,6 +191,95 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, ] +[[package]] +name = "charset-normalizer" +version = "3.4.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/d7/b5b7020a0565c2e9fa8c09f4b5fa6232feb326b8c20081ccded47ea368fd/charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7", size = 309705, upload-time = "2026-04-02T09:26:02.191Z" }, + { url = "https://files.pythonhosted.org/packages/5a/53/58c29116c340e5456724ecd2fff4196d236b98f3da97b404bc5e51ac3493/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7", size = 206419, upload-time = "2026-04-02T09:26:03.583Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/e8146dc6591a37a00e5144c63f29fb7c97a734ea8a111190783c0e60ab63/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e", size = 227901, upload-time = "2026-04-02T09:26:04.738Z" }, + { url = "https://files.pythonhosted.org/packages/fb/73/77486c4cd58f1267bf17db420e930c9afa1b3be3fe8c8b8ebbebc9624359/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c", size = 222742, upload-time = "2026-04-02T09:26:06.36Z" }, + { url = "https://files.pythonhosted.org/packages/a1/fa/f74eb381a7d94ded44739e9d94de18dc5edc9c17fb8c11f0a6890696c0a9/charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df", size = 214061, upload-time = "2026-04-02T09:26:08.347Z" }, + { url = "https://files.pythonhosted.org/packages/dc/92/42bd3cefcf7687253fb86694b45f37b733c97f59af3724f356fa92b8c344/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265", size = 199239, upload-time = "2026-04-02T09:26:09.823Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/069e7184e2aa3b3cddc700e3dd267413dc259854adc3380421c805c6a17d/charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4", size = 210173, upload-time = "2026-04-02T09:26:10.953Z" }, + { url = "https://files.pythonhosted.org/packages/62/51/9d56feb5f2e7074c46f93e0ebdbe61f0848ee246e2f0d89f8e20b89ebb8f/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e", size = 209841, upload-time = "2026-04-02T09:26:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/d2/59/893d8f99cc4c837dda1fe2f1139079703deb9f321aabcb032355de13b6c7/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38", size = 200304, upload-time = "2026-04-02T09:26:13.711Z" }, + { url = "https://files.pythonhosted.org/packages/7d/1d/ee6f3be3464247578d1ed5c46de545ccc3d3ff933695395c402c21fa6b77/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c", size = 229455, upload-time = "2026-04-02T09:26:14.941Z" }, + { url = "https://files.pythonhosted.org/packages/54/bb/8fb0a946296ea96a488928bdce8ef99023998c48e4713af533e9bb98ef07/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b", size = 210036, upload-time = "2026-04-02T09:26:16.478Z" }, + { url = "https://files.pythonhosted.org/packages/9a/bc/015b2387f913749f82afd4fcba07846d05b6d784dd16123cb66860e0237d/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c", size = 224739, upload-time = "2026-04-02T09:26:17.751Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/63133691f56baae417493cba6b7c641571a2130eb7bceba6773367ab9ec5/charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d", size = 216277, upload-time = "2026-04-02T09:26:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/06/6d/3be70e827977f20db77c12a97e6a9f973631a45b8d186c084527e53e77a4/charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad", size = 147819, upload-time = "2026-04-02T09:26:20.295Z" }, + { url = "https://files.pythonhosted.org/packages/20/d9/5f67790f06b735d7c7637171bbfd89882ad67201891b7275e51116ed8207/charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00", size = 159281, upload-time = "2026-04-02T09:26:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/ca/83/6413f36c5a34afead88ce6f66684d943d91f233d76dd083798f9602b75ae/charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1", size = 147843, upload-time = "2026-04-02T09:26:22.901Z" }, + { url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" }, + { url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" }, + { url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" }, + { url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" }, + { url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" }, + { url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" }, + { url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" }, + { url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" }, + { url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" }, + { url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" }, + { url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, +] + [[package]] name = "click" version = "8.3.1" @@ -220,77 +360,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1a/89/843b53614b47f97fe1abc13f9a86efa5ec9e275292c457af1d4a60dc80e0/cryptography-46.0.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6728c49e3b2c180ef26f8e9f0a883a2c585638db64cf265b49c9ba10652d430e", size = 3409955, upload-time = "2026-03-25T23:34:48.465Z" }, ] -[[package]] -name = "cuda-bindings" -version = "13.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cuda-pathfinder" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/a9/3a8241c6e19483ac1f1dcf5c10238205dcb8a6e9d0d4d4709240dff28ff4/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:721104c603f059780d287969be3d194a18d0cc3b713ed9049065a1107706759d", size = 5730273, upload-time = "2026-03-11T00:12:37.18Z" }, - { url = "https://files.pythonhosted.org/packages/e9/94/2748597f47bb1600cd466b20cab4159f1530a3a33fe7f70fee199b3abb9e/cuda_bindings-13.2.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1eba9504ac70667dd48313395fe05157518fd6371b532790e96fbb31bbb5a5e1", size = 6313924, upload-time = "2026-03-11T00:12:39.462Z" }, - { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, - { url = "https://files.pythonhosted.org/packages/1f/92/f899f7bbb5617bb65ec52a6eac1e9a1447a86b916c4194f8a5001b8cde0c/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46d8776a55d6d5da9dd6e9858fba2efcda2abe6743871dee47dd06eb8cb6d955", size = 6320619, upload-time = "2026-03-11T00:12:45.939Z" }, - { url = "https://files.pythonhosted.org/packages/df/93/eef988860a3ca985f82c4f3174fc0cdd94e07331ba9a92e8e064c260337f/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6629ca2df6f795b784752409bcaedbd22a7a651b74b56a165ebc0c9dcbd504d0", size = 5614610, upload-time = "2026-03-11T00:12:50.337Z" }, - { url = "https://files.pythonhosted.org/packages/18/23/6db3aba46864aee357ab2415135b3fe3da7e9f1fa0221fa2a86a5968099c/cuda_bindings-13.2.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dca0da053d3b4cc4869eff49c61c03f3c5dbaa0bcd712317a358d5b8f3f385d", size = 6149914, upload-time = "2026-03-11T00:12:52.374Z" }, - { url = "https://files.pythonhosted.org/packages/c0/87/87a014f045b77c6de5c8527b0757fe644417b184e5367db977236a141602/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6464b30f46692d6c7f65d4a0e0450d81dd29de3afc1bb515653973d01c2cd6e", size = 5685673, upload-time = "2026-03-11T00:12:56.371Z" }, - { url = "https://files.pythonhosted.org/packages/ee/5e/c0fe77a73aaefd3fff25ffaccaac69c5a63eafdf8b9a4c476626ef0ac703/cuda_bindings-13.2.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4af9f3e1be603fa12d5ad6cfca7844c9d230befa9792b5abdf7dd79979c3626", size = 6191386, upload-time = "2026-03-11T00:12:58.965Z" }, - { url = "https://files.pythonhosted.org/packages/5f/58/ed2c3b39c8dd5f96aa7a4abef0d47a73932c7a988e30f5fa428f00ed0da1/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df850a1ff8ce1b3385257b08e47b70e959932f5f432d0a4e46a355962b4e4771", size = 5507469, upload-time = "2026-03-11T00:13:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/1f/01/0c941b112ceeb21439b05895eace78ca1aa2eaaf695c8521a068fd9b4c00/cuda_bindings-13.2.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8a16384c6494e5485f39314b0b4afb04bee48d49edb16d5d8593fd35bbd231b", size = 6059693, upload-time = "2026-03-11T00:13:06.003Z" }, -] - -[[package]] -name = "cuda-pathfinder" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/66/0c02bd330e7d976f83fa68583d6198d76f23581bcbb5c0e98a6148f326e5/cuda_pathfinder-1.5.0-py3-none-any.whl", hash = "sha256:498f90a9e9de36044a7924742aecce11c50c49f735f1bc53e05aa46de9ea4110", size = 49739, upload-time = "2026-03-24T21:14:30.869Z" }, -] - -[[package]] -name = "cuda-toolkit" -version = "13.0.2" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/57/b2/453099f5f3b698d7d0eab38916aac44c7f76229f451709e2eb9db6615dcd/cuda_toolkit-13.0.2-py2.py3-none-any.whl", hash = "sha256:b198824cf2f54003f50d64ada3a0f184b42ca0846c1c94192fa269ecd97a66eb", size = 2364, upload-time = "2025-12-19T23:24:07.328Z" }, -] - -[package.optional-dependencies] -cublas = [ - { name = "nvidia-cublas", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cudart = [ - { name = "nvidia-cuda-runtime", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cufft = [ - { name = "nvidia-cufft", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cufile = [ - { name = "nvidia-cufile", marker = "sys_platform == 'linux'" }, -] -cupti = [ - { name = "nvidia-cuda-cupti", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -curand = [ - { name = "nvidia-curand", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cusolver = [ - { name = "nvidia-cusolver", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -cusparse = [ - { name = "nvidia-cusparse", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -nvjitlink = [ - { name = "nvidia-nvjitlink", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -nvrtc = [ - { name = "nvidia-cuda-nvrtc", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] -nvtx = [ - { name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, -] - [[package]] name = "distro" version = "1.9.0" @@ -431,22 +500,21 @@ wheels = [ [[package]] name = "huggingface-hub" -version = "1.8.0" +version = "0.36.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, { name = "fsspec" }, - { name = "hf-xet", marker = "platform_machine == 'AMD64' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, - { name = "httpx" }, + { name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" }, { name = "packaging" }, { name = "pyyaml" }, + { name = "requests" }, { name = "tqdm" }, - { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8e/2a/a847fd02261cd051da218baf99f90ee7c7040c109a01833db4f838f25256/huggingface_hub-1.8.0.tar.gz", hash = "sha256:c5627b2fd521e00caf8eff4ac965ba988ea75167fad7ee72e17f9b7183ec63f3", size = 735839, upload-time = "2026-03-25T16:01:28.152Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/b7/8cb61d2eece5fb05a83271da168186721c450eb74e3c31f7ef3169fa475b/huggingface_hub-0.36.2.tar.gz", hash = "sha256:1934304d2fb224f8afa3b87007d58501acfda9215b334eed53072dd5e815ff7a", size = 649782, upload-time = "2026-02-06T09:24:13.098Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/ae/8a3a16ea4d202cb641b51d2681bdd3d482c1c592d7570b3fa264730829ce/huggingface_hub-1.8.0-py3-none-any.whl", hash = "sha256:d3eb5047bd4e33c987429de6020d4810d38a5bef95b3b40df9b17346b7f353f2", size = 625208, upload-time = "2026-03-25T16:01:26.603Z" }, + { url = "https://files.pythonhosted.org/packages/a8/af/48ac8483240de756d2438c380746e7130d1c6f75802ef22f3c6d49982787/huggingface_hub-0.36.2-py3-none-any.whl", hash = "sha256:48f0c8eac16145dfce371e9d2d7772854a4f591bcb56c9cf548accf531d54270", size = 566395, upload-time = "2026-02-06T09:24:11.133Z" }, ] [[package]] @@ -606,6 +674,8 @@ version = "0.1.0" source = { editable = "." } dependencies = [ { name = "anthropic" }, + { name = "bitsandbytes", version = "0.47.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, + { name = "bitsandbytes", version = "0.49.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13' or platform_machine != 'x86_64' or sys_platform != 'darwin'" }, { name = "click" }, { name = "gitpython" }, { name = "httpx" }, @@ -618,6 +688,7 @@ dependencies = [ { name = "scipy" }, { name = "sentence-transformers" }, { name = "structlog" }, + { name = "transformers" }, { name = "tree-sitter" }, { name = "unidiff" }, { name = "watchdog" }, @@ -631,10 +702,21 @@ dev = [ { name = "pytest-timeout" }, { name = "respx" }, ] +latent = [ + { name = "accelerate" }, + { name = "bitsandbytes", version = "0.49.2", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "torch", version = "2.2.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, + { name = "torch", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "transformers" }, +] [package.metadata] requires-dist = [ + { name = "accelerate", marker = "extra == 'latent'", specifier = ">=0.28" }, { name = "anthropic", specifier = ">=0.40" }, + { name = "bitsandbytes", specifier = ">=0.46.1" }, + { name = "bitsandbytes", marker = "platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'latent'", specifier = ">=0.43" }, + { name = "bitsandbytes", marker = "sys_platform == 'linux' and extra == 'latent'", specifier = ">=0.43" }, { name = "click", specifier = ">=8.1" }, { name = "gitpython", specifier = ">=3.1" }, { name = "httpx", specifier = ">=0.27" }, @@ -652,11 +734,15 @@ requires-dist = [ { name = "scipy", specifier = ">=1.13" }, { name = "sentence-transformers", specifier = ">=3.0" }, { name = "structlog", specifier = ">=24.0" }, + { name = "torch", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'latent') or (sys_platform == 'linux' and extra == 'latent')", specifier = "==2.4.1" }, + { name = "torch", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin' and extra == 'latent'", specifier = ">=2.2,<=2.2.2" }, + { name = "transformers", specifier = "==4.48.2" }, + { name = "transformers", marker = "extra == 'latent'", specifier = ">=4.40" }, { name = "tree-sitter", specifier = ">=0.22" }, { name = "unidiff", specifier = ">=0.7" }, { name = "watchdog", specifier = ">=4.0" }, ] -provides-extras = ["dev"] +provides-extras = ["latent", "dev"] [[package]] name = "markdown-it-py" @@ -876,152 +962,110 @@ wheels = [ ] [[package]] -name = "nvidia-cublas" -version = "13.1.0.3" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/a5/fce49e2ae977e0ccc084e5adafceb4f0ac0c8333cb6863501618a7277f67/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c86fc7f7ae36d7528288c5d88098edcb7b02c633d262e7ddbb86b0ad91be5df2", size = 542851226, upload-time = "2025-10-09T08:59:04.818Z" }, - { url = "https://files.pythonhosted.org/packages/e7/44/423ac00af4dd95a5aeb27207e2c0d9b7118702149bf4704c3ddb55bb7429/nvidia_cublas-13.1.0.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:ee8722c1f0145ab246bccb9e452153b5e0515fd094c3678df50b2a0888b8b171", size = 423133236, upload-time = "2025-10-09T08:59:32.536Z" }, -] - -[[package]] -name = "nvidia-cuda-cupti" -version = "13.0.85" +name = "nvidia-cublas-cu12" +version = "12.1.3.1" 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" }, + { url = "https://files.pythonhosted.org/packages/37/6d/121efd7382d5b0284239f4ab1fc1590d86d34ed4a4a2fdb13b30ca8e5740/nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:ee53ccca76a6fc08fb9701aa95b6ceb242cdaab118c3bb152af4e579af792728", size = 410594774, upload-time = "2023-04-19T15:50:03.519Z" }, ] [[package]] -name = "nvidia-cuda-nvrtc" -version = "13.0.88" +name = "nvidia-cuda-cupti-cu12" +version = "12.1.105" 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" }, + { url = "https://files.pythonhosted.org/packages/7e/00/6b218edd739ecfc60524e585ba8e6b00554dd908de2c9c66c1af3e44e18d/nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:e54fde3983165c624cb79254ae9818a456eb6e87a7fd4d56a2352c24ee542d7e", size = 14109015, upload-time = "2023-04-19T15:47:32.502Z" }, ] [[package]] -name = "nvidia-cuda-runtime" -version = "13.0.96" +name = "nvidia-cuda-nvrtc-cu12" +version = "12.1.105" 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" }, + { url = "https://files.pythonhosted.org/packages/b6/9f/c64c03f49d6fbc56196664d05dba14e3a561038a81a638eeb47f4d4cfd48/nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:339b385f50c309763ca65456ec75e17bbefcbbf2893f462cb8b90584cd27a1c2", size = 23671734, upload-time = "2023-04-19T15:48:32.42Z" }, ] [[package]] -name = "nvidia-cudnn-cu13" -version = "9.19.0.56" +name = "nvidia-cuda-runtime-cu12" +version = "12.1.105" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nvidia-cublas" }, -] wheels = [ - { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" }, - { url = "https://files.pythonhosted.org/packages/a3/22/0b4b932655d17a6da1b92fa92ab12844b053bb2ac2475e179ba6f043da1e/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:d20e1734305e9d68889a96e3f35094d733ff1f83932ebe462753973e53a572bf", size = 366066321, upload-time = "2026-02-03T20:44:52.837Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d5/c68b1d2cdfcc59e72e8a5949a37ddb22ae6cade80cd4a57a84d4c8b55472/nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:6e258468ddf5796e25f1dc591a31029fa317d97a0a94ed93468fc86301d61e40", size = 823596, upload-time = "2023-04-19T15:47:22.471Z" }, ] [[package]] -name = "nvidia-cufft" -version = "12.0.0.61" +name = "nvidia-cudnn-cu12" +version = "9.1.0.70" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink" }, + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, ] 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" }, + { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741, upload-time = "2024-04-22T15:24:15.253Z" }, ] [[package]] -name = "nvidia-cufile" -version = "1.15.1.6" +name = "nvidia-cufft-cu12" +version = "11.0.2.54" 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" }, + { url = "https://files.pythonhosted.org/packages/86/94/eb540db023ce1d162e7bea9f8f5aa781d57c65aed513c33ee9a5123ead4d/nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl", hash = "sha256:794e3948a1aa71fd817c3775866943936774d1c14e7628c74f6f7417224cdf56", size = 121635161, upload-time = "2023-04-19T15:50:46Z" }, ] [[package]] -name = "nvidia-curand" -version = "10.4.0.35" +name = "nvidia-curand-cu12" +version = "10.3.2.106" 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" }, + { url = "https://files.pythonhosted.org/packages/44/31/4890b1c9abc496303412947fc7dcea3d14861720642b49e8ceed89636705/nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:9d264c5036dde4e64f1de8c50ae753237c12e0b1348738169cd0f8a536c0e1e0", size = 56467784, upload-time = "2023-04-19T15:51:04.804Z" }, ] [[package]] -name = "nvidia-cusolver" -version = "12.0.4.66" +name = "nvidia-cusolver-cu12" +version = "11.4.5.107" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas" }, - { name = "nvidia-cusparse" }, - { name = "nvidia-nvjitlink" }, + { name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, ] 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" }, + { url = "https://files.pythonhosted.org/packages/bc/1d/8de1e5c67099015c834315e333911273a8c6aaba78923dd1d1e25fc5f217/nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl", hash = "sha256:8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd", size = 124161928, upload-time = "2023-04-19T15:51:25.781Z" }, ] [[package]] -name = "nvidia-cusparse" -version = "12.6.3.3" +name = "nvidia-cusparse-cu12" +version = "12.1.0.106" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink" }, + { name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" }, ] 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" }, + { url = "https://files.pythonhosted.org/packages/65/5b/cfaeebf25cd9fdec14338ccb16f6b2c4c7fa9163aefcf057d86b9cc248bb/nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c", size = 195958278, upload-time = "2023-04-19T15:51:49.939Z" }, ] [[package]] -name = "nvidia-cusparselt-cu13" -version = "0.8.0" +name = "nvidia-nccl-cu12" +version = "2.20.5" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/46/10/8dcd1175260706a2fc92a16a52e306b71d4c1ea0b0cc4a9484183399818a/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:400c6ed1cf6780fc6efedd64ec9f1345871767e6a1a0a552a1ea0578117ea77c", size = 220791277, upload-time = "2025-08-13T19:22:40.982Z" }, - { url = "https://files.pythonhosted.org/packages/fd/53/43b0d71f4e702fa9733f8b4571fdca50a8813f1e450b656c239beff12315/nvidia_cusparselt_cu13-0.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:25e30a8a7323935d4ad0340b95a0b69926eee755767e8e0b1cf8dd85b197d3fd", size = 169884119, upload-time = "2025-08-13T19:23:41.967Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2a/0a131f572aa09f741c30ccd45a8e56316e8be8dfc7bc19bf0ab7cfef7b19/nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:057f6bf9685f75215d0c53bf3ac4a10b3e6578351de307abad9e18a99182af56", size = 176249402, upload-time = "2024-03-06T04:30:20.663Z" }, ] [[package]] -name = "nvidia-nccl-cu13" -version = "2.28.9" +name = "nvidia-nvjitlink-cu12" +version = "12.9.86" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/55/1920646a2e43ffd4fc958536b276197ed740e9e0c54105b4bb3521591fc7/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_aarch64.whl", hash = "sha256:01c873ba1626b54caa12272ed228dc5b2781545e0ae8ba3f432a8ef1c6d78643", size = 196561677, upload-time = "2025-11-18T05:49:03.45Z" }, - { url = "https://files.pythonhosted.org/packages/b0/b4/878fefaad5b2bcc6fcf8d474a25e3e3774bc5133e4b58adff4d0bca238bc/nvidia_nccl_cu13-2.28.9-py3-none-manylinux_2_18_x86_64.whl", hash = "sha256:e4553a30f34195f3fa1da02a6da3d6337d28f2003943aa0a3d247bbc25fefc42", size = 196493177, upload-time = "2025-11-18T05:49:17.677Z" }, + { url = "https://files.pythonhosted.org/packages/46/0c/c75bbfb967457a0b7670b8ad267bfc4fffdf341c074e0a80db06c24ccfd4/nvidia_nvjitlink_cu12-12.9.86-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:e3f1171dbdc83c5932a45f0f4c99180a70de9bd2718c1ab77d14104f6d7147f9", size = 39748338, upload-time = "2025-06-05T20:10:25.613Z" }, ] [[package]] -name = "nvidia-nvjitlink" -version = "13.0.88" +name = "nvidia-nvtx-cu12" +version = "12.1.105" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/56/7a/123e033aaff487c77107195fa5a2b8686795ca537935a24efae476c41f05/nvidia_nvjitlink-13.0.88-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:13a74f429e23b921c1109976abefacc69835f2f433ebd323d3946e11d804e47b", size = 40713933, upload-time = "2025-09-04T08:35:43.553Z" }, - { url = "https://files.pythonhosted.org/packages/ab/2c/93c5250e64df4f894f1cbb397c6fd71f79813f9fd79d7cd61de3f97b3c2d/nvidia_nvjitlink-13.0.88-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e931536ccc7d467a98ba1d8b89ff7fa7f1fa3b13f2b0069118cd7f47bff07d0c", size = 38768748, upload-time = "2025-09-04T08:35:20.008Z" }, -] - -[[package]] -name = "nvidia-nvshmem-cu13" -version = "3.4.5" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/0f/05cc9c720236dcd2db9c1ab97fff629e96821be2e63103569da0c9b72f19/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dc2a197f38e5d0376ad52cd1a2a3617d3cdc150fd5966f4aee9bcebb1d68fe9", size = 60215947, upload-time = "2025-09-06T00:32:20.022Z" }, - { url = "https://files.pythonhosted.org/packages/3c/35/a9bf80a609e74e3b000fef598933235c908fcefcef9026042b8e6dfde2a9/nvidia_nvshmem_cu13-3.4.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:290f0a2ee94c9f3687a02502f3b9299a9f9fe826e6d0287ee18482e78d495b80", size = 60412546, upload-time = "2025-09-06T00:32:41.564Z" }, -] - -[[package]] -name = "nvidia-nvtx" -version = "13.0.85" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/f3/d86c845465a2723ad7e1e5c36dcd75ddb82898b3f53be47ebd429fb2fa5d/nvidia_nvtx-13.0.85-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4936d1d6780fbe68db454f5e72a42ff64d1fd6397df9f363ae786930fd5c1cd4", size = 148047, upload-time = "2025-09-04T08:29:01.761Z" }, - { url = "https://files.pythonhosted.org/packages/a8/64/3708a90d1ebe202ffdeb7185f878a3c84d15c2b2c31858da2ce0583e2def/nvidia_nvtx-13.0.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb7780edb6b14107373c835bf8b72e7a178bac7367e23da7acb108f973f157a6", size = 148878, upload-time = "2025-09-04T08:28:53.627Z" }, + { url = "https://files.pythonhosted.org/packages/da/d3/8057f0587683ed2fcd4dbfbdfdfa807b9160b809976099d36b8f60d08f03/nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:dc21cf308ca5691e7c04d962e213f8a4aa9bbfa23d95412f452254c2caeb09e5", size = 99138, upload-time = "2023-04-19T15:48:43.556Z" }, ] [[package]] @@ -1042,6 +1086,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" }, + { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" }, + { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" }, + { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" }, + { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" }, + { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" }, + { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" }, + { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" }, + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + [[package]] name = "pycparser" version = "3.0" @@ -1451,6 +1523,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/91/043d9a00d6123c5fa22a3dc96b10445ce434a8110e1d5e53efb01f243c8b/regex-2026.3.32-cp314-cp314t-win_arm64.whl", hash = "sha256:1a6ac1ed758902e664e0d95c1ee5991aa6fb355423f378ed184c6ec47a1ec0e9", size = 275700, upload-time = "2026-03-28T21:49:19.348Z" }, ] +[[package]] +name = "requests" +version = "2.33.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, +] + [[package]] name = "respx" version = "0.22.0" @@ -1736,7 +1823,9 @@ dependencies = [ { name = "numpy" }, { name = "scikit-learn" }, { name = "scipy" }, - { name = "torch" }, + { name = "torch", version = "2.2.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, + { name = "torch", version = "2.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "torch", version = "2.11.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, { name = "tqdm" }, { name = "transformers" }, { name = "typing-extensions" }, @@ -1755,15 +1844,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, ] -[[package]] -name = "shellingham" -version = "1.5.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, -] - [[package]] name = "smmap" version = "5.0.3" @@ -1840,74 +1920,114 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.22.2" +version = "0.21.4" 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" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/2f/402986d0823f8d7ca139d969af2917fefaa9b947d1fb32f6168c509f2492/tokenizers-0.21.4.tar.gz", hash = "sha256:fa23f85fbc9a02ec5c6978da172cdcbac23498c3ca9f3645c5c68740ac007880", size = 351253, upload-time = "2025-07-28T15:48:54.325Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, - { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, - { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, - { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, - { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, - { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, - { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, - { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, - { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, - { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, - { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, - { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, - { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, - { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, - { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, + { url = "https://files.pythonhosted.org/packages/98/c6/fdb6f72bf6454f52eb4a2510be7fb0f614e541a2554d6210e370d85efff4/tokenizers-0.21.4-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2ccc10a7c3bcefe0f242867dc914fc1226ee44321eb618cfe3019b5df3400133", size = 2863987, upload-time = "2025-07-28T15:48:44.877Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a6/28975479e35ddc751dc1ddc97b9b69bf7fcf074db31548aab37f8116674c/tokenizers-0.21.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5e2f601a8e0cd5be5cc7506b20a79112370b9b3e9cb5f13f68ab11acd6ca7d60", size = 2732457, upload-time = "2025-07-28T15:48:43.265Z" }, + { url = "https://files.pythonhosted.org/packages/aa/8f/24f39d7b5c726b7b0be95dca04f344df278a3fe3a4deb15a975d194cbb32/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b376f5a1aee67b4d29032ee85511bbd1b99007ec735f7f35c8a2eb104eade5", size = 3012624, upload-time = "2025-07-28T13:22:43.895Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/26358925717687a58cb74d7a508de96649544fad5778f0cd9827398dc499/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2107ad649e2cda4488d41dfd031469e9da3fcbfd6183e74e4958fa729ffbf9c6", size = 2939681, upload-time = "2025-07-28T13:22:47.499Z" }, + { url = "https://files.pythonhosted.org/packages/99/6f/cc300fea5db2ab5ddc2c8aea5757a27b89c84469899710c3aeddc1d39801/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c73012da95afafdf235ba80047699df4384fdc481527448a078ffd00e45a7d9", size = 3247445, upload-time = "2025-07-28T15:48:39.711Z" }, + { url = "https://files.pythonhosted.org/packages/be/bf/98cb4b9c3c4afd8be89cfa6423704337dc20b73eb4180397a6e0d456c334/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f23186c40395fc390d27f519679a58023f368a0aad234af145e0f39ad1212732", size = 3428014, upload-time = "2025-07-28T13:22:49.569Z" }, + { url = "https://files.pythonhosted.org/packages/75/c7/96c1cc780e6ca7f01a57c13235dd05b7bc1c0f3588512ebe9d1331b5f5ae/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc88bb34e23a54cc42713d6d98af5f1bf79c07653d24fe984d2d695ba2c922a2", size = 3193197, upload-time = "2025-07-28T13:22:51.471Z" }, + { url = "https://files.pythonhosted.org/packages/f2/90/273b6c7ec78af547694eddeea9e05de771278bd20476525ab930cecaf7d8/tokenizers-0.21.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51b7eabb104f46c1c50b486520555715457ae833d5aee9ff6ae853d1130506ff", size = 3115426, upload-time = "2025-07-28T15:48:41.439Z" }, + { url = "https://files.pythonhosted.org/packages/91/43/c640d5a07e95f1cf9d2c92501f20a25f179ac53a4f71e1489a3dcfcc67ee/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:714b05b2e1af1288bd1bc56ce496c4cebb64a20d158ee802887757791191e6e2", size = 9089127, upload-time = "2025-07-28T15:48:46.472Z" }, + { url = "https://files.pythonhosted.org/packages/44/a1/dd23edd6271d4dca788e5200a807b49ec3e6987815cd9d0a07ad9c96c7c2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:1340ff877ceedfa937544b7d79f5b7becf33a4cfb58f89b3b49927004ef66f78", size = 9055243, upload-time = "2025-07-28T15:48:48.539Z" }, + { url = "https://files.pythonhosted.org/packages/21/2b/b410d6e9021c4b7ddb57248304dc817c4d4970b73b6ee343674914701197/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3c1f4317576e465ac9ef0d165b247825a2a4078bcd01cba6b54b867bdf9fdd8b", size = 9298237, upload-time = "2025-07-28T15:48:50.443Z" }, + { url = "https://files.pythonhosted.org/packages/b7/0a/42348c995c67e2e6e5c89ffb9cfd68507cbaeb84ff39c49ee6e0a6dd0fd2/tokenizers-0.21.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:c212aa4e45ec0bb5274b16b6f31dd3f1c41944025c2358faaa5782c754e84c24", size = 9461980, upload-time = "2025-07-28T15:48:52.325Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d3/dacccd834404cd71b5c334882f3ba40331ad2120e69ded32cf5fda9a7436/tokenizers-0.21.4-cp39-abi3-win32.whl", hash = "sha256:6c42a930bc5f4c47f4ea775c91de47d27910881902b0f20e4990ebe045a415d0", size = 2329871, upload-time = "2025-07-28T15:48:56.841Z" }, + { url = "https://files.pythonhosted.org/packages/41/f2/fd673d979185f5dcbac4be7d09461cbb99751554ffb6718d0013af8604cb/tokenizers-0.21.4-cp39-abi3-win_amd64.whl", hash = "sha256:475d807a5c3eb72c59ad9b5fcdb254f6e17f53dfcbb9903233b0dfa9c943b597", size = 2507568, upload-time = "2025-07-28T15:48:55.456Z" }, +] + +[[package]] +name = "torch" +version = "2.2.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", +] +dependencies = [ + { name = "filelock", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, + { name = "fsspec", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, + { name = "jinja2", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, + { name = "networkx", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, + { name = "sympy", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/14/e105b8ef6d324e789c1589e95cb0ab63f3e07c2216d68b1178b7c21b7d2a/torch-2.2.2-cp311-none-macosx_10_9_x86_64.whl", hash = "sha256:95b9b44f3bcebd8b6cd8d37ec802048c872d9c567ba52c894bba90863a439059", size = 150796474, upload-time = "2024-03-27T21:09:29.142Z" }, + { url = "https://files.pythonhosted.org/packages/79/78/29dcab24a344ffd9ee9549ec0ab2c7885c13df61cde4c65836ee275efaeb/torch-2.2.2-cp312-none-macosx_10_9_x86_64.whl", hash = "sha256:eb4d6e9d3663e26cd27dc3ad266b34445a16b54908e74725adb241aa56987533", size = 150797270, upload-time = "2024-03-27T21:08:29.623Z" }, +] + +[[package]] +name = "torch" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "sys_platform == 'linux'", + "platform_machine == 'arm64' and sys_platform == 'darwin'", +] +dependencies = [ + { name = "filelock", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "fsspec", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "jinja2", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "networkx", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "sympy", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "triton", marker = "python_full_version < '3.13' and platform_machine == 'x86_64' and sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/ea/4ab009e953bca6ff35ad75b8ab58c0923308636c182c145dc63084f7d136/torch-2.4.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:0b5f88afdfa05a335d80351e3cea57d38e578c8689f751d35e0ff36bce872113", size = 797111232, upload-time = "2024-09-04T19:14:13.409Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a1/b31f94b4631c1731261db9fdc9a749ef58facc3b76094a6fe974f611f239/torch-2.4.1-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:ef503165f2341942bfdf2bd520152f19540d0c0e34961232f134dc59ad435be8", size = 89719574, upload-time = "2024-09-04T19:13:24.64Z" }, + { url = "https://files.pythonhosted.org/packages/1f/34/c93873c37f93154d982172755f7e504fdbae6c760499303a3111ce6ce327/torch-2.4.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:ddddbd8b066e743934a4200b3d54267a46db02106876d21cf31f7da7a96f98ea", size = 62145176, upload-time = "2024-09-04T19:13:29.897Z" }, + { url = "https://files.pythonhosted.org/packages/cc/df/5204a13a7a973c23c7ade615bafb1a3112b5d0ec258d8390f078fa4ab0f7/torch-2.4.1-cp312-cp312-manylinux1_x86_64.whl", hash = "sha256:fdc4fe11db3eb93c1115d3e973a27ac7c1a8318af8934ffa36b0370efe28e042", size = 797019590, upload-time = "2024-09-04T19:10:42.948Z" }, + { url = "https://files.pythonhosted.org/packages/4f/16/d23a689e5ef8001ed2ace1a3a59f2fda842889b0c3f3877799089925282a/torch-2.4.1-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:18835374f599207a9e82c262153c20ddf42ea49bc76b6eadad8e5f49729f6e4d", size = 89613802, upload-time = "2024-09-04T19:13:19.273Z" }, + { url = "https://files.pythonhosted.org/packages/ac/30/8b6f77ea4ce84f015ee024b8dfef0dac289396254e8bfd493906d4cbb848/torch-2.4.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:72b484d5b6cec1a735bf3fa5a1c4883d01748698c5e9cfdbeb4ffab7c7987e0d", size = 62123443, upload-time = "2024-09-04T19:12:59.242Z" }, ] [[package]] name = "torch" version = "2.11.0" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')", +] 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" }, + { name = "filelock", marker = "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "fsspec", marker = "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "jinja2", marker = "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "networkx", marker = "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "setuptools", marker = "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "sympy", marker = "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "typing-extensions", marker = "(python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine != 'arm64' and platform_machine != 'x86_64' and sys_platform == 'darwin') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ae/0d/98b410492609e34a155fa8b121b55c7dca229f39636851c3a9ec20edea21/torch-2.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7b6a60d48062809f58595509c524b88e6ddec3ebe25833d6462eeab81e5f2ce4", size = 80529712, upload-time = "2026-03-23T18:12:02.608Z" }, - { url = "https://files.pythonhosted.org/packages/84/03/acea680005f098f79fd70c1d9d5ccc0cb4296ec2af539a0450108232fc0c/torch-2.11.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:d91aac77f24082809d2c5a93f52a5f085032740a1ebc9252a7b052ef5a4fddc6", size = 419718178, upload-time = "2026-03-23T18:10:46.675Z" }, - { url = "https://files.pythonhosted.org/packages/8c/8b/d7be22fbec9ffee6cff31a39f8750d4b3a65d349a286cf4aec74c2375662/torch-2.11.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7aa2f9bbc6d4595ba72138026b2074be1233186150e9292865e04b7a63b8c67a", size = 530604548, upload-time = "2026-03-23T18:10:03.569Z" }, { url = "https://files.pythonhosted.org/packages/d1/bd/9912d30b68845256aabbb4a40aeefeef3c3b20db5211ccda653544ada4b6/torch-2.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:73e24aaf8f36ab90d95cd1761208b2eb70841c2a9ca1a3f9061b39fc5331b708", size = 114519675, upload-time = "2026-03-23T18:11:52.995Z" }, { url = "https://files.pythonhosted.org/packages/6f/8b/69e3008d78e5cee2b30183340cc425081b78afc5eff3d080daab0adda9aa/torch-2.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b5866312ee6e52ea625cd211dcb97d6a2cdc1131a5f15cc0d87eec948f6dd34", size = 80606338, upload-time = "2026-03-23T18:11:34.781Z" }, - { url = "https://files.pythonhosted.org/packages/13/16/42e5915ebe4868caa6bac83a8ed59db57f12e9a61b7d749d584776ed53d5/torch-2.11.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f99924682ef0aa6a4ab3b1b76f40dc6e273fca09f367d15a524266db100a723f", size = 419731115, upload-time = "2026-03-23T18:11:06.944Z" }, - { url = "https://files.pythonhosted.org/packages/1a/c9/82638ef24d7877510f83baf821f5619a61b45568ce21c0a87a91576510aa/torch-2.11.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:0f68f4ac6d95d12e896c3b7a912b5871619542ec54d3649cf48cc1edd4dd2756", size = 530712279, upload-time = "2026-03-23T18:10:31.481Z" }, { url = "https://files.pythonhosted.org/packages/1c/ff/6756f1c7ee302f6d202120e0f4f05b432b839908f9071157302cedfc5232/torch-2.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:fbf39280699d1b869f55eac536deceaa1b60bd6788ba74f399cc67e60a5fab10", size = 114556047, upload-time = "2026-03-23T18:10:55.931Z" }, { url = "https://files.pythonhosted.org/packages/87/89/5ea6722763acee56b045435fb84258db7375c48165ec8be7880ab2b281c5/torch-2.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6debd97ccd3205bbb37eb806a9d8219e1139d15419982c09e23ef7d4369d18", size = 80606801, upload-time = "2026-03-23T18:10:18.649Z" }, - { url = "https://files.pythonhosted.org/packages/32/d1/8ed2173589cbfe744ed54e5a73efc107c0085ba5777ee93a5f4c1ab90553/torch-2.11.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:63a68fa59de8f87acc7e85a5478bb2dddbb3392b7593ec3e78827c793c4b73fd", size = 419732382, upload-time = "2026-03-23T18:08:30.835Z" }, - { url = "https://files.pythonhosted.org/packages/3d/e1/b73f7c575a4b8f87a5928f50a1e35416b5e27295d8be9397d5293e7e8d4c/torch-2.11.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:cc89b9b173d9adfab59fd227f0ab5e5516d9a52b658ae41d64e59d2e55a418db", size = 530711509, upload-time = "2026-03-23T18:08:47.213Z" }, { url = "https://files.pythonhosted.org/packages/66/82/3e3fcdd388fbe54e29fd3f991f36846ff4ac90b0d0181e9c8f7236565f82/torch-2.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:4dda3b3f52d121063a731ddb835f010dc137b920d7fec2778e52f60d8e4bf0cd", size = 114555842, upload-time = "2026-03-23T18:09:52.111Z" }, { url = "https://files.pythonhosted.org/packages/db/38/8ac78069621b8c2b4979c2f96dc8409ef5e9c4189f6aac629189a78677ca/torch-2.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8b394322f49af4362d4f80e424bcaca7efcd049619af03a4cf4501520bdf0fb4", size = 80959574, upload-time = "2026-03-23T18:10:14.214Z" }, - { url = "https://files.pythonhosted.org/packages/6d/6c/56bfb37073e7136e6dd86bfc6af7339946dd684e0ecf2155ac0eee687ae1/torch-2.11.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:2658f34ce7e2dabf4ec73b45e2ca68aedad7a5be87ea756ad656eaf32bf1e1ea", size = 419732324, upload-time = "2026-03-23T18:09:36.604Z" }, - { url = "https://files.pythonhosted.org/packages/07/f4/1b666b6d61d3394cca306ea543ed03a64aad0a201b6cd159f1d41010aeb1/torch-2.11.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:98bb213c3084cfe176302949bdc360074b18a9da7ab59ef2edc9d9f742504778", size = 530596026, upload-time = "2026-03-23T18:09:20.842Z" }, { url = "https://files.pythonhosted.org/packages/48/6b/30d1459fa7e4b67e9e3fe1685ca1d8bb4ce7c62ef436c3a615963c6c866c/torch-2.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a97b94bbf62992949b4730c6cd2cc9aee7b335921ee8dc207d930f2ed09ae2db", size = 114793702, upload-time = "2026-03-23T18:09:47.304Z" }, { url = "https://files.pythonhosted.org/packages/26/0d/8603382f61abd0db35841148ddc1ffd607bf3100b11c6e1dab6d2fc44e72/torch-2.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01018087326984a33b64e04c8cb5c2795f9120e0d775ada1f6638840227b04d7", size = 80573442, upload-time = "2026-03-23T18:09:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/c7/86/7cd7c66cb9cec6be330fff36db5bd0eef386d80c031b581ec81be1d4b26c/torch-2.11.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:2bb3cc54bd0dea126b0060bb1ec9de0f9c7f7342d93d436646516b0330cd5be7", size = 419749385, upload-time = "2026-03-23T18:07:33.77Z" }, - { url = "https://files.pythonhosted.org/packages/47/e8/b98ca2d39b2e0e4730c0ee52537e488e7008025bc77ca89552ff91021f7c/torch-2.11.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4dc8b3809469b6c30b411bb8c4cad3828efd26236153d9beb6a3ec500f211a60", size = 530716756, upload-time = "2026-03-23T18:07:50.02Z" }, { url = "https://files.pythonhosted.org/packages/78/88/d4a4cda8362f8a30d1ed428564878c3cafb0d87971fbd3947d4c84552095/torch-2.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:2b4e811728bd0cc58fb2b0948fe939a1ee2bf1422f6025be2fca4c7bd9d79718", size = 114552300, upload-time = "2026-03-23T18:09:05.617Z" }, { url = "https://files.pythonhosted.org/packages/bf/46/4419098ed6d801750f26567b478fc185c3432e11e2cad712bc6b4c2ab0d0/torch-2.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8245477871c3700d4370352ffec94b103cfcb737229445cf9946cddb7b2ca7cd", size = 80959460, upload-time = "2026-03-23T18:09:00.818Z" }, - { url = "https://files.pythonhosted.org/packages/fd/66/54a56a4a6ceaffb567231994a9745821d3af922a854ed33b0b3a278e0a99/torch-2.11.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:ab9a8482f475f9ba20e12db84b0e55e2f58784bdca43a854a6ccd3fd4b9f75e6", size = 419735835, upload-time = "2026-03-23T18:07:18.974Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e7/0b6665f533aa9e337662dc190425abc0af1fe3234088f4454c52393ded61/torch-2.11.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:563ed3d25542d7e7bbc5b235ccfacfeb97fb470c7fee257eae599adb8005c8a2", size = 530613405, upload-time = "2026-03-23T18:08:07.014Z" }, { url = "https://files.pythonhosted.org/packages/cf/bf/c8d12a2c86dbfd7f40fb2f56fbf5a505ccf2d9ce131eb559dfc7c51e1a04/torch-2.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b2a43985ff5ef6ddd923bbcf99943e5f58059805787c5c9a2622bf05ca2965b0", size = 114792991, upload-time = "2026-03-23T18:08:19.216Z" }, ] @@ -1925,22 +2045,23 @@ wheels = [ [[package]] name = "transformers" -version = "5.4.0" +version = "4.48.2" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "filelock" }, { name = "huggingface-hub" }, { name = "numpy" }, { name = "packaging" }, { name = "pyyaml" }, { name = "regex" }, + { name = "requests" }, { name = "safetensors" }, { name = "tokenizers" }, { name = "tqdm" }, - { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0b/4c/42a8e1c7bbe668d8e073941ec3205263afb1cd02683fa5a8a75e615fdfbe/transformers-5.4.0.tar.gz", hash = "sha256:cb34ca89dce345ae3224b290346b9c0fa9694b951d54f3ed16334a4b1bfe3d04", size = 8152836, upload-time = "2026-03-27T00:24:24.692Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/cf/1093586e09c8d889d2f6b8ffe6a1369e1e179eb7b8e732fc0f348a8fe58f/transformers-4.48.2.tar.gz", hash = "sha256:dcfb73473e61f22fb3366fe2471ed2e42779ecdd49527a1bdf1937574855d516", size = 8370945, upload-time = "2025-01-30T19:52:28.908Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/a0/0a87883e564e364baab32adcacb4bec2e200b28a568423c8cf7fde316461/transformers-5.4.0-py3-none-any.whl", hash = "sha256:9fbe50602d2a4e6d0aa8a35a605433dfac72d595ee2192eae192590a6cc2df86", size = 10105556, upload-time = "2026-03-27T00:24:21.735Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/902c95a2a6f5d2d120c940ac4bd1f937c01035af529803c13d65ca33c2d1/transformers-4.48.2-py3-none-any.whl", hash = "sha256:493bc5b0268b116eff305edf6656367fc89cf570e7a9d5891369e04751db698a", size = 9667774, upload-time = "2025-01-30T19:52:24.789Z" }, ] [[package]] @@ -1981,36 +2102,14 @@ wheels = [ [[package]] name = "triton" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/2c/96f92f3c60387e14cc45aed49487f3486f89ea27106c1b1376913c62abe4/triton-3.6.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49df5ef37379c0c2b5c0012286f80174fcf0e073e5ade1ca9a86c36814553651", size = 176081190, upload-time = "2026-01-20T16:16:00.523Z" }, - { url = "https://files.pythonhosted.org/packages/e0/12/b05ba554d2c623bffa59922b94b0775673de251f468a9609bc9e45de95e9/triton-3.6.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8e323d608e3a9bfcc2d9efcc90ceefb764a82b99dea12a86d643c72539ad5d3", size = 188214640, upload-time = "2026-01-20T16:00:35.869Z" }, - { url = "https://files.pythonhosted.org/packages/17/5d/08201db32823bdf77a0e2b9039540080b2e5c23a20706ddba942924ebcd6/triton-3.6.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:374f52c11a711fd062b4bfbb201fd9ac0a5febd28a96fb41b4a0f51dde3157f4", size = 176128243, upload-time = "2026-01-20T16:16:07.857Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a8/cdf8b3e4c98132f965f88c2313a4b493266832ad47fb52f23d14d4f86bb5/triton-3.6.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74caf5e34b66d9f3a429af689c1c7128daba1d8208df60e81106b115c00d6fca", size = 188266850, upload-time = "2026-01-20T16:00:43.041Z" }, - { url = "https://files.pythonhosted.org/packages/3c/12/34d71b350e89a204c2c7777a9bba0dcf2f19a5bfdd70b57c4dbc5ffd7154/triton-3.6.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448e02fe6dc898e9e5aa89cf0ee5c371e99df5aa5e8ad976a80b93334f3494fd", size = 176133521, upload-time = "2026-01-20T16:16:13.321Z" }, - { url = "https://files.pythonhosted.org/packages/f9/0b/37d991d8c130ce81a8728ae3c25b6e60935838e9be1b58791f5997b24a54/triton-3.6.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c7f76c6e72d2ef08df639e3d0d30729112f47a56b0c81672edc05ee5116ac9", size = 188289450, upload-time = "2026-01-20T16:00:49.136Z" }, - { url = "https://files.pythonhosted.org/packages/ce/4e/41b0c8033b503fd3cfcd12392cdd256945026a91ff02452bef40ec34bee7/triton-3.6.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1722e172d34e32abc3eb7711d0025bb69d7959ebea84e3b7f7a341cd7ed694d6", size = 176276087, upload-time = "2026-01-20T16:16:18.989Z" }, - { url = "https://files.pythonhosted.org/packages/35/f8/9c66bfc55361ec6d0e4040a0337fb5924ceb23de4648b8a81ae9d33b2b38/triton-3.6.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d002e07d7180fd65e622134fbd980c9a3d4211fb85224b56a0a0efbd422ab72f", size = 188400296, upload-time = "2026-01-20T16:00:56.042Z" }, - { url = "https://files.pythonhosted.org/packages/49/55/5ecf0dcaa0f2fbbd4420f7ef227ee3cb172e91e5fede9d0ecaddc43363b4/triton-3.6.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5523241e7d1abca00f1d240949eebdd7c673b005edbbce0aca95b8191f1d43", size = 176138577, upload-time = "2026-01-20T16:16:25.426Z" }, - { url = "https://files.pythonhosted.org/packages/df/3d/9e7eee57b37c80cec63322c0231bb6da3cfe535a91d7a4d64896fcb89357/triton-3.6.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a17a5d5985f0ac494ed8a8e54568f092f7057ef60e1b0fa09d3fd1512064e803", size = 188273063, upload-time = "2026-01-20T16:01:07.278Z" }, - { url = "https://files.pythonhosted.org/packages/48/db/56ee649cab5eaff4757541325aca81f52d02d4a7cd3506776cad2451e060/triton-3.6.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b3a97e8ed304dfa9bd23bb41ca04cdf6b2e617d5e782a8653d616037a5d537d", size = 176274804, upload-time = "2026-01-20T16:16:31.528Z" }, - { url = "https://files.pythonhosted.org/packages/f6/56/6113c23ff46c00aae423333eb58b3e60bdfe9179d542781955a5e1514cb3/triton-3.6.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46bd1c1af4b6704e554cad2eeb3b0a6513a980d470ccfa63189737340c7746a7", size = 188397994, upload-time = "2026-01-20T16:01:14.236Z" }, -] - -[[package]] -name = "typer" -version = "0.24.1" +version = "3.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "annotated-doc" }, - { name = "click" }, - { name = "rich" }, - { name = "shellingham" }, + { name = "filelock", marker = "sys_platform == 'linux'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" }, + { url = "https://files.pythonhosted.org/packages/33/3e/a2f59384587eff6aeb7d37b6780de7fedd2214935e27520430ca9f5b7975/triton-3.0.0-1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ce8520437c602fb633f1324cc3871c47bee3b67acf9756c1a66309b60e3216c", size = 209438883, upload-time = "2024-07-19T20:56:52.275Z" }, + { url = "https://files.pythonhosted.org/packages/fe/7b/7757205dee3628f75e7991021d15cd1bd0c9b044ca9affe99b50879fc0e1/triton-3.0.0-1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e509deb77f1c067d8640725ef00c5cbfcb2052a1a3cb6a6d343841f92624eb", size = 209464695, upload-time = "2024-07-19T20:57:22.532Z" }, ] [[package]] @@ -2043,6 +2142,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/54/57c411a6e8f7bd7848c8b66e4dcaffa586bf4c02e63f2280db0327a4e6eb/unidiff-0.7.5-py2.py3-none-any.whl", hash = "sha256:c93bf2265cc1ba2a520e415ab05da587370bc2a3ae9e0414329f54f0c2fc09e8", size = 14386, upload-time = "2023-03-10T01:05:36.594Z" }, ] +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + [[package]] name = "uvicorn" version = "0.42.0"