From ec95997a34320a75c8af2b0fd7c726b87eca49c5 Mon Sep 17 00:00:00 2001 From: Alexander Belikov Date: Mon, 9 Mar 2026 23:14:47 +0100 Subject: [PATCH] WIP: pluggable coref --- pyproject.toml | 2 + run/analysis/benchmark_coref_backends.py | 565 ++++++++++++++++++++++ run/analysis/benchmark_manifest.json | 87 ++++ run/analysis/coref_benchmark_results.csv | 5 + run/analysis/coref_benchmark_results.json | 164 +++++++ run/analysis/samples.jsonl | 79 +++ run/profiling/profile_top.py | 36 +- run/test_self.py | 32 +- test/conftest.py | 12 +- test/test_benchmark_coref_backends.py | 84 ++++ test/test_coref.py | 55 +++ test/test_cuda.py | 3 +- test/test_top.py | 49 +- triel/cli/serve.py | 59 ++- triel/coref.py | 58 ++- triel/coref_adapter.py | 279 +++++++++++ triel/top.py | 126 ++++- uv.lock | 371 +++++++++++++- 18 files changed, 2018 insertions(+), 48 deletions(-) create mode 100644 run/analysis/benchmark_coref_backends.py create mode 100644 run/analysis/benchmark_manifest.json create mode 100644 run/analysis/coref_benchmark_results.csv create mode 100644 run/analysis/coref_benchmark_results.json create mode 100644 run/analysis/samples.jsonl create mode 100644 test/test_benchmark_coref_backends.py create mode 100644 triel/coref_adapter.py diff --git a/pyproject.toml b/pyproject.toml index 02f012e..7893100 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ dependencies = [ "cupy-cuda12x==13.2.0", "curlify>=2.2.1,<3", "dataclass-wizard[timedelta]>=0.34.0,<0.35", + "fastcoref>=2.1.6", "flask-compress>=1.13,<2", "flask-cors>=4.0.1,<5", "flask-limiter>=3.7.0,<4", @@ -48,6 +49,7 @@ dependencies = [ "scipy>=1.14.0,<2", "setuptools>=80.9.0,<81", "simple-websocket>=0.5.2,<0.6", + "spacy-transformers==1.2.5", "spacy==3.5", "suthing>=0.3.0,<0.4", "torch==1.13.0", diff --git a/run/analysis/benchmark_coref_backends.py b/run/analysis/benchmark_coref_backends.py new file mode 100644 index 0000000..3b510b3 --- /dev/null +++ b/run/analysis/benchmark_coref_backends.py @@ -0,0 +1,565 @@ +import csv +import json +import pathlib +import time +from enum import StrEnum +from statistics import mean +from typing import Any + +import click +import spacy +from pydantic import BaseModel, Field +from suthing import FileHandle + +from triel.coref import stitch_coreference +from triel.coref_adapter import ( + CorefBackend, + configure_nlp_coref_backend, + get_coref_resolver, +) +from triel.linking.onto import EntityLinkerManager +from triel.text import normalize_text +from triel.top import text_to_graph_mentions_entities + + +class BenchmarkManifest(BaseModel): + source_path: str + selected_indices: list[int] = Field(default_factory=list) + sample_size: int + notes: str | None = None + + +class FailureKind(StrEnum): + IMPORT = "import" + SETUP = "setup" + INFERENCE = "inference" + OUTPUT_SHAPE = "output_shape" + + +class TrialSummary(BaseModel): + avg_latency_s: float | None = None + p95_latency_s: float | None = None + coverage_rate: float = 0.0 + avg_triples: float = 0.0 + avg_chain_density: float = 0.0 + avg_mention_span_len: float = 0.0 + failure_counts: dict[str, int] = Field(default_factory=dict) + errors: list[str] = Field(default_factory=list) + triples_per_text: list[int] = Field(default_factory=list) + + +def load_pruning_rules() -> dict[str, Any]: + return FileHandle.load("triel.config", "prune_noun_compound_v3.yaml") + + +def load_texts(input_path: pathlib.Path) -> list[str]: + suffix = input_path.suffix.lower() + if suffix == ".jsonl": + texts: list[str] = [] + for line in input_path.read_text(encoding="utf-8").splitlines(): + if not line.strip(): + continue + item = json.loads(line) + text = item.get("text") + if isinstance(text, str) and text.strip(): + texts.append(text) + return texts + + if suffix == ".json": + payload = json.loads(input_path.read_text(encoding="utf-8")) + if isinstance(payload, list): + if all(isinstance(x, str) for x in payload): + return [x for x in payload if x.strip()] + return [ + item["text"] + for item in payload + if isinstance(item, dict) + and isinstance(item.get("text"), str) + and item["text"].strip() + ] + if isinstance(payload, dict) and isinstance(payload.get("texts"), list): + return [x for x in payload["texts"] if isinstance(x, str) and x.strip()] + raise ValueError("JSON input must be list[str], list[{text}], or {texts:[str]}") + + return [ + line.strip() + for line in input_path.read_text(encoding="utf-8").splitlines() + if line.strip() + ] + + +def build_stratified_indices(texts: list[str], sample_size: int) -> list[int]: + if sample_size >= len(texts): + return list(range(len(texts))) + + short = [i for i, txt in enumerate(texts) if len(txt) <= 160] + medium = [i for i, txt in enumerate(texts) if 160 < len(txt) <= 420] + long_ = [i for i, txt in enumerate(texts) if len(txt) > 420] + strata = [short, medium, long_] + + per_bucket = max(1, sample_size // 3) + selected: list[int] = [] + for bucket in strata: + selected.extend(bucket[:per_bucket]) + if len(selected) < sample_size: + all_remaining = [i for i in range(len(texts)) if i not in set(selected)] + selected.extend(all_remaining[: sample_size - len(selected)]) + return sorted(selected[:sample_size]) + + +def analyze_coref_quality( + phrases: list[str], edges_chain_token_global +) -> tuple[float, float]: + if not edges_chain_token_global: + return 0.0, 0.0 + mentions_per_chain: dict[Any, list[tuple[int, ...]]] = {} + mention_lengths: list[int] = [] + for chain, mention in edges_chain_token_global: + mentions_per_chain.setdefault(chain, []).append(mention) + mention_lengths.append(len(mention)) + chain_density = ( + (sum(len(v) for v in mentions_per_chain.values()) / len(mentions_per_chain)) + if mentions_per_chain + else 0.0 + ) + avg_mention_span_len = mean(mention_lengths) if mention_lengths else 0.0 + return float(chain_density), float(avg_mention_span_len) + + +def classify_failure(exc: Exception) -> FailureKind: + msg = str(exc).lower() + if "no module named" in msg or "cannot import" in msg: + return FailureKind.IMPORT + if "add_pipe" in msg or "factory" in msg or "not in pipeline" in msg: + return FailureKind.SETUP + if "shape" in msg or "tuple" in msg or "span" in msg: + return FailureKind.OUTPUT_SHAPE + return FailureKind.INFERENCE + + +def benchmark_backend_trial( + texts: list[str], model_name: str, backend: CorefBackend, rules: dict[str, Any] +) -> TrialSummary: + try: + nlp = spacy.load(model_name) + nlp = configure_nlp_coref_backend(nlp, backend) + resolver = get_coref_resolver(backend) + except Exception as e: + return TrialSummary( + failure_counts={FailureKind.SETUP.value: len(texts)}, + errors=[str(e)], + triples_per_text=[0 for _ in texts], + ) + + elm = EntityLinkerManager({}) + latencies: list[float] = [] + coverage_flags: list[bool] = [] + triples_per_text: list[int] = [] + chain_densities: list[float] = [] + mention_spans: list[float] = [] + errors: list[str] = [] + failure_counts: dict[str, int] = {kind.value: 0 for kind in FailureKind} + + for text in texts: + try: + t0 = time.perf_counter() + response = text_to_graph_mentions_entities( + text=text, + nlp=nlp, + rules=rules, + elm=elm, + coref_resolver=resolver, + ) + latencies.append(time.perf_counter() - t0) + triples_per_text.append(len(response.triples)) + + phrases = normalize_text(text, nlp) + edges_chain_token_global, _ = stitch_coreference( + phrases_for_coref=phrases, + nlp=nlp, + window_size=2, + coref_resolver=resolver, + ) + coverage_flags.append(bool(edges_chain_token_global)) + chain_density, avg_mention_span_len = analyze_coref_quality( + phrases, edges_chain_token_global + ) + chain_densities.append(chain_density) + mention_spans.append(avg_mention_span_len) + except Exception as e: + fk = classify_failure(e).value + failure_counts[fk] = failure_counts.get(fk, 0) + 1 + errors.append(str(e)) + latencies.append(float("nan")) + triples_per_text.append(0) + coverage_flags.append(False) + chain_densities.append(0.0) + mention_spans.append(0.0) + + valid_latencies = [x for x in latencies if x == x] + return TrialSummary( + avg_latency_s=mean(valid_latencies) if valid_latencies else None, + p95_latency_s=( + sorted(valid_latencies)[int(0.95 * (len(valid_latencies) - 1))] + if valid_latencies + else None + ), + coverage_rate=( + (sum(1 for x in coverage_flags if x) / len(coverage_flags)) + if coverage_flags + else 0.0 + ), + avg_triples=mean(triples_per_text) if triples_per_text else 0.0, + avg_chain_density=mean(chain_densities) if chain_densities else 0.0, + avg_mention_span_len=mean(mention_spans) if mention_spans else 0.0, + failure_counts=failure_counts, + errors=errors[:10], + triples_per_text=triples_per_text, + ) + + +def benchmark_backend( + texts: list[str], + model_name: str, + backend: CorefBackend, + rules: dict[str, Any], +) -> dict[str, Any]: + raise NotImplementedError("Use benchmark_backend_with_trials") + + +def benchmark_backend_with_trials( + texts: list[str], + model_name: str, + backend: CorefBackend, + rules: dict[str, Any], + trials: int, +) -> dict[str, Any]: + trial_summaries = [ + benchmark_backend_trial(texts, model_name, backend, rules) + for _ in range(trials) + ] + avg_latency = [ + x.avg_latency_s for x in trial_summaries if x.avg_latency_s is not None + ] + p95_latency = [ + x.p95_latency_s for x in trial_summaries if x.p95_latency_s is not None + ] + coverage = [x.coverage_rate for x in trial_summaries] + avg_triples = [x.avg_triples for x in trial_summaries] + chain_density = [x.avg_chain_density for x in trial_summaries] + mention_span = [x.avg_mention_span_len for x in trial_summaries] + + failure_counts: dict[str, int] = {kind.value: 0 for kind in FailureKind} + errors: list[str] = [] + for summary in trial_summaries: + for key, value in summary.failure_counts.items(): + failure_counts[key] = failure_counts.get(key, 0) + value + errors.extend(summary.errors) + + triples_per_text = trial_summaries[-1].triples_per_text if trial_summaries else [] + return { + "backend": backend.value, + "sample_size": len(texts), + "trials": trials, + "avg_latency_s": mean(avg_latency) if avg_latency else None, + "p95_latency_s": mean(p95_latency) if p95_latency else None, + "coverage_rate": mean(coverage) if coverage else 0.0, + "avg_triples": mean(avg_triples) if avg_triples else 0.0, + "avg_chain_density": mean(chain_density) if chain_density else 0.0, + "avg_mention_span_len": mean(mention_span) if mention_span else 0.0, + "failure_counts": failure_counts, + "errors": errors[:10], + "triples_per_text": triples_per_text, + } + + +@click.command(context_settings={"help_option_names": ["-h", "--help"]}) +@click.option( + "--input-path", + type=click.Path(path_type=pathlib.Path, exists=True), + required=True, + help="Path to input corpus (.txt, .json, or .jsonl).", +) +@click.option( + "--sample-size", + type=click.IntRange(min=1), + default=50, + show_default=True, + help="Number of texts to benchmark (recommendation: 50-100).", +) +@click.option( + "--trials", + type=click.IntRange(min=1), + default=1, + show_default=True, + help="Number of repeated trials per backend.", +) +@click.option( + "--stratified-sampling/--no-stratified-sampling", + default=True, + show_default=True, + help="Use stratified sampling by text length buckets.", +) +@click.option( + "--model", + type=str, + default="en_core_web_trf", + show_default=True, + help="spaCy model name.", +) +@click.option( + "--backend", + "backends", + type=click.Choice([item.value for item in CorefBackend]), + multiple=True, + default=( + # CorefBackend.COREFEREE.value, + CorefBackend.FASTCOREF.value, + ), + show_default=True, + help="Coreference backend(s) to benchmark.", +) +@click.option( + "--output-json", + type=click.Path(path_type=pathlib.Path), + default=pathlib.Path("coref_benchmark_results.json"), + show_default=True, +) +@click.option( + "--output-csv", + type=click.Path(path_type=pathlib.Path), + default=pathlib.Path("coref_benchmark_results.csv"), + show_default=True, +) +@click.option( + "--manifest-path", + type=click.Path(path_type=pathlib.Path, exists=True), + default=None, + help="Path to fixed benchmark manifest with selected indices.", +) +@click.option( + "--write-manifest", + type=click.Path(path_type=pathlib.Path), + default=None, + help="Optional path to save the selected subset manifest.", +) +@click.option( + "--min-coverage-rate", + type=float, + default=0.6, + show_default=True, + help="Promotion gate: minimum coverage rate.", +) +@click.option( + "--max-latency-ratio-vs-coreferee", + type=float, + default=1.8, + show_default=True, + help="Promotion gate: max avg latency ratio vs coreferee.", +) +@click.option( + "--max-triple-drift-vs-coreferee", + type=float, + default=3.0, + show_default=True, + help="Promotion gate: max average triple-count drift vs coreferee.", +) +def main( + input_path: pathlib.Path, + sample_size: int, + trials: int, + stratified_sampling: bool, + model: str, + backends: tuple[str, ...], + output_json: pathlib.Path, + output_csv: pathlib.Path, + manifest_path: pathlib.Path | None, + write_manifest: pathlib.Path | None, + min_coverage_rate: float, + max_latency_ratio_vs_coreferee: float, + max_triple_drift_vs_coreferee: float, +) -> None: + texts = load_texts(input_path) + if not texts: + raise ValueError(f"No texts found in {input_path}") + + if manifest_path is not None: + manifest = BenchmarkManifest( + **json.loads(manifest_path.read_text(encoding="utf-8")) + ) + source_path = pathlib.Path(manifest.source_path) + source_texts = load_texts(source_path) + selected_indices = manifest.selected_indices[: manifest.sample_size] + texts = [source_texts[i] for i in selected_indices if i < len(source_texts)] + sample_size = len(texts) + else: + if sample_size > len(texts): + sample_size = len(texts) + selected_indices = ( + build_stratified_indices(texts, sample_size) + if stratified_sampling + else list(range(sample_size)) + ) + texts = [texts[i] for i in selected_indices] + if write_manifest is not None: + manifest = BenchmarkManifest( + source_path=input_path.as_posix(), + selected_indices=selected_indices, + sample_size=len(selected_indices), + notes="Generated by benchmark_coref_backends.py", + ) + write_manifest.write_text( + manifest.model_dump_json(indent=2), encoding="utf-8" + ) + + rules = load_pruning_rules() + baseline_backend = CorefBackend.COREFEREE + if baseline_backend.value not in backends: + bench_backends = (baseline_backend.value, *backends) + else: + bench_backends = backends + + results = [] + for backend_name in bench_backends: + backend = CorefBackend(backend_name) + click.echo( + f"Running backend={backend.value} on {len(texts)} texts across {trials} trial(s)..." + ) + try: + result = benchmark_backend_with_trials( + texts=texts, + model_name=model, + backend=backend, + rules=rules, + trials=trials, + ) + except Exception as e: + result = { + "backend": backend.value, + "sample_size": len(texts), + "trials": trials, + "avg_latency_s": None, + "p95_latency_s": None, + "coverage_rate": 0.0, + "avg_triples": 0.0, + "avg_chain_density": 0.0, + "avg_mention_span_len": 0.0, + "failure_counts": {FailureKind.INFERENCE.value: len(texts) * trials}, + "errors": [str(e)], + "triples_per_text": [0 for _ in texts], + } + results.append(result) + + baseline = next( + (r for r in results if r["backend"] == baseline_backend.value), None + ) + if baseline is not None: + baseline_triples = baseline["triples_per_text"] + baseline_latency = baseline["avg_latency_s"] + for result in results: + current = result["triples_per_text"] + if len(current) == len(baseline_triples): + drift = [abs(a - b) for a, b in zip(current, baseline_triples)] + result["avg_triple_count_drift_vs_coreferee"] = ( + mean(drift) if drift else 0.0 + ) + else: + result["avg_triple_count_drift_vs_coreferee"] = None + if baseline_latency and result["avg_latency_s"]: + result["latency_ratio_vs_coreferee"] = ( + result["avg_latency_s"] / baseline_latency + ) + else: + result["latency_ratio_vs_coreferee"] = None + else: + for result in results: + result["avg_triple_count_drift_vs_coreferee"] = None + result["latency_ratio_vs_coreferee"] = None + + gates = [] + for result in results: + if result["backend"] == CorefBackend.COREFEREE.value: + continue + gate_pass = ( + (result["coverage_rate"] >= min_coverage_rate) + and ( + result["latency_ratio_vs_coreferee"] is None + or result["latency_ratio_vs_coreferee"] + <= max_latency_ratio_vs_coreferee + ) + and ( + result["avg_triple_count_drift_vs_coreferee"] is None + or result["avg_triple_count_drift_vs_coreferee"] + <= max_triple_drift_vs_coreferee + ) + ) + gates.append( + { + "backend": result["backend"], + "pass": gate_pass, + "coverage_rate": result["coverage_rate"], + "latency_ratio_vs_coreferee": result["latency_ratio_vs_coreferee"], + "avg_triple_count_drift_vs_coreferee": result[ + "avg_triple_count_drift_vs_coreferee" + ], + } + ) + + payload = { + "results": results, + "promotion_gates": { + "min_coverage_rate": min_coverage_rate, + "max_latency_ratio_vs_coreferee": max_latency_ratio_vs_coreferee, + "max_triple_drift_vs_coreferee": max_triple_drift_vs_coreferee, + "evaluation": gates, + }, + } + output_json.write_text(json.dumps(payload, indent=2), encoding="utf-8") + + with output_csv.open("w", encoding="utf-8", newline="") as f: + writer = csv.DictWriter( + f, + fieldnames=[ + "backend", + "sample_size", + "trials", + "avg_latency_s", + "p95_latency_s", + "coverage_rate", + "avg_triples", + "avg_chain_density", + "avg_mention_span_len", + "latency_ratio_vs_coreferee", + "avg_triple_count_drift_vs_coreferee", + "failure_counts", + "errors", + ], + ) + writer.writeheader() + for item in payload["results"]: + writer.writerow( + { + "backend": item["backend"], + "sample_size": item["sample_size"], + "trials": item["trials"], + "avg_latency_s": item["avg_latency_s"], + "p95_latency_s": item["p95_latency_s"], + "coverage_rate": item["coverage_rate"], + "avg_triples": item["avg_triples"], + "avg_chain_density": item["avg_chain_density"], + "avg_mention_span_len": item["avg_mention_span_len"], + "latency_ratio_vs_coreferee": item["latency_ratio_vs_coreferee"], + "avg_triple_count_drift_vs_coreferee": item[ + "avg_triple_count_drift_vs_coreferee" + ], + "failure_counts": json.dumps(item["failure_counts"]), + "errors": " | ".join(item["errors"]), + } + ) + + click.echo(f"Wrote JSON results to {output_json}") + click.echo(f"Wrote CSV summary to {output_csv}") + + +if __name__ == "__main__": + main() diff --git a/run/analysis/benchmark_manifest.json b/run/analysis/benchmark_manifest.json new file mode 100644 index 0000000..eac816e --- /dev/null +++ b/run/analysis/benchmark_manifest.json @@ -0,0 +1,87 @@ +{ + "source_path": "run/analysis/benchmark_sample_texts.jsonl", + "selected_indices": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79 + ], + "sample_size": 80, + "notes": "Fixed benchmark subset for phase 2 canary and backend gate evaluation." +} diff --git a/run/analysis/coref_benchmark_results.csv b/run/analysis/coref_benchmark_results.csv new file mode 100644 index 0000000..a8a6be7 --- /dev/null +++ b/run/analysis/coref_benchmark_results.csv @@ -0,0 +1,5 @@ +backend,sample_size,trials,avg_latency_s,p95_latency_s,coverage_rate,avg_triples,avg_chain_density,avg_mention_span_len,latency_ratio_vs_coreferee,avg_triple_count_drift_vs_coreferee,failure_counts,errors +coreferee,50,1,0.22466238578082995,0.2284006059926469,0.28,2.6,0.6,0.28,1.0,0,"{""import"": 0, ""setup"": 0, ""inference"": 0, ""output_shape"": 0}", +fastcoref,50,1,,,0.0,0.0,0.0,0.0,,2.6,"{""import"": 0, ""setup"": 50, ""inference"": 0, ""output_shape"": 0}","Failed to configure coref backend 'fastcoref': [E002] Can't find factory for 'fastcoref' for language English (en). This usually happens when spaCy calls `nlp.create_pipe` with a custom component name that's not registered on the current language class. If you're using a Transformer, make sure to install 'spacy-transformers'. If you're using a custom component, make sure you've added the decorator `@Language.component` (for function components) or `@Language.factory` (for class components). + +Available factories: attribute_ruler, tok2vec, merge_noun_chunks, merge_entities, merge_subtokens, token_splitter, doc_cleaner, parser, beam_parser, lemmatizer, trainable_lemmatizer, entity_linker, ner, beam_ner, entity_ruler, tagger, morphologizer, senter, sentencizer, textcat, spancat, future_entity_ruler, span_ruler, textcat_multilabel, en.lemmatizer" diff --git a/run/analysis/coref_benchmark_results.json b/run/analysis/coref_benchmark_results.json new file mode 100644 index 0000000..256e7cf --- /dev/null +++ b/run/analysis/coref_benchmark_results.json @@ -0,0 +1,164 @@ +{ + "results": [ + { + "backend": "coreferee", + "sample_size": 50, + "trials": 1, + "avg_latency_s": 0.22466238578082995, + "p95_latency_s": 0.2284006059926469, + "coverage_rate": 0.28, + "avg_triples": 2.6, + "avg_chain_density": 0.6, + "avg_mention_span_len": 0.28, + "failure_counts": { + "import": 0, + "setup": 0, + "inference": 0, + "output_shape": 0 + }, + "errors": [], + "triples_per_text": [ + 1, + 6, + 2, + 4, + 3, + 5, + 2, + 2, + 3, + 3, + 2, + 3, + 2, + 4, + 2, + 3, + 2, + 2, + 4, + 3, + 3, + 3, + 3, + 2, + 3, + 2, + 2, + 3, + 3, + 2, + 3, + 1, + 2, + 2, + 3, + 2, + 3, + 2, + 2, + 1, + 2, + 3, + 3, + 4, + 3, + 2, + 2, + 2, + 2, + 2 + ], + "avg_triple_count_drift_vs_coreferee": 0, + "latency_ratio_vs_coreferee": 1.0 + }, + { + "backend": "fastcoref", + "sample_size": 50, + "trials": 1, + "avg_latency_s": null, + "p95_latency_s": null, + "coverage_rate": 0.0, + "avg_triples": 0.0, + "avg_chain_density": 0.0, + "avg_mention_span_len": 0.0, + "failure_counts": { + "import": 0, + "setup": 50, + "inference": 0, + "output_shape": 0 + }, + "errors": [ + "Failed to configure coref backend 'fastcoref': [E002] Can't find factory for 'fastcoref' for language English (en). This usually happens when spaCy calls `nlp.create_pipe` with a custom component name that's not registered on the current language class. If you're using a Transformer, make sure to install 'spacy-transformers'. If you're using a custom component, make sure you've added the decorator `@Language.component` (for function components) or `@Language.factory` (for class components).\n\nAvailable factories: attribute_ruler, tok2vec, merge_noun_chunks, merge_entities, merge_subtokens, token_splitter, doc_cleaner, parser, beam_parser, lemmatizer, trainable_lemmatizer, entity_linker, ner, beam_ner, entity_ruler, tagger, morphologizer, senter, sentencizer, textcat, spancat, future_entity_ruler, span_ruler, textcat_multilabel, en.lemmatizer" + ], + "triples_per_text": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "avg_triple_count_drift_vs_coreferee": 2.6, + "latency_ratio_vs_coreferee": null + } + ], + "promotion_gates": { + "min_coverage_rate": 0.6, + "max_latency_ratio_vs_coreferee": 1.8, + "max_triple_drift_vs_coreferee": 3.0, + "evaluation": [ + { + "backend": "fastcoref", + "pass": false, + "coverage_rate": 0.0, + "latency_ratio_vs_coreferee": null, + "avg_triple_count_drift_vs_coreferee": 2.6 + } + ] + } +} diff --git a/run/analysis/samples.jsonl b/run/analysis/samples.jsonl new file mode 100644 index 0000000..605986c --- /dev/null +++ b/run/analysis/samples.jsonl @@ -0,0 +1,79 @@ +{"text":"Dr. Alvarez analyzed TP53 mutations in lung cancer samples. The gene was frequently altered, and the biomarker predicted poor survival."} +{"text":"Pfizer announced new data on its mRNA vaccine. The company said it expects the shot to receive FDA review next quarter."} +{"text":"Researchers measured IL-6 levels in patients with severe COVID-19. The cytokine was significantly elevated, and it correlated with disease severity."} +{"text":"NVIDIA reported record GPU sales this quarter. The chipmaker attributed the growth to demand for AI accelerators."} +{"text":"The team trained a CNN to classify pathology slides. The neural network achieved higher accuracy than the baseline model."} +{"text":"Goldman Sachs upgraded Tesla to 'buy'. The bank said the EV maker could expand margins next year."} +{"text":"Scientists studied BRCA1 expression in ovarian tumors. The protein appeared suppressed in aggressive cases."} +{"text":"The startup deployed a Kubernetes cluster for its inference pipeline. The system automatically scaled when traffic increased."} +{"text":"The CDC issued a warning about a new influenza strain. The agency said it is monitoring the virus closely."} +{"text":"Researchers sequenced the SARS-CoV-2 genome from several patients. The virus showed a mutation in the spike protein."} +{"text":"Apple introduced a new M-series processor. The chip significantly improves battery efficiency."} +{"text":"The economists estimated GDP growth for Q3. They believe the expansion will slow next year."} +{"text":"A study examined PD-1 expression in T cells. The receptor was highly expressed in exhausted lymphocytes."} +{"text":"OpenAI released a new LLM for enterprise users. The company claims the model handles complex reasoning tasks."} +{"text":"The firm reported EBITDA above analyst expectations. The result pushed its stock higher."} +{"text":"Researchers treated mice with a CRISPR-based therapy. The gene-editing approach corrected the mutation."} +{"text":"The WHO updated its malaria guidelines. The organization recommends broader use of the vaccine."} +{"text":"The engineers implemented a transformer architecture. The model processed sequences more efficiently than the previous RNN."} +{"text":"A biotech company tested an EGFR inhibitor in phase II trials. The drug reduced tumor growth in several patients."} +{"text":"The hedge fund increased its position in Nvidia. The investor believes the chip designer will benefit from AI demand."} +{"text":"Scientists measured C-reactive protein in the cohort. The biomarker indicated systemic inflammation."} +{"text":"The startup launched a SaaS platform for supply chains. The service analyzes logistics data in real time."} +{"text":"Researchers investigated tau accumulation in Alzheimer's disease. The protein formed aggregates in neuronal tissue."} +{"text":"The ECB raised interest rates by 25 basis points. The central bank said inflation remains too high."} +{"text":"The team benchmarked an LSTM model on the dataset. The architecture performed well on temporal prediction tasks."} +{"text":"A study measured TNF-alpha levels after treatment. The cytokine decreased significantly."} +{"text":"Amazon reported strong AWS revenue growth. The cloud division remains the firm's most profitable unit."} +{"text":"The laboratory detected elevated PSA in several subjects. The biomarker suggested possible prostate abnormalities."} +{"text":"Researchers built a graph neural network for drug discovery. The system identified several promising compounds."} +{"text":"Moody's downgraded the company's credit rating. The agency cited declining cash flow."} +{"text":"Scientists observed overexpression of HER2 in tumor biopsies. The receptor is a known driver of breast cancer."} +{"text":"The firm announced a share buyback program. The board believes the stock is undervalued."} +{"text":"A research group mapped the microbiome of the gut. The community of bacteria varied widely among individuals."} +{"text":"The developers migrated their services to a microservice architecture. The platform became easier to maintain."} +{"text":"The FDA approved a new monoclonal antibody. The regulator said the therapy targets a rare disease."} +{"text":"Researchers examined STAT3 activation in immune cells. The transcription factor regulated inflammatory pathways."} +{"text":"The company unveiled a new VR headset. The device features improved optics and tracking."} +{"text":"The IMF revised its global growth forecast. The institution warned of rising debt risks."} +{"text":"Scientists evaluated a small molecule inhibitor of JAK2. The compound suppressed signaling in vitro."} +{"text":"The trading desk reported unusual activity in oil futures. The traders believe a supply shock may be coming."} +{"text":"A study analyzed VEGF signaling in endothelial cells. The pathway controls blood vessel formation."} +{"text":"The AI team fine-tuned a BERT model on legal documents. The language model improved contract classification."} +{"text":"Researchers isolated a new strain of E. coli from water samples. The bacterium carried antibiotic resistance genes."} +{"text":"The venture firm invested $30M in a robotics startup. The fund believes the company will disrupt warehouse automation."} +{"text":"Scientists detected increased IL-10 production after therapy. The cytokine reduced inflammatory signaling."} +{"text":"The developers optimized a CUDA kernel for matrix multiplication. The routine now runs twice as fast."} +{"text":"The SEC opened an investigation into the firm's disclosures. The regulator is reviewing its filings."} +{"text":"Researchers analyzed APOE variants in a dementia cohort. The allele was strongly associated with risk."} +{"text":"The telecom company rolled out 5G infrastructure nationwide. The network promises lower latency."} +{"text":"A team studied oxidative stress markers in neurons. The indicators suggested mitochondrial dysfunction."} +{"text":"The quant fund trained a reinforcement learning agent. The algorithm optimized its trading strategy."} +{"text":"Scientists measured HbA1c in diabetic patients. The marker reflects long-term glucose levels."} +{"text":"The company filed an S-1 ahead of its IPO. The document revealed strong revenue growth."} +{"text":"Researchers examined NF-kB activation in macrophages. The pathway triggered immune responses."} +{"text":"The bank launched a new ETF tracking semiconductor stocks. The product quickly attracted investors."} +{"text":"A study reported elevated D-dimer levels in severe cases. The biomarker indicated coagulation abnormalities."} +{"text":"The engineers deployed a REST API for the analytics service. The interface allows external clients to query the system."} +{"text":"The regulator fined the firm for AML violations. The authority said compliance failures were systemic."} +{"text":"Scientists observed increased lactate production in tumor cells. The metabolite reflects altered metabolism."} +{"text":"The researchers pre-trained a diffusion model on image datasets. The system generated realistic samples."} +{"text":"The investment bank revised its oil price forecast. The analysts expect tighter supply."} +{"text":"A lab tested a CAR-T therapy targeting CD19. The treatment eliminated malignant B cells."} +{"text":"The startup built a knowledge graph from clinical trials. The graph links drugs, genes, and outcomes."} +{"text":"The agency released new PM2.5 pollution data. The report shows rising concentrations in several cities."} +{"text":"Researchers evaluated mitochondrial DNA mutations in muscle tissue. The variants impaired energy production."} +{"text":"The firm secured a patent for its lithium-sulfur battery. The invention could increase energy density."} +{"text":"Scientists studied microRNA expression in cancer cells. The small RNAs regulated multiple genes."} +{"text":"The trading algorithm reacted to CPI data. The model immediately adjusted its positions."} +{"text":"The hospital implemented an EHR system. The platform integrates patient records across departments."} +{"text":"Researchers tested a kinase inhibitor targeting BRAF. The drug blocked tumor signaling."} +{"text":"The aerospace company launched a new satellite. The spacecraft will monitor climate variables."} +{"text":"A paper examined epigenetic methylation patterns in stem cells. The modification influenced gene expression."} +{"text":"The consulting firm analyzed supply chain disruptions. The report attributes them to geopolitical tensions."} +{"text":"Scientists detected prion proteins in the samples. The agents are associated with neurodegenerative disease."} +{"text":"The team evaluated a recommender system using AUC metrics. The model outperformed collaborative filtering."} +{"text":"The central bank published minutes from its FOMC meeting. The document revealed concerns about inflation."} +{"text":"Researchers observed increased cortisol levels in stressed subjects. The hormone affects metabolism."} +{"text":"The company upgraded its data warehouse to Snowflake. The platform improved query performance."} +{"text":"Scientists mapped CRP expression across tissues. The protein acts as an inflammatory marker."} \ No newline at end of file diff --git a/run/profiling/profile_top.py b/run/profiling/profile_top.py index 67e5e22..ba35ce0 100644 --- a/run/profiling/profile_top.py +++ b/run/profiling/profile_top.py @@ -19,6 +19,11 @@ from spacy.language import Language from suthing import SProfiler +from triel.coref_adapter import ( + CorefBackend, + configure_nlp_coref_backend, + get_coref_resolver, +) from triel.linking.onto import EntityLinkerManager from triel.top import cast_response_redux, text_to_graph_mentions_entities @@ -36,16 +41,21 @@ def load_pruning_rules() -> dict[str, Any]: return yaml.safe_load(raw_rules) or {} -def build_nlp(model_name: str) -> Language: +def build_nlp( + model_name: str, coref_backend: CorefBackend = CorefBackend.COREFEREE +) -> Language: nlp = spacy.load(model_name) - if "coreferee" not in nlp.pipe_names: - nlp.add_pipe("coreferee") - return nlp + return configure_nlp_coref_backend(nlp, coref_backend) -def run_profile(text: str, model_name: str) -> tuple[dict[str, Any], Any]: +def run_profile( + text: str, + model_name: str, + coref_backend: CorefBackend = CorefBackend.COREFEREE, +) -> tuple[dict[str, Any], Any]: rules = load_pruning_rules() - nlp = build_nlp(model_name) + nlp = build_nlp(model_name, coref_backend=coref_backend) + coref_resolver = get_coref_resolver(coref_backend) entity_linker_manager = EntityLinkerManager({}) profiler = SProfiler() @@ -54,6 +64,7 @@ def run_profile(text: str, model_name: str) -> tuple[dict[str, Any], Any]: nlp, rules, entity_linker_manager, + coref_resolver=coref_resolver, _profiler=profiler, ) response_redux = cast_response_redux(response).model_dump() @@ -73,8 +84,17 @@ def run_profile(text: str, model_name: str) -> tuple[dict[str, Any], Any]: show_default=True, help="spaCy model to load.", ) -def main(text: str, model: str) -> None: - response_redux, stats = run_profile(text, model) +@click.option( + "--coref-backend", + default=CorefBackend.COREFEREE.value, + show_default=True, + type=click.Choice([item.value for item in CorefBackend]), + help="Coreference backend to use.", +) +def main(text: str, model: str, coref_backend: str) -> None: + response_redux, stats = run_profile( + text, model, coref_backend=CorefBackend(coref_backend) + ) click.echo("== TriEL response (redux) ==") click.echo(json.dumps(response_redux, indent=2, default=str)) diff --git a/run/test_self.py b/run/test_self.py index fdc7fe9..25f9b07 100644 --- a/run/test_self.py +++ b/run/test_self.py @@ -6,6 +6,11 @@ import spacy from suthing import FileHandle +from triel.coref_adapter import ( + CorefBackend, + configure_nlp_coref_backend, + get_coref_resolver, +) from triel.linking.onto import EntityLinkerManager from triel.top import ( cast_response_entity_representation, @@ -25,7 +30,21 @@ ) @click.option("--phrase-indexes", "-i", type=click.INT, multiple=True) @click.option("--localhost-linkers", type=click.STRING, multiple=True) -def run(host, conf_el_path, input_path, output, phrase_indexes, localhost_linkers): +@click.option( + "--coref-backend", + default=CorefBackend.COREFEREE.value, + show_default=True, + type=click.Choice([item.value for item in CorefBackend]), +) +def run( + host, + conf_el_path, + input_path, + output, + phrase_indexes, + localhost_linkers, + coref_backend, +): el_conf = FileHandle.load(fpath=conf_el_path) for c in el_conf["linkers"]: if "host" not in c: @@ -36,8 +55,10 @@ def run(host, conf_el_path, input_path, output, phrase_indexes, localhost_linker elm = EntityLinkerManager.from_dict(el_conf) rules = FileHandle.load("triel.config", "prune_noun_compound_v3.yaml") + backend = CorefBackend(coref_backend) nlp = spacy.load("en_core_web_trf") - nlp.add_pipe("coreferee") + nlp = configure_nlp_coref_backend(nlp, backend) + coref_resolver = get_coref_resolver(backend) logger.info("nlp loaded") @@ -51,7 +72,12 @@ def run(host, conf_el_path, input_path, output, phrase_indexes, localhost_linker for name, data in inputs: response = text_to_graph_mentions_entities( - data["text"], nlp, rules, elm, ix_phrases=phrase_indexes + data["text"], + nlp, + rules, + elm, + ix_phrases=phrase_indexes, + coref_resolver=coref_resolver, ) response_redux = cast_response_redux(response) diff --git a/test/conftest.py b/test/conftest.py index 69b4492..29e3ac7 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -7,6 +7,11 @@ import suthing from suthing import FileHandle +from triel.coref_adapter import ( + CorefBackend, + configure_nlp_coref_backend, + get_coref_resolver, +) from triel.linking.onto import APISpec, EntityLinker, LocalEntity @@ -153,10 +158,15 @@ def lconf(el_conf): @pytest.fixture(scope="module") def nlp_fixture(): nlp = spacy.load("en_core_web_trf") - nlp.add_pipe("coreferee") + nlp = configure_nlp_coref_backend(nlp, CorefBackend.COREFEREE) return nlp +@pytest.fixture(scope="module") +def coref_resolver_fixture(): + return get_coref_resolver(CorefBackend.COREFEREE) + + @pytest.fixture(scope="module") def bern_example(): return suthing.FileHandle.load("test.data", "bern.v2.response.json") diff --git a/test/test_benchmark_coref_backends.py b/test/test_benchmark_coref_backends.py new file mode 100644 index 0000000..8effffa --- /dev/null +++ b/test/test_benchmark_coref_backends.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +import json + +from click.testing import CliRunner + +from run.analysis import benchmark_coref_backends as bench + + +def test_benchmark_cli_help(): + runner = CliRunner() + result = runner.invoke(bench.main, ["--help"]) + assert result.exit_code == 0 + assert "sample-size" in result.output + assert "trials" in result.output + assert "manifest-path" in result.output + + +def test_benchmark_output_schema(tmp_path, monkeypatch): + input_path = tmp_path / "texts.jsonl" + input_path.write_text( + "\n".join( + [ + json.dumps({"text": "One sentence."}), + json.dumps({"text": "Another sentence."}), + ] + ), + encoding="utf-8", + ) + output_json = tmp_path / "out.json" + output_csv = tmp_path / "out.csv" + + monkeypatch.setattr(bench, "load_pruning_rules", lambda: {}) + monkeypatch.setattr( + bench, + "benchmark_backend_with_trials", + lambda **kwargs: { + "backend": kwargs["backend"].value, + "sample_size": len(kwargs["texts"]), + "trials": kwargs["trials"], + "avg_latency_s": 0.1, + "p95_latency_s": 0.2, + "coverage_rate": 1.0, + "avg_triples": 2.0, + "avg_chain_density": 1.5, + "avg_mention_span_len": 1.2, + "failure_counts": { + "import": 0, + "setup": 0, + "inference": 0, + "output_shape": 0, + }, + "errors": [], + "triples_per_text": [2 for _ in kwargs["texts"]], + }, + ) + + runner = CliRunner() + result = runner.invoke( + bench.main, + [ + "--input-path", + input_path.as_posix(), + "--sample-size", + "2", + "--trials", + "2", + "--backend", + "none", + "--output-json", + output_json.as_posix(), + "--output-csv", + output_csv.as_posix(), + ], + ) + assert result.exit_code == 0 + payload = json.loads(output_json.read_text(encoding="utf-8")) + assert "results" in payload + assert "promotion_gates" in payload + assert payload["results"] + row = payload["results"][0] + assert "failure_counts" in row + assert "avg_chain_density" in row + assert output_csv.exists() diff --git a/test/test_coref.py b/test/test_coref.py index 5882d87..96537eb 100644 --- a/test/test_coref.py +++ b/test/test_coref.py @@ -1,9 +1,21 @@ +import pytest +from typing import TYPE_CHECKING, cast + +from triel.coref_adapter import ( + CorefBackend, + CorefSetupError, + get_coref_resolver, + get_ready_coref_runtime, +) from triel.coref import ( render_coref_maps_wrapper, stitch_coreference, text_to_coref_classes, ) +if TYPE_CHECKING: + from spacy import Language + def test_coref_maps(doc_coref): edges_chain_token, edges_chain_order = render_coref_maps_wrapper(doc_coref) @@ -58,3 +70,46 @@ def test_stitching(nlp_fixture, phrases_for_coref, fig_path): assert len(edges_chain_token_global) == 13 assert len(edges_chain_order_global) == 1 + + +def test_coref_backend_none_returns_empty(nlp_fixture, phrases_for_coref): + resolver = get_coref_resolver(CorefBackend.NONE) + edges_chain_token_global, edges_chain_order_global = stitch_coreference( + nlp=nlp_fixture, + phrases_for_coref=phrases_for_coref, + window_size=2, + coref_resolver=resolver, + ) + assert edges_chain_token_global == set() + assert edges_chain_order_global == [] + + +def test_get_ready_coref_runtime_fallback_to_none(): + class FakeNLP: + pipe_names = [] + + def add_pipe(self, _): + raise RuntimeError("boom") + + nlp, resolver = get_ready_coref_runtime( + cast("Language", FakeNLP()), + CorefBackend.COREFEREE, + fallback_to_none=True, + ) + assert nlp is not None + assert resolver.backend == CorefBackend.NONE + + +def test_get_ready_coref_runtime_raises_without_fallback(): + class FakeNLP: + pipe_names = [] + + def add_pipe(self, _): + raise RuntimeError("boom") + + with pytest.raises(CorefSetupError): + get_ready_coref_runtime( + cast("Language", FakeNLP()), + CorefBackend.COREFEREE, + fallback_to_none=False, + ) diff --git a/test/test_cuda.py b/test/test_cuda.py index 7728b46..db5f057 100644 --- a/test/test_cuda.py +++ b/test/test_cuda.py @@ -1,4 +1,5 @@ import torch +from torch import version as torch_version def test_cuda(): @@ -6,7 +7,7 @@ def test_cuda(): print(f"Is CUDA supported by this system? {available}") if available: - print(f"CUDA version: {torch.version.cuda}") + print(f"CUDA version: {torch_version.cuda}") # Storing ID of current CUDA device cuda_id = torch.cuda.current_device() diff --git a/test/test_top.py b/test/test_top.py index ea375ab..2ef8dc8 100644 --- a/test/test_top.py +++ b/test/test_top.py @@ -1,6 +1,7 @@ import pytest from suthing import FileHandle +from triel.coref_adapter import CorefBackend, get_coref_resolver from triel.linking.onto import EntityLinkerManager from triel.response.onto import ( REELResponse, @@ -34,14 +35,58 @@ def test_cast_response_er(reel_response): assert len(r.entities) == 79 -def test_complete(nlp_fixture, rules_v2, el_conf, sample_a): +def test_complete(nlp_fixture, rules_v2, el_conf, sample_a, coref_resolver_fixture): elm = EntityLinkerManager.from_dict(el_conf) response = text_to_graph_mentions_entities( - sample_a["text"], nlp_fixture, rules_v2, elm + sample_a["text"], + nlp_fixture, + rules_v2, + elm, + coref_resolver=coref_resolver_fixture, ) _ = cast_response_redux(response) response_ent = cast_response_entity_representation(response) assert len(response_ent.entities) == 45 assert len(response_ent.triples) == 36 + + +def test_dual_run_logs_without_changing_response(monkeypatch, caplog): + monkeypatch.setattr("triel.top.normalize_text", lambda text, nlp: ["Sample text."]) + monkeypatch.setattr( + "triel.top.phrases_to_triples", lambda *args, **kwargs: ({}, {}) + ) + monkeypatch.setattr("triel.top.iterate_over_linkers", lambda **kwargs: []) + monkeypatch.setattr( + "triel.top.map_mentions_to_entities", lambda *args, **kwargs: ({}, [], []) + ) + monkeypatch.setattr( + "triel.top.link_unlinked_entities", lambda *args, **kwargs: ({}, []) + ) + + call_count = {"n": 0} + + def _stitch_mock(*args, **kwargs): + call_count["n"] += 1 + if call_count["n"] == 1: + return {("c0", ((0, 0),))}, [] + return {("s0", ((0, 0),)), ("s1", ((1, 0),))}, [] + + monkeypatch.setattr("triel.top.stitch_coreference", _stitch_mock) + + primary = get_coref_resolver(CorefBackend.NONE) + shadow = get_coref_resolver(CorefBackend.NONE) + caplog.set_level("INFO") + + response = text_to_graph_mentions_entities( + text="Sample text.", + nlp=object(), + rules={}, + elm=object(), + coref_resolver=primary, + coref_shadow_resolver=shadow, + coref_dual_run_enabled=True, + ) + assert isinstance(response, REELResponse) + assert any("coref_dual_run" in rec.message for rec in caplog.records) diff --git a/triel/cli/serve.py b/triel/cli/serve.py index 1c4ea10..583428e 100644 --- a/triel/cli/serve.py +++ b/triel/cli/serve.py @@ -3,6 +3,7 @@ import logging import logging.config import pathlib +import random import traceback from typing import Any @@ -23,6 +24,11 @@ EntityLinkerManager, EntityLinkerTypeNotAvailable, ) +from triel.coref_adapter import ( + CorefAdapterError, + CorefBackend, + get_ready_coref_runtime, +) from triel.top import ( cast_response_entity_representation, cast_response_redux, @@ -157,6 +163,20 @@ class Config: gpu: bool = Field(default=True, description="Load spaCy models to GPU") spacy_model: str = Field(default="en_core_web_trf", description="spaCy model name") + coref_backend: CorefBackend = Field( + default=CorefBackend.COREFEREE, + description="Coreference resolver backend", + ) + coref_backend_shadow: CorefBackend | None = Field( + default=None, + description="Optional shadow backend for dual-run canary comparison.", + ) + coref_dual_run_enabled: bool = Field( + default=False, description="Enable dual-run canary mode for coref." + ) + coref_dual_run_sample_rate: float = Field( + default=0.1, description="Request sampling rate for dual-run mode." + ) rules_file: str = Field( default="prune_noun_compound_v3.yaml", description="Rules configuration file name", @@ -317,14 +337,47 @@ def main( if app_config.model.gpu: spacy.prefer_gpu() - # Load spaCy model + # Load spaCy model and primary coref runtime. nlp = spacy.load(app_config.model.spacy_model) - nlp.add_pipe("coreferee") + nlp, coref_resolver = get_ready_coref_runtime( + nlp, app_config.model.coref_backend, fallback_to_none=False + ) + + shadow_nlp = None + shadow_resolver = None + if ( + app_config.model.coref_dual_run_enabled + and app_config.model.coref_backend_shadow + ): + try: + shadow_nlp = spacy.load(app_config.model.spacy_model) + shadow_nlp, shadow_resolver = get_ready_coref_runtime( + shadow_nlp, + app_config.model.coref_backend_shadow, + fallback_to_none=True, + ) + except CorefAdapterError as e: + logger.warning(f"coref_shadow_setup_failed error={e}") + shadow_nlp, shadow_resolver = None, None def work(request0): json_data = request0.json text = json_data["text"] - response = text_to_graph_mentions_entities(text, nlp, rules, elm) + canary_enabled = ( + app_config.model.coref_dual_run_enabled + and shadow_resolver is not None + and random.random() < app_config.model.coref_dual_run_sample_rate + ) + response = text_to_graph_mentions_entities( + text, + nlp, + rules, + elm, + coref_resolver=coref_resolver, + nlp_shadow=shadow_nlp, + coref_shadow_resolver=shadow_resolver, + coref_dual_run_enabled=canary_enabled, + ) return response # Get paths from config diff --git a/triel/coref.py b/triel/coref.py index 3502da6..9a5413c 100644 --- a/triel/coref.py +++ b/triel/coref.py @@ -13,6 +13,7 @@ phrase_to_deptree, relabel_nodes_and_key, ) +from triel.coref_adapter import CorefAdapterError, CorefResolver, get_coref_resolver from triel.onto import AbsToken, Candidate, ChainIndex, Token, TokenIndexT from triel.piles import ExtCandidateList from triel.relation import logger @@ -38,7 +39,9 @@ def graph_component_maps( return map_tree_subtree_index -def render_coref_graph(rdoc: Doc) -> nx.DiGraph: +def render_coref_graph( + rdoc: Doc, coref_resolver: CorefResolver | None = None +) -> nx.DiGraph: """ render super graph using coreferee package @@ -55,7 +58,17 @@ def render_coref_graph(rdoc: Doc) -> nx.DiGraph: NB: most specific mention is encoded by blank attribute `most_specific` """ - chains = rdoc._.coref_chains if rdoc._.coref_chains is not None else [] + resolver = coref_resolver or get_coref_resolver() + try: + resolution = resolver.resolve_doc(rdoc) + chains = resolution.chains + except CorefAdapterError as e: + logger.warning( + "coref_resolution_failed backend=%s error=%s", + resolver.backend.value, + e, + ) + chains = [] # nodes for coref graph vs_coref = [] @@ -78,7 +91,8 @@ def render_coref_graph(rdoc: Doc) -> nx.DiGraph: ] vertex_counter += 1 - for jchain, chain in enumerate(chains): + for chain in chains: + jchain = chain.chain_index coref_chain = (-1, vertex_counter) chain_state: dict[str, Any] = { "tag_": "coref", @@ -92,13 +106,12 @@ def render_coref_graph(rdoc: Doc) -> nx.DiGraph: vs_coref += [(coref_chain, chain_state)] es_coref.append((coref_root, coref_chain)) vertex_counter += 1 - for kth, x in enumerate(chain.mentions): + for x in chain.mentions: coref_blank = (-1, vertex_counter) blank_state: dict[str, Any] = { "tag_": "coref", - "dep_": "blank" - + ("*" if kth == chain.most_specific_mention_index else ""), - "most_specific": kth == chain.most_specific_mention_index, + "dep_": "blank" + ("*" if x.most_specific else ""), + "most_specific": x.most_specific, "chain": jchain, } blank_state["label"] = ( @@ -182,9 +195,13 @@ def render_coref_candidate_map( def render_coref_maps_wrapper( - rdoc, initial_phrase_index=None, map_tree_subtree_index=None, **kwargs + rdoc, + initial_phrase_index=None, + map_tree_subtree_index=None, + coref_resolver: CorefResolver | None = None, + **kwargs, ) -> tuple[list[tuple[int, tuple[int, ...]]], list[tuple[int, int]]]: - coref_graph = render_coref_graph(rdoc) + coref_graph = render_coref_graph(rdoc, coref_resolver=coref_resolver) plot_path = kwargs.pop("plot_path", None) if plot_path is not None: @@ -335,7 +352,11 @@ def coref_candidates( def stitch_coreference( - phrases_for_coref: list[str], nlp: spacy.Language, window_size: int, plot_path=None + phrases_for_coref: list[str], + nlp: spacy.Language, + window_size: int, + plot_path=None, + coref_resolver: CorefResolver | None = None, ): """ go over phrases with stride 1 and window `window` to identify coreferences; @@ -354,7 +375,11 @@ def stitch_coreference( for i in range(nmax): fragment = " ".join(phrases_for_coref[i : i + window_size]) edges_chain_token, edges_chain_order = text_to_coref_classes( - nlp, fragment, initial_phrase_index=i, plot_path=plot_path + nlp, + fragment, + initial_phrase_index=i, + plot_path=plot_path, + coref_resolver=coref_resolver, ) acc_chain_token += edges_chain_token acc_chain_order += edges_chain_order @@ -388,7 +413,11 @@ def stitch_coreference( def text_to_coref_classes( - nlp, text, initial_phrase_index, **kwargs + nlp, + text, + initial_phrase_index, + coref_resolver: CorefResolver | None = None, + **kwargs, ) -> tuple[ list[tuple[ChainIndex, tuple[TokenIndexT, ...]]], list[tuple[ChainIndex, ChainIndex]], @@ -409,7 +438,10 @@ def text_to_coref_classes( # coref maps (edges_chain_token, edges_chain_order) = render_coref_maps_wrapper( - rdoc, initial_phrase_index, **kwargs + rdoc, + initial_phrase_index, + coref_resolver=coref_resolver, + **kwargs, ) edges_chain_tokenit: list[tuple[ChainIndex, tuple[TokenIndexT, ...]]] = [ ( diff --git a/triel/coref_adapter.py b/triel/coref_adapter.py new file mode 100644 index 0000000..0087413 --- /dev/null +++ b/triel/coref_adapter.py @@ -0,0 +1,279 @@ +from __future__ import annotations + +import logging +from enum import StrEnum +from typing import Protocol + +from pydantic import BaseModel, Field +from spacy import Language +from spacy.tokens import Doc, Span + +logger = logging.getLogger(__name__) + + +class CorefAdapterError(Exception): + """Base exception for coref adapter failures.""" + + +class CorefSetupError(CorefAdapterError): + """Raised when backend setup fails.""" + + +class CorefInferenceError(CorefAdapterError): + """Raised when backend inference/parsing fails.""" + + +class CorefBackend(StrEnum): + COREFEREE = "coreferee" + SPACY_NATIVE = "spacy_native" + FASTCOREF = "fastcoref" + NONE = "none" + + +class CorefMention(BaseModel): + token_indexes: tuple[int, ...] + most_specific: bool = False + + +class CorefChain(BaseModel): + chain_index: int + mentions: list[CorefMention] + + +class CorefResolution(BaseModel): + chains: list[CorefChain] = Field(default_factory=list) + + +class CorefResolver(Protocol): + backend: CorefBackend + + def is_ready(self, nlp: Language | None = None) -> bool: ... + + def resolve_doc(self, rdoc: Doc) -> CorefResolution: ... + + +def _span_to_token_indexes(span: Span) -> tuple[int, ...]: + return tuple(range(span.start, span.end)) + + +class NoneCorefResolver: + backend = CorefBackend.NONE + + def is_ready(self, nlp: Language | None = None) -> bool: + _ = nlp + return True + + def resolve_doc(self, rdoc: Doc) -> CorefResolution: + _ = rdoc + return CorefResolution() + + +class CorefereeResolver: + backend = CorefBackend.COREFEREE + + def is_ready(self, nlp: Language | None = None) -> bool: + if nlp is None: + return True + return "coreferee" in nlp.pipe_names + + def resolve_doc(self, rdoc: Doc) -> CorefResolution: + try: + if not hasattr(rdoc._, "coref_chains"): + return CorefResolution() + chains = rdoc._.coref_chains if rdoc._.coref_chains is not None else [] + out: list[CorefChain] = [] + for jchain, chain in enumerate(chains): + mentions = [ + CorefMention( + token_indexes=tuple(x.token_indexes), + most_specific=(kth == chain.most_specific_mention_index), + ) + for kth, x in enumerate(chain.mentions) + ] + out.append(CorefChain(chain_index=jchain, mentions=mentions)) + return CorefResolution(chains=out) + except Exception as e: + raise CorefInferenceError(f"Coreferee resolution failed: {e}") from e + + +class SpacyNativeCorefResolver: + backend = CorefBackend.SPACY_NATIVE + + def is_ready(self, nlp: Language | None = None) -> bool: + if nlp is None: + return True + return "coref" in nlp.pipe_names or "coreference_resolver" in nlp.pipe_names + + def resolve_doc(self, rdoc: Doc) -> CorefResolution: + try: + chain_spans = sorted( + ( + (k, v) + for k, v in rdoc.spans.items() + if k.startswith("coref_clusters") + ), + key=lambda item: item[0], + ) + chains: list[CorefChain] = [] + for jchain, (_, spangroup) in enumerate(chain_spans): + mentions: list[CorefMention] = [] + spans = list(spangroup) + for kth, span in enumerate(spans): + tokens = _span_to_token_indexes(span) + if not tokens: + continue + # Use longest mention as most specific approximation. + mentions.append( + CorefMention(token_indexes=tokens, most_specific=(kth == 0)) + ) + if mentions: + longest_ix = max( + range(len(mentions)), + key=lambda i: len(mentions[i].token_indexes), + ) + mentions = [ + CorefMention( + token_indexes=m.token_indexes, + most_specific=(i == longest_ix), + ) + for i, m in enumerate(mentions) + ] + chains.append(CorefChain(chain_index=jchain, mentions=mentions)) + return CorefResolution(chains=chains) + except Exception as e: + raise CorefInferenceError( + f"spaCy native coref resolution failed: {e}" + ) from e + + +class FastCorefResolver: + backend = CorefBackend.FASTCOREF + + def is_ready(self, nlp: Language | None = None) -> bool: + if nlp is None: + return True + return "fastcoref" in nlp.pipe_names + + def resolve_doc(self, rdoc: Doc) -> CorefResolution: + try: + if not hasattr(rdoc._, "coref_clusters"): + return CorefResolution() + chains: list[CorefChain] = [] + raw_clusters = ( + rdoc._.coref_clusters if rdoc._.coref_clusters is not None else [] + ) + for jchain, cluster in enumerate(raw_clusters): + mentions: list[CorefMention] = [] + for mention in cluster: + if not isinstance(mention, tuple) or len(mention) != 2: + continue + char_a, char_b = mention + span = rdoc.char_span(char_a, char_b, alignment_mode="expand") + if span is None: + continue + mentions.append( + CorefMention( + token_indexes=_span_to_token_indexes(span), + most_specific=False, + ) + ) + if mentions: + longest_ix = max( + range(len(mentions)), + key=lambda i: len(mentions[i].token_indexes), + ) + mentions = [ + CorefMention( + token_indexes=m.token_indexes, + most_specific=(i == longest_ix), + ) + for i, m in enumerate(mentions) + ] + chains.append(CorefChain(chain_index=jchain, mentions=mentions)) + return CorefResolution(chains=chains) + except Exception as e: + raise CorefInferenceError(f"fastcoref resolution failed: {e}") from e + + +def get_coref_resolver(backend: str | CorefBackend | None = None) -> CorefResolver: + selected = CorefBackend(backend or CorefBackend.COREFEREE) + if selected == CorefBackend.COREFEREE: + return CorefereeResolver() + if selected == CorefBackend.SPACY_NATIVE: + return SpacyNativeCorefResolver() + if selected == CorefBackend.FASTCOREF: + return FastCorefResolver() + return NoneCorefResolver() + + +def configure_nlp_coref_backend( + nlp: Language, backend: str | CorefBackend | None = None +) -> Language: + selected = CorefBackend(backend or CorefBackend.COREFEREE) + if selected == CorefBackend.COREFEREE: + if "coreferee" not in nlp.pipe_names: + try: + nlp.add_pipe("coreferee") + except Exception as e: + raise CorefSetupError( + f"Failed to configure coref backend '{selected.value}': {e}" + ) from e + return nlp + + if selected == CorefBackend.FASTCOREF: + if "fastcoref" not in nlp.pipe_names: + try: + nlp.add_pipe( + "fastcoref", + config={ + "model_architecture": "LingMessCoref", + "model_path": "biu-nlp/lingmess-coref", + }, + ) + except Exception as e: + raise CorefSetupError( + f"Failed to configure coref backend '{selected.value}': {e}" + ) from e + return nlp + + if selected == CorefBackend.SPACY_NATIVE: + # Native spaCy coref models can provide the component already. + # Try adding only if factory is available. + if ( + "coref" not in nlp.pipe_names + and "coreference_resolver" not in nlp.pipe_names + ): + try: + nlp.add_pipe("coref") + except Exception as e: + raise CorefSetupError( + f"Failed to configure coref backend '{selected.value}': {e}" + ) from e + return nlp + + return nlp + + +def get_ready_coref_runtime( + nlp: Language, + backend: str | CorefBackend | None = None, + *, + fallback_to_none: bool = False, +) -> tuple[Language, CorefResolver]: + selected = CorefBackend(backend or CorefBackend.COREFEREE) + try: + nlp_ready = configure_nlp_coref_backend(nlp, selected) + resolver = get_coref_resolver(selected) + if not resolver.is_ready(nlp_ready): + raise CorefSetupError( + f"Coref backend '{selected.value}' is not ready after setup." + ) + return nlp_ready, resolver + except CorefAdapterError: + if fallback_to_none: + logger.warning( + "Falling back to 'none' coref backend after setup failure " + f"for backend='{selected.value}'." + ) + return nlp, get_coref_resolver(CorefBackend.NONE) + raise diff --git a/triel/top.py b/triel/top.py index 5668906..3708c3f 100644 --- a/triel/top.py +++ b/triel/top.py @@ -8,6 +8,7 @@ from suthing import Timer, profile from triel.coref import stitch_coreference +from triel.coref_adapter import CorefAdapterError from triel.hash import hashme from triel.linking.onto import EntityLinker from triel.linking.util import ( @@ -29,9 +30,54 @@ logger = logging.getLogger(__name__) +def _coref_chain_signatures( + edges_chain_token_global: set[tuple[MuIndex, tuple[MuIndex, ...]]] | set[tuple], +) -> set[frozenset[tuple]]: + chain_map: dict[MuIndex, set[tuple]] = defaultdict(set) + for chain_id, mention_tokens in edges_chain_token_global: + chain_map[chain_id].add(tuple(mention_tokens)) + return {frozenset(mentions) for mentions in chain_map.values()} + + +def _log_dual_run_coref_diff( + *, + text_hash: str, + primary_backend: str, + shadow_backend: str, + primary_edges_chain_token, + shadow_edges_chain_token, + primary_edges_chain_order, + shadow_edges_chain_order, +) -> None: + primary_signatures = _coref_chain_signatures(set(primary_edges_chain_token)) + shadow_signatures = _coref_chain_signatures(set(shadow_edges_chain_token)) + added_chains = len(shadow_signatures - primary_signatures) + removed_chains = len(primary_signatures - shadow_signatures) + edge_drift = abs(len(shadow_edges_chain_token) - len(primary_edges_chain_token)) + order_edge_drift = abs( + len(shadow_edges_chain_order) - len(primary_edges_chain_order) + ) + logger.info( + "coref_dual_run text_hash=%s primary=%s shadow=%s " + "added_chains=%s removed_chains=%s edge_drift=%s order_edge_drift=%s", + text_hash, + primary_backend, + shadow_backend, + added_chains, + removed_chains, + edge_drift, + order_edge_drift, + ) + + @profile def text_to_graph_mentions_entities(text, nlp, rules, elm, ix_phrases=None, **kwargs): + coref_resolver = kwargs.pop("coref_resolver", None) + coref_shadow_resolver = kwargs.pop("coref_shadow_resolver", None) + nlp_shadow = kwargs.pop("nlp_shadow", None) + coref_dual_run_enabled = kwargs.pop("coref_dual_run_enabled", False) phrases = normalize_text(text, nlp) + text_hash = hashme(text) if ix_phrases is not None: if ix_phrases: @@ -41,9 +87,83 @@ def text_to_graph_mentions_entities(text, nlp, rules, elm, ix_phrases=None, **kw phrases, nlp, rules, **kwargs ) with Timer() as t_coref: - edges_chain_token_global, edges_chain_order_global = stitch_coreference( - nlp=nlp, phrases_for_coref=phrases, window_size=2 - ) + try: + edges_chain_token_global, edges_chain_order_global = stitch_coreference( + nlp=nlp, + phrases_for_coref=phrases, + window_size=2, + coref_resolver=coref_resolver, + ) + except CorefAdapterError as e: + backend = ( + coref_resolver.backend.value + if coref_resolver is not None + else "unknown" + ) + logger.warning( + "coref_primary_failed text_hash=%s backend=%s error=%s", + text_hash, + backend, + e, + ) + edges_chain_token_global, edges_chain_order_global = set(), [] + except Exception as e: + backend = ( + coref_resolver.backend.value + if coref_resolver is not None + else "unknown" + ) + logger.warning( + "coref_primary_unexpected_error text_hash=%s backend=%s error=%s", + text_hash, + backend, + e, + ) + edges_chain_token_global, edges_chain_order_global = set(), [] + + if coref_dual_run_enabled and coref_shadow_resolver is not None: + with Timer() as t_coref_shadow: + try: + shadow_nlp = nlp_shadow if nlp_shadow is not None else nlp + shadow_edges_chain_token, shadow_edges_chain_order = stitch_coreference( + nlp=shadow_nlp, + phrases_for_coref=phrases, + window_size=2, + coref_resolver=coref_shadow_resolver, + ) + _log_dual_run_coref_diff( + text_hash=text_hash, + primary_backend=( + coref_resolver.backend.value + if coref_resolver is not None + else "unknown" + ), + shadow_backend=coref_shadow_resolver.backend.value, + primary_edges_chain_token=edges_chain_token_global, + shadow_edges_chain_token=shadow_edges_chain_token, + primary_edges_chain_order=edges_chain_order_global, + shadow_edges_chain_order=shadow_edges_chain_order, + ) + logger.info( + "coref_dual_run_timing text_hash=%s shadow_backend=%s elapsed=%.4f", + text_hash, + coref_shadow_resolver.backend.value, + t_coref_shadow.elapsed, + ) + except CorefAdapterError as e: + logger.warning( + "coref_shadow_failed text_hash=%s backend=%s error=%s", + text_hash, + coref_shadow_resolver.backend.value, + e, + ) + except Exception as e: + logger.warning( + "coref_shadow_unexpected_error text_hash=%s backend=%s error=%s", + text_hash, + coref_shadow_resolver.backend.value, + e, + ) with Timer() as t_el: entity_pack = iterate_over_linkers( diff --git a/uv.lock b/uv.lock index 6a484d6..ad4c693 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,62 @@ wheels = [ {hash = "sha256:9035092855a98e41b66e3d0998bd7b96280e85ceb3a04cc035636138a1943eaf", size = 165627, upload-time = "2025-04-25T03:17:58.89Z", url = "https://files.pythonhosted.org/packages/e3/52/6ad8f63ec8da1bf40f96996d25d5b650fdd38f5975f8c813732c47388f18/aenum-3.1.16-py3-none-any.whl"} ] +[[package]] +name = "aiohappyeyeballs" +sdist = {hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z", url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "2.6.1" +wheels = [ + {hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z", url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl"} +] + +[[package]] +dependencies = [ + {name = "aiohappyeyeballs"}, + {name = "aiosignal"}, + {name = "attrs"}, + {name = "frozenlist"}, + {name = "multidict"}, + {name = "propcache"}, + {name = "yarl"} +] +name = "aiohttp" +sdist = {hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z", url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "3.13.3" +wheels = [ + {hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", size = 499234, upload-time = "2026-01-03T17:29:44.822Z", url = "https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl"}, + {hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", size = 494979, upload-time = "2026-01-03T17:29:46.405Z", url = "https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl"}, + {hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", size = 1804590, upload-time = "2026-01-03T17:30:07.135Z", url = "https://files.pythonhosted.org/packages/55/58/4345b5f26661a6180afa686c473620c30a66afdf120ed3dd545bbc809e85/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl"}, + {hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", size = 1748297, upload-time = "2026-01-03T17:29:48.083Z", url = "https://files.pythonhosted.org/packages/75/b5/31d4d2e802dfd59f74ed47eba48869c1c21552c586d5e81a9d0d5c2ad640/aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", size = 1899449, upload-time = "2026-01-03T17:29:53.938Z", url = "https://files.pythonhosted.org/packages/6c/70/ddc1b7169cf64075e864f64595a14b147a895a868394a48f6a8031979038/aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl"}, + {hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", size = 746051, upload-time = "2026-01-03T17:29:43.287Z", url = "https://files.pythonhosted.org/packages/f1/4c/a164164834f03924d9a29dc3acd9e7ee58f95857e0b467f6d04298594ebb/aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl"}, + {hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", size = 1596024, upload-time = "2026-01-03T17:30:05.132Z", url = "https://files.pythonhosted.org/packages/9d/d4/dd1ca234c794fd29c057ce8c0566b8ef7fd6a51069de5f06fa84b9a1971c/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl"}, + {hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", size = 457678, upload-time = "2026-01-03T17:30:12.719Z", url = "https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl"}, + {hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", size = 1805405, upload-time = "2026-01-03T17:29:51.244Z", url = "https://files.pythonhosted.org/packages/c5/3a/54a64299fac2891c346cdcf2aa6803f994a2e4beeaf2e5a09dcc54acc842/aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", size = 1724156, upload-time = "2026-01-03T17:29:58.914Z", url = "https://files.pythonhosted.org/packages/0a/c1/778d011920cae03ae01424ec202c513dc69243cf2db303965615b81deeea/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl"}, + {hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", size = 1707172, upload-time = "2026-01-03T17:29:49.648Z", url = "https://files.pythonhosted.org/packages/1a/3e/eefad0ad42959f226bb79664826883f2687d602a9ae2941a18e0484a74d3/aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl"}, + {hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", size = 1740355, upload-time = "2026-01-03T17:30:09.083Z", url = "https://files.pythonhosted.org/packages/7b/06/05950619af6c2df7e0a431d889ba2813c9f0129cec76f663e547a5ad56f2/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl"}, + {hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", size = 1748444, upload-time = "2026-01-03T17:29:55.484Z", url = "https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}, + {hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", size = 433701, upload-time = "2026-01-03T17:30:10.869Z", url = "https://files.pythonhosted.org/packages/3e/80/958f16de79ba0422d7c1e284b2abd0c84bc03394fbe631d0a39ffa10e1eb/aiohttp-3.13.3-cp311-cp311-win32.whl"}, + {hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", size = 1783041, upload-time = "2026-01-03T17:30:03.609Z", url = "https://files.pythonhosted.org/packages/7a/e5/76cf77bdbc435bf233c1f114edad39ed4177ccbfab7c329482b179cff4f4/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl"}, + {hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", size = 1722340, upload-time = "2026-01-03T17:30:01.962Z", url = "https://files.pythonhosted.org/packages/0e/cb/3419eabf4ec1e9ec6f242c32b689248365a1cf621891f6f0386632525494/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl"}, + {hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", size = 1606038, upload-time = "2026-01-03T17:29:57.179Z", url = "https://files.pythonhosted.org/packages/6b/f2/073b145c4100da5511f457dc0f7558e99b2987cf72600d42b559db856fbc/aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"} +] + +[[package]] +dependencies = [ + {name = "frozenlist"}, + {name = "typing-extensions"} +] +name = "aiosignal" +sdist = {hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z", url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "1.4.0" +wheels = [ + {hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z", url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl"} +] + [[package]] name = "aniso8601" sdist = {hash = "sha256:25488f8663dd1528ae1f54f94ac1ea51ae25b4d531539b8bc707fed184d16845", size = 47190, upload-time = "2025-04-18T17:29:42.995Z", url = "https://files.pythonhosted.org/packages/8b/8d/52179c4e3f1978d3d9a285f98c706642522750ef343e9738286130423730/aniso8601-10.0.1.tar.gz"} @@ -20,6 +76,28 @@ wheels = [ {hash = "sha256:eb19717fd4e0db6de1aab06f12450ab92144246b257423fe020af5748c0cb89e", size = 52848, upload-time = "2025-04-18T17:29:41.492Z", url = "https://files.pythonhosted.org/packages/59/75/e0e10dc7ed1408c28e03a6cb2d7a407f99320eb953f229d008a7a6d05546/aniso8601-10.0.1-py2.py3-none-any.whl"} ] +[[package]] +dependencies = [ + {name = "idna"}, + {name = "typing-extensions"} +] +name = "anyio" +sdist = {hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z", url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "4.12.1" +wheels = [ + {hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z", url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl"} +] + +[[package]] +name = "attrs" +sdist = {hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z", url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "25.4.0" +wheels = [ + {hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z", url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl"} +] + [[package]] name = "backports-zstd" sdist = {hash = "sha256:e8b2d68e2812f5c9970cabc5e21da8b409b5ed04e79b4585dbffa33e9b45ebe2", size = 997138, upload-time = "2025-12-29T17:28:06.143Z", url = "https://files.pythonhosted.org/packages/f4/b1/36a5182ce1d8ef9ef32bff69037bd28b389bbdb66338f8069e61da7028cb/backports_zstd-1.3.0.tar.gz"} @@ -301,6 +379,31 @@ timedelta = [ {name = "pytimeparse"} ] +[[package]] +dependencies = [ + {extra = ["http"], name = "fsspec"}, + {name = "dill"}, + {name = "filelock"}, + {name = "httpx"}, + {name = "huggingface-hub"}, + {name = "multiprocess"}, + {name = "numpy"}, + {name = "packaging"}, + {name = "pandas"}, + {name = "pyarrow"}, + {name = "pyyaml"}, + {name = "requests"}, + {name = "tqdm"}, + {name = "xxhash"} +] +name = "datasets" +sdist = {hash = "sha256:140ce500bc41939ff6ce995702d66b1f4b2ee7f117bb9b07512fab6804d4070a", size = 593865, upload-time = "2026-02-27T23:26:49.482Z", url = "https://files.pythonhosted.org/packages/d7/94/eb81c6fe32e9b6ef92223141b5a553aeff2e9456968424a8533cbe88f476/datasets-4.6.1.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "4.6.1" +wheels = [ + {hash = "sha256:f53228e6dadc9f837037b1bf3051d7d8c054abbb3eb29f1f022926e08090e0da", size = 520667, upload-time = "2026-02-27T23:26:46.855Z", url = "https://files.pythonhosted.org/packages/37/f0/99fe6eb530c7ee9ee1faee48059eb8a6437f80c893a496b98a78864e0fc6/datasets-4.6.1-py3-none-any.whl"} +] + [[package]] dependencies = [ {name = "wrapt"} @@ -315,11 +418,11 @@ wheels = [ [[package]] name = "dill" -sdist = {hash = "sha256:423092df4182177d4d8ba8290c8a5b640c66ab35ec7da59ccfa00f6fa3eea5fa", size = 187315, upload-time = "2026-01-19T02:36:56.85Z", url = "https://files.pythonhosted.org/packages/81/e1/56027a71e31b02ddc53c7d65b01e68edf64dea2932122fe7746a516f75d5/dill-0.4.1.tar.gz"} +sdist = {hash = "sha256:0633f1d2df477324f53a895b02c901fb961bdbf65a17122586ea7019292cbcf0", size = 186976, upload-time = "2025-04-16T00:41:48.867Z", url = "https://files.pythonhosted.org/packages/12/80/630b4b88364e9a8c8c5797f4602d0f76ef820909ee32f0bacb9f90654042/dill-0.4.0.tar.gz"} source = {registry = "https://pypi.org/simple"} -version = "0.4.1" +version = "0.4.0" wheels = [ - {hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z", url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl"} + {hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z", url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl"} ] [[package]] @@ -331,6 +434,21 @@ wheels = [ {hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z", url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl"} ] +[[package]] +dependencies = [ + {name = "datasets"}, + {name = "numpy"}, + {name = "scipy"}, + {name = "spacy"}, + {name = "torch"}, + {name = "tqdm"}, + {name = "transformers"} +] +name = "fastcoref" +sdist = {hash = "sha256:cf3bc2764cca1db06df8ff40e513d7f79d26f0c340d0e442bd66a85673610b9e", size = 27476, upload-time = "2023-05-26T14:45:28.579Z", url = "https://files.pythonhosted.org/packages/5c/35/d8e3906a6887e7dfbaf1d09fc9a7c16f117064eefc139ddacf7595b0db20/fastcoref-2.1.6.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "2.1.6" + [[package]] name = "fastrlock" sdist = {hash = "sha256:4af6734d92eaa3ab4373e6c9a1dd0d5ad1304e172b1521733c6c3b3d73c8fa5d", size = 79327, upload-time = "2024-12-17T11:03:39.638Z", url = "https://files.pythonhosted.org/packages/73/b1/1c3d635d955f2b4bf34d45abf8f35492e04dbd7804e94ce65d9f928ef3ec/fastrlock-0.8.3.tar.gz"} @@ -442,6 +560,31 @@ wheels = [ {hash = "sha256:1e44564ddb5019101b0d475fd538d3436aca9c081a79749a317a9bf0a831e5fd", size = 18260, upload-time = "2024-12-18T18:32:59.414Z", url = "https://files.pythonhosted.org/packages/84/39/1ea66fcc8c64230128c7911fe1a27be70f7bd65b5af70cdcb2f27caedc0f/Flask_SocketIO-5.5.0-py3-none-any.whl"} ] +[[package]] +name = "frozenlist" +sdist = {hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z", url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "1.8.0" +wheels = [ + {hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z", url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl"}, + {hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z", url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl"}, + {hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z", url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl"}, + {hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z", url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl"}, + {hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z", url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl"}, + {hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z", url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl"}, + {hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z", url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl"}, + {hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z", url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl"}, + {hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z", url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z", url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl"}, + {hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z", url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl"}, + {hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z", url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl"}, + {hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z", url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl"}, + {hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z", url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl"}, + {hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z", url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z", url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl"}, + {hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z", url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl"} +] + [[package]] name = "fsspec" sdist = {hash = "sha256:6544e34b16869f5aacd5b90bdf1a71acb37792ea3ddf6125ee69a22a53fb8bff", size = 313441, upload-time = "2026-02-05T21:50:53.743Z", url = "https://files.pythonhosted.org/packages/51/7c/f60c259dcbf4f0c47cc4ddb8f7720d2dcdc8888c8e5ad84c73ea4531cc5b/fsspec-2026.2.0.tar.gz"} @@ -451,6 +594,11 @@ wheels = [ {hash = "sha256:98de475b5cb3bd66bedd5c4679e87b4fdfe1a3bf4d707b151b3c07e58c9a2437", size = 202505, upload-time = "2026-02-05T21:50:51.819Z", url = "https://files.pythonhosted.org/packages/e6/ab/fb21f4c939bb440104cc2b396d3be1d9b7a9fd3c6c2a53d98c45b3d7c954/fsspec-2026.2.0-py3-none-any.whl"} ] +[package.optional-dependencies] +http = [ + {name = "aiohttp"} +] + [[package]] dependencies = [ {name = "wcwidth"} @@ -488,6 +636,34 @@ wheels = [ {hash = "sha256:f93b7595f1d8fefddfede775c18b5c9256757824f7f6832930b49858483cd56f", size = 3763961, upload-time = "2026-02-27T17:25:52.537Z", url = "https://files.pythonhosted.org/packages/d8/28/dbb024e2e3907f6f3052847ca7d1a2f7a3972fafcd53ff79018977fcb3e4/hf_xet-1.3.2-cp37-abi3-macosx_10_12_x86_64.whl"} ] +[[package]] +dependencies = [ + {name = "certifi"}, + {name = "h11"} +] +name = "httpcore" +sdist = {hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z", url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "1.0.9" +wheels = [ + {hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z", url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl"} +] + +[[package]] +dependencies = [ + {name = "anyio"}, + {name = "certifi"}, + {name = "httpcore"}, + {name = "idna"} +] +name = "httpx" +sdist = {hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z", url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "0.28.1" +wheels = [ + {hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z", url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl"} +] + [[package]] dependencies = [ {marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'", name = "hf-xet"}, @@ -665,21 +841,49 @@ wheels = [ {hash = "sha256:d198d275222dc54244bf3327eb8cbe00307d220241d9cec4d306d49a44e85f68", size = 71633, upload-time = "2025-10-08T09:14:59.177Z", url = "https://files.pythonhosted.org/packages/2a/79/309d0e637f6f37e83c711f547308b91af02b72d2326ddd860b966080ef29/msgpack-1.1.2-cp311-cp311-win_amd64.whl"} ] +[[package]] +name = "multidict" +sdist = {hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z", url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "6.7.1" +wheels = [ + {hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z", url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl"}, + {hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z", url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl"}, + {hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z", url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl"}, + {hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z", url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl"}, + {hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z", url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z", url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl"}, + {hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z", url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}, + {hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z", url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl"}, + {hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z", url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl"}, + {hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z", url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z", url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl"}, + {hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z", url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl"}, + {hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z", url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl"}, + {hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z", url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z", url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl"}, + {hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z", url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl"}, + {hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z", url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl"}, + {hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z", url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl"}, + {hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z", url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl"} +] + [[package]] dependencies = [ {name = "dill"} ] name = "multiprocess" -sdist = {hash = "sha256:952021e0e6c55a4a9fe4cd787895b86e239a40e76802a789d6305398d3975897", size = 2079989, upload-time = "2026-01-19T06:47:39.744Z", url = "https://files.pythonhosted.org/packages/a2/f2/e783ac7f2aeeed14e9e12801f22529cc7e6b7ab80928d6dcce4e9f00922d/multiprocess-0.70.19.tar.gz"} +sdist = {hash = "sha256:f9597128e6b3e67b23956da07cf3d2e5cba79e2f4e0fba8d7903636663ec6d0d", size = 1798503, upload-time = "2025-04-17T03:11:27.742Z", url = "https://files.pythonhosted.org/packages/72/fd/2ae3826f5be24c6ed87266bc4e59c46ea5b059a103f3d7e7eb76a52aeecb/multiprocess-0.70.18.tar.gz"} source = {registry = "https://pypi.org/simple"} -version = "0.70.19" +version = "0.70.18" wheels = [ - {hash = "sha256:0d4b4397ed669d371c81dcd1ef33fd384a44d6c3de1bd0ca7ac06d837720d3c5", size = 133477, upload-time = "2026-01-19T06:47:38.619Z", url = "https://files.pythonhosted.org/packages/7e/82/69e539c4c2027f1e1697e09aaa2449243085a0edf81ae2c6341e84d769b6/multiprocess-0.70.19-py39-none-any.whl"}, - {hash = "sha256:1bbf1b69af1cf64cd05f65337d9215b88079ec819cd0ea7bac4dab84e162efe7", size = 144743, upload-time = "2026-01-19T06:47:24.562Z", url = "https://files.pythonhosted.org/packages/7e/aa/714635c727dbfc251139226fa4eaf1b07f00dc12d9cd2eb25f931adaf873/multiprocess-0.70.19-pp311-pypy311_pp73-macosx_10_15_x86_64.whl"}, - {hash = "sha256:1c3dce098845a0db43b32a0b76a228ca059a668071cfeaa0f40c36c0b1585d45", size = 144741, upload-time = "2026-01-19T06:47:27.985Z", url = "https://files.pythonhosted.org/packages/af/cb/f421c2869d75750a4f32301cc20c4b63fab6376e9a75c8e5e655bdeb3d9b/multiprocess-0.70.19-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl"}, - {hash = "sha256:5be9ec7f0c1c49a4f4a6fd20d5dda4aeabc2d39a50f4ad53720f1cd02b3a7c2e", size = 144738, upload-time = "2026-01-19T06:47:26.636Z", url = "https://files.pythonhosted.org/packages/0f/e1/155f6abf5e6b5d9cef29b6d0167c180846157a4aca9b9bee1a217f67c959/multiprocess-0.70.19-pp311-pypy311_pp73-macosx_11_0_arm64.whl"}, - {hash = "sha256:928851ae7973aea4ce0eaf330bbdafb2e01398a91518d5c8818802845564f45c", size = 144457, upload-time = "2026-01-19T06:47:33.711Z", url = "https://files.pythonhosted.org/packages/86/c2/dec9722dc3474c164a0b6bcd9a7ed7da542c98af8cabce05374abab35edd/multiprocess-0.70.19-py311-none-any.whl"}, - {hash = "sha256:97404393419dcb2a8385910864eedf47a3cadf82c66345b44f036420eb0b5d87", size = 134948, upload-time = "2026-01-19T06:47:32.325Z", url = "https://files.pythonhosted.org/packages/e3/45/8004d1e6b9185c1a444d6b55ac5682acf9d98035e54386d967366035a03a/multiprocess-0.70.19-py310-none-any.whl"} + {hash = "sha256:0929ba95831adb938edbd5fb801ac45e705ecad9d100b3e653946b7716cb6bd3", size = 144742, upload-time = "2025-04-17T03:11:10.072Z", url = "https://files.pythonhosted.org/packages/17/bf/87323e79dd0562474fad3373c21c66bc6c3c9963b68eb2a209deb4c8575e/multiprocess-0.70.18-pp311-pypy311_pp73-macosx_11_0_arm64.whl"}, + {hash = "sha256:4d77f8e4bfe6c6e2e661925bbf9aed4d5ade9a1c6502d5dfc10129b9d1141797", size = 144745, upload-time = "2025-04-17T03:11:11.453Z", url = "https://files.pythonhosted.org/packages/dd/74/cb8c831e58dc6d5cf450b17c7db87f14294a1df52eb391da948b5e0a0b94/multiprocess-0.70.18-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl"}, + {hash = "sha256:5aa6eef98e691281b3ad923be2832bf1c55dd2c859acd73e5ec53a66aae06a1d", size = 144462, upload-time = "2025-04-17T03:11:21.657Z", url = "https://files.pythonhosted.org/packages/4b/88/9039f2fed1012ef584751d4ceff9ab4a51e5ae264898f0b7cbf44340a859/multiprocess-0.70.18-py311-none-any.whl"}, + {hash = "sha256:60c194974c31784019c1f459d984e8f33ee48f10fcf42c309ba97b30d9bd53ea", size = 134948, upload-time = "2025-04-17T03:11:20.223Z", url = "https://files.pythonhosted.org/packages/ba/d8/0cba6cf51a1a31f20471fbc823a716170c73012ddc4fb85d706630ed6e8f/multiprocess-0.70.18-py310-none-any.whl"}, + {hash = "sha256:8b8940ae30139e04b076da6c5b83e9398585ebdf0f2ad3250673fef5b2ff06d6", size = 144695, upload-time = "2025-04-17T03:11:09.161Z", url = "https://files.pythonhosted.org/packages/55/4d/9af0d1279c84618bcd35bf5fd7e371657358c7b0a523e54a9cffb87461f8/multiprocess-0.70.18-pp311-pypy311_pp73-macosx_10_15_x86_64.whl"}, + {hash = "sha256:dbf705e52a154fe5e90fb17b38f02556169557c2dd8bb084f2e06c2784d8279b", size = 132636, upload-time = "2025-04-17T03:11:24.936Z", url = "https://files.pythonhosted.org/packages/3b/c3/ca84c19bd14cdfc21c388fdcebf08b86a7a470ebc9f5c3c084fc2dbc50f7/multiprocess-0.70.18-py38-none-any.whl"}, + {hash = "sha256:e78ca805a72b1b810c690b6b4cc32579eba34f403094bbbae962b7b5bf9dfcb8", size = 133478, upload-time = "2025-04-17T03:11:26.253Z", url = "https://files.pythonhosted.org/packages/6c/28/dd72947e59a6a8c856448a5e74da6201cb5502ddff644fbc790e4bd40b9a/multiprocess-0.70.18-py39-none-any.whl"} ] [[package]] @@ -842,11 +1046,11 @@ dependencies = [ {name = "ppft"} ] name = "pathos" -sdist = {hash = "sha256:8fe041b8545c5d3880a038f866022bdebf935e5cf68f56ed3407cb7e65193a61", size = 166975, upload-time = "2026-01-20T00:06:57.848Z", url = "https://files.pythonhosted.org/packages/32/37/0c730979d3890f8096a86af2683fac74edd4d15cb037391098dca70dcb1d/pathos-0.3.5.tar.gz"} +sdist = {hash = "sha256:bad4912d0ef865654a7cc478da65f2e1d5b69f3d92c4a7d9c9845657783c0754", size = 167076, upload-time = "2025-04-17T03:37:08.234Z", url = "https://files.pythonhosted.org/packages/be/90/fdbe3bbfe79933db439e1844083cb6e9d5a9d3b686738549b3d22d06eae7/pathos-0.3.4.tar.gz"} source = {registry = "https://pypi.org/simple"} -version = "0.3.5" +version = "0.3.4" wheels = [ - {hash = "sha256:c95b04103c40a16c08db69cd4b5c52624d55208beadf1348681edece809ec4f8", size = 82248, upload-time = "2026-01-20T00:06:56.291Z", url = "https://files.pythonhosted.org/packages/37/44/be2146c650ee9ca4d9a770c995f5c92c1ea52292dcf618ce1a336d3146dd/pathos-0.3.5-py3-none-any.whl"} + {hash = "sha256:fe44883448c05c80d518b61df491b496f6190bb6860253f3254d8c9afb53c340", size = 82261, upload-time = "2025-04-17T03:37:06.936Z", url = "https://files.pythonhosted.org/packages/11/0a/daece46e65c821d153746566a1604ac90338f0279b1fb858a3617eb60472/pathos-0.3.4-py3-none-any.whl"} ] [[package]] @@ -944,6 +1148,45 @@ wheels = [ {hash = "sha256:d1099253bf73dd3c39313280bd5331841f769637b27ddb576ff362c4e7bad298", size = 825969, upload-time = "2025-11-17T12:59:26.493Z", url = "https://files.pythonhosted.org/packages/79/dc/d888b328fcedae530df53396d9fc0006026aa8793fec54d7d34f57f31ff5/preshed-3.0.12-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl"} ] +[[package]] +name = "propcache" +sdist = {hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z", url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "0.4.1" +wheels = [ + {hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z", url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl"}, + {hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z", url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl"}, + {hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z", url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z", url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl"}, + {hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z", url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl"}, + {hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z", url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl"}, + {hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z", url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl"}, + {hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z", url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z", url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl"}, + {hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z", url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl"}, + {hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z", url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl"}, + {hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z", url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl"}, + {hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z", url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl"}, + {hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z", url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl"}, + {hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z", url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl"}, + {hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z", url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"} +] + +[[package]] +name = "pyarrow" +sdist = {hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336, upload-time = "2026-02-16T10:14:12.39Z", url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "23.0.1" +wheels = [ + {hash = "sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350", size = 35850050, upload-time = "2026-02-16T10:09:11.877Z", url = "https://files.pythonhosted.org/packages/bf/4a/1472c00392f521fea03ae93408bf445cc7bfa1ab81683faf9bc188e36629/pyarrow-23.0.1-cp311-cp311-macosx_12_0_x86_64.whl"}, + {hash = "sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9", size = 47562811, upload-time = "2026-02-16T10:09:25.792Z", url = "https://files.pythonhosted.org/packages/0b/62/96459ef5b67957eac38a90f541d1c28833d1b367f014a482cb63f3b7cd2d/pyarrow-23.0.1-cp311-cp311-manylinux_2_28_x86_64.whl"}, + {hash = "sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701", size = 48183766, upload-time = "2026-02-16T10:09:34.645Z", url = "https://files.pythonhosted.org/packages/7d/94/1170e235add1f5f45a954e26cd0e906e7e74e23392dcb560de471f7366ec/pyarrow-23.0.1-cp311-cp311-musllinux_1_2_aarch64.whl"}, + {hash = "sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb", size = 34302230, upload-time = "2026-02-16T10:09:03.859Z", url = "https://files.pythonhosted.org/packages/b0/41/8e6b6ef7e225d4ceead8459427a52afdc23379768f54dd3566014d7618c1/pyarrow-23.0.1-cp311-cp311-macosx_12_0_arm64.whl"}, + {hash = "sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78", size = 50607669, upload-time = "2026-02-16T10:09:44.153Z", url = "https://files.pythonhosted.org/packages/0e/2d/39a42af4570377b99774cdb47f63ee6c7da7616bd55b3d5001aa18edfe4f/pyarrow-23.0.1-cp311-cp311-musllinux_1_2_x86_64.whl"}, + {hash = "sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919", size = 27527698, upload-time = "2026-02-16T10:09:50.263Z", url = "https://files.pythonhosted.org/packages/00/ca/db94101c187f3df742133ac837e93b1f269ebdac49427f8310ee40b6a58f/pyarrow-23.0.1-cp311-cp311-win_amd64.whl"}, + {hash = "sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd", size = 44491918, upload-time = "2026-02-16T10:09:18.144Z", url = "https://files.pythonhosted.org/packages/0c/b2/bd1f2f05ded56af7f54d702c8364c9c43cd6abb91b0e9933f3d77b4f4132/pyarrow-23.0.1-cp311-cp311-manylinux_2_28_aarch64.whl"} +] + [[package]] name = "pybind11" sdist = {hash = "sha256:ba6af10348c12b24e92fa086b39cfba0eff619b61ac77c406167d813b096d39a", size = 218403, upload-time = "2024-09-14T00:35:22.606Z", url = "https://files.pythonhosted.org/packages/d2/c1/72b9622fcb32ff98b054f724e213c7f70d6898baa714f4516288456ceaba/pybind11-2.13.6.tar.gz"} @@ -1334,6 +1577,21 @@ wheels = [ {hash = "sha256:e27e938fca23b87bab978a6098a30ad7d0974d10a630d2f5ba43103eefba4d06", size = 6508961, upload-time = "2023-01-20T09:49:58.926Z", url = "https://files.pythonhosted.org/packages/58/40/57d5fbb29050d08de344715530fb7ad25fc232650005768d1d385b103003/spacy-3.5.0-cp311-cp311-macosx_11_0_arm64.whl"} ] +[[package]] +name = "spacy-alignments" +sdist = {hash = "sha256:1c50b50d261550d3220887cfd0a12a6e85e709d815a66949f6b92340e4278d69", size = 7100, upload-time = "2025-06-03T11:19:09.405Z", url = "https://files.pythonhosted.org/packages/6d/15/37dd9a5dd5ebe1c3b0b2a56a1a922ed3c775b3e7d85d1f81e6b068739ee7/spacy_alignments-0.9.2.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "0.9.2" +wheels = [ + {hash = "sha256:44e9138b7bdb71bd69c11a87893c34ebeff29be6f3f2dd1870dc8a02ea353086", size = 292995, upload-time = "2025-06-03T11:18:23.429Z", url = "https://files.pythonhosted.org/packages/72/f3/bb28e38d154c98dea692c5d54d063e2008ceb2806c04856740fec544918a/spacy_alignments-0.9.2-cp311-cp311-macosx_10_12_x86_64.whl"}, + {hash = "sha256:6c31c794612e23be31ca3a558f70ec8836063f90de0bb70a0fc271de7a281db0", size = 288839, upload-time = "2025-06-03T11:18:25.063Z", url = "https://files.pythonhosted.org/packages/41/94/3ab5d11aab379be71a1ecc43294eb7b6aead60dfe21d82f0923d2bc0c1fc/spacy_alignments-0.9.2-cp311-cp311-macosx_11_0_arm64.whl"}, + {hash = "sha256:70c86a047665d33dbd470dc6e8b62b9a40bbc5ddf622982279b3ef7ebaa5925e", size = 184563, upload-time = "2025-06-03T11:18:33.67Z", url = "https://files.pythonhosted.org/packages/c4/0d/b54bf88cd900a2bbccd8094704cfdab7e8e606c90dc17ef075e35f43396b/spacy_alignments-0.9.2-cp311-cp311-win_amd64.whl"}, + {hash = "sha256:a79e76995deda6ddc2b53bfb55d4be8d73770844f9aea58cb3f7520d87feeb0e", size = 314523, upload-time = "2025-06-03T11:18:26.312Z", url = "https://files.pythonhosted.org/packages/3e/92/b48eab9fae4dc7c1e17a93a19e4c382657df1baa572464f6028a89f2ae62/spacy_alignments-0.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"}, + {hash = "sha256:c8628a5dc20c7226e6a0d102f41d6fd36832a617a39d8d5169a1cc2f908b1b98", size = 362532, upload-time = "2025-06-03T11:18:30.118Z", url = "https://files.pythonhosted.org/packages/f7/2e/b2ef0f4960d4f3d000ff60cee5841a114803137a559d98abf7d48a1cc3e2/spacy_alignments-0.9.2-cp311-cp311-musllinux_1_2_aarch64.whl"}, + {hash = "sha256:e8452dda516c7f94692e03570b1335bc17443ca3232a555c11efab5f97dedba0", size = 375666, upload-time = "2025-06-03T11:18:31.82Z", url = "https://files.pythonhosted.org/packages/f0/3f/cc4aced9e5b1814941752bd381fdf2a99a8d12ac4bc5253b375e8175fbfd/spacy_alignments-0.9.2-cp311-cp311-musllinux_1_2_x86_64.whl"}, + {hash = "sha256:f204610daa25dd7602d00cefecdea5e3e106eb434dc510facf2f1e6cf0bc95a5", size = 314214, upload-time = "2025-06-03T11:18:28.015Z", url = "https://files.pythonhosted.org/packages/a2/19/346e7325f68cf0e54feb7650e47c4668612c8a92b148d8a1392a9af974ed/spacy_alignments-0.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"} +] + [[package]] name = "spacy-legacy" sdist = {hash = "sha256:b37d6e0c9b6e1d7ca1cf5bc7152ab64a4c4671f59c85adaf7a3fcb870357a774", size = 23806, upload-time = "2023-01-23T09:04:15.104Z", url = "https://files.pythonhosted.org/packages/d9/79/91f9d7cc8db5642acad830dcc4b49ba65a7790152832c4eceb305e46d681/spacy-legacy-3.0.12.tar.gz"} @@ -1352,6 +1610,27 @@ wheels = [ {hash = "sha256:196284c9c446cc0cdb944005384270d775fdeaf4f494d8e269466cfa497ef645", size = 22343, upload-time = "2023-09-11T12:26:50.586Z", url = "https://files.pythonhosted.org/packages/33/78/d1a1a026ef3af911159398c939b1509d5c36fe524c7b644f34a5146c4e16/spacy_loggers-1.0.5-py3-none-any.whl"} ] +[[package]] +dependencies = [ + {name = "numpy"}, + {name = "spacy"}, + {name = "spacy-alignments"}, + {name = "srsly"}, + {name = "torch"}, + {name = "transformers"} +] +name = "spacy-transformers" +sdist = {hash = "sha256:f952105dc7dfa1dcd1e9012bd997ef108046231a8ac2c359a2d23e121d04388c", size = 47783, upload-time = "2023-06-12T11:23:54.855Z", url = "https://files.pythonhosted.org/packages/ed/b1/78c88ad9508572140a50adf574fbde435a9558f26d4430bcdd606f958738/spacy-transformers-1.2.5.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "1.2.5" +wheels = [ + {hash = "sha256:45aa4c07ff588c8ac1721997e59a8d485c589abc481ce8275edb23c6bd91aa59", size = 181672, upload-time = "2023-06-12T11:23:22.193Z", url = "https://files.pythonhosted.org/packages/f9/4c/37a137fe22ad83d38f2cdab7135b847c900c843e72e002e70b9b83e2ca38/spacy_transformers-1.2.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"}, + {hash = "sha256:7583df5a95151e6f2c6c80b325c85753ae0d738036eef665ab3643d4eb27670e", size = 190420, upload-time = "2023-06-12T11:23:24.227Z", url = "https://files.pythonhosted.org/packages/74/36/d27f73be691e6a7a9f8482b3817d57a48fd745f387b176e7404f70798645/spacy_transformers-1.2.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"}, + {hash = "sha256:b0a41ec5cf9cffe5b17c10a9b32e6e95ba63781f924c5be41706e38f2ef81cc3", size = 300752, upload-time = "2023-06-12T11:23:25.941Z", url = "https://files.pythonhosted.org/packages/ee/af/c7d0b47fe8a3aeebe7e6d50de69955ced232054bf2e861bd7cc492814685/spacy_transformers-1.2.5-cp311-cp311-win_amd64.whl"}, + {hash = "sha256:c791f2922a3b917e9389e33eeb6b7e5253441071bd82e2a8faeea72d8033657f", size = 171220, upload-time = "2023-06-12T11:23:21.009Z", url = "https://files.pythonhosted.org/packages/00/1d/f01ad2e5ca83b44c37163d3e29b21bb970f3d6b261d6917dbd597378c183/spacy_transformers-1.2.5-cp311-cp311-macosx_11_0_arm64.whl"}, + {hash = "sha256:d63646ddcec27e269fdd1ed5db58ba9a66bf71925bf81568cf0ddb90f98e3083", size = 176789, upload-time = "2023-06-12T11:23:19.554Z", url = "https://files.pythonhosted.org/packages/76/8d/5c5dec9d8f904a4c3e9c1a7d071e4401a9fc7f593913a5bf2f191134aca0/spacy_transformers-1.2.5-cp311-cp311-macosx_10_9_x86_64.whl"} +] + [[package]] dependencies = [ {name = "catalogue"} @@ -1484,6 +1763,7 @@ dependencies = [ {name = "coreferee"}, {name = "cupy-cuda12x"}, {name = "curlify"}, + {name = "fastcoref"}, {name = "flask"}, {name = "flask-compress"}, {name = "flask-cors"}, @@ -1507,6 +1787,7 @@ dependencies = [ {name = "setuptools"}, {name = "simple-websocket"}, {name = "spacy"}, + {name = "spacy-transformers"}, {name = "suthing"}, {name = "torch"}, {name = "transformers"}, @@ -1541,6 +1822,7 @@ requires-dist = [ {name = "coreferee", specifier = ">=1.4.0,<2"}, {name = "cupy-cuda12x", specifier = "==13.2.0"}, {name = "curlify", specifier = ">=2.2.1,<3"}, + {name = "fastcoref", specifier = ">=2.1.6"}, {name = "flask", specifier = ">=2.3.2,<3"}, {name = "flask-compress", specifier = ">=1.13,<2"}, {name = "flask-cors", specifier = ">=4.0.1,<5"}, @@ -1564,6 +1846,7 @@ requires-dist = [ {name = "setuptools", specifier = ">=80.9.0,<81"}, {name = "simple-websocket", specifier = ">=0.5.2,<0.6"}, {name = "spacy", specifier = "==3.5"}, + {name = "spacy-transformers", specifier = "==1.2.5"}, {name = "suthing", specifier = ">=0.3.0,<0.4"}, {name = "torch", specifier = "==1.13.0"}, {name = "transformers", specifier = "==4.30.2"}, @@ -1791,3 +2074,63 @@ version = "1.3.2" wheels = [ {hash = "sha256:61eea322cdf56e8cc904bd3ad7573359a242ba65688716b0710a5eb12beab584", size = 24405, upload-time = "2025-11-20T18:18:00.454Z", url = "https://files.pythonhosted.org/packages/a4/f5/10b68b7b1544245097b2a1b8238f66f2fc6dcaeb24ba5d917f52bd2eed4f/wsproto-1.3.2-py3-none-any.whl"} ] + +[[package]] +name = "xxhash" +sdist = {hash = "sha256:f0162a78b13a0d7617b2845b90c763339d1f1d82bb04a4b07f4ab535cc5e05d6", size = 85160, upload-time = "2025-10-02T14:37:08.097Z", url = "https://files.pythonhosted.org/packages/02/84/30869e01909fb37a6cc7e18688ee8bf1e42d57e7e0777636bd47524c43c7/xxhash-3.6.0.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "3.6.0" +wheels = [ + {hash = "sha256:08d45aef063a4531b785cd72de4887766d01dc8f362a515693df349fdb825e0c", size = 210867, upload-time = "2025-10-02T14:34:27.203Z", url = "https://files.pythonhosted.org/packages/67/74/b044fcd6b3d89e9b1b665924d85d3f400636c23590226feb1eb09e1176ce/xxhash-3.6.0-cp311-cp311-musllinux_1_2_ppc64le.whl"}, + {hash = "sha256:0f7b7e2ec26c1666ad5fc9dbfa426a6a3367ceaf79db5dd76264659d509d73b0", size = 30662, upload-time = "2025-10-02T14:37:01.743Z", url = "https://files.pythonhosted.org/packages/93/1e/8aec23647a34a249f62e2398c42955acd9b4c6ed5cf08cbea94dc46f78d2/xxhash-3.6.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl"}, + {hash = "sha256:15e0dac10eb9309508bfc41f7f9deaa7755c69e35af835db9cb10751adebc35d", size = 31565, upload-time = "2025-10-02T14:37:06.966Z", url = "https://files.pythonhosted.org/packages/7b/d9/8d95e906764a386a3d3b596f3c68bb63687dfca806373509f51ce8eea81f/xxhash-3.6.0-pp311-pypy311_pp73-win_amd64.whl"}, + {hash = "sha256:26734cdc2d4ffe449b41d186bbeac416f704a482ed835d375a5c0cb02bc63fef", size = 31481, upload-time = "2025-10-02T14:34:32.062Z", url = "https://files.pythonhosted.org/packages/65/79/9d24d7f53819fe301b231044ea362ce64e86c74f6e8c8e51320de248b3e5/xxhash-3.6.0-cp311-cp311-win_amd64.whl"}, + {hash = "sha256:297b7fbf86c82c550e12e8fb71968b3f033d27b874276ba3624ea868c11165a8", size = 193880, upload-time = "2025-10-02T14:34:22.431Z", url = "https://files.pythonhosted.org/packages/a5/86/cf2c0321dc3940a7aa73076f4fd677a0fb3e405cb297ead7d864fd90847e/xxhash-3.6.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}, + {hash = "sha256:2b6821e94346f96db75abaa6e255706fb06ebd530899ed76d32cd99f20dc52fa", size = 30809, upload-time = "2025-10-02T14:34:15.484Z", url = "https://files.pythonhosted.org/packages/5e/ec/1cc11cd13e26ea8bc3cb4af4eaadd8d46d5014aebb67be3f71fb0b68802a/xxhash-3.6.0-cp311-cp311-macosx_11_0_arm64.whl"}, + {hash = "sha256:4ccbff013972390b51a18ef1255ef5ac125c92dc9143b2d1909f59abc765540e", size = 445749, upload-time = "2025-10-02T14:34:20.659Z", url = "https://files.pythonhosted.org/packages/0f/18/ccc194ee698c6c623acbf0f8c2969811a8a4b6185af5e824cd27b9e4fd3e/xxhash-3.6.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl"}, + {hash = "sha256:51312c768403d8540487dbbfb557454cfc55589bbde6424456951f7fcd4facb3", size = 191409, upload-time = "2025-10-02T14:34:29.696Z", url = "https://files.pythonhosted.org/packages/ba/b3/5a4241309217c5c876f156b10778f3ab3af7ba7e3259e6d5f5c7d0129eb2/xxhash-3.6.0-cp311-cp311-musllinux_1_2_x86_64.whl"}, + {hash = "sha256:5dc1e14d14fa0f5789ec29a7062004b5933964bb9b02aae6622b8f530dc40296", size = 41056, upload-time = "2025-10-02T14:37:02.879Z", url = "https://files.pythonhosted.org/packages/b8/0b/b14510b38ba91caf43006209db846a696ceea6a847a0c9ba0a5b1adc53d6/xxhash-3.6.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {hash = "sha256:6812c25fe0d6c36a46ccb002f40f27ac903bf18af9f6dd8f9669cb4d176ab18f", size = 212384, upload-time = "2025-10-02T14:34:19.182Z", url = "https://files.pythonhosted.org/packages/c4/ef/3a9b05eb527457d5db13a135a2ae1a26c80fecd624d20f3e8dcc4cb170f3/xxhash-3.6.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {hash = "sha256:7a0b169aafb98f4284f73635a8e93f0735f9cbde17bd5ec332480484241aaa77", size = 198654, upload-time = "2025-10-02T14:34:25.644Z", url = "https://files.pythonhosted.org/packages/40/aa/4395e669b0606a096d6788f40dbdf2b819d6773aa290c19e6e83cbfc312f/xxhash-3.6.0-cp311-cp311-musllinux_1_2_i686.whl"}, + {hash = "sha256:881b47fc47e051b37d94d13e7455131054b56749b91b508b0907eb07900d1c13", size = 36251, upload-time = "2025-10-02T14:37:04.44Z", url = "https://files.pythonhosted.org/packages/50/55/15a7b8a56590e66ccd374bbfa3f9ffc45b810886c8c3b614e3f90bd2367c/xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {hash = "sha256:8b29ee68625ab37b04c0b40c3fafdf24d2f75ccd778333cfb698f65f6c463f62", size = 213550, upload-time = "2025-10-02T14:34:17.878Z", url = "https://files.pythonhosted.org/packages/90/3b/d1f1a8f5442a5fd8beedae110c5af7604dc37349a8e16519c13c19a9a2de/xxhash-3.6.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {hash = "sha256:929142361a48ee07f09121fe9e96a84950e8d4df3bb298ca5d88061969f34d7b", size = 414012, upload-time = "2025-10-02T14:34:28.409Z", url = "https://files.pythonhosted.org/packages/bc/fd/3ce73bf753b08cb19daee1eb14aa0d7fe331f8da9c02dd95316ddfe5275e/xxhash-3.6.0-cp311-cp311-musllinux_1_2_s390x.whl"}, + {hash = "sha256:b47bbd8cf2d72797f3c2772eaaac0ded3d3af26481a26d7d7d41dc2d3c46b04a", size = 32844, upload-time = "2025-10-02T14:34:14.037Z", url = "https://files.pythonhosted.org/packages/17/d4/cc2f0400e9154df4b9964249da78ebd72f318e35ccc425e9f403c392f22a/xxhash-3.6.0-cp311-cp311-macosx_10_9_x86_64.whl"}, + {hash = "sha256:c6dc31591899f5e5666f04cc2e529e69b4072827085c1ef15294d91a004bc1bd", size = 32481, upload-time = "2025-10-02T14:37:05.869Z", url = "https://files.pythonhosted.org/packages/62/b2/5ac99a041a29e58e95f907876b04f7067a0242cb85b5f39e726153981503/xxhash-3.6.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}, + {hash = "sha256:d0a9751f71a1a65ce3584e9cae4467651c7e70c9d31017fa57574583a4540248", size = 194665, upload-time = "2025-10-02T14:34:16.541Z", url = "https://files.pythonhosted.org/packages/04/5f/19fe357ea348d98ca22f456f75a30ac0916b51c753e1f8b2e0e6fb884cce/xxhash-3.6.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl"}, + {hash = "sha256:d1927a69feddc24c987b337ce81ac15c4720955b667fe9b588e02254b80446fd", size = 30574, upload-time = "2025-10-02T14:34:31.028Z", url = "https://files.pythonhosted.org/packages/c0/01/99bfbc15fb9abb9a72b088c1d95219fc4782b7d01fc835bd5744d66dd0b8/xxhash-3.6.0-cp311-cp311-win32.whl"}, + {hash = "sha256:d72f67ef8bf36e05f5b6c65e8524f265bd61071471cd4cf1d36743ebeeeb06b7", size = 27861, upload-time = "2025-10-02T14:34:33.555Z", url = "https://files.pythonhosted.org/packages/30/4e/15cd0e3e8772071344eab2961ce83f6e485111fed8beb491a3f1ce100270/xxhash-3.6.0-cp311-cp311-win_arm64.whl"}, + {hash = "sha256:dea26ae1eb293db089798d3973a5fc928a18fdd97cc8801226fae705b02b14b0", size = 210912, upload-time = "2025-10-02T14:34:23.937Z", url = "https://files.pythonhosted.org/packages/82/fb/96213c8560e6f948a1ecc9a7613f8032b19ee45f747f4fca4eb31bb6d6ed/xxhash-3.6.0-cp311-cp311-musllinux_1_2_aarch64.whl"} +] + +[[package]] +dependencies = [ + {name = "idna"}, + {name = "multidict"}, + {name = "propcache"} +] +name = "yarl" +sdist = {hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z", url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz"} +source = {registry = "https://pypi.org/simple"} +version = "1.23.0" +wheels = [ + {hash = "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", size = 107852, upload-time = "2026-03-01T22:05:04.986Z", url = "https://files.pythonhosted.org/packages/d3/1c/1a3387ee6d73589f6f2a220ae06f2984f6c20b40c734989b0a44f5987308/yarl-1.23.0-cp311-cp311-musllinux_1_2_s390x.whl"}, + {hash = "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", size = 108202, upload-time = "2026-03-01T22:05:02.273Z", url = "https://files.pythonhosted.org/packages/38/69/912e6c5e146793e5d4b5fe39ff5b00f4d22463dfd5a162bec565ac757673/yarl-1.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl"}, + {hash = "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", size = 102919, upload-time = "2026-03-01T22:05:06.397Z", url = "https://files.pythonhosted.org/packages/a4/b8/35c0750fcd5a3f781058bfd954515dd4b1eab45e218cbb85cf11132215f1/yarl-1.23.0-cp311-cp311-musllinux_1_2_x86_64.whl"}, + {hash = "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", size = 93859, upload-time = "2026-03-01T22:05:00.268Z", url = "https://files.pythonhosted.org/packages/e7/64/6980f99ab00e1f0ff67cb84766c93d595b067eed07439cfccfc8fb28c1a6/yarl-1.23.0-cp311-cp311-musllinux_1_2_armv7l.whl"}, + {hash = "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", size = 99866, upload-time = "2026-03-01T22:05:03.597Z", url = "https://files.pythonhosted.org/packages/59/97/35ca6767524687ad64e5f5c31ad54bc76d585585a9fcb40f649e7e82ffed/yarl-1.23.0-cp311-cp311-musllinux_1_2_riscv64.whl"}, + {hash = "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", size = 108099, upload-time = "2026-03-01T22:04:52.499Z", url = "https://files.pythonhosted.org/packages/85/fb/115b16f22c37ea4437d323e472945bea97301c8ec6089868fa560abab590/yarl-1.23.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl"}, + {hash = "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", size = 82602, upload-time = "2026-03-01T22:05:08.444Z", url = "https://files.pythonhosted.org/packages/e5/1c/9a1979aec4a81896d597bcb2177827f2dbee3f5b7cc48b2d0dadb644b41d/yarl-1.23.0-cp311-cp311-win32.whl"}, + {hash = "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", size = 100163, upload-time = "2026-03-01T22:04:58.492Z", url = "https://files.pythonhosted.org/packages/9e/c0/b39770b56d4a9f0bb5f77e2f1763cd2d75cc2f6c0131e3b4c360348fcd65/yarl-1.23.0-cp311-cp311-musllinux_1_2_aarch64.whl"}, + {hash = "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", size = 102678, upload-time = "2026-03-01T22:04:55.176Z", url = "https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl"}, + {hash = "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", size = 108741, upload-time = "2026-03-01T22:04:50.838Z", url = "https://files.pythonhosted.org/packages/d9/13/d269aa1aed3e4f50a5a103f96327210cc5fa5dd2d50882778f13c7a14606/yarl-1.23.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl"}, + {hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z", url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl"}, + {hash = "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", size = 92079, upload-time = "2026-03-01T22:04:48.925Z", url = "https://files.pythonhosted.org/packages/49/fb/c438fb5108047e629f6282a371e6e91cf3f97ee087c4fb748a1f32ceef55/yarl-1.23.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl"}, + {hash = "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", size = 100566, upload-time = "2026-03-01T22:04:47.639Z", url = "https://files.pythonhosted.org/packages/8c/6c/4a90d59c572e46b270ca132aca66954f1175abd691f74c1ef4c6711828e2/yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl"}, + {hash = "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", size = 123641, upload-time = "2026-03-01T22:04:42.841Z", url = "https://files.pythonhosted.org/packages/a2/aa/60da938b8f0997ba3a911263c40d82b6f645a67902a490b46f3355e10fae/yarl-1.23.0-cp311-cp311-macosx_10_9_universal2.whl"}, + {hash = "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", size = 82336, upload-time = "2026-03-01T22:05:11.554Z", url = "https://files.pythonhosted.org/packages/93/95/07e3553fe6f113e6864a20bdc53a78113cda3b9ced8784ee52a52c9f80d8/yarl-1.23.0-cp311-cp311-win_arm64.whl"}, + {hash = "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", size = 87461, upload-time = "2026-03-01T22:05:10.145Z", url = "https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl"}, + {hash = "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", size = 86248, upload-time = "2026-03-01T22:04:44.757Z", url = "https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl"}, + {hash = "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", size = 85988, upload-time = "2026-03-01T22:04:46.365Z", url = "https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl"}, + {hash = "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", size = 100803, upload-time = "2026-03-01T22:04:56.588Z", url = "https://files.pythonhosted.org/packages/85/59/cd98e556fbb2bf8fab29c1a722f67ad45c5f3447cac798ab85620d1e70af/yarl-1.23.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl"} +]