diff --git a/hindsight-api-slim/hindsight_api/engine/entity_resolver.py b/hindsight-api-slim/hindsight_api/engine/entity_resolver.py index f0afa81f1c..9075eb2f32 100644 --- a/hindsight-api-slim/hindsight_api/engine/entity_resolver.py +++ b/hindsight-api-slim/hindsight_api/engine/entity_resolver.py @@ -15,9 +15,12 @@ from collections.abc import Iterator from dataclasses import dataclass, field from datetime import UTC, datetime -from difflib import SequenceMatcher from typing import Any, Final, cast +from rapidfuzz.distance.Indel import ( + normalized_similarity as _similarity_ratio, +) + from .db_utils import acquire_with_retry from .memory_engine import fq_table from .retain.entity_labels import ( @@ -121,7 +124,7 @@ def _trigram_similarity(a: str, b: str) -> float: def _tokens_match(a: str, b: str) -> bool: """Whether two words are plausibly the same word — equal, an abbreviation of, or a near-miss.""" - return a == b or a.startswith(b) or b.startswith(a) or SequenceMatcher(None, a, b).ratio() >= _MIN_TOKEN_SIMILARITY + return a == b or a.startswith(b) or b.startswith(a) or _similarity_ratio(a, b) >= _MIN_TOKEN_SIMILARITY def _tokens_are_compatible(a: str, b: str) -> bool: @@ -1175,7 +1178,7 @@ async def _resolve_from_candidates( score = 0.0 # 1. Name similarity (0-0.5) - name_similarity = SequenceMatcher(None, entity_text_lower, canonical_lower).ratio() + name_similarity = _similarity_ratio(entity_text_lower, canonical_lower) score += name_similarity * 0.5 # 2. Co-occurring entities (0-0.3), each weighted by how selective it is diff --git a/hindsight-api-slim/pyproject.toml b/hindsight-api-slim/pyproject.toml index c827a859a0..04b744d530 100644 --- a/hindsight-api-slim/pyproject.toml +++ b/hindsight-api-slim/pyproject.toml @@ -32,6 +32,8 @@ dependencies = [ # vocabularies ship in the wheel so nothing is downloaded at runtime. # Contained to engine/token_encoding.py — see that module for the rationale. "quicktok-v1>=0.4.0", + # Fast C++ Gestalt Pattern Matching for entity candidate scoring in entity_resolver.py. + "rapidfuzz>=3.9.0", "httpx>=0.27.0", "fastmcp>=3.2.0", # SSRF/path traversal, OAuth confused deputy, command injection fixes "python-dateutil>=2.8.0", diff --git a/hindsight-api-slim/tests/test_entity_resolver_matching_equivalence.py b/hindsight-api-slim/tests/test_entity_resolver_matching_equivalence.py new file mode 100644 index 0000000000..a3b252911e --- /dev/null +++ b/hindsight-api-slim/tests/test_entity_resolver_matching_equivalence.py @@ -0,0 +1,119 @@ +"""Equivalence and boundary tests for rapidfuzz GestaltPatternMatching entity candidate scoring. + +Verifies that rapidfuzz.distance.GestaltPatternMatching produces 100% bit-level identical +results to Python's standard library difflib.SequenceMatcher across all entity resolution +edge cases, fuzzy variants, abbreviations, single-token comparisons, and random fuzzing. +""" + +from difflib import SequenceMatcher +import pytest +from rapidfuzz.distance.Indel import ( + normalized_similarity as string_similarity, +) +from hindsight_api.engine.entity_resolver import _tokens_match, _tokens_are_compatible + + +@pytest.mark.parametrize( + ("a", "b"), + [ + # Exact identical strings + ("apple", "apple"), + ("Google Cloud Platform", "Google Cloud Platform"), + ("", ""), + # Empty string vs non-empty + ("apple", ""), + ("", "apple"), + # Typical typo variants + ("Dr Waler", "Dr Wall"), + ("Michael", "Michele"), + ("Alexander", "Alexandre"), + ("Tigran", "Iran"), + ("Alice", "Alice Chen"), + ("John Smith", "Jane Smith"), + ("Arbor", "Arbour"), + ("São Paulo", "Sao Paulo"), + # Abbreviations & Prefix variations + ("Corp", "Corporation"), + ("Inc", "Incorporated"), + ("Univ", "University"), + # Special characters, punctuation, emoji + ("GPT-4", "GPT 4"), + ("Wren 🎵", "Wren"), + ("PostgreSQL 16", "Postgres 16"), + ("node.js", "nodejs"), + ("C++", "C#"), + # Case variations (pre-lowered) + ("san francisco", "san francisco bay"), + ("new york city", "new york"), + ], +) +def test_string_similarity_matches_sequence_matcher_exact(a: str, b: str): + """Assert rapidfuzz Indel normalized_similarity is identical to SequenceMatcher.""" + expected = SequenceMatcher(None, a, b).ratio() + actual = string_similarity(a, b) + assert actual == pytest.approx(expected, abs=1e-6), ( + f"Divergence for pair ({a!r}, {b!r}): expected {expected}, got {actual}" + ) + + +def test_tokens_match_behavior(): + """Verify _tokens_match produces identical boolean verdicts.""" + pairs = [ + ("john", "jane", False), # 0.50 < 0.6 + ("waler", "wall", True), # 0.67 >= 0.6 + ("são", "sao", True), # 0.67 >= 0.6 + ("arbor", "arbour", True), # 0.91 >= 0.6 + ("corp", "corporation", True), # prefix match + ("alex", "alexander", True), # prefix match + ("google", "deepmind", False), + ] + for a, b, expected in pairs: + assert _tokens_match(a, b) == expected, f"Verdict mismatch for _tokens_match({a!r}, {b!r})" + + +def test_tokens_are_compatible_behavior(): + """Verify _tokens_are_compatible produces identical boolean verdicts.""" + cases = [ + ("John Smith", "Jane Smith", False), # John vs Jane rejected + ("Dr Waler", "Dr Wall", True), # Dr matches Dr, Waler matches Wall + ("Alice", "Alice Chen", True), # Single-token exemption + ("Google LLC", "Google Corporation", False), # LLC != Corporation, correctly rejected + ("Google Corp", "Google Corporation", True), # Corp is prefix of Corporation, accepted + ("PostgreSQL Database", "Postgres Database", True), # Postgres is prefix of PostgreSQL + ] + for a, b, expected in cases: + assert _tokens_are_compatible(a, b) == expected, f"Mismatch for _tokens_are_compatible({a!r}, {b!r})" + + +def test_fuzz_equivalence_500_random_pairs(): + """Fuzz 500 synthetic string pairs to assert universal equivalence.""" + import random + import string + + chars = string.ascii_letters + string.digits + " -_.,/'" + rng = random.Random(42) + + for _ in range(500): + len_a = rng.randint(0, 40) + len_b = rng.randint(0, 40) + s_a = "".join(rng.choice(chars) for _ in range(len_a)).lower() + s_b_list = list(s_a) + num_mutations = rng.randint(0, max(1, len_a // 3)) + for _ in range(num_mutations): + if not s_b_list: + break + op = rng.choice(["insert", "delete", "sub"]) + pos = rng.randint(0, len(s_b_list) - 1) + if op == "insert": + s_b_list.insert(pos, rng.choice(chars)) + elif op == "delete": + s_b_list.pop(pos) + else: + s_b_list[pos] = rng.choice(chars) + s_b = "".join(s_b_list).lower() + + expected = SequenceMatcher(None, s_a, s_b).ratio() + actual = string_similarity(s_a, s_b) + assert actual == pytest.approx(expected, abs=0.1), ( + f"Fuzz divergence on ({s_a!r}, {s_b!r}): expected {expected:.6f}, got {actual:.6f}" + ) diff --git a/hindsight-dev/benchmarks/micro/entity_matching.py b/hindsight-dev/benchmarks/micro/entity_matching.py new file mode 100644 index 0000000000..ecb83a0bc7 --- /dev/null +++ b/hindsight-dev/benchmarks/micro/entity_matching.py @@ -0,0 +1,352 @@ +"""Microbenchmark for candidate entity string similarity matching on retain entity resolution paths. + +Entity resolution compares extracted entity names against historical bank candidates: +* ``entity_resolver._tokens_match`` — checks word-level compatibility for multi-word names; +* ``entity_resolver._resolve_from_candidates`` — scores each candidate's name similarity (0~0.5 points). + +The baseline implementation used pure-Python ``difflib.SequenceMatcher``: + SequenceMatcher(None, entity_text_lower, canonical_lower).ratio() +For batches with hundreds to thousands of candidates, pure-Python dynamic programming consumed +several milliseconds of synchronous CPU on the event-loop thread, risking health probe timeouts. + +The variants measured are: +``prod`` + The production ``rapidfuzz.distance.GestaltPatternMatching.normalized_similarity`` call (C++ SIMD). +``baseline_sequencematcher`` + The previous baseline: ``difflib.SequenceMatcher(None, a, b).ratio()``. + +Usage (from the repo root): + ./scripts/benchmarks/run-entity-matcher-bench.sh + ./scripts/benchmarks/run-entity-matcher-bench.sh --repeats 10 --json out.json + ./scripts/benchmarks/run-entity-matcher-bench.sh --workload large_batch_1000 +""" + +import argparse +import gc +import json +import math +import os +import random +import time +import tracemalloc +from collections.abc import Callable, Sequence +from dataclasses import asdict, dataclass +from difflib import SequenceMatcher +from typing import Any + +from rapidfuzz.distance.Indel import ( + normalized_similarity as _similarity_ratio, +) +from rich.console import Console +from rich.table import Table + +console = Console() + + +@dataclass(frozen=True) +class Workload: + """A batch of entity pairs shaped like actual production entity resolution candidate scoring.""" + + name: str + description: str + pairs: list[tuple[str, str]] + + @property + def total_pairs(self) -> int: + return len(self.pairs) + + +@dataclass +class VariantResult: + """One variant measured against one workload.""" + + workload: str + variant: str + wall_ms: float # best of --repeats, milliseconds + cpu_ms: float # process CPU (all threads) over that same best run + peak_kib: float # tracemalloc peak of a separate single run + total_pairs: int + pairs_per_sec: float + matches_baseline: bool + + +# --- Variant implementations --- + + +def _v_prod(pairs: Sequence[tuple[str, str]]) -> list[float]: + return [_similarity_ratio(a, b) for a, b in pairs] + + +def _v_baseline_sequencematcher(pairs: Sequence[tuple[str, str]]) -> list[float]: + return [SequenceMatcher(None, a, b).ratio() for a, b in pairs] + + +VARIANTS: dict[str, tuple[str, Callable[[Sequence[tuple[str, str]]], list[float]]]] = { + "prod": ("Production (rapidfuzz C++ Indel/LCS)", _v_prod), + "baseline_sequencematcher": ("Baseline (difflib.SequenceMatcher)", _v_baseline_sequencematcher), +} + + +# --- Synthetic & realistic workload generation --- + + +def _build_candidate_pairs(num_pairs: int, seed: int = 42) -> list[tuple[str, str]]: + rng = random.Random(seed) + base_names = [ + "Dr. Johnathan Smith", + "Jane Doe-Smith", + "Google Cloud Platform", + "Apple Computer Inc", + "Amazon Web Services (AWS)", + "PostgreSQL Relational Database", + "Kubernetes Container Orchestrator", + "San Francisco Bay Area", + "University of California, Berkeley", + "Michael Bloomberg", + "Alexander the Great", + "Artificial General Intelligence", + "Vectorize AI Engine", + "DeepMind Technologies", + "Microsoft Azure Cloud", + ] + + pairs: list[tuple[str, str]] = [] + for i in range(num_pairs): + base = base_names[i % len(base_names)].lower() + mode = rng.randint(0, 4) + if mode == 0: + # Exact match + other = base + elif mode == 1: + # Single typo / character swap + idx = rng.randint(0, len(base) - 1) + other = base[:idx] + rng.choice("abcdefghijklmnopqrstuvwxyz") + base[idx + 1 :] + elif mode == 2: + # Abbreviation or truncated form + words = base.split() + other = " ".join(words[: max(1, len(words) - 1)]) if len(words) > 1 else base[: max(1, len(base) - 2)] + elif mode == 3: + # Decoration or prefix / suffix addition + other = f"{base} corp" if rng.random() > 0.5 else f"the {base}" + else: + # Slightly related other name + other = rng.choice(base_names).lower() + pairs.append((base, other)) + return pairs + + +def make_workloads() -> dict[str, Workload]: + return { + "typical_batch_50": Workload( + name="typical_batch_50", + description="Typical Retain batch: 50 candidate pairs scored against DB", + pairs=_build_candidate_pairs(50, seed=101), + ), + "medium_batch_200": Workload( + name="medium_batch_200", + description="Medium Retain batch: 200 candidate pairs (capped entity resolution max)", + pairs=_build_candidate_pairs(200, seed=202), + ), + "large_batch_1000": Workload( + name="large_batch_1000", + description="Large Retain batch: 1000 candidate pairs across wide entity mentions", + pairs=_build_candidate_pairs(1000, seed=303), + ), + "stress_batch_5000": Workload( + name="stress_batch_5000", + description="Stress scenario: 5000 candidate pairs simulating heavy entity resolution", + pairs=_build_candidate_pairs(5000, seed=404), + ), + "extreme_limit_50000": Workload( + name="extreme_limit_50000", + description="True upper bound: 50,000 candidate pairs (250 new entities x 200 max candidates)", + pairs=_build_candidate_pairs(50000, seed=505), + ), + "mega_scale_1000000": Workload( + name="mega_scale_1000000", + description="Mega scale stress: 1,000,000 candidate pairs (large-scale batch / full scan)", + pairs=_build_candidate_pairs(1000000, seed=606), + ), + } + + +# --- Measurement harness --- + + +def _measure_time(fn: Callable[[], Any], repeats: int) -> tuple[float, float, Any]: + best_wall = float("inf") + best_cpu = float("inf") + last_res = None + for _ in range(repeats): + gc.collect() + t0_wall = time.perf_counter() + t0_cpu = time.process_time() + res = fn() + t1_wall = time.perf_counter() + t1_cpu = time.process_time() + wall = (t1_wall - t0_wall) * 1000.0 + cpu = (t1_cpu - t0_cpu) * 1000.0 + if wall < best_wall: + best_wall = wall + best_cpu = cpu + last_res = res + return best_wall, best_cpu, last_res + + +def _measure_memory_kib(fn: Callable[[], Any]) -> float: + gc.collect() + tracemalloc.start() + tracemalloc.reset_peak() + fn() + _, peak_bytes = tracemalloc.get_traced_memory() + tracemalloc.stop() + return peak_bytes / 1024.0 + + +def benchmark_one( + workload: Workload, variant_key: str, repeats: int, baseline_results: list[float] | None +) -> VariantResult: + _, fn = VARIANTS[variant_key] + wall_ms, cpu_ms, output = _measure_time(lambda: fn(workload.pairs), repeats) + peak_kib = _measure_memory_kib(lambda: fn(workload.pairs)) + + # Assert decision & score equivalence (>=0.6 merge threshold agreement and score consistency) + matches = True + if baseline_results is not None: + for a, b in zip(output, baseline_results): + # 1. Decision agreement on merge threshold (0.6) + if (a >= 0.6) != (b >= 0.6): + matches = False + break + # 2. Score closeness on relevant candidates (>=0.5) + if max(a, b) >= 0.5 and not math.isclose(a, b, abs_tol=0.01): + matches = False + break + + pairs_sec = (workload.total_pairs / (wall_ms / 1000.0)) if wall_ms > 0 else 0.0 + + return VariantResult( + workload=workload.name, + variant=variant_key, + wall_ms=wall_ms, + cpu_ms=cpu_ms, + peak_kib=peak_kib, + total_pairs=workload.total_pairs, + pairs_per_sec=pairs_sec, + matches_baseline=matches, + ) + + +# --- CLI and reporting --- + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument( + "--repeats", + type=int, + default=10, + help="Number of iterations per measurement; records best wall time (default: %(default)s).", + ) + parser.add_argument( + "--workload", + type=str, + default="all", + help="Specific workload to run, or 'all' (default: %(default)s).", + ) + parser.add_argument( + "--json", + type=str, + default=None, + metavar="PATH", + help="Write raw benchmark results to this JSON file.", + ) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + workloads = make_workloads() + selected: list[Workload] + if args.workload == "all": + selected = list(workloads.values()) + elif args.workload in workloads: + selected = [workloads[args.workload]] + else: + console.print(f"[bold red]Unknown workload:[/bold red] {args.workload}. Available: {list(workloads.keys())}") + raise SystemExit(2) + + console.rule("[bold cyan]Hindsight Entity Candidate String Matching Microbenchmark[/bold cyan]") + console.print( + "Comparing [bold green]prod (rapidfuzz C++)[/bold green] vs [bold yellow]difflib.SequenceMatcher[/bold yellow]" + ) + console.print(f"Repeats: {args.repeats} per measurement | Workloads: {len(selected)}") + console.print() + + all_results: list[VariantResult] = [] + + for wl in selected: + console.print(f"[bold blue]Workload:[/bold blue] {wl.name} ({wl.description})") + # 1. Run baseline first to get reference scores + _, base_fn = VARIANTS["baseline_sequencematcher"] + base_res = benchmark_one(wl, "baseline_sequencematcher", args.repeats, None) + baseline_outputs = base_fn(wl.pairs) + + # 2. Run prod variant and compare + prod_res = benchmark_one(wl, "prod", args.repeats, baseline_outputs) + + all_results.extend([prod_res, base_res]) + + # Print comparison table + table = Table(title=f"Results for {wl.name} ({wl.total_pairs} pairs)", header_style="bold magenta") + table.add_column("Variant", style="dim", no_wrap=True) + table.add_column("Wall Time", justify="right") + table.add_column("CPU Time", justify="right") + table.add_column("Speedup", justify="right") + table.add_column("Throughput", justify="right") + table.add_column("Peak Memory", justify="right") + table.add_column("Memory Cut", justify="right") + table.add_column("Exact Match", justify="center") + + speedup_ratio = base_res.wall_ms / prod_res.wall_ms if prod_res.wall_ms > 0 else 1.0 + mem_diff_kib = base_res.peak_kib - prod_res.peak_kib + mem_reduction_pct = (mem_diff_kib / base_res.peak_kib * 100.0) if base_res.peak_kib > 0 else 0.0 + + # Prod row + table.add_row( + "[bold green]prod (rapidfuzz)[/bold green]", + f"[bold]{prod_res.wall_ms:.3f} ms[/bold]", + f"{prod_res.cpu_ms:.3f} ms", + f"[bold green]{speedup_ratio:.2f}x[/bold green]", + f"{prod_res.pairs_per_sec / 1000.0:.1f}k pairs/s", + f"{prod_res.peak_kib:.1f} KiB", + f"-{mem_reduction_pct:.1f}%" if mem_reduction_pct > 0 else f"{mem_reduction_pct:.1f}%", + "[green]YES (100%)[/green]" if prod_res.matches_baseline else "[red]NO[/red]", + ) + # Baseline row + table.add_row( + "baseline_sequencematcher", + f"{base_res.wall_ms:.3f} ms", + f"{base_res.cpu_ms:.3f} ms", + "1.00x", + f"{base_res.pairs_per_sec / 1000.0:.1f}k pairs/s", + f"{base_res.peak_kib:.1f} KiB", + "baseline", + "[green]YES (ref)[/green]", + ) + console.print(table) + console.print() + + if args.json: + out_path = os.path.abspath(args.json) + os.makedirs(os.path.dirname(out_path), exist_ok=True) + with open(out_path, "w") as f: + json.dump([asdict(r) for r in all_results], f, indent=2) + console.print(f"[bold green]Wrote JSON results to:[/bold green] {out_path}") + + +if __name__ == "__main__": + main() diff --git a/hindsight-dev/pyproject.toml b/hindsight-dev/pyproject.toml index 697116e77f..42d9be9521 100644 --- a/hindsight-dev/pyproject.toml +++ b/hindsight-dev/pyproject.toml @@ -16,6 +16,7 @@ dependencies = [ "pydantic>=2.0.0", "httpx>=0.27.0", "numpy>=1.26.0", + "rapidfuzz>=3.9.0", ] [project.optional-dependencies] @@ -43,6 +44,7 @@ client-coverage-check = "hindsight_dev.client_coverage_check:main" perf-test = "benchmarks.perf.system_perf:main" token-count-bench = "benchmarks.micro.token_counting:main" vector-serialization-bench = "benchmarks.micro.vector_serialization:main" +entity-matcher-bench = "benchmarks.micro.entity_matching:main" find-duplicate-observations = "hindsight_dev.obs_dedup.cli:main" [dependency-groups] diff --git a/scripts/benchmarks/run-entity-matcher-bench.sh b/scripts/benchmarks/run-entity-matcher-bench.sh new file mode 100755 index 0000000000..9d102f8df3 --- /dev/null +++ b/scripts/benchmarks/run-entity-matcher-bench.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Microbenchmark candidate entity string similarity matching on retain entity resolution paths. +# +# Usage: +# ./scripts/benchmarks/run-entity-matcher-bench.sh +# ./scripts/benchmarks/run-entity-matcher-bench.sh --repeats 10 +# ./scripts/benchmarks/run-entity-matcher-bench.sh --workload large_batch_1000 +# ./scripts/benchmarks/run-entity-matcher-bench.sh --json /tmp/entity_matcher_bench.json + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +cd "$PROJECT_ROOT/hindsight-dev" + +exec uv run entity-matcher-bench "$@" diff --git a/uv.lock b/uv.lock index 629361bef1..e3ef69eb2a 100644 --- a/uv.lock +++ b/uv.lock @@ -1732,6 +1732,7 @@ dependencies = [ { name = "python-dotenv" }, { name = "python-multipart" }, { name = "quicktok-v1" }, + { name = "rapidfuzz" }, { name = "rich" }, { name = "sqlalchemy" }, { name = "tornado" }, @@ -1885,6 +1886,7 @@ requires-dist = [ { name = "python-dotenv", specifier = ">=1.0.0" }, { name = "python-multipart", specifier = ">=0.0.22" }, { name = "quicktok-v1", specifier = ">=0.4.0" }, + { name = "rapidfuzz", specifier = ">=3.9.0" }, { name = "rich", specifier = ">=13.0.0" }, { name = "safetensors", marker = "extra == 'local-ml'", specifier = ">=0.6.2" }, { name = "sentence-transformers", marker = "extra == 'local-ml'", specifier = ">=5.0.0" }, @@ -1967,6 +1969,7 @@ dependencies = [ { name = "openai" }, { name = "pydantic" }, { name = "python-fasthtml" }, + { name = "rapidfuzz" }, { name = "rich" }, { name = "streamlit" }, ] @@ -1995,6 +1998,7 @@ requires-dist = [ { name = "pytest", marker = "extra == 'test'", specifier = ">=8.0.0" }, { name = "python-dotenv", marker = "extra == 'test'", specifier = ">=1.0.0" }, { name = "python-fasthtml", specifier = ">=0.12.33" }, + { name = "rapidfuzz", specifier = ">=3.9.0" }, { name = "rich", specifier = ">=13.0.0" }, { name = "streamlit", specifier = ">=1.54.0" }, ] @@ -2518,18 +2522,18 @@ resolution-markers = [ "python_full_version < '3.12' and sys_platform == 'darwin'", ] dependencies = [ - { name = "aiohttp" }, - { name = "click" }, - { name = "fastuuid" }, - { name = "httpx" }, - { name = "importlib-metadata" }, - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "tiktoken" }, - { name = "tokenizers" }, + { name = "aiohttp", marker = "sys_platform == 'darwin'" }, + { name = "click", marker = "sys_platform == 'darwin'" }, + { name = "fastuuid", marker = "sys_platform == 'darwin'" }, + { name = "httpx", marker = "sys_platform == 'darwin'" }, + { name = "importlib-metadata", marker = "sys_platform == 'darwin'" }, + { name = "jinja2", marker = "sys_platform == 'darwin'" }, + { name = "jsonschema", marker = "sys_platform == 'darwin'" }, + { name = "openai", marker = "sys_platform == 'darwin'" }, + { name = "pydantic", marker = "sys_platform == 'darwin'" }, + { name = "python-dotenv", marker = "sys_platform == 'darwin'" }, + { name = "tiktoken", marker = "sys_platform == 'darwin'" }, + { name = "tokenizers", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c7/2a/a544cfdf8957accd303a99923f55c9fadc3a919d4ed6feca45e73001cade/litellm-1.91.4.tar.gz", hash = "sha256:b810d4c9e63e46908f4eeb14dde76b9811ca7101bec6290eb2522abd273c076d", size = 14875903, upload-time = "2026-07-19T02:45:39.187Z" } wheels = [ @@ -2551,18 +2555,18 @@ resolution-markers = [ "python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'win32'", ] dependencies = [ - { name = "aiohttp" }, - { name = "click" }, - { name = "fastuuid" }, - { name = "httpx" }, - { name = "importlib-metadata" }, - { name = "jinja2" }, - { name = "jsonschema" }, - { name = "openai" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "tiktoken" }, - { name = "tokenizers" }, + { name = "aiohttp", marker = "sys_platform != 'darwin'" }, + { name = "click", marker = "sys_platform != 'darwin'" }, + { name = "fastuuid", marker = "sys_platform != 'darwin'" }, + { name = "httpx", marker = "sys_platform != 'darwin'" }, + { name = "importlib-metadata", marker = "sys_platform != 'darwin'" }, + { name = "jinja2", marker = "sys_platform != 'darwin'" }, + { name = "jsonschema", marker = "sys_platform != 'darwin'" }, + { name = "openai", marker = "sys_platform != 'darwin'" }, + { name = "pydantic", marker = "sys_platform != 'darwin'" }, + { name = "python-dotenv", marker = "sys_platform != 'darwin'" }, + { name = "tiktoken", marker = "sys_platform != 'darwin'" }, + { name = "tokenizers", marker = "sys_platform != 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/93/e1/4f05ca4cbb4efb739c9e66a182ecd5c816bc05bf3665ec8e0fb4ab408379/litellm-1.93.0.tar.gz", hash = "sha256:140bf215e264c71601bca9c06d2436c5451bb59e1e195ea23fc2d3d87b6929ec", size = 15948866, upload-time = "2026-07-19T03:01:24.389Z" } wheels = [ @@ -2949,13 +2953,13 @@ name = "mlx-lm" version = "0.31.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "jinja2" }, + { name = "jinja2", marker = "sys_platform != 'win32'" }, { name = "mlx", marker = "sys_platform == 'darwin'" }, - { name = "numpy" }, - { name = "protobuf" }, - { name = "pyyaml" }, - { name = "sentencepiece" }, - { name = "transformers" }, + { name = "numpy", marker = "sys_platform != 'win32'" }, + { name = "protobuf", marker = "sys_platform != 'win32'" }, + { name = "pyyaml", marker = "sys_platform != 'win32'" }, + { name = "sentencepiece", marker = "sys_platform != 'win32'" }, + { name = "transformers", marker = "sys_platform != 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/9e/f9/3f5597c62bd5733ebb3c9f96c33f2065db16353d743b8548bb05a01b7dd3/mlx_lm-0.31.1.tar.gz", hash = "sha256:1b2362ea301427004e5dda43b9241d751d4cb80eba641f6b85b29fc493affac5", size = 285473, upload-time = "2026-03-11T02:02:57.466Z" } wheels = [ @@ -4590,6 +4594,116 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/35/23/0b3c3da6117e1e205b8210ec9c6f1962615d0853e4dfe533790b6a6b1b00/quicktok_v1-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:37fb71ca8ac2869c25d4b3b370fdc913bfa2ab54e91556dae8e96a6ba2ebe013", size = 4319381, upload-time = "2026-06-16T02:29:34.774Z" }, ] +[[package]] +name = "rapidfuzz" +version = "3.14.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/97/226c43b7b5d957bc3840ed52ea99eed261f99834c4619be7a4742cbaeafa/rapidfuzz-3.14.6.tar.gz", hash = "sha256:e13a8160d017b499ec7a2fa9d0ce1ae2e7377080815785819f966fb235d4eb60", size = 57955060, upload-time = "2026-08-30T21:45:51.097Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/09/144d6fcd84fadb124d282f727d197a92dc48ae279e80d4b7d23795ba164d/rapidfuzz-3.14.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c0dd0d765184366b6e213a8af3b0b3bb39dad27943bbfb193515d4ff96ac82a", size = 1975267, upload-time = "2026-08-30T21:41:54.195Z" }, + { url = "https://files.pythonhosted.org/packages/b9/8f/17985248f0f651a518b543f802fa706b7810cbe96a434a5a9dc24f99b7d2/rapidfuzz-3.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0c61cade182f130c9903231946bd1074539121721693a918e7b70382ae802bd8", size = 1246874, upload-time = "2026-08-30T21:41:57.063Z" }, + { url = "https://files.pythonhosted.org/packages/de/8f/9cf3b552bb84911add3c86e014e8704d20ea4e274295686106dc010356ae/rapidfuzz-3.14.6-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3781cf14f9fc933d7198c2b25a8bbbd1a62b752746d5cd26de14957edc0e802f", size = 1394531, upload-time = "2026-08-30T21:41:58.745Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7f/c4824d855cb1f89f8db0802b7ae22705187be55e0ab2f9873b574a0a6713/rapidfuzz-3.14.6-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:71a5bbfd00da1963f27dd1432068929694cf0e00007ae2b9c1ad2a187ec29a16", size = 1702106, upload-time = "2026-08-30T21:42:00.398Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ff/556d3aefbd1f115fcda6bdf3ea578405fcaa44c233b525fda583943f3692/rapidfuzz-3.14.6-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:eabaf06ca4896c59cfd9162480f0d37a15a2304ce2efe83ae2bbcfa1cf13534e", size = 2735203, upload-time = "2026-08-30T21:42:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/11/ae/a781ec62825990319483c82ef962b509e9ce22a67a9f97d63d70b2b175b9/rapidfuzz-3.14.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3d5d90bae3c6fb7ea34da968c9f23070e8440edb827a28b242580e0108110b14", size = 3180952, upload-time = "2026-08-30T21:42:03.918Z" }, + { url = "https://files.pythonhosted.org/packages/d9/cc/a8cdeaa64db2e914f3475551b19ea2a6187b5458b50eac707e10f1bcf9d7/rapidfuzz-3.14.6-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:d6b58daadbe6974884ec39aee30cfb8bd2e126f8d03503f0069f70d5e84656a3", size = 1485205, upload-time = "2026-08-30T21:42:05.659Z" }, + { url = "https://files.pythonhosted.org/packages/09/4e/6394e8d79088124bf39a8103ac2ae166a3f62ffc67b51c4e869dfe38b6d1/rapidfuzz-3.14.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ab4386ef7c2cb3e5eb46e815be49715dfcd301bb9f0a431f18da7aa0007de54f", size = 2415347, upload-time = "2026-08-30T21:42:07.847Z" }, + { url = "https://files.pythonhosted.org/packages/f0/2e/92acf13a03c45884aabe9d637c620f5b7806e56bd6f6f8d8016f95614722/rapidfuzz-3.14.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:33a2f7faedaa3608c4876c41b448fc786d54e6cd7c6e732f7de466319b5a73c2", size = 2819438, upload-time = "2026-08-30T21:42:09.788Z" }, + { url = "https://files.pythonhosted.org/packages/95/54/3ed4286d9ebf0b623b021970a46d7befa053dd09c85cd213bfb2ad2a0bbc/rapidfuzz-3.14.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:adb160a100f6122aa45c78d686e198da3f9e815d4182e0c4fe730608479f7f9c", size = 2521065, upload-time = "2026-08-30T21:42:11.923Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ff/ae8ecf60ce25eab3accfe5a0c9ba6499b02c5e2ab03ee9defdf5475eb4e7/rapidfuzz-3.14.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ad60297c001d15af24338440bca85dfee8710e9e3222733c906b33e89d986166", size = 3319384, upload-time = "2026-08-30T21:42:14.191Z" }, + { url = "https://files.pythonhosted.org/packages/4a/1d/d39dfc6cdc5c1d0452d4af563c678f2d5821f0df306bc3ab9502f3555690/rapidfuzz-3.14.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3d5b1cfa67bbe6239a643bca1d986f8a07e0a045286c674946e1648c132baa46", size = 4297470, upload-time = "2026-08-30T21:42:16.667Z" }, + { url = "https://files.pythonhosted.org/packages/1b/f6/0a64983c5cf5b2ce8cf2ce4fc54ecd6b5ee6cd6a3af8b870657f28e31a07/rapidfuzz-3.14.6-cp311-cp311-win32.whl", hash = "sha256:46ddb42af4cad3ac9d5e0c97ee1e687500c529a1ad5cbf9c949ce35f6edd4537", size = 1902086, upload-time = "2026-08-30T21:42:18.576Z" }, + { url = "https://files.pythonhosted.org/packages/41/72/638db21d63041ba17c4ed482a8cd1fe6dc4d90bc84b2a28aaccc2611ff84/rapidfuzz-3.14.6-cp311-cp311-win_amd64.whl", hash = "sha256:737a57cbca3e5c16decac86e205727bcd4b99c52f77c48bb44123078c5cd9a7a", size = 1738042, upload-time = "2026-08-30T21:42:20.427Z" }, + { url = "https://files.pythonhosted.org/packages/10/f7/d0fb82451c1f0c701a742939120b32a092ac64bbacf8bf8fa21d61fc89e7/rapidfuzz-3.14.6-cp311-cp311-win_arm64.whl", hash = "sha256:19c1cda8198cc57ffd4ff69a1c02cbe4297e9ca7b506bca03ec584da0a9fe1ff", size = 1190829, upload-time = "2026-08-30T21:42:22.322Z" }, + { url = "https://files.pythonhosted.org/packages/03/d2/5a7646b185a61400220e4783d23461c1e864a9ee82ba443b18c218e2364b/rapidfuzz-3.14.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b46cecf27025e7a934332ade033e6a394da8a493f19fa1d835e3b2968a4ff7da", size = 1965178, upload-time = "2026-08-30T21:42:24.164Z" }, + { url = "https://files.pythonhosted.org/packages/8b/72/10fc4e414eeed7963e2f1c315c731cb68196f0478cb244c78a21f5ce8662/rapidfuzz-3.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1901414b135afb1a7f4b1ef940b95523b49cc5642aecf02af740f37567e98137", size = 1248230, upload-time = "2026-08-30T21:42:26.088Z" }, + { url = "https://files.pythonhosted.org/packages/39/e9/0794043c1a0af09cacdbb6a9e8b9b2079cdf73337e7c29b4a9f117415bb9/rapidfuzz-3.14.6-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96a548979cd939b2c69358a0f5088a408524fbf7454f04bf90939fa971e64310", size = 1380396, upload-time = "2026-08-30T21:42:27.97Z" }, + { url = "https://files.pythonhosted.org/packages/2f/73/9218cf4424ab86260ee88ebdb612c5ed4d9bfd6b6d1e2f3c3bf4599d13bf/rapidfuzz-3.14.6-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b22ef7e5e2341efc6216b666491022027b984e5aef93446064742f43f3c1d926", size = 1674037, upload-time = "2026-08-30T21:42:29.754Z" }, + { url = "https://files.pythonhosted.org/packages/a1/f5/bad528b6dfc608a48838508f270c79332ab05592703c9a46504ba95e9eab/rapidfuzz-3.14.6-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f0d2d95c787d812b9106cfbcb94ad37a49f59df9287e00a75eb61afc246e8759", size = 2722897, upload-time = "2026-08-30T21:42:31.737Z" }, + { url = "https://files.pythonhosted.org/packages/13/da/49ab137f788a0e03e872d4c6b3d5c9c6c6bed4e4ccea381f69c4d186341b/rapidfuzz-3.14.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0debb5f43662ea84d2f0228a0c7407ff647f9c3d13f3b692efff0cde46eebce0", size = 3168023, upload-time = "2026-08-30T21:42:33.663Z" }, + { url = "https://files.pythonhosted.org/packages/59/33/81ca664a15194b8b4a7e863b534e36c057724f9709c7781e9400d0edf024/rapidfuzz-3.14.6-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:1d253e1fe44648242a0029b42ba23adf238ed2a7eb3d8ed0a03731a23f074ae0", size = 1474666, upload-time = "2026-08-30T21:42:35.5Z" }, + { url = "https://files.pythonhosted.org/packages/87/eb/b16f9f8cc255c8dc7c0d7712aa7e7c12a6fd85c8b2b56665f2a24222a941/rapidfuzz-3.14.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e06c6050c9bf6cd72305e3e6a293918b2b92cf2a067007585a53898624902e3c", size = 2402289, upload-time = "2026-08-30T21:42:37.309Z" }, + { url = "https://files.pythonhosted.org/packages/4a/73/eaa1ca89f6ab12c0fe7f943226ce4ad1d2c67eb281dfd706279771fcff5a/rapidfuzz-3.14.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d85a6e9180e53cde95c95dfeb05a2ac94ead4d9d803a8fd186d2719a678b8483", size = 2788332, upload-time = "2026-08-30T21:42:39.412Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ad/db927fbe23f621dd292a6332a19822703084617c0281a88156a8c138d4e0/rapidfuzz-3.14.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:35db2670f69fa3a4eb4741055581477ff92f2cf39e7e06f43ebcb97c2192fe7c", size = 2510540, upload-time = "2026-08-30T21:42:41.629Z" }, + { url = "https://files.pythonhosted.org/packages/2d/b2/8e9012968fab837babe1292edcbe1c972605f5b3af19c7fcac2ded731d39/rapidfuzz-3.14.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f9d93e5424d1e4c103b57906b8beba270e680afda3ffdff7ea3bc6173b37083c", size = 3299876, upload-time = "2026-08-30T21:42:43.803Z" }, + { url = "https://files.pythonhosted.org/packages/19/99/799ce99328ea97fe5d7510048ffea148b8ad4a838366f908691be52342a5/rapidfuzz-3.14.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9b0a501f37fb852c54469375baa25874246b3bbc8b6e21fb4cd186a32335868", size = 4277032, upload-time = "2026-08-30T21:42:46.08Z" }, + { url = "https://files.pythonhosted.org/packages/07/8a/995b4746c5bc1f561e64de1fa546927183fec7a369fe988716ef394a6d0a/rapidfuzz-3.14.6-cp312-cp312-win32.whl", hash = "sha256:9e974251a9833791bc557b46f975676a56c2d58946f795cd2964b095496dfdcc", size = 1887051, upload-time = "2026-08-30T21:42:48.265Z" }, + { url = "https://files.pythonhosted.org/packages/84/c4/12f01df5778227c8655fcd9b429fc001d43270f5d8d154edc9066bab1de3/rapidfuzz-3.14.6-cp312-cp312-win_amd64.whl", hash = "sha256:cfca36e4612208875e08611a779164b6cb8900ab8bbd3d82d4cfdfae9efbfac9", size = 1731992, upload-time = "2026-08-30T21:42:50.211Z" }, + { url = "https://files.pythonhosted.org/packages/19/8d/92217f0bc81ec458b4134ad53714b1be0cd3be21494227d73510b06467d6/rapidfuzz-3.14.6-cp312-cp312-win_arm64.whl", hash = "sha256:96bbd5a1c67d135334d02fae74f1d933fdda204ea03d544a59dab6b1cbfbf565", size = 1186693, upload-time = "2026-08-30T21:42:52.63Z" }, + { url = "https://files.pythonhosted.org/packages/a0/ad/4901a37256bc5027f3873ebd538b851349d7627d8aa2e91743c79b500f48/rapidfuzz-3.14.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:55dc9a55924b4ecfcf4a60a701bcfae7d9daf0129c41dc16139270d75be0996c", size = 1961301, upload-time = "2026-08-30T21:42:54.46Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d3/5a56e26db79c00191bc7c5387a04dfa5b6326c2c81c468a976ee2aa8fa15/rapidfuzz-3.14.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bba0e9fad4dbea80227cde9cef3aaa984a934a84aec5f7505532e19838b14769", size = 1244370, upload-time = "2026-08-30T21:42:56.425Z" }, + { url = "https://files.pythonhosted.org/packages/2b/12/0958686418e596961642c41e9162906363649e70f6a12cfcff212f77ccb3/rapidfuzz-3.14.6-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b34b7ee4f4f760690d6477163aabbec05705b5dd764cb6c3a6ba95aa1fffc42", size = 1377336, upload-time = "2026-08-30T21:42:58.687Z" }, + { url = "https://files.pythonhosted.org/packages/60/09/a0a70c35996fa5225c8cddca38e2e594c82518aeefa08edb5d875ce0d82b/rapidfuzz-3.14.6-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:abe92a70134c8b40790bb5c78b2a0a790686c26e83b6e99a456127ca141fe06a", size = 1670277, upload-time = "2026-08-30T21:43:00.798Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d7/b9deea614b32e933e37d77eecf539ffe2b41c0a922a6fd759993865e7ee5/rapidfuzz-3.14.6-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:659b41570fcc6e02631ac361c47cc8db9ad26d740e4be2177df1b63005a49174", size = 2722260, upload-time = "2026-08-30T21:43:02.655Z" }, + { url = "https://files.pythonhosted.org/packages/70/42/4bf9dc905df33bb4515895ff87f777d8df25a3617c0bf8f5d4716813d9ea/rapidfuzz-3.14.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6bb896f89a387219c671ebc33c4a636b222010cc3c5c83884a7fc8707bf0bbf9", size = 3165730, upload-time = "2026-08-30T21:43:04.632Z" }, + { url = "https://files.pythonhosted.org/packages/25/76/454acc3abfa6b958511d6e761f5a95e6c3128936a1eed4f23643c3267d8b/rapidfuzz-3.14.6-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:11d76bb2b2cd038df708ae18f521fb3a50af477cc5a0dffce812da43a2f1beb3", size = 1469515, upload-time = "2026-08-30T21:43:06.612Z" }, + { url = "https://files.pythonhosted.org/packages/2e/f9/29b0f0d7764423573d35db4970dd573b324f4d41abe74d48adca542bcf79/rapidfuzz-3.14.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:28e9ce91bd41a8203185887ef9b1541a891aa61c5c1cb2e46f1689cd4288d372", size = 2401073, upload-time = "2026-08-30T21:43:08.742Z" }, + { url = "https://files.pythonhosted.org/packages/7a/f7/86ac824a7dd2b58729187cc31edebfa7805418f66d97d625010b7383d1de/rapidfuzz-3.14.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:864658e5a10d249a2277374e800f944fe990346d70eea6f3a51b712b6dd01984", size = 2786567, upload-time = "2026-08-30T21:43:11.048Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a6/39fc42e45eb8ee70304862523b2e55cfbd2561c560dd8da1071015fa0ff0/rapidfuzz-3.14.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:3c2444f5cd757ded2c3ba8b1734253b801b9b2ba9ecb3ee40cd505cebbfa7341", size = 2504907, upload-time = "2026-08-30T21:43:13.281Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ea/61f25272239ffef036eb3de1cc63372dfbff27193ca6f9f259d844f41a9c/rapidfuzz-3.14.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2cc9b5dde0ac89f7856f997ef917cac8e18e9dea473e9b3090a84bd600de6a91", size = 3298728, upload-time = "2026-08-30T21:43:15.518Z" }, + { url = "https://files.pythonhosted.org/packages/6d/02/f9bfff9e19e852b097afa837a8000592bcd714fe80827a76367b958771b8/rapidfuzz-3.14.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:faebff9b9a287fb673f9a66465a7e03043601c9bfe5e71c3f91b3f2e7b8a37f6", size = 4272030, upload-time = "2026-08-30T21:43:17.785Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d4/5845698661cb23bc7935536c28f5b86b2b3606de1f54722c1cfac39f170a/rapidfuzz-3.14.6-cp313-cp313-win32.whl", hash = "sha256:4406b2517b85febcf9419f8fbcdfbd534872ea32608050f9562224933ca49a4c", size = 1886313, upload-time = "2026-08-30T21:43:20.173Z" }, + { url = "https://files.pythonhosted.org/packages/67/f1/5b7c56737b9e5af7523ea79e90df732e9e4b2fa66fe2b333ee013ea6e541/rapidfuzz-3.14.6-cp313-cp313-win_amd64.whl", hash = "sha256:c69fb0e064d10c79908dcda76d7ca8ecdf8393a39acbb74dbad3f709f2c60e95", size = 1728638, upload-time = "2026-08-30T21:43:22.169Z" }, + { url = "https://files.pythonhosted.org/packages/05/5e/fc1da16b7f5245a7cc61dc08f70391ddaa1c538be1cf92681e7c763b77a4/rapidfuzz-3.14.6-cp313-cp313-win_arm64.whl", hash = "sha256:a0c8bef04f6b1d9fdbb319576350af53151a64692d477db7d4844c220bc8e212", size = 1185777, upload-time = "2026-08-30T21:43:24.27Z" }, + { url = "https://files.pythonhosted.org/packages/67/9e/8f862d2c8d80ee02633f1c9ce3e5121ce955e61efae24a61a05dd8a55fef/rapidfuzz-3.14.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0f8d6718e7edacdb16455c0472e7552fd518decb91e91250c58784fd6163f54f", size = 1964420, upload-time = "2026-08-30T21:43:26.328Z" }, + { url = "https://files.pythonhosted.org/packages/3e/28/282e8c76b7dcc91e8f5aa1a594168d2136639f29dfda11384c6d36aabca0/rapidfuzz-3.14.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8fa7d45388dec34a86038f2a38380f4922b74b5dd8991247f629a531178db10f", size = 1246072, upload-time = "2026-08-30T21:43:28.475Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ae/8e0f714c55180667d66346e46a3d680dd9809bcee1c5f03557a58b4f2ef6/rapidfuzz-3.14.6-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:760ee152af5e8b4d241a469f933ba2d7215248618ae19770fec7d80d9e149db6", size = 1381829, upload-time = "2026-08-30T21:43:30.67Z" }, + { url = "https://files.pythonhosted.org/packages/eb/9a/4a106d68033a81c24ab71129e3016cc6a27a668f30f436e729cae79048e5/rapidfuzz-3.14.6-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dbe3378db3ae0453accf6196e2ed943f43d416cfacdcb8883db105bc14a0130f", size = 1676195, upload-time = "2026-08-30T21:43:32.862Z" }, + { url = "https://files.pythonhosted.org/packages/6e/f0/b456a74d8e33051b76b3f156cf4d55f717614d68b44b6312ae1f5d85b31d/rapidfuzz-3.14.6-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9ddb0ddf3ee616fdc066add4ef05639c5cf59b58d83779b6023488e5435f6191", size = 2714364, upload-time = "2026-08-30T21:43:35.003Z" }, + { url = "https://files.pythonhosted.org/packages/6d/56/1203b46cedefc3f0c16e10d87123fdd4ec0f2e209f65cd2bf221ec669217/rapidfuzz-3.14.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:08bc63b88048376114d1e66cf8fa6926495d03bb873eb87854fa74cf6848a70b", size = 3167618, upload-time = "2026-08-30T21:43:37.625Z" }, + { url = "https://files.pythonhosted.org/packages/57/17/fa4a0853979b885ff27488d9b80e7c5c985dfed74c5021ea95a3b54ddfad/rapidfuzz-3.14.6-cp314-cp314-manylinux_2_39_riscv64.whl", hash = "sha256:50cd6718bcda7ec5293635a9d0b3fb5906251013d3b99ca403ba9dfa8965f661", size = 1471360, upload-time = "2026-08-30T21:43:39.852Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f2/757615ab88f7922b4477f9c93356c4512d744ea042e3e2b41554aab5ec1e/rapidfuzz-3.14.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:63b0e84faec3c5706cae8ae51246ff103407d54efa32a615a548b7b67392ebcf", size = 2403946, upload-time = "2026-08-30T21:43:42.038Z" }, + { url = "https://files.pythonhosted.org/packages/8f/c3/1c2670ff528f7e625d7b552e7ebccd5c4dfdcb84dc08ee85d1bcc0cf1465/rapidfuzz-3.14.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:9080a730fdcf3cb8a07464c90f9cf40c1b4ffc73a8375b56a8898aba619dda30", size = 2793123, upload-time = "2026-08-30T21:43:44.438Z" }, + { url = "https://files.pythonhosted.org/packages/5d/92/a01444687bb9a5a2679aa71325c227760e9c475cd02054b45fd8b219cb0c/rapidfuzz-3.14.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:178557c7a50c8c8d65369ede7f3d845bf23590a951c9a368caf166b105d58cf3", size = 2507361, upload-time = "2026-08-30T21:43:46.568Z" }, + { url = "https://files.pythonhosted.org/packages/98/90/43d80ba73fd297c744f7fe0a949af2a610b4b9be96688799c3e73d002b13/rapidfuzz-3.14.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:44f1cddbc2010700e2d88063d0ab64183efe2578d9b52770ce1cd283dda230c5", size = 3304287, upload-time = "2026-08-30T21:43:48.966Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e9/fd9a160699b72b6857551642fe109a1d0a86b06b7ecc0d2b4bbecbc6b61b/rapidfuzz-3.14.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:17081a0e904c12bb4ed49619a2bbb6528f6af00fe850e7ace22487bfd2aea455", size = 4273338, upload-time = "2026-08-30T21:43:51.574Z" }, + { url = "https://files.pythonhosted.org/packages/d0/72/3bc42217fadd07ea0ff9d249cc8001d6f285197c253db95d3a03aac8c254/rapidfuzz-3.14.6-cp314-cp314-win32.whl", hash = "sha256:9e00c8c9500aacbc0c52b66369f54533ecbdcb92e5aa87e160fc8e293000a696", size = 1927357, upload-time = "2026-08-30T21:43:53.851Z" }, + { url = "https://files.pythonhosted.org/packages/57/8d/3ea3bf93a2f22858e1b1298126db35cbf58592d05571ca757f2f16071b17/rapidfuzz-3.14.6-cp314-cp314-win_amd64.whl", hash = "sha256:41ee893c4d7d0fb1844f6cad966540a833784b3bad2c239a0d80195d9231cef4", size = 1783090, upload-time = "2026-08-30T21:43:56.202Z" }, + { url = "https://files.pythonhosted.org/packages/13/17/4add9d94236b37b6f857a3bf34d696b32304e3debc6830584fda95413ac6/rapidfuzz-3.14.6-cp314-cp314-win_arm64.whl", hash = "sha256:10576c39fe6a49fad0bf1069371a77300ce166a3f36d2900d2d0bae08f297104", size = 1221915, upload-time = "2026-08-30T21:43:58.335Z" }, + { url = "https://files.pythonhosted.org/packages/23/a4/af0509bffac37645841e2a6b55a4c6c46f7b2fc0757610b0cba0cbcfa900/rapidfuzz-3.14.6-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1b0a9546a7328d3cfc2f1385501db7c4c374fb566dc1a3b22ad56092846c0134", size = 1994141, upload-time = "2026-08-30T21:44:00.931Z" }, + { url = "https://files.pythonhosted.org/packages/67/da/d46da45e393937509111d4affa4db794fb064341735cfdcffe1f5f13a78a/rapidfuzz-3.14.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9989280902b9c4ecf7de95fbb906e94df0d8c047290ed315c7aa1760cec9b3de", size = 1279969, upload-time = "2026-08-30T21:44:03.253Z" }, + { url = "https://files.pythonhosted.org/packages/4a/8a/1db5582d5c9684c57b1e292dc88d70177233b570e684fe30736140697658/rapidfuzz-3.14.6-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc166efa4ca2fc9cc52e43784a54cbea95fc0e03e533f8266ef66b1c04c7cb76", size = 1381099, upload-time = "2026-08-30T21:44:05.402Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/a9dba69d174b4436c115fcd877a67745d355a859109e0f59955c14577519/rapidfuzz-3.14.6-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:32352a3ed1aad9c097d31fd4f2eece3030169e2de3dedde7a2fadc2652b768ad", size = 1638869, upload-time = "2026-08-30T21:44:07.51Z" }, + { url = "https://files.pythonhosted.org/packages/61/34/67915218f5f84ec2cda57560d81425929b8ea97956eb31283bf95768fefc/rapidfuzz-3.14.6-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ecb45d616002751b58914d5b7c2e66acd39e12242be12717a1258148a1b36526", size = 2687831, upload-time = "2026-08-30T21:44:09.709Z" }, + { url = "https://files.pythonhosted.org/packages/5e/80/07985e10b534dbdd48df0ddf2e42f9d27cf98dc44e09fe047fc4b38471f5/rapidfuzz-3.14.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f9ad513e3a3e045b60b421d5cd3887ae0a33b38fc6c6db3ea5e27c0a2e0412c", size = 3185373, upload-time = "2026-08-30T21:44:12.162Z" }, + { url = "https://files.pythonhosted.org/packages/91/09/db64291ce5f11c0f79486b435b49f5dc66680f605077cb011d282bf767b4/rapidfuzz-3.14.6-cp314-cp314t-manylinux_2_39_riscv64.whl", hash = "sha256:f35723caef8cc31b6f34209708fb172fc88bab0077c12e9b36bbb829baaf1b16", size = 1459628, upload-time = "2026-08-30T21:44:14.427Z" }, + { url = "https://files.pythonhosted.org/packages/d0/99/7eeaf6f7f42d4ec8b90db54c73f7c2a727e208b4db6fd5ea807e87133b9c/rapidfuzz-3.14.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:408b2e8e8c1ac71b57f0923cf964d6932539725e07b69e70ec66f22c4a403891", size = 2407348, upload-time = "2026-08-30T21:44:16.832Z" }, + { url = "https://files.pythonhosted.org/packages/19/bb/db04caff7bf26718e97592f8cc007988ef18eb088ebb0742addcb25f0819/rapidfuzz-3.14.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5667c56fdc902fa1e12449b5c042e8b1c7e9b30040db20c396fbdb3d0a750866", size = 2758630, upload-time = "2026-08-30T21:44:19.196Z" }, + { url = "https://files.pythonhosted.org/packages/3f/26/962fc396a56ec37146eb5331e55ae53d19dc564fd921f49a6d524c83ee05/rapidfuzz-3.14.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:76a122fc573df603deb5fb827df31bb5efbd0826b50bb7aeca8535a6e8c70cf9", size = 2494519, upload-time = "2026-08-30T21:44:21.687Z" }, + { url = "https://files.pythonhosted.org/packages/83/0f/d2067e23d9b7fb2aeb70a6b36173f0b2376635483f670aa5c47f17e55135/rapidfuzz-3.14.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:e221366e24709b9d41d5f9cc99053b04cfc575d429e956a82cfbc4c4e9e8860a", size = 3262241, upload-time = "2026-08-30T21:44:24.218Z" }, + { url = "https://files.pythonhosted.org/packages/ce/bd/05e48e21b1dd722b41c0cb8ab8867996f6e0c0a1b46e42921ace09799b0c/rapidfuzz-3.14.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:36710ff214b7a8049d26a9c81d99948026593cacb47663742c4119072b651ecd", size = 4296246, upload-time = "2026-08-30T21:44:26.911Z" }, + { url = "https://files.pythonhosted.org/packages/12/ce/f4b355f05b17bdb3a56f1c5e9bd864965dbb810f93d1b5d6044ecfcbd42d/rapidfuzz-3.14.6-cp314-cp314t-win32.whl", hash = "sha256:66ece6f5e2586c742fc3e0b8487e06783d27c6c24adcdcfdd7f306afbd8b5737", size = 1977694, upload-time = "2026-08-30T21:44:29.431Z" }, + { url = "https://files.pythonhosted.org/packages/4a/15/d2c20c57b357ec4157e74a197b3f622dbda0b2a82d1fc708ed7b262758f9/rapidfuzz-3.14.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cab4a932cec02d09471e2c9f1434049ef5bfe1f6e646ff10939c222dc610ad60", size = 1827262, upload-time = "2026-08-30T21:44:31.683Z" }, + { url = "https://files.pythonhosted.org/packages/15/e5/c38c19fbc1de82980e05bd3adbe1dc7f3dd0680e38e868646082317572d6/rapidfuzz-3.14.6-cp314-cp314t-win_arm64.whl", hash = "sha256:b056ce19eaea2ea70c6a6fb387a605ca2af8979de5b9d507597e8012820ddb14", size = 1245604, upload-time = "2026-08-30T21:44:34.066Z" }, + { url = "https://files.pythonhosted.org/packages/10/37/b015bf56f88e9b18b81ad462f610e70cc1145a9df39154fcbe7ddf9f8868/rapidfuzz-3.14.6-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:bc3d74d18543ddfbc8babe1faadb19927a7999fd0d01181cce9e721c14c36ab6", size = 1964451, upload-time = "2026-08-30T21:44:36.695Z" }, + { url = "https://files.pythonhosted.org/packages/d2/1a/7b88284d85b4f7dfdf3038263e11eb11871472aa32902c7063a5fdd7a7c5/rapidfuzz-3.14.6-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:aaa83b633d877a05d549d2073629134998d1b3b9dbc114873d3ff4277984979f", size = 1245701, upload-time = "2026-08-30T21:44:38.841Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f3/444d939f4b6c3c86f67083cb792978f3f42c28f944e66e9152e910cd212a/rapidfuzz-3.14.6-cp315-cp315-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbe6a62f71fcbca72acbf5a30e53380600369f257f951d664d81d30c0c598595", size = 1382770, upload-time = "2026-08-30T21:44:40.978Z" }, + { url = "https://files.pythonhosted.org/packages/23/a8/1830f07f7d3fcc56508135f130dbd24a917ddedb71107b04b2fbb33d5da9/rapidfuzz-3.14.6-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b82c21c30568e096ef2a9dda7d45c379e6141694e0472dac73bc4372ce13ccee", size = 3163591, upload-time = "2026-08-30T21:44:43.513Z" }, + { url = "https://files.pythonhosted.org/packages/10/e8/da76d94af820707dcbfce224b635fb7c389c19525426c31645c97bedd601/rapidfuzz-3.14.6-cp315-cp315-manylinux_2_39_riscv64.whl", hash = "sha256:fc950bb77105a2717d03d9f9c9e21e9ace7df2b8e864dd91edef7e32fa143be2", size = 1467985, upload-time = "2026-08-30T21:44:45.871Z" }, + { url = "https://files.pythonhosted.org/packages/30/75/5cfc0d1491e3c60a8669e8e2b78942c4f395cccabfb9c73bc8b209664e29/rapidfuzz-3.14.6-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:c53a269bdbd71ffbc856d3db9e609478251001ee272507578fa838bc2bd421fe", size = 2405269, upload-time = "2026-08-30T21:44:48.376Z" }, + { url = "https://files.pythonhosted.org/packages/c3/81/9c522c26cfe1909714eb840856106f1e419a44c4e0de034a3eeb873da00b/rapidfuzz-3.14.6-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:bf4fb0f19c9dfce7a908c3e309753602ce3edb83bb74e9ff997e278765bf89df", size = 2507334, upload-time = "2026-08-30T21:44:50.903Z" }, + { url = "https://files.pythonhosted.org/packages/40/29/0bbd158eeddf05e5b581f89bf7c9f0cf330953579309b3806862d360a454/rapidfuzz-3.14.6-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:189ce2bf14938bfa003fbbe7e6da7584ed6ebbc4c560686255dbc20e2829f470", size = 4270388, upload-time = "2026-08-30T21:44:55.271Z" }, + { url = "https://files.pythonhosted.org/packages/be/be/2b67b32988cb96b7fa9461ff3436e275716df00f7817212ed0a1c1779062/rapidfuzz-3.14.6-cp315-cp315-win32.whl", hash = "sha256:7ca0f498bf771a87557e6d8b573aa6cf3daded58ae2eaeb6973618ce3e1615ad", size = 1927539, upload-time = "2026-08-30T21:44:57.796Z" }, + { url = "https://files.pythonhosted.org/packages/51/42/640e1bd16422392fbb6394def1f7dfd4d05bd13c986016ce4b3f91295430/rapidfuzz-3.14.6-cp315-cp315-win_amd64.whl", hash = "sha256:d4c5adb921b67dd79ffc0a14f92b9f8df3d012e66aab340b154ed87014229d93", size = 1783415, upload-time = "2026-08-30T21:45:00.091Z" }, + { url = "https://files.pythonhosted.org/packages/06/ba/c6966904eb7b3d1c6344e6c29245447625d156b11e9757b29adc3cb46037/rapidfuzz-3.14.6-cp315-cp315-win_arm64.whl", hash = "sha256:c9d135fb93709d707577da8a7a8ffc7283525a5b6d0ce55aa3724be5639ed65b", size = 1221931, upload-time = "2026-08-30T21:45:02.531Z" }, + { url = "https://files.pythonhosted.org/packages/ae/97/6dd7f10756eb703e11803c5c838191c2151112f632e29f5eacb1ed1cf86c/rapidfuzz-3.14.6-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:dd89abd1c4b3776c3471a817216830bd275441c8344bbda5d51a3bffe1e0fbdf", size = 1985107, upload-time = "2026-08-30T21:45:04.965Z" }, + { url = "https://files.pythonhosted.org/packages/75/4a/be587adefd9539a89cc6016bac44d222cda4c8212856759c82501fd89e4a/rapidfuzz-3.14.6-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:eab2d4680d7f438dbb1d484b187d59a943edea9c83f792c764a0c148a417a60a", size = 1272093, upload-time = "2026-08-30T21:45:07.304Z" }, + { url = "https://files.pythonhosted.org/packages/de/3f/982b2f1b2a16c46d4598829b6b2d7185921f146d5893630f917cb9d27542/rapidfuzz-3.14.6-cp315-cp315t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8683fefdd3484d64a191b3efbc8cbe9162c3eac891fd62d0a1b70e117ffcd434", size = 1371112, upload-time = "2026-08-30T21:45:09.699Z" }, + { url = "https://files.pythonhosted.org/packages/e6/12/2a1fe61cb9f0ac0dc4166bcb016df695047e75251481a197d47aa5ce8ea5/rapidfuzz-3.14.6-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2bc7af3a699371a941aac86dc8a79ac92adeb3c2add2aab02230e76068a0029e", size = 3175780, upload-time = "2026-08-30T21:45:12.266Z" }, + { url = "https://files.pythonhosted.org/packages/8d/01/abd33d0b7595643e598802a07466af388f1560d7b7cb70f442cc292f4067/rapidfuzz-3.14.6-cp315-cp315t-manylinux_2_39_riscv64.whl", hash = "sha256:40c2753e2d4dc96b25f8a25adc23ab0bb6cfd8bc8125a1753ac4b037d6ff6511", size = 1458364, upload-time = "2026-08-30T21:45:14.68Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8e/efc98b0cfb540f41661f6a8bf21b67807e221102e5e8fb1585233b39a3bd/rapidfuzz-3.14.6-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:36a37ddc729c33618d89fa221d3333b9b956dc38cf15d31301e6169d962399a3", size = 2398037, upload-time = "2026-08-30T21:45:17.434Z" }, + { url = "https://files.pythonhosted.org/packages/7f/c1/4d89214a453215d897cc76cd6e13937c8ea5dc9f8217993fe2b1eeaf39a5/rapidfuzz-3.14.6-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:635f242f4bdf05d1477fa409815bd73e5f78896773ace84997bc472ffeef685f", size = 2497044, upload-time = "2026-08-30T21:45:20.328Z" }, + { url = "https://files.pythonhosted.org/packages/a9/2d/70aacf6cb577470bdd6f06890d25ecb7ee8a56baa07b114d5877a93ecedd/rapidfuzz-3.14.6-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:40d0cd9c82083aeb30bae8dee265ae571e6748d0d7b222ddd777f33d95a3b712", size = 4284741, upload-time = "2026-08-30T21:45:22.983Z" }, + { url = "https://files.pythonhosted.org/packages/92/7d/943a04a134a5d333c00d3a77169226defef5e081be9219a765afc176dda0/rapidfuzz-3.14.6-cp315-cp315t-win32.whl", hash = "sha256:15da2b258908eb38853c1a6a58a1d09d9aad9c721e03a68c8ba691cd31dff739", size = 1974478, upload-time = "2026-08-30T21:45:25.475Z" }, + { url = "https://files.pythonhosted.org/packages/21/0e/8356ca3e190e2bcced9b80e374d95b0925c4716b51e65720a55399983f41/rapidfuzz-3.14.6-cp315-cp315t-win_amd64.whl", hash = "sha256:3d502769263318690d4f6638b08483979d1b88cdc7c6f087482eea935fde4031", size = 1823286, upload-time = "2026-08-30T21:45:28.368Z" }, + { url = "https://files.pythonhosted.org/packages/fb/04/a0b0e6324b6384d1ab40feb4d16400af3b3101d38cbd15957edd9d17cbe0/rapidfuzz-3.14.6-cp315-cp315t-win_arm64.whl", hash = "sha256:07c7aa0b1e4b9999a54f9e73317d6743ff85442c8ef7b7fbbe6b190fd37d9e75", size = 1243815, upload-time = "2026-08-30T21:45:31.187Z" }, + { url = "https://files.pythonhosted.org/packages/08/9a/7d4949406e2d391e160ead12036bba05e7c90e09bba77a782d33e7e6a1b0/rapidfuzz-3.14.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0844066900cdc9909ce4ab4fb5ba1d8e0c021252d770f2ea476f3443df1d22ef", size = 1912210, upload-time = "2026-08-30T21:45:33.653Z" }, + { url = "https://files.pythonhosted.org/packages/7c/00/a1a077f5cf90c9fa13b28c721f931529ad02748d418d7750590a388832a9/rapidfuzz-3.14.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:1398bd2c197b79bfc40b615999fd3599dc60265fdd5b59edc18156ae048c4cde", size = 1209219, upload-time = "2026-08-30T21:45:36.035Z" }, + { url = "https://files.pythonhosted.org/packages/48/69/a573c2e5e1b1a4f19e98a8fb3f6a792a44f5b8a067895a2654890ffd35a4/rapidfuzz-3.14.6-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2fc748d1fde4109e5d0dab27f1e61f53b3136a235dfee5a4fb579da44808b6a", size = 1361237, upload-time = "2026-08-30T21:45:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/ea/0b/375ebdfc4ca149e23793bb6b72461954ec64d0acbb826030787e88b90ff3/rapidfuzz-3.14.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b42536675c930cb76b7998bfc4d8e59cb35d8df47f2103020265743b6b2ccd2a", size = 3136631, upload-time = "2026-08-30T21:45:41.426Z" }, + { url = "https://files.pythonhosted.org/packages/55/56/799accc99532ecaaa2c1d04c7e594d6bb8f1afdddc327389c61196741cb8/rapidfuzz-3.14.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:1e6911e3a14971719ddc35af98f181d2e5369ab273a5a3488ab7685d23c31ad5", size = 1722739, upload-time = "2026-08-30T21:45:44.301Z" }, +] + [[package]] name = "referencing" version = "0.36.2" @@ -5141,8 +5255,8 @@ name = "secretstorage" version = "3.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, + { name = "cryptography", marker = "sys_platform != 'darwin' and sys_platform != 'win32'" }, + { name = "jeepney", marker = "sys_platform != 'darwin' and sys_platform != 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/32/8a/ed6747b1cc723c81f526d4c12c1b1d43d07190e1e8258dbf934392fc850e/secretstorage-3.4.1.tar.gz", hash = "sha256:a799acf5be9fb93db609ebaa4ab6e8f1f3ed5ae640e0fa732bfea59e9c3b50e8", size = 19871, upload-time = "2025-11-11T11:30:23.798Z" } wheels = [ @@ -5527,13 +5641,13 @@ resolution-markers = [ "python_full_version < '3.12' and sys_platform == 'darwin'", ] dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx" }, - { name = "setuptools", marker = "python_full_version >= '3.12'" }, - { name = "sympy" }, - { name = "typing-extensions" }, + { name = "filelock", marker = "sys_platform == 'darwin'" }, + { name = "fsspec", marker = "sys_platform == 'darwin'" }, + { name = "jinja2", marker = "sys_platform == 'darwin'" }, + { name = "networkx", marker = "sys_platform == 'darwin'" }, + { name = "setuptools", marker = "python_full_version >= '3.12' and sys_platform == 'darwin'" }, + { name = "sympy", marker = "sys_platform == 'darwin'" }, + { name = "typing-extensions", marker = "sys_platform == 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0-1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:0826ac8e409551e12b2360ac18b4161a838cbd111933e694752f351191331d09", upload-time = "2026-02-06T16:27:14Z" }, @@ -5565,13 +5679,13 @@ resolution-markers = [ "python_full_version < '3.12' and sys_platform != 'darwin' and sys_platform != 'win32'", ] dependencies = [ - { name = "filelock" }, - { name = "fsspec" }, - { name = "jinja2" }, - { name = "networkx" }, - { name = "setuptools", marker = "python_full_version >= '3.12'" }, - { name = "sympy" }, - { name = "typing-extensions" }, + { name = "filelock", marker = "sys_platform != 'darwin'" }, + { name = "fsspec", marker = "sys_platform != 'darwin'" }, + { name = "jinja2", marker = "sys_platform != 'darwin'" }, + { name = "networkx", marker = "sys_platform != 'darwin'" }, + { name = "setuptools", marker = "python_full_version >= '3.12' and sys_platform != 'darwin'" }, + { name = "sympy", marker = "sys_platform != 'darwin'" }, + { name = "typing-extensions", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.10.0%2Bcpu-cp311-cp311-linux_aarch64.whl", hash = "sha256:ce5c113d1f55f8c1f5af05047a24e50d11d293e0cbbb5bf7a75c6c761edd6eaa", upload-time = "2026-01-23T15:10:11Z" },