From 10525c5fe791314ab1bd6917b088116fcc091804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CPiotr?= Date: Mon, 15 Jun 2026 18:16:04 +0200 Subject: [PATCH 1/8] draft MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: “Piotr --- ai4rag/core/experiment/experiment.py | 10 +- ai4rag/evaluator/__init__.py | 8 + ai4rag/evaluator/base_evaluator.py | 1 + .../evaluator/mlflow_llm_judge_evaluator.py | 368 ++++++++++++++++++ docs/design/llm-as-judge-design.md | 178 +++++++++ pyproject.toml | 6 + .../test_mlflow_llm_judge_evaluator.py | 332 ++++++++++++++++ 7 files changed, 901 insertions(+), 2 deletions(-) create mode 100644 ai4rag/evaluator/mlflow_llm_judge_evaluator.py create mode 100644 docs/design/llm-as-judge-design.md create mode 100644 tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py diff --git a/ai4rag/core/experiment/experiment.py b/ai4rag/core/experiment/experiment.py index f6f0c5d0..3cea920e 100644 --- a/ai4rag/core/experiment/experiment.py +++ b/ai4rag/core/experiment/experiment.py @@ -203,14 +203,20 @@ def optimization_metric(self) -> str: @optimization_metric.setter def optimization_metric(self, val: str) -> None: """Validate and set optimization metrics""" - if val not in MetricType: + if val not in MetricType and not self._is_custom_evaluator_metric(val): raise RAGExperimentError( f"Provided optimization metric: '{val}' is not supported. " - f"Available metrics: ['answer_correctness', 'faithfulness', 'context_correctness']." + f"Available metrics: {list(MetricType)}." ) self._optimization_metric = val + def _is_custom_evaluator_metric(self, metric_name: str) -> bool: + """Check if a metric is supported by a custom evaluator.""" + if hasattr(self, "evaluator") and hasattr(self.evaluator, "get_supported_metrics"): + return metric_name in self.evaluator.get_supported_metrics() + return False + @property def benchmark_data(self) -> BenchmarkData: """Get benchmark data.""" diff --git a/ai4rag/evaluator/__init__.py b/ai4rag/evaluator/__init__.py index f1769d02..3665a1ef 100644 --- a/ai4rag/evaluator/__init__.py +++ b/ai4rag/evaluator/__init__.py @@ -4,3 +4,11 @@ # ----------------------------------------------------------------------------- from ai4rag.evaluator.base_evaluator import BaseEvaluator from ai4rag.evaluator.unitxt_evaluator import UnitxtEvaluator + +try: + from ai4rag.evaluator.mlflow_llm_judge_evaluator import ( + LLMJudgeConfig, + MlflowLLMJudgeEvaluator, + ) +except ImportError: + pass diff --git a/ai4rag/evaluator/base_evaluator.py b/ai4rag/evaluator/base_evaluator.py index 8487d6fc..5b62bfc5 100644 --- a/ai4rag/evaluator/base_evaluator.py +++ b/ai4rag/evaluator/base_evaluator.py @@ -77,6 +77,7 @@ class MetricType(metaclass=ConstantMeta): ANSWER_CORRECTNESS = "answer_correctness" FAITHFULNESS = "faithfulness" CONTEXT_CORRECTNESS = "context_correctness" + ANSWER_RELEVANCE = "answer_relevance" class BaseEvaluator(ABC): diff --git a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py new file mode 100644 index 00000000..47395bbf --- /dev/null +++ b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py @@ -0,0 +1,368 @@ +# ----------------------------------------------------------------------------- +# Copyright IBM Corp. 2025-2026 +# SPDX-License-Identifier: Apache-2.0 +# ----------------------------------------------------------------------------- +import json +from dataclasses import dataclass, field +from typing import Sequence + +import numpy as np + +from ai4rag.evaluator.base_evaluator import BaseEvaluator, EvaluationData, MetricType + +try: + import mlflow.genai + from mlflow.entities import Feedback + from mlflow.genai.scorers import scorer as mlflow_scorer + from openai import OpenAI +except ImportError as exc: + raise ImportError( + "mlflow and openai packages are required for LLM-as-a-Judge evaluation. " + "Install with: pip install ai4rag[llm-judge]" + ) from exc + + +@dataclass +class CustomMetricDefinition: + """ + Definition for a user-provided custom LLM judge metric. + + Parameters + ---------- + name : str + Metric identifier string (e.g., "medical_accuracy"). + + guidelines : str + Evaluation guidelines for the judge LLM describing what to assess. + """ + + name: str + guidelines: str + + +@dataclass +class LLMJudgeConfig: + """ + Configuration for the LLM-as-a-Judge evaluator. + + Parameters + ---------- + base_url : str + Base URL of the OpenAI-compatible API endpoint. + + api_key : str + API key for authentication. + + model : str + Model name as reported by the endpoint (e.g., ``"llama-31-8b-instruct"``). + + temperature : float + Temperature for the judge model. + + custom_metrics : list[CustomMetricDefinition] + Optional list of user-defined custom LLM judge metrics. + """ + + base_url: str = "https://api.openai.com/v1" + api_key: str = "" + model: str = "gpt-4o-mini" + temperature: float = 0.0 + custom_metrics: list[CustomMetricDefinition] = field(default_factory=list) + + +JUDGE_PROMPT_TEMPLATE = """\ +You are an impartial judge evaluating the quality of an AI assistant's response. + +## Task +{guidelines} + +## Context +Question: {question} +Retrieved context: {context} +Ground truth answer: {ground_truth} + +## Response to evaluate +{answer} + +## Instructions +Respond with ONLY a JSON object (no markdown, no extra text): +{{"score": , "rationale": ""}} + +Where: +- 1 = completely fails the criterion +- 2 = mostly fails with some relevant elements +- 3 = partially meets the criterion +- 4 = mostly meets with minor gaps +- 5 = fully meets the criterion +""" + + +class MlflowLLMJudgeEvaluator(BaseEvaluator): + """ + Evaluator that uses MLflow's evaluation framework with LLM-as-a-Judge. + + Uses ``mlflow.genai.evaluate()`` with custom ``@scorer`` functions that call + a judge LLM via the OpenAI client. This provides full MLflow tracking and + evaluation integration while routing judge calls to any OpenAI-compatible + endpoint via ``base_url``. + + All scores are normalized from the judge scale (1-5) to [0.0, 1.0]. + + Parameters + ---------- + config : LLMJudgeConfig + Configuration specifying the judge model endpoint and behavior. + """ + + METRIC_TYPE_MAP = { + MetricType.ANSWER_CORRECTNESS: { + "name": "answer_correctness", + "guidelines": ( + "Evaluate how factually correct the response is compared to the ground truth. " + "A correct answer must contain the same key facts as the ground truth." + ), + }, + MetricType.FAITHFULNESS: { + "name": "faithfulness", + "guidelines": ( + "Evaluate whether the response is grounded in the provided context. " + "The answer should only contain claims supported by the context, " + "without hallucinating information." + ), + }, + MetricType.CONTEXT_CORRECTNESS: { + "name": "context_correctness", + "guidelines": ( + "Evaluate how relevant the retrieved context is for answering the question. " + "Good context should contain the information necessary to answer the question." + ), + }, + MetricType.ANSWER_RELEVANCE: { + "name": "answer_relevance", + "guidelines": ( + "Evaluate how relevant and helpful the response is to the original question. " + "A relevant answer directly addresses what was asked." + ), + }, + } + + def __init__(self, config: LLMJudgeConfig): + self.config = config + self._client = OpenAI(base_url=config.base_url, api_key=config.api_key) + self._custom_metric_names: set[str] = set() + for cm in config.custom_metrics: + self._custom_metric_names.add(cm.name) + + def evaluate_metrics( + self, + evaluation_data: list[EvaluationData], + metrics: Sequence[str], + ) -> dict: + """ + Evaluate the model's responses using MLflow with LLM-as-a-Judge scorers. + + All scores are normalized to [0.0, 1.0]. + + Parameters + ---------- + evaluation_data : list[EvaluationData] + List of EvaluationData instances. + + metrics : Sequence[str] + Metric names from MetricType or custom metric names. + + Returns + ------- + dict + ``{"scores": {metric: {mean, ci_low, ci_high}}, "question_scores": {metric: {q_id: score}}}`` + """ + eval_data = self._build_eval_data(evaluation_data) + scorers = self._build_scorers(metrics) + if not scorers: + return {"scores": {}, "question_scores": {}} + + mlflow_result = mlflow.genai.evaluate(data=eval_data, scorers=scorers) + + question_ids = [ed.question_id or str(i) for i, ed in enumerate(evaluation_data)] + return self._format_results(mlflow_result, metrics, question_ids) + + @staticmethod + def _build_eval_data(evaluation_data: list[EvaluationData]) -> list[dict]: + """Convert EvaluationData list to MLflow evaluation data format.""" + rows = [] + for ed in evaluation_data: + contexts_str = "\n\n".join(ed.contexts) if ed.contexts else "" + ground_truths_str = "\n".join(ed.ground_truths) if ed.ground_truths else "" + rows.append({ + "inputs": { + "question": ed.question or "", + "context": contexts_str, + }, + "outputs": ed.answer or "", + "expectations": { + "expected_response": ground_truths_str, + }, + }) + return rows + + def _build_scorers(self, metrics: Sequence[str]) -> list: + """Build MLflow scorer functions for the requested metrics.""" + scorers = [] + for metric_name in metrics: + guidelines = self._get_guidelines(metric_name) + if guidelines is None: + continue + scorers.append(self._make_scorer(metric_name, guidelines)) + return scorers + + def _make_scorer(self, metric_name: str, guidelines: str): + """Create an MLflow @scorer that judges via the OpenAI client.""" + client = self._client + model = self.config.model + temperature = self.config.temperature + + @mlflow_scorer(name=metric_name) + def judge_scorer(inputs, outputs, expectations): + prompt = JUDGE_PROMPT_TEMPLATE.format( + guidelines=guidelines, + question=inputs.get("question", ""), + context=inputs.get("context", ""), + ground_truth=expectations.get("expected_response", "") if expectations else "", + answer=outputs or "", + ) + try: + response = client.chat.completions.create( + model=model, + messages=[{"role": "user", "content": prompt}], + temperature=temperature, + max_tokens=256, + ) + content = response.choices[0].message.content.strip() + parsed = _parse_score(content) + normalized = _normalize_score(parsed) + rationale = _parse_rationale(content) + return Feedback( + name=metric_name, + value=normalized, + rationale=rationale, + ) + except Exception as exc: + return Feedback( + name=metric_name, + value=None, + rationale=f"Judge call failed: {exc}", + ) + + return judge_scorer + + def _get_guidelines(self, metric_name: str) -> str | None: + """Get the guidelines string for a metric.""" + if metric_name in self.METRIC_TYPE_MAP: + return self.METRIC_TYPE_MAP[metric_name]["guidelines"] + if metric_name in self._custom_metric_names: + cm = next((c for c in self.config.custom_metrics if c.name == metric_name), None) + if cm is None: + return None + return cm.guidelines + return None + + def _format_results( + self, + mlflow_result, + metrics: Sequence[str], + question_ids: list[str], + ) -> dict: + """Extract per-metric scores from MLflow EvaluationResult.""" + scores = {} + question_scores = {} + + eval_table = mlflow_result.tables.get("eval_results") + + for metric_name in metrics: + col_name = f"{metric_name}/value" + if eval_table is None or col_name not in eval_table.columns: + continue + + raw_values = eval_table[col_name].tolist() + valid = [float(v) for v in raw_values if v is not None and not _is_nan(v)] + + mean_score = round(float(np.mean(valid)), 4) if valid else None + ci_low, ci_high = self._compute_confidence_interval(valid) + + scores[metric_name] = { + "mean": mean_score, + "ci_low": ci_low, + "ci_high": ci_high, + } + + question_scores[metric_name] = {} + for i, qid in enumerate(question_ids): + val = raw_values[i] if i < len(raw_values) else None + if val is not None and not _is_nan(val): + question_scores[metric_name][qid] = round(float(val), 4) + else: + question_scores[metric_name][qid] = None + + return {"scores": scores, "question_scores": question_scores} + + @staticmethod + def _compute_confidence_interval( + scores: list[float], confidence: float = 0.95, n_bootstrap: int = 1000 + ) -> tuple[float | None, float | None]: + """Compute bootstrap confidence interval for the mean score.""" + if len(scores) < 2: + return None, None + + rng = np.random.default_rng(seed=42) + bootstrap_means = [] + for _ in range(n_bootstrap): + sample = rng.choice(scores, size=len(scores), replace=True) + bootstrap_means.append(float(np.mean(sample))) + + alpha = (1 - confidence) / 2 + ci_low = round(float(np.percentile(bootstrap_means, alpha * 100)), 4) + ci_high = round(float(np.percentile(bootstrap_means, (1 - alpha) * 100)), 4) + + return ci_low, ci_high + + def get_supported_metrics(self) -> list[str]: + """Return all metric names this evaluator supports.""" + built_in = list(self.METRIC_TYPE_MAP.keys()) + custom = [cm.name for cm in self.config.custom_metrics] + return built_in + custom + + +def _parse_score(content: str) -> int | None: + """Parse a score (1-5) from the judge's JSON response.""" + try: + data = json.loads(content) + score = int(data["score"]) + if 1 <= score <= 5: + return score + except (json.JSONDecodeError, KeyError, ValueError, TypeError): + pass + return None + + +def _parse_rationale(content: str) -> str | None: + """Parse a rationale string from the judge's JSON response.""" + try: + data = json.loads(content) + return data.get("rationale") + except (json.JSONDecodeError, KeyError, ValueError, TypeError): + return None + + +def _normalize_score(score: int | None) -> float | None: + """Normalize a score from 1-5 to [0.0, 1.0].""" + if score is None: + return None + return (score - 1) / 4 + + +def _is_nan(value) -> bool: + """Check if a value is NaN.""" + try: + return np.isnan(float(value)) + except (TypeError, ValueError): + return False diff --git a/docs/design/llm-as-judge-design.md b/docs/design/llm-as-judge-design.md new file mode 100644 index 00000000..9f3c9c0c --- /dev/null +++ b/docs/design/llm-as-judge-design.md @@ -0,0 +1,178 @@ +# Design: LLM-as-a-Judge Optimization Metric + +## 1. Motivation + +The current ai4rag evaluation stack uses **unitxt** with algorithmic (non-LLM) metrics: +`answer_correctness`, `faithfulness`, and `context_correctness`. These are fast and deterministic +but limited in capturing nuanced quality dimensions like helpfulness, coherence, or domain-specific +correctness that only an LLM judge can assess. + +**Goal:** Allow users to use LLM-as-a-Judge as optimization metrics in ai4rag's +HPO loop, by swapping the evaluator. The same metric names (`answer_correctness`, `faithfulness`, +etc.) are used — the evaluator determines the evaluation method. + +--- + +## 2. Architecture + +``` +BaseEvaluator (ABC) + └── evaluate_metrics(evaluation_data, metrics) -> dict + +UnitxtEvaluator(BaseEvaluator) # algorithmic metrics +MlflowLLMJudgeEvaluator(BaseEvaluator) # LLM-as-a-Judge metrics + +MetricType (ConstantMeta) + └── ANSWER_CORRECTNESS, FAITHFULNESS, CONTEXT_CORRECTNESS, ANSWER_RELEVANCE +``` + +The evaluator is a constructor parameter of `AI4RAGExperiment`. Users swap between unitxt and +LLM-as-a-Judge by passing a different evaluator — no changes to the optimizer or experiment +orchestrator are needed. Both evaluators use the same metric names and return the same dict format. + +All scores are normalized to **[0.0, 1.0]**. + +--- + +## 3. New Metric Type + +One new metric added to `MetricType`: + +```python +class MetricType(metaclass=ConstantMeta): + ANSWER_CORRECTNESS = "answer_correctness" + FAITHFULNESS = "faithfulness" + CONTEXT_CORRECTNESS = "context_correctness" + ANSWER_RELEVANCE = "answer_relevance" # NEW +``` + +No `LLM_` prefix — the metric name describes *what* is measured, not *how*. + +--- + +## 4. MlflowLLMJudgeEvaluator + +New file: `ai4rag/evaluator/mlflow_llm_judge_evaluator.py` + +### 4.1 Configuration + +```python +@dataclass +class LLMJudgeConfig: + base_url: str = "https://api.openai.com/v1" + api_key: str = "" + model: str = "gpt-4o-mini" + temperature: float = 0.0 + custom_metrics: list[CustomMetricDefinition] = field(default_factory=list) +``` + +The evaluator uses **MLflow's evaluation framework** (`mlflow.genai.evaluate()`) with custom +`@scorer` functions. The scorers call the judge LLM via the **OpenAI Python client** with a +configurable `base_url`, enabling any OpenAI-compatible endpoint (vLLM, TGI, etc.). This gives +full MLflow tracking/logging integration while working around MLflow's internal gateway routing +which does not respect custom base URLs for `openai:/` URIs. + +### 4.2 Built-in Metric Prompts + +The evaluator ships with grading prompts for all four `MetricType` values: +- `answer_correctness` — factual correctness vs ground truth +- `faithfulness` — grounding in retrieved context +- `context_correctness` — relevance of retrieved documents +- `answer_relevance` — relevance and helpfulness to the question + +Each uses a 1-5 scale that is normalized to [0.0, 1.0] via `(score - 1) / 4`. + +### 4.3 Evaluation Flow + +``` +1. Convert list[EvaluationData] → MLflow eval data format + (inputs: {question, context}, outputs: answer, expectations: {expected_response}) +2. Build MLflow @scorer functions for each requested metric + - Each scorer calls the judge LLM via OpenAI client + - Parses JSON {"score": 1-5, "rationale": "..."} response + - Normalizes to [0.0, 1.0] and returns mlflow.entities.Feedback +3. Call mlflow.genai.evaluate(data=eval_data, scorers=scorers) +4. Extract per-row scores from eval_results table +5. Compute mean + bootstrap confidence intervals (seed=42, n=1000) +6. Return {"scores": {metric: {mean, ci_low, ci_high}}, + "question_scores": {metric: {q_id: score}}} +``` + +### 4.4 Custom LLM Judge Metrics + +```python +config = LLMJudgeConfig( + base_url="https://my-llm-endpoint.example.com/v1", + api_key="my-token", + model="llama-31-8b-instruct", + custom_metrics=[ + CustomMetricDefinition( + name="medical_accuracy", + guidelines="Evaluate whether the answer contains medically accurate information.", + ) + ] +) +``` + +Custom metric names are accepted as `optimization_metric` when the evaluator supports them. + +--- + +## 5. Integration Points + +| File | Change | +|------|--------| +| `evaluator/base_evaluator.py` | Add `ANSWER_RELEVANCE` to `MetricType` | +| `evaluator/mlflow_llm_judge_evaluator.py` | **New file** | +| `evaluator/__init__.py` | Conditional export of new classes | +| `core/experiment/experiment.py` | Accept custom evaluator metric names in validation | +| `pyproject.toml` | Add `mlflow` + `openai` as optional dependencies (`ai4rag[llm-judge]`) | + +--- + +## 6. Dependency Management + +```toml +[project.optional-dependencies] +llm-judge = ["mlflow>=3.0.0", "openai>=1.0.0"] +``` + +The evaluator raises a clear `ImportError` if the packages are not installed. + +--- + +## 7. Usage Example + +```python +from ai4rag.evaluator.mlflow_llm_judge_evaluator import MlflowLLMJudgeEvaluator, LLMJudgeConfig +from ai4rag.evaluator.base_evaluator import MetricType + +config = LLMJudgeConfig( + base_url="https://llama-31-8b-instruct.apps.example.com/v1", + api_key="my-token", + model="llama-31-8b-instruct", +) + +experiment = AI4RAGExperiment( + documents=documents, + benchmark_data=benchmark_df, + search_space=search_space, + vector_store_type="chroma", + optimizer_settings=optimizer_settings, + event_handler=event_handler, + evaluator=MlflowLLMJudgeEvaluator(config), + optimization_metric=MetricType.FAITHFULNESS, + metrics=(MetricType.ANSWER_CORRECTNESS, MetricType.FAITHFULNESS), +) + +experiment.search() +``` + +--- + +## References + +- [MLflow LLM Evaluation docs](https://mlflow.org/docs/latest/genai/eval-monitor/llm-evaluation/) +- [MLflow Custom Scorers](https://mlflow.org/docs/latest/genai/eval-monitor/scorers/) +- [OpenAI Python Client](https://github.com/openai/openai-python) +- [vLLM OpenAI Compatible Server](https://docs.vllm.ai/en/latest/serving/openai_compatible_server.html) diff --git a/pyproject.toml b/pyproject.toml index 44f64349..c79269f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,10 +57,16 @@ Documentation = "https://ibm.github.io/ai4rag/" [project.optional-dependencies] +llm-judge = [ + "mlflow>=3.0.0", + "openai>=1.0.0", +] + dev = [ "ai4rag[test]", "ai4rag[code_check]", "ai4rag[docs]", + "ai4rag[llm-judge]", "beautifulsoup4", # reading data from html "dotenv", "ipykernel", diff --git a/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py b/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py new file mode 100644 index 00000000..68b85283 --- /dev/null +++ b/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py @@ -0,0 +1,332 @@ +# ----------------------------------------------------------------------------- +# Copyright IBM Corp. 2026 +# SPDX-License-Identifier: Apache-2.0 +# ----------------------------------------------------------------------------- +import json +from unittest.mock import MagicMock, patch + +import numpy as np +import pandas as pd +import pytest + +from ai4rag.evaluator.base_evaluator import BaseEvaluator, EvaluationData, MetricType +from ai4rag.evaluator.mlflow_llm_judge_evaluator import ( + CustomMetricDefinition, + LLMJudgeConfig, + MlflowLLMJudgeEvaluator, + _normalize_score, + _parse_rationale, + _parse_score, +) + + +@pytest.fixture +def sample_evaluation_data() -> list[EvaluationData]: + return [ + EvaluationData( + question="What is Python?", + answer="Python is a programming language.", + contexts=["Python is a high-level programming language."], + context_ids=["doc1"], + ground_truths=["Python is a programming language."], + ground_truths_context_ids=["doc1"], + question_id="q1", + ), + EvaluationData( + question="What is AI?", + answer="AI is Artificial Intelligence.", + contexts=["AI stands for Artificial Intelligence."], + context_ids=["doc2"], + ground_truths=["AI is Artificial Intelligence."], + ground_truths_context_ids=["doc2"], + question_id="q2", + ), + ] + + +def _make_chat_response(score: int, rationale: str = "OK") -> MagicMock: + resp = MagicMock() + resp.choices = [MagicMock()] + resp.choices[0].message.content = json.dumps( + {"score": score, "rationale": rationale} + ) + return resp + + +class TestLLMJudgeConfig: + def test_default_config(self): + config = LLMJudgeConfig() + assert config.base_url == "https://api.openai.com/v1" + assert config.api_key == "" + assert config.model == "gpt-4o-mini" + assert config.temperature == 0.0 + assert config.custom_metrics == [] + + def test_custom_config(self): + config = LLMJudgeConfig( + base_url="https://my-llm.example.com/v1", + api_key="sk-test", + model="llama-31-8b-instruct", + temperature=0.1, + ) + assert config.base_url == "https://my-llm.example.com/v1" + assert config.model == "llama-31-8b-instruct" + assert config.temperature == 0.1 + + +class TestCustomMetricDefinition: + def test_basic_definition(self): + metric = CustomMetricDefinition( + name="medical_accuracy", + guidelines="Evaluate medical accuracy of the response.", + ) + assert metric.name == "medical_accuracy" + assert "medical" in metric.guidelines + + +class TestParseScore: + def test_valid_scores(self): + assert _parse_score('{"score": 1, "rationale": "bad"}') == 1 + assert _parse_score('{"score": 3, "rationale": "ok"}') == 3 + assert _parse_score('{"score": 5, "rationale": "great"}') == 5 + + def test_invalid_scores(self): + assert _parse_score("not json") is None + assert _parse_score('{"score": 0}') is None + assert _parse_score('{"score": 6}') is None + assert _parse_score('{"no_score": 3}') is None + + +class TestParseRationale: + def test_valid(self): + assert _parse_rationale('{"score": 4, "rationale": "Good"}') == "Good" + + def test_invalid(self): + assert _parse_rationale("not json") is None + + def test_missing_key(self): + assert _parse_rationale('{"score": 4}') is None + + +class TestNormalizeScore: + def test_normalize(self): + assert _normalize_score(1) == 0.0 + assert _normalize_score(5) == 1.0 + assert _normalize_score(3) == 0.5 + assert _normalize_score(None) is None + + +class TestMlflowLLMJudgeEvaluator: + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_is_base_evaluator(self, mock_openai_cls): + evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) + assert isinstance(evaluator, BaseEvaluator) + + def test_metric_type_map_has_all_metrics(self): + assert MetricType.ANSWER_CORRECTNESS in MlflowLLMJudgeEvaluator.METRIC_TYPE_MAP + assert MetricType.FAITHFULNESS in MlflowLLMJudgeEvaluator.METRIC_TYPE_MAP + assert MetricType.CONTEXT_CORRECTNESS in MlflowLLMJudgeEvaluator.METRIC_TYPE_MAP + assert MetricType.ANSWER_RELEVANCE in MlflowLLMJudgeEvaluator.METRIC_TYPE_MAP + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_get_supported_metrics_builtin_only(self, mock_openai_cls): + evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) + supported = evaluator.get_supported_metrics() + assert MetricType.ANSWER_CORRECTNESS in supported + assert MetricType.FAITHFULNESS in supported + assert MetricType.CONTEXT_CORRECTNESS in supported + assert MetricType.ANSWER_RELEVANCE in supported + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_get_supported_metrics_with_custom(self, mock_openai_cls): + config = LLMJudgeConfig( + custom_metrics=[ + CustomMetricDefinition( + name="domain_accuracy", + guidelines="Evaluate domain accuracy.", + ) + ] + ) + evaluator = MlflowLLMJudgeEvaluator(config) + supported = evaluator.get_supported_metrics() + assert "domain_accuracy" in supported + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_build_eval_data(self, mock_openai_cls, sample_evaluation_data): + evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) + data = evaluator._build_eval_data(sample_evaluation_data) + + assert len(data) == 2 + assert data[0]["inputs"]["question"] == "What is Python?" + assert data[0]["outputs"] == "Python is a programming language." + assert data[0]["expectations"]["expected_response"] == "Python is a programming language." + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_build_eval_data_handles_none(self, mock_openai_cls): + evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) + data = evaluator._build_eval_data([EvaluationData(question_id="q1")]) + + assert data[0]["inputs"]["question"] == "" + assert data[0]["outputs"] == "" + assert data[0]["inputs"]["context"] == "" + assert data[0]["expectations"]["expected_response"] == "" + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_build_scorers(self, mock_openai_cls): + evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) + scorers = evaluator._build_scorers([MetricType.FAITHFULNESS]) + assert len(scorers) == 1 + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_build_scorers_unknown_skipped(self, mock_openai_cls): + evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) + scorers = evaluator._build_scorers(["nonexistent_metric"]) + assert len(scorers) == 0 + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_get_guidelines_builtin(self, mock_openai_cls): + evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) + guidelines = evaluator._get_guidelines(MetricType.FAITHFULNESS) + assert guidelines is not None + assert "grounded" in guidelines + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_get_guidelines_custom(self, mock_openai_cls): + config = LLMJudgeConfig( + custom_metrics=[ + CustomMetricDefinition(name="custom", guidelines="Custom eval.") + ] + ) + evaluator = MlflowLLMJudgeEvaluator(config) + assert evaluator._get_guidelines("custom") == "Custom eval." + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_get_guidelines_unknown(self, mock_openai_cls): + evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) + assert evaluator._get_guidelines("nonexistent") is None + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_get_guidelines_stale_custom_metric_names(self, mock_openai_cls): + config = LLMJudgeConfig( + custom_metrics=[ + CustomMetricDefinition(name="custom", guidelines="Custom eval.") + ] + ) + evaluator = MlflowLLMJudgeEvaluator(config) + config.custom_metrics.clear() + assert evaluator._get_guidelines("custom") is None + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_format_results(self, mock_openai_cls): + evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) + + mock_result = MagicMock() + mock_result.tables = { + "eval_results": pd.DataFrame({ + "faithfulness/value": [0.75, 0.5, 1.0], + }) + } + + result = evaluator._format_results( + mock_result, + [MetricType.FAITHFULNESS], + ["q1", "q2", "q3"], + ) + + assert "scores" in result + assert "question_scores" in result + assert MetricType.FAITHFULNESS in result["scores"] + assert result["scores"][MetricType.FAITHFULNESS]["mean"] is not None + + q_scores = result["question_scores"][MetricType.FAITHFULNESS] + assert q_scores["q1"] == 0.75 + assert q_scores["q2"] == 0.5 + assert q_scores["q3"] == 1.0 + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_format_results_all_scores_in_0_1(self, mock_openai_cls): + evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) + + mock_result = MagicMock() + mock_result.tables = { + "eval_results": pd.DataFrame({ + "answer_correctness/value": [0.0, 0.25, 0.5, 0.75, 1.0], + }) + } + + result = evaluator._format_results( + mock_result, + [MetricType.ANSWER_CORRECTNESS], + ["q1", "q2", "q3", "q4", "q5"], + ) + + for qid, score in result["question_scores"][MetricType.ANSWER_CORRECTNESS].items(): + assert 0.0 <= score <= 1.0, f"Score {score} for {qid} not in [0.0, 1.0]" + + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.mlflow") + @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") + def test_evaluate_metrics_full_flow(self, mock_openai_cls, mock_mlflow, sample_evaluation_data): + mock_client = MagicMock() + mock_openai_cls.return_value = mock_client + mock_client.chat.completions.create.side_effect = [ + _make_chat_response(4), + _make_chat_response(3), + ] + + mock_eval_result = MagicMock() + mock_eval_result.tables = { + "eval_results": pd.DataFrame({ + "faithfulness/value": [0.75, 0.5], + }) + } + mock_mlflow.genai.evaluate.return_value = mock_eval_result + + evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) + result = evaluator.evaluate_metrics( + sample_evaluation_data, + [MetricType.FAITHFULNESS], + ) + + assert "scores" in result + assert "question_scores" in result + assert MetricType.FAITHFULNESS in result["scores"] + + mean = result["scores"][MetricType.FAITHFULNESS]["mean"] + assert 0.0 <= mean <= 1.0 + + mock_mlflow.genai.evaluate.assert_called_once() + + def test_compute_confidence_interval(self): + scores = [0.8, 0.85, 0.9, 0.75, 0.88, 0.92, 0.7, 0.95] + ci_low, ci_high = MlflowLLMJudgeEvaluator._compute_confidence_interval(scores) + assert ci_low is not None + assert ci_high is not None + assert ci_low < ci_high + assert ci_low >= 0.0 + assert ci_high <= 1.0 + + def test_compute_confidence_interval_single_score(self): + ci_low, ci_high = MlflowLLMJudgeEvaluator._compute_confidence_interval([0.5]) + assert ci_low is None + assert ci_high is None + + def test_compute_confidence_interval_empty(self): + ci_low, ci_high = MlflowLLMJudgeEvaluator._compute_confidence_interval([]) + assert ci_low is None + assert ci_high is None + + +class TestMetricTypeExtensions: + def test_answer_relevance_metric_exists(self): + assert MetricType.ANSWER_RELEVANCE == "answer_relevance" + + def test_answer_relevance_in_iteration(self): + assert "answer_relevance" in list(MetricType) + + def test_answer_relevance_in_containment(self): + assert "answer_relevance" in MetricType + + def test_original_metrics_still_present(self): + assert "answer_correctness" in MetricType + assert "faithfulness" in MetricType + assert "context_correctness" in MetricType From 3ac3bbb69776c568ca9db7d8a828e3b7922d0a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CPiotr?= Date: Tue, 16 Jun 2026 09:35:50 +0200 Subject: [PATCH 2/8] lint fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: “Piotr --- ai4rag/core/experiment/experiment.py | 3 +-- .../evaluator/mlflow_llm_judge_evaluator.py | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ai4rag/core/experiment/experiment.py b/ai4rag/core/experiment/experiment.py index 3cea920e..088695d9 100644 --- a/ai4rag/core/experiment/experiment.py +++ b/ai4rag/core/experiment/experiment.py @@ -205,8 +205,7 @@ def optimization_metric(self, val: str) -> None: """Validate and set optimization metrics""" if val not in MetricType and not self._is_custom_evaluator_metric(val): raise RAGExperimentError( - f"Provided optimization metric: '{val}' is not supported. " - f"Available metrics: {list(MetricType)}." + f"Provided optimization metric: '{val}' is not supported. " f"Available metrics: {list(MetricType)}." ) self._optimization_metric = val diff --git a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py index 47395bbf..dde93f91 100644 --- a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py +++ b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py @@ -193,16 +193,18 @@ def _build_eval_data(evaluation_data: list[EvaluationData]) -> list[dict]: for ed in evaluation_data: contexts_str = "\n\n".join(ed.contexts) if ed.contexts else "" ground_truths_str = "\n".join(ed.ground_truths) if ed.ground_truths else "" - rows.append({ - "inputs": { - "question": ed.question or "", - "context": contexts_str, - }, - "outputs": ed.answer or "", - "expectations": { - "expected_response": ground_truths_str, - }, - }) + rows.append( + { + "inputs": { + "question": ed.question or "", + "context": contexts_str, + }, + "outputs": ed.answer or "", + "expectations": { + "expected_response": ground_truths_str, + }, + } + ) return rows def _build_scorers(self, metrics: Sequence[str]) -> list: From f2a1874b3ff2e94b006b1d78788ebb72fe414fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CPiotr?= Date: Tue, 16 Jun 2026 09:59:07 +0200 Subject: [PATCH 3/8] fix: resolve pylint broad-exception and too-many-locals warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Narrow except clause from Exception to (OpenAIError, ValueError, KeyError) and reduce local variables in _format_results by inlining intermediates. Signed-off-by: Piotr Co-Authored-By: Claude Opus 4.6 Signed-off-by: “Piotr --- ai4rag/evaluator/mlflow_llm_judge_evaluator.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py index dde93f91..07cc8ffa 100644 --- a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py +++ b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py @@ -14,7 +14,7 @@ import mlflow.genai from mlflow.entities import Feedback from mlflow.genai.scorers import scorer as mlflow_scorer - from openai import OpenAI + from openai import OpenAI, OpenAIError except ImportError as exc: raise ImportError( "mlflow and openai packages are required for LLM-as-a-Judge evaluation. " @@ -248,7 +248,7 @@ def judge_scorer(inputs, outputs, expectations): value=normalized, rationale=rationale, ) - except Exception as exc: + except (OpenAIError, ValueError, KeyError) as exc: return Feedback( name=metric_name, value=None, @@ -288,13 +288,11 @@ def _format_results( raw_values = eval_table[col_name].tolist() valid = [float(v) for v in raw_values if v is not None and not _is_nan(v)] - mean_score = round(float(np.mean(valid)), 4) if valid else None - ci_low, ci_high = self._compute_confidence_interval(valid) - + ci = self._compute_confidence_interval(valid) scores[metric_name] = { - "mean": mean_score, - "ci_low": ci_low, - "ci_high": ci_high, + "mean": round(float(np.mean(valid)), 4) if valid else None, + "ci_low": ci[0], + "ci_high": ci[1], } question_scores[metric_name] = {} From 933d4fae0c0b391ba461ba372f0fc9241f68722d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CPiotr?= Date: Tue, 16 Jun 2026 10:06:37 +0200 Subject: [PATCH 4/8] fix: update copyright year to match file creation date MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 Signed-off-by: “Piotr --- ai4rag/evaluator/mlflow_llm_judge_evaluator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py index 07cc8ffa..0d5f863e 100644 --- a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py +++ b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py @@ -1,5 +1,5 @@ # ----------------------------------------------------------------------------- -# Copyright IBM Corp. 2025-2026 +# Copyright IBM Corp. 2026 # SPDX-License-Identifier: Apache-2.0 # ----------------------------------------------------------------------------- import json From 90ad6eb26526e204ae9d51cd61aa425312403a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CPiotr?= Date: Tue, 16 Jun 2026 10:18:46 +0200 Subject: [PATCH 5/8] fix: guard LLM judge tests with importorskip for optional deps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add pytest.importorskip for mlflow and openai so tests are skipped when these optional dependencies are not installed. Also remove unused numpy import and apply Black formatting. Co-Authored-By: Claude Opus 4.6 Signed-off-by: “Piotr --- .../test_mlflow_llm_judge_evaluator.py | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py b/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py index 68b85283..802a06c3 100644 --- a/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py +++ b/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py @@ -5,10 +5,12 @@ import json from unittest.mock import MagicMock, patch -import numpy as np import pandas as pd import pytest +pytest.importorskip("mlflow", reason="mlflow is required for LLM-as-a-Judge tests") +pytest.importorskip("openai", reason="openai is required for LLM-as-a-Judge tests") + from ai4rag.evaluator.base_evaluator import BaseEvaluator, EvaluationData, MetricType from ai4rag.evaluator.mlflow_llm_judge_evaluator import ( CustomMetricDefinition, @@ -47,9 +49,7 @@ def sample_evaluation_data() -> list[EvaluationData]: def _make_chat_response(score: int, rationale: str = "OK") -> MagicMock: resp = MagicMock() resp.choices = [MagicMock()] - resp.choices[0].message.content = json.dumps( - {"score": score, "rationale": rationale} - ) + resp.choices[0].message.content = json.dumps({"score": score, "rationale": rationale}) return resp @@ -192,11 +192,7 @@ def test_get_guidelines_builtin(self, mock_openai_cls): @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") def test_get_guidelines_custom(self, mock_openai_cls): - config = LLMJudgeConfig( - custom_metrics=[ - CustomMetricDefinition(name="custom", guidelines="Custom eval.") - ] - ) + config = LLMJudgeConfig(custom_metrics=[CustomMetricDefinition(name="custom", guidelines="Custom eval.")]) evaluator = MlflowLLMJudgeEvaluator(config) assert evaluator._get_guidelines("custom") == "Custom eval." @@ -207,11 +203,7 @@ def test_get_guidelines_unknown(self, mock_openai_cls): @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") def test_get_guidelines_stale_custom_metric_names(self, mock_openai_cls): - config = LLMJudgeConfig( - custom_metrics=[ - CustomMetricDefinition(name="custom", guidelines="Custom eval.") - ] - ) + config = LLMJudgeConfig(custom_metrics=[CustomMetricDefinition(name="custom", guidelines="Custom eval.")]) evaluator = MlflowLLMJudgeEvaluator(config) config.custom_metrics.clear() assert evaluator._get_guidelines("custom") is None @@ -222,9 +214,11 @@ def test_format_results(self, mock_openai_cls): mock_result = MagicMock() mock_result.tables = { - "eval_results": pd.DataFrame({ - "faithfulness/value": [0.75, 0.5, 1.0], - }) + "eval_results": pd.DataFrame( + { + "faithfulness/value": [0.75, 0.5, 1.0], + } + ) } result = evaluator._format_results( @@ -249,9 +243,11 @@ def test_format_results_all_scores_in_0_1(self, mock_openai_cls): mock_result = MagicMock() mock_result.tables = { - "eval_results": pd.DataFrame({ - "answer_correctness/value": [0.0, 0.25, 0.5, 0.75, 1.0], - }) + "eval_results": pd.DataFrame( + { + "answer_correctness/value": [0.0, 0.25, 0.5, 0.75, 1.0], + } + ) } result = evaluator._format_results( @@ -275,9 +271,11 @@ def test_evaluate_metrics_full_flow(self, mock_openai_cls, mock_mlflow, sample_e mock_eval_result = MagicMock() mock_eval_result.tables = { - "eval_results": pd.DataFrame({ - "faithfulness/value": [0.75, 0.5], - }) + "eval_results": pd.DataFrame( + { + "faithfulness/value": [0.75, 0.5], + } + ) } mock_mlflow.genai.evaluate.return_value = mock_eval_result From 8b9b9e0b30ab04616dfbcafc5bcf4f3b4ebd09fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CPiotr?= Date: Tue, 16 Jun 2026 10:49:12 +0200 Subject: [PATCH 6/8] fix: robust JSON parsing for LLM judge responses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Llama and other open models often wrap JSON in markdown fences or add preamble text. Add _extract_json() that tries direct parsing, then markdown fence extraction, then first brace-pair extraction. Co-Authored-By: Claude Opus 4.6 Signed-off-by: “Piotr --- .../evaluator/mlflow_llm_judge_evaluator.py | 38 ++++++++++++++++--- .../test_mlflow_llm_judge_evaluator.py | 33 ++++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py index 0d5f863e..0a39225e 100644 --- a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py +++ b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 # ----------------------------------------------------------------------------- import json +import re from dataclasses import dataclass, field from typing import Sequence @@ -332,25 +333,50 @@ def get_supported_metrics(self) -> list[str]: return built_in + custom +def _extract_json(content: str) -> dict | None: + """Extract a JSON object from LLM output that may contain markdown fences or extra text.""" + try: + return json.loads(content) + except (json.JSONDecodeError, ValueError): + pass + + fence_match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", content, re.DOTALL) + if fence_match: + try: + return json.loads(fence_match.group(1)) + except (json.JSONDecodeError, ValueError): + pass + + brace_match = re.search(r"\{[^{}]*\}", content) + if brace_match: + try: + return json.loads(brace_match.group(0)) + except (json.JSONDecodeError, ValueError): + pass + + return None + + def _parse_score(content: str) -> int | None: """Parse a score (1-5) from the judge's JSON response.""" + data = _extract_json(content) + if data is None: + return None try: - data = json.loads(content) score = int(data["score"]) if 1 <= score <= 5: return score - except (json.JSONDecodeError, KeyError, ValueError, TypeError): + except (KeyError, ValueError, TypeError): pass return None def _parse_rationale(content: str) -> str | None: """Parse a rationale string from the judge's JSON response.""" - try: - data = json.loads(content) - return data.get("rationale") - except (json.JSONDecodeError, KeyError, ValueError, TypeError): + data = _extract_json(content) + if data is None: return None + return data.get("rationale") def _normalize_score(score: int | None) -> float | None: diff --git a/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py b/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py index 802a06c3..9afc7345 100644 --- a/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py +++ b/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py @@ -16,6 +16,7 @@ CustomMetricDefinition, LLMJudgeConfig, MlflowLLMJudgeEvaluator, + _extract_json, _normalize_score, _parse_rationale, _parse_score, @@ -84,12 +85,41 @@ def test_basic_definition(self): assert "medical" in metric.guidelines +class TestExtractJson: + def test_plain_json(self): + assert _extract_json('{"score": 3, "rationale": "ok"}') == {"score": 3, "rationale": "ok"} + + def test_markdown_fenced_json(self): + content = '```json\n{"score": 4, "rationale": "good"}\n```' + assert _extract_json(content) == {"score": 4, "rationale": "good"} + + def test_markdown_fenced_no_lang(self): + content = '```\n{"score": 2, "rationale": "poor"}\n```' + assert _extract_json(content) == {"score": 2, "rationale": "poor"} + + def test_json_with_preamble(self): + content = 'Here is my evaluation:\n{"score": 5, "rationale": "excellent"}' + assert _extract_json(content) == {"score": 5, "rationale": "excellent"} + + def test_no_json(self): + assert _extract_json("no json here") is None + + def test_empty_string(self): + assert _extract_json("") is None + + class TestParseScore: def test_valid_scores(self): assert _parse_score('{"score": 1, "rationale": "bad"}') == 1 assert _parse_score('{"score": 3, "rationale": "ok"}') == 3 assert _parse_score('{"score": 5, "rationale": "great"}') == 5 + def test_markdown_fenced_scores(self): + assert _parse_score('```json\n{"score": 4, "rationale": "good"}\n```') == 4 + + def test_score_with_preamble(self): + assert _parse_score('Here is my evaluation:\n{"score": 3, "rationale": "ok"}') == 3 + def test_invalid_scores(self): assert _parse_score("not json") is None assert _parse_score('{"score": 0}') is None @@ -101,6 +131,9 @@ class TestParseRationale: def test_valid(self): assert _parse_rationale('{"score": 4, "rationale": "Good"}') == "Good" + def test_markdown_fenced(self): + assert _parse_rationale('```json\n{"score": 4, "rationale": "Good"}\n```') == "Good" + def test_invalid(self): assert _parse_rationale("not json") is None From 7f30dac5612c068b136e6e016d691fdd27d86f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CPiotr?= Date: Mon, 6 Jul 2026 17:42:32 +0200 Subject: [PATCH 7/8] feat: wire llm_judge_model into run_rag_optimization component When llm_judge_model is provided, run_rag_optimization constructs a MlflowLLMJudgeEvaluator and passes it to AI4RAGExperiment, unlocking the answer_relevance metric. When None (default), the existing UnitxtEvaluator path is unchanged. Co-Authored-By: Claude Opus 4.7 --- ai4rag/components/optimization/__init__.py | 7 +- .../rag_templates_optimization.py | 38 +- .../evaluator/mlflow_llm_judge_evaluator.py | 13 +- .../optimization/test_rag_optimization.py | 72 ++ uv.lock | 914 ++++++++++++++++-- 5 files changed, 947 insertions(+), 97 deletions(-) diff --git a/ai4rag/components/optimization/__init__.py b/ai4rag/components/optimization/__init__.py index fe351579..7a6b0b7b 100644 --- a/ai4rag/components/optimization/__init__.py +++ b/ai4rag/components/optimization/__init__.py @@ -2,10 +2,15 @@ # Copyright IBM Corp. 2026 # SPDX-License-Identifier: Apache-2.0 # ----------------------------------------------------------------------------- -from ai4rag.components.optimization.rag_templates_optimization import OptimizationResult, run_rag_optimization +from ai4rag.components.optimization.rag_templates_optimization import ( + LLM_JUDGE_METRICS, + OptimizationResult, + run_rag_optimization, +) from ai4rag.components.optimization.search_space_preparation import SearchSpaceReport, prepare_search_space_report __all__ = [ + "LLM_JUDGE_METRICS", "OptimizationResult", "prepare_search_space_report", "run_rag_optimization", diff --git a/ai4rag/components/optimization/rag_templates_optimization.py b/ai4rag/components/optimization/rag_templates_optimization.py index 501cafea..41b6304f 100644 --- a/ai4rag/components/optimization/rag_templates_optimization.py +++ b/ai4rag/components/optimization/rag_templates_optimization.py @@ -32,6 +32,7 @@ MIN_MAX_RAG_PATTERNS_RANGE = (4, 20) DEFAULT_METRIC = "faithfulness" SUPPORTED_OPTIMIZATION_METRICS = frozenset({"faithfulness", "answer_correctness", "context_correctness"}) +LLM_JUDGE_METRICS = frozenset(SUPPORTED_OPTIMIZATION_METRICS | {"answer_relevance"}) @dataclass @@ -61,6 +62,7 @@ def run_rag_optimization( # pylint: disable=too-many-locals,too-many-arguments, input_data_key: str = "", optimization_settings: dict | None = None, max_threads: int = 10, + llm_judge_model: str | None = None, ) -> OptimizationResult: """Run a full AI4RAG optimization experiment and generate output artefacts. @@ -96,6 +98,12 @@ def run_rag_optimization( # pylint: disable=too-many-locals,too-many-arguments, RAG service during benchmark evaluation. Lower values reduce per-request concurrency (useful when each request carries more retrieved context). Defaults to ``10``. + llm_judge_model + Model identifier served by the OGX inference endpoint to use as + an LLM judge (e.g. ``"llama-31-8b-instruct"``). When provided, + evaluation uses :class:`MlflowLLMJudgeEvaluator` and unlocks the + ``answer_relevance`` metric. When ``None`` (default), the + deterministic :class:`UnitxtEvaluator` is used. Returns ------- @@ -122,12 +130,18 @@ def run_rag_optimization( # pylint: disable=too-many-locals,too-many-arguments, settings = _validate_optimization_settings(optimization_settings) optimization_metric = settings.get("metric") or DEFAULT_METRIC - if optimization_metric not in SUPPORTED_OPTIMIZATION_METRICS: + + allowed_metrics = LLM_JUDGE_METRICS if llm_judge_model else SUPPORTED_OPTIMIZATION_METRICS + if optimization_metric not in allowed_metrics: raise ValueError( f"Optimization metric {optimization_metric} is not supported. " - f"Select one of {SUPPORTED_OPTIMIZATION_METRICS}." + f"Select one of {sorted(allowed_metrics)}." ) + evaluator_kwargs: dict[str, Any] = {} + if llm_judge_model: + evaluator_kwargs.update(_build_llm_judge_kwargs(llm_judge_model, ogx_client)) + documents = load_docling_documents(extracted_text_path) with open(search_space_report_path, "r", encoding="utf-8") as f: @@ -162,6 +176,7 @@ def run_rag_optimization( # pylint: disable=too-many-locals,too-many-arguments, optimization_metric=optimization_metric, ogx_vector_io_provider_id=vector_io_provider_id, max_threads=max_threads, + **evaluator_kwargs, ) # --- Run the optimization loop --- @@ -217,6 +232,25 @@ def run_rag_optimization( # pylint: disable=too-many-locals,too-many-arguments, ) +def _build_llm_judge_kwargs(llm_judge_model: str, ogx_client: OgxClient) -> dict[str, Any]: + """Build evaluator and metrics kwargs for AI4RAGExperiment when using LLM judge.""" + from ai4rag.evaluator.mlflow_llm_judge_evaluator import LLMJudgeConfig, MlflowLLMJudgeEvaluator + + base_url = ogx_client.base_url.rstrip("/") + "/v1" + api_key = getattr(ogx_client, "api_key", "") or "" + + config = LLMJudgeConfig( + base_url=base_url, + api_key=api_key, + model=llm_judge_model, + ) + evaluator = MlflowLLMJudgeEvaluator(config) + return { + "evaluator": evaluator, + "metrics": tuple(sorted(LLM_JUDGE_METRICS)), + } + + def _deserialize_model(data: dict[str, Any], ogx_client: OgxClient) -> OGXEmbeddingModel | OGXFoundationModel: """Reconstruct a model instance from its serialized dictionary. diff --git a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py index 0a39225e..2bf568af 100644 --- a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py +++ b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py @@ -182,10 +182,17 @@ def evaluate_metrics( if not scorers: return {"scores": {}, "question_scores": {}} - mlflow_result = mlflow.genai.evaluate(data=eval_data, scorers=scorers) - question_ids = [ed.question_id or str(i) for i, ed in enumerate(evaluation_data)] - return self._format_results(mlflow_result, metrics, question_ids) + all_scores: dict = {} + all_question_scores: dict = {} + + for scorer in scorers: + mlflow_result = mlflow.genai.evaluate(data=eval_data, scorers=[scorer]) + partial = self._format_results(mlflow_result, metrics, question_ids) + all_scores.update(partial.get("scores", {})) + all_question_scores.update(partial.get("question_scores", {})) + + return {"scores": all_scores, "question_scores": all_question_scores} @staticmethod def _build_eval_data(evaluation_data: list[EvaluationData]) -> list[dict]: diff --git a/tests/unit/ai4rag/components/optimization/test_rag_optimization.py b/tests/unit/ai4rag/components/optimization/test_rag_optimization.py index a8226482..8f836fd5 100644 --- a/tests/unit/ai4rag/components/optimization/test_rag_optimization.py +++ b/tests/unit/ai4rag/components/optimization/test_rag_optimization.py @@ -10,6 +10,7 @@ from ai4rag.components.optimization.rag_templates_optimization import ( DEFAULT_MAX_RAG_PATTERNS, + LLM_JUDGE_METRICS, MIN_MAX_RAG_PATTERNS_RANGE, SUPPORTED_OPTIMIZATION_METRICS, _validate_optimization_settings, @@ -261,3 +262,74 @@ def test_max_threads_is_accepted(self, mock_ogx_client): test_data_key="bench.json", max_threads=4, ) + + +# --------------------------------------------------------------------------- +# run_rag_optimization -- llm_judge_model parameter +# --------------------------------------------------------------------------- + + +class TestRunRagOptimizationLLMJudge: + """Tests for the llm_judge_model parameter on run_rag_optimization.""" + + def test_llm_judge_model_defaults_to_none(self): + """llm_judge_model must default to None.""" + import inspect + + sig = inspect.signature(run_rag_optimization) + param = sig.parameters["llm_judge_model"] + assert param.default is None + + def test_answer_relevance_rejected_without_llm_judge(self, mock_ogx_client): + """answer_relevance requires llm_judge_model; without it must raise ValueError.""" + with pytest.raises(ValueError, match="is not supported"): + run_rag_optimization( + extracted_text_path="dummy", + test_data_path="dummy.json", + search_space_report_path="dummy.json", + output_dir="out", + ogx_client=mock_ogx_client, + vector_io_provider_id="provider-1", + test_data_key="bench.json", + optimization_settings={"metric": "answer_relevance"}, + ) + + def test_answer_relevance_accepted_with_llm_judge(self, mock_ogx_client): + """answer_relevance must pass validation when llm_judge_model is set. + + The call will fail later (no real endpoint), but must not raise + ValueError about an unsupported metric. + """ + with pytest.raises(Exception, match="(?!is not supported)"): + run_rag_optimization( + extracted_text_path="dummy", + test_data_path="dummy.json", + search_space_report_path="dummy.json", + output_dir="out", + ogx_client=mock_ogx_client, + vector_io_provider_id="provider-1", + test_data_key="bench.json", + optimization_settings={"metric": "answer_relevance"}, + llm_judge_model="llama-31-8b-instruct", + ) + + def test_llm_judge_metrics_superset_of_supported(self): + """LLM_JUDGE_METRICS must be a strict superset of SUPPORTED_OPTIMIZATION_METRICS.""" + assert SUPPORTED_OPTIMIZATION_METRICS < LLM_JUDGE_METRICS + assert "answer_relevance" in LLM_JUDGE_METRICS + assert "answer_relevance" not in SUPPORTED_OPTIMIZATION_METRICS + + def test_llm_judge_model_is_accepted(self, mock_ogx_client): + """Passing llm_judge_model alongside an invalid input must still raise + the expected validation error (not a TypeError from an unknown param).""" + with pytest.raises(ValueError, match="non-empty string"): + run_rag_optimization( + extracted_text_path="dummy", + test_data_path="dummy.json", + search_space_report_path="dummy.json", + output_dir="out", + ogx_client=mock_ogx_client, + vector_io_provider_id="", + test_data_key="bench.json", + llm_judge_model="some-model", + ) diff --git a/uv.lock b/uv.lock index 8a27ef1c..0658439e 100644 --- a/uv.lock +++ b/uv.lock @@ -2,10 +2,8 @@ version = 1 revision = 3 requires-python = ">=3.12, <3.14" resolution-markers = [ - "python_full_version >= '3.13' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and sys_platform != 'darwin'", - "python_full_version < '3.13' and sys_platform == 'darwin'", - "python_full_version < '3.13' and sys_platform != 'darwin'", + "python_full_version >= '3.13'", + "python_full_version < '3.13'", ] [[package]] @@ -30,13 +28,10 @@ wheels = [ name = "ai4rag" source = { editable = "." } dependencies = [ - { name = "boto3" }, - { name = "docling" }, - { name = "docling-core", extra = ["chunking"] }, + { name = "docling-core", extra = ["chunking", "chunking-openai"] }, { name = "langchain" }, { name = "langchain-chroma" }, { name = "langchain-text-splitters" }, - { name = "multiprocess" }, { name = "ogx-client" }, { name = "pandas" }, { name = "pydantic" }, @@ -54,15 +49,19 @@ code-check = [ dev = [ { name = "beautifulsoup4" }, { name = "black" }, + { name = "docling" }, { name = "dotenv" }, { name = "ipykernel" }, { name = "isort" }, + { name = "mike" }, { name = "mkdocs" }, { name = "mkdocs-git-revision-date-localized-plugin" }, { name = "mkdocs-material" }, { name = "mkdocs-minify-plugin" }, { name = "mkdocstrings", extra = ["python"] }, + { name = "mlflow" }, { name = "nbformat" }, + { name = "openai" }, { name = "psutil" }, { name = "pylint" }, { name = "pytest" }, @@ -70,12 +69,17 @@ dev = [ { name = "pytest-mock" }, ] docs = [ + { name = "mike" }, { name = "mkdocs" }, { name = "mkdocs-git-revision-date-localized-plugin" }, { name = "mkdocs-material" }, { name = "mkdocs-minify-plugin" }, { name = "mkdocstrings", extra = ["python"] }, ] +llm-judge = [ + { name = "mlflow" }, + { name = "openai" }, +] test = [ { name = "nbformat" }, { name = "psutil" }, @@ -88,26 +92,28 @@ test = [ requires-dist = [ { name = "ai4rag", extras = ["code-check"], marker = "extra == 'dev'" }, { name = "ai4rag", extras = ["docs"], marker = "extra == 'dev'" }, + { name = "ai4rag", extras = ["llm-judge"], marker = "extra == 'dev'" }, { name = "ai4rag", extras = ["test"], marker = "extra == 'dev'" }, { name = "beautifulsoup4", marker = "extra == 'dev'" }, { name = "black", marker = "extra == 'code-check'" }, - { name = "boto3", specifier = ">=1.28" }, - { name = "docling", specifier = "~=2.107.0" }, - { name = "docling-core", extras = ["chunking"], specifier = "~=2.84.0" }, + { name = "docling", marker = "extra == 'dev'" }, + { name = "docling-core", extras = ["chunking", "chunking-openai"], specifier = "~=2.74.1" }, { name = "dotenv", marker = "extra == 'dev'" }, { name = "ipykernel", marker = "extra == 'dev'" }, { name = "isort", marker = "extra == 'code-check'" }, { name = "langchain", specifier = "~=1.1.3" }, { name = "langchain-chroma", specifier = "~=1.1.0" }, { name = "langchain-text-splitters", specifier = "~=1.1.0" }, + { name = "mike", marker = "extra == 'docs'", specifier = ">=2.0.0" }, { name = "mkdocs", marker = "extra == 'docs'", specifier = "~=1.6.0" }, { name = "mkdocs-git-revision-date-localized-plugin", marker = "extra == 'docs'", specifier = ">=1.2.0" }, { name = "mkdocs-material", marker = "extra == 'docs'", specifier = "~=9.5" }, { name = "mkdocs-minify-plugin", marker = "extra == 'docs'", specifier = ">=0.8.0" }, { name = "mkdocstrings", extras = ["python"], marker = "extra == 'docs'", specifier = ">=0.25.0" }, - { name = "multiprocess", specifier = ">=0.70" }, + { name = "mlflow", marker = "extra == 'llm-judge'", specifier = ">=3.0.0" }, { name = "nbformat", marker = "extra == 'test'" }, - { name = "ogx-client", specifier = "~=1.1.0" }, + { name = "ogx-client", specifier = "~=1.0.0" }, + { name = "openai", marker = "extra == 'llm-judge'", specifier = ">=1.0.0" }, { name = "pandas", specifier = "==2.2.*" }, { name = "psutil", marker = "extra == 'test'" }, { name = "pydantic", specifier = "==2.11.*" }, @@ -119,7 +125,7 @@ requires-dist = [ { name = "scikit-learn", specifier = "==1.8.*" }, { name = "unitxt", specifier = "~=1.26.1" }, ] -provides-extras = ["dev", "test", "code-check", "docs"] +provides-extras = ["llm-judge", "dev", "test", "code-check", "docs"] [[package]] name = "aiohappyeyeballs" @@ -194,6 +200,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] +[[package]] +name = "alembic" +version = "1.18.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mako" }, + { name = "sqlalchemy" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/94/13/8b084e0f2efb0275a1d534838844926f798bd766566b1375174e2448cd31/alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc", size = 2056725, upload-time = "2026-02-10T16:00:47.195Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/29/6533c317b74f707ea28f8d633734dbda2119bbadfc61b2f3640ba835d0f7/alembic-1.18.4-py3-none-any.whl", hash = "sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", size = 263893, upload-time = "2026-02-10T16:00:49.997Z" }, +] + [[package]] name = "annotated-doc" version = "0.0.4" @@ -382,31 +402,12 @@ wheels = [ ] [[package]] -name = "boto3" -version = "1.43.29" +name = "blinker" +version = "1.9.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "botocore" }, - { name = "jmespath" }, - { name = "s3transfer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/85/a6/9c02ff00d08ea87908934351d244e35bb6fb5cbc169e1a14fc5bd80d124b/boto3-1.43.29.tar.gz", hash = "sha256:354006c512cdb87ef8214a095f2ade961c8145734475cd7a7e6b39260ff5494a", size = 113198, upload-time = "2026-06-12T19:32:23.442Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/5c/f12a9978526c7068c873ccf9788161fc6af338c6a025f1354a46134a6e46/boto3-1.43.29-py3-none-any.whl", hash = "sha256:77c27ada27cdbf619a3bbc41fa9e991caef818d3a2988cf92ea722e107d90108", size = 140537, upload-time = "2026-06-12T19:32:21.682Z" }, -] - -[[package]] -name = "botocore" -version = "1.43.29" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jmespath" }, - { name = "python-dateutil" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6f/54/df99c5ca5c9ef275e34b87e177782e3ca054fc35f1f462c40fe180936c81/botocore-1.43.29.tar.gz", hash = "sha256:dce39d33b707aa162aa3820975f99d7f8f746d46576169fb42ce4f2b3b56b261", size = 15512384, upload-time = "2026-06-12T19:32:13.754Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/91/b1/aa410c22355f8f6c4ac2433db6a1c557dd959acf2953ccae4bfc37488119/botocore-1.43.29-py3-none-any.whl", hash = "sha256:5d62f2a03ed279a50207ca2824e009313df15f082b6bb591a095a4f04c7faef3", size = 15194135, upload-time = "2026-06-12T19:32:09.463Z" }, + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, ] [[package]] @@ -414,7 +415,7 @@ name = "build" version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "os_name == 'nt' and sys_platform != 'darwin'" }, + { name = "colorama", marker = "os_name == 'nt'" }, { name = "packaging" }, { name = "pyproject-hooks" }, ] @@ -423,6 +424,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl", hash = "sha256:13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f", size = 26018, upload-time = "2026-04-30T03:18:23.644Z" }, ] +[[package]] +name = "cachetools" +version = "7.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f4/8b/0d3945a13955303b81272f759a0331e54c5c793da455e6f5706b89d2639c/cachetools-7.1.4.tar.gz", hash = "sha256:437f55a4e0c1b01a4f3077cc470e6991d47430970e36fbcb77e2be0df4fc1cd6", size = 40085, upload-time = "2026-05-21T22:40:43.376Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/7b/1fc1c09cc0756cf25861a3be10565915953876da48bb228fb9a672b20a42/cachetools-7.1.4-py3-none-any.whl", hash = "sha256:323dc4127934744db5b54eb4924482d7edafbf9554e820d1531c2e08c0e4ef54", size = 16761, upload-time = "2026-05-21T22:40:41.845Z" }, +] + [[package]] name = "certifi" version = "2026.4.22" @@ -562,6 +572,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, ] +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -592,6 +611,50 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, ] +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, +] + [[package]] name = "coverage" version = "7.13.5" @@ -646,6 +709,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" }, ] +[[package]] +name = "cryptography" +version = "48.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/12/45/870e7f4bef50e5f53b9f51d4428aee5290eedf58ba443f16b1ebb7ab8e66/cryptography-48.0.1.tar.gz", hash = "sha256:266f4ee051abb2f725b74ef8072b521ce1feacf685a3364fa6a6b45548db791a", size = 832989, upload-time = "2026-06-09T22:32:31.8Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/bc/ee4137cbbe105652c0ee4252792b78fc8e7afa4b8e61d9d5dc05a7f45731/cryptography-48.0.1-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3e4a1a3232eef2e6c732827d5722db29a0cc8b27af2a4d865b094cf954be9ca1", size = 8008324, upload-time = "2026-06-09T22:31:00.702Z" }, + { url = "https://files.pythonhosted.org/packages/d5/85/6379d42181bfc713094f081360fc5784d6c816b599d45e7f082502d173ce/cryptography-48.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:32143b24adb918f078134e1e230f1eb8cc04886b92c28b5f0041aaf3e5699225", size = 4696243, upload-time = "2026-06-09T22:32:33.446Z" }, + { url = "https://files.pythonhosted.org/packages/9c/87/c85d147b53323c7eb4d850920c8901377323c2a0ff8d79c262d4fee89aa2/cryptography-48.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0d27a5696721ef7a672b8c810f6aded391058e0b9486e63e6d93baf765da691", size = 4713235, upload-time = "2026-06-09T22:31:40.141Z" }, + { url = "https://files.pythonhosted.org/packages/79/58/67cbf8cf1ee7c54b439ca07bbecf8362c07afc11a3724fea70f745784add/cryptography-48.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb86ce1af36fe65041b6db9a8bb064ee621a7e5fded0f80d475ec243477cd242", size = 4702323, upload-time = "2026-06-09T22:31:42.191Z" }, + { url = "https://files.pythonhosted.org/packages/89/c6/24266ac10c47f6cd2a865f4446062b466da1d1f10b27189eac00e61bf0c9/cryptography-48.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b024e784ad6c077ee0147b35ea9cbfc1e34e1fd4c1dcca214c2794d73a12df08", size = 5300085, upload-time = "2026-06-09T22:31:58.703Z" }, + { url = "https://files.pythonhosted.org/packages/d2/bb/cc4b78784f97efc8c5874c2a9743708d172be6663024b34a0467885ae0c8/cryptography-48.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3752f2dbc8f07a30aad2932c986cea495b03bb554887828225da104f732852b6", size = 4746137, upload-time = "2026-06-09T22:31:31.01Z" }, + { url = "https://files.pythonhosted.org/packages/1f/52/0c44de3f5267f8fbe8e835138017522a333436166e406f0db9b9e6e3033f/cryptography-48.0.1-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:bd81490cd5801d755cf97bb68ac191f14b708470b1c7cf4580f669b9c9264cd8", size = 4333867, upload-time = "2026-06-09T22:32:28.096Z" }, + { url = "https://files.pythonhosted.org/packages/9a/2e/772d7adbfa931537bc401640b7cac9976bff689bda187833e5d63b428e49/cryptography-48.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:66fd0771e7b9c6dcd44cf1120690d2338d16d72795cf40cae2786a39eba65429", size = 4701805, upload-time = "2026-06-09T22:31:38.284Z" }, + { url = "https://files.pythonhosted.org/packages/f8/a3/b06844f303873493c963caf581c04df31c7035e0c1b0f02c4814d319ec80/cryptography-48.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:3fd2ca57062b241c856670b073487d2e86c4637937ca5601e48f97bf8e11fc8f", size = 5258461, upload-time = "2026-06-09T22:31:04.187Z" }, + { url = "https://files.pythonhosted.org/packages/9f/13/8b765e2e12b07c74941caadb9d1c8fdc006c4dfbf2b8f2d610519758954d/cryptography-48.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:0ee6ea481db1ab889cba043ec1eda17bb9c1ea79db6722f779c3667f9f70322f", size = 4745488, upload-time = "2026-06-09T22:32:30.07Z" }, + { url = "https://files.pythonhosted.org/packages/2e/aa/48972bce55049b32a94f4907eda4d75fa385aad8a39506cc2fc72196ecf0/cryptography-48.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f2ceef93cb096aa3c4cc4b5c94ca6131f9196d28c64d6111533402a9b2054d41", size = 4830256, upload-time = "2026-06-09T22:31:43.868Z" }, + { url = "https://files.pythonhosted.org/packages/47/a2/e5079a032fb85cf6005046ca92bbd78b0c82dad2b5751ab8c311659da06f/cryptography-48.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bd3f92d76217892b15df84ca256c2c113d386fdda7a7d8691aeeced976507c6", size = 4979117, upload-time = "2026-06-09T22:31:05.845Z" }, + { url = "https://files.pythonhosted.org/packages/b7/a0/8f50cae9c74e718ed769d63ed5c74bd0ea830c9550a74629cebd1b9c7bc7/cryptography-48.0.1-cp311-abi3-win32.whl", hash = "sha256:b9a32b876490d66c8bcc9963ef220199569748434ab01a9d6aaeabf88e7f5158", size = 3304154, upload-time = "2026-06-09T22:32:16.845Z" }, + { url = "https://files.pythonhosted.org/packages/c5/69/0572c77dbace6fef72f33755bd52ea399c71367250d366237f8691826b9e/cryptography-48.0.1-cp311-abi3-win_amd64.whl", hash = "sha256:39489bfca54c7a1f6b297efcd8bc608ab92d16c4ca631b0cad4da46724588b24", size = 3817138, upload-time = "2026-06-09T22:32:00.388Z" }, + { url = "https://files.pythonhosted.org/packages/ca/6c/00fa2a95997164c8b2072ce327c23d4ab20809ccc323ea5fab91e53a4bba/cryptography-48.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:4fdc69f8e4316bcf0c8c8ec1f26f285d12e8142d88d96c876a59a03be3f6ae67", size = 7987408, upload-time = "2026-06-09T22:32:20.777Z" }, + { url = "https://files.pythonhosted.org/packages/b0/d9/45f309a7e4e5f3f8f121d6d3be9e94024a7726ec598d6e08ae04edb2f04d/cryptography-48.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48fe40804d4caa2288f24e70ca8c64c42dd826da0ad7e4f1b41b2128d679e6c8", size = 4690196, upload-time = "2026-06-09T22:31:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9f/a1bc8bcc798811b8527eb374bbccf30a3f3e806829d967118222bf1125eb/cryptography-48.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:86be3b1b0b6bf09482fb50a979c508d2950ed95f5621ec77f4e385962006b83a", size = 4696782, upload-time = "2026-06-09T22:31:45.615Z" }, + { url = "https://files.pythonhosted.org/packages/66/c2/81a4fb4e4373c500bb526bc337ac5719dd31dd15b970b84a238168c6aa08/cryptography-48.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4ab0a343c807bbcd90c971cd1ecf072937cd01847a9e002bef88fb47ac6be577", size = 4696618, upload-time = "2026-06-09T22:31:11.564Z" }, + { url = "https://files.pythonhosted.org/packages/e5/0b/aa68b221dde92d09cb29a024ede17550ee21e77a404e59fc093c82bb51e1/cryptography-48.0.1-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9621de99d2da096006b629979efd8ae7eb2d8b822488d0c89ee4000c306c59b1", size = 5289970, upload-time = "2026-06-09T22:31:20.368Z" }, + { url = "https://files.pythonhosted.org/packages/78/13/fba657f958d2af66ea959a4ba01212632089249d34af1ae48054136344d7/cryptography-48.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:88c852a0ae366e262e5a1744b685e6a433dc8788dd2a277e418bf4904203609d", size = 4731873, upload-time = "2026-06-09T22:31:22.253Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4c/9a964756d24a26b3e34dfcb16f961b89838786e6700b635b0d1e3adff4b6/cryptography-48.0.1-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:43c5835e2cb98c8733d86f57d6fc879b613f5c3478607281c3e36daffc6dd8a6", size = 4330804, upload-time = "2026-06-09T22:31:36.56Z" }, + { url = "https://files.pythonhosted.org/packages/4b/0f/a10f3a6eb12950a10e3a874070283aa2dd5875b2bfd15fad8a3e17b3f13e/cryptography-48.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:fe0180af5bf9236518a087e35bf2d9a347d5f5f51e63c579d683ddff424e3d46", size = 4696217, upload-time = "2026-06-09T22:31:13.351Z" }, + { url = "https://files.pythonhosted.org/packages/f3/6f/5cd12f951165ea73ef85266775d97e4c763b2474ccfd816dd69d3a18d6f8/cryptography-48.0.1-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:b7a2d1a937a738a881737cec135a38bb61470589b17515b9f73f571d0ae10401", size = 5245252, upload-time = "2026-06-09T22:32:02.193Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/8aaa12e4516ec4464033ab79b6f3b592bd5a92102467c4ace8a0d970203f/cryptography-48.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b74ca3b8e5ecdd833bf6a002ca41b4793bb27fb8f1c06ffaf2643c9e9140e31b", size = 4731388, upload-time = "2026-06-09T22:32:04.019Z" }, + { url = "https://files.pythonhosted.org/packages/1b/24/50027ea4dca85ec1f40688f3c24fb32ccacd520583c9592c3cc95628e6fb/cryptography-48.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c37f2461406063b417837f5f3daab668652acd82423efcd7f0a9f04be972de1", size = 4824186, upload-time = "2026-06-09T22:32:18.707Z" }, + { url = "https://files.pythonhosted.org/packages/52/41/04cb5eb17085ade6f50cc611fb657df6a0f5885350de8764ece89c050197/cryptography-48.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:86fe77abb1bd87afb251d4d02ada7ecf53a32cee9b67d976abb2e45a13297475", size = 4964539, upload-time = "2026-06-09T22:31:18.793Z" }, + { url = "https://files.pythonhosted.org/packages/36/bf/ed70785c496e89d7e73b7cda2d21f2447fd6d4e821714b8d04ff217fed92/cryptography-48.0.1-cp39-abi3-win32.whl", hash = "sha256:6b2c0c3e6ccf3ade7750f836ef3ee36eea250cc467d45c256895573ac08cc6f1", size = 3282307, upload-time = "2026-06-09T22:30:53.162Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ff/371ea7d252656ee1eb6d83eeeef3d1d0c6baf1d6497687d081ea03814670/cryptography-48.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:9a49ca6c81417f6a5edb50375a60cccdd70fa0a91a5211829dbea74eba94d2ac", size = 3793408, upload-time = "2026-06-09T22:32:15.191Z" }, +] + [[package]] name = "csscompressor" version = "0.9.5" @@ -657,7 +759,7 @@ name = "cuda-bindings" version = "13.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-pathfinder", marker = "sys_platform != 'darwin'" }, + { name = "cuda-pathfinder" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, @@ -717,6 +819,30 @@ nvtx = [ { name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "databricks-sdk" +version = "0.117.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "protobuf" }, + { name = "requests" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/86/fe149c523a9181deb3a90ae4e83708f54a6ee80c40ca392cf66ffdf425df/databricks_sdk-0.117.0.tar.gz", hash = "sha256:30a6fc21a8f9c4a41830c43332ae63b38c3679a83ac1bda4734ce7ced114faf1", size = 989404, upload-time = "2026-06-11T18:19:29.034Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/4d/3e28a5a1ae9aa42237bd0f4e6b34867b554c046a0f3e7c2ef49fa74800f6/databricks_sdk-0.117.0-py3-none-any.whl", hash = "sha256:bafd588c067ee353c905e56cd2eb37c7eca7a56a4c1b57656621b77208c32f86", size = 936854, upload-time = "2026-06-11T18:19:27.395Z" }, +] + [[package]] name = "datasets" version = "4.8.5" @@ -804,21 +930,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] +[[package]] +name = "docker" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "requests" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" }, +] + [[package]] name = "docling" -version = "2.107.0" +version = "2.93.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docling-slim", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/b3/c0563269a680c4f3cf2b5814b8a2cb7e490d26343a25834124808896d1b3/docling-2.107.0.tar.gz", hash = "sha256:cabd688c00681361d13ccf00d802e4ef4e0514fcf6e9088709d58c10fbef08c1", size = 8946, upload-time = "2026-06-24T13:13:21.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/e7/41a7ef58935b914332a7924e0a9d817d50214201aeca7507944404229161/docling-2.93.0.tar.gz", hash = "sha256:c8b4455466d9a6314c27cf84debfdf491532298f65364ec3449acaad517bfea6", size = 8726, upload-time = "2026-05-07T11:55:38.745Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/4d/f4ce1777977290f112b561b28862e6205bf4b419d09f9506067c007fd21f/docling-2.107.0-py3-none-any.whl", hash = "sha256:526bb6f28650174e5a4b0bcd341876cf9a753719d98cd93f2512ca008965e382", size = 5111, upload-time = "2026-06-24T13:13:20.505Z" }, + { url = "https://files.pythonhosted.org/packages/47/6f/3befa6e4cd42f3a9cf284165d18ca1039ab676ab36da327242aa701e6b63/docling-2.93.0-py3-none-any.whl", hash = "sha256:30a2dc2db733d6a24095e624cc133ce67e9edc46e778f982cf719e137bcee200", size = 4827, upload-time = "2026-05-07T11:55:37.457Z" }, ] [[package]] name = "docling-core" -version = "2.84.0" +version = "2.74.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "defusedxml" }, @@ -834,9 +974,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/bc/41d9d5a38502538a7cd3c7fb66a06eb676f8cc1d6d275d7fbe971352efd7/docling_core-2.84.0.tar.gz", hash = "sha256:331035d5032be683a6d66d476146652491d2c75e4562e04da3f1a8d989d74bfc", size = 317993, upload-time = "2026-06-23T09:44:42.339Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/94/4856d01de4afd565d0ea12198f157f634cf65dc373a4ee97cb0d789f8554/docling_core-2.74.1.tar.gz", hash = "sha256:46bf298686f2c51ddd69b6935a27dff1cc80838f2f5f1a8823492d99cf1a357b", size = 319269, upload-time = "2026-04-22T14:33:45.241Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/d4/a8742fdd4c8b782608005ffde77a24bbfd54602c4c49825bf04ea25363ab/docling_core-2.84.0-py3-none-any.whl", hash = "sha256:2aee881b94138234c703298066b5ed94999490323ae6a29cbe5110f9248b83dd", size = 257936, upload-time = "2026-06-23T09:44:40.506Z" }, + { url = "https://files.pythonhosted.org/packages/d7/c3/5f8c9a9b0cad437cb7e6ffcd48ccc309b2290cea6aac067569bf61b84743/docling_core-2.74.1-py3-none-any.whl", hash = "sha256:e6464078012b3d45f4e0accd101fcb277063903f355eabbb9aee8de00527a789", size = 276973, upload-time = "2026-04-22T14:33:43.366Z" }, ] [package.optional-dependencies] @@ -849,6 +989,15 @@ chunking = [ { name = "tree-sitter-python" }, { name = "tree-sitter-typescript" }, ] +chunking-openai = [ + { name = "semchunk" }, + { name = "tiktoken" }, + { name = "tree-sitter" }, + { name = "tree-sitter-c" }, + { name = "tree-sitter-javascript" }, + { name = "tree-sitter-python" }, + { name = "tree-sitter-typescript" }, +] [[package]] name = "docling-ibm-models" @@ -876,29 +1025,30 @@ wheels = [ [[package]] name = "docling-parse" -version = "7.0.0" +version = "5.10.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docling-core" }, { name = "pillow" }, { name = "pydantic" }, { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "tabulate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7a/28/feffb968ddb332b1d2c2a5d2b9be47e36f9ab8121367ff9781f4ef0f43ae/docling_parse-7.0.0.tar.gz", hash = "sha256:bc7710a13d0e0619ee288499ac163637f9356425781f4bed83c3e6a061cb86d4", size = 6682303, upload-time = "2026-06-22T10:17:58.181Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/b8/e68f8ec44692d2f913210dd46cb3e7e6e1959053bb05d5c94c5331010f3c/docling_parse-5.10.1.tar.gz", hash = "sha256:10a3d2ba211134f6d1fa9b6be8ef690eb0b1a03b043473a3ef8408ad7b4a857a", size = 6651696, upload-time = "2026-04-24T15:02:19.106Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/8b/ef2c2b7837d01474851f17829404796232e78204aa737a1cce5d29cf6f13/docling_parse-7.0.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:37ef02b683e3a36557d45c9038f4e43cc5a293e544df0ab3ea5db159f5a32b21", size = 9158273, upload-time = "2026-06-22T10:17:33.184Z" }, - { url = "https://files.pythonhosted.org/packages/05/6d/83d0e80bcd87d0810c3ef37d617b24a1af854af84f297a06d126d563b02e/docling_parse-7.0.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a453afacbf659133aae4072b708828e3f9c745215d23aeac70620f65ab71e239", size = 9827022, upload-time = "2026-06-22T10:17:35.003Z" }, - { url = "https://files.pythonhosted.org/packages/8d/e8/2be20897f8569d02c93a26390e517c1faa336a566a84a867f5a97ae89c5f/docling_parse-7.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da3a005be3e8a58795ab2f75cf2277be933c677c880e9ee58c33506cd69176ea", size = 10209846, upload-time = "2026-06-22T10:17:36.838Z" }, - { url = "https://files.pythonhosted.org/packages/66/a8/2c3054477eb7e171bb5f44eb9792b66c9f27ace2fabde5dcbada56085bda/docling_parse-7.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca0411a058ce8ac823dd9b0e665917f3a53ac65b278390e052da868825513cef", size = 10973091, upload-time = "2026-06-22T10:17:39.312Z" }, - { url = "https://files.pythonhosted.org/packages/78/bc/b042eb64a18aabeab11ba16c46df64ac69590feabd267afecb1afc961794/docling_parse-7.0.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:b0982fff0d7a754d7a037bff1a3afd2f2b601dc5fb6f1d5ed7cfc9b24afe7e95", size = 9158220, upload-time = "2026-06-22T10:17:41.44Z" }, - { url = "https://files.pythonhosted.org/packages/e8/5f/387cc3a3c48d7591594f165e997f471fd8958b019faa240ef42a3a1ccc58/docling_parse-7.0.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b9e9807fa8d1705ba589d1b935b8aa506c6260dede8d3c02f31e92fad7930b4", size = 9826867, upload-time = "2026-06-22T10:17:43.242Z" }, - { url = "https://files.pythonhosted.org/packages/4a/67/3bfe88293ae70cba56abb1751649ce9cd6ba2d4c26e9228f1bb6abbe222a/docling_parse-7.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fe5d5fcdd098166d0bf4f0b3c449aca26bf736c37fcad02179d61853a31ce4e3", size = 10210039, upload-time = "2026-06-22T10:17:45.155Z" }, - { url = "https://files.pythonhosted.org/packages/c0/fb/88fcc144e438d4588b994af1b71a0a0bc2a6a986f6f9b597fe6e697aa672/docling_parse-7.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:c386c18bb4f7aba42bec227c4efeeb99468a1b5ca3a7886a2dd08dec75d0b5fa", size = 10973044, upload-time = "2026-06-22T10:17:47.557Z" }, + { url = "https://files.pythonhosted.org/packages/3e/4a/27e213493bac0877a030f030d44152ba9ef676aebc5890f4dd3e8037592e/docling_parse-5.10.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8f58e1bf1c6cdf1bfe0594f0903b4b9c33dfc3c2dbba61681f8533158ed640a6", size = 9112936, upload-time = "2026-04-24T15:01:44.878Z" }, + { url = "https://files.pythonhosted.org/packages/ee/b3/85737cecca0e5ed9dc13370e78862054075f64eb988024069296898ec741/docling_parse-5.10.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:59880a29231083c17a73533e09abc0610f10a99343762d795de4ceac5b15dfbf", size = 9780913, upload-time = "2026-04-24T15:01:47.626Z" }, + { url = "https://files.pythonhosted.org/packages/17/fa/11dd3328ab708143a291ae53d388a5170095a90b8dc8428e5274e2c09194/docling_parse-5.10.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:478ada90c52b704a04a3c8b4171e3385bb8b5b2f02b9d57c7a5bb06d9cac34fa", size = 10159004, upload-time = "2026-04-24T15:01:50.366Z" }, + { url = "https://files.pythonhosted.org/packages/e7/73/20384b93e220bbeee09b0bc3978907afda1a13599d7a9990614ad1c682ed/docling_parse-5.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:e8e4ae0929b55301c59252453ce87406c003229adac16258c4b6eb31a76d5cb5", size = 10912188, upload-time = "2026-04-24T15:01:52.968Z" }, + { url = "https://files.pythonhosted.org/packages/65/99/46e50685ff0d8b7ab7eb39a7425540771378b2dea04a094d1d6e85467e57/docling_parse-5.10.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:450a9dc433d511f647a178f4558624c4f274679e4ae847febe698b14842f536a", size = 9112935, upload-time = "2026-04-24T15:01:55.565Z" }, + { url = "https://files.pythonhosted.org/packages/5f/b1/a5a9132b3dc47f5d21be9b38f8f4e006b017af86fabaf0acac24bf5db122/docling_parse-5.10.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c1f9bdc5259dd78db70becbe7e53cc7f93ecfce53ed8886e7ad2fadcb7df17bc", size = 9781358, upload-time = "2026-04-24T15:01:57.992Z" }, + { url = "https://files.pythonhosted.org/packages/40/a5/53df8e581d4ab933c8f8bed4091716172c09fd2fd17a83f438a524659b91/docling_parse-5.10.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0934853bd3bea3a193dcef7e22eca4087a8ec1664f8ea9b5bceb6dddcbb3759", size = 10158871, upload-time = "2026-04-24T15:02:00.765Z" }, + { url = "https://files.pythonhosted.org/packages/62/9b/f465a56a838b19e950e3f7bff46b94ccfdfce7e478c03f76f674d1a989a4/docling_parse-5.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:2c24dc14f45efa16d1882cd1bb5bcc48e3acff1fd5de1505abf95ad7f49950a8", size = 10911943, upload-time = "2026-04-24T15:02:03.131Z" }, ] [[package]] name = "docling-slim" -version = "2.107.0" +version = "2.93.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -910,9 +1060,9 @@ dependencies = [ { name = "requests" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a5/54/639a7517fcd128d0a488c71e033c194f72e105bc77ca18c50f8ccd3861da/docling_slim-2.107.0.tar.gz", hash = "sha256:bd89476fae7558c68a2f5b61f9afead0afc6797fb7bd74a8a19e2b0e8ec49e07", size = 476596, upload-time = "2026-06-24T13:11:55.64Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c4/51/2ee874abcd62b990f0a86abec2e3b87b4cc00731b675ed446fefff6199d9/docling_slim-2.93.0.tar.gz", hash = "sha256:2962f4fc5bdf9dd6d67d6f36f09334f7187039985c0fd4b2d4b1d375e4799157", size = 390036, upload-time = "2026-05-07T11:54:14.562Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/ad/9d52dc9929f3128aee448815e3cbbf3a17601881def3dc8019e6fc1b1a6e/docling_slim-2.107.0-py3-none-any.whl", hash = "sha256:3aabe2dcd3bfec0c12162c9774d29ae17779ef6fc290d7f92a5791f93e94ea61", size = 604722, upload-time = "2026-06-24T13:11:53.611Z" }, + { url = "https://files.pythonhosted.org/packages/f3/3f/f2195f79a62fd6cd10c768c04c758c9d6bd0c804a46175d0f3f10a784fca/docling_slim-2.93.0-py3-none-any.whl", hash = "sha256:98e3db67f7976f051f132e6a6e04f73e7fcd4017877eddf9e849e0969c39fbe7", size = 506046, upload-time = "2026-05-07T11:54:10.884Z" }, ] [package.optional-dependencies] @@ -926,7 +1076,6 @@ standard = [ { name = "httpx" }, { name = "huggingface-hub" }, { name = "lxml" }, - { name = "mail-parser" }, { name = "marko" }, { name = "numpy" }, { name = "openpyxl" }, @@ -935,7 +1084,6 @@ standard = [ { name = "pylatexenc" }, { name = "pypdfium2" }, { name = "python-docx" }, - { name = "python-dotenv" }, { name = "python-pptx" }, { name = "rapidocr" }, { name = "rich" }, @@ -1019,6 +1167,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a7/a7/a600f8f30d4505e89166de51dd121bd540ab8e560e8cf0901de00a81de8c/faker-40.15.0-py3-none-any.whl", hash = "sha256:71ab3c3370da9d2205ab74ffb0fd51273063ad562b3a3bb69d0026a20923e318", size = 2004447, upload-time = "2026-04-17T20:05:25.437Z" }, ] +[[package]] +name = "fastapi" +version = "0.137.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-doc" }, + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/b1/e5b92c59d2c37817e77c1a8c2fc1f79cdcc04c68253e5406b43e3204cba7/fastapi-0.137.1.tar.gz", hash = "sha256:822360704230d9533d8d9475399613525968aa2f0b5bd2a3ccc9f18c88fd541c", size = 408293, upload-time = "2026-06-15T11:28:20.79Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/35/380b9a5922f4340e51c309cde09e5bd32e62f02302971bee30dc15aa0624/fastapi-0.137.1-py3-none-any.whl", hash = "sha256:64f6983c59e45c4b9fdc44e57cb8035c2451ee91ea8e8ec042aca37de7cf6b69", size = 121877, upload-time = "2026-06-15T11:28:19.523Z" }, +] + [[package]] name = "fastjsonschema" version = "2.21.2" @@ -1058,6 +1222,36 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945, upload-time = "2025-08-16T20:20:22.87Z" }, ] +[[package]] +name = "flask" +version = "3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blinker" }, + { name = "click" }, + { name = "itsdangerous" }, + { name = "jinja2" }, + { name = "markupsafe" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, +] + +[[package]] +name = "flask-cors" +version = "6.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flask" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/47/03/4e464a50860f9adf08b5c1d3479cb8ea1f12af2aa69535c7042c6e628135/flask_cors-6.0.5.tar.gz", hash = "sha256:30c5031552cd59f620ac0c8211dac45b345d3b2df310e7721879e4f46ef9c601", size = 101386, upload-time = "2026-06-08T20:20:17.765Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/55/5bb1a2d918e9f02f131e47a59032bae70e48050e986e941511fd737a935c/flask_cors-6.0.5-py3-none-any.whl", hash = "sha256:68fcf75693e961f3af26683b23c4b9a8fb6b64de17d20d0c37b95e8de7ab2ed8", size = 16692, upload-time = "2026-06-08T20:20:16.247Z" }, +] + [[package]] name = "flatbuffers" version = "25.12.19" @@ -1066,6 +1260,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, ] +[[package]] +name = "fonttools" +version = "4.63.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131, upload-time = "2026-05-14T12:03:13.386Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704, upload-time = "2026-05-14T12:03:15.801Z" }, + { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298, upload-time = "2026-05-14T12:03:18.085Z" }, + { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800, upload-time = "2026-05-14T12:03:20.161Z" }, + { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666, upload-time = "2026-05-14T12:03:22.87Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598, upload-time = "2026-05-14T12:03:25.168Z" }, + { url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575, upload-time = "2026-05-14T12:03:27.496Z" }, + { url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211, upload-time = "2026-05-14T12:03:30.057Z" }, + { url = "https://files.pythonhosted.org/packages/0f/8d/d8fec3dcde2963f8c908fb315e5ff2cd0ac34f82394bbbf73a2aa5145ce3/fonttools-4.63.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd", size = 2876062, upload-time = "2026-05-14T12:03:32.554Z" }, + { url = "https://files.pythonhosted.org/packages/ef/71/d935dc54e4ff121bfdd11e08702db63a7e6f25af21d8a3d7b7212df53641/fonttools-4.63.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59", size = 2424594, upload-time = "2026-05-14T12:03:34.86Z" }, + { url = "https://files.pythonhosted.org/packages/8e/40/e76320afa1df918e146155ef239b1719ee266092e96f5423bfd075affba1/fonttools-4.63.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d", size = 5024840, upload-time = "2026-05-14T12:03:36.745Z" }, + { url = "https://files.pythonhosted.org/packages/ce/36/0b805d8c485f872f65a509cbe3b58a5d0d17bee855333b54a150c79d3061/fonttools-4.63.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68", size = 4975801, upload-time = "2026-05-14T12:03:38.833Z" }, + { url = "https://files.pythonhosted.org/packages/c8/26/2cee03d0aa083ab022da5c07aff9ed3f689da1defb81ad6917c9627896da/fonttools-4.63.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be", size = 4965009, upload-time = "2026-05-14T12:03:41.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/48/cc4b66d9058c0d0982c833fad10127c4b0e9324606aafa41382295ca4102/fonttools-4.63.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27", size = 5105892, upload-time = "2026-05-14T12:03:43.525Z" }, + { url = "https://files.pythonhosted.org/packages/d8/1f/a98a30a814b9ddef3a2e706025f90b9e0bc94890e6cb15254bc86547d11a/fonttools-4.63.0-cp313-cp313-win32.whl", hash = "sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380", size = 2291313, upload-time = "2026-05-14T12:03:45.594Z" }, + { url = "https://files.pythonhosted.org/packages/92/46/5177b01f3b4abfdd4409f31cca4ab279c9343a26efbe9ec78c97fc612e02/fonttools-4.63.0-cp313-cp313-win_amd64.whl", hash = "sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b", size = 2342299, upload-time = "2026-05-14T12:03:47.414Z" }, + { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" }, +] + [[package]] name = "frozenlist" version = "1.8.0" @@ -1173,6 +1392,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl", hash = "sha256:d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9", size = 212507, upload-time = "2026-05-06T04:01:23.799Z" }, ] +[[package]] +name = "google-auth" +version = "2.55.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "pyasn1-modules" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/1c/70b23fc52b2bb3c70b379f3bd05c4a60ab3a873e30c6bd21c57e0154848a/google_auth-2.55.0.tar.gz", hash = "sha256:fcd3a130f575fa36403d38774af1c64a4fbfbca09215f0589d2372b5119697cb", size = 349379, upload-time = "2026-06-15T22:33:16.466Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/71/c0321dc6d63d99946da45f7c06299b934e4f7f7da5c4f14d101bcb39adf1/google_auth-2.55.0-py3-none-any.whl", hash = "sha256:a17cef9dedf98c4ebae2fb0c48c8f75952c877cbc2efe09f329ef16c2783d88a", size = 252400, upload-time = "2026-06-15T22:33:14.992Z" }, +] + [[package]] name = "googleapis-common-protos" version = "1.75.0" @@ -1185,6 +1417,66 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/c8/e2645aa8ed02fd4c7a2f59d68783b65b1f3cbdfe39a6308e156509d1fee8/googleapis_common_protos-1.75.0-py3-none-any.whl", hash = "sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed", size = 300631, upload-time = "2026-05-07T08:03:30.345Z" }, ] +[[package]] +name = "graphene" +version = "3.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "graphql-core" }, + { name = "graphql-relay" }, + { name = "python-dateutil" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cc/f6/bf62ff950c317ed03e77f3f6ddd7e34aaa98fe89d79ebd660c55343d8054/graphene-3.4.3.tar.gz", hash = "sha256:2a3786948ce75fe7e078443d37f609cbe5bb36ad8d6b828740ad3b95ed1a0aaa", size = 44739, upload-time = "2024-11-09T20:44:25.757Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/e0/61d8e98007182e6b2aca7cf65904721fb2e4bce0192272ab9cb6f69d8812/graphene-3.4.3-py2.py3-none-any.whl", hash = "sha256:820db6289754c181007a150db1f7fff544b94142b556d12e3ebc777a7bf36c71", size = 114894, upload-time = "2024-11-09T20:44:23.851Z" }, +] + +[[package]] +name = "graphql-core" +version = "3.2.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/90/f2aff026ab4aebd80eb71905106a0885f4cfde85dcf965543f45bed0d9ee/graphql_core-3.2.11.tar.gz", hash = "sha256:e7e156d10beb127cab5c89ff0da71416fc73d27c484a4757d3b2d35633774802", size = 528407, upload-time = "2026-06-05T13:45:22.915Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/15/b92b4e1d88d02c6eff9733c9eea21846ab435cc4d813d84ccc5d335955df/graphql_core-3.2.11-py3-none-any.whl", hash = "sha256:0b3e35ff41e9adba53021ab0cef475eb18f57c7f53f0f2ca55567fbf3c537ea0", size = 214879, upload-time = "2026-06-05T13:45:21.245Z" }, +] + +[[package]] +name = "graphql-relay" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "graphql-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d1/13/98fbf8d67552f102488ffc16c6f559ce71ea15f6294728d33928ab5ff14d/graphql-relay-3.2.0.tar.gz", hash = "sha256:1ff1c51298356e481a0be009ccdff249832ce53f30559c1338f22a0e0d17250c", size = 50027, upload-time = "2022-04-16T11:03:45.447Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/16/a4cf06adbc711bd364a73ce043b0b08d8fa5aae3df11b6ee4248bcdad2e0/graphql_relay-3.2.0-py3-none-any.whl", hash = "sha256:c9b22bd28b170ba1fe674c74384a8ff30a76c8e26f88ac3aa1584dd3179953e5", size = 16940, upload-time = "2022-04-16T11:03:43.895Z" }, +] + +[[package]] +name = "greenlet" +version = "3.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/6e/802acd792aebb2256fbbee8cacf2727faaeb6f240ac11008f09eae4414bc/greenlet-3.5.1.tar.gz", hash = "sha256:5a56aeb7d5d9cc4b3a735efb5095bd4b4f6f0e4f93e5ca876d0e2315137b7829", size = 197356, upload-time = "2026-05-20T15:05:03.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/37/4549f149c9797c21b32c2683c33522af22522099de128b2406672526d005/greenlet-3.5.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:fa4f98af3a528f0c3fd592a26df7f376f93329c8f4d987f6bb979057af8bf5e2", size = 286220, upload-time = "2026-05-20T13:07:28.463Z" }, + { url = "https://files.pythonhosted.org/packages/38/ff/a4f436709716965eaab9f36ea7b906c8a927fbe32fb1372a2071d964f6b1/greenlet-3.5.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffea73584b216150eab159b6d12348fb253e68757974de1e2c40d8a318ac89ed", size = 601585, upload-time = "2026-05-20T14:00:06.141Z" }, + { url = "https://files.pythonhosted.org/packages/65/ad/54bc3fcee3ad368a61b19b67d88117f7a8c29727bf71fffdeda81fbd946e/greenlet-3.5.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1072b4f9edcc1e192d9283a66a3e68d6b84c561de33a83d7858beb9ba1effe10", size = 614215, upload-time = "2026-05-20T14:05:42.675Z" }, + { url = "https://files.pythonhosted.org/packages/40/69/b91cda0647df839483201545913514c2827ebea5e5ccdf931842763bc127/greenlet-3.5.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:add5217d68b31130f0beca584d7fef4878327d2e31642b66618a14eef312b63b", size = 611358, upload-time = "2026-05-20T13:14:26.37Z" }, + { url = "https://files.pythonhosted.org/packages/59/90/3cf77e080350cd02fa307bb2abf05df48f4482c240275bbd2c203ba8bb1c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a5ea42a752d47a145eae922b605cd1634665ac3d5ec1e72402d5048e8d60d207", size = 1570475, upload-time = "2026-05-20T14:02:25.29Z" }, + { url = "https://files.pythonhosted.org/packages/65/2c/18cece62045e74598c3c393f70dce4a63f56222015ba29a5d4eeb04f764c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5551170cf4f5ff5623e9af81323751979fee2c731e2287b61f73cd27257b823", size = 1635625, upload-time = "2026-05-20T13:14:34.027Z" }, + { url = "https://files.pythonhosted.org/packages/30/f5/310d104ddf41eb5a70f4c268d22508dfb0c3c8e86fec152be34d0d2ed819/greenlet-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c8bb982ad117d29478ef8f5533e97df21f1e2befd17a299257b0c96d1371c0b", size = 238791, upload-time = "2026-05-20T13:10:39.018Z" }, + { url = "https://files.pythonhosted.org/packages/62/90/ceca11f504cd23a8047a3dea31919adc48df9b626dd0c13f0d858734fdfd/greenlet-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:80eb4b04dadc4e67df3fae179a32c4706a3f495bc7f22fc8a81115d5f5512188", size = 235580, upload-time = "2026-05-20T13:08:45.056Z" }, + { url = "https://files.pythonhosted.org/packages/27/69/7f7e5372d998b81001899b1c0823c957aa413ba0f2662e65821611cc31e4/greenlet-3.5.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:51518ff74664078fc51bffcc6fc529b0df5ae58da192691cee765d45ce944a2b", size = 285060, upload-time = "2026-05-20T13:08:51.899Z" }, + { url = "https://files.pythonhosted.org/packages/b1/bf/387f9b6b865fd2ae0d0be09e0004827295a01b71be76ed350dd1e28a91a4/greenlet-3.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ffdb3c0bb002c99cd8f298957e046c3dbf6006b5b7cdf11a4e19194624a0a0a", size = 604370, upload-time = "2026-05-20T14:00:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/32/f5/169ce3d4e4c67291bd18f8cbe0299c9f3e45102c7f1fb3c14780c93e4532/greenlet-3.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7715a5a2c3378ba602c3a440558261e13a820bb53a82693aacd7b7f6d964e283", size = 616987, upload-time = "2026-05-20T14:05:44.237Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e5/7f2e41d5273be07e77560d61ea4e56485b4d6c316d2a84518c62d1364061/greenlet-3.5.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc71ff466927a201b08305acac451ebe1aedfcea002f62f1f2f2ac2ac1e6a135", size = 613911, upload-time = "2026-05-20T13:14:27.539Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a4/fbdc67579b73615a1f91615e814303cc71e06128f7baaba87be79b8fb90c/greenlet-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cd443683db272ebaaca03af98c0b063ab30db70ea8a31a1559f35e3f7b744ccd", size = 1570689, upload-time = "2026-05-20T14:02:27.225Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b4/77abbe35078be39718a46cd49caf16bceb35662f97a34101dca28aa98e47/greenlet-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:089fff7a6ce8d9316d1f65ebc00273a56be258c1725b32b94de90a3a979557e1", size = 1635602, upload-time = "2026-05-20T13:14:36.344Z" }, + { url = "https://files.pythonhosted.org/packages/37/f7/129f27ca700845b8ee8ca88ce7f43435a1239c2eddb7677fc938822762cf/greenlet-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:110a1ca7b49b014b097f6078272c3f4ed31af45b254de5228b79adba879f6af9", size = 238683, upload-time = "2026-05-20T13:11:50.57Z" }, + { url = "https://files.pythonhosted.org/packages/6d/5c/a485a36e87df8d8fd0632ee01511244f5156a20ed3746cc6599340326395/greenlet-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f16ba1efc0715b680a18b8123d90dad887c6112ae3555b4b5c32c149540c6b4e", size = 235499, upload-time = "2026-05-20T13:12:42.028Z" }, +] + [[package]] name = "griffelib" version = "2.0.2" @@ -1225,6 +1517,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cf/e6/283326a27da9e2c3038bc93eeea36fb118ce0b2d03922a9cda6688f53c5b/grpcio-1.80.0-cp313-cp313-win_amd64.whl", hash = "sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50", size = 4882833, upload-time = "2026-03-30T08:48:07.363Z" }, ] +[[package]] +name = "gunicorn" +version = "26.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/b7/a4a3f632f823e432ce6bc65f62961b7980c898c77f075a2f7118cb3846fe/gunicorn-26.0.0.tar.gz", hash = "sha256:ca9346f85e3a4aeeb64d491045c16b9a35647abd37ea15efe53080eb8b090baf", size = 727286, upload-time = "2026-05-05T06:38:25.529Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/40/9c2384fc2be4ad25dd4a49decd5ad9ea5a3639814c11bd40ab77cb9f0a14/gunicorn-26.0.0-py3-none-any.whl", hash = "sha256:40233d26a5f0d1872916188c276e21641155111c2853f0c2cd55260aec0d24fc", size = 212009, upload-time = "2026-05-05T06:38:23.007Z" }, +] + [[package]] name = "h11" version = "0.16.0" @@ -1316,6 +1620,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[[package]] +name = "huey" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/cb/58f229149944602917a976d533d3fe7d54770d6ea18df5931e3f4f313fa0/huey-3.0.3.tar.gz", hash = "sha256:1a17fef95fc8432f75413f1b77439cef5f3493c1ddbfba9151756b31a1b2dad3", size = 263604, upload-time = "2026-06-12T01:53:55.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/82/f85d8918949786420716a5e421525ab12aa084bcbe32d86c9743e50bcf3d/huey-3.0.3-py3-none-any.whl", hash = "sha256:d1c687734778b8282c035a943eead8368c1736bb28abc006596fbbc01bdc96dc", size = 94945, upload-time = "2026-06-12T01:53:53.981Z" }, +] + [[package]] name = "huggingface-hub" version = "1.14.0" @@ -1442,6 +1755,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, ] +[[package]] +name = "itsdangerous" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, +] + [[package]] name = "jedi" version = "0.20.0" @@ -1467,12 +1789,48 @@ wheels = [ ] [[package]] -name = "jmespath" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +name = "jiter" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/b5/55f06bb281d92fb3cc86d14e1def2bd908bb77693183e7cb1f5a3c388b0c/jiter-0.15.0.tar.gz", hash = "sha256:4251acc80e2b7c9b7b8823456ea0fceeb0734dac2df7636d3c711b38476b5a76", size = 166640, upload-time = "2026-05-19T10:09:48.361Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/53/4f6bddbcde3c71e56d0aa1337ec95950f3d27dd4153e25aadf0feac71751/jiter-0.15.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0e90a1c315a0226ec822d973817967f9223b7701546c8c2a7913e7ab0926294d", size = 308793, upload-time = "2026-05-19T10:07:35.25Z" }, + { url = "https://files.pythonhosted.org/packages/01/84/c01099b59a285a1ebba64ae93f62bfa036675340fd1b0045ae65890a0442/jiter-0.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c9004af7c8d67cce7f1aae1026fb55607f4aa600710d08ede3a3ce4aeefe7e0", size = 309570, upload-time = "2026-05-19T10:07:36.919Z" }, + { url = "https://files.pythonhosted.org/packages/58/64/8fb7f9d45bb98190355454cd04dad8d8f27223d6bd52f83af07f637168a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c210f8b35dc6f30aafd4b4365ca89b9d1189f21ab49b8e68fa6322a847aef138", size = 336783, upload-time = "2026-05-19T10:07:38.694Z" }, + { url = "https://files.pythonhosted.org/packages/c3/b6/f5739011d009b3a30f6a53c5240979030ba29ae46a8c67e3a15759f7c37d/jiter-0.15.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f30bae8bc1c2d613e28e5af3e8cceb09b742f1c8a8a5f839fb67afaffc03b61", size = 363555, upload-time = "2026-05-19T10:07:40.832Z" }, + { url = "https://files.pythonhosted.org/packages/e5/12/98a9d9f766665e8a3b6252454e17cb0c464606a28cf2fa09399b003345fa/jiter-0.15.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e71b6d10cfc284c9bf36bd885e8d44c46f688ce50aa91b5edd90181dea687", size = 452255, upload-time = "2026-05-19T10:07:42.62Z" }, + { url = "https://files.pythonhosted.org/packages/e8/d5/60f972840f79c5e7544fce567c56f1e4e50468f996baba3e78d823dd62a6/jiter-0.15.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ab068bce62a45aa3e7367eceaffb5dde60b7eb853be8dece45132e3d0ff4879", size = 373559, upload-time = "2026-05-19T10:07:44.201Z" }, + { url = "https://files.pythonhosted.org/packages/ee/cf/d46ef1234ba335aabc2f013210db8e0821a22f5e644a2e9449df199ecc23/jiter-0.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa248c9eb220197d363f688818dac2fd4b2f0cd7d843ca7105d652034823427d", size = 346055, upload-time = "2026-05-19T10:07:46.005Z" }, + { url = "https://files.pythonhosted.org/packages/f0/63/4d2749d8d54d230bad9b3a6b0d00cc28c6ff6b2fdffc26a8ccf76cc5a974/jiter-0.15.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2a77aadd57cac1682e4401a72724d2796d89a4ba129b1a5812aa94ee480826eb", size = 351406, upload-time = "2026-05-19T10:07:47.855Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b9/9965b990035d8773328e0a8c8b457a87bf2b19f6c4126d9d99296be5d16a/jiter-0.15.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2ae901f3a55bfafdde31d289590fa25e3245735a2b1e8c7cc15871710a002871", size = 389357, upload-time = "2026-05-19T10:07:49.665Z" }, + { url = "https://files.pythonhosted.org/packages/2d/55/9ddf903deda1413e87fed792f416b7123daee5b8efbad6a202a7421c36a5/jiter-0.15.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f0b271b462769543716f92d3a4f90527df6ef5ed05ee95ec4137f513e21e1b77", size = 517263, upload-time = "2026-05-19T10:07:51.537Z" }, + { url = "https://files.pythonhosted.org/packages/e8/76/a0c40ad064d3a20a4fde231e35d56e9a01ce82164278180e82d5daf85469/jiter-0.15.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2fb6a5d26af81fc0f00f9360a891e05cf755e149bba391c4d563adc54812973d", size = 548646, upload-time = "2026-05-19T10:07:53.196Z" }, + { url = "https://files.pythonhosted.org/packages/23/4f/eca9b954942916ba2f453891b8593ab444cd872396fe66a3936616f236f3/jiter-0.15.0-cp312-cp312-win32.whl", hash = "sha256:c2f6bb8b5216ab9e7873bc08b5d7bef2b8abbb578a3069bf1cd14a45d71d771d", size = 206427, upload-time = "2026-05-19T10:07:55.307Z" }, + { url = "https://files.pythonhosted.org/packages/95/bf/8ead82a87495149542748e828d153fd232a512a22c83b02c4815c1a9c7d8/jiter-0.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:40b2c7e92c44a84d748d21706c68dc6ff8161d80b59c99d774721a0d2317d7c7", size = 197300, upload-time = "2026-05-19T10:07:56.651Z" }, + { url = "https://files.pythonhosted.org/packages/f4/e4/9b8a78fb2d894471bc344e37f1949bdd784bd914d031dba0ba3a40c71dd7/jiter-0.15.0-cp312-cp312-win_arm64.whl", hash = "sha256:cc0bc345cf2df9d1c00ac443f50d543c1ccfa8b0422cb85b1ab70d681c0b255b", size = 192702, upload-time = "2026-05-19T10:07:58.307Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f4/f708c900ecee41b2025ef8413d5351e5649eb2125c506f6720cc69b06f5c/jiter-0.15.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1c11465f97e2abf45a014b83b730222f8f1c5335e802c7055a67d50de6f1f4e3", size = 307829, upload-time = "2026-05-19T10:07:59.704Z" }, + { url = "https://files.pythonhosted.org/packages/86/59/db537c0949e83668c38481d426b9f2fd5ab758c4ee53a811dd0a510626a0/jiter-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e7b1776f0797956c509e123d0952d10d293a9492dea9f288ab9570ec01d1a5", size = 308445, upload-time = "2026-05-19T10:08:01.184Z" }, + { url = "https://files.pythonhosted.org/packages/37/38/ea0e13b18c30ef951da0d47d39e7fa9edb82a93a62990ffbd7cea9b622d4/jiter-0.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:351a341c2105aa430b7047e30f1bf7975f6313b00165d3fc07be2edaf741f279", size = 336181, upload-time = "2026-05-19T10:08:02.688Z" }, + { url = "https://files.pythonhosted.org/packages/58/fc/2303901b16c4ba05865588990a420c0b4156270b44379c20931544a1d962/jiter-0.15.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ab395feec8d249ec4044e228e98a7033f043426a265df439dc3698823f0a4e4", size = 362985, upload-time = "2026-05-19T10:08:04.394Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6f/11bace093c52e7d4d26c8e606ccd7ae8c972189622469ec0d9e28161e28b/jiter-0.15.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2a438005b6f22d0273413484d6094d7c2c5d10ec1b3a3bf128e0d1d3ba53258", size = 453292, upload-time = "2026-05-19T10:08:05.967Z" }, + { url = "https://files.pythonhosted.org/packages/22/db/987f2f086ca4d7a6582eb4ccd513f9b26b42d9e4243a087609a3137a8fc7/jiter-0.15.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f18f85e4218d1b40f000f42a92239a7a61a902cd42c65e6c360dbd17dcb20894", size = 373501, upload-time = "2026-05-19T10:08:07.857Z" }, + { url = "https://files.pythonhosted.org/packages/8f/7c/89fbcabb2739b7a5b8dc959a1b6c5761f6484f5fed3486854b3c789bb1de/jiter-0.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1aa62e277fc1cbd80e6deacae6f4d983b41b3d7728e0645c5d741a6149bba45", size = 344683, upload-time = "2026-05-19T10:08:09.431Z" }, + { url = "https://files.pythonhosted.org/packages/30/6f/6cca7692e7dddfec6d8d76c54dc97f2af2a41df4ac0674b999df1f09a5f3/jiter-0.15.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:6550fa135c7deb8ead6af49ed7ff648532ea8334a1447fe34a36315ef79c5c29", size = 350892, upload-time = "2026-05-19T10:08:11.352Z" }, + { url = "https://files.pythonhosted.org/packages/39/14/0338d6190cb8e6d22e677ab1d4eabd4117f67cca70c54cd04b82ff64e068/jiter-0.15.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:066f8f33f18b2419cd8213b2436fa7fbc9c499f315971cfa3ce1f9820c001b1b", size = 388723, upload-time = "2026-05-19T10:08:12.912Z" }, + { url = "https://files.pythonhosted.org/packages/90/31/cc19f4a1bdb6afb09ce6a2f2615aa8d44d994eba0d8e6105ed1af920e736/jiter-0.15.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:75e8a04e91432dde9f1838373cf93d23726c79d3e908d319acf0e796f85592e7", size = 516648, upload-time = "2026-05-19T10:08:14.808Z" }, + { url = "https://files.pythonhosted.org/packages/49/9f/833c541512cd091b63c10c0381973dfe11bc7a503a818c16384417e0c81e/jiter-0.15.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a97261f1fccb8e50ecd2890a96e46efdc3f57c80a197324c6777827231eca712", size = 547382, upload-time = "2026-05-19T10:08:16.927Z" }, + { url = "https://files.pythonhosted.org/packages/d2/11/e7b70e91f90bc4477e8eee9e8a5f7cf3cb41b4525d6394dc98a714eb8f7f/jiter-0.15.0-cp313-cp313-win32.whl", hash = "sha256:c77496cb10bd7549690fbbab3e5ec05857b83e49276f4a9423a766ddd2afcd4c", size = 205845, upload-time = "2026-05-19T10:08:18.401Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/5c20d9ad6f02c493e4023e5d2d09e1c1f15fe2753c9102c544aff068a88e/jiter-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b15741f501469009ae0ae90b7147958a664a7dede40aa7ff174a8a4645f546d0", size = 196842, upload-time = "2026-05-19T10:08:20.131Z" }, + { url = "https://files.pythonhosted.org/packages/6b/11/1eb400ef248e8c925fd883fbe325daf5e42cd1b0d308539dd332bd4f7ffc/jiter-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d6a60072b44c3c2b797a7ddcbcbbf2b34ea3cfd4721580fbfd2a09d9d9b84ba", size = 192212, upload-time = "2026-05-19T10:08:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/8a/60/2fd8d7c79da8acf9b7b277c7616847773779356b92acfc9bb158452174da/jiter-0.15.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ef1fd24d9413f6209e00d3d5a453e67acfe004a25cc6c8e8484faed4311ab9e8", size = 315065, upload-time = "2026-05-19T10:08:23.218Z" }, + { url = "https://files.pythonhosted.org/packages/46/f4/008fb7d65e8ac2abf00811651a661e025c4ba80bbc6f378450384ddd3aed/jiter-0.15.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:144f8e72cb53dab146347b91cceac01f5481237f2b93b4a339a1ee8f8878b67c", size = 339444, upload-time = "2026-05-19T10:08:24.701Z" }, + { url = "https://files.pythonhosted.org/packages/00/55/90b0c7b9c6896c0f2a591dd36d36b71d22e09674bfef178fa03ba3f81499/jiter-0.15.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553fcac2ef2cb990877f9fc0833b8b629a3e6a5670b6b5fd58219b41a653ddc4", size = 347779, upload-time = "2026-05-19T10:08:26.408Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/69666cec5000fd57734c118437394516c749ae8dbeea9fb66d6fef9c4775/jiter-0.15.0-cp313-cp313t-win_amd64.whl", hash = "sha256:774f93f65031856bf14ad9f59bdcab8b8cad501e5ceabd51ba3525f76937a25b", size = 200395, upload-time = "2026-05-19T10:08:28.055Z" }, + { url = "https://files.pythonhosted.org/packages/39/04/a6aa62cd27e8149b0d28df5561f10f6cceaf7935a9ccf3f1c5a05f9a0cd8/jiter-0.15.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f1e1754960f38ec40613a07e5e372df67acb3b890fb383b6fb3de3e49ddbf3c7", size = 190516, upload-time = "2026-05-19T10:08:29.35Z" }, + { url = "https://files.pythonhosted.org/packages/73/38/505941b2b092fd5bbbd60a52a880db1173f1690ae6751bed3af1c9ddcb4e/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:631f13a3d04e97d4e083993b10f4b99530e3a10d953e2eb5e196b7dc7f812ce0", size = 303769, upload-time = "2026-05-19T10:09:42.203Z" }, + { url = "https://files.pythonhosted.org/packages/e7/95/a06692b29e77473f286e1ec1f426d3ca44d7b5843be8ad21d7a5f3fcdcc0/jiter-0.15.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b6c0ffae686c39bf3737be60793783267628783ea42545632c10b291105aee45", size = 305128, upload-time = "2026-05-19T10:09:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/23/85/7270d7ad41d6061a25b950c6bf91d638bd9aacb113200a8c8d57a055fd67/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d54fb5b31dea401a41af3f8a7d2512e9b6a6a005491e6166c7e4ffab9639a9c", size = 340459, upload-time = "2026-05-19T10:09:45.452Z" }, + { url = "https://files.pythonhosted.org/packages/c8/8d/302cb2057b7513327b4d575cff6b1d066ee6431a5357fc3f8867cd684406/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d5d6090cdc1b7c9e780dfb04949a990adb1e301a2fc0bbcee7de4638d33f9a", size = 344469, upload-time = "2026-05-19T10:09:46.864Z" }, ] [[package]] @@ -1588,6 +1946,62 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, ] +[[package]] +name = "kiwisolver" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" }, + { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" }, + { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" }, + { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, + { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, + { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, + { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, + { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, + { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, + { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, + { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" }, + { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" }, + { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" }, + { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" }, + { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" }, + { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" }, + { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" }, + { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" }, + { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" }, + { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" }, + { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" }, + { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" }, + { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" }, + { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" }, + { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" }, + { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" }, + { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" }, + { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" }, + { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" }, + { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" }, + { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" }, + { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" }, + { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" }, + { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" }, + { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" }, + { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, + { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" }, +] + [[package]] name = "kubernetes" version = "35.0.0" @@ -1810,12 +2224,15 @@ wheels = [ ] [[package]] -name = "mail-parser" -version = "4.4.0" +name = "mako" +version = "1.3.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/6e/2cee08e0d6fbce02a301d5ab72ef5ffe8f47dd59ac3d8ce61324d8a9143a/mail_parser-4.4.0.tar.gz", hash = "sha256:82d2c0fa98a138620feaa87a378af2a2643b2941b2e125d7abfc65dd21094b33", size = 2852896, upload-time = "2026-06-11T06:56:49.789Z" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/62/791b31e69ae182791ec67f04850f2f062716bbd205483d63a215f3e062d3/mako-1.3.12.tar.gz", hash = "sha256:9f778e93289bd410bb35daadeb4fc66d95a746f0b75777b942088b7fd7af550a", size = 400219, upload-time = "2026-04-28T19:01:08.512Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/26/a7/99dfed167ba728384521acd54fadbb7649ce49333018f42e1f190ed25911/mail_parser-4.4.0-py3-none-any.whl", hash = "sha256:46f7931817660fec4f7723e6f1722e66a9cce44c2daeacbeb9e705078379561f", size = 35504, upload-time = "2026-06-11T06:56:48.348Z" }, + { url = "https://files.pythonhosted.org/packages/bc/b1/a0ec7a5a9db730a08daef1fdfb8090435b82465abbf758a596f0ea88727e/mako-1.3.12-py3-none-any.whl", hash = "sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9", size = 78521, upload-time = "2026-04-28T19:01:10.393Z" }, ] [[package]] @@ -1889,6 +2306,46 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, ] +[[package]] +name = "matplotlib" +version = "3.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "contourpy" }, + { name = "cycler" }, + { name = "fonttools" }, + { name = "kiwisolver" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pillow" }, + { name = "pyparsing" }, + { name = "python-dateutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/24/080c99d223d158d3a8902769269ab6da5b50f7a0e6e072513907e02b7a6c/matplotlib-3.11.0.tar.gz", hash = "sha256:68c0c7be01b30dcca3638934f7f591df73401235cbdbf0d1ab1c71e7db7f8b57", size = 33251176, upload-time = "2026-06-12T02:29:15.508Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/17/f5276b496c61477a6c4fc5e7401f4bfe1c2e5ef7c6cd67896f2ade3809cb/matplotlib-3.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b5872e9cf11adc8f589ded3ce11bc3e1061ad498259664fabc1f6615beb918", size = 9449976, upload-time = "2026-06-12T02:27:50.989Z" }, + { url = "https://files.pythonhosted.org/packages/82/34/bdd77418adb2178a1d59f044bd67bfebb115896e91b840b8a197eb3f4f4e/matplotlib-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0515d495124be3124340e59f164d901ed4484e2246a5b74cfa483cac3b80bd97", size = 9279307, upload-time = "2026-06-12T02:27:53.247Z" }, + { url = "https://files.pythonhosted.org/packages/94/95/7f522393c88313336b20d70fc849555757b2e5febc22b83b3a3f0fd4bce9/matplotlib-3.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be5f93a1d21981bfb802ded0d77a0caa92d4342a47d45754fac77e314a506344", size = 10031353, upload-time = "2026-06-12T02:27:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/87/ce/8f25a0e3186aefd61913e7467d1b999465bcd0d0c03ac695c1b26ca559b7/matplotlib-3.11.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41635d7909d19e52e924a521dde6d8f670b0f53ab1d0e8c331fa831554f681d1", size = 10839232, upload-time = "2026-06-12T02:27:57.746Z" }, + { url = "https://files.pythonhosted.org/packages/85/c2/db15da2bbdf9e3ca66df7db8e2c33a1dfed67be24a24d2c878efaaff01d6/matplotlib-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:94f5000f67ca9faa300863ea17f8bce9175cb67b88bec4bc7780502d53dd7c9e", size = 10923899, upload-time = "2026-06-12T02:28:00.223Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2f/a58a4443a4d052a4ea77557478336aefc26c7981f6408d37adba763aa758/matplotlib-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ac6f1ef39f3d0f9e2463303013094992cdbe0f85f43bc54155bc472b2042768e", size = 9329528, upload-time = "2026-06-12T02:28:02.27Z" }, + { url = "https://files.pythonhosted.org/packages/61/0f/4b669589d47733b97ab9df4b58d6fc1e68acb5ea42a928dc7cbdd6bf5871/matplotlib-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:9dd11fb612ce7bc60b1de5b4fc87ff959d22317b5de42aabf392f66f97af22eb", size = 9003413, upload-time = "2026-06-12T02:28:04.49Z" }, + { url = "https://files.pythonhosted.org/packages/55/41/aa47f156b061d14c98b906f76c428507397708ec63ff94f410ae1752b426/matplotlib-3.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce3b839b34ae1f430b4616893a2945a2999debaa7e94e7e29a2a8bbf286f7b5", size = 9450532, upload-time = "2026-06-12T02:28:06.769Z" }, + { url = "https://files.pythonhosted.org/packages/8c/4f/5a9eb0375e81413953febf8af7b012a6b6357f53438a15c4f5ad86c6bbb5/matplotlib-3.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:373db8f91214e8ccaf35ac833cc1dd59dd961e148bbd55dd027141591dde1313", size = 9279760, upload-time = "2026-06-12T02:28:09.152Z" }, + { url = "https://files.pythonhosted.org/packages/a4/c0/1117d53077e3ac3152503a84e9cf7a5c239576805ee71276e80c2aaa7471/matplotlib-3.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be152b7570324dc8d01574cc9474dd2d803237acf528bcbb5b211fa347461a09", size = 10031623, upload-time = "2026-06-12T02:28:11.26Z" }, + { url = "https://files.pythonhosted.org/packages/92/7e/e937138daffad65b71bf831a377809dcbc830fb4f31a31e067dc1faa2575/matplotlib-3.11.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:126f256df600652d7e4b394cf3164ff75210a00038f287c95a012a6f58d0e83f", size = 10839372, upload-time = "2026-06-12T02:28:14.102Z" }, + { url = "https://files.pythonhosted.org/packages/1d/c2/438ecc197ffb8023b6b9922915542f2172f5fd45b76703b0b4fc47322243/matplotlib-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:03acfeddf87b0dddb11b081ef7740ad445a3ca8bcb6b8e3011b08f2cf802b75c", size = 10924099, upload-time = "2026-06-12T02:28:16.383Z" }, + { url = "https://files.pythonhosted.org/packages/40/2e/395883da416f378b3ed2c9f3e843ac477eae1ce731b671b79adaa6f0bacd/matplotlib-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:ab3722f04f3ff34c23b5012c5873d2894174e06c3822fcdac3610965a5ac7d06", size = 9329727, upload-time = "2026-06-12T02:28:18.581Z" }, + { url = "https://files.pythonhosted.org/packages/61/82/2c388956abf8bf392dfb5b8917c502f1082df6a941b781ab8c8e5ba2474b/matplotlib-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c945824670fb8915b4ac879e5e61f3c58e0913022f70a0de4c082b17372f8771", size = 9003506, upload-time = "2026-06-12T02:28:20.474Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c1/34454baa44da7975ada82e9aea37105ec47059514dc967d3be14426ba8dc/matplotlib-3.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3489c3dc487669b4a980bc3068f87856de7a1564248d3f6c629efb2a58b03f24", size = 9499838, upload-time = "2026-06-12T02:28:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/b1/c3/98fe79a398cf232219f090163a7fa7e6766e9f2e0ad26df54d6f8934d8ee/matplotlib-3.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6a98f5476ce784a50ce09998f4ae1e6a9f25043cef8a480c98949902eda74620", size = 9332298, upload-time = "2026-06-12T02:28:24.796Z" }, + { url = "https://files.pythonhosted.org/packages/95/e4/b4b7c33151e74e5c802f3cde1ba807ebfc38401e329b44e215a5888dd76d/matplotlib-3.11.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:565af866fd63e4bd3f987d580afe27c44c2552a3b3305f4ecbb85133601ea6f3", size = 10045491, upload-time = "2026-06-12T02:28:27.141Z" }, + { url = "https://files.pythonhosted.org/packages/71/28/394548efd68354110c1a1be11fe6b6e559e06d1a23da35908a0e316c55a9/matplotlib-3.11.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6b3e64dea5062c570f04358e2711859f3531b459f29516274fbad889079e4f3", size = 10857059, upload-time = "2026-06-12T02:28:29.222Z" }, + { url = "https://files.pythonhosted.org/packages/c8/44/e7922e6e2a4d63bdfbc9dc4a53e3850ab438d46cf42e6779bb15ec92c948/matplotlib-3.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:942b37c5db1899610bd1543ce8e13e4ecff9a4633e7f63bb6aa9205d2644ebd1", size = 10939576, upload-time = "2026-06-12T02:28:31.66Z" }, + { url = "https://files.pythonhosted.org/packages/3d/be/b1ca96003a441d619b727fee21d671fdff7a5ce2f1bb797b2521aa2f679a/matplotlib-3.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c08e649a6313e1291e713623b97a38e5bb4aa580b2a100a94a3309bc6b9c8eb3", size = 9379519, upload-time = "2026-06-12T02:28:33.888Z" }, + { url = "https://files.pythonhosted.org/packages/e3/72/4bf3b91821c34596dd6a7bdac5836d94f744144c8208939ef49d8ec43f7e/matplotlib-3.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2746cd2c113742ff6ce37a864c5ac5fd7aa644568f445e66166e457ac78e40e0", size = 9055456, upload-time = "2026-06-12T02:28:35.878Z" }, +] + [[package]] name = "matplotlib-inline" version = "0.2.1" @@ -1928,6 +2385,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, ] +[[package]] +name = "mike" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "mkdocs" }, + { name = "pyparsing" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "verspec" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b4/47/fa87e9d56bef16cdfe34b059a437e8c6f7ec6f1b9c378871c3cf95ebea9c/mike-2.2.0.tar.gz", hash = "sha256:1e3858e32c0f125aac14432fc7848434358f9ae0962c5c5cde387ad47f6ad25e", size = 38450, upload-time = "2026-04-14T04:59:03.944Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/8e/56ccb09c7232a55403a7637caa21922f3b65901a37f5e8bdb405d0de0946/mike-2.2.0-py3-none-any.whl", hash = "sha256:e1f4981c1152eec7c2490a3401142292cc47d686194188416db2648fdfe1d040", size = 34026, upload-time = "2026-04-14T04:59:02.602Z" }, +] + [[package]] name = "mkdocs" version = "1.6.1" @@ -2077,6 +2551,87 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl", hash = "sha256:0b83513478bdfd803ff05aa43e9b1fca9dd22bcd9471f09ca6257f009bc5ee12", size = 104779, upload-time = "2026-02-20T10:38:34.517Z" }, ] +[[package]] +name = "mlflow" +version = "3.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohttp" }, + { name = "alembic" }, + { name = "cryptography" }, + { name = "docker" }, + { name = "flask" }, + { name = "flask-cors" }, + { name = "graphene" }, + { name = "gunicorn", marker = "sys_platform != 'win32'" }, + { name = "huey" }, + { name = "matplotlib" }, + { name = "mlflow-skinny" }, + { name = "mlflow-tracing" }, + { name = "numpy" }, + { name = "pandas" }, + { name = "pyarrow" }, + { name = "scikit-learn" }, + { name = "scipy" }, + { name = "skops" }, + { name = "sqlalchemy" }, + { name = "waitress", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/69/d71afc475fa7e7b22bb27392247d2a3015c9da202dea44f150a54be4bd67/mlflow-3.13.0.tar.gz", hash = "sha256:a95198d592a8a15fad3db7f56b228acc9422c09f0daa7c6c976a9996ab73c3e2", size = 10086808, upload-time = "2026-06-01T05:55:09.555Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/1f/d44140128356f2f5db37f9fb4d9da31123d839d49f3fe00a079cd35bfe20/mlflow-3.13.0-py3-none-any.whl", hash = "sha256:7ca9cb2f623f300dabadaf5e985c85af77c5db3d7c36f56769d22c101b132f6c", size = 10788121, upload-time = "2026-06-01T05:55:06.86Z" }, +] + +[[package]] +name = "mlflow-skinny" +version = "3.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "click" }, + { name = "cloudpickle" }, + { name = "databricks-sdk" }, + { name = "fastapi" }, + { name = "gitpython" }, + { name = "importlib-metadata" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "sqlparse" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "uvicorn" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/13/840db21a4f46ebe6ba9837a38bc93d748e23b6b61986799c8040cd4bf728/mlflow_skinny-3.13.0.tar.gz", hash = "sha256:d2273bfa21f776359f7d6ab2267967e3a6732a5fb00996ad433d0e777dfa3b71", size = 2814837, upload-time = "2026-06-01T05:54:54.175Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/fd/f2739de1b6a09da981927aa90db87340cbe4b3cf6cd175fd5e6e4366208e/mlflow_skinny-3.13.0-py3-none-any.whl", hash = "sha256:ced3d9a580564fae093d14732df8531fb180574f6483d4c642b6083879eb86fc", size = 3365675, upload-time = "2026-06-01T05:54:52.166Z" }, +] + +[[package]] +name = "mlflow-tracing" +version = "3.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "databricks-sdk" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-proto" }, + { name = "opentelemetry-sdk" }, + { name = "packaging" }, + { name = "protobuf" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/b0/5912313e895e6ce02f1f67110164d147593d1b5379a4767a30a5b9e730c5/mlflow_tracing-3.13.0.tar.gz", hash = "sha256:42c435b0fdcab00f1865cab4a52f7a85a2a08d68a959f36bcf90a1c9fe65db0a", size = 1393079, upload-time = "2026-06-01T05:54:44.906Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/fd/2c53ebc2f7fbb34ed4a3913a71ff53962e6be35fa02f7cee1f52c77388cd/mlflow_tracing-3.13.0-py3-none-any.whl", hash = "sha256:2f8187ce2b1af7419be71d2d8ab5fec53d207d4b8d703cd15e5db64939098d72", size = 1664279, upload-time = "2026-06-01T05:54:42.911Z" }, +] + [[package]] name = "mmh3" version = "5.2.1" @@ -2352,7 +2907,7 @@ name = "nvidia-cudnn-cu13" version = "9.19.0.56" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas", marker = "sys_platform != 'darwin'" }, + { name = "nvidia-cublas" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" }, @@ -2364,7 +2919,7 @@ name = "nvidia-cufft" version = "12.0.0.61" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin'" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, @@ -2394,9 +2949,9 @@ name = "nvidia-cusolver" version = "12.0.4.66" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas", marker = "sys_platform != 'darwin'" }, - { name = "nvidia-cusparse", marker = "sys_platform != 'darwin'" }, - { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin'" }, + { name = "nvidia-cublas" }, + { name = "nvidia-cusparse" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, @@ -2408,7 +2963,7 @@ name = "nvidia-cusparse" version = "12.6.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin'" }, + { name = "nvidia-nvjitlink" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, @@ -2471,7 +3026,7 @@ wheels = [ [[package]] name = "ogx-client" -version = "1.1.2" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -2490,9 +3045,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/15/5f02de6eba355443a5687abfc8474beec4ac0aae3357629dc889547f62cb/ogx_client-1.1.2.tar.gz", hash = "sha256:2bb2e7d9100e6332d45dc003777027f9a2f7e9ec82d77cfa6c08ccb670172657", size = 440847, upload-time = "2026-06-17T14:11:44.312Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/ed/bbcd77921d29dc0a84a4767cc3661d027bfc5db6a0c32a4f82dadc182542/ogx_client-1.0.0.tar.gz", hash = "sha256:a13133105149b77384ba804cc1fa340c1defce9ebb4820e3c593c36b103f0010", size = 435209, upload-time = "2026-05-12T13:58:58.883Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/4f/c17e5520c0fc282b4b1654bdd5432c7c5e393ce1c5a5b305b34e5ce3ea14/ogx_client-1.1.2-py3-none-any.whl", hash = "sha256:c8b0a0657231da8151ed7267261d3dd7713482ef7d19d97adc13bb42ca25a5da", size = 314013, upload-time = "2026-06-17T14:11:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/0b/95/2cafdc4e3ff4a89db82862c0e0ed10fed2154ba74c7b16c2a5e371bac5d9/ogx_client-1.0.0-py3-none-any.whl", hash = "sha256:d0ef468f3a46b5bf3af9de3540902aa0d4a0c9d4370444697ca710473843c439", size = 309279, upload-time = "2026-05-12T13:58:57.356Z" }, ] [[package]] @@ -2533,6 +3088,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3c/bb/410a760694f8ae7bbfc5fa81ccbeb7da241e6d520ee02a333a439cf462a2/onnxruntime-1.25.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd83ef5c10cfc051a1cb465db692d57b996a1bc75a2a97b161398e29cdbc47ff", size = 18021727, upload-time = "2026-04-27T22:00:13.846Z" }, ] +[[package]] +name = "openai" +version = "2.41.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/36/4c926a91554483977608951360c18c2e911592785eb87a6437813f6123f7/openai-2.41.1.tar.gz", hash = "sha256:23d617a0432457ad844973bee8f540be9da90894f7c5686852d2d365da058f57", size = 783584, upload-time = "2026-06-10T16:10:37.667Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/74/925d7b3892927e9804aaf58d374a45dc28e4420ff90e992272b77286343e/openai-2.41.1-py3-none-any.whl", hash = "sha256:a939565f350cb7443cb843b801b88c716ac8024b492fb94ca269d5f6b1bbefd6", size = 1353380, upload-time = "2026-06-10T16:10:35.756Z" }, +] + [[package]] name = "opencv-python" version = "4.13.0.92" @@ -2875,6 +3449,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/34/b6f19941adcdaf415b5e8a8d577499f5b6a76b59cbae37f9b125a9ffe9f2/polyfactory-3.3.0-py3-none-any.whl", hash = "sha256:686abcaa761930d3df87b91e95b26b8d8cb9fdbbbe0b03d5f918acff5c72606e", size = 62707, upload-time = "2026-02-22T09:46:25.985Z" }, ] +[[package]] +name = "prettytable" +version = "3.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/45/b0847d88d6cfeb4413566738c8bbf1e1995fad3d42515327ff32cc1eb578/prettytable-3.17.0.tar.gz", hash = "sha256:59f2590776527f3c9e8cf9fe7b66dd215837cca96a9c39567414cbc632e8ddb0", size = 67892, upload-time = "2025-11-14T17:33:20.212Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl", hash = "sha256:aad69b294ddbe3e1f95ef8886a060ed1666a0b83018bbf56295f6f226c43d287", size = 34433, upload-time = "2025-11-14T17:33:19.093Z" }, +] + [[package]] name = "progressbar2" version = "4.5.0" @@ -3049,6 +3635,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" }, ] +[[package]] +name = "pyasn1" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + [[package]] name = "pybase64" version = "1.4.3" @@ -3284,6 +3891,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl", hash = "sha256:5c0fd2a2bea14eb39af8ff284f1066d898ab2187d81b889b75d46d4348c01638", size = 268901, upload-time = "2026-03-29T15:01:53.244Z" }, ] +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + [[package]] name = "pypdfium2" version = "5.8.0" @@ -3760,18 +4376,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/50/0a9e7e7afe7339bd5e36911f0ceb15fed51945836ed803ae5afd661057fd/rtree-1.4.1-py3-none-win_arm64.whl", hash = "sha256:3d46f55729b28138e897ffef32f7ce93ac335cb67f9120125ad3742a220800f0", size = 355253, upload-time = "2025-08-13T19:32:00.296Z" }, ] -[[package]] -name = "s3transfer" -version = "0.18.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "botocore" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e0/1f/12417f7f493fc45e1f9fd5d4a9b6c125cf8d2cf3f8ddbdfab3e76406e9d6/s3transfer-0.18.0.tar.gz", hash = "sha256:3760b8b7ec1315da54048b2d626276732bee4300d054d492d4e1d43e20d4ecbd", size = 160560, upload-time = "2026-05-28T19:39:09.124Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/58/a58fc997655386daa2e25784e30c288aa3e3819e401f77029ee4899fb55a/s3transfer-0.18.0-py3-none-any.whl", hash = "sha256:239c13b09e65ad0346e1be7348b8a202dcad44ac7ea7c6eb858fc881dce739b6", size = 88572, upload-time = "2026-05-28T19:39:07.999Z" }, -] - [[package]] name = "safetensors" version = "0.7.0" @@ -3949,6 +4553,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "skops" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "packaging" }, + { name = "prettytable" }, + { name = "scikit-learn" }, + { name = "scipy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c8/9f/46448c4e41a4c5ee4bdb74b3758af48e5ff0faeffe40f4e301bfc7594894/skops-0.14.0.tar.gz", hash = "sha256:6c8c0e047f691a3a582c3258943eecafcbfd79c8c7eef66260f3703e363254f0", size = 608084, upload-time = "2026-04-20T18:23:55.336Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/0e/3ae19fa941522cd98e119762e7181d371c8dba0b2d72bfaf9522692e329c/skops-0.14.0-py3-none-any.whl", hash = "sha256:60a5db78a9db46ccee2139a0ba13ab5afb1c96f4749b382e75a371291bbe3e36", size = 132198, upload-time = "2026-04-20T18:23:54.018Z" }, +] + [[package]] name = "smmap" version = "5.0.3" @@ -3976,6 +4596,42 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" }, ] +[[package]] +name = "sqlalchemy" +version = "2.0.51" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/f1/a7a892f18d4d224e6b26f706531eafccc41e37594d37d304786969ee13cb/sqlalchemy-2.0.51.tar.gz", hash = "sha256:804dccd8a4a6242c4e30ad961e540e18a588f6527202f2d6791b01845d59fdc9", size = 9912201, upload-time = "2026-06-15T15:41:20.012Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/70/e868bc5412acd101a8280f25c95f10eeae0771c4eb806b02491142810ee8/sqlalchemy-2.0.51-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d78702b26ba1c18b2d0fb2ea940ba7f17a9581b42e8361ff93920ebbee1235a", size = 2160291, upload-time = "2026-06-15T16:08:48.918Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1c/71ee0f8a6b9d7316a1ccd30430b4c62b6c2e36adc96017a4e3a72dce49d6/sqlalchemy-2.0.51-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581921d849d6e6f994d560389192955e80e2950e18fcdfe2ccea863e01158e6e", size = 3343835, upload-time = "2026-06-15T16:19:42.613Z" }, + { url = "https://files.pythonhosted.org/packages/2b/7c/7ab9f9aadc5944fdd06612484ed7918fe376ad871a5f50404dc1536e0194/sqlalchemy-2.0.51-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d21ce524ab86c23046e992a5b81cb54c21079c6df6e78b8fc77d77cac70a6b9", size = 3358470, upload-time = "2026-06-15T16:26:38.011Z" }, + { url = "https://files.pythonhosted.org/packages/d0/7d/ff77169fee6186de145a7f2b87006c39638391130abbab2b1f63ac6ea583/sqlalchemy-2.0.51-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c5d98a2709840027f5a347c3af0a7c3d5f6c1ff93af2ca1c54494e23cba8f389", size = 3289874, upload-time = "2026-06-15T16:19:45.212Z" }, + { url = "https://files.pythonhosted.org/packages/6f/3b/6c505903710d781b55bc3141ee34a062bf9745a6b5bc7333305b9ed63b33/sqlalchemy-2.0.51-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1181256e0f16479691b5616d36375dc2620ad8332b25978763c3d206ad3f3f1d", size = 3321692, upload-time = "2026-06-15T16:26:39.747Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b7/c5ffe50aa2f4d947c9250e1519d939260329a07fe6272edfccd784b3d007/sqlalchemy-2.0.51-cp312-cp312-win32.whl", hash = "sha256:9f380393be5abeb6815f68fd39271b95127173511b6706b0a630a9995d53f8f5", size = 2119674, upload-time = "2026-06-15T16:23:09.543Z" }, + { url = "https://files.pythonhosted.org/packages/25/dc/46a65916af68a06ef6b972c6050ba4c8f97070fe3fb33097d34229d9bef6/sqlalchemy-2.0.51-cp312-cp312-win_amd64.whl", hash = "sha256:2cf39aabdf48e87c1c2c2ed6d20d33ffa0733b3071ce9c5f66357947dd009080", size = 2146670, upload-time = "2026-06-15T16:23:11.048Z" }, + { url = "https://files.pythonhosted.org/packages/54/fe/a210d52fd1a90ecfae8a78e9d8b27e18d733d60818a8bf250ff690b75120/sqlalchemy-2.0.51-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c2056838b6685b72fdb36c99996cf862753461a62f2e84f4196371d3b2d6a07", size = 2157184, upload-time = "2026-06-15T16:08:50.374Z" }, + { url = "https://files.pythonhosted.org/packages/17/6b/2dce8369b199cb855110e056032f94a9f66dacc2237d3d39c115a86eac56/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:483b11bd46bf35fc14c52faf338b04300c9e6ce554bce9b11be85bfec3bc3195", size = 3284735, upload-time = "2026-06-15T16:19:46.934Z" }, + { url = "https://files.pythonhosted.org/packages/53/ff/dbc495b8a14da840faffb353857a72d4190113cac33727906fb997047f0f/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1bed1ee8b01da6088210aa9412023326fb98a599ba502e6118308601dcbef77f", size = 3302756, upload-time = "2026-06-15T16:26:41.336Z" }, + { url = "https://files.pythonhosted.org/packages/cf/d5/fde8f4dddcf518ee15ab35a7c6a28acc32c8ba548d1d2aa451f96e6dbb0b/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72ca54c952107ba5cd58854b67a5a6268631289d21651a1235396f3b98b47400", size = 3232055, upload-time = "2026-06-15T16:19:49.286Z" }, + { url = "https://files.pythonhosted.org/packages/67/d1/43d3a0ac955a58601c24fa23038b1c55ee3a1ec02c0f96ebb1eae2bcf614/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b3e693d15533a45cd5906f0589f9c35090bef6ef45bf1e8195c424aa0ae06a8d", size = 3269850, upload-time = "2026-06-15T16:26:43.017Z" }, + { url = "https://files.pythonhosted.org/packages/94/df/de669c7054cd47c4439ac34b1b2ee8b804a794791fbb10720e997a2c87c7/sqlalchemy-2.0.51-cp313-cp313-win32.whl", hash = "sha256:b93ab07b5292dbe7e6b8da89475275e7042744283921344b56105f3eeb0f828b", size = 2117721, upload-time = "2026-06-15T16:23:12.36Z" }, + { url = "https://files.pythonhosted.org/packages/d0/8a/403c51d064196bae20a0bc2476577f83a3f8dd299719a97417086b7f2ec5/sqlalchemy-2.0.51-cp313-cp313-win_amd64.whl", hash = "sha256:0f053118c30e53161857a953e4de667d90e274980dccbe5dd3829bbbeece72a5", size = 2143615, upload-time = "2026-06-15T16:23:13.906Z" }, + { url = "https://files.pythonhosted.org/packages/e2/22/dbf013a12ec759e54a34a119e9e217435b3f71b2dd5c61a7ade0a25dae87/sqlalchemy-2.0.51-py3-none-any.whl", hash = "sha256:bb024d8b621d0be75f4f44ecc7c950450026e76d66dc8f791bb5331d7fed59d5", size = 1944334, upload-time = "2026-06-15T16:09:22.418Z" }, +] + +[[package]] +name = "sqlparse" +version = "0.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, +] + [[package]] name = "stack-data" version = "0.6.3" @@ -3990,6 +4646,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] +[[package]] +name = "starlette" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6", size = 73632, upload-time = "2026-06-12T09:23:10.017Z" }, +] + [[package]] name = "sympy" version = "1.14.0" @@ -4038,6 +4707,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, ] +[[package]] +name = "tiktoken" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "regex" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", size = 1050728, upload-time = "2025-10-06T20:21:52.756Z" }, + { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", size = 994049, upload-time = "2025-10-06T20:21:53.782Z" }, + { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", size = 1129008, upload-time = "2025-10-06T20:21:54.832Z" }, + { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", size = 1152665, upload-time = "2025-10-06T20:21:56.129Z" }, + { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", size = 1194230, upload-time = "2025-10-06T20:21:57.546Z" }, + { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", size = 1254688, upload-time = "2025-10-06T20:21:58.619Z" }, + { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd", size = 878694, upload-time = "2025-10-06T20:21:59.876Z" }, + { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802, upload-time = "2025-10-06T20:22:00.96Z" }, + { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995, upload-time = "2025-10-06T20:22:02.788Z" }, + { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948, upload-time = "2025-10-06T20:22:03.814Z" }, + { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986, upload-time = "2025-10-06T20:22:05.173Z" }, + { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222, upload-time = "2025-10-06T20:22:06.265Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097, upload-time = "2025-10-06T20:22:07.403Z" }, + { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117, upload-time = "2025-10-06T20:22:08.418Z" }, + { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309, upload-time = "2025-10-06T20:22:10.939Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712, upload-time = "2025-10-06T20:22:12.115Z" }, + { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725, upload-time = "2025-10-06T20:22:13.541Z" }, + { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875, upload-time = "2025-10-06T20:22:14.559Z" }, + { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451, upload-time = "2025-10-06T20:22:15.545Z" }, + { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794, upload-time = "2025-10-06T20:22:16.624Z" }, + { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777, upload-time = "2025-10-06T20:22:18.036Z" }, +] + [[package]] name = "tokenizers" version = "0.22.2" @@ -4423,6 +5125,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, ] +[[package]] +name = "verspec" +version = "0.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/44/8126f9f0c44319b2efc65feaad589cadef4d77ece200ae3c9133d58464d0/verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e", size = 27123, upload-time = "2020-11-30T02:24:09.646Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31", size = 19640, upload-time = "2020-11-30T02:24:08.387Z" }, +] + +[[package]] +name = "waitress" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/cb/04ddb054f45faa306a230769e868c28b8065ea196891f09004ebace5b184/waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f", size = 179901, upload-time = "2024-11-16T20:02:35.195Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/57/a27182528c90ef38d82b636a11f606b0cbb0e17588ed205435f8affe3368/waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e", size = 56232, upload-time = "2024-11-16T20:02:33.858Z" }, +] + [[package]] name = "watchdog" version = "6.0.0" @@ -4539,6 +5259,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, ] +[[package]] +name = "werkzeug" +version = "3.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" }, +] + [[package]] name = "xlsxwriter" version = "3.2.9" From ab15caba6f3e38fb5bf095751a07261d24e2dd08 Mon Sep 17 00:00:00 2001 From: Nikodem Szwast Date: Tue, 7 Jul 2026 11:35:42 +0200 Subject: [PATCH 8/8] Adjust LLM as a judge implementation to better align with ADR. Assited by: Cursor Signed-off-by: Nikodem Szwast --- ai4rag/components/optimization/__init__.py | 2 - .../optimization/judge_selection.py | 165 ++++ .../rag_templates_optimization.py | 96 +- .../optimization/search_space_preparation.py | 30 + ai4rag/core/experiment/experiment.py | 3 + ai4rag/evaluator/__init__.py | 12 +- ai4rag/evaluator/base_evaluator.py | 13 +- ai4rag/evaluator/llmaj_evaluator.py | 207 +++++ .../evaluator/mlflow_llm_judge_evaluator.py | 401 -------- ai4rag/evaluator/score_utils.py | 68 ++ ai4rag/evaluator/unitxt_evaluator.py | 3 +- pyproject.toml | 3 +- .../optimization/test_judge_selection.py | 11 + .../optimization/test_rag_optimization.py | 55 +- .../ai4rag/evaluator/test_llmaj_evaluator.py | 98 ++ .../test_mlflow_llm_judge_evaluator.py | 363 -------- .../unit/ai4rag/evaluator/test_score_utils.py | 28 + .../ai4rag/evaluator/test_unitxt_evaluator.py | 6 +- uv.lock | 856 ++---------------- 19 files changed, 809 insertions(+), 1611 deletions(-) create mode 100644 ai4rag/components/optimization/judge_selection.py create mode 100644 ai4rag/evaluator/llmaj_evaluator.py delete mode 100644 ai4rag/evaluator/mlflow_llm_judge_evaluator.py create mode 100644 ai4rag/evaluator/score_utils.py create mode 100644 tests/unit/ai4rag/components/optimization/test_judge_selection.py create mode 100644 tests/unit/ai4rag/evaluator/test_llmaj_evaluator.py delete mode 100644 tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py create mode 100644 tests/unit/ai4rag/evaluator/test_score_utils.py diff --git a/ai4rag/components/optimization/__init__.py b/ai4rag/components/optimization/__init__.py index 7a6b0b7b..3abbdc3e 100644 --- a/ai4rag/components/optimization/__init__.py +++ b/ai4rag/components/optimization/__init__.py @@ -3,14 +3,12 @@ # SPDX-License-Identifier: Apache-2.0 # ----------------------------------------------------------------------------- from ai4rag.components.optimization.rag_templates_optimization import ( - LLM_JUDGE_METRICS, OptimizationResult, run_rag_optimization, ) from ai4rag.components.optimization.search_space_preparation import SearchSpaceReport, prepare_search_space_report __all__ = [ - "LLM_JUDGE_METRICS", "OptimizationResult", "prepare_search_space_report", "run_rag_optimization", diff --git a/ai4rag/components/optimization/judge_selection.py b/ai4rag/components/optimization/judge_selection.py new file mode 100644 index 00000000..09770d47 --- /dev/null +++ b/ai4rag/components/optimization/judge_selection.py @@ -0,0 +1,165 @@ +# ----------------------------------------------------------------------------- +# Copyright IBM Corp. 2026 +# SPDX-License-Identifier: Apache-2.0 +# ----------------------------------------------------------------------------- +import logging +from typing import Any + +import numpy as np +from docling_core.types.doc import DoclingDocument + +from ai4rag import handler +from ai4rag.core.experiment.benchmark_data import BenchmarkData +from ai4rag.core.experiment.utils import build_evaluation_data, query_rag +from ai4rag.evaluator.base_evaluator import MetricType +from ai4rag.evaluator.llmaj_evaluator import LLMaJConfig, LLMaJEvaluator +from ai4rag.evaluator.unitxt_evaluator import UnitxtEvaluator +from ai4rag.rag.chunking.langchain_chunker import LangChainChunker +from ai4rag.rag.embedding.base_model import BaseEmbeddingModel +from ai4rag.rag.foundation_models.base_model import BaseFoundationModel +from ai4rag.rag.retrieval.retriever import Retriever +from ai4rag.rag.template.simple_rag_template import SimpleRAG +from ai4rag.rag.vector_store.chroma import ChromaVectorStore + +_logger = logging.getLogger("judge-selection") +_logger.addHandler(handler) + + +def calibration_subset_size(total_rows: int) -> int: + """Return calibration row count: min(20, 10% of benchmark rows).""" + if total_rows <= 0: + return 0 + return max(1, min(20, int(total_rows * 0.1))) + + +def select_judge_model( + *, + evaluator: str, + judge_model_id: str | None, + generation_models: list[BaseFoundationModel], + embedding_models: list[BaseEmbeddingModel], + benchmark_data: BenchmarkData, + documents: list[DoclingDocument], + ogx_base_url: str, + ogx_api_key: str, + max_threads: int = 10, +) -> str | None: + """ + Resolve the judge model for an optimization run. + + When ``evaluator`` is ``unitxt``, returns ``None``. When ``judge_model_id`` + is provided, returns it unchanged. Otherwise auto-selects from the generation + model pool using faithfulness calibration against Unitxt. + """ + if evaluator != "judge": + return None + + if judge_model_id: + return judge_model_id + + candidates = [model.model_id for model in generation_models] + if not candidates: + raise ValueError("At least one generation model is required to select a judge model.") + if len(candidates) == 1: + return candidates[0] + + return _calibrate_judge_model( + candidates=candidates, + generation_models=generation_models, + embedding_models=embedding_models, + benchmark_data=benchmark_data, + documents=documents, + ogx_base_url=ogx_base_url, + ogx_api_key=ogx_api_key, + max_threads=max_threads, + ) + + +def _calibrate_judge_model( + *, + candidates: list[str], + generation_models: list[BaseFoundationModel], + embedding_models: list[BaseEmbeddingModel], + benchmark_data: BenchmarkData, + documents: list[DoclingDocument], + ogx_base_url: str, + ogx_api_key: str, + max_threads: int, +) -> str: + subset_size = calibration_subset_size(len(benchmark_data.questions)) + calibration_benchmark = benchmark_data.get_random_sample(n_records=subset_size, random_seed=17) + eval_data = _run_reference_rag( + foundation_model=generation_models[0], + embedding_model=embedding_models[0], + documents=documents, + benchmark_data=calibration_benchmark, + max_threads=max_threads, + ) + + unitxt_scores = UnitxtEvaluator().evaluate_metrics(eval_data, [MetricType.FAITHFULNESS]) + reference = _ordered_question_scores(unitxt_scores, MetricType.FAITHFULNESS) + + rankings: list[dict[str, Any]] = [] + for model_id in candidates: + judge = LLMaJEvaluator( + LLMaJConfig( + base_url=ogx_base_url.rstrip("/") + "/v1", + api_key=ogx_api_key, + model=model_id, + ) + ) + judge_scores = judge.evaluate_metrics(eval_data, [MetricType.FAITHFULNESS]) + candidate_scores = _ordered_question_scores(judge_scores, MetricType.FAITHFULNESS) + correlation = _pearson_correlation(reference, candidate_scores) + rankings.append({"model_id": model_id, "judge_calibration_score": correlation}) + _logger.info("Judge calibration for %s: correlation=%.4f", model_id, correlation) + + rankings.sort( + key=lambda item: ( + item["judge_calibration_score"] if item["judge_calibration_score"] is not None else -2.0, + -candidates.index(item["model_id"]), + item["model_id"], + ), + reverse=True, + ) + selected = rankings[0]["model_id"] + _logger.info("Selected judge model: %s (calibration score=%s)", selected, rankings[0]["judge_calibration_score"]) + return selected + + +def _run_reference_rag( + *, + foundation_model: BaseFoundationModel, + embedding_model: BaseEmbeddingModel, + documents: list[DoclingDocument], + benchmark_data: BenchmarkData, + max_threads: int, +) -> list: + chunker = LangChainChunker(chunk_size=512, method="recursive", chunk_overlap=128) + chunks = chunker.split_documents(documents) + vector_store = ChromaVectorStore(embedding_model=embedding_model, collection_name="judge_calibration") + vector_store.add_documents(chunks) + retriever = Retriever(vector_store=vector_store, number_of_chunks=3, method="simple", search_mode="vector") + rag = SimpleRAG(foundation_model=foundation_model, retriever=retriever) + inference_response = query_rag( + rag=rag, + questions=list(benchmark_data.questions), + max_threads=max_threads, + ) + return build_evaluation_data(benchmark_data=benchmark_data, inference_response=inference_response) + + +def _ordered_question_scores(evaluation_result: dict, metric: str) -> list[float | None]: + question_scores = (evaluation_result.get("question_scores") or {}).get(metric) or {} + return [question_scores.get(qid) for qid in sorted(question_scores.keys())] + + +def _pearson_correlation(reference: list[float | None], candidate: list[float | None]) -> float | None: + paired = [(a, b) for a, b in zip(reference, candidate) if a is not None and b is not None] + if len(paired) < 2: + return None + ref_vals = np.array([pair[0] for pair in paired], dtype=float) + cand_vals = np.array([pair[1] for pair in paired], dtype=float) + if np.std(ref_vals) == 0 or np.std(cand_vals) == 0: + return None + return float(np.corrcoef(ref_vals, cand_vals)[0, 1]) diff --git a/ai4rag/components/optimization/rag_templates_optimization.py b/ai4rag/components/optimization/rag_templates_optimization.py index 41b6304f..a5bd6e31 100644 --- a/ai4rag/components/optimization/rag_templates_optimization.py +++ b/ai4rag/components/optimization/rag_templates_optimization.py @@ -18,6 +18,9 @@ from ai4rag.components.utils.docling_io import load_docling_documents from ai4rag.core.experiment.experiment import AI4RAGExperiment from ai4rag.core.hpo.gam_opt import GAMOptSettings +from ai4rag.evaluator.base_evaluator import MetricType +from ai4rag.evaluator.llmaj_evaluator import LLMaJConfig, LLMaJEvaluator +from ai4rag.evaluator.unitxt_evaluator import UnitxtEvaluator from ai4rag.rag.embedding.ogx import OGXEmbeddingModel from ai4rag.rag.foundation_models.base_model import Language from ai4rag.rag.foundation_models.ogx import OGXFoundationModel @@ -32,7 +35,11 @@ MIN_MAX_RAG_PATTERNS_RANGE = (4, 20) DEFAULT_METRIC = "faithfulness" SUPPORTED_OPTIMIZATION_METRICS = frozenset({"faithfulness", "answer_correctness", "context_correctness"}) -LLM_JUDGE_METRICS = frozenset(SUPPORTED_OPTIMIZATION_METRICS | {"answer_relevance"}) +STANDARD_EVALUATION_METRICS = ( + MetricType.ANSWER_CORRECTNESS, + MetricType.FAITHFULNESS, + MetricType.CONTEXT_CORRECTNESS, +) @dataclass @@ -62,7 +69,8 @@ def run_rag_optimization( # pylint: disable=too-many-locals,too-many-arguments, input_data_key: str = "", optimization_settings: dict | None = None, max_threads: int = 10, - llm_judge_model: str | None = None, + evaluator: str = "judge", + judge_model_id: str | None = None, ) -> OptimizationResult: """Run a full AI4RAG optimization experiment and generate output artefacts. @@ -98,12 +106,11 @@ def run_rag_optimization( # pylint: disable=too-many-locals,too-many-arguments, RAG service during benchmark evaluation. Lower values reduce per-request concurrency (useful when each request carries more retrieved context). Defaults to ``10``. - llm_judge_model - Model identifier served by the OGX inference endpoint to use as - an LLM judge (e.g. ``"llama-31-8b-instruct"``). When provided, - evaluation uses :class:`MlflowLLMJudgeEvaluator` and unlocks the - ``answer_relevance`` metric. When ``None`` (default), the - deterministic :class:`UnitxtEvaluator` is used. + evaluator + Evaluation backend: ``"judge"`` (default) or ``"unitxt"``. + judge_model_id + Judge model identifier for ``evaluator="judge"``. When omitted, the + value from the search-space report is used. Returns ------- @@ -128,27 +135,63 @@ def run_rag_optimization( # pylint: disable=too-many-locals,too-many-arguments, raise ValueError("vector_io_provider_id must be a non-empty string.") vector_io_provider_id = vector_io_provider_id.strip() + if evaluator not in ("judge", "unitxt"): + raise ValueError(f"Evaluator {evaluator!r} is not supported. Supported evaluators are ['judge', 'unitxt'].") + settings = _validate_optimization_settings(optimization_settings) optimization_metric = settings.get("metric") or DEFAULT_METRIC - allowed_metrics = LLM_JUDGE_METRICS if llm_judge_model else SUPPORTED_OPTIMIZATION_METRICS - if optimization_metric not in allowed_metrics: + if optimization_metric not in SUPPORTED_OPTIMIZATION_METRICS: raise ValueError( f"Optimization metric {optimization_metric} is not supported. " - f"Select one of {sorted(allowed_metrics)}." + f"Select one of {sorted(SUPPORTED_OPTIMIZATION_METRICS)}." ) - evaluator_kwargs: dict[str, Any] = {} - if llm_judge_model: - evaluator_kwargs.update(_build_llm_judge_kwargs(llm_judge_model, ogx_client)) + if evaluator == "judge" and not judge_model_id: + raise ValueError( + "judge_model_id is required when evaluator='judge'. " + "Provide it explicitly or run search-space preparation first." + ) documents = load_docling_documents(extracted_text_path) with open(search_space_report_path, "r", encoding="utf-8") as f: search_space_raw: dict[str, Any] = json.load(f) + evaluation_block = search_space_raw.get("evaluation") or {} + resolved_evaluator = evaluator + resolved_judge_model_id = judge_model_id or evaluation_block.get("judge_model_id") + if resolved_evaluator == "judge" and not resolved_judge_model_id: + raise ValueError( + "judge_model_id is required when evaluator='judge'. " + "Provide it explicitly or run search-space preparation first." + ) + + evaluator_kwargs: dict[str, Any] + if resolved_evaluator == "unitxt": + evaluator_kwargs = { + "evaluator": UnitxtEvaluator(), + "metrics": STANDARD_EVALUATION_METRICS, + } + else: + config = LLMaJConfig( + base_url=ogx_client.base_url.rstrip("/") + "/v1", + api_key=getattr(ogx_client, "api_key", "") or "", + model=resolved_judge_model_id or "", + ) + evaluator_kwargs = { + "evaluator": LLMaJEvaluator(config), + "metrics": STANDARD_EVALUATION_METRICS, + } + + evaluation_config = {"evaluator": resolved_evaluator} + if resolved_evaluator == "judge": + evaluation_config["judge_model_id"] = resolved_judge_model_id + params: list[Parameter] = [] for param_name, values in search_space_raw.items(): + if param_name in ("evaluation",): + continue if param_name in ("foundation_model", "embedding_model"): values = [_deserialize_model(m, ogx_client) for m in values] params.append(Parameter(param_name, "C", values=values)) @@ -175,7 +218,8 @@ def run_rag_optimization( # pylint: disable=too-many-locals,too-many-arguments, documents=documents, optimization_metric=optimization_metric, ogx_vector_io_provider_id=vector_io_provider_id, - max_threads=max_threads, + inference_max_threads=max_threads, + evaluation_config=evaluation_config, **evaluator_kwargs, ) @@ -214,12 +258,9 @@ def run_rag_optimization( # pylint: disable=too-many-locals,too-many-arguments, ogx_base_url=ogx_base_url, ) - # Attach scores to pattern data and write pattern.json - with (patt_dir / "pattern.json").open("w+", encoding="utf-8") as f: json_dump(pattern_data, f, indent=2) - # Write evaluation results evaluation_result_list = pattern.get("evaluation_results", []) with (patt_dir / "evaluation_results.json").open("w+", encoding="utf-8") as f: json_dump(evaluation_result_list, f, indent=2) @@ -232,25 +273,6 @@ def run_rag_optimization( # pylint: disable=too-many-locals,too-many-arguments, ) -def _build_llm_judge_kwargs(llm_judge_model: str, ogx_client: OgxClient) -> dict[str, Any]: - """Build evaluator and metrics kwargs for AI4RAGExperiment when using LLM judge.""" - from ai4rag.evaluator.mlflow_llm_judge_evaluator import LLMJudgeConfig, MlflowLLMJudgeEvaluator - - base_url = ogx_client.base_url.rstrip("/") + "/v1" - api_key = getattr(ogx_client, "api_key", "") or "" - - config = LLMJudgeConfig( - base_url=base_url, - api_key=api_key, - model=llm_judge_model, - ) - evaluator = MlflowLLMJudgeEvaluator(config) - return { - "evaluator": evaluator, - "metrics": tuple(sorted(LLM_JUDGE_METRICS)), - } - - def _deserialize_model(data: dict[str, Any], ogx_client: OgxClient) -> OGXEmbeddingModel | OGXFoundationModel: """Reconstruct a model instance from its serialized dictionary. diff --git a/ai4rag/components/optimization/search_space_preparation.py b/ai4rag/components/optimization/search_space_preparation.py index ca1d0a5e..5244a291 100644 --- a/ai4rag/components/optimization/search_space_preparation.py +++ b/ai4rag/components/optimization/search_space_preparation.py @@ -12,6 +12,7 @@ from ogx_client import OgxClient from ai4rag import handler +from ai4rag.components.optimization.judge_selection import select_judge_model from ai4rag.components.utils.docling_io import load_docling_documents from ai4rag.core.experiment.benchmark_data import BenchmarkData from ai4rag.core.experiment.mps import ModelsPreSelector @@ -114,6 +115,8 @@ def prepare_search_space_report( # pylint: disable=too-many-locals,too-many-arg random_seed: int = _DEFAULT_SEED, chunking_methods: list[str] | None = None, inference_max_threads: int = 10, + evaluator: str = "judge", + judge_model_id: str | None = None, ) -> SearchSpaceReport: """Run model pre-selection and prepare a search-space report. @@ -158,6 +161,11 @@ def prepare_search_space_report( # pylint: disable=too-many-locals,too-many-arg RAG service during benchmark evaluation. Lower values reduce per-request concurrency (useful when each request carries more retrieved context). Defaults to ``10``. + evaluator + Evaluation backend: ``"judge"`` (default) or ``"unitxt"``. + judge_model_id + Optional judge model identifier. When omitted and ``evaluator`` is + ``"judge"``, a judge model is auto-selected during preparation. Returns ------- @@ -175,6 +183,9 @@ def prepare_search_space_report( # pylint: disable=too-many-locals,too-many-arg if metric not in SUPPORTED_METRICS: raise ValueError(f"Metric {metric!r} is not supported. Supported metrics are {list(SUPPORTED_METRICS)}.") + if evaluator not in ("judge", "unitxt"): + raise ValueError(f"Evaluator {evaluator!r} is not supported. Supported evaluators are ['judge', 'unitxt'].") + _validate_model_list(embedding_models, "embedding_models") _validate_model_list(generation_models, "generation_models") _validate_chunking_methods(chunking_methods) @@ -240,6 +251,25 @@ def prepare_search_space_report( # pylint: disable=too-many-locals,too-many-arg verbose_repr["chunking_method"] = chunking_methods _logger.info("Chunking methods constrained to: %s", verbose_repr["chunking_method"]) + evaluation_block: dict[str, str] = {"evaluator": evaluator} + if evaluator == "judge": + ogx_api_key = getattr(ogx_client, "api_key", "") or "" + resolved_judge_id = select_judge_model( + evaluator=evaluator, + judge_model_id=judge_model_id, + generation_models=selected_models["foundation_model"], + embedding_models=selected_models["embedding_model"], + benchmark_data=benchmark_data, + documents=documents, + ogx_base_url=ogx_client.base_url, + ogx_api_key=ogx_api_key, + max_threads=inference_max_threads, + ) + if not resolved_judge_id: + raise ValueError("Failed to resolve judge_model_id for evaluator='judge'.") + evaluation_block["judge_model_id"] = resolved_judge_id + verbose_repr["evaluation"] = evaluation_block + return SearchSpaceReport( search_space=verbose_repr, selected_models=selected_models, diff --git a/ai4rag/core/experiment/experiment.py b/ai4rag/core/experiment/experiment.py index 088695d9..219cef98 100644 --- a/ai4rag/core/experiment/experiment.py +++ b/ai4rag/core/experiment/experiment.py @@ -165,6 +165,7 @@ def __init__( self.n_mps_embedding_models = kwargs.pop("n_mps_embedding_models", ModelsPreSelector.DEFAULT_N_EMBEDDING_MODELS) self.known_observations: list[dict] | None = kwargs.pop("known_observations", None) self.inference_max_threads: int = kwargs.pop("inference_max_threads", 10) + self.evaluation_config: dict[str, str] | None = kwargs.pop("evaluation_config", None) self.results: ExperimentResults = ExperimentResults() self._exception_handler = ExperimentExceptionHandler(self.event_handler) @@ -649,6 +650,8 @@ def _stream_finished_pattern( }, "iteration": len(self.results) + n_known, } + if self.evaluation_config: + payload["evaluation"] = self.evaluation_config self.event_handler.on_pattern_creation( payload=payload, diff --git a/ai4rag/evaluator/__init__.py b/ai4rag/evaluator/__init__.py index 3665a1ef..d0937990 100644 --- a/ai4rag/evaluator/__init__.py +++ b/ai4rag/evaluator/__init__.py @@ -2,13 +2,15 @@ # Copyright IBM Corp. 2025-2026 # SPDX-License-Identifier: Apache-2.0 # ----------------------------------------------------------------------------- +from ai4rag import logger from ai4rag.evaluator.base_evaluator import BaseEvaluator from ai4rag.evaluator.unitxt_evaluator import UnitxtEvaluator try: - from ai4rag.evaluator.mlflow_llm_judge_evaluator import ( - LLMJudgeConfig, - MlflowLLMJudgeEvaluator, + from ai4rag.evaluator.llmaj_evaluator import LLMaJConfig, LLMaJEvaluator +except ImportError as exc: + logger.info( + "LLM-as-a-Judge evaluator is unavailable (%s). " + "Install optional dependencies with: pip install ai4rag[llm-judge]", + exc, ) -except ImportError: - pass diff --git a/ai4rag/evaluator/base_evaluator.py b/ai4rag/evaluator/base_evaluator.py index 5b62bfc5..c95a5409 100644 --- a/ai4rag/evaluator/base_evaluator.py +++ b/ai4rag/evaluator/base_evaluator.py @@ -63,21 +63,16 @@ def to_dict(self) -> dict[str, Any]: class MetricType(metaclass=ConstantMeta): """ - Holder for metric names. + Holder for metric names used inpip install -e ".[dev]" + evaluation. - Attributes - ---------- - ANSWER_CORRECTNESS : str, default="answer_correctness" - - FAITHFULNESS : str, default="faithfulness" - - CONTEXT_CORRECTNESS : str, default="context_correctness" + Uses :class:`~ai4rag.utils.constants.ConstantMeta` so values can be + iterated and membership-tested without defining a standard ``Enum``. """ ANSWER_CORRECTNESS = "answer_correctness" FAITHFULNESS = "faithfulness" CONTEXT_CORRECTNESS = "context_correctness" - ANSWER_RELEVANCE = "answer_relevance" class BaseEvaluator(ABC): diff --git a/ai4rag/evaluator/llmaj_evaluator.py b/ai4rag/evaluator/llmaj_evaluator.py new file mode 100644 index 00000000..3c470b5e --- /dev/null +++ b/ai4rag/evaluator/llmaj_evaluator.py @@ -0,0 +1,207 @@ +# ----------------------------------------------------------------------------- +# Copyright IBM Corp. 2026 +# SPDX-License-Identifier: Apache-2.0 +# ----------------------------------------------------------------------------- +import json +import re +from dataclasses import dataclass +from typing import Sequence + +import numpy as np + +from ai4rag.evaluator.base_evaluator import BaseEvaluator, EvaluationData, MetricType +from ai4rag.evaluator.score_utils import compute_confidence_interval, enrich_with_overall_score + +try: + from openai import OpenAI, OpenAIError +except ImportError as exc: + raise ImportError( + "openai package is required for LLM-as-a-Judge evaluation. " + "Install with: pip install ai4rag[llm-judge]" + ) from exc + + +@dataclass +class LLMaJConfig: + """ + Configuration for the LLM-as-a-Judge evaluator. + + Parameters + ---------- + base_url : str + Base URL of the OpenAI-compatible API endpoint. + + api_key : str + API key for authentication. + + model : str + Model name as reported by the endpoint (e.g. ``"llama-31-8b-instruct"``). + + temperature : float + Temperature for the judge model. + """ + + base_url: str = "https://api.openai.com/v1" + api_key: str = "" + model: str = "gpt-4o-mini" + temperature: float = 0.0 + + +JUDGE_PROMPT_TEMPLATE = """\ +You are an impartial judge evaluating the quality of an AI assistant's response. + +## Task +{guidelines} + +## Context +Question: {question} +Retrieved context: {context} +Ground truth answer: {ground_truth} + +## Response to evaluate +{answer} + +## Instructions +Respond with ONLY a JSON object (no markdown, no extra text): +{{"score": , "rationale": ""}} + +Where: +- 1 = completely fails the criterion +- 2 = mostly fails with some relevant elements +- 3 = partially meets the criterion +- 4 = mostly meets with minor gaps +- 5 = fully meets the criterion +""" + + +class LLMaJEvaluator(BaseEvaluator): + """ + Evaluator that scores RAG responses with an LLM judge via an OpenAI-compatible API. + + All scores are normalized from the judge scale (1-5) to [0.0, 1.0]. + """ + + METRIC_GUIDELINES = { + MetricType.ANSWER_CORRECTNESS: ( + "Evaluate how factually correct the response is compared to the ground truth. " + "A correct answer must contain the same key facts as the ground truth." + ), + MetricType.FAITHFULNESS: ( + "Evaluate whether the response is grounded in the provided context. " + "The answer should only contain claims supported by the context, " + "without hallucinating information." + ), + MetricType.CONTEXT_CORRECTNESS: ( + "Evaluate how relevant the retrieved context is for answering the question. " + "Good context should contain the information necessary to answer the question." + ), + } + + def __init__(self, config: LLMaJConfig): + self.config = config + self._client = OpenAI(base_url=config.base_url, api_key=config.api_key) + + def evaluate_metrics( + self, + evaluation_data: list[EvaluationData], + metrics: Sequence[str], + ) -> dict: + """Evaluate responses with the configured judge model.""" + scores: dict[str, dict[str, float | None]] = {} + question_scores: dict[str, dict[str, float | None]] = {} + question_ids = [ed.question_id or str(i) for i, ed in enumerate(evaluation_data)] + + for metric_name in metrics: + guidelines = self.METRIC_GUIDELINES.get(metric_name) + if guidelines is None: + continue + + row_scores: list[float] = [] + question_scores[metric_name] = {} + for i, ed in enumerate(evaluation_data): + qid = question_ids[i] + normalized = self._judge_row(ed, guidelines) + question_scores[metric_name][qid] = round(normalized, 4) if normalized is not None else None + if normalized is not None: + row_scores.append(normalized) + + ci_low, ci_high = compute_confidence_interval(row_scores) + scores[metric_name] = { + "mean": round(float(np.mean(row_scores)), 4) if row_scores else None, + "ci_low": ci_low, + "ci_high": ci_high, + } + + return enrich_with_overall_score({"scores": scores, "question_scores": question_scores}) + + def _judge_row(self, evaluation_data: EvaluationData, guidelines: str) -> float | None: + """Score a single row with the judge model.""" + contexts_str = "\n\n".join(evaluation_data.contexts) if evaluation_data.contexts else "" + ground_truths_str = "\n".join(evaluation_data.ground_truths) if evaluation_data.ground_truths else "" + prompt = JUDGE_PROMPT_TEMPLATE.format( + guidelines=guidelines, + question=evaluation_data.question or "", + context=contexts_str, + ground_truth=ground_truths_str, + answer=evaluation_data.answer or "", + ) + try: + response = self._client.chat.completions.create( + model=self.config.model, + messages=[{"role": "user", "content": prompt}], + temperature=self.config.temperature, + max_tokens=256, + ) + content = response.choices[0].message.content.strip() + return _normalize_score(_parse_score(content)) + except (OpenAIError, ValueError, KeyError, AttributeError): + return None + + def get_supported_metrics(self) -> list[str]: + """Return metric names supported by this evaluator.""" + return list(self.METRIC_GUIDELINES.keys()) + + +def _extract_json(content: str) -> dict | None: + """Extract a JSON object from LLM output that may contain markdown fences or extra text.""" + try: + return json.loads(content) + except (json.JSONDecodeError, ValueError): + pass + + fence_match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", content, re.DOTALL) + if fence_match: + try: + return json.loads(fence_match.group(1)) + except (json.JSONDecodeError, ValueError): + pass + + brace_match = re.search(r"\{[^{}]*\}", content) + if brace_match: + try: + return json.loads(brace_match.group(0)) + except (json.JSONDecodeError, ValueError): + pass + + return None + + +def _parse_score(content: str) -> int | None: + """Parse a score (1-5) from the judge's JSON response.""" + data = _extract_json(content) + if data is None: + return None + try: + score = int(data["score"]) + if 1 <= score <= 5: + return score + except (KeyError, ValueError, TypeError): + pass + return None + + +def _normalize_score(score: int | None) -> float | None: + """Normalize a score from 1-5 to [0.0, 1.0].""" + if score is None: + return None + return (score - 1) / 4 diff --git a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py b/ai4rag/evaluator/mlflow_llm_judge_evaluator.py deleted file mode 100644 index 2bf568af..00000000 --- a/ai4rag/evaluator/mlflow_llm_judge_evaluator.py +++ /dev/null @@ -1,401 +0,0 @@ -# ----------------------------------------------------------------------------- -# Copyright IBM Corp. 2026 -# SPDX-License-Identifier: Apache-2.0 -# ----------------------------------------------------------------------------- -import json -import re -from dataclasses import dataclass, field -from typing import Sequence - -import numpy as np - -from ai4rag.evaluator.base_evaluator import BaseEvaluator, EvaluationData, MetricType - -try: - import mlflow.genai - from mlflow.entities import Feedback - from mlflow.genai.scorers import scorer as mlflow_scorer - from openai import OpenAI, OpenAIError -except ImportError as exc: - raise ImportError( - "mlflow and openai packages are required for LLM-as-a-Judge evaluation. " - "Install with: pip install ai4rag[llm-judge]" - ) from exc - - -@dataclass -class CustomMetricDefinition: - """ - Definition for a user-provided custom LLM judge metric. - - Parameters - ---------- - name : str - Metric identifier string (e.g., "medical_accuracy"). - - guidelines : str - Evaluation guidelines for the judge LLM describing what to assess. - """ - - name: str - guidelines: str - - -@dataclass -class LLMJudgeConfig: - """ - Configuration for the LLM-as-a-Judge evaluator. - - Parameters - ---------- - base_url : str - Base URL of the OpenAI-compatible API endpoint. - - api_key : str - API key for authentication. - - model : str - Model name as reported by the endpoint (e.g., ``"llama-31-8b-instruct"``). - - temperature : float - Temperature for the judge model. - - custom_metrics : list[CustomMetricDefinition] - Optional list of user-defined custom LLM judge metrics. - """ - - base_url: str = "https://api.openai.com/v1" - api_key: str = "" - model: str = "gpt-4o-mini" - temperature: float = 0.0 - custom_metrics: list[CustomMetricDefinition] = field(default_factory=list) - - -JUDGE_PROMPT_TEMPLATE = """\ -You are an impartial judge evaluating the quality of an AI assistant's response. - -## Task -{guidelines} - -## Context -Question: {question} -Retrieved context: {context} -Ground truth answer: {ground_truth} - -## Response to evaluate -{answer} - -## Instructions -Respond with ONLY a JSON object (no markdown, no extra text): -{{"score": , "rationale": ""}} - -Where: -- 1 = completely fails the criterion -- 2 = mostly fails with some relevant elements -- 3 = partially meets the criterion -- 4 = mostly meets with minor gaps -- 5 = fully meets the criterion -""" - - -class MlflowLLMJudgeEvaluator(BaseEvaluator): - """ - Evaluator that uses MLflow's evaluation framework with LLM-as-a-Judge. - - Uses ``mlflow.genai.evaluate()`` with custom ``@scorer`` functions that call - a judge LLM via the OpenAI client. This provides full MLflow tracking and - evaluation integration while routing judge calls to any OpenAI-compatible - endpoint via ``base_url``. - - All scores are normalized from the judge scale (1-5) to [0.0, 1.0]. - - Parameters - ---------- - config : LLMJudgeConfig - Configuration specifying the judge model endpoint and behavior. - """ - - METRIC_TYPE_MAP = { - MetricType.ANSWER_CORRECTNESS: { - "name": "answer_correctness", - "guidelines": ( - "Evaluate how factually correct the response is compared to the ground truth. " - "A correct answer must contain the same key facts as the ground truth." - ), - }, - MetricType.FAITHFULNESS: { - "name": "faithfulness", - "guidelines": ( - "Evaluate whether the response is grounded in the provided context. " - "The answer should only contain claims supported by the context, " - "without hallucinating information." - ), - }, - MetricType.CONTEXT_CORRECTNESS: { - "name": "context_correctness", - "guidelines": ( - "Evaluate how relevant the retrieved context is for answering the question. " - "Good context should contain the information necessary to answer the question." - ), - }, - MetricType.ANSWER_RELEVANCE: { - "name": "answer_relevance", - "guidelines": ( - "Evaluate how relevant and helpful the response is to the original question. " - "A relevant answer directly addresses what was asked." - ), - }, - } - - def __init__(self, config: LLMJudgeConfig): - self.config = config - self._client = OpenAI(base_url=config.base_url, api_key=config.api_key) - self._custom_metric_names: set[str] = set() - for cm in config.custom_metrics: - self._custom_metric_names.add(cm.name) - - def evaluate_metrics( - self, - evaluation_data: list[EvaluationData], - metrics: Sequence[str], - ) -> dict: - """ - Evaluate the model's responses using MLflow with LLM-as-a-Judge scorers. - - All scores are normalized to [0.0, 1.0]. - - Parameters - ---------- - evaluation_data : list[EvaluationData] - List of EvaluationData instances. - - metrics : Sequence[str] - Metric names from MetricType or custom metric names. - - Returns - ------- - dict - ``{"scores": {metric: {mean, ci_low, ci_high}}, "question_scores": {metric: {q_id: score}}}`` - """ - eval_data = self._build_eval_data(evaluation_data) - scorers = self._build_scorers(metrics) - if not scorers: - return {"scores": {}, "question_scores": {}} - - question_ids = [ed.question_id or str(i) for i, ed in enumerate(evaluation_data)] - all_scores: dict = {} - all_question_scores: dict = {} - - for scorer in scorers: - mlflow_result = mlflow.genai.evaluate(data=eval_data, scorers=[scorer]) - partial = self._format_results(mlflow_result, metrics, question_ids) - all_scores.update(partial.get("scores", {})) - all_question_scores.update(partial.get("question_scores", {})) - - return {"scores": all_scores, "question_scores": all_question_scores} - - @staticmethod - def _build_eval_data(evaluation_data: list[EvaluationData]) -> list[dict]: - """Convert EvaluationData list to MLflow evaluation data format.""" - rows = [] - for ed in evaluation_data: - contexts_str = "\n\n".join(ed.contexts) if ed.contexts else "" - ground_truths_str = "\n".join(ed.ground_truths) if ed.ground_truths else "" - rows.append( - { - "inputs": { - "question": ed.question or "", - "context": contexts_str, - }, - "outputs": ed.answer or "", - "expectations": { - "expected_response": ground_truths_str, - }, - } - ) - return rows - - def _build_scorers(self, metrics: Sequence[str]) -> list: - """Build MLflow scorer functions for the requested metrics.""" - scorers = [] - for metric_name in metrics: - guidelines = self._get_guidelines(metric_name) - if guidelines is None: - continue - scorers.append(self._make_scorer(metric_name, guidelines)) - return scorers - - def _make_scorer(self, metric_name: str, guidelines: str): - """Create an MLflow @scorer that judges via the OpenAI client.""" - client = self._client - model = self.config.model - temperature = self.config.temperature - - @mlflow_scorer(name=metric_name) - def judge_scorer(inputs, outputs, expectations): - prompt = JUDGE_PROMPT_TEMPLATE.format( - guidelines=guidelines, - question=inputs.get("question", ""), - context=inputs.get("context", ""), - ground_truth=expectations.get("expected_response", "") if expectations else "", - answer=outputs or "", - ) - try: - response = client.chat.completions.create( - model=model, - messages=[{"role": "user", "content": prompt}], - temperature=temperature, - max_tokens=256, - ) - content = response.choices[0].message.content.strip() - parsed = _parse_score(content) - normalized = _normalize_score(parsed) - rationale = _parse_rationale(content) - return Feedback( - name=metric_name, - value=normalized, - rationale=rationale, - ) - except (OpenAIError, ValueError, KeyError) as exc: - return Feedback( - name=metric_name, - value=None, - rationale=f"Judge call failed: {exc}", - ) - - return judge_scorer - - def _get_guidelines(self, metric_name: str) -> str | None: - """Get the guidelines string for a metric.""" - if metric_name in self.METRIC_TYPE_MAP: - return self.METRIC_TYPE_MAP[metric_name]["guidelines"] - if metric_name in self._custom_metric_names: - cm = next((c for c in self.config.custom_metrics if c.name == metric_name), None) - if cm is None: - return None - return cm.guidelines - return None - - def _format_results( - self, - mlflow_result, - metrics: Sequence[str], - question_ids: list[str], - ) -> dict: - """Extract per-metric scores from MLflow EvaluationResult.""" - scores = {} - question_scores = {} - - eval_table = mlflow_result.tables.get("eval_results") - - for metric_name in metrics: - col_name = f"{metric_name}/value" - if eval_table is None or col_name not in eval_table.columns: - continue - - raw_values = eval_table[col_name].tolist() - valid = [float(v) for v in raw_values if v is not None and not _is_nan(v)] - - ci = self._compute_confidence_interval(valid) - scores[metric_name] = { - "mean": round(float(np.mean(valid)), 4) if valid else None, - "ci_low": ci[0], - "ci_high": ci[1], - } - - question_scores[metric_name] = {} - for i, qid in enumerate(question_ids): - val = raw_values[i] if i < len(raw_values) else None - if val is not None and not _is_nan(val): - question_scores[metric_name][qid] = round(float(val), 4) - else: - question_scores[metric_name][qid] = None - - return {"scores": scores, "question_scores": question_scores} - - @staticmethod - def _compute_confidence_interval( - scores: list[float], confidence: float = 0.95, n_bootstrap: int = 1000 - ) -> tuple[float | None, float | None]: - """Compute bootstrap confidence interval for the mean score.""" - if len(scores) < 2: - return None, None - - rng = np.random.default_rng(seed=42) - bootstrap_means = [] - for _ in range(n_bootstrap): - sample = rng.choice(scores, size=len(scores), replace=True) - bootstrap_means.append(float(np.mean(sample))) - - alpha = (1 - confidence) / 2 - ci_low = round(float(np.percentile(bootstrap_means, alpha * 100)), 4) - ci_high = round(float(np.percentile(bootstrap_means, (1 - alpha) * 100)), 4) - - return ci_low, ci_high - - def get_supported_metrics(self) -> list[str]: - """Return all metric names this evaluator supports.""" - built_in = list(self.METRIC_TYPE_MAP.keys()) - custom = [cm.name for cm in self.config.custom_metrics] - return built_in + custom - - -def _extract_json(content: str) -> dict | None: - """Extract a JSON object from LLM output that may contain markdown fences or extra text.""" - try: - return json.loads(content) - except (json.JSONDecodeError, ValueError): - pass - - fence_match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", content, re.DOTALL) - if fence_match: - try: - return json.loads(fence_match.group(1)) - except (json.JSONDecodeError, ValueError): - pass - - brace_match = re.search(r"\{[^{}]*\}", content) - if brace_match: - try: - return json.loads(brace_match.group(0)) - except (json.JSONDecodeError, ValueError): - pass - - return None - - -def _parse_score(content: str) -> int | None: - """Parse a score (1-5) from the judge's JSON response.""" - data = _extract_json(content) - if data is None: - return None - try: - score = int(data["score"]) - if 1 <= score <= 5: - return score - except (KeyError, ValueError, TypeError): - pass - return None - - -def _parse_rationale(content: str) -> str | None: - """Parse a rationale string from the judge's JSON response.""" - data = _extract_json(content) - if data is None: - return None - return data.get("rationale") - - -def _normalize_score(score: int | None) -> float | None: - """Normalize a score from 1-5 to [0.0, 1.0].""" - if score is None: - return None - return (score - 1) / 4 - - -def _is_nan(value) -> bool: - """Check if a value is NaN.""" - try: - return np.isnan(float(value)) - except (TypeError, ValueError): - return False diff --git a/ai4rag/evaluator/score_utils.py b/ai4rag/evaluator/score_utils.py new file mode 100644 index 00000000..fdc431d6 --- /dev/null +++ b/ai4rag/evaluator/score_utils.py @@ -0,0 +1,68 @@ +# ----------------------------------------------------------------------------- +# Copyright IBM Corp. 2026 +# SPDX-License-Identifier: Apache-2.0 +# ----------------------------------------------------------------------------- +from typing import Any + +import numpy as np + +from ai4rag.evaluator.base_evaluator import MetricType + +STANDARD_TRIO = ( + MetricType.FAITHFULNESS, + MetricType.ANSWER_CORRECTNESS, + MetricType.CONTEXT_CORRECTNESS, +) + + +def compute_confidence_interval( + scores: list[float], confidence: float = 0.95, n_bootstrap: int = 1000 +) -> tuple[float | None, float | None]: + """Compute bootstrap confidence interval for the mean score.""" + if len(scores) < 2: + return None, None + + rng = np.random.default_rng(seed=42) + bootstrap_means = [float(np.mean(rng.choice(scores, size=len(scores), replace=True))) for _ in range(n_bootstrap)] + + alpha = (1 - confidence) / 2 + return ( + round(float(np.percentile(bootstrap_means, alpha * 100)), 4), + round(float(np.percentile(bootstrap_means, (1 - alpha) * 100)), 4), + ) + + +def enrich_with_overall_score(result: dict[str, Any]) -> dict[str, Any]: + """Add derived ``overall_score`` to pattern-level and per-question scores.""" + scores = result.get("scores") or {} + question_scores = result.get("question_scores") or {} + + question_ids: set[str] = set() + for metric in STANDARD_TRIO: + question_ids.update((question_scores.get(metric) or {}).keys()) + + per_question_overall: list[float] = [] + overall_by_question: dict[str, float | None] = {} + for qid in question_ids: + values = [ + question_scores[metric][qid] + for metric in STANDARD_TRIO + if metric in question_scores and question_scores[metric].get(qid) is not None + ] + if values: + mean_val = round(float(np.mean(values)), 4) + overall_by_question[qid] = mean_val + per_question_overall.append(mean_val) + else: + overall_by_question[qid] = None + + question_scores["overall_score"] = overall_by_question + + ci_low, ci_high = compute_confidence_interval(per_question_overall) + scores["overall_score"] = { + "mean": round(float(np.mean(per_question_overall)), 4) if per_question_overall else None, + "ci_low": ci_low, + "ci_high": ci_high, + } + + return {"scores": scores, "question_scores": question_scores} diff --git a/ai4rag/evaluator/unitxt_evaluator.py b/ai4rag/evaluator/unitxt_evaluator.py index 81fedcea..24eab266 100644 --- a/ai4rag/evaluator/unitxt_evaluator.py +++ b/ai4rag/evaluator/unitxt_evaluator.py @@ -13,6 +13,7 @@ EvaluationData, MetricType, ) +from ai4rag.evaluator.score_utils import enrich_with_overall_score class UnitxtEvaluator(BaseEvaluator): @@ -55,7 +56,7 @@ def evaluate_metrics( returned_ci = self._handle_ci_calculations(ci_table=ci_table) question_scores = self._handle_questions_scores(scores_df=scores_df) - return {"scores": returned_ci, "question_scores": question_scores} + return enrich_with_overall_score({"scores": returned_ci, "question_scores": question_scores}) except Exception as exc: raise EvaluationError(exc) from exc diff --git a/pyproject.toml b/pyproject.toml index c79269f1..1aba08e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,8 +58,7 @@ Documentation = "https://ibm.github.io/ai4rag/" [project.optional-dependencies] llm-judge = [ - "mlflow>=3.0.0", - "openai>=1.0.0", + "openai~=1.109.1", ] dev = [ diff --git a/tests/unit/ai4rag/components/optimization/test_judge_selection.py b/tests/unit/ai4rag/components/optimization/test_judge_selection.py new file mode 100644 index 00000000..11b27369 --- /dev/null +++ b/tests/unit/ai4rag/components/optimization/test_judge_selection.py @@ -0,0 +1,11 @@ +# ----------------------------------------------------------------------------- +# Copyright IBM Corp. 2026 +# SPDX-License-Identifier: Apache-2.0 +# ----------------------------------------------------------------------------- +from ai4rag.components.optimization.judge_selection import calibration_subset_size + + +def test_calibration_subset_size(): + assert calibration_subset_size(5) == 1 + assert calibration_subset_size(100) == 10 + assert calibration_subset_size(500) == 20 diff --git a/tests/unit/ai4rag/components/optimization/test_rag_optimization.py b/tests/unit/ai4rag/components/optimization/test_rag_optimization.py index 8f836fd5..d89d031b 100644 --- a/tests/unit/ai4rag/components/optimization/test_rag_optimization.py +++ b/tests/unit/ai4rag/components/optimization/test_rag_optimization.py @@ -10,7 +10,6 @@ from ai4rag.components.optimization.rag_templates_optimization import ( DEFAULT_MAX_RAG_PATTERNS, - LLM_JUDGE_METRICS, MIN_MAX_RAG_PATTERNS_RANGE, SUPPORTED_OPTIMIZATION_METRICS, _validate_optimization_settings, @@ -265,24 +264,21 @@ def test_max_threads_is_accepted(self, mock_ogx_client): # --------------------------------------------------------------------------- -# run_rag_optimization -- llm_judge_model parameter +# run_rag_optimization -- evaluator / judge_model_id parameters # --------------------------------------------------------------------------- -class TestRunRagOptimizationLLMJudge: - """Tests for the llm_judge_model parameter on run_rag_optimization.""" +class TestRunRagOptimizationEvaluator: + """Tests for ADR evaluator parameters on run_rag_optimization.""" - def test_llm_judge_model_defaults_to_none(self): - """llm_judge_model must default to None.""" + def test_evaluator_defaults_to_judge(self): import inspect sig = inspect.signature(run_rag_optimization) - param = sig.parameters["llm_judge_model"] - assert param.default is None + assert sig.parameters["evaluator"].default == "judge" - def test_answer_relevance_rejected_without_llm_judge(self, mock_ogx_client): - """answer_relevance requires llm_judge_model; without it must raise ValueError.""" - with pytest.raises(ValueError, match="is not supported"): + def test_judge_requires_model_id(self, mock_ogx_client): + with pytest.raises(ValueError, match="judge_model_id is required"): run_rag_optimization( extracted_text_path="dummy", test_data_path="dummy.json", @@ -291,16 +287,11 @@ def test_answer_relevance_rejected_without_llm_judge(self, mock_ogx_client): ogx_client=mock_ogx_client, vector_io_provider_id="provider-1", test_data_key="bench.json", - optimization_settings={"metric": "answer_relevance"}, + evaluator="judge", ) - def test_answer_relevance_accepted_with_llm_judge(self, mock_ogx_client): - """answer_relevance must pass validation when llm_judge_model is set. - - The call will fail later (no real endpoint), but must not raise - ValueError about an unsupported metric. - """ - with pytest.raises(Exception, match="(?!is not supported)"): + def test_unitxt_evaluator_does_not_require_judge_model(self, mock_ogx_client): + with pytest.raises(Exception, match="(?!judge_model_id is required)"): run_rag_optimization( extracted_text_path="dummy", test_data_path="dummy.json", @@ -309,19 +300,23 @@ def test_answer_relevance_accepted_with_llm_judge(self, mock_ogx_client): ogx_client=mock_ogx_client, vector_io_provider_id="provider-1", test_data_key="bench.json", - optimization_settings={"metric": "answer_relevance"}, - llm_judge_model="llama-31-8b-instruct", + evaluator="unitxt", ) - def test_llm_judge_metrics_superset_of_supported(self): - """LLM_JUDGE_METRICS must be a strict superset of SUPPORTED_OPTIMIZATION_METRICS.""" - assert SUPPORTED_OPTIMIZATION_METRICS < LLM_JUDGE_METRICS - assert "answer_relevance" in LLM_JUDGE_METRICS - assert "answer_relevance" not in SUPPORTED_OPTIMIZATION_METRICS + def test_invalid_evaluator_rejected(self): + with pytest.raises(ValueError, match="is not supported"): + run_rag_optimization( + extracted_text_path="dummy", + test_data_path="dummy.json", + search_space_report_path="dummy.json", + output_dir="out", + ogx_client=MagicMock(), + vector_io_provider_id="provider-1", + test_data_key="bench.json", + evaluator="unknown", + ) - def test_llm_judge_model_is_accepted(self, mock_ogx_client): - """Passing llm_judge_model alongside an invalid input must still raise - the expected validation error (not a TypeError from an unknown param).""" + def test_judge_model_id_is_accepted(self, mock_ogx_client): with pytest.raises(ValueError, match="non-empty string"): run_rag_optimization( extracted_text_path="dummy", @@ -331,5 +326,5 @@ def test_llm_judge_model_is_accepted(self, mock_ogx_client): ogx_client=mock_ogx_client, vector_io_provider_id="", test_data_key="bench.json", - llm_judge_model="some-model", + judge_model_id="some-model", ) diff --git a/tests/unit/ai4rag/evaluator/test_llmaj_evaluator.py b/tests/unit/ai4rag/evaluator/test_llmaj_evaluator.py new file mode 100644 index 00000000..0bb9604b --- /dev/null +++ b/tests/unit/ai4rag/evaluator/test_llmaj_evaluator.py @@ -0,0 +1,98 @@ +# ----------------------------------------------------------------------------- +# Copyright IBM Corp. 2026 +# SPDX-License-Identifier: Apache-2.0 +# ----------------------------------------------------------------------------- +import json +from unittest.mock import MagicMock, patch + +import pytest + +from ai4rag.evaluator.base_evaluator import BaseEvaluator, EvaluationData, MetricType +from ai4rag.evaluator.llmaj_evaluator import ( + LLMaJConfig, + LLMaJEvaluator, + _extract_json, + _normalize_score, + _parse_score, +) + + +@pytest.fixture +def sample_evaluation_data() -> list[EvaluationData]: + return [ + EvaluationData( + question="What is Python?", + answer="Python is a programming language.", + contexts=["Python is a high-level programming language."], + context_ids=["doc1"], + ground_truths=["Python is a programming language."], + ground_truths_context_ids=["doc1"], + question_id="q1", + ), + EvaluationData( + question="What is AI?", + answer="AI is Artificial Intelligence.", + contexts=["AI stands for Artificial Intelligence."], + context_ids=["doc2"], + ground_truths=["AI is Artificial Intelligence."], + ground_truths_context_ids=["doc2"], + question_id="q2", + ), + ] + + +def _make_chat_response(score: int) -> MagicMock: + resp = MagicMock() + resp.choices = [MagicMock()] + resp.choices[0].message.content = json.dumps({"score": score, "rationale": "OK"}) + return resp + + +class TestLLMaJConfig: + def test_default_config(self): + config = LLMaJConfig() + assert config.base_url == "https://api.openai.com/v1" + assert config.model == "gpt-4o-mini" + assert config.temperature == 0.0 + + +class TestLLMaJEvaluator: + @patch("ai4rag.evaluator.llmaj_evaluator.OpenAI") + def test_supported_metrics(self, _mock_openai): + evaluator = LLMaJEvaluator(LLMaJConfig()) + supported = evaluator.get_supported_metrics() + assert MetricType.FAITHFULNESS in supported + assert MetricType.ANSWER_CORRECTNESS in supported + assert MetricType.CONTEXT_CORRECTNESS in supported + + @patch("ai4rag.evaluator.llmaj_evaluator.OpenAI") + def test_evaluate_metrics(self, mock_openai_cls, sample_evaluation_data): + client = MagicMock() + mock_openai_cls.return_value = client + client.chat.completions.create.side_effect = [ + _make_chat_response(5), + _make_chat_response(4), + ] + + evaluator = LLMaJEvaluator(LLMaJConfig(model="judge-model")) + result = evaluator.evaluate_metrics(sample_evaluation_data, [MetricType.FAITHFULNESS]) + + assert result["scores"][MetricType.FAITHFULNESS]["mean"] == 0.875 + assert result["question_scores"][MetricType.FAITHFULNESS]["q1"] == 1.0 + assert result["question_scores"][MetricType.FAITHFULNESS]["q2"] == 0.75 + + @patch("ai4rag.evaluator.llmaj_evaluator.OpenAI") + def test_is_base_evaluator_subclass(self, _mock_openai): + assert issubclass(LLMaJEvaluator, BaseEvaluator) + + +class TestParsingHelpers: + def test_extract_json_plain(self): + assert _extract_json('{"score": 4}') == {"score": 4} + + def test_parse_score_valid(self): + assert _parse_score('{"score": 3}') == 3 + + def test_normalize_score(self): + assert _normalize_score(1) == 0.0 + assert _normalize_score(5) == 1.0 diff --git a/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py b/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py deleted file mode 100644 index 9afc7345..00000000 --- a/tests/unit/ai4rag/evaluator/test_mlflow_llm_judge_evaluator.py +++ /dev/null @@ -1,363 +0,0 @@ -# ----------------------------------------------------------------------------- -# Copyright IBM Corp. 2026 -# SPDX-License-Identifier: Apache-2.0 -# ----------------------------------------------------------------------------- -import json -from unittest.mock import MagicMock, patch - -import pandas as pd -import pytest - -pytest.importorskip("mlflow", reason="mlflow is required for LLM-as-a-Judge tests") -pytest.importorskip("openai", reason="openai is required for LLM-as-a-Judge tests") - -from ai4rag.evaluator.base_evaluator import BaseEvaluator, EvaluationData, MetricType -from ai4rag.evaluator.mlflow_llm_judge_evaluator import ( - CustomMetricDefinition, - LLMJudgeConfig, - MlflowLLMJudgeEvaluator, - _extract_json, - _normalize_score, - _parse_rationale, - _parse_score, -) - - -@pytest.fixture -def sample_evaluation_data() -> list[EvaluationData]: - return [ - EvaluationData( - question="What is Python?", - answer="Python is a programming language.", - contexts=["Python is a high-level programming language."], - context_ids=["doc1"], - ground_truths=["Python is a programming language."], - ground_truths_context_ids=["doc1"], - question_id="q1", - ), - EvaluationData( - question="What is AI?", - answer="AI is Artificial Intelligence.", - contexts=["AI stands for Artificial Intelligence."], - context_ids=["doc2"], - ground_truths=["AI is Artificial Intelligence."], - ground_truths_context_ids=["doc2"], - question_id="q2", - ), - ] - - -def _make_chat_response(score: int, rationale: str = "OK") -> MagicMock: - resp = MagicMock() - resp.choices = [MagicMock()] - resp.choices[0].message.content = json.dumps({"score": score, "rationale": rationale}) - return resp - - -class TestLLMJudgeConfig: - def test_default_config(self): - config = LLMJudgeConfig() - assert config.base_url == "https://api.openai.com/v1" - assert config.api_key == "" - assert config.model == "gpt-4o-mini" - assert config.temperature == 0.0 - assert config.custom_metrics == [] - - def test_custom_config(self): - config = LLMJudgeConfig( - base_url="https://my-llm.example.com/v1", - api_key="sk-test", - model="llama-31-8b-instruct", - temperature=0.1, - ) - assert config.base_url == "https://my-llm.example.com/v1" - assert config.model == "llama-31-8b-instruct" - assert config.temperature == 0.1 - - -class TestCustomMetricDefinition: - def test_basic_definition(self): - metric = CustomMetricDefinition( - name="medical_accuracy", - guidelines="Evaluate medical accuracy of the response.", - ) - assert metric.name == "medical_accuracy" - assert "medical" in metric.guidelines - - -class TestExtractJson: - def test_plain_json(self): - assert _extract_json('{"score": 3, "rationale": "ok"}') == {"score": 3, "rationale": "ok"} - - def test_markdown_fenced_json(self): - content = '```json\n{"score": 4, "rationale": "good"}\n```' - assert _extract_json(content) == {"score": 4, "rationale": "good"} - - def test_markdown_fenced_no_lang(self): - content = '```\n{"score": 2, "rationale": "poor"}\n```' - assert _extract_json(content) == {"score": 2, "rationale": "poor"} - - def test_json_with_preamble(self): - content = 'Here is my evaluation:\n{"score": 5, "rationale": "excellent"}' - assert _extract_json(content) == {"score": 5, "rationale": "excellent"} - - def test_no_json(self): - assert _extract_json("no json here") is None - - def test_empty_string(self): - assert _extract_json("") is None - - -class TestParseScore: - def test_valid_scores(self): - assert _parse_score('{"score": 1, "rationale": "bad"}') == 1 - assert _parse_score('{"score": 3, "rationale": "ok"}') == 3 - assert _parse_score('{"score": 5, "rationale": "great"}') == 5 - - def test_markdown_fenced_scores(self): - assert _parse_score('```json\n{"score": 4, "rationale": "good"}\n```') == 4 - - def test_score_with_preamble(self): - assert _parse_score('Here is my evaluation:\n{"score": 3, "rationale": "ok"}') == 3 - - def test_invalid_scores(self): - assert _parse_score("not json") is None - assert _parse_score('{"score": 0}') is None - assert _parse_score('{"score": 6}') is None - assert _parse_score('{"no_score": 3}') is None - - -class TestParseRationale: - def test_valid(self): - assert _parse_rationale('{"score": 4, "rationale": "Good"}') == "Good" - - def test_markdown_fenced(self): - assert _parse_rationale('```json\n{"score": 4, "rationale": "Good"}\n```') == "Good" - - def test_invalid(self): - assert _parse_rationale("not json") is None - - def test_missing_key(self): - assert _parse_rationale('{"score": 4}') is None - - -class TestNormalizeScore: - def test_normalize(self): - assert _normalize_score(1) == 0.0 - assert _normalize_score(5) == 1.0 - assert _normalize_score(3) == 0.5 - assert _normalize_score(None) is None - - -class TestMlflowLLMJudgeEvaluator: - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_is_base_evaluator(self, mock_openai_cls): - evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) - assert isinstance(evaluator, BaseEvaluator) - - def test_metric_type_map_has_all_metrics(self): - assert MetricType.ANSWER_CORRECTNESS in MlflowLLMJudgeEvaluator.METRIC_TYPE_MAP - assert MetricType.FAITHFULNESS in MlflowLLMJudgeEvaluator.METRIC_TYPE_MAP - assert MetricType.CONTEXT_CORRECTNESS in MlflowLLMJudgeEvaluator.METRIC_TYPE_MAP - assert MetricType.ANSWER_RELEVANCE in MlflowLLMJudgeEvaluator.METRIC_TYPE_MAP - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_get_supported_metrics_builtin_only(self, mock_openai_cls): - evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) - supported = evaluator.get_supported_metrics() - assert MetricType.ANSWER_CORRECTNESS in supported - assert MetricType.FAITHFULNESS in supported - assert MetricType.CONTEXT_CORRECTNESS in supported - assert MetricType.ANSWER_RELEVANCE in supported - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_get_supported_metrics_with_custom(self, mock_openai_cls): - config = LLMJudgeConfig( - custom_metrics=[ - CustomMetricDefinition( - name="domain_accuracy", - guidelines="Evaluate domain accuracy.", - ) - ] - ) - evaluator = MlflowLLMJudgeEvaluator(config) - supported = evaluator.get_supported_metrics() - assert "domain_accuracy" in supported - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_build_eval_data(self, mock_openai_cls, sample_evaluation_data): - evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) - data = evaluator._build_eval_data(sample_evaluation_data) - - assert len(data) == 2 - assert data[0]["inputs"]["question"] == "What is Python?" - assert data[0]["outputs"] == "Python is a programming language." - assert data[0]["expectations"]["expected_response"] == "Python is a programming language." - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_build_eval_data_handles_none(self, mock_openai_cls): - evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) - data = evaluator._build_eval_data([EvaluationData(question_id="q1")]) - - assert data[0]["inputs"]["question"] == "" - assert data[0]["outputs"] == "" - assert data[0]["inputs"]["context"] == "" - assert data[0]["expectations"]["expected_response"] == "" - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_build_scorers(self, mock_openai_cls): - evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) - scorers = evaluator._build_scorers([MetricType.FAITHFULNESS]) - assert len(scorers) == 1 - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_build_scorers_unknown_skipped(self, mock_openai_cls): - evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) - scorers = evaluator._build_scorers(["nonexistent_metric"]) - assert len(scorers) == 0 - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_get_guidelines_builtin(self, mock_openai_cls): - evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) - guidelines = evaluator._get_guidelines(MetricType.FAITHFULNESS) - assert guidelines is not None - assert "grounded" in guidelines - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_get_guidelines_custom(self, mock_openai_cls): - config = LLMJudgeConfig(custom_metrics=[CustomMetricDefinition(name="custom", guidelines="Custom eval.")]) - evaluator = MlflowLLMJudgeEvaluator(config) - assert evaluator._get_guidelines("custom") == "Custom eval." - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_get_guidelines_unknown(self, mock_openai_cls): - evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) - assert evaluator._get_guidelines("nonexistent") is None - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_get_guidelines_stale_custom_metric_names(self, mock_openai_cls): - config = LLMJudgeConfig(custom_metrics=[CustomMetricDefinition(name="custom", guidelines="Custom eval.")]) - evaluator = MlflowLLMJudgeEvaluator(config) - config.custom_metrics.clear() - assert evaluator._get_guidelines("custom") is None - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_format_results(self, mock_openai_cls): - evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) - - mock_result = MagicMock() - mock_result.tables = { - "eval_results": pd.DataFrame( - { - "faithfulness/value": [0.75, 0.5, 1.0], - } - ) - } - - result = evaluator._format_results( - mock_result, - [MetricType.FAITHFULNESS], - ["q1", "q2", "q3"], - ) - - assert "scores" in result - assert "question_scores" in result - assert MetricType.FAITHFULNESS in result["scores"] - assert result["scores"][MetricType.FAITHFULNESS]["mean"] is not None - - q_scores = result["question_scores"][MetricType.FAITHFULNESS] - assert q_scores["q1"] == 0.75 - assert q_scores["q2"] == 0.5 - assert q_scores["q3"] == 1.0 - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_format_results_all_scores_in_0_1(self, mock_openai_cls): - evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) - - mock_result = MagicMock() - mock_result.tables = { - "eval_results": pd.DataFrame( - { - "answer_correctness/value": [0.0, 0.25, 0.5, 0.75, 1.0], - } - ) - } - - result = evaluator._format_results( - mock_result, - [MetricType.ANSWER_CORRECTNESS], - ["q1", "q2", "q3", "q4", "q5"], - ) - - for qid, score in result["question_scores"][MetricType.ANSWER_CORRECTNESS].items(): - assert 0.0 <= score <= 1.0, f"Score {score} for {qid} not in [0.0, 1.0]" - - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.mlflow") - @patch("ai4rag.evaluator.mlflow_llm_judge_evaluator.OpenAI") - def test_evaluate_metrics_full_flow(self, mock_openai_cls, mock_mlflow, sample_evaluation_data): - mock_client = MagicMock() - mock_openai_cls.return_value = mock_client - mock_client.chat.completions.create.side_effect = [ - _make_chat_response(4), - _make_chat_response(3), - ] - - mock_eval_result = MagicMock() - mock_eval_result.tables = { - "eval_results": pd.DataFrame( - { - "faithfulness/value": [0.75, 0.5], - } - ) - } - mock_mlflow.genai.evaluate.return_value = mock_eval_result - - evaluator = MlflowLLMJudgeEvaluator(LLMJudgeConfig()) - result = evaluator.evaluate_metrics( - sample_evaluation_data, - [MetricType.FAITHFULNESS], - ) - - assert "scores" in result - assert "question_scores" in result - assert MetricType.FAITHFULNESS in result["scores"] - - mean = result["scores"][MetricType.FAITHFULNESS]["mean"] - assert 0.0 <= mean <= 1.0 - - mock_mlflow.genai.evaluate.assert_called_once() - - def test_compute_confidence_interval(self): - scores = [0.8, 0.85, 0.9, 0.75, 0.88, 0.92, 0.7, 0.95] - ci_low, ci_high = MlflowLLMJudgeEvaluator._compute_confidence_interval(scores) - assert ci_low is not None - assert ci_high is not None - assert ci_low < ci_high - assert ci_low >= 0.0 - assert ci_high <= 1.0 - - def test_compute_confidence_interval_single_score(self): - ci_low, ci_high = MlflowLLMJudgeEvaluator._compute_confidence_interval([0.5]) - assert ci_low is None - assert ci_high is None - - def test_compute_confidence_interval_empty(self): - ci_low, ci_high = MlflowLLMJudgeEvaluator._compute_confidence_interval([]) - assert ci_low is None - assert ci_high is None - - -class TestMetricTypeExtensions: - def test_answer_relevance_metric_exists(self): - assert MetricType.ANSWER_RELEVANCE == "answer_relevance" - - def test_answer_relevance_in_iteration(self): - assert "answer_relevance" in list(MetricType) - - def test_answer_relevance_in_containment(self): - assert "answer_relevance" in MetricType - - def test_original_metrics_still_present(self): - assert "answer_correctness" in MetricType - assert "faithfulness" in MetricType - assert "context_correctness" in MetricType diff --git a/tests/unit/ai4rag/evaluator/test_score_utils.py b/tests/unit/ai4rag/evaluator/test_score_utils.py new file mode 100644 index 00000000..79b04f01 --- /dev/null +++ b/tests/unit/ai4rag/evaluator/test_score_utils.py @@ -0,0 +1,28 @@ +# ----------------------------------------------------------------------------- +# Copyright IBM Corp. 2026 +# SPDX-License-Identifier: Apache-2.0 +# ----------------------------------------------------------------------------- +import numpy as np + +from ai4rag.evaluator.score_utils import enrich_with_overall_score + + +def test_enrich_with_overall_score(): + result = enrich_with_overall_score( + { + "scores": { + "faithfulness": {"mean": 0.8, "ci_low": 0.7, "ci_high": 0.9}, + "answer_correctness": {"mean": 0.6, "ci_low": 0.5, "ci_high": 0.7}, + "context_correctness": {"mean": 0.4, "ci_low": 0.3, "ci_high": 0.5}, + }, + "question_scores": { + "faithfulness": {"q1": 0.8, "q2": 1.0}, + "answer_correctness": {"q1": 0.6, "q2": 0.8}, + "context_correctness": {"q1": 0.4, "q2": 0.6}, + }, + } + ) + + assert result["scores"]["overall_score"]["mean"] == round(float(np.mean([0.6, 0.8])), 4) + assert result["question_scores"]["overall_score"]["q1"] == 0.6 + assert result["question_scores"]["overall_score"]["q2"] == 0.8 diff --git a/tests/unit/ai4rag/evaluator/test_unitxt_evaluator.py b/tests/unit/ai4rag/evaluator/test_unitxt_evaluator.py index 1296287f..8fc09090 100644 --- a/tests/unit/ai4rag/evaluator/test_unitxt_evaluator.py +++ b/tests/unit/ai4rag/evaluator/test_unitxt_evaluator.py @@ -429,8 +429,10 @@ def test_evaluate_metrics_with_all_metrics(self, mocker, sample_evaluation_data_ [MetricType.ANSWER_CORRECTNESS, MetricType.FAITHFULNESS, MetricType.CONTEXT_CORRECTNESS], ) - assert len(result["scores"]) == 3 - assert len(result["question_scores"]) == 3 + assert len(result["scores"]) == 4 + assert "overall_score" in result["scores"] + assert "overall_score" in result["question_scores"] + assert len(result["question_scores"]) == 4 class TestUnitxtEvaluatorIntegration: diff --git a/uv.lock b/uv.lock index 0658439e..28ac81e2 100644 --- a/uv.lock +++ b/uv.lock @@ -2,8 +2,10 @@ version = 1 revision = 3 requires-python = ">=3.12, <3.14" resolution-markers = [ - "python_full_version >= '3.13'", - "python_full_version < '3.13'", + "python_full_version >= '3.13' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform != 'darwin'", + "python_full_version < '3.13' and sys_platform == 'darwin'", + "python_full_version < '3.13' and sys_platform != 'darwin'", ] [[package]] @@ -28,10 +30,13 @@ wheels = [ name = "ai4rag" source = { editable = "." } dependencies = [ - { name = "docling-core", extra = ["chunking", "chunking-openai"] }, + { name = "boto3" }, + { name = "docling" }, + { name = "docling-core", extra = ["chunking"] }, { name = "langchain" }, { name = "langchain-chroma" }, { name = "langchain-text-splitters" }, + { name = "multiprocess" }, { name = "ogx-client" }, { name = "pandas" }, { name = "pydantic" }, @@ -49,17 +54,14 @@ code-check = [ dev = [ { name = "beautifulsoup4" }, { name = "black" }, - { name = "docling" }, { name = "dotenv" }, { name = "ipykernel" }, { name = "isort" }, - { name = "mike" }, { name = "mkdocs" }, { name = "mkdocs-git-revision-date-localized-plugin" }, { name = "mkdocs-material" }, { name = "mkdocs-minify-plugin" }, { name = "mkdocstrings", extra = ["python"] }, - { name = "mlflow" }, { name = "nbformat" }, { name = "openai" }, { name = "psutil" }, @@ -69,7 +71,6 @@ dev = [ { name = "pytest-mock" }, ] docs = [ - { name = "mike" }, { name = "mkdocs" }, { name = "mkdocs-git-revision-date-localized-plugin" }, { name = "mkdocs-material" }, @@ -77,7 +78,6 @@ docs = [ { name = "mkdocstrings", extra = ["python"] }, ] llm-judge = [ - { name = "mlflow" }, { name = "openai" }, ] test = [ @@ -96,24 +96,24 @@ requires-dist = [ { name = "ai4rag", extras = ["test"], marker = "extra == 'dev'" }, { name = "beautifulsoup4", marker = "extra == 'dev'" }, { name = "black", marker = "extra == 'code-check'" }, - { name = "docling", marker = "extra == 'dev'" }, - { name = "docling-core", extras = ["chunking", "chunking-openai"], specifier = "~=2.74.1" }, + { name = "boto3", specifier = ">=1.28" }, + { name = "docling", specifier = "~=2.107.0" }, + { name = "docling-core", extras = ["chunking"], specifier = "~=2.84.0" }, { name = "dotenv", marker = "extra == 'dev'" }, { name = "ipykernel", marker = "extra == 'dev'" }, { name = "isort", marker = "extra == 'code-check'" }, { name = "langchain", specifier = "~=1.1.3" }, { name = "langchain-chroma", specifier = "~=1.1.0" }, { name = "langchain-text-splitters", specifier = "~=1.1.0" }, - { name = "mike", marker = "extra == 'docs'", specifier = ">=2.0.0" }, { name = "mkdocs", marker = "extra == 'docs'", specifier = "~=1.6.0" }, { name = "mkdocs-git-revision-date-localized-plugin", marker = "extra == 'docs'", specifier = ">=1.2.0" }, { name = "mkdocs-material", marker = "extra == 'docs'", specifier = "~=9.5" }, { name = "mkdocs-minify-plugin", marker = "extra == 'docs'", specifier = ">=0.8.0" }, { name = "mkdocstrings", extras = ["python"], marker = "extra == 'docs'", specifier = ">=0.25.0" }, - { name = "mlflow", marker = "extra == 'llm-judge'", specifier = ">=3.0.0" }, + { name = "multiprocess", specifier = ">=0.70" }, { name = "nbformat", marker = "extra == 'test'" }, - { name = "ogx-client", specifier = "~=1.0.0" }, - { name = "openai", marker = "extra == 'llm-judge'", specifier = ">=1.0.0" }, + { name = "ogx-client", specifier = "~=1.1.0" }, + { name = "openai", marker = "extra == 'llm-judge'", specifier = "~=1.109.1" }, { name = "pandas", specifier = "==2.2.*" }, { name = "psutil", marker = "extra == 'test'" }, { name = "pydantic", specifier = "==2.11.*" }, @@ -200,20 +200,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] -[[package]] -name = "alembic" -version = "1.18.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mako" }, - { name = "sqlalchemy" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/94/13/8b084e0f2efb0275a1d534838844926f798bd766566b1375174e2448cd31/alembic-1.18.4.tar.gz", hash = "sha256:cb6e1fd84b6174ab8dbb2329f86d631ba9559dd78df550b57804d607672cedbc", size = 2056725, upload-time = "2026-02-10T16:00:47.195Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/29/6533c317b74f707ea28f8d633734dbda2119bbadfc61b2f3640ba835d0f7/alembic-1.18.4-py3-none-any.whl", hash = "sha256:a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", size = 263893, upload-time = "2026-02-10T16:00:49.997Z" }, -] - [[package]] name = "annotated-doc" version = "0.0.4" @@ -402,12 +388,31 @@ wheels = [ ] [[package]] -name = "blinker" -version = "1.9.0" +name = "boto3" +version = "1.43.41" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460, upload-time = "2024-11-08T17:25:47.436Z" } +dependencies = [ + { name = "botocore" }, + { name = "jmespath" }, + { name = "s3transfer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/5b/e91af651bf4e902f86b9e2f8bc09ca20dbd1ad3c1e21b70bf34651cf3cee/boto3-1.43.41.tar.gz", hash = "sha256:0f56811f13677bfb4542daa0cce8532c95d9afd27b4ba7b681af36a0568624ad", size = 112677, upload-time = "2026-07-06T19:39:38.788Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/4a/babb2de16f6ff0688697d2eac7d95566151c255f6b08f547306f57dabfc2/boto3-1.43.41-py3-none-any.whl", hash = "sha256:f48f862d2720ea9203ed2d842d436b8eb2d459ea31654a7ad7c0756fdf36c6b2", size = 140029, upload-time = "2026-07-06T19:39:37.125Z" }, +] + +[[package]] +name = "botocore" +version = "1.43.41" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/a1/48a0f38b0cac8196764607c0bca7e3ca50d3cffc825087b743d3635413f2/botocore-1.43.41.tar.gz", hash = "sha256:27627d79af0df7dcb7ecf78d8d3d1310da09a5e9460be30bf759f1c2ed095ee8", size = 15647567, upload-time = "2026-07-06T19:39:27.119Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458, upload-time = "2024-11-08T17:25:46.184Z" }, + { url = "https://files.pythonhosted.org/packages/61/4f/19e0b97ce1801c66a4d1d35117f36240aa20f864338f093fccc23873a231/botocore-1.43.41-py3-none-any.whl", hash = "sha256:0cc6e79b30a2a98374f16a31cd9c7a9106a51b60650bd8c34cc8223f58ae6b8d", size = 15331199, upload-time = "2026-07-06T19:39:23.694Z" }, ] [[package]] @@ -415,7 +420,7 @@ name = "build" version = "1.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "os_name == 'nt'" }, + { name = "colorama", marker = "os_name == 'nt' and sys_platform != 'darwin'" }, { name = "packaging" }, { name = "pyproject-hooks" }, ] @@ -424,15 +429,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl", hash = "sha256:13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f", size = 26018, upload-time = "2026-04-30T03:18:23.644Z" }, ] -[[package]] -name = "cachetools" -version = "7.1.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/8b/0d3945a13955303b81272f759a0331e54c5c793da455e6f5706b89d2639c/cachetools-7.1.4.tar.gz", hash = "sha256:437f55a4e0c1b01a4f3077cc470e6991d47430970e36fbcb77e2be0df4fc1cd6", size = 40085, upload-time = "2026-05-21T22:40:43.376Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/7b/1fc1c09cc0756cf25861a3be10565915953876da48bb228fb9a672b20a42/cachetools-7.1.4-py3-none-any.whl", hash = "sha256:323dc4127934744db5b54eb4924482d7edafbf9554e820d1531c2e08c0e4ef54", size = 16761, upload-time = "2026-05-21T22:40:41.845Z" }, -] - [[package]] name = "certifi" version = "2026.4.22" @@ -572,15 +568,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, ] -[[package]] -name = "cloudpickle" -version = "3.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -611,50 +598,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, ] -[[package]] -name = "contourpy" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, - { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, - { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, - { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, - { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, - { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, - { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, - { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, - { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, - { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, - { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, - { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, - { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, - { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, - { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, - { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, - { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, - { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, - { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, - { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, - { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, - { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, - { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, - { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, - { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, - { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, - { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, - { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, - { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, - { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, - { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, -] - [[package]] name = "coverage" version = "7.13.5" @@ -709,45 +652,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" }, ] -[[package]] -name = "cryptography" -version = "48.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/12/45/870e7f4bef50e5f53b9f51d4428aee5290eedf58ba443f16b1ebb7ab8e66/cryptography-48.0.1.tar.gz", hash = "sha256:266f4ee051abb2f725b74ef8072b521ce1feacf685a3364fa6a6b45548db791a", size = 832989, upload-time = "2026-06-09T22:32:31.8Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/bc/ee4137cbbe105652c0ee4252792b78fc8e7afa4b8e61d9d5dc05a7f45731/cryptography-48.0.1-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3e4a1a3232eef2e6c732827d5722db29a0cc8b27af2a4d865b094cf954be9ca1", size = 8008324, upload-time = "2026-06-09T22:31:00.702Z" }, - { url = "https://files.pythonhosted.org/packages/d5/85/6379d42181bfc713094f081360fc5784d6c816b599d45e7f082502d173ce/cryptography-48.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:32143b24adb918f078134e1e230f1eb8cc04886b92c28b5f0041aaf3e5699225", size = 4696243, upload-time = "2026-06-09T22:32:33.446Z" }, - { url = "https://files.pythonhosted.org/packages/9c/87/c85d147b53323c7eb4d850920c8901377323c2a0ff8d79c262d4fee89aa2/cryptography-48.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0d27a5696721ef7a672b8c810f6aded391058e0b9486e63e6d93baf765da691", size = 4713235, upload-time = "2026-06-09T22:31:40.141Z" }, - { url = "https://files.pythonhosted.org/packages/79/58/67cbf8cf1ee7c54b439ca07bbecf8362c07afc11a3724fea70f745784add/cryptography-48.0.1-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb86ce1af36fe65041b6db9a8bb064ee621a7e5fded0f80d475ec243477cd242", size = 4702323, upload-time = "2026-06-09T22:31:42.191Z" }, - { url = "https://files.pythonhosted.org/packages/89/c6/24266ac10c47f6cd2a865f4446062b466da1d1f10b27189eac00e61bf0c9/cryptography-48.0.1-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b024e784ad6c077ee0147b35ea9cbfc1e34e1fd4c1dcca214c2794d73a12df08", size = 5300085, upload-time = "2026-06-09T22:31:58.703Z" }, - { url = "https://files.pythonhosted.org/packages/d2/bb/cc4b78784f97efc8c5874c2a9743708d172be6663024b34a0467885ae0c8/cryptography-48.0.1-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3752f2dbc8f07a30aad2932c986cea495b03bb554887828225da104f732852b6", size = 4746137, upload-time = "2026-06-09T22:31:31.01Z" }, - { url = "https://files.pythonhosted.org/packages/1f/52/0c44de3f5267f8fbe8e835138017522a333436166e406f0db9b9e6e3033f/cryptography-48.0.1-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:bd81490cd5801d755cf97bb68ac191f14b708470b1c7cf4580f669b9c9264cd8", size = 4333867, upload-time = "2026-06-09T22:32:28.096Z" }, - { url = "https://files.pythonhosted.org/packages/9a/2e/772d7adbfa931537bc401640b7cac9976bff689bda187833e5d63b428e49/cryptography-48.0.1-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:66fd0771e7b9c6dcd44cf1120690d2338d16d72795cf40cae2786a39eba65429", size = 4701805, upload-time = "2026-06-09T22:31:38.284Z" }, - { url = "https://files.pythonhosted.org/packages/f8/a3/b06844f303873493c963caf581c04df31c7035e0c1b0f02c4814d319ec80/cryptography-48.0.1-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:3fd2ca57062b241c856670b073487d2e86c4637937ca5601e48f97bf8e11fc8f", size = 5258461, upload-time = "2026-06-09T22:31:04.187Z" }, - { url = "https://files.pythonhosted.org/packages/9f/13/8b765e2e12b07c74941caadb9d1c8fdc006c4dfbf2b8f2d610519758954d/cryptography-48.0.1-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:0ee6ea481db1ab889cba043ec1eda17bb9c1ea79db6722f779c3667f9f70322f", size = 4745488, upload-time = "2026-06-09T22:32:30.07Z" }, - { url = "https://files.pythonhosted.org/packages/2e/aa/48972bce55049b32a94f4907eda4d75fa385aad8a39506cc2fc72196ecf0/cryptography-48.0.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f2ceef93cb096aa3c4cc4b5c94ca6131f9196d28c64d6111533402a9b2054d41", size = 4830256, upload-time = "2026-06-09T22:31:43.868Z" }, - { url = "https://files.pythonhosted.org/packages/47/a2/e5079a032fb85cf6005046ca92bbd78b0c82dad2b5751ab8c311659da06f/cryptography-48.0.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bd3f92d76217892b15df84ca256c2c113d386fdda7a7d8691aeeced976507c6", size = 4979117, upload-time = "2026-06-09T22:31:05.845Z" }, - { url = "https://files.pythonhosted.org/packages/b7/a0/8f50cae9c74e718ed769d63ed5c74bd0ea830c9550a74629cebd1b9c7bc7/cryptography-48.0.1-cp311-abi3-win32.whl", hash = "sha256:b9a32b876490d66c8bcc9963ef220199569748434ab01a9d6aaeabf88e7f5158", size = 3304154, upload-time = "2026-06-09T22:32:16.845Z" }, - { url = "https://files.pythonhosted.org/packages/c5/69/0572c77dbace6fef72f33755bd52ea399c71367250d366237f8691826b9e/cryptography-48.0.1-cp311-abi3-win_amd64.whl", hash = "sha256:39489bfca54c7a1f6b297efcd8bc608ab92d16c4ca631b0cad4da46724588b24", size = 3817138, upload-time = "2026-06-09T22:32:00.388Z" }, - { url = "https://files.pythonhosted.org/packages/ca/6c/00fa2a95997164c8b2072ce327c23d4ab20809ccc323ea5fab91e53a4bba/cryptography-48.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:4fdc69f8e4316bcf0c8c8ec1f26f285d12e8142d88d96c876a59a03be3f6ae67", size = 7987408, upload-time = "2026-06-09T22:32:20.777Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d9/45f309a7e4e5f3f8f121d6d3be9e94024a7726ec598d6e08ae04edb2f04d/cryptography-48.0.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48fe40804d4caa2288f24e70ca8c64c42dd826da0ad7e4f1b41b2128d679e6c8", size = 4690196, upload-time = "2026-06-09T22:31:54.74Z" }, - { url = "https://files.pythonhosted.org/packages/5f/9f/a1bc8bcc798811b8527eb374bbccf30a3f3e806829d967118222bf1125eb/cryptography-48.0.1-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:86be3b1b0b6bf09482fb50a979c508d2950ed95f5621ec77f4e385962006b83a", size = 4696782, upload-time = "2026-06-09T22:31:45.615Z" }, - { url = "https://files.pythonhosted.org/packages/66/c2/81a4fb4e4373c500bb526bc337ac5719dd31dd15b970b84a238168c6aa08/cryptography-48.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:4ab0a343c807bbcd90c971cd1ecf072937cd01847a9e002bef88fb47ac6be577", size = 4696618, upload-time = "2026-06-09T22:31:11.564Z" }, - { url = "https://files.pythonhosted.org/packages/e5/0b/aa68b221dde92d09cb29a024ede17550ee21e77a404e59fc093c82bb51e1/cryptography-48.0.1-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9621de99d2da096006b629979efd8ae7eb2d8b822488d0c89ee4000c306c59b1", size = 5289970, upload-time = "2026-06-09T22:31:20.368Z" }, - { url = "https://files.pythonhosted.org/packages/78/13/fba657f958d2af66ea959a4ba01212632089249d34af1ae48054136344d7/cryptography-48.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:88c852a0ae366e262e5a1744b685e6a433dc8788dd2a277e418bf4904203609d", size = 4731873, upload-time = "2026-06-09T22:31:22.253Z" }, - { url = "https://files.pythonhosted.org/packages/4c/4c/9a964756d24a26b3e34dfcb16f961b89838786e6700b635b0d1e3adff4b6/cryptography-48.0.1-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:43c5835e2cb98c8733d86f57d6fc879b613f5c3478607281c3e36daffc6dd8a6", size = 4330804, upload-time = "2026-06-09T22:31:36.56Z" }, - { url = "https://files.pythonhosted.org/packages/4b/0f/a10f3a6eb12950a10e3a874070283aa2dd5875b2bfd15fad8a3e17b3f13e/cryptography-48.0.1-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:fe0180af5bf9236518a087e35bf2d9a347d5f5f51e63c579d683ddff424e3d46", size = 4696217, upload-time = "2026-06-09T22:31:13.351Z" }, - { url = "https://files.pythonhosted.org/packages/f3/6f/5cd12f951165ea73ef85266775d97e4c763b2474ccfd816dd69d3a18d6f8/cryptography-48.0.1-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:b7a2d1a937a738a881737cec135a38bb61470589b17515b9f73f571d0ae10401", size = 5245252, upload-time = "2026-06-09T22:32:02.193Z" }, - { url = "https://files.pythonhosted.org/packages/68/ab/8aaa12e4516ec4464033ab79b6f3b592bd5a92102467c4ace8a0d970203f/cryptography-48.0.1-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b74ca3b8e5ecdd833bf6a002ca41b4793bb27fb8f1c06ffaf2643c9e9140e31b", size = 4731388, upload-time = "2026-06-09T22:32:04.019Z" }, - { url = "https://files.pythonhosted.org/packages/1b/24/50027ea4dca85ec1f40688f3c24fb32ccacd520583c9592c3cc95628e6fb/cryptography-48.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2c37f2461406063b417837f5f3daab668652acd82423efcd7f0a9f04be972de1", size = 4824186, upload-time = "2026-06-09T22:32:18.707Z" }, - { url = "https://files.pythonhosted.org/packages/52/41/04cb5eb17085ade6f50cc611fb657df6a0f5885350de8764ece89c050197/cryptography-48.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:86fe77abb1bd87afb251d4d02ada7ecf53a32cee9b67d976abb2e45a13297475", size = 4964539, upload-time = "2026-06-09T22:31:18.793Z" }, - { url = "https://files.pythonhosted.org/packages/36/bf/ed70785c496e89d7e73b7cda2d21f2447fd6d4e821714b8d04ff217fed92/cryptography-48.0.1-cp39-abi3-win32.whl", hash = "sha256:6b2c0c3e6ccf3ade7750f836ef3ee36eea250cc467d45c256895573ac08cc6f1", size = 3282307, upload-time = "2026-06-09T22:30:53.162Z" }, - { url = "https://files.pythonhosted.org/packages/b3/ff/371ea7d252656ee1eb6d83eeeef3d1d0c6baf1d6497687d081ea03814670/cryptography-48.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:9a49ca6c81417f6a5edb50375a60cccdd70fa0a91a5211829dbea74eba94d2ac", size = 3793408, upload-time = "2026-06-09T22:32:15.191Z" }, -] - [[package]] name = "csscompressor" version = "0.9.5" @@ -759,7 +663,7 @@ name = "cuda-bindings" version = "13.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cuda-pathfinder" }, + { name = "cuda-pathfinder", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/52/c8/b2589d68acf7e3d63e2be330b84bc25712e97ed799affbca7edd7eae25d6/cuda_bindings-13.2.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e865447abfb83d6a98ad5130ed3c70b1fc295ae3eeee39fd07b4ddb0671b6788", size = 5722404, upload-time = "2026-03-11T00:12:44.041Z" }, @@ -819,30 +723,6 @@ nvtx = [ { name = "nvidia-nvtx", marker = "sys_platform == 'linux' or sys_platform == 'win32'" }, ] -[[package]] -name = "cycler" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, -] - -[[package]] -name = "databricks-sdk" -version = "0.117.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-auth" }, - { name = "protobuf" }, - { name = "requests" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/95/86/fe149c523a9181deb3a90ae4e83708f54a6ee80c40ca392cf66ffdf425df/databricks_sdk-0.117.0.tar.gz", hash = "sha256:30a6fc21a8f9c4a41830c43332ae63b38c3679a83ac1bda4734ce7ced114faf1", size = 989404, upload-time = "2026-06-11T18:19:29.034Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/92/4d/3e28a5a1ae9aa42237bd0f4e6b34867b554c046a0f3e7c2ef49fa74800f6/databricks_sdk-0.117.0-py3-none-any.whl", hash = "sha256:bafd588c067ee353c905e56cd2eb37c7eca7a56a4c1b57656621b77208c32f86", size = 936854, upload-time = "2026-06-11T18:19:27.395Z" }, -] - [[package]] name = "datasets" version = "4.8.5" @@ -930,35 +810,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] -[[package]] -name = "docker" -version = "7.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "requests" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/91/9b/4a2ea29aeba62471211598dac5d96825bb49348fa07e906ea930394a83ce/docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c", size = 117834, upload-time = "2024-05-23T11:13:57.216Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" }, -] - [[package]] name = "docling" -version = "2.93.0" +version = "2.107.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docling-slim", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/df/e7/41a7ef58935b914332a7924e0a9d817d50214201aeca7507944404229161/docling-2.93.0.tar.gz", hash = "sha256:c8b4455466d9a6314c27cf84debfdf491532298f65364ec3449acaad517bfea6", size = 8726, upload-time = "2026-05-07T11:55:38.745Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/b3/c0563269a680c4f3cf2b5814b8a2cb7e490d26343a25834124808896d1b3/docling-2.107.0.tar.gz", hash = "sha256:cabd688c00681361d13ccf00d802e4ef4e0514fcf6e9088709d58c10fbef08c1", size = 8946, upload-time = "2026-06-24T13:13:21.841Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/6f/3befa6e4cd42f3a9cf284165d18ca1039ab676ab36da327242aa701e6b63/docling-2.93.0-py3-none-any.whl", hash = "sha256:30a2dc2db733d6a24095e624cc133ce67e9edc46e778f982cf719e137bcee200", size = 4827, upload-time = "2026-05-07T11:55:37.457Z" }, + { url = "https://files.pythonhosted.org/packages/db/4d/f4ce1777977290f112b561b28862e6205bf4b419d09f9506067c007fd21f/docling-2.107.0-py3-none-any.whl", hash = "sha256:526bb6f28650174e5a4b0bcd341876cf9a753719d98cd93f2512ca008965e382", size = 5111, upload-time = "2026-06-24T13:13:20.505Z" }, ] [[package]] name = "docling-core" -version = "2.74.1" +version = "2.84.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "defusedxml" }, @@ -974,9 +840,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e8/94/4856d01de4afd565d0ea12198f157f634cf65dc373a4ee97cb0d789f8554/docling_core-2.74.1.tar.gz", hash = "sha256:46bf298686f2c51ddd69b6935a27dff1cc80838f2f5f1a8823492d99cf1a357b", size = 319269, upload-time = "2026-04-22T14:33:45.241Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/bc/41d9d5a38502538a7cd3c7fb66a06eb676f8cc1d6d275d7fbe971352efd7/docling_core-2.84.0.tar.gz", hash = "sha256:331035d5032be683a6d66d476146652491d2c75e4562e04da3f1a8d989d74bfc", size = 317993, upload-time = "2026-06-23T09:44:42.339Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/c3/5f8c9a9b0cad437cb7e6ffcd48ccc309b2290cea6aac067569bf61b84743/docling_core-2.74.1-py3-none-any.whl", hash = "sha256:e6464078012b3d45f4e0accd101fcb277063903f355eabbb9aee8de00527a789", size = 276973, upload-time = "2026-04-22T14:33:43.366Z" }, + { url = "https://files.pythonhosted.org/packages/50/d4/a8742fdd4c8b782608005ffde77a24bbfd54602c4c49825bf04ea25363ab/docling_core-2.84.0-py3-none-any.whl", hash = "sha256:2aee881b94138234c703298066b5ed94999490323ae6a29cbe5110f9248b83dd", size = 257936, upload-time = "2026-06-23T09:44:40.506Z" }, ] [package.optional-dependencies] @@ -989,15 +855,6 @@ chunking = [ { name = "tree-sitter-python" }, { name = "tree-sitter-typescript" }, ] -chunking-openai = [ - { name = "semchunk" }, - { name = "tiktoken" }, - { name = "tree-sitter" }, - { name = "tree-sitter-c" }, - { name = "tree-sitter-javascript" }, - { name = "tree-sitter-python" }, - { name = "tree-sitter-typescript" }, -] [[package]] name = "docling-ibm-models" @@ -1025,30 +882,29 @@ wheels = [ [[package]] name = "docling-parse" -version = "5.10.1" +version = "7.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docling-core" }, { name = "pillow" }, { name = "pydantic" }, { name = "pywin32", marker = "sys_platform == 'win32'" }, - { name = "tabulate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/b8/e68f8ec44692d2f913210dd46cb3e7e6e1959053bb05d5c94c5331010f3c/docling_parse-5.10.1.tar.gz", hash = "sha256:10a3d2ba211134f6d1fa9b6be8ef690eb0b1a03b043473a3ef8408ad7b4a857a", size = 6651696, upload-time = "2026-04-24T15:02:19.106Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7a/28/feffb968ddb332b1d2c2a5d2b9be47e36f9ab8121367ff9781f4ef0f43ae/docling_parse-7.0.0.tar.gz", hash = "sha256:bc7710a13d0e0619ee288499ac163637f9356425781f4bed83c3e6a061cb86d4", size = 6682303, upload-time = "2026-06-22T10:17:58.181Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/4a/27e213493bac0877a030f030d44152ba9ef676aebc5890f4dd3e8037592e/docling_parse-5.10.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8f58e1bf1c6cdf1bfe0594f0903b4b9c33dfc3c2dbba61681f8533158ed640a6", size = 9112936, upload-time = "2026-04-24T15:01:44.878Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b3/85737cecca0e5ed9dc13370e78862054075f64eb988024069296898ec741/docling_parse-5.10.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:59880a29231083c17a73533e09abc0610f10a99343762d795de4ceac5b15dfbf", size = 9780913, upload-time = "2026-04-24T15:01:47.626Z" }, - { url = "https://files.pythonhosted.org/packages/17/fa/11dd3328ab708143a291ae53d388a5170095a90b8dc8428e5274e2c09194/docling_parse-5.10.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:478ada90c52b704a04a3c8b4171e3385bb8b5b2f02b9d57c7a5bb06d9cac34fa", size = 10159004, upload-time = "2026-04-24T15:01:50.366Z" }, - { url = "https://files.pythonhosted.org/packages/e7/73/20384b93e220bbeee09b0bc3978907afda1a13599d7a9990614ad1c682ed/docling_parse-5.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:e8e4ae0929b55301c59252453ce87406c003229adac16258c4b6eb31a76d5cb5", size = 10912188, upload-time = "2026-04-24T15:01:52.968Z" }, - { url = "https://files.pythonhosted.org/packages/65/99/46e50685ff0d8b7ab7eb39a7425540771378b2dea04a094d1d6e85467e57/docling_parse-5.10.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:450a9dc433d511f647a178f4558624c4f274679e4ae847febe698b14842f536a", size = 9112935, upload-time = "2026-04-24T15:01:55.565Z" }, - { url = "https://files.pythonhosted.org/packages/5f/b1/a5a9132b3dc47f5d21be9b38f8f4e006b017af86fabaf0acac24bf5db122/docling_parse-5.10.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c1f9bdc5259dd78db70becbe7e53cc7f93ecfce53ed8886e7ad2fadcb7df17bc", size = 9781358, upload-time = "2026-04-24T15:01:57.992Z" }, - { url = "https://files.pythonhosted.org/packages/40/a5/53df8e581d4ab933c8f8bed4091716172c09fd2fd17a83f438a524659b91/docling_parse-5.10.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b0934853bd3bea3a193dcef7e22eca4087a8ec1664f8ea9b5bceb6dddcbb3759", size = 10158871, upload-time = "2026-04-24T15:02:00.765Z" }, - { url = "https://files.pythonhosted.org/packages/62/9b/f465a56a838b19e950e3f7bff46b94ccfdfce7e478c03f76f674d1a989a4/docling_parse-5.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:2c24dc14f45efa16d1882cd1bb5bcc48e3acff1fd5de1505abf95ad7f49950a8", size = 10911943, upload-time = "2026-04-24T15:02:03.131Z" }, + { url = "https://files.pythonhosted.org/packages/de/8b/ef2c2b7837d01474851f17829404796232e78204aa737a1cce5d29cf6f13/docling_parse-7.0.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:37ef02b683e3a36557d45c9038f4e43cc5a293e544df0ab3ea5db159f5a32b21", size = 9158273, upload-time = "2026-06-22T10:17:33.184Z" }, + { url = "https://files.pythonhosted.org/packages/05/6d/83d0e80bcd87d0810c3ef37d617b24a1af854af84f297a06d126d563b02e/docling_parse-7.0.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a453afacbf659133aae4072b708828e3f9c745215d23aeac70620f65ab71e239", size = 9827022, upload-time = "2026-06-22T10:17:35.003Z" }, + { url = "https://files.pythonhosted.org/packages/8d/e8/2be20897f8569d02c93a26390e517c1faa336a566a84a867f5a97ae89c5f/docling_parse-7.0.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da3a005be3e8a58795ab2f75cf2277be933c677c880e9ee58c33506cd69176ea", size = 10209846, upload-time = "2026-06-22T10:17:36.838Z" }, + { url = "https://files.pythonhosted.org/packages/66/a8/2c3054477eb7e171bb5f44eb9792b66c9f27ace2fabde5dcbada56085bda/docling_parse-7.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca0411a058ce8ac823dd9b0e665917f3a53ac65b278390e052da868825513cef", size = 10973091, upload-time = "2026-06-22T10:17:39.312Z" }, + { url = "https://files.pythonhosted.org/packages/78/bc/b042eb64a18aabeab11ba16c46df64ac69590feabd267afecb1afc961794/docling_parse-7.0.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:b0982fff0d7a754d7a037bff1a3afd2f2b601dc5fb6f1d5ed7cfc9b24afe7e95", size = 9158220, upload-time = "2026-06-22T10:17:41.44Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5f/387cc3a3c48d7591594f165e997f471fd8958b019faa240ef42a3a1ccc58/docling_parse-7.0.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b9e9807fa8d1705ba589d1b935b8aa506c6260dede8d3c02f31e92fad7930b4", size = 9826867, upload-time = "2026-06-22T10:17:43.242Z" }, + { url = "https://files.pythonhosted.org/packages/4a/67/3bfe88293ae70cba56abb1751649ce9cd6ba2d4c26e9228f1bb6abbe222a/docling_parse-7.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fe5d5fcdd098166d0bf4f0b3c449aca26bf736c37fcad02179d61853a31ce4e3", size = 10210039, upload-time = "2026-06-22T10:17:45.155Z" }, + { url = "https://files.pythonhosted.org/packages/c0/fb/88fcc144e438d4588b994af1b71a0a0bc2a6a986f6f9b597fe6e697aa672/docling_parse-7.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:c386c18bb4f7aba42bec227c4efeeb99468a1b5ca3a7886a2dd08dec75d0b5fa", size = 10973044, upload-time = "2026-06-22T10:17:47.557Z" }, ] [[package]] name = "docling-slim" -version = "2.93.0" +version = "2.107.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -1060,9 +916,9 @@ dependencies = [ { name = "requests" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/51/2ee874abcd62b990f0a86abec2e3b87b4cc00731b675ed446fefff6199d9/docling_slim-2.93.0.tar.gz", hash = "sha256:2962f4fc5bdf9dd6d67d6f36f09334f7187039985c0fd4b2d4b1d375e4799157", size = 390036, upload-time = "2026-05-07T11:54:14.562Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/54/639a7517fcd128d0a488c71e033c194f72e105bc77ca18c50f8ccd3861da/docling_slim-2.107.0.tar.gz", hash = "sha256:bd89476fae7558c68a2f5b61f9afead0afc6797fb7bd74a8a19e2b0e8ec49e07", size = 476596, upload-time = "2026-06-24T13:11:55.64Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/3f/f2195f79a62fd6cd10c768c04c758c9d6bd0c804a46175d0f3f10a784fca/docling_slim-2.93.0-py3-none-any.whl", hash = "sha256:98e3db67f7976f051f132e6a6e04f73e7fcd4017877eddf9e849e0969c39fbe7", size = 506046, upload-time = "2026-05-07T11:54:10.884Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ad/9d52dc9929f3128aee448815e3cbbf3a17601881def3dc8019e6fc1b1a6e/docling_slim-2.107.0-py3-none-any.whl", hash = "sha256:3aabe2dcd3bfec0c12162c9774d29ae17779ef6fc290d7f92a5791f93e94ea61", size = 604722, upload-time = "2026-06-24T13:11:53.611Z" }, ] [package.optional-dependencies] @@ -1076,6 +932,7 @@ standard = [ { name = "httpx" }, { name = "huggingface-hub" }, { name = "lxml" }, + { name = "mail-parser" }, { name = "marko" }, { name = "numpy" }, { name = "openpyxl" }, @@ -1084,6 +941,7 @@ standard = [ { name = "pylatexenc" }, { name = "pypdfium2" }, { name = "python-docx" }, + { name = "python-dotenv" }, { name = "python-pptx" }, { name = "rapidocr" }, { name = "rich" }, @@ -1167,22 +1025,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a7/a7/a600f8f30d4505e89166de51dd121bd540ab8e560e8cf0901de00a81de8c/faker-40.15.0-py3-none-any.whl", hash = "sha256:71ab3c3370da9d2205ab74ffb0fd51273063ad562b3a3bb69d0026a20923e318", size = 2004447, upload-time = "2026-04-17T20:05:25.437Z" }, ] -[[package]] -name = "fastapi" -version = "0.137.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "annotated-doc" }, - { name = "pydantic" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "typing-inspection" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d5/b1/e5b92c59d2c37817e77c1a8c2fc1f79cdcc04c68253e5406b43e3204cba7/fastapi-0.137.1.tar.gz", hash = "sha256:822360704230d9533d8d9475399613525968aa2f0b5bd2a3ccc9f18c88fd541c", size = 408293, upload-time = "2026-06-15T11:28:20.79Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/35/380b9a5922f4340e51c309cde09e5bd32e62f02302971bee30dc15aa0624/fastapi-0.137.1-py3-none-any.whl", hash = "sha256:64f6983c59e45c4b9fdc44e57cb8035c2451ee91ea8e8ec042aca37de7cf6b69", size = 121877, upload-time = "2026-06-15T11:28:19.523Z" }, -] - [[package]] name = "fastjsonschema" version = "2.21.2" @@ -1222,36 +1064,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/4c/93d0f85318da65923e4b91c1c2ff03d8a458cbefebe3bc612a6693c7906d/fire-0.7.1-py3-none-any.whl", hash = "sha256:e43fd8a5033a9001e7e2973bab96070694b9f12f2e0ecf96d4683971b5ab1882", size = 115945, upload-time = "2025-08-16T20:20:22.87Z" }, ] -[[package]] -name = "flask" -version = "3.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "blinker" }, - { name = "click" }, - { name = "itsdangerous" }, - { name = "jinja2" }, - { name = "markupsafe" }, - { name = "werkzeug" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/26/00/35d85dcce6c57fdc871f3867d465d780f302a175ea360f62533f12b27e2b/flask-3.1.3.tar.gz", hash = "sha256:0ef0e52b8a9cd932855379197dd8f94047b359ca0a78695144304cb45f87c9eb", size = 759004, upload-time = "2026-02-19T05:00:57.678Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/9c/34f6962f9b9e9c71f6e5ed806e0d0ff03c9d1b0b2340088a0cf4bce09b18/flask-3.1.3-py3-none-any.whl", hash = "sha256:f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", size = 103424, upload-time = "2026-02-19T05:00:56.027Z" }, -] - -[[package]] -name = "flask-cors" -version = "6.0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flask" }, - { name = "werkzeug" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/47/03/4e464a50860f9adf08b5c1d3479cb8ea1f12af2aa69535c7042c6e628135/flask_cors-6.0.5.tar.gz", hash = "sha256:30c5031552cd59f620ac0c8211dac45b345d3b2df310e7721879e4f46ef9c601", size = 101386, upload-time = "2026-06-08T20:20:17.765Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/55/5bb1a2d918e9f02f131e47a59032bae70e48050e986e941511fd737a935c/flask_cors-6.0.5-py3-none-any.whl", hash = "sha256:68fcf75693e961f3af26683b23c4b9a8fb6b64de17d20d0c37b95e8de7ab2ed8", size = 16692, upload-time = "2026-06-08T20:20:16.247Z" }, -] - [[package]] name = "flatbuffers" version = "25.12.19" @@ -1260,31 +1072,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, ] -[[package]] -name = "fonttools" -version = "4.63.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131, upload-time = "2026-05-14T12:03:13.386Z" }, - { url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704, upload-time = "2026-05-14T12:03:15.801Z" }, - { url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298, upload-time = "2026-05-14T12:03:18.085Z" }, - { url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800, upload-time = "2026-05-14T12:03:20.161Z" }, - { url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666, upload-time = "2026-05-14T12:03:22.87Z" }, - { url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598, upload-time = "2026-05-14T12:03:25.168Z" }, - { url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575, upload-time = "2026-05-14T12:03:27.496Z" }, - { url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211, upload-time = "2026-05-14T12:03:30.057Z" }, - { url = "https://files.pythonhosted.org/packages/0f/8d/d8fec3dcde2963f8c908fb315e5ff2cd0ac34f82394bbbf73a2aa5145ce3/fonttools-4.63.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd7e9857e5e63738b9d9fd707bc1f59c8b09e5177726d23664db393c59bb08bd", size = 2876062, upload-time = "2026-05-14T12:03:32.554Z" }, - { url = "https://files.pythonhosted.org/packages/ef/71/d935dc54e4ff121bfdd11e08702db63a7e6f25af21d8a3d7b7212df53641/fonttools-4.63.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2a2a42198b696a6f48fad91709afb55176e66a5e566131219dba372fb7f8c59", size = 2424594, upload-time = "2026-05-14T12:03:34.86Z" }, - { url = "https://files.pythonhosted.org/packages/8e/40/e76320afa1df918e146155ef239b1719ee266092e96f5423bfd075affba1/fonttools-4.63.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e874792a8212b44583ea02189d9e693906b2f78b261f372f95d6c563210ac1d", size = 5024840, upload-time = "2026-05-14T12:03:36.745Z" }, - { url = "https://files.pythonhosted.org/packages/ce/36/0b805d8c485f872f65a509cbe3b58a5d0d17bee855333b54a150c79d3061/fonttools-4.63.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22135da48a348785c5e2d5d2d9d6bec5ed44adacbaeb9db12d9493bf6c6bfa68", size = 4975801, upload-time = "2026-05-14T12:03:38.833Z" }, - { url = "https://files.pythonhosted.org/packages/c8/26/2cee03d0aa083ab022da5c07aff9ed3f689da1defb81ad6917c9627896da/fonttools-4.63.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ccf41f2efdf56994d22d73bef4ced1052161958169428d06ba9724ea9e9a64be", size = 4965009, upload-time = "2026-05-14T12:03:41.494Z" }, - { url = "https://files.pythonhosted.org/packages/7e/48/cc4b66d9058c0d0982c833fad10127c4b0e9324606aafa41382295ca4102/fonttools-4.63.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9ced0bd02ac751dd6319b0da88aaef24414e3b0dbc32bb4f24944821a3741a27", size = 5105892, upload-time = "2026-05-14T12:03:43.525Z" }, - { url = "https://files.pythonhosted.org/packages/d8/1f/a98a30a814b9ddef3a2e706025f90b9e0bc94890e6cb15254bc86547d11a/fonttools-4.63.0-cp313-cp313-win32.whl", hash = "sha256:85be818f5506e8a7753153def2c9550178f0ecae6a47b5e0e8dbb23f7cc90380", size = 2291313, upload-time = "2026-05-14T12:03:45.594Z" }, - { url = "https://files.pythonhosted.org/packages/92/46/5177b01f3b4abfdd4409f31cca4ab279c9343a26efbe9ec78c97fc612e02/fonttools-4.63.0-cp313-cp313-win_amd64.whl", hash = "sha256:ba04cb5891d4c0c21b6da95eda8d7b090021508a294fff33464fc7d241e0856b", size = 2342299, upload-time = "2026-05-14T12:03:47.414Z" }, - { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" }, -] - [[package]] name = "frozenlist" version = "1.8.0" @@ -1392,19 +1179,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl", hash = "sha256:d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9", size = 212507, upload-time = "2026-05-06T04:01:23.799Z" }, ] -[[package]] -name = "google-auth" -version = "2.55.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography" }, - { name = "pyasn1-modules" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/81/1c/70b23fc52b2bb3c70b379f3bd05c4a60ab3a873e30c6bd21c57e0154848a/google_auth-2.55.0.tar.gz", hash = "sha256:fcd3a130f575fa36403d38774af1c64a4fbfbca09215f0589d2372b5119697cb", size = 349379, upload-time = "2026-06-15T22:33:16.466Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/71/c0321dc6d63d99946da45f7c06299b934e4f7f7da5c4f14d101bcb39adf1/google_auth-2.55.0-py3-none-any.whl", hash = "sha256:a17cef9dedf98c4ebae2fb0c48c8f75952c877cbc2efe09f329ef16c2783d88a", size = 252400, upload-time = "2026-06-15T22:33:14.992Z" }, -] - [[package]] name = "googleapis-common-protos" version = "1.75.0" @@ -1417,66 +1191,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/c8/e2645aa8ed02fd4c7a2f59d68783b65b1f3cbdfe39a6308e156509d1fee8/googleapis_common_protos-1.75.0-py3-none-any.whl", hash = "sha256:961ed60399c457ceb0ee8f285a84c870aabc9c6a832b9d37bb281b5bebde43ed", size = 300631, upload-time = "2026-05-07T08:03:30.345Z" }, ] -[[package]] -name = "graphene" -version = "3.4.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "graphql-core" }, - { name = "graphql-relay" }, - { name = "python-dateutil" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cc/f6/bf62ff950c317ed03e77f3f6ddd7e34aaa98fe89d79ebd660c55343d8054/graphene-3.4.3.tar.gz", hash = "sha256:2a3786948ce75fe7e078443d37f609cbe5bb36ad8d6b828740ad3b95ed1a0aaa", size = 44739, upload-time = "2024-11-09T20:44:25.757Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/66/e0/61d8e98007182e6b2aca7cf65904721fb2e4bce0192272ab9cb6f69d8812/graphene-3.4.3-py2.py3-none-any.whl", hash = "sha256:820db6289754c181007a150db1f7fff544b94142b556d12e3ebc777a7bf36c71", size = 114894, upload-time = "2024-11-09T20:44:23.851Z" }, -] - -[[package]] -name = "graphql-core" -version = "3.2.11" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4d/90/f2aff026ab4aebd80eb71905106a0885f4cfde85dcf965543f45bed0d9ee/graphql_core-3.2.11.tar.gz", hash = "sha256:e7e156d10beb127cab5c89ff0da71416fc73d27c484a4757d3b2d35633774802", size = 528407, upload-time = "2026-06-05T13:45:22.915Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/15/b92b4e1d88d02c6eff9733c9eea21846ab435cc4d813d84ccc5d335955df/graphql_core-3.2.11-py3-none-any.whl", hash = "sha256:0b3e35ff41e9adba53021ab0cef475eb18f57c7f53f0f2ca55567fbf3c537ea0", size = 214879, upload-time = "2026-06-05T13:45:21.245Z" }, -] - -[[package]] -name = "graphql-relay" -version = "3.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "graphql-core" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/13/98fbf8d67552f102488ffc16c6f559ce71ea15f6294728d33928ab5ff14d/graphql-relay-3.2.0.tar.gz", hash = "sha256:1ff1c51298356e481a0be009ccdff249832ce53f30559c1338f22a0e0d17250c", size = 50027, upload-time = "2022-04-16T11:03:45.447Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/74/16/a4cf06adbc711bd364a73ce043b0b08d8fa5aae3df11b6ee4248bcdad2e0/graphql_relay-3.2.0-py3-none-any.whl", hash = "sha256:c9b22bd28b170ba1fe674c74384a8ff30a76c8e26f88ac3aa1584dd3179953e5", size = 16940, upload-time = "2022-04-16T11:03:43.895Z" }, -] - -[[package]] -name = "greenlet" -version = "3.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/6e/802acd792aebb2256fbbee8cacf2727faaeb6f240ac11008f09eae4414bc/greenlet-3.5.1.tar.gz", hash = "sha256:5a56aeb7d5d9cc4b3a735efb5095bd4b4f6f0e4f93e5ca876d0e2315137b7829", size = 197356, upload-time = "2026-05-20T15:05:03.917Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/37/4549f149c9797c21b32c2683c33522af22522099de128b2406672526d005/greenlet-3.5.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:fa4f98af3a528f0c3fd592a26df7f376f93329c8f4d987f6bb979057af8bf5e2", size = 286220, upload-time = "2026-05-20T13:07:28.463Z" }, - { url = "https://files.pythonhosted.org/packages/38/ff/a4f436709716965eaab9f36ea7b906c8a927fbe32fb1372a2071d964f6b1/greenlet-3.5.1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ffea73584b216150eab159b6d12348fb253e68757974de1e2c40d8a318ac89ed", size = 601585, upload-time = "2026-05-20T14:00:06.141Z" }, - { url = "https://files.pythonhosted.org/packages/65/ad/54bc3fcee3ad368a61b19b67d88117f7a8c29727bf71fffdeda81fbd946e/greenlet-3.5.1-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1072b4f9edcc1e192d9283a66a3e68d6b84c561de33a83d7858beb9ba1effe10", size = 614215, upload-time = "2026-05-20T14:05:42.675Z" }, - { url = "https://files.pythonhosted.org/packages/40/69/b91cda0647df839483201545913514c2827ebea5e5ccdf931842763bc127/greenlet-3.5.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:add5217d68b31130f0beca584d7fef4878327d2e31642b66618a14eef312b63b", size = 611358, upload-time = "2026-05-20T13:14:26.37Z" }, - { url = "https://files.pythonhosted.org/packages/59/90/3cf77e080350cd02fa307bb2abf05df48f4482c240275bbd2c203ba8bb1c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a5ea42a752d47a145eae922b605cd1634665ac3d5ec1e72402d5048e8d60d207", size = 1570475, upload-time = "2026-05-20T14:02:25.29Z" }, - { url = "https://files.pythonhosted.org/packages/65/2c/18cece62045e74598c3c393f70dce4a63f56222015ba29a5d4eeb04f764c/greenlet-3.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5551170cf4f5ff5623e9af81323751979fee2c731e2287b61f73cd27257b823", size = 1635625, upload-time = "2026-05-20T13:14:34.027Z" }, - { url = "https://files.pythonhosted.org/packages/30/f5/310d104ddf41eb5a70f4c268d22508dfb0c3c8e86fec152be34d0d2ed819/greenlet-3.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c8bb982ad117d29478ef8f5533e97df21f1e2befd17a299257b0c96d1371c0b", size = 238791, upload-time = "2026-05-20T13:10:39.018Z" }, - { url = "https://files.pythonhosted.org/packages/62/90/ceca11f504cd23a8047a3dea31919adc48df9b626dd0c13f0d858734fdfd/greenlet-3.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:80eb4b04dadc4e67df3fae179a32c4706a3f495bc7f22fc8a81115d5f5512188", size = 235580, upload-time = "2026-05-20T13:08:45.056Z" }, - { url = "https://files.pythonhosted.org/packages/27/69/7f7e5372d998b81001899b1c0823c957aa413ba0f2662e65821611cc31e4/greenlet-3.5.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:51518ff74664078fc51bffcc6fc529b0df5ae58da192691cee765d45ce944a2b", size = 285060, upload-time = "2026-05-20T13:08:51.899Z" }, - { url = "https://files.pythonhosted.org/packages/b1/bf/387f9b6b865fd2ae0d0be09e0004827295a01b71be76ed350dd1e28a91a4/greenlet-3.5.1-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ffdb3c0bb002c99cd8f298957e046c3dbf6006b5b7cdf11a4e19194624a0a0a", size = 604370, upload-time = "2026-05-20T14:00:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/32/f5/169ce3d4e4c67291bd18f8cbe0299c9f3e45102c7f1fb3c14780c93e4532/greenlet-3.5.1-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7715a5a2c3378ba602c3a440558261e13a820bb53a82693aacd7b7f6d964e283", size = 616987, upload-time = "2026-05-20T14:05:44.237Z" }, - { url = "https://files.pythonhosted.org/packages/ee/e5/7f2e41d5273be07e77560d61ea4e56485b4d6c316d2a84518c62d1364061/greenlet-3.5.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc71ff466927a201b08305acac451ebe1aedfcea002f62f1f2f2ac2ac1e6a135", size = 613911, upload-time = "2026-05-20T13:14:27.539Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a4/fbdc67579b73615a1f91615e814303cc71e06128f7baaba87be79b8fb90c/greenlet-3.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cd443683db272ebaaca03af98c0b063ab30db70ea8a31a1559f35e3f7b744ccd", size = 1570689, upload-time = "2026-05-20T14:02:27.225Z" }, - { url = "https://files.pythonhosted.org/packages/e6/b4/77abbe35078be39718a46cd49caf16bceb35662f97a34101dca28aa98e47/greenlet-3.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:089fff7a6ce8d9316d1f65ebc00273a56be258c1725b32b94de90a3a979557e1", size = 1635602, upload-time = "2026-05-20T13:14:36.344Z" }, - { url = "https://files.pythonhosted.org/packages/37/f7/129f27ca700845b8ee8ca88ce7f43435a1239c2eddb7677fc938822762cf/greenlet-3.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:110a1ca7b49b014b097f6078272c3f4ed31af45b254de5228b79adba879f6af9", size = 238683, upload-time = "2026-05-20T13:11:50.57Z" }, - { url = "https://files.pythonhosted.org/packages/6d/5c/a485a36e87df8d8fd0632ee01511244f5156a20ed3746cc6599340326395/greenlet-3.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f16ba1efc0715b680a18b8123d90dad887c6112ae3555b4b5c32c149540c6b4e", size = 235499, upload-time = "2026-05-20T13:12:42.028Z" }, -] - [[package]] name = "griffelib" version = "2.0.2" @@ -1517,18 +1231,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cf/e6/283326a27da9e2c3038bc93eeea36fb118ce0b2d03922a9cda6688f53c5b/grpcio-1.80.0-cp313-cp313-win_amd64.whl", hash = "sha256:e172cf795a3ba5246d3529e4d34c53db70e888fa582a8ffebd2e6e48bc0cba50", size = 4882833, upload-time = "2026-03-30T08:48:07.363Z" }, ] -[[package]] -name = "gunicorn" -version = "26.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6d/b7/a4a3f632f823e432ce6bc65f62961b7980c898c77f075a2f7118cb3846fe/gunicorn-26.0.0.tar.gz", hash = "sha256:ca9346f85e3a4aeeb64d491045c16b9a35647abd37ea15efe53080eb8b090baf", size = 727286, upload-time = "2026-05-05T06:38:25.529Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/40/9c2384fc2be4ad25dd4a49decd5ad9ea5a3639814c11bd40ab77cb9f0a14/gunicorn-26.0.0-py3-none-any.whl", hash = "sha256:40233d26a5f0d1872916188c276e21641155111c2853f0c2cd55260aec0d24fc", size = 212009, upload-time = "2026-05-05T06:38:23.007Z" }, -] - [[package]] name = "h11" version = "0.16.0" @@ -1620,15 +1322,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] -[[package]] -name = "huey" -version = "3.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d1/cb/58f229149944602917a976d533d3fe7d54770d6ea18df5931e3f4f313fa0/huey-3.0.3.tar.gz", hash = "sha256:1a17fef95fc8432f75413f1b77439cef5f3493c1ddbfba9151756b31a1b2dad3", size = 263604, upload-time = "2026-06-12T01:53:55.49Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/82/f85d8918949786420716a5e421525ab12aa084bcbe32d86c9743e50bcf3d/huey-3.0.3-py3-none-any.whl", hash = "sha256:d1c687734778b8282c035a943eead8368c1736bb28abc006596fbbc01bdc96dc", size = 94945, upload-time = "2026-06-12T01:53:53.981Z" }, -] - [[package]] name = "huggingface-hub" version = "1.14.0" @@ -1755,15 +1448,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, ] -[[package]] -name = "itsdangerous" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173", size = 54410, upload-time = "2024-04-16T21:28:15.614Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", size = 16234, upload-time = "2024-04-16T21:28:14.499Z" }, -] - [[package]] name = "jedi" version = "0.20.0" @@ -1833,6 +1517,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c8/8d/302cb2057b7513327b4d575cff6b1d066ee6431a5357fc3f8867cd684406/jiter-0.15.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54d5d6090cdc1b7c9e780dfb04949a990adb1e301a2fc0bbcee7de4638d33f9a", size = 344469, upload-time = "2026-05-19T10:09:46.864Z" }, ] +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + [[package]] name = "joblib" version = "1.5.3" @@ -1946,62 +1639,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/e7/80988e32bf6f73919a113473a604f5a8f09094de312b9d52b79c2df7612b/jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407", size = 29032, upload-time = "2025-10-16T19:19:16.783Z" }, ] -[[package]] -name = "kiwisolver" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" }, - { url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" }, - { url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" }, - { url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" }, - { url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" }, - { url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" }, - { url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" }, - { url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" }, - { url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" }, - { url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" }, - { url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" }, - { url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" }, - { url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" }, - { url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" }, - { url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" }, - { url = "https://files.pythonhosted.org/packages/9d/69/024d6711d5ba575aa65d5538042e99964104e97fa153a9f10bc369182bc2/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:fd40bb9cd0891c4c3cb1ddf83f8bbfa15731a248fdc8162669405451e2724b09", size = 123166, upload-time = "2026-03-09T13:13:48.032Z" }, - { url = "https://files.pythonhosted.org/packages/ce/48/adbb40df306f587054a348831220812b9b1d787aff714cfbc8556e38fccd/kiwisolver-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0e1403fd7c26d77c1f03e096dc58a5c726503fa0db0456678b8668f76f521e3", size = 66395, upload-time = "2026-03-09T13:13:49.365Z" }, - { url = "https://files.pythonhosted.org/packages/a8/3a/d0a972b34e1c63e2409413104216cd1caa02c5a37cb668d1687d466c1c45/kiwisolver-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dda366d548e89a90d88a86c692377d18d8bd64b39c1fb2b92cb31370e2896bbd", size = 64065, upload-time = "2026-03-09T13:13:50.562Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0a/7b98e1e119878a27ba8618ca1e18b14f992ff1eda40f47bccccf4de44121/kiwisolver-1.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:332b4f0145c30b5f5ad9374881133e5aa64320428a57c2c2b61e9d891a51c2f3", size = 1477903, upload-time = "2026-03-09T13:13:52.084Z" }, - { url = "https://files.pythonhosted.org/packages/18/d8/55638d89ffd27799d5cc3d8aa28e12f4ce7a64d67b285114dbedc8ea4136/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c50b89ffd3e1a911c69a1dd3de7173c0cd10b130f56222e57898683841e4f96", size = 1278751, upload-time = "2026-03-09T13:13:54.673Z" }, - { url = "https://files.pythonhosted.org/packages/b8/97/b4c8d0d18421ecceba20ad8701358453b88e32414e6f6950b5a4bad54e65/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4db576bb8c3ef9365f8b40fe0f671644de6736ae2c27a2c62d7d8a1b4329f099", size = 1296793, upload-time = "2026-03-09T13:13:56.287Z" }, - { url = "https://files.pythonhosted.org/packages/c4/10/f862f94b6389d8957448ec9df59450b81bec4abb318805375c401a1e6892/kiwisolver-1.5.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b85aad90cea8ac6797a53b5d5f2e967334fa4d1149f031c4537569972596cb8", size = 1346041, upload-time = "2026-03-09T13:13:58.269Z" }, - { url = "https://files.pythonhosted.org/packages/a3/6a/f1650af35821eaf09de398ec0bc2aefc8f211f0cda50204c9f1673741ba9/kiwisolver-1.5.0-cp313-cp313-manylinux_2_39_riscv64.whl", hash = "sha256:d36ca54cb4c6c4686f7cbb7b817f66f5911c12ddb519450bbe86707155028f87", size = 987292, upload-time = "2026-03-09T13:13:59.871Z" }, - { url = "https://files.pythonhosted.org/packages/de/19/d7fb82984b9238115fe629c915007be608ebd23dc8629703d917dbfaffd4/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:38f4a703656f493b0ad185211ccfca7f0386120f022066b018eb5296d8613e23", size = 2227865, upload-time = "2026-03-09T13:14:01.401Z" }, - { url = "https://files.pythonhosted.org/packages/7f/b9/46b7f386589fd222dac9e9de9c956ce5bcefe2ee73b4e79891381dda8654/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ac2360e93cb41be81121755c6462cff3beaa9967188c866e5fce5cf13170859", size = 2324369, upload-time = "2026-03-09T13:14:02.972Z" }, - { url = "https://files.pythonhosted.org/packages/92/8b/95e237cf3d9c642960153c769ddcbe278f182c8affb20cecc1cc983e7cc5/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c95cab08d1965db3d84a121f1c7ce7479bdd4072c9b3dafd8fecce48a2e6b902", size = 1977989, upload-time = "2026-03-09T13:14:04.503Z" }, - { url = "https://files.pythonhosted.org/packages/1b/95/980c9df53501892784997820136c01f62bc1865e31b82b9560f980c0e649/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fc20894c3d21194d8041a28b65622d5b86db786da6e3cfe73f0c762951a61167", size = 2491645, upload-time = "2026-03-09T13:14:06.106Z" }, - { url = "https://files.pythonhosted.org/packages/cb/32/900647fd0840abebe1561792c6b31e6a7c0e278fc3973d30572a965ca14c/kiwisolver-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7a32f72973f0f950c1920475d5c5ea3d971b81b6f0ec53b8d0a956cc965f22e0", size = 2295237, upload-time = "2026-03-09T13:14:08.891Z" }, - { url = "https://files.pythonhosted.org/packages/be/8a/be60e3bbcf513cc5a50f4a3e88e1dcecebb79c1ad607a7222877becaa101/kiwisolver-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bf3acf1419fa93064a4c2189ac0b58e3be7872bf6ee6177b0d4c63dc4cea276", size = 73573, upload-time = "2026-03-09T13:14:12.327Z" }, - { url = "https://files.pythonhosted.org/packages/4d/d2/64be2e429eb4fca7f7e1c52a91b12663aeaf25de3895e5cca0f47ef2a8d0/kiwisolver-1.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:fa8eb9ecdb7efb0b226acec134e0d709e87a909fa4971a54c0c4f6e88635484c", size = 64998, upload-time = "2026-03-09T13:14:13.469Z" }, - { url = "https://files.pythonhosted.org/packages/b0/69/ce68dd0c85755ae2de490bf015b62f2cea5f6b14ff00a463f9d0774449ff/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:db485b3847d182b908b483b2ed133c66d88d49cacf98fd278fadafe11b4478d1", size = 125700, upload-time = "2026-03-09T13:14:14.636Z" }, - { url = "https://files.pythonhosted.org/packages/74/aa/937aac021cf9d4349990d47eb319309a51355ed1dbdc9c077cdc9224cb11/kiwisolver-1.5.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:be12f931839a3bdfe28b584db0e640a65a8bcbc24560ae3fdb025a449b3d754e", size = 67537, upload-time = "2026-03-09T13:14:15.808Z" }, - { url = "https://files.pythonhosted.org/packages/ee/20/3a87fbece2c40ad0f6f0aefa93542559159c5f99831d596050e8afae7a9f/kiwisolver-1.5.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:16b85d37c2cbb3253226d26e64663f755d88a03439a9c47df6246b35defbdfb7", size = 65514, upload-time = "2026-03-09T13:14:18.035Z" }, - { url = "https://files.pythonhosted.org/packages/f0/7f/f943879cda9007c45e1f7dba216d705c3a18d6b35830e488b6c6a4e7cdf0/kiwisolver-1.5.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4432b835675f0ea7414aab3d37d119f7226d24869b7a829caeab49ebda407b0c", size = 1584848, upload-time = "2026-03-09T13:14:19.745Z" }, - { url = "https://files.pythonhosted.org/packages/37/f8/4d4f85cc1870c127c88d950913370dd76138482161cd07eabbc450deff01/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b0feb50971481a2cc44d94e88bdb02cdd497618252ae226b8eb1201b957e368", size = 1391542, upload-time = "2026-03-09T13:14:21.54Z" }, - { url = "https://files.pythonhosted.org/packages/04/0b/65dd2916c84d252b244bd405303220f729e7c17c9d7d33dca6feeff9ffc4/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56fa888f10d0f367155e76ce849fa1166fc9730d13bd2d65a2aa13b6f5424489", size = 1404447, upload-time = "2026-03-09T13:14:23.205Z" }, - { url = "https://files.pythonhosted.org/packages/39/5c/2606a373247babce9b1d056c03a04b65f3cf5290a8eac5d7bdead0a17e21/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:940dda65d5e764406b9fb92761cbf462e4e63f712ab60ed98f70552e496f3bf1", size = 1455918, upload-time = "2026-03-09T13:14:24.74Z" }, - { url = "https://files.pythonhosted.org/packages/d5/d1/c6078b5756670658e9192a2ef11e939c92918833d2745f85cd14a6004bdf/kiwisolver-1.5.0-cp313-cp313t-manylinux_2_39_riscv64.whl", hash = "sha256:89fc958c702ee9a745e4700378f5d23fddbc46ff89e8fdbf5395c24d5c1452a3", size = 1072856, upload-time = "2026-03-09T13:14:26.597Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c8/7def6ddf16eb2b3741d8b172bdaa9af882b03c78e9b0772975408801fa63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9027d773c4ff81487181a925945743413f6069634d0b122d0b37684ccf4f1e18", size = 2333580, upload-time = "2026-03-09T13:14:28.237Z" }, - { url = "https://files.pythonhosted.org/packages/9e/87/2ac1fce0eb1e616fcd3c35caa23e665e9b1948bb984f4764790924594128/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5b233ea3e165e43e35dba1d2b8ecc21cf070b45b65ae17dd2747d2713d942021", size = 2423018, upload-time = "2026-03-09T13:14:30.018Z" }, - { url = "https://files.pythonhosted.org/packages/67/13/c6700ccc6cc218716bfcda4935e4b2997039869b4ad8a94f364c5a3b8e63/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ce9bf03dad3b46408c08649c6fbd6ca28a9fce0eb32fdfffa6775a13103b5310", size = 2062804, upload-time = "2026-03-09T13:14:32.888Z" }, - { url = "https://files.pythonhosted.org/packages/1b/bd/877056304626943ff0f1f44c08f584300c199b887cb3176cd7e34f1515f1/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fc4d3f1fb9ca0ae9f97b095963bc6326f1dbfd3779d6679a1e016b9baaa153d3", size = 2597482, upload-time = "2026-03-09T13:14:34.971Z" }, - { url = "https://files.pythonhosted.org/packages/75/19/c60626c47bf0f8ac5dcf72c6c98e266d714f2fbbfd50cf6dab5ede3aaa50/kiwisolver-1.5.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f443b4825c50a51ee68585522ab4a1d1257fac65896f282b4c6763337ac9f5d2", size = 2394328, upload-time = "2026-03-09T13:14:36.816Z" }, - { url = "https://files.pythonhosted.org/packages/47/84/6a6d5e5bb8273756c27b7d810d47f7ef2f1f9b9fd23c9ee9a3f8c75c9cef/kiwisolver-1.5.0-cp313-cp313t-win_arm64.whl", hash = "sha256:893ff3a711d1b515ba9da14ee090519bad4610ed1962fbe298a434e8c5f8db53", size = 68410, upload-time = "2026-03-09T13:14:38.695Z" }, - { url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" }, - { url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" }, - { url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" }, - { url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" }, -] - [[package]] name = "kubernetes" version = "35.0.0" @@ -2224,15 +1861,12 @@ wheels = [ ] [[package]] -name = "mako" -version = "1.3.12" +name = "mail-parser" +version = "4.4.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/62/791b31e69ae182791ec67f04850f2f062716bbd205483d63a215f3e062d3/mako-1.3.12.tar.gz", hash = "sha256:9f778e93289bd410bb35daadeb4fc66d95a746f0b75777b942088b7fd7af550a", size = 400219, upload-time = "2026-04-28T19:01:08.512Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/6e/2cee08e0d6fbce02a301d5ab72ef5ffe8f47dd59ac3d8ce61324d8a9143a/mail_parser-4.4.0.tar.gz", hash = "sha256:82d2c0fa98a138620feaa87a378af2a2643b2941b2e125d7abfc65dd21094b33", size = 2852896, upload-time = "2026-06-11T06:56:49.789Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bc/b1/a0ec7a5a9db730a08daef1fdfb8090435b82465abbf758a596f0ea88727e/mako-1.3.12-py3-none-any.whl", hash = "sha256:8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9", size = 78521, upload-time = "2026-04-28T19:01:10.393Z" }, + { url = "https://files.pythonhosted.org/packages/26/a7/99dfed167ba728384521acd54fadbb7649ce49333018f42e1f190ed25911/mail_parser-4.4.0-py3-none-any.whl", hash = "sha256:46f7931817660fec4f7723e6f1722e66a9cce44c2daeacbeb9e705078379561f", size = 35504, upload-time = "2026-06-11T06:56:48.348Z" }, ] [[package]] @@ -2306,46 +1940,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, ] -[[package]] -name = "matplotlib" -version = "3.11.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "contourpy" }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy" }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1f/24/080c99d223d158d3a8902769269ab6da5b50f7a0e6e072513907e02b7a6c/matplotlib-3.11.0.tar.gz", hash = "sha256:68c0c7be01b30dcca3638934f7f591df73401235cbdbf0d1ab1c71e7db7f8b57", size = 33251176, upload-time = "2026-06-12T02:29:15.508Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/da/17/f5276b496c61477a6c4fc5e7401f4bfe1c2e5ef7c6cd67896f2ade3809cb/matplotlib-3.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b5872e9cf11adc8f589ded3ce11bc3e1061ad498259664fabc1f6615beb918", size = 9449976, upload-time = "2026-06-12T02:27:50.989Z" }, - { url = "https://files.pythonhosted.org/packages/82/34/bdd77418adb2178a1d59f044bd67bfebb115896e91b840b8a197eb3f4f4e/matplotlib-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0515d495124be3124340e59f164d901ed4484e2246a5b74cfa483cac3b80bd97", size = 9279307, upload-time = "2026-06-12T02:27:53.247Z" }, - { url = "https://files.pythonhosted.org/packages/94/95/7f522393c88313336b20d70fc849555757b2e5febc22b83b3a3f0fd4bce9/matplotlib-3.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be5f93a1d21981bfb802ded0d77a0caa92d4342a47d45754fac77e314a506344", size = 10031353, upload-time = "2026-06-12T02:27:55.215Z" }, - { url = "https://files.pythonhosted.org/packages/87/ce/8f25a0e3186aefd61913e7467d1b999465bcd0d0c03ac695c1b26ca559b7/matplotlib-3.11.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41635d7909d19e52e924a521dde6d8f670b0f53ab1d0e8c331fa831554f681d1", size = 10839232, upload-time = "2026-06-12T02:27:57.746Z" }, - { url = "https://files.pythonhosted.org/packages/85/c2/db15da2bbdf9e3ca66df7db8e2c33a1dfed67be24a24d2c878efaaff01d6/matplotlib-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:94f5000f67ca9faa300863ea17f8bce9175cb67b88bec4bc7780502d53dd7c9e", size = 10923899, upload-time = "2026-06-12T02:28:00.223Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2f/a58a4443a4d052a4ea77557478336aefc26c7981f6408d37adba763aa758/matplotlib-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ac6f1ef39f3d0f9e2463303013094992cdbe0f85f43bc54155bc472b2042768e", size = 9329528, upload-time = "2026-06-12T02:28:02.27Z" }, - { url = "https://files.pythonhosted.org/packages/61/0f/4b669589d47733b97ab9df4b58d6fc1e68acb5ea42a928dc7cbdd6bf5871/matplotlib-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:9dd11fb612ce7bc60b1de5b4fc87ff959d22317b5de42aabf392f66f97af22eb", size = 9003413, upload-time = "2026-06-12T02:28:04.49Z" }, - { url = "https://files.pythonhosted.org/packages/55/41/aa47f156b061d14c98b906f76c428507397708ec63ff94f410ae1752b426/matplotlib-3.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce3b839b34ae1f430b4616893a2945a2999debaa7e94e7e29a2a8bbf286f7b5", size = 9450532, upload-time = "2026-06-12T02:28:06.769Z" }, - { url = "https://files.pythonhosted.org/packages/8c/4f/5a9eb0375e81413953febf8af7b012a6b6357f53438a15c4f5ad86c6bbb5/matplotlib-3.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:373db8f91214e8ccaf35ac833cc1dd59dd961e148bbd55dd027141591dde1313", size = 9279760, upload-time = "2026-06-12T02:28:09.152Z" }, - { url = "https://files.pythonhosted.org/packages/a4/c0/1117d53077e3ac3152503a84e9cf7a5c239576805ee71276e80c2aaa7471/matplotlib-3.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be152b7570324dc8d01574cc9474dd2d803237acf528bcbb5b211fa347461a09", size = 10031623, upload-time = "2026-06-12T02:28:11.26Z" }, - { url = "https://files.pythonhosted.org/packages/92/7e/e937138daffad65b71bf831a377809dcbc830fb4f31a31e067dc1faa2575/matplotlib-3.11.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:126f256df600652d7e4b394cf3164ff75210a00038f287c95a012a6f58d0e83f", size = 10839372, upload-time = "2026-06-12T02:28:14.102Z" }, - { url = "https://files.pythonhosted.org/packages/1d/c2/438ecc197ffb8023b6b9922915542f2172f5fd45b76703b0b4fc47322243/matplotlib-3.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:03acfeddf87b0dddb11b081ef7740ad445a3ca8bcb6b8e3011b08f2cf802b75c", size = 10924099, upload-time = "2026-06-12T02:28:16.383Z" }, - { url = "https://files.pythonhosted.org/packages/40/2e/395883da416f378b3ed2c9f3e843ac477eae1ce731b671b79adaa6f0bacd/matplotlib-3.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:ab3722f04f3ff34c23b5012c5873d2894174e06c3822fcdac3610965a5ac7d06", size = 9329727, upload-time = "2026-06-12T02:28:18.581Z" }, - { url = "https://files.pythonhosted.org/packages/61/82/2c388956abf8bf392dfb5b8917c502f1082df6a941b781ab8c8e5ba2474b/matplotlib-3.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:c945824670fb8915b4ac879e5e61f3c58e0913022f70a0de4c082b17372f8771", size = 9003506, upload-time = "2026-06-12T02:28:20.474Z" }, - { url = "https://files.pythonhosted.org/packages/c8/c1/34454baa44da7975ada82e9aea37105ec47059514dc967d3be14426ba8dc/matplotlib-3.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3489c3dc487669b4a980bc3068f87856de7a1564248d3f6c629efb2a58b03f24", size = 9499838, upload-time = "2026-06-12T02:28:22.713Z" }, - { url = "https://files.pythonhosted.org/packages/b1/c3/98fe79a398cf232219f090163a7fa7e6766e9f2e0ad26df54d6f8934d8ee/matplotlib-3.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:6a98f5476ce784a50ce09998f4ae1e6a9f25043cef8a480c98949902eda74620", size = 9332298, upload-time = "2026-06-12T02:28:24.796Z" }, - { url = "https://files.pythonhosted.org/packages/95/e4/b4b7c33151e74e5c802f3cde1ba807ebfc38401e329b44e215a5888dd76d/matplotlib-3.11.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:565af866fd63e4bd3f987d580afe27c44c2552a3b3305f4ecbb85133601ea6f3", size = 10045491, upload-time = "2026-06-12T02:28:27.141Z" }, - { url = "https://files.pythonhosted.org/packages/71/28/394548efd68354110c1a1be11fe6b6e559e06d1a23da35908a0e316c55a9/matplotlib-3.11.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e6b3e64dea5062c570f04358e2711859f3531b459f29516274fbad889079e4f3", size = 10857059, upload-time = "2026-06-12T02:28:29.222Z" }, - { url = "https://files.pythonhosted.org/packages/c8/44/e7922e6e2a4d63bdfbc9dc4a53e3850ab438d46cf42e6779bb15ec92c948/matplotlib-3.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:942b37c5db1899610bd1543ce8e13e4ecff9a4633e7f63bb6aa9205d2644ebd1", size = 10939576, upload-time = "2026-06-12T02:28:31.66Z" }, - { url = "https://files.pythonhosted.org/packages/3d/be/b1ca96003a441d619b727fee21d671fdff7a5ce2f1bb797b2521aa2f679a/matplotlib-3.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:c08e649a6313e1291e713623b97a38e5bb4aa580b2a100a94a3309bc6b9c8eb3", size = 9379519, upload-time = "2026-06-12T02:28:33.888Z" }, - { url = "https://files.pythonhosted.org/packages/e3/72/4bf3b91821c34596dd6a7bdac5836d94f744144c8208939ef49d8ec43f7e/matplotlib-3.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2746cd2c113742ff6ce37a864c5ac5fd7aa644568f445e66166e457ac78e40e0", size = 9055456, upload-time = "2026-06-12T02:28:35.878Z" }, -] - [[package]] name = "matplotlib-inline" version = "0.2.1" @@ -2385,23 +1979,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, ] -[[package]] -name = "mike" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "mkdocs" }, - { name = "pyparsing" }, - { name = "pyyaml" }, - { name = "pyyaml-env-tag" }, - { name = "verspec" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b4/47/fa87e9d56bef16cdfe34b059a437e8c6f7ec6f1b9c378871c3cf95ebea9c/mike-2.2.0.tar.gz", hash = "sha256:1e3858e32c0f125aac14432fc7848434358f9ae0962c5c5cde387ad47f6ad25e", size = 38450, upload-time = "2026-04-14T04:59:03.944Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/8e/56ccb09c7232a55403a7637caa21922f3b65901a37f5e8bdb405d0de0946/mike-2.2.0-py3-none-any.whl", hash = "sha256:e1f4981c1152eec7c2490a3401142292cc47d686194188416db2648fdfe1d040", size = 34026, upload-time = "2026-04-14T04:59:02.602Z" }, -] - [[package]] name = "mkdocs" version = "1.6.1" @@ -2551,87 +2128,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl", hash = "sha256:0b83513478bdfd803ff05aa43e9b1fca9dd22bcd9471f09ca6257f009bc5ee12", size = 104779, upload-time = "2026-02-20T10:38:34.517Z" }, ] -[[package]] -name = "mlflow" -version = "3.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "alembic" }, - { name = "cryptography" }, - { name = "docker" }, - { name = "flask" }, - { name = "flask-cors" }, - { name = "graphene" }, - { name = "gunicorn", marker = "sys_platform != 'win32'" }, - { name = "huey" }, - { name = "matplotlib" }, - { name = "mlflow-skinny" }, - { name = "mlflow-tracing" }, - { name = "numpy" }, - { name = "pandas" }, - { name = "pyarrow" }, - { name = "scikit-learn" }, - { name = "scipy" }, - { name = "skops" }, - { name = "sqlalchemy" }, - { name = "waitress", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8e/69/d71afc475fa7e7b22bb27392247d2a3015c9da202dea44f150a54be4bd67/mlflow-3.13.0.tar.gz", hash = "sha256:a95198d592a8a15fad3db7f56b228acc9422c09f0daa7c6c976a9996ab73c3e2", size = 10086808, upload-time = "2026-06-01T05:55:09.555Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/1f/d44140128356f2f5db37f9fb4d9da31123d839d49f3fe00a079cd35bfe20/mlflow-3.13.0-py3-none-any.whl", hash = "sha256:7ca9cb2f623f300dabadaf5e985c85af77c5db3d7c36f56769d22c101b132f6c", size = 10788121, upload-time = "2026-06-01T05:55:06.86Z" }, -] - -[[package]] -name = "mlflow-skinny" -version = "3.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cachetools" }, - { name = "click" }, - { name = "cloudpickle" }, - { name = "databricks-sdk" }, - { name = "fastapi" }, - { name = "gitpython" }, - { name = "importlib-metadata" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "pydantic" }, - { name = "python-dotenv" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "sqlparse" }, - { name = "starlette" }, - { name = "typing-extensions" }, - { name = "uvicorn" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/13/840db21a4f46ebe6ba9837a38bc93d748e23b6b61986799c8040cd4bf728/mlflow_skinny-3.13.0.tar.gz", hash = "sha256:d2273bfa21f776359f7d6ab2267967e3a6732a5fb00996ad433d0e777dfa3b71", size = 2814837, upload-time = "2026-06-01T05:54:54.175Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/fd/f2739de1b6a09da981927aa90db87340cbe4b3cf6cd175fd5e6e4366208e/mlflow_skinny-3.13.0-py3-none-any.whl", hash = "sha256:ced3d9a580564fae093d14732df8531fb180574f6483d4c642b6083879eb86fc", size = 3365675, upload-time = "2026-06-01T05:54:52.166Z" }, -] - -[[package]] -name = "mlflow-tracing" -version = "3.13.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cachetools" }, - { name = "databricks-sdk" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-proto" }, - { name = "opentelemetry-sdk" }, - { name = "packaging" }, - { name = "protobuf" }, - { name = "pydantic" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ec/b0/5912313e895e6ce02f1f67110164d147593d1b5379a4767a30a5b9e730c5/mlflow_tracing-3.13.0.tar.gz", hash = "sha256:42c435b0fdcab00f1865cab4a52f7a85a2a08d68a959f36bcf90a1c9fe65db0a", size = 1393079, upload-time = "2026-06-01T05:54:44.906Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/fd/2c53ebc2f7fbb34ed4a3913a71ff53962e6be35fa02f7cee1f52c77388cd/mlflow_tracing-3.13.0-py3-none-any.whl", hash = "sha256:2f8187ce2b1af7419be71d2d8ab5fec53d207d4b8d703cd15e5db64939098d72", size = 1664279, upload-time = "2026-06-01T05:54:42.911Z" }, -] - [[package]] name = "mmh3" version = "5.2.1" @@ -2907,7 +2403,7 @@ name = "nvidia-cudnn-cu13" version = "9.19.0.56" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas" }, + { name = "nvidia-cublas", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/f1/84/26025437c1e6b61a707442184fa0c03d083b661adf3a3eecfd6d21677740/nvidia_cudnn_cu13-9.19.0.56-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:6ed29ffaee1176c612daf442e4dd6cfeb6a0caa43ddcbeb59da94953030b1be4", size = 433781201, upload-time = "2026-02-03T20:40:53.805Z" }, @@ -2919,7 +2415,7 @@ name = "nvidia-cufft" version = "12.0.0.61" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/8b/ae/f417a75c0259e85c1d2f83ca4e960289a5f814ed0cea74d18c353d3e989d/nvidia_cufft-12.0.0.61-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2708c852ef8cd89d1d2068bdbece0aa188813a0c934db3779b9b1faa8442e5f5", size = 214053554, upload-time = "2025-09-04T08:31:38.196Z" }, @@ -2949,9 +2445,9 @@ name = "nvidia-cusolver" version = "12.0.4.66" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas" }, - { name = "nvidia-cusparse" }, - { name = "nvidia-nvjitlink" }, + { name = "nvidia-cublas", marker = "sys_platform != 'darwin'" }, + { name = "nvidia-cusparse", marker = "sys_platform != 'darwin'" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c8/c3/b30c9e935fc01e3da443ec0116ed1b2a009bb867f5324d3f2d7e533e776b/nvidia_cusolver-12.0.4.66-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:02c2457eaa9e39de20f880f4bd8820e6a1cfb9f9a34f820eb12a155aa5bc92d2", size = 223467760, upload-time = "2025-09-04T08:33:04.222Z" }, @@ -2963,7 +2459,7 @@ name = "nvidia-cusparse" version = "12.6.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink" }, + { name = "nvidia-nvjitlink", marker = "sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/f8/94/5c26f33738ae35276672f12615a64bd008ed5be6d1ebcb23579285d960a9/nvidia_cusparse-12.6.3.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:80bcc4662f23f1054ee334a15c72b8940402975e0eab63178fc7e670aa59472c", size = 162155568, upload-time = "2025-09-04T08:33:42.864Z" }, @@ -3026,7 +2522,7 @@ wheels = [ [[package]] name = "ogx-client" -version = "1.0.0" +version = "1.1.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -3045,9 +2541,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/ed/bbcd77921d29dc0a84a4767cc3661d027bfc5db6a0c32a4f82dadc182542/ogx_client-1.0.0.tar.gz", hash = "sha256:a13133105149b77384ba804cc1fa340c1defce9ebb4820e3c593c36b103f0010", size = 435209, upload-time = "2026-05-12T13:58:58.883Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/ac/2fd77ab57ef26b8689d29d3f0a2e9020966282797b715ecf537806ca3486/ogx_client-1.1.3.tar.gz", hash = "sha256:d61a6f4cd996a5ce5cd6eb1389215f520cc1ca49d485b454d00faa021d4f4f0a", size = 440850, upload-time = "2026-06-24T15:07:27.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/95/2cafdc4e3ff4a89db82862c0e0ed10fed2154ba74c7b16c2a5e371bac5d9/ogx_client-1.0.0-py3-none-any.whl", hash = "sha256:d0ef468f3a46b5bf3af9de3540902aa0d4a0c9d4370444697ca710473843c439", size = 309279, upload-time = "2026-05-12T13:58:57.356Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f0/be21078666dc51b30a92dcd83e7711f923230b2cdd4a274994c36059233d/ogx_client-1.1.3-py3-none-any.whl", hash = "sha256:56390f857807f99c1927ab2de46d3427d63970d55533945c92e575fbe51d2252", size = 314011, upload-time = "2026-06-24T15:07:25.85Z" }, ] [[package]] @@ -3090,7 +2586,7 @@ wheels = [ [[package]] name = "openai" -version = "2.41.1" +version = "1.109.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -3102,9 +2598,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/36/4c926a91554483977608951360c18c2e911592785eb87a6437813f6123f7/openai-2.41.1.tar.gz", hash = "sha256:23d617a0432457ad844973bee8f540be9da90894f7c5686852d2d365da058f57", size = 783584, upload-time = "2026-06-10T16:10:37.667Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c6/a1/a303104dc55fc546a3f6914c842d3da471c64eec92043aef8f652eb6c524/openai-1.109.1.tar.gz", hash = "sha256:d173ed8dbca665892a6db099b4a2dfac624f94d20a93f46eb0b56aae940ed869", size = 564133, upload-time = "2025-09-24T13:00:53.075Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/74/925d7b3892927e9804aaf58d374a45dc28e4420ff90e992272b77286343e/openai-2.41.1-py3-none-any.whl", hash = "sha256:a939565f350cb7443cb843b801b88c716ac8024b492fb94ca269d5f6b1bbefd6", size = 1353380, upload-time = "2026-06-10T16:10:35.756Z" }, + { url = "https://files.pythonhosted.org/packages/1d/2a/7dd3d207ec669cacc1f186fd856a0f61dbc255d24f6fdc1a6715d6051b0f/openai-1.109.1-py3-none-any.whl", hash = "sha256:6bcaf57086cf59159b8e27447e4e7dd019db5d29a438072fbd49c290c7e65315", size = 948627, upload-time = "2025-09-24T13:00:50.754Z" }, ] [[package]] @@ -3449,18 +2945,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/34/b6f19941adcdaf415b5e8a8d577499f5b6a76b59cbae37f9b125a9ffe9f2/polyfactory-3.3.0-py3-none-any.whl", hash = "sha256:686abcaa761930d3df87b91e95b26b8d8cb9fdbbbe0b03d5f918acff5c72606e", size = 62707, upload-time = "2026-02-22T09:46:25.985Z" }, ] -[[package]] -name = "prettytable" -version = "3.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/79/45/b0847d88d6cfeb4413566738c8bbf1e1995fad3d42515327ff32cc1eb578/prettytable-3.17.0.tar.gz", hash = "sha256:59f2590776527f3c9e8cf9fe7b66dd215837cca96a9c39567414cbc632e8ddb0", size = 67892, upload-time = "2025-11-14T17:33:20.212Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/8c/83087ebc47ab0396ce092363001fa37c17153119ee282700c0713a195853/prettytable-3.17.0-py3-none-any.whl", hash = "sha256:aad69b294ddbe3e1f95ef8886a060ed1666a0b83018bbf56295f6f226c43d287", size = 34433, upload-time = "2025-11-14T17:33:19.093Z" }, -] - [[package]] name = "progressbar2" version = "4.5.0" @@ -3635,27 +3119,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/1a/8dd5cafab7b66573fa91c03d06d213356ad4edd71813aa75e08ce2b3a844/pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d", size = 27388127, upload-time = "2026-04-21T10:49:37.334Z" }, ] -[[package]] -name = "pyasn1" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, -] - -[[package]] -name = "pyasn1-modules" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyasn1" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, -] - [[package]] name = "pybase64" version = "1.4.3" @@ -3891,15 +3354,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl", hash = "sha256:5c0fd2a2bea14eb39af8ff284f1066d898ab2187d81b889b75d46d4348c01638", size = 268901, upload-time = "2026-03-29T15:01:53.244Z" }, ] -[[package]] -name = "pyparsing" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, -] - [[package]] name = "pypdfium2" version = "5.8.0" @@ -4376,6 +3830,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/50/0a9e7e7afe7339bd5e36911f0ceb15fed51945836ed803ae5afd661057fd/rtree-1.4.1-py3-none-win_arm64.whl", hash = "sha256:3d46f55729b28138e897ffef32f7ce93ac335cb67f9120125ad3742a220800f0", size = 355253, upload-time = "2025-08-13T19:32:00.296Z" }, ] +[[package]] +name = "s3transfer" +version = "0.19.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f6/94/dcdaeb1713cab9c84def276cfac7388b17c7d9855bbcfe88d77e4dbafd44/s3transfer-0.19.0.tar.gz", hash = "sha256:ce436931687addc4c1712d52d40b32f53e88315723f107ffa20ba82b05a0f685", size = 165171, upload-time = "2026-06-16T19:44:51.599Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/5f/4c174edad94f82de888ac00a5ddd8d07b35609b6c94f0bdf4d74af57703e/s3transfer-0.19.0-py3-none-any.whl", hash = "sha256:777cc2415536f1debadb5c2ef7779275d0fc0fe0e042411cdd6caebeb2685262", size = 90101, upload-time = "2026-06-16T19:44:50.439Z" }, +] + [[package]] name = "safetensors" version = "0.7.0" @@ -4553,22 +4019,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] -[[package]] -name = "skops" -version = "0.14.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, - { name = "packaging" }, - { name = "prettytable" }, - { name = "scikit-learn" }, - { name = "scipy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c8/9f/46448c4e41a4c5ee4bdb74b3758af48e5ff0faeffe40f4e301bfc7594894/skops-0.14.0.tar.gz", hash = "sha256:6c8c0e047f691a3a582c3258943eecafcbfd79c8c7eef66260f3703e363254f0", size = 608084, upload-time = "2026-04-20T18:23:55.336Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/0e/3ae19fa941522cd98e119762e7181d371c8dba0b2d72bfaf9522692e329c/skops-0.14.0-py3-none-any.whl", hash = "sha256:60a5db78a9db46ccee2139a0ba13ab5afb1c96f4749b382e75a371291bbe3e36", size = 132198, upload-time = "2026-04-20T18:23:54.018Z" }, -] - [[package]] name = "smmap" version = "5.0.3" @@ -4596,42 +4046,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" }, ] -[[package]] -name = "sqlalchemy" -version = "2.0.51" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/02/f1/a7a892f18d4d224e6b26f706531eafccc41e37594d37d304786969ee13cb/sqlalchemy-2.0.51.tar.gz", hash = "sha256:804dccd8a4a6242c4e30ad961e540e18a588f6527202f2d6791b01845d59fdc9", size = 9912201, upload-time = "2026-06-15T15:41:20.012Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/70/e868bc5412acd101a8280f25c95f10eeae0771c4eb806b02491142810ee8/sqlalchemy-2.0.51-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d78702b26ba1c18b2d0fb2ea940ba7f17a9581b42e8361ff93920ebbee1235a", size = 2160291, upload-time = "2026-06-15T16:08:48.918Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1c/71ee0f8a6b9d7316a1ccd30430b4c62b6c2e36adc96017a4e3a72dce49d6/sqlalchemy-2.0.51-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581921d849d6e6f994d560389192955e80e2950e18fcdfe2ccea863e01158e6e", size = 3343835, upload-time = "2026-06-15T16:19:42.613Z" }, - { url = "https://files.pythonhosted.org/packages/2b/7c/7ab9f9aadc5944fdd06612484ed7918fe376ad871a5f50404dc1536e0194/sqlalchemy-2.0.51-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1d21ce524ab86c23046e992a5b81cb54c21079c6df6e78b8fc77d77cac70a6b9", size = 3358470, upload-time = "2026-06-15T16:26:38.011Z" }, - { url = "https://files.pythonhosted.org/packages/d0/7d/ff77169fee6186de145a7f2b87006c39638391130abbab2b1f63ac6ea583/sqlalchemy-2.0.51-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c5d98a2709840027f5a347c3af0a7c3d5f6c1ff93af2ca1c54494e23cba8f389", size = 3289874, upload-time = "2026-06-15T16:19:45.212Z" }, - { url = "https://files.pythonhosted.org/packages/6f/3b/6c505903710d781b55bc3141ee34a062bf9745a6b5bc7333305b9ed63b33/sqlalchemy-2.0.51-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1181256e0f16479691b5616d36375dc2620ad8332b25978763c3d206ad3f3f1d", size = 3321692, upload-time = "2026-06-15T16:26:39.747Z" }, - { url = "https://files.pythonhosted.org/packages/3c/b7/c5ffe50aa2f4d947c9250e1519d939260329a07fe6272edfccd784b3d007/sqlalchemy-2.0.51-cp312-cp312-win32.whl", hash = "sha256:9f380393be5abeb6815f68fd39271b95127173511b6706b0a630a9995d53f8f5", size = 2119674, upload-time = "2026-06-15T16:23:09.543Z" }, - { url = "https://files.pythonhosted.org/packages/25/dc/46a65916af68a06ef6b972c6050ba4c8f97070fe3fb33097d34229d9bef6/sqlalchemy-2.0.51-cp312-cp312-win_amd64.whl", hash = "sha256:2cf39aabdf48e87c1c2c2ed6d20d33ffa0733b3071ce9c5f66357947dd009080", size = 2146670, upload-time = "2026-06-15T16:23:11.048Z" }, - { url = "https://files.pythonhosted.org/packages/54/fe/a210d52fd1a90ecfae8a78e9d8b27e18d733d60818a8bf250ff690b75120/sqlalchemy-2.0.51-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c2056838b6685b72fdb36c99996cf862753461a62f2e84f4196371d3b2d6a07", size = 2157184, upload-time = "2026-06-15T16:08:50.374Z" }, - { url = "https://files.pythonhosted.org/packages/17/6b/2dce8369b199cb855110e056032f94a9f66dacc2237d3d39c115a86eac56/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:483b11bd46bf35fc14c52faf338b04300c9e6ce554bce9b11be85bfec3bc3195", size = 3284735, upload-time = "2026-06-15T16:19:46.934Z" }, - { url = "https://files.pythonhosted.org/packages/53/ff/dbc495b8a14da840faffb353857a72d4190113cac33727906fb997047f0f/sqlalchemy-2.0.51-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1bed1ee8b01da6088210aa9412023326fb98a599ba502e6118308601dcbef77f", size = 3302756, upload-time = "2026-06-15T16:26:41.336Z" }, - { url = "https://files.pythonhosted.org/packages/cf/d5/fde8f4dddcf518ee15ab35a7c6a28acc32c8ba548d1d2aa451f96e6dbb0b/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72ca54c952107ba5cd58854b67a5a6268631289d21651a1235396f3b98b47400", size = 3232055, upload-time = "2026-06-15T16:19:49.286Z" }, - { url = "https://files.pythonhosted.org/packages/67/d1/43d3a0ac955a58601c24fa23038b1c55ee3a1ec02c0f96ebb1eae2bcf614/sqlalchemy-2.0.51-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b3e693d15533a45cd5906f0589f9c35090bef6ef45bf1e8195c424aa0ae06a8d", size = 3269850, upload-time = "2026-06-15T16:26:43.017Z" }, - { url = "https://files.pythonhosted.org/packages/94/df/de669c7054cd47c4439ac34b1b2ee8b804a794791fbb10720e997a2c87c7/sqlalchemy-2.0.51-cp313-cp313-win32.whl", hash = "sha256:b93ab07b5292dbe7e6b8da89475275e7042744283921344b56105f3eeb0f828b", size = 2117721, upload-time = "2026-06-15T16:23:12.36Z" }, - { url = "https://files.pythonhosted.org/packages/d0/8a/403c51d064196bae20a0bc2476577f83a3f8dd299719a97417086b7f2ec5/sqlalchemy-2.0.51-cp313-cp313-win_amd64.whl", hash = "sha256:0f053118c30e53161857a953e4de667d90e274980dccbe5dd3829bbbeece72a5", size = 2143615, upload-time = "2026-06-15T16:23:13.906Z" }, - { url = "https://files.pythonhosted.org/packages/e2/22/dbf013a12ec759e54a34a119e9e217435b3f71b2dd5c61a7ade0a25dae87/sqlalchemy-2.0.51-py3-none-any.whl", hash = "sha256:bb024d8b621d0be75f4f44ecc7c950450026e76d66dc8f791bb5331d7fed59d5", size = 1944334, upload-time = "2026-06-15T16:09:22.418Z" }, -] - -[[package]] -name = "sqlparse" -version = "0.5.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, -] - [[package]] name = "stack-data" version = "0.6.3" @@ -4646,19 +4060,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] -[[package]] -name = "starlette" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/e3/7c1dc7381d9f8ab7d854328ebfa884e62cb3f3d8549ddfd37c7814f42afa/starlette-1.3.1.tar.gz", hash = "sha256:05d0213193f2fbaae60e2ecb593b4add4262ad4e46536b54abe36f11a71724e0", size = 2703240, upload-time = "2026-06-12T09:23:11.602Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6", size = 73632, upload-time = "2026-06-12T09:23:10.017Z" }, -] - [[package]] name = "sympy" version = "1.14.0" @@ -4707,39 +4108,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, ] -[[package]] -name = "tiktoken" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/7d/ab/4d017d0f76ec3171d469d80fc03dfbb4e48a4bcaddaa831b31d526f05edc/tiktoken-0.12.0.tar.gz", hash = "sha256:b18ba7ee2b093863978fcb14f74b3707cdc8d4d4d3836853ce7ec60772139931", size = 37806, upload-time = "2025-10-06T20:22:45.419Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/85/be65d39d6b647c79800fd9d29241d081d4eeb06271f383bb87200d74cf76/tiktoken-0.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b97f74aca0d78a1ff21b8cd9e9925714c15a9236d6ceacf5c7327c117e6e21e8", size = 1050728, upload-time = "2025-10-06T20:21:52.756Z" }, - { url = "https://files.pythonhosted.org/packages/4a/42/6573e9129bc55c9bf7300b3a35bef2c6b9117018acca0dc760ac2d93dffe/tiktoken-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b90f5ad190a4bb7c3eb30c5fa32e1e182ca1ca79f05e49b448438c3e225a49b", size = 994049, upload-time = "2025-10-06T20:21:53.782Z" }, - { url = "https://files.pythonhosted.org/packages/66/c5/ed88504d2f4a5fd6856990b230b56d85a777feab84e6129af0822f5d0f70/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:65b26c7a780e2139e73acc193e5c63ac754021f160df919add909c1492c0fb37", size = 1129008, upload-time = "2025-10-06T20:21:54.832Z" }, - { url = "https://files.pythonhosted.org/packages/f4/90/3dae6cc5436137ebd38944d396b5849e167896fc2073da643a49f372dc4f/tiktoken-0.12.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:edde1ec917dfd21c1f2f8046b86348b0f54a2c0547f68149d8600859598769ad", size = 1152665, upload-time = "2025-10-06T20:21:56.129Z" }, - { url = "https://files.pythonhosted.org/packages/a3/fe/26df24ce53ffde419a42f5f53d755b995c9318908288c17ec3f3448313a3/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:35a2f8ddd3824608b3d650a000c1ef71f730d0c56486845705a8248da00f9fe5", size = 1194230, upload-time = "2025-10-06T20:21:57.546Z" }, - { url = "https://files.pythonhosted.org/packages/20/cc/b064cae1a0e9fac84b0d2c46b89f4e57051a5f41324e385d10225a984c24/tiktoken-0.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83d16643edb7fa2c99eff2ab7733508aae1eebb03d5dfc46f5565862810f24e3", size = 1254688, upload-time = "2025-10-06T20:21:58.619Z" }, - { url = "https://files.pythonhosted.org/packages/81/10/b8523105c590c5b8349f2587e2fdfe51a69544bd5a76295fc20f2374f470/tiktoken-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffc5288f34a8bc02e1ea7047b8d041104791d2ddbf42d1e5fa07822cbffe16bd", size = 878694, upload-time = "2025-10-06T20:21:59.876Z" }, - { url = "https://files.pythonhosted.org/packages/00/61/441588ee21e6b5cdf59d6870f86beb9789e532ee9718c251b391b70c68d6/tiktoken-0.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:775c2c55de2310cc1bc9a3ad8826761cbdc87770e586fd7b6da7d4589e13dab3", size = 1050802, upload-time = "2025-10-06T20:22:00.96Z" }, - { url = "https://files.pythonhosted.org/packages/1f/05/dcf94486d5c5c8d34496abe271ac76c5b785507c8eae71b3708f1ad9b45a/tiktoken-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a01b12f69052fbe4b080a2cfb867c4de12c704b56178edf1d1d7b273561db160", size = 993995, upload-time = "2025-10-06T20:22:02.788Z" }, - { url = "https://files.pythonhosted.org/packages/a0/70/5163fe5359b943f8db9946b62f19be2305de8c3d78a16f629d4165e2f40e/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:01d99484dc93b129cd0964f9d34eee953f2737301f18b3c7257bf368d7615baa", size = 1128948, upload-time = "2025-10-06T20:22:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/0c/da/c028aa0babf77315e1cef357d4d768800c5f8a6de04d0eac0f377cb619fa/tiktoken-0.12.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4a1a4fcd021f022bfc81904a911d3df0f6543b9e7627b51411da75ff2fe7a1be", size = 1151986, upload-time = "2025-10-06T20:22:05.173Z" }, - { url = "https://files.pythonhosted.org/packages/a0/5a/886b108b766aa53e295f7216b509be95eb7d60b166049ce2c58416b25f2a/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:981a81e39812d57031efdc9ec59fa32b2a5a5524d20d4776574c4b4bd2e9014a", size = 1194222, upload-time = "2025-10-06T20:22:06.265Z" }, - { url = "https://files.pythonhosted.org/packages/f4/f8/4db272048397636ac7a078d22773dd2795b1becee7bc4922fe6207288d57/tiktoken-0.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9baf52f84a3f42eef3ff4e754a0db79a13a27921b457ca9832cf944c6be4f8f3", size = 1255097, upload-time = "2025-10-06T20:22:07.403Z" }, - { url = "https://files.pythonhosted.org/packages/8e/32/45d02e2e0ea2be3a9ed22afc47d93741247e75018aac967b713b2941f8ea/tiktoken-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:b8a0cd0c789a61f31bf44851defbd609e8dd1e2c8589c614cc1060940ef1f697", size = 879117, upload-time = "2025-10-06T20:22:08.418Z" }, - { url = "https://files.pythonhosted.org/packages/ce/76/994fc868f88e016e6d05b0da5ac24582a14c47893f4474c3e9744283f1d5/tiktoken-0.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d5f89ea5680066b68bcb797ae85219c72916c922ef0fcdd3480c7d2315ffff16", size = 1050309, upload-time = "2025-10-06T20:22:10.939Z" }, - { url = "https://files.pythonhosted.org/packages/f6/b8/57ef1456504c43a849821920d582a738a461b76a047f352f18c0b26c6516/tiktoken-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b4e7ed1c6a7a8a60a3230965bdedba8cc58f68926b835e519341413370e0399a", size = 993712, upload-time = "2025-10-06T20:22:12.115Z" }, - { url = "https://files.pythonhosted.org/packages/72/90/13da56f664286ffbae9dbcfadcc625439142675845baa62715e49b87b68b/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:fc530a28591a2d74bce821d10b418b26a094bf33839e69042a6e86ddb7a7fb27", size = 1128725, upload-time = "2025-10-06T20:22:13.541Z" }, - { url = "https://files.pythonhosted.org/packages/05/df/4f80030d44682235bdaecd7346c90f67ae87ec8f3df4a3442cb53834f7e4/tiktoken-0.12.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:06a9f4f49884139013b138920a4c393aa6556b2f8f536345f11819389c703ebb", size = 1151875, upload-time = "2025-10-06T20:22:14.559Z" }, - { url = "https://files.pythonhosted.org/packages/22/1f/ae535223a8c4ef4c0c1192e3f9b82da660be9eb66b9279e95c99288e9dab/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:04f0e6a985d95913cabc96a741c5ffec525a2c72e9df086ff17ebe35985c800e", size = 1194451, upload-time = "2025-10-06T20:22:15.545Z" }, - { url = "https://files.pythonhosted.org/packages/78/a7/f8ead382fce0243cb625c4f266e66c27f65ae65ee9e77f59ea1653b6d730/tiktoken-0.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0ee8f9ae00c41770b5f9b0bb1235474768884ae157de3beb5439ca0fd70f3e25", size = 1253794, upload-time = "2025-10-06T20:22:16.624Z" }, - { url = "https://files.pythonhosted.org/packages/93/e0/6cc82a562bc6365785a3ff0af27a2a092d57c47d7a81d9e2295d8c36f011/tiktoken-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dc2dd125a62cb2b3d858484d6c614d136b5b848976794edfb63688d539b8b93f", size = 878777, upload-time = "2025-10-06T20:22:18.036Z" }, -] - [[package]] name = "tokenizers" version = "0.22.2" @@ -5125,24 +4493,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" }, ] -[[package]] -name = "verspec" -version = "0.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/44/8126f9f0c44319b2efc65feaad589cadef4d77ece200ae3c9133d58464d0/verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e", size = 27123, upload-time = "2020-11-30T02:24:09.646Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31", size = 19640, upload-time = "2020-11-30T02:24:08.387Z" }, -] - -[[package]] -name = "waitress" -version = "3.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/bf/cb/04ddb054f45faa306a230769e868c28b8065ea196891f09004ebace5b184/waitress-3.0.2.tar.gz", hash = "sha256:682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f", size = 179901, upload-time = "2024-11-16T20:02:35.195Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/57/a27182528c90ef38d82b636a11f606b0cbb0e17588ed205435f8affe3368/waitress-3.0.2-py3-none-any.whl", hash = "sha256:c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e", size = 56232, upload-time = "2024-11-16T20:02:33.858Z" }, -] - [[package]] name = "watchdog" version = "6.0.0" @@ -5259,18 +4609,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, ] -[[package]] -name = "werkzeug" -version = "3.1.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", hash = "sha256:9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44", size = 875852, upload-time = "2026-04-02T18:49:14.268Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", hash = "sha256:63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", size = 226459, upload-time = "2026-04-02T18:49:12.72Z" }, -] - [[package]] name = "xlsxwriter" version = "3.2.9"