diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index edffb28636..3266e3c2ce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -158,8 +158,7 @@ Documentation tooling requires Node.js 22. The Fern CLI version is pinned in `fern/fern.config.json` and invoked through `npx`; no separate Python docs dependency group is required. -Valid optional extras are `sdd`, `eval`, `gcp`, `tracing`, `jailbreak`, -`multilingual`, `server`, `chat-ui`, and `all`. For example: +Valid optional extras are `eval`, `tracing`, `server`, `chat-ui`, and `all`. For example: ```bash uv sync --locked --extra server --extra tracing diff --git a/docs/configure-rails/guardrail-catalog/agentic-security.mdx b/docs/configure-rails/guardrail-catalog/agentic-security.mdx index 03135e409d..5bf58d5c96 100644 --- a/docs/configure-rails/guardrail-catalog/agentic-security.mdx +++ b/docs/configure-rails/guardrail-catalog/agentic-security.mdx @@ -108,7 +108,7 @@ For information about writing YARA rules, refer to the [YARA documentation](http ### Example -Before you begin, install the `yara-python` package or you can install the NeMo Guardrails package with `pip install nemoguardrails[jailbreak]`. +Before you begin, install the `yara-python` package with `pip install "yara-python>=4.5.1,<5"`. 1. Set your NVIDIA API key as an environment variable: diff --git a/docs/configure-rails/guardrail-catalog/community/cleanlab.mdx b/docs/configure-rails/guardrail-catalog/community/cleanlab.mdx index 2cd929eb0c..ce11581652 100644 --- a/docs/configure-rails/guardrail-catalog/community/cleanlab.mdx +++ b/docs/configure-rails/guardrail-catalog/community/cleanlab.mdx @@ -28,8 +28,8 @@ define bot response untrustworthy Install the Python client to use Cleanlab's trustworthiness score: -``` -pip install --upgrade cleanlab-tlm +```bash +pip install --upgrade cleanlab-studio ``` You can get an API key for free by [creating a Cleanlab account](https://tlm.cleanlab.ai/) or experiment with the trustworthiness scores in the [playground](https://tlm.cleanlab.ai/). Feel free to [email Cleanlab](mailto:support@cleanlab.ai) with any questions. diff --git a/docs/configure-rails/guardrail-catalog/community/gcp-text-moderations.mdx b/docs/configure-rails/guardrail-catalog/community/gcp-text-moderations.mdx index c8d7470f4d..293452b329 100644 --- a/docs/configure-rails/guardrail-catalog/community/gcp-text-moderations.mdx +++ b/docs/configure-rails/guardrail-catalog/community/gcp-text-moderations.mdx @@ -3,7 +3,19 @@ # SPDX-License-Identifier: Apache-2.0 title: "GCP Text Moderation Integration" --- -NeMo Guardrails supports using the [GCP Text Moderation API](https://cloud.google.com/natural-language/docs/moderating-text) as an input rail out-of-the-box. There are many ways you can authentication on GCP, refer to this [link](https://cloud.google.com/docs/authentication/application-default-credentials) for more details . +NeMo Guardrails supports using the [GCP Text Moderation API](https://cloud.google.com/natural-language/docs/moderating-text) as an input rail out-of-the-box. + +## Setup + +Install the Google Cloud Natural Language client: + +```bash +pip install "google-cloud-language>=2.14" +``` + +Configure [Application Default Credentials](https://cloud.google.com/docs/authentication/application-default-credentials) for the environment that runs the rail. + +## Usage ```yaml rails: diff --git a/docs/configure-rails/guardrail-catalog/community/presidio.mdx b/docs/configure-rails/guardrail-catalog/community/presidio.mdx index 18feb52226..386fdf450d 100644 --- a/docs/configure-rails/guardrail-catalog/community/presidio.mdx +++ b/docs/configure-rails/guardrail-catalog/community/presidio.mdx @@ -7,19 +7,16 @@ NeMo Guardrails supports detecting sensitive data out-of-the-box using [Presidio ## Setup -To use the built-in sensitive data detection rails, you must install Presidio and download the `en_core_web_lg` model for `spacy`. +To use the built-in sensitive data detection rails, you must use Python 3.10 through 3.12, install Presidio and spaCy, and download the `en_core_web_lg` spaCy model. ```bash -pip install presidio-analyzer presidio-anonymizer spacy +pip install "presidio-analyzer>=2.2; python_version < '3.13'" \ + "presidio-anonymizer>=2.2; python_version < '3.13'" \ + "spacy>=3.4.4,<4,!=3.7.0; python_version < '3.13'" python -m spacy download en_core_web_lg ``` -As an alternative, you can also use the `sdd` extra. - -```bash -pip install nemoguardrails[sdd] -python -m spacy download en_core_web_lg -``` +The sensitive data detection rail is not supported on Python 3.13. The `nemoguardrails rails validate` command reports this as an unsupported environment. ## Usage diff --git a/docs/configure-rails/guardrail-catalog/content-safety.mdx b/docs/configure-rails/guardrail-catalog/content-safety.mdx index 27d90536bf..52ca6a3258 100644 --- a/docs/configure-rails/guardrail-catalog/content-safety.mdx +++ b/docs/configure-rails/guardrail-catalog/content-safety.mdx @@ -159,10 +159,10 @@ If the detected language is not in this list, English is used as the fallback. ### Installation -To use multilingual refusal messages, install the NeMo Guardrails library with the `multilingual` extra: +To use multilingual refusal messages, install `fast-langdetect`: ```bash -pip install nemoguardrails[multilingual] +pip install "fast-langdetect>=1" ``` ### Usage diff --git a/docs/configure-rails/guardrail-catalog/jailbreak-protection.mdx b/docs/configure-rails/guardrail-catalog/jailbreak-protection.mdx index 056d8d79f3..ea2c4466e2 100644 --- a/docs/configure-rails/guardrail-catalog/jailbreak-protection.mdx +++ b/docs/configure-rails/guardrail-catalog/jailbreak-protection.mdx @@ -109,10 +109,10 @@ There is currently one available model-based detection, using a random forest-ba The recommended way to use the jailbreak detection heuristics in production is to [deploy the jailbreak detection heuristics server](/more-deployment-options/using-docker#jailbreak-detection-heuristics) separately. For a hands-on configuration walkthrough, see [Use Jailbreak Detection Heuristics](/get-started/tutorials/jailbreak-detection-heuristics). -For quick testing, you can use the jailbreak detection heuristics rail locally by first installing `transformers` and `torch`. +For quick testing, you can use the jailbreak detection heuristics rail locally by installing its optional local dependencies. ```bash -pip install transformers torch +pip install "transformers>=4.35" "torch>=2" huggingface-hub ``` ### Latency diff --git a/docs/getting-started/installation-guide.mdx b/docs/getting-started/installation-guide.mdx index 01d935dd0a..292b05c452 100644 --- a/docs/getting-started/installation-guide.mdx +++ b/docs/getting-started/installation-guide.mdx @@ -98,22 +98,33 @@ uv run nemoguardrails server --config examples/configs -## Extra Dependencies - -You can install the NeMo Guardrails library with optional extra packages to add useful functionalities. The table below shows a comprehensive list. - -| Extra | Description | -| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `server` | [Guardrails API server](/run-guardrailed-inference/using-fastapi-server/overview) dependencies (aiofiles for async file handling, openai for API schemas). FastAPI is a core dependency. Required to run `nemoguardrails server`. | -| `sdd` | [Sensitive data detection](/configure-guardrails/guardrail-catalog/pii-detection#presidio-based-sensitive-data-detection) using Presidio | -| `eval` | [Policy-based evaluation tools](/evaluation/evaluate-configuration) for testing guardrails | -| `tracing` | OpenTelemetry tracing support | -| `gcp` | Google Cloud Platform language services | -| `jailbreak` | YARA-based jailbreak detection heuristics | -| `multilingual` | Language detection for multilingual content | -| `all` | All optional packages | - -Some features such as [AlignScore](/configure-guardrails/guardrail-catalog/third-party/alignscore) have additional requirements. See the feature documentation for details. +## Optional Extras and Rail Dependencies + +### Optional Extras + +Install an optional extra when you need platform or development functionality that is not part of the default installation. + +| Extra | Description | +| --------- | ----------- | +| `server` | Dependencies for the [Guardrails API server](/run-guardrailed-inference/using-fastapi-server/overview), including API schemas and file watching. | +| `eval` | [Policy-based evaluation tools](/evaluation/evaluate-configuration) for testing guardrails. | +| `tracing` | OpenTelemetry tracing support. | +| `chat-ui` | Chainlit-based chat interface. | +| `all` | Packages from all of the extras listed above. | + +### Rail Dependencies + +Catalog rails declare their own Python package, model, service, and environment-variable requirements. Rail-specific Python packages are not included in the NeMo Guardrails extras. Install them directly by following the setup instructions in the [Guardrail Catalog](/configure-guardrails/guardrail-catalog). + +After you configure your rails, validate their requirements: + +```bash +nemoguardrails rails validate --config ./config +``` + +The command reports missing or incompatible packages and missing environment variables. It does not install packages. For the command options and validation behavior, refer to the [`rails validate` CLI reference](/reference/cli#rails-validate). + +Some rails, such as [AlignScore](/configure-guardrails/guardrail-catalog/third-party/alignscore), require additional setup. See each rail's catalog page for package, model, credential, and service requirements. ## Docker diff --git a/docs/getting-started/tutorials/jailbreak-detection-heuristics.mdx b/docs/getting-started/tutorials/jailbreak-detection-heuristics.mdx index 590dfe419d..fd59beda4a 100644 --- a/docs/getting-started/tutorials/jailbreak-detection-heuristics.mdx +++ b/docs/getting-started/tutorials/jailbreak-detection-heuristics.mdx @@ -37,7 +37,7 @@ export OPENAI_API_KEY=$OPENAI_API_KEY # Replace with your own key 1. Install the following packages to test the jailbreak detection heuristics locally: ```bash -pip install transformers torch +pip install "transformers>=4.35" "torch>=2" huggingface-hub ``` 1. If you're running this inside a notebook, patch the `AsyncIO` loop. diff --git a/docs/reference/cli/index.mdx b/docs/reference/cli/index.mdx index b56cba3744..c96634b15f 100644 --- a/docs/reference/cli/index.mdx +++ b/docs/reference/cli/index.mdx @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 title: "NeMo Guardrails Library CLI Reference" sidebar-title: "CLI" -description: "Use the nemoguardrails command-line interface to chat, serve, evaluate, and convert configurations." +description: "Use the nemoguardrails command-line interface to chat, serve, validate, evaluate, and convert configurations." content: type: "reference" --- @@ -32,6 +32,7 @@ Commands: the latest. actions-server Start a NeMo Guardrails actions server. find-providers List and select LLM providers interactively. + rails Inspect and validate catalog rails. eval Evaluation a guardrail configuration. ``` @@ -58,6 +59,38 @@ nemoguardrails chat --config examples/configs/content_safety [--verbose] [--stre | `--server-url` | Connect to a server instead of loading config locally. Requires `--config-id`. | | `--config-id` | The config ID to use when connecting to a server. | +--- + + +## `rails validate` + +Validate the dependencies declared by the catalog rails referenced in a guardrails configuration: + +```bash +nemoguardrails rails validate --config ./config +``` + +If you omit `--config`, the command loads the configuration from `./config`. + +By default, validation uses installed distribution metadata to check Python package versions. It also checks whether required environment-variable names are present and reports declared services and models as unverified. Static validation does not import optional packages, read or display environment-variable values, initialize or download models, or contact services. + +Use runtime validation to additionally import each declared Python package: + +```bash +nemoguardrails rails validate --config ./config --runtime +``` + +Runtime validation does not initialize or download models or contact services. + +The command exits with status `1` when a required package is missing, incompatible, or unsupported in the active environment; when a required environment variable is missing; or when a required runtime import fails. Optional requirements produce warnings and do not cause a nonzero exit status. When possible, the output includes separate `pip install` commands for required and optional packages. The command never installs packages. + +### Options + +| Option | Description | +|--------|-------------| +| `--config` | Path to a guardrails configuration file or directory. Default: `config`. | +| `--runtime` | Import declared Python packages after static validation. | + ## `actions-server` Start a separate server for running custom actions in an isolated environment. diff --git a/nemoguardrails/cli/__init__.py b/nemoguardrails/cli/__init__.py index 154f36eb03..9fba63e7b0 100644 --- a/nemoguardrails/cli/__init__.py +++ b/nemoguardrails/cli/__init__.py @@ -25,6 +25,7 @@ from nemoguardrails.cli.chat import run_chat from nemoguardrails.cli.migration import migrate from nemoguardrails.cli.providers import _list_providers, select_provider_with_type +from nemoguardrails.cli.rails import app as rails_app from nemoguardrails.eval import cli as eval_cli from nemoguardrails.logging.verbose import set_verbose from nemoguardrails.utils import init_random_seed @@ -41,6 +42,7 @@ class ColangVersions(str, Enum): app = typer.Typer() app.add_typer(eval_cli.app, name="eval", short_help="Evaluation a guardrail configuration.") +app.add_typer(rails_app, name="rails", short_help="Inspect and validate catalog rails.") app.pretty_exceptions_enable = False logging.getLogger().setLevel(logging.WARNING) diff --git a/nemoguardrails/cli/rails.py b/nemoguardrails/cli/rails.py new file mode 100644 index 0000000000..0d7ee57c20 --- /dev/null +++ b/nemoguardrails/cli/rails.py @@ -0,0 +1,63 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import shlex +from pathlib import Path + +import typer + +from nemoguardrails.manifests import ( + RequirementStatus, + configured_rail_manifests, + default_rail_catalog, + validate_rail_requirements, +) +from nemoguardrails.rails.llm.config import RailsConfig + +app = typer.Typer() + + +@app.command("validate") +def validate_rails( + config: Path = typer.Option(Path("config"), "--config", exists=True, help="Guardrails configuration path."), + runtime: bool = typer.Option(False, "--runtime", help="Import declared Python packages after static checks."), +): + """Validate dependencies declared by configured rails.""" + rails_config = RailsConfig.from_path(str(config)) + catalog = default_rail_catalog() + manifests = configured_rail_manifests(rails_config, catalog) + report = validate_rail_requirements(manifests, runtime=runtime) + if not report.results: + typer.echo("No catalog rails are configured.") + for result in report.results: + typer.echo(f"{result.rail_name}:") + if not result.checks: + typer.echo(" [OK] no declared requirements") + for check in result.checks: + label = { + RequirementStatus.OK: "OK", + RequirementStatus.WARNING: "WARNING", + RequirementStatus.ERROR: "ERROR", + RequirementStatus.INFO: "INFO", + }[check.status] + typer.echo(f" [{label}] {check.kind}: {check.name} ({check.message})") + if report.required_packages_to_install: + packages = " ".join(shlex.quote(package) for package in report.required_packages_to_install) + typer.echo(f"Install required packages with: pip install {packages}") + if report.optional_packages_to_install: + packages = " ".join(shlex.quote(package) for package in report.optional_packages_to_install) + typer.echo(f"Install optional packages to enable additional functionality with: pip install {packages}") + if not report.valid: + raise typer.Exit(1) diff --git a/nemoguardrails/library/cleanlab/actions.py b/nemoguardrails/library/cleanlab/actions.py index 85dc30b2af..0a606946cd 100644 --- a/nemoguardrails/library/cleanlab/actions.py +++ b/nemoguardrails/library/cleanlab/actions.py @@ -18,6 +18,8 @@ from nemoguardrails.actions import action from nemoguardrails.actions.rail_outcome import RailOutcome +from nemoguardrails.library.cleanlab.rail import CLEANLAB_PACKAGE +from nemoguardrails.manifests import require_python_package log = logging.getLogger(__name__) @@ -43,10 +45,8 @@ async def call_cleanlab_api( if api_key is None: raise ValueError("CLEANLAB_API_KEY environment variable not set.") - try: - from cleanlab_studio import Studio # type: ignore[reportMissingImports] - except ImportError: - raise ImportError("Please install cleanlab-studio using 'pip install --upgrade cleanlab-studio' command") + cleanlab_studio = require_python_package("cleanlab", CLEANLAB_PACKAGE) + Studio = cleanlab_studio.Studio context = context or {} bot_response = context.get("bot_message") diff --git a/nemoguardrails/library/cleanlab/rail.py b/nemoguardrails/library/cleanlab/rail.py index ae4edae6ff..099e31eb2c 100644 --- a/nemoguardrails/library/cleanlab/rail.py +++ b/nemoguardrails/library/cleanlab/rail.py @@ -16,6 +16,7 @@ from nemoguardrails.manifests import ( ActionRef, EnvVar, + PythonPackage, RailActions, RailDirection, RailFlows, @@ -32,6 +33,7 @@ name="call_cleanlab_api", target="nemoguardrails.library.cleanlab.actions:call_cleanlab_api", ) +CLEANLAB_PACKAGE = PythonPackage(distribution="cleanlab-studio", import_name="cleanlab_studio") RAIL = RailManifest( name="cleanlab", @@ -52,7 +54,7 @@ requirements=RailRequirements( env_vars=(EnvVar(name="CLEANLAB_API_KEY", required=True),), services=(ServiceRequirement(name="Cleanlab TLM", required=True),), - optional_dependencies=("cleanlab-studio",), + python_packages=(CLEANLAB_PACKAGE,), ), privacy=RailPrivacy(sends_user_text=True, sends_bot_text=True, remote_services=("Cleanlab TLM",)), ), diff --git a/nemoguardrails/library/content_safety/rail.py b/nemoguardrails/library/content_safety/rail.py index 678b4cc081..eb90849a08 100644 --- a/nemoguardrails/library/content_safety/rail.py +++ b/nemoguardrails/library/content_safety/rail.py @@ -17,6 +17,7 @@ ActionRef, Binding, ConfigSpecRef, + PythonPackage, RailActions, RailConfigSchema, RailDirection, @@ -24,6 +25,7 @@ RailManifest, RailMetadata, RailPrivacy, + RailRequirements, RailSpec, RailSurface, ) @@ -40,6 +42,9 @@ name="detect_language", target="nemoguardrails.library.content_safety.actions:detect_language", ) +FAST_LANGDETECT_PACKAGE = PythonPackage( + distribution="fast-langdetect", import_name="fast_langdetect", version=">=1", required=False +) RAIL = RailManifest( name="content_safety", metadata=RailMetadata( @@ -79,6 +84,7 @@ bindings=(Binding.surface_param("model_name", "model"),), ), ), + requirements=RailRequirements(python_packages=(FAST_LANGDETECT_PACKAGE,)), privacy=RailPrivacy(sends_user_text=True, sends_bot_text=True), ), ) diff --git a/nemoguardrails/library/gcp_moderate_text/actions.py b/nemoguardrails/library/gcp_moderate_text/actions.py index 6d6b36dc44..138697dcbd 100644 --- a/nemoguardrails/library/gcp_moderate_text/actions.py +++ b/nemoguardrails/library/gcp_moderate_text/actions.py @@ -18,6 +18,8 @@ from nemoguardrails.actions import action from nemoguardrails.actions.rail_outcome import RailOutcome +from nemoguardrails.library.gcp_moderate_text.rail import GCP_LANGUAGE_PACKAGE +from nemoguardrails.manifests import require_python_package log = logging.getLogger(__name__) @@ -95,13 +97,7 @@ async def call_gcp_text_moderation_api( For more information check https://cloud.google.com/docs/authentication/application-default-credentials """ - try: - from google.cloud import language_v2 # type: ignore[reportAttributeAccessIssue] - - except ImportError: - raise ImportError( - "Could not import google.cloud.language_v2, please install it with `pip install google-cloud-language`." - ) + language_v2 = require_python_package("gcp_moderate_text", GCP_LANGUAGE_PACKAGE) context = context or {} user_message = context.get("user_message") diff --git a/nemoguardrails/library/gcp_moderate_text/rail.py b/nemoguardrails/library/gcp_moderate_text/rail.py index 6ed6416d97..03be82270d 100644 --- a/nemoguardrails/library/gcp_moderate_text/rail.py +++ b/nemoguardrails/library/gcp_moderate_text/rail.py @@ -17,6 +17,7 @@ ActionRef, Binding, EnvVar, + PythonPackage, RailActions, RailDirection, RailFlows, @@ -33,6 +34,9 @@ name="call_gcpnlp_api", target="nemoguardrails.library.gcp_moderate_text.actions:call_gcp_text_moderation_api", ) +GCP_LANGUAGE_PACKAGE = PythonPackage( + distribution="google-cloud-language", import_name="google.cloud.language_v2", version=">=2.14" +) RAIL = RailManifest( name="gcp_moderate_text", @@ -64,7 +68,7 @@ requirements=RailRequirements( env_vars=(EnvVar(name="GOOGLE_APPLICATION_CREDENTIALS", required=False),), services=(ServiceRequirement(name="Google Cloud Natural Language API", required=True),), - optional_dependencies=("google-cloud-language",), + python_packages=(GCP_LANGUAGE_PACKAGE,), ), privacy=RailPrivacy(sends_user_text=True, remote_services=("Google Cloud Natural Language API",)), ), diff --git a/nemoguardrails/library/guardrails_ai/actions.py b/nemoguardrails/library/guardrails_ai/actions.py index af4d89413a..23dec46e7a 100644 --- a/nemoguardrails/library/guardrails_ai/actions.py +++ b/nemoguardrails/library/guardrails_ai/actions.py @@ -20,25 +20,12 @@ from functools import lru_cache from typing import Any, Dict, Optional, Type -try: - from guardrails import Guard # type: ignore[reportAttributeAccessIssue] -except ImportError: - - class Guard: - def __init__(self): - pass - - def use(self, validator): - return self - - def validate(self, text, metadata=None): - return None - - from nemoguardrails.actions import action from nemoguardrails.actions.rail_outcome import RailOutcome from nemoguardrails.library.guardrails_ai.errors import GuardrailsAIValidationError +from nemoguardrails.library.guardrails_ai.rail import GUARDRAILS_AI_PACKAGE from nemoguardrails.library.guardrails_ai.registry import get_validator_info +from nemoguardrails.manifests import RailDependencyError, require_python_package from nemoguardrails.rails.llm.config import RailsConfig log = logging.getLogger(__name__) @@ -46,7 +33,13 @@ def validate(self, text, metadata=None): # cache for loaded validator classes and guard instances _validator_class_cache: Dict[str, Type] = {} -_guard_cache: Dict[tuple, Guard] = {} +_guard_cache: Dict[tuple, Any] = {} + + +@lru_cache +def _load_guard_class(): + module = require_python_package("guardrails_ai", GUARDRAILS_AI_PACKAGE) + return module.Guard def _guardrails_ai_validation_passed(result: Dict[str, Any]) -> bool: @@ -221,6 +214,8 @@ class FailedValidation: return {"validation_result": FailedValidation()} + except RailDependencyError: + raise except Exception as e: log.error(f"Error validating with {validator_name}: {str(e)}") raise GuardrailsAIValidationError(f"Validation failed: {str(e)}") @@ -259,9 +254,11 @@ def _load_validator_class(validator_name: str) -> Type: raise ImportError(f"Failed to load validator {validator_name}: {str(e)}") -def _get_guard(validator_name: str, **validator_params) -> Guard: +def _get_guard(validator_name: str, **validator_params): """Get or create a Guard instance for a validator.""" + guard_class = _load_guard_class() + # create a hashable cache key def make_hashable(obj): if isinstance(obj, list): @@ -287,7 +284,7 @@ def make_hashable(obj): log.error(f"Failed to instantiate {validator_name} with params {validator_params}: {str(e)}") raise - guard = Guard().use(validator_instance) + guard = guard_class().use(validator_instance) _guard_cache[cache_key] = guard return _guard_cache[cache_key] diff --git a/nemoguardrails/library/guardrails_ai/rail.py b/nemoguardrails/library/guardrails_ai/rail.py index 98d019daeb..5fe09a229b 100644 --- a/nemoguardrails/library/guardrails_ai/rail.py +++ b/nemoguardrails/library/guardrails_ai/rail.py @@ -17,6 +17,7 @@ ActionRef, Binding, ConfigSpecRef, + PythonPackage, RailActions, RailConfigSchema, RailDirection, @@ -38,6 +39,7 @@ name="validate_guardrails_ai_output", target="nemoguardrails.library.guardrails_ai.actions:validate_guardrails_ai_output", ) +GUARDRAILS_AI_PACKAGE = PythonPackage(distribution="guardrails-ai", import_name="guardrails") RAIL = RailManifest( name="guardrails_ai", @@ -86,7 +88,7 @@ ), requirements=RailRequirements( services=(ServiceRequirement(name="Guardrails Hub", required=False),), - optional_dependencies=("guardrails-ai",), + python_packages=(GUARDRAILS_AI_PACKAGE,), ), privacy=RailPrivacy(sends_user_text=True, sends_bot_text=True), ), diff --git a/nemoguardrails/library/hf_classifier/backends.py b/nemoguardrails/library/hf_classifier/backends.py index c3be644be1..f44d489925 100644 --- a/nemoguardrails/library/hf_classifier/backends.py +++ b/nemoguardrails/library/hf_classifier/backends.py @@ -27,6 +27,9 @@ import httpx +from nemoguardrails.library.hf_classifier.rail import TORCH_PACKAGE, TRANSFORMERS_PACKAGE +from nemoguardrails.manifests import raise_for_missing_package, require_python_package + if TYPE_CHECKING: from nemoguardrails.rails.llm.config import ( HFClassifierConfig, @@ -223,15 +226,13 @@ def _get_or_create_pipeline( return pending.wait() try: - try: - from transformers import pipeline - except ImportError: - raise ImportError( - "The 'transformers' package is required for the local HF classifier " - "backend. Install it with: pip install transformers torch" - ) + transformers = require_python_package("hf_classifier", TRANSFORMERS_PACKAGE) + pipeline = transformers.pipeline kwargs = {k: v for k, v in parameters.items() if k not in _HTTP_ONLY_PARAMS} - pipe = pipeline(task=task, model=model_name, **kwargs) + try: + pipe = pipeline(task=task, model=model_name, **kwargs) + except ModuleNotFoundError as error: + raise_for_missing_package("hf_classifier", (TRANSFORMERS_PACKAGE, TORCH_PACKAGE), error) except BaseException as exc: loading.error = exc loading.ready.set() diff --git a/nemoguardrails/library/hf_classifier/rail.py b/nemoguardrails/library/hf_classifier/rail.py index c44e7465f8..0611ba3be1 100644 --- a/nemoguardrails/library/hf_classifier/rail.py +++ b/nemoguardrails/library/hf_classifier/rail.py @@ -17,6 +17,8 @@ ActionRef, Binding, ConfigSpecRef, + EnvVar, + PythonPackage, RailActions, RailConfigSchema, RailDirection, @@ -43,6 +45,10 @@ name="hf_classifier_check_retrieval", target="nemoguardrails.library.hf_classifier.actions:hf_classifier_check_retrieval", ) +TRANSFORMERS_PACKAGE = PythonPackage( + distribution="transformers", import_name="transformers", version=">=4.35", required=False +) +TORCH_PACKAGE = PythonPackage(distribution="torch", import_name="torch", version=">=2", required=False) RAIL = RailManifest( name="hf_classifier", @@ -94,12 +100,13 @@ ), ), requirements=RailRequirements( + python_packages=(TRANSFORMERS_PACKAGE, TORCH_PACKAGE), + env_vars=(EnvVar(name="HF_TOKEN", required=False),), services=( ServiceRequirement(name="vLLM classifier endpoint", required=False), ServiceRequirement(name="KServe classifier endpoint", required=False), ServiceRequirement(name="FMS guardrails-detectors endpoint", required=False), ), - optional_dependencies=("transformers",), ), privacy=RailPrivacy( sends_user_text=True, diff --git a/nemoguardrails/library/injection_detection/actions.py b/nemoguardrails/library/injection_detection/actions.py index 54f6fc2b2e..3b3b4e05ca 100644 --- a/nemoguardrails/library/injection_detection/actions.py +++ b/nemoguardrails/library/injection_detection/actions.py @@ -29,19 +29,16 @@ # limitations under the License. import logging +from functools import lru_cache from pathlib import Path from typing import Dict, List, Optional, Tuple, TypedDict, Union -yara = None -try: - import yara -except ImportError: - pass - from nemoguardrails import RailsConfig # noqa: E402 from nemoguardrails.actions import action # noqa: E402 from nemoguardrails.actions.rail_outcome import RailOutcome, TransformTarget # noqa: E402 +from nemoguardrails.library.injection_detection.rail import YARA_PACKAGE from nemoguardrails.library.injection_detection.yara_config import ActionOptions, Rules # noqa: E402 +from nemoguardrails.manifests import require_python_package YARA_DIR = Path(__file__).resolve().parent.joinpath("yara_rules") @@ -68,11 +65,9 @@ def _injection_detection_outcome( return RailOutcome.allow(metadata=metadata) -def _check_yara_available(): - if yara is None: - raise ImportError( - "The yara module is required for injection detection. Please install it using: pip install yara-python" - ) +@lru_cache +def _load_yara(): + return require_python_package("injection_detection", YARA_PACKAGE) def _validate_injection_config(config: RailsConfig) -> None: @@ -183,6 +178,7 @@ def _load_rules( log.warning("Injection config was provided but no modules were specified. Returning None.") return None + yara = _load_yara() try: if yara_rules: rules_source = {name: rule for name, rule in yara_rules.items() if name in rule_names} @@ -336,7 +332,7 @@ async def injection_detection(text: str, config: RailsConfig) -> RailOutcome: NotImplementedError: If an unsupported action is encountered. ImportError: If the yara module is not installed. """ - _check_yara_available() + _load_yara() _validate_injection_config(config) diff --git a/nemoguardrails/library/injection_detection/rail.py b/nemoguardrails/library/injection_detection/rail.py index 59ca12be61..c443ca37cd 100644 --- a/nemoguardrails/library/injection_detection/rail.py +++ b/nemoguardrails/library/injection_detection/rail.py @@ -17,6 +17,7 @@ ActionRef, Binding, ConfigSpecRef, + PythonPackage, RailActions, RailConfigSchema, RailDirection, @@ -34,6 +35,7 @@ name="injection_detection", target="nemoguardrails.library.injection_detection.actions:injection_detection", ) +YARA_PACKAGE = PythonPackage(distribution="yara-python", import_name="yara", version=">=4.5.1,<5") RAIL = RailManifest( name="injection_detection", @@ -61,7 +63,7 @@ transform_target=TransformTarget.BOT_MESSAGE, ), ), - requirements=RailRequirements(optional_dependencies=("yara-python",)), + requirements=RailRequirements(python_packages=(YARA_PACKAGE,)), privacy=RailPrivacy(sends_bot_text=True), ), ) diff --git a/nemoguardrails/library/jailbreak_detection/actions.py b/nemoguardrails/library/jailbreak_detection/actions.py index af50e1ab0a..2029b84fc6 100644 --- a/nemoguardrails/library/jailbreak_detection/actions.py +++ b/nemoguardrails/library/jailbreak_detection/actions.py @@ -35,6 +35,11 @@ from nemoguardrails.actions import action from nemoguardrails.actions.rail_outcome import RailOutcome from nemoguardrails.context import llm_call_info_var +from nemoguardrails.library.jailbreak_detection.rail import ( + HUGGINGFACE_HUB_PACKAGE, + TORCH_PACKAGE, + TRANSFORMERS_PACKAGE, +) from nemoguardrails.library.jailbreak_detection.request import ( jailbreak_detection_heuristics_request, jailbreak_detection_model_request, @@ -49,6 +54,7 @@ from nemoguardrails.llm.taskmanager import LLMTaskManager from nemoguardrails.logging.explain import LLMCallInfo from nemoguardrails.logging.processing_log import processing_log_var +from nemoguardrails.manifests import raise_for_missing_package log = logging.getLogger(__name__) @@ -69,10 +75,15 @@ async def jailbreak_detection_heuristics( prompt = context.get("user_message") if not jailbreak_api_url: - from nemoguardrails.library.jailbreak_detection.heuristics.checks import ( - check_jailbreak_length_per_perplexity, - check_jailbreak_prefix_suffix_perplexity, - ) + try: + from nemoguardrails.library.jailbreak_detection.heuristics.checks import ( + check_jailbreak_length_per_perplexity, + check_jailbreak_prefix_suffix_perplexity, + ) + except ModuleNotFoundError as error: + raise_for_missing_package( + "jailbreak_detection", (TRANSFORMERS_PACKAGE, TORCH_PACKAGE, HUGGINGFACE_HUB_PACKAGE), error + ) log.warning("No jailbreak detection endpoint set. Running in-process, NOT RECOMMENDED FOR PRODUCTION.") lp_check = check_jailbreak_length_per_perplexity(prompt, lp_threshold) @@ -132,9 +143,14 @@ async def jailbreak_detection_model( api_start_time = time() if not jailbreak_api_url and not nim_base_url: - from nemoguardrails.library.jailbreak_detection.model_based.checks import ( - check_jailbreak, - ) + try: + from nemoguardrails.library.jailbreak_detection.model_based.checks import ( + check_jailbreak, + ) + except ModuleNotFoundError as error: + raise_for_missing_package( + "jailbreak_detection", (TRANSFORMERS_PACKAGE, TORCH_PACKAGE, HUGGINGFACE_HUB_PACKAGE), error + ) log.warning("No jailbreak detection endpoint set. Running in-process, NOT RECOMMENDED FOR PRODUCTION.") try: @@ -144,12 +160,6 @@ async def jailbreak_detection_model( except RuntimeError as e: log.error(f"Jailbreak detection model not available: {e}") jailbreak_result = False - except ImportError as e: - log.error( - "Failed to import required dependencies for local model. Install scikit-learn and torch, or use NIM-based approach", - exc_info=e, - ) - jailbreak_result = False else: if nim_base_url: jailbreak = await jailbreak_nim_request( diff --git a/nemoguardrails/library/jailbreak_detection/rail.py b/nemoguardrails/library/jailbreak_detection/rail.py index 920ab3382e..42329b882b 100644 --- a/nemoguardrails/library/jailbreak_detection/rail.py +++ b/nemoguardrails/library/jailbreak_detection/rail.py @@ -17,6 +17,7 @@ ActionRef, ConfigSpecRef, EnvVar, + PythonPackage, RailActions, RailConfigSchema, RailDirection, @@ -38,6 +39,11 @@ name="jailbreak_detection_model", target="nemoguardrails.library.jailbreak_detection.actions:jailbreak_detection_model", ) +TRANSFORMERS_PACKAGE = PythonPackage( + distribution="transformers", import_name="transformers", version=">=4.35", required=False +) +TORCH_PACKAGE = PythonPackage(distribution="torch", import_name="torch", version=">=2", required=False) +HUGGINGFACE_HUB_PACKAGE = PythonPackage(distribution="huggingface-hub", import_name="huggingface_hub", required=False) RAIL = RailManifest( name="jailbreak_detection", metadata=RailMetadata( @@ -75,6 +81,7 @@ ), ), requirements=RailRequirements( + python_packages=(TRANSFORMERS_PACKAGE, TORCH_PACKAGE, HUGGINGFACE_HUB_PACKAGE), env_vars=( EnvVar( name="NVIDIA_API_KEY", @@ -108,7 +115,6 @@ ), ), services=(ServiceRequirement(name="NVIDIA NIM", required=False),), - optional_dependencies=("scikit-learn", "torch"), ), privacy=RailPrivacy(sends_user_text=True, remote_services=("NVIDIA NIM",)), ), diff --git a/nemoguardrails/library/sensitive_data_detection/actions.py b/nemoguardrails/library/sensitive_data_detection/actions.py index 7ca852221c..24f2721823 100644 --- a/nemoguardrails/library/sensitive_data_detection/actions.py +++ b/nemoguardrails/library/sensitive_data_detection/actions.py @@ -17,20 +17,15 @@ from functools import lru_cache from typing import Any -try: - from presidio_analyzer import PatternRecognizer # type: ignore[reportMissingImports] - from presidio_analyzer.nlp_engine import NlpEngineProvider # type: ignore[reportMissingImports] - from presidio_anonymizer import AnonymizerEngine # type: ignore[reportMissingImports] - from presidio_anonymizer.entities import OperatorConfig # type: ignore[reportMissingImports] -except ImportError: - PatternRecognizer = None - NlpEngineProvider = None - AnonymizerEngine = None - OperatorConfig = None - from nemoguardrails import RailsConfig from nemoguardrails.actions import action from nemoguardrails.actions.rail_outcome import RailOutcome, TransformTarget +from nemoguardrails.library.sensitive_data_detection.rail import ( + PRESIDIO_ANALYZER_PACKAGE, + PRESIDIO_ANONYMIZER_PACKAGE, + SPACY_PACKAGE, +) +from nemoguardrails.manifests import require_python_package from nemoguardrails.rails.llm.config import ( SensitiveDataDetection, SensitiveDataDetectionOptions, @@ -39,22 +34,24 @@ log = logging.getLogger(__name__) +@lru_cache +def _load_presidio(): + require_python_package("sensitive_data_detection", PRESIDIO_ANALYZER_PACKAGE) + require_python_package("sensitive_data_detection", PRESIDIO_ANONYMIZER_PACKAGE) + from presidio_analyzer import AnalyzerEngine, PatternRecognizer + from presidio_analyzer.nlp_engine import NlpEngineProvider + from presidio_anonymizer import AnonymizerEngine + from presidio_anonymizer.entities import OperatorConfig + + return AnalyzerEngine, PatternRecognizer, NlpEngineProvider, AnonymizerEngine, OperatorConfig + + @lru_cache def _get_analyzer(score_threshold: float = 0.4): if not 0.0 <= score_threshold <= 1.0: raise ValueError("score_threshold must be a float between 0 and 1 (inclusive).") - try: - from presidio_analyzer import AnalyzerEngine # type: ignore[reportMissingImports] - - except ImportError: - raise ImportError( - "Could not import presidio, please install it with `pip install presidio-analyzer presidio-anonymizer`." - ) - - try: - import spacy # type: ignore[reportMissingImports] - except ImportError: - raise RuntimeError("The spacy module is not installed. Please install it using pip: pip install spacy.") + AnalyzerEngine, _, NlpEngineProvider, _, _ = _load_presidio() + spacy = require_python_package("sensitive_data_detection", SPACY_PACKAGE) if not spacy.util.is_package("en_core_web_lg"): raise RuntimeError( @@ -69,11 +66,6 @@ def _get_analyzer(score_threshold: float = 0.4): } # Create NLP engine based on configuration - if NlpEngineProvider is None: - raise ImportError( - "Could not import presidio, please install it with `pip install presidio-analyzer presidio-anonymizer`." - ) - provider = NlpEngineProvider(nlp_configuration=configuration) nlp_engine = provider.create_engine() @@ -83,10 +75,7 @@ def _get_analyzer(score_threshold: float = 0.4): def _get_ad_hoc_recognizers(sdd_config: SensitiveDataDetection): """Helper to compute the ad hoc recognizers for a config.""" - if PatternRecognizer is None: - raise ImportError( - "Could not import presidio, please install it with `pip install presidio-analyzer presidio-anonymizer`." - ) + _, PatternRecognizer, _, _, _ = _load_presidio() ad_hoc_recognizers = [] for recognizer in sdd_config.recognizers: @@ -180,10 +169,7 @@ async def mask_sensitive_data(source: str, text: str, config: RailsConfig) -> Ra return _mask_sensitive_data_outcome(source, text, text) analyzer = _get_analyzer() - if OperatorConfig is None or AnonymizerEngine is None: - raise ImportError( - "Could not import presidio, please install it with `pip install presidio-analyzer presidio-anonymizer`." - ) + _, _, _, AnonymizerEngine, OperatorConfig = _load_presidio() operators: dict[str, Any] = {} for entity in options.entities: diff --git a/nemoguardrails/library/sensitive_data_detection/rail.py b/nemoguardrails/library/sensitive_data_detection/rail.py index 0790c86a3a..b0cba30dd6 100644 --- a/nemoguardrails/library/sensitive_data_detection/rail.py +++ b/nemoguardrails/library/sensitive_data_detection/rail.py @@ -18,6 +18,7 @@ Binding, ConfigSpecRef, ModelRequirement, + PythonPackage, RailActions, RailConfigSchema, RailDirection, @@ -39,6 +40,24 @@ name="mask_sensitive_data", target="nemoguardrails.library.sensitive_data_detection.actions:mask_sensitive_data", ) +PRESIDIO_ANALYZER_PACKAGE = PythonPackage( + distribution="presidio-analyzer", + import_name="presidio_analyzer", + version=">=2.2", + marker="python_version < '3.13'", +) +PRESIDIO_ANONYMIZER_PACKAGE = PythonPackage( + distribution="presidio-anonymizer", + import_name="presidio_anonymizer", + version=">=2.2", + marker="python_version < '3.13'", +) +SPACY_PACKAGE = PythonPackage( + distribution="spacy", + import_name="spacy", + version=">=3.4.4,<4,!=3.7.0", + marker="python_version < '3.13'", +) RAIL = RailManifest( name="sensitive_data_detection", @@ -108,9 +127,8 @@ ), ), requirements=RailRequirements( - extras=("sdd",), + python_packages=(PRESIDIO_ANALYZER_PACKAGE, PRESIDIO_ANONYMIZER_PACKAGE, SPACY_PACKAGE), models=(ModelRequirement(type="spacy:en_core_web_lg", required=True),), - optional_dependencies=("presidio-analyzer", "presidio-anonymizer", "spacy"), ), privacy=RailPrivacy(sends_user_text=True, sends_bot_text=True, sends_retrieved_chunks=True), ), diff --git a/nemoguardrails/manifests/__init__.py b/nemoguardrails/manifests/__init__.py index d3dd61f3e5..3f0b95fda7 100644 --- a/nemoguardrails/manifests/__init__.py +++ b/nemoguardrails/manifests/__init__.py @@ -23,6 +23,7 @@ EnvVar, ImportRef, ModelRequirement, + PythonPackage, RailActions, RailCapability, RailCategory, @@ -50,6 +51,17 @@ normalize_configured_surface_name, parse_configured_surface, ) +from nemoguardrails.manifests.validation import ( + RailDependencyError, + RailValidationReport, + RailValidationResult, + RequirementCheck, + RequirementStatus, + configured_rail_manifests, + raise_for_missing_package, + require_python_package, + validate_rail_requirements, +) __all__ = [ "ActionRef", @@ -58,28 +70,38 @@ "EnvVar", "ImportRef", "ModelRequirement", + "PythonPackage", "RailActions", "RailCapability", "RailCatalog", "RailCategory", "RailConfigSchema", "RailDirection", + "RailDependencyError", "RailFlows", "RailLifecycle", "RailManifest", "RailManifestRecord", + "RailValidationReport", + "RailValidationResult", "RailMetadata", "RailPrivacy", "RailRequirements", "RailSpec", "RailSurface", "ServiceRequirement", + "RequirementCheck", + "RequirementStatus", "TransformTarget", "all_rail_manifests", + "configured_rail_manifests", "default_rail_catalog", "import_ref_target", "iter_manifest_import_refs", "normalize_configured_surface_name", "parse_configured_surface", + "raise_for_missing_package", + "require_python_package", "resolve_import_ref", + "validate_rail_requirements", ] diff --git a/nemoguardrails/manifests/catalog.py b/nemoguardrails/manifests/catalog.py index d60ed03255..cb481c2a9f 100644 --- a/nemoguardrails/manifests/catalog.py +++ b/nemoguardrails/manifests/catalog.py @@ -112,6 +112,7 @@ def __init__(self, records: Iterable[RailManifestRecord] = ()) -> None: records_by_name[manifest.name] = record self._records = records_by_name self._surfaces = surfaces + self._config_owners = config_owners self._flow_owners = flow_owners @classmethod @@ -148,3 +149,7 @@ def surfaces(self, direction: Optional[RailDirection] = None) -> Dict[Tuple[Rail def owner_for_flow(self, flow_name: str) -> Optional[str]: """Return the manifest that owns a declared public flow name.""" return self._flow_owners.get(flow_name) + + def owner_for_config_key(self, config_key: str) -> Optional[str]: + """Return the manifest that owns a declared rail config key.""" + return self._config_owners.get(config_key) diff --git a/nemoguardrails/manifests/manifest.py b/nemoguardrails/manifests/manifest.py index 9b063a1c68..b9f0360e01 100644 --- a/nemoguardrails/manifests/manifest.py +++ b/nemoguardrails/manifests/manifest.py @@ -26,6 +26,8 @@ from enum import Enum from typing import Any, Literal, Optional, Tuple, Union +from packaging.markers import InvalidMarker, Marker +from packaging.specifiers import InvalidSpecifier, SpecifierSet from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator from nemoguardrails.actions.rail_outcome import TransformTarget @@ -278,9 +280,48 @@ class ModelRequirement(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) +class PythonPackage(BaseModel): + distribution: str + import_name: str + version: Optional[str] = None + required: bool = True + marker: Optional[str] = None + description: Optional[str] = None + + model_config = ConfigDict(extra="forbid", frozen=True) + + @field_validator("distribution", "import_name") + @classmethod + def _name_must_not_be_empty(cls, value: str) -> str: + if not value.strip(): + raise ValueError("Python package names must not be empty.") + return value + + @field_validator("version") + @classmethod + def _version_must_be_valid(cls, value: Optional[str]) -> Optional[str]: + if value is not None: + try: + SpecifierSet(value) + except InvalidSpecifier as error: + raise ValueError(f"Invalid Python package version specifier {value!r}.") from error + return value + + @field_validator("marker") + @classmethod + def _marker_must_be_valid(cls, value: Optional[str]) -> Optional[str]: + if value is not None: + try: + Marker(value) + except InvalidMarker as error: + raise ValueError(f"Invalid Python package environment marker {value!r}.") from error + return value + + class RailRequirements(BaseModel): """Installation and runtime resources declared by a rail.""" + python_packages: Tuple[PythonPackage, ...] = () extras: Tuple[str, ...] = () env_vars: Tuple[EnvVar, ...] = () services: Tuple[ServiceRequirement, ...] = () @@ -289,6 +330,14 @@ class RailRequirements(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) + @model_validator(mode="after") + def _python_packages_must_be_unique(self) -> "RailRequirements": + normalized = [package.distribution.lower().replace("_", "-") for package in self.python_packages] + duplicates = sorted({name for name in normalized if normalized.count(name) > 1}) + if duplicates: + raise ValueError(f"Python package requirements cannot be declared more than once: {duplicates}.") + return self + class RailPrivacy(BaseModel): """Data handling and remote-service behavior declared by a rail.""" diff --git a/nemoguardrails/manifests/validation.py b/nemoguardrails/manifests/validation.py new file mode 100644 index 0000000000..2d219f544c --- /dev/null +++ b/nemoguardrails/manifests/validation.py @@ -0,0 +1,220 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib +import importlib.metadata +import os +from dataclasses import dataclass +from enum import Enum +from types import ModuleType +from typing import Iterable, NoReturn, Optional, Tuple + +from packaging.markers import Marker +from packaging.specifiers import SpecifierSet + +from nemoguardrails.manifests.catalog import RailCatalog +from nemoguardrails.manifests.manifest import PythonPackage, RailManifest +from nemoguardrails.manifests.surface_reference import normalize_configured_surface_name + + +class RequirementStatus(str, Enum): + OK = "ok" + WARNING = "warning" + ERROR = "error" + INFO = "info" + + +@dataclass(frozen=True, slots=True) +class RequirementCheck: + kind: str + name: str + status: RequirementStatus + required: bool + message: str + install_requirement: Optional[str] = None + + +@dataclass(frozen=True, slots=True) +class RailValidationResult: + rail_name: str + checks: Tuple[RequirementCheck, ...] + + @property + def valid(self) -> bool: + return all(check.status != RequirementStatus.ERROR for check in self.checks) + + +@dataclass(frozen=True, slots=True) +class RailValidationReport: + results: Tuple[RailValidationResult, ...] + + @property + def valid(self) -> bool: + return all(result.valid for result in self.results) + + @property + def required_packages_to_install(self) -> Tuple[str, ...]: + packages = [] + for result in self.results: + for check in result.checks: + if check.required and check.install_requirement is not None: + packages.append(check.install_requirement) + return tuple(sorted(set(packages), key=str.lower)) + + @property + def optional_packages_to_install(self) -> Tuple[str, ...]: + packages = [] + for result in self.results: + for check in result.checks: + if not check.required and check.install_requirement is not None: + packages.append(check.install_requirement) + return tuple(sorted(set(packages), key=str.lower)) + + @property + def packages_to_install(self) -> Tuple[str, ...]: + return self.required_packages_to_install + + +class RailDependencyError(ImportError): + def __init__(self, rail_name: str, requirement: PythonPackage) -> None: + self.rail_name = rail_name + self.requirement = requirement + package = requirement.distribution + (requirement.version or "") + super().__init__( + f"Rail {rail_name!r} requires Python package {package!r}. Install it with: pip install '{package}'" + ) + + +def _package_requirement(requirement: PythonPackage) -> str: + package = requirement.distribution + (requirement.version or "") + if requirement.marker is not None: + package = f"{package}; {Marker(requirement.marker)}" + return package + + +def _package_check(requirement: PythonPackage, runtime: bool) -> RequirementCheck: + package_name = _package_requirement(requirement) + marker_matches = requirement.marker is None or Marker(requirement.marker).evaluate() + if not marker_matches: + status = RequirementStatus.ERROR if requirement.required else RequirementStatus.INFO + message = ( + "required package does not support this environment" + if requirement.required + else "optional package does not apply to this environment" + ) + return RequirementCheck("python_package", package_name, status, requirement.required, message) + try: + installed = importlib.metadata.version(requirement.distribution) + except importlib.metadata.PackageNotFoundError: + status = RequirementStatus.ERROR if requirement.required else RequirementStatus.WARNING + return RequirementCheck( + "python_package", package_name, status, requirement.required, "not installed", package_name + ) + if requirement.version and installed not in SpecifierSet(requirement.version): + status = RequirementStatus.ERROR if requirement.required else RequirementStatus.WARNING + return RequirementCheck( + "python_package", + package_name, + status, + requirement.required, + f"installed version {installed} is incompatible", + package_name, + ) + if runtime: + try: + importlib.import_module(requirement.import_name) + except ImportError: + status = RequirementStatus.ERROR if requirement.required else RequirementStatus.WARNING + return RequirementCheck( + "python_package", + package_name, + status, + requirement.required, + "runtime import failed", + package_name, + ) + return RequirementCheck( + "python_package", package_name, RequirementStatus.OK, requirement.required, f"installed ({installed})" + ) + + +def validate_rail_requirements(manifests: Iterable[RailManifest], *, runtime: bool = False) -> RailValidationReport: + results = [] + for manifest in sorted(manifests, key=lambda item: item.name): + checks = [_package_check(requirement, runtime) for requirement in manifest.requirements.python_packages] + for env_var in manifest.requirements.env_vars: + present = env_var.name in os.environ + if present: + status = RequirementStatus.OK + message = "set" + elif env_var.required: + status = RequirementStatus.ERROR + message = "not set" + else: + status = RequirementStatus.INFO + message = "optional and not set" + checks.append(RequirementCheck("environment_variable", env_var.name, status, env_var.required, message)) + for service in manifest.requirements.services: + checks.append( + RequirementCheck( + "service", service.name, RequirementStatus.INFO, service.required, "declared; not verified" + ) + ) + for model in manifest.requirements.models: + checks.append( + RequirementCheck("model", model.type, RequirementStatus.INFO, model.required, "declared; not verified") + ) + results.append(RailValidationResult(manifest.name, tuple(checks))) + return RailValidationReport(tuple(results)) + + +def configured_rail_manifests(config, catalog: RailCatalog) -> Tuple[RailManifest, ...]: + names = set() + rail_groups = ( + config.rails.input, + config.rails.output, + config.rails.retrieval, + config.rails.tool_input, + config.rails.tool_output, + ) + for group in rail_groups: + for configured_flow in group.flows: + flow_name = normalize_configured_surface_name(configured_flow) + owner = catalog.owner_for_flow(flow_name) + if owner is not None: + names.add(owner) + for config_key in config.rails.config.model_fields_set: + owner = catalog.owner_for_config_key(config_key) + if owner is not None: + names.add(owner) + return tuple(catalog.manifests[name] for name in sorted(names)) + + +def require_python_package(rail_name: str, requirement: PythonPackage) -> ModuleType: + try: + return importlib.import_module(requirement.import_name) + except ModuleNotFoundError as error: + if error.name == requirement.import_name or requirement.import_name.startswith(f"{error.name}."): + raise RailDependencyError(rail_name, requirement) from error + raise + + +def raise_for_missing_package( + rail_name: str, requirements: Iterable[PythonPackage], error: ModuleNotFoundError +) -> NoReturn: + for requirement in requirements: + if error.name == requirement.import_name or requirement.import_name.startswith(f"{error.name}."): + raise RailDependencyError(rail_name, requirement) from error + raise error diff --git a/pyproject.toml b/pyproject.toml index b7dcc39d07..9176247f40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ dependencies = [ "numpy (>=1.21) ; python_version>='3.10' and python_version<'3.12'", "numpy (>=1.26) ; python_version>='3.12' and python_version<'3.13'", "numpy (>=2.1.0) ; python_version>='3.13'", + "packaging (>=23.0)", "prompt-toolkit (>=3.0)", "protobuf (>=5.29.5)", "pydantic (>=2.5,<3.0)", @@ -42,24 +43,12 @@ dependencies = [ "simpleeval (>=0.9.13)", "typer (>=0.8)", ] - [project.optional-dependencies] tracing = ["opentelemetry-api (>=1.27.0,<2.0.0)", "aiofiles (>=24.1.0)"] server = ["aiofiles (>=24.1.0)", "openai (>=1.0.0,<3.0.0)", "fastapi (>=0.103.0)", "starlette (>=0.49.1)", "uvicorn (>=0.23)", "watchdog (>=3.0.0)"] -sdd = [ - "presidio-analyzer (>=2.2) ; python_version<'3.13'", - "presidio-anonymizer (>=2.2) ; python_version<'3.13'", -] - -gcp = ["google-cloud-language (>=2.14.0)"] - -jailbreak = ["yara-python (>=4.5.1,<5.0.0)"] - -multilingual = ["fast-langdetect (>=1.0.0)"] - chat-ui = ["chainlit (>=2.11.0,<3.0.0)"] # eval @@ -72,10 +61,7 @@ eval = [ "numpy (>=1.26) ; python_version>='3.12' and python_version<'3.13'", "numpy (>=2.1.0) ; python_version>='3.13'", ] - all = [ - "presidio-analyzer (>=2.2) ; python_version<'3.13'", - "presidio-anonymizer (>=2.2) ; python_version<'3.13'", "tqdm (>=4.65,<5.0)", "streamlit (>=1.37.0,<2.0.0) ; python_version>='3.10' and python_version<'3.14'", "tornado (>=6.5.0) ; python_version>='3.10' and python_version<'3.14'", @@ -84,11 +70,8 @@ all = [ "numpy (>=1.26) ; python_version>='3.12' and python_version<'3.13'", "numpy (>=2.1.0) ; python_version>='3.13'", "openai (>=1.0.0,<3.0.0)", - "google-cloud-language (>=2.14.0)", "opentelemetry-api (>=1.27.0,<2.0.0)", "aiofiles (>=24.1.0)", - "yara-python (>=4.5.1,<5.0.0)", - "fast-langdetect (>=1.0.0)", "fastapi (>=0.103.0)", "starlette (>=0.49.1)", "uvicorn (>=0.23)", diff --git a/tests/cli/test_cli_main.py b/tests/cli/test_cli_main.py index c6356df863..c2810b9c18 100644 --- a/tests/cli/test_cli_main.py +++ b/tests/cli/test_cli_main.py @@ -14,6 +14,7 @@ # limitations under the License. import os +from pathlib import Path from unittest.mock import MagicMock, patch from typer.testing import CliRunner @@ -382,3 +383,80 @@ def test_eval_command_exists(self): assert result.exit_code == 0 assert "run" in result.stdout assert "check-compliance" in result.stdout + + +class TestRailsValidateCommand: + @staticmethod + def _write_config(path: Path): + path.mkdir() + path.joinpath("config.yml").write_text( + "models: []\nrails:\n output:\n flows:\n - cleanlab trustworthiness\n", + encoding="utf-8", + ) + + @patch("nemoguardrails.manifests.validation.importlib.metadata.version") + def test_explicit_config_reports_install_guidance_and_failure(self, mock_version, tmp_path): + from importlib.metadata import PackageNotFoundError + + mock_version.side_effect = PackageNotFoundError("cleanlab-studio") + config = tmp_path / "guardrails" + self._write_config(config) + + result = runner.invoke(app, ["rails", "validate", "--config", str(config)]) + + assert result.exit_code == 1 + assert "cleanlab:" in result.stdout + assert "Install required packages with: pip install cleanlab-studio" in result.stdout + + @patch("nemoguardrails.manifests.validation.importlib.metadata.version") + def test_optional_packages_are_reported_separately(self, mock_version, tmp_path): + from importlib.metadata import PackageNotFoundError + + mock_version.side_effect = PackageNotFoundError("fast-langdetect") + config = tmp_path / "guardrails" + config.mkdir() + config.joinpath("config.yml").write_text( + "models: []\n" + "rails:\n" + " input:\n" + " flows:\n" + " - jailbreak detection heuristics\n" + " config:\n" + " jailbreak_detection:\n" + " server_endpoint: http://localhost:1337/heuristics\n", + encoding="utf-8", + ) + + result = runner.invoke(app, ["rails", "validate", "--config", str(config)]) + + assert result.exit_code == 0 + assert "Install optional packages to enable additional functionality with: pip install" in result.stdout + assert "huggingface-hub" in result.stdout + assert "torch>=2" in result.stdout + assert "transformers>=4.35" in result.stdout + assert "Install required packages" not in result.stdout + + @patch("nemoguardrails.manifests.validation.importlib.metadata.version") + def test_default_config_path(self, mock_version, monkeypatch, tmp_path): + from importlib.metadata import PackageNotFoundError + + mock_version.side_effect = PackageNotFoundError("cleanlab-studio") + monkeypatch.chdir(tmp_path) + self._write_config(Path("config")) + result = runner.invoke(app, ["rails", "validate"]) + + assert result.exit_code == 1 + assert "cleanlab-studio" in result.stdout + + @patch("nemoguardrails.cli.rails.validate_rail_requirements") + def test_runtime_flag_is_forwarded(self, mock_validate, tmp_path): + from nemoguardrails.manifests import RailValidationReport + + mock_validate.return_value = RailValidationReport(()) + config = tmp_path / "guardrails" + self._write_config(config) + + result = runner.invoke(app, ["rails", "validate", "--runtime", "--config", str(config)]) + + assert result.exit_code == 0 + assert mock_validate.call_args.kwargs == {"runtime": True} diff --git a/tests/manifests/test_manifest.py b/tests/manifests/test_manifest.py index 428ccb137f..ded26bb30c 100644 --- a/tests/manifests/test_manifest.py +++ b/tests/manifests/test_manifest.py @@ -20,11 +20,13 @@ ActionRef, Binding, ConfigSpecRef, + PythonPackage, RailActions, RailConfigSchema, RailDirection, RailManifest, RailMetadata, + RailRequirements, RailSpec, RailSurface, import_ref_target, @@ -78,6 +80,34 @@ def test_spec_rejects_unknown_keys(): RailSpec.model_validate({"unknown_field": 1}) +def test_python_package_requirement_round_trip(): + package = PythonPackage( + distribution="example-package", + import_name="example_package", + version=">=1,<2", + marker="python_version < '3.13'", + description="Example runtime", + ) + + assert PythonPackage.model_validate(package.model_dump()) == package + + +@pytest.mark.parametrize("field,value", [("version", ">>1"), ("marker", "python_version ??? '3.12'")]) +def test_python_package_rejects_invalid_constraints(field, value): + with pytest.raises(ValueError): + PythonPackage(distribution="example", import_name="example", **{field: value}) + + +def test_requirements_reject_duplicate_python_packages(): + with pytest.raises(ValueError, match="cannot be declared more than once"): + RailRequirements( + python_packages=( + PythonPackage(distribution="Example_Package", import_name="example"), + PythonPackage(distribution="example-package", import_name="example"), + ) + ) + + @pytest.mark.parametrize( "factory", ( diff --git a/tests/rails/llm/test_rail_requirements.py b/tests/rails/llm/test_rail_requirements.py new file mode 100644 index 0000000000..253caa93cd --- /dev/null +++ b/tests/rails/llm/test_rail_requirements.py @@ -0,0 +1,281 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib +import importlib.metadata + +import pytest + +from nemoguardrails.manifests import ( + EnvVar, + PythonPackage, + RailCatalog, + RailDependencyError, + RailManifest, + RailManifestRecord, + RailRequirements, + RailSpec, + RequirementStatus, + configured_rail_manifests, + require_python_package, + validate_rail_requirements, +) +from nemoguardrails.rails.llm.config import RailsConfig + + +def _manifest(name="test", *, package=None, env_vars=(), flow_names=(), config_schema=None): + requirements = RailRequirements(python_packages=(package,) if package else (), env_vars=env_vars) + from nemoguardrails.manifests import RailFlows + + return RailManifest( + name=name, + spec=RailSpec( + flows=RailFlows(flow_names=flow_names) if flow_names else None, + config_schema=config_schema, + requirements=requirements, + ), + ) + + +def test_static_validation_uses_distribution_metadata_without_importing(monkeypatch): + package = PythonPackage(distribution="example", import_name="example", version=">=2") + monkeypatch.setattr(importlib.metadata, "version", lambda name: "2.1") + imported = [] + monkeypatch.setattr(importlib, "import_module", lambda name: imported.append(name)) + + report = validate_rail_requirements([_manifest(package=package)]) + + assert report.valid + assert report.results[0].checks[0].status == RequirementStatus.OK + assert imported == [] + + +def test_missing_required_and_optional_packages(monkeypatch): + def missing(name): + raise importlib.metadata.PackageNotFoundError(name) + + monkeypatch.setattr(importlib.metadata, "version", missing) + required = _manifest(package=PythonPackage(distribution="required", import_name="required")) + optional = _manifest( + "optional", package=PythonPackage(distribution="optional", import_name="optional", required=False) + ) + + report = validate_rail_requirements([required, optional]) + + assert not report.valid + assert report.results[0].checks[0].status == RequirementStatus.WARNING + assert report.results[1].checks[0].status == RequirementStatus.ERROR + assert report.required_packages_to_install == ("required",) + assert report.optional_packages_to_install == ("optional",) + assert report.packages_to_install == report.required_packages_to_install + + +def test_required_unsupported_environment_is_an_error(): + package = PythonPackage( + distribution="example", + import_name="example", + marker="python_version < '1'", + ) + + report = validate_rail_requirements([_manifest(package=package)]) + + assert not report.valid + assert report.results[0].checks[0].message == "required package does not support this environment" + assert report.required_packages_to_install == () + + +def test_optional_unsupported_environment_is_informational(): + package = PythonPackage( + distribution="example", + import_name="example", + required=False, + marker="python_version < '1'", + ) + + report = validate_rail_requirements([_manifest(package=package)]) + + assert report.valid + assert report.results[0].checks[0].status == RequirementStatus.INFO + assert report.optional_packages_to_install == () + + +def test_install_guidance_preserves_version_and_marker(monkeypatch): + package = PythonPackage( + distribution="example", + import_name="example", + version=">=2,<3", + marker="python_version >= '3.10'", + ) + + def missing(name): + raise importlib.metadata.PackageNotFoundError(name) + + monkeypatch.setattr(importlib.metadata, "version", missing) + + report = validate_rail_requirements([_manifest(package=package)]) + + assert report.required_packages_to_install == ('example>=2,<3; python_version >= "3.10"',) + + +def test_runtime_validation_reports_transitive_import_failure(monkeypatch): + requirement = PythonPackage(distribution="example", import_name="example") + monkeypatch.setattr(importlib.metadata, "version", lambda name: "1.0") + + def missing(name): + raise ModuleNotFoundError("missing", name="transitive_dependency") + + monkeypatch.setattr(importlib, "import_module", missing) + + report = validate_rail_requirements([_manifest(package=requirement)], runtime=True) + + assert not report.valid + assert report.results[0].checks[0].message == "runtime import failed" + + +def test_runtime_import_translates_only_matching_missing_package(monkeypatch): + requirement = PythonPackage(distribution="example", import_name="example") + + def missing(name): + raise ModuleNotFoundError("missing", name=name) + + monkeypatch.setattr(importlib, "import_module", missing) + + with pytest.raises(RailDependencyError) as exc_info: + require_python_package("test", requirement) + + assert exc_info.value.rail_name == "test" + assert exc_info.value.requirement == requirement + + +def test_runtime_import_preserves_transitive_missing_package(monkeypatch): + requirement = PythonPackage(distribution="example", import_name="example") + + def missing(name): + raise ModuleNotFoundError("missing", name="transitive_dependency") + + monkeypatch.setattr(importlib, "import_module", missing) + + with pytest.raises(ModuleNotFoundError) as exc_info: + require_python_package("test", requirement) + + assert exc_info.value.name == "transitive_dependency" + + +def test_environment_validation_does_not_expose_values(monkeypatch): + monkeypatch.setenv("SECRET_TOKEN", "top-secret") + manifest = _manifest(env_vars=(EnvVar(name="SECRET_TOKEN", required=True),)) + + report = validate_rail_requirements([manifest]) + + assert report.valid + assert "top-secret" not in repr(report) + + +def test_configured_manifest_selection_normalizes_parameters_and_ignores_custom_flows(): + manifest = _manifest("owned", flow_names=("owned flow",)) + catalog = RailCatalog((RailManifestRecord(manifest=manifest, source="test"),)) + config = RailsConfig.from_content( + yaml_content=""" +models: [] +rails: + input: + flows: + - owned flow $mode=\"strict\" + - custom flow +""" + ) + + assert configured_rail_manifests(config, catalog) == (manifest,) + + +def test_configured_manifest_selection_includes_explicit_config_sections(): + from nemoguardrails.manifests import default_rail_catalog + + catalog = default_rail_catalog() + config = RailsConfig.from_content( + yaml_content=""" +models: [] +rails: + config: + sensitive_data_detection: + input: + entities: [EMAIL_ADDRESS] +""" + ) + + assert tuple(item.name for item in configured_rail_manifests(config, catalog)) == ("sensitive_data_detection",) + + +def test_f5_validation_reports_environment_and_service_requirements(monkeypatch): + from nemoguardrails.manifests import default_rail_catalog + + monkeypatch.delenv("F5_GUARDRAILS_API_KEY", raising=False) + monkeypatch.delenv("F5_GUARDRAILS_API_URL", raising=False) + catalog = default_rail_catalog() + config = RailsConfig.from_content( + yaml_content=""" +models: [] +rails: + input: + flows: + - f5 guardrails scan input + config: + f5: + fail_open: false +""" + ) + + manifests = configured_rail_manifests(config, catalog) + report = validate_rail_requirements(manifests) + + assert tuple(manifest.name for manifest in manifests) == ("f5",) + assert not report.valid + assert report.packages_to_install == () + assert tuple( + (check.kind, check.name, check.status, check.required, check.message) for check in report.results[0].checks + ) == ( + ("environment_variable", "F5_GUARDRAILS_API_KEY", RequirementStatus.ERROR, True, "not set"), + ( + "environment_variable", + "F5_GUARDRAILS_API_URL", + RequirementStatus.INFO, + False, + "optional and not set", + ), + ("service", "F5 AI Guardrails API", RequirementStatus.INFO, True, "declared; not verified"), + ) + + +def test_built_in_runtime_package_declarations(): + from nemoguardrails.manifests import default_rail_catalog + + expected = { + "cleanlab": ("cleanlab-studio",), + "content_safety": ("fast-langdetect",), + "gcp_moderate_text": ("google-cloud-language",), + "guardrails_ai": ("guardrails-ai",), + "hf_classifier": ("transformers", "torch"), + "injection_detection": ("yara-python",), + "jailbreak_detection": ("transformers", "torch", "huggingface-hub"), + "sensitive_data_detection": ("presidio-analyzer", "presidio-anonymizer", "spacy"), + } + + catalog = default_rail_catalog() + + for rail_name, distributions in expected.items(): + requirements = catalog.manifests[rail_name].requirements + assert tuple(package.distribution for package in requirements.python_packages) == distributions + assert requirements.extras == () + assert requirements.optional_dependencies == () diff --git a/tests/test_guardrails_ai_actions.py b/tests/test_guardrails_ai_actions.py index ddab64dee7..6b693d7e78 100644 --- a/tests/test_guardrails_ai_actions.py +++ b/tests/test_guardrails_ai_actions.py @@ -262,18 +262,20 @@ def test_validate_guardrails_ai_error_handling(self, mock_get_guard): assert "Validation service error" in str(exc_info.value) @patch("nemoguardrails.library.guardrails_ai.actions._load_validator_class") - @patch("nemoguardrails.library.guardrails_ai.actions.Guard") - def test_get_guard_creates_and_caches(self, mock_guard_class, mock_load_validator): + @patch("nemoguardrails.library.guardrails_ai.actions._load_guard_class") + def test_get_guard_creates_and_caches(self, mock_load_guard, mock_load_validator): """Test that _get_guard creates and caches guards properly.""" from nemoguardrails.library.guardrails_ai.actions import _get_guard mock_validator_class = Mock() mock_validator_instance = Mock() mock_guard_instance = Mock() + mock_guard_class = Mock() mock_guard = Mock() mock_load_validator.return_value = mock_validator_class mock_validator_class.return_value = mock_validator_instance + mock_load_guard.return_value = mock_guard_class mock_guard_class.return_value = mock_guard mock_guard.use.return_value = mock_guard_instance @@ -328,16 +330,18 @@ def test_validate_guardrails_ai_signature(self): assert any(param.kind == param.VAR_KEYWORD for param in sig.parameters.values()) @patch("nemoguardrails.library.guardrails_ai.actions._load_validator_class") - @patch("nemoguardrails.library.guardrails_ai.actions.Guard") - def test_guard_cache_key_generation(self, mock_guard_class, mock_load): + @patch("nemoguardrails.library.guardrails_ai.actions._load_guard_class") + def test_guard_cache_key_generation(self, mock_load_guard, mock_load): """Test that guard cache keys are generated correctly for different parameter combinations.""" from nemoguardrails.library.guardrails_ai.actions import _get_guard mock_validator_class = Mock() mock_guard_instance = Mock() + mock_guard_class = Mock() mock_guard = Mock() mock_load.return_value = mock_validator_class + mock_load_guard.return_value = mock_guard_class mock_guard_class.return_value = mock_guard mock_guard.use.return_value = mock_guard_instance diff --git a/tests/test_guardrails_ai_e2e_actions.py b/tests/test_guardrails_ai_e2e_actions.py index 229f0517a4..7536737949 100644 --- a/tests/test_guardrails_ai_e2e_actions.py +++ b/tests/test_guardrails_ai_e2e_actions.py @@ -19,7 +19,7 @@ They can be skipped in CI/environments where validators aren't available. """ -from unittest.mock import Mock +from unittest.mock import Mock, patch import pytest @@ -250,9 +250,9 @@ def test_error_handling_unknown_validator_e2e(self): GuardrailsAIValidationError, ) - # Test with completely unknown validator - with pytest.raises(GuardrailsAIValidationError) as exc_info: - validate_guardrails_ai(validator_name="completely_unknown_validator", text="Test text") + with patch("nemoguardrails.library.guardrails_ai.actions._load_guard_class"): + with pytest.raises(GuardrailsAIValidationError) as exc_info: + validate_guardrails_ai(validator_name="completely_unknown_validator", text="Test text") assert "Validation failed" in str(exc_info.value) diff --git a/tests/test_injection_detection.py b/tests/test_injection_detection.py index beaeb2ee77..c333e34dd9 100644 --- a/tests/test_injection_detection.py +++ b/tests/test_injection_detection.py @@ -39,10 +39,10 @@ from nemoguardrails import RailsConfig from nemoguardrails.actions.rail_outcome import RailOutcome, TransformTarget from nemoguardrails.library.injection_detection.actions import ( - _check_yara_available, _extract_injection_config, _injection_detection_outcome, _load_rules, + _load_yara, _omit_injection, _reject_injection, _sanitize_injection, @@ -560,18 +560,16 @@ async def test_sanitize_action_not_implemented(): ) -def test_yara_import_error(): - """Test that appropriate error is raised when yara module is not available.""" +def test_yara_loader_uses_declared_package(): + """Test that YARA is loaded lazily from its declared package.""" + import nemoguardrails.library.injection_detection.actions as actions - with patch("nemoguardrails.library.injection_detection.actions.yara", None): - with pytest.raises(ImportError) as exc_info: - _check_yara_available() - assert str(exc_info.value) == ( - "The yara module is required for injection detection. Please install it using: pip install yara-python" - ) - - with patch("nemoguardrails.library.injection_detection.actions.yara", yara): - _check_yara_available() + _load_yara.cache_clear() + with patch.object(actions, "require_python_package", return_value=yara) as mock_require: + assert _load_yara() is yara + assert _load_yara() is yara + mock_require.assert_called_once_with("injection_detection", actions.YARA_PACKAGE) + _load_yara.cache_clear() @pytest.mark.asyncio diff --git a/tests/test_jailbreak_actions.py b/tests/test_jailbreak_actions.py index 88347a529d..2b205010d3 100644 --- a/tests/test_jailbreak_actions.py +++ b/tests/test_jailbreak_actions.py @@ -195,7 +195,7 @@ async def test_jailbreak_detection_model_local_runtime_error(self, monkeypatch, @pytest.mark.asyncio async def test_jailbreak_detection_model_local_import_error(self, monkeypatch, caplog): - """Test ImportError handling when dependencies are missing.""" + """Test unrelated local model import errors are preserved.""" from nemoguardrails.library.jailbreak_detection.actions import ( jailbreak_detection_model, ) @@ -223,11 +223,8 @@ async def test_jailbreak_detection_model_local_import_error(self, monkeypatch, c llm_task_manager = LLMTaskManager(config=config) context = {"user_message": "test prompt"} - result = await jailbreak_detection_model(llm_task_manager, context) - assert result.is_blocked is False - - assert "Failed to import required dependencies for local model" in caplog.text - assert "Install scikit-learn and torch, or use NIM-based approach" in caplog.text + with pytest.raises(ImportError, match="sklearn"): + await jailbreak_detection_model(llm_task_manager, context) @pytest.mark.asyncio async def test_jailbreak_detection_model_local_success(self, monkeypatch, caplog): diff --git a/tests/test_rail_packaging.py b/tests/test_rail_packaging.py new file mode 100644 index 0000000000..7597fc6697 --- /dev/null +++ b/tests/test_rail_packaging.py @@ -0,0 +1,42 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pathlib import Path + +try: + import tomllib +except ModuleNotFoundError: + import tomli as tomllib +from packaging.requirements import Requirement +from packaging.utils import canonicalize_name + + +def test_rail_dependencies_are_not_package_extras(): + project = tomllib.loads(Path("pyproject.toml").read_text()) + dependencies = {canonicalize_name(Requirement(value).name) for value in project["project"]["dependencies"]} + extras = project["project"]["optional-dependencies"] + rail_packages = { + "presidio-analyzer", + "presidio-anonymizer", + "google-cloud-language", + "yara-python", + "fast-langdetect", + "transformers", + "torch", + } + + assert rail_packages.isdisjoint(dependencies) + assert {"sdd", "gcp", "jailbreak", "multilingual", "hf-classifier"}.isdisjoint(extras) + assert rail_packages.isdisjoint(canonicalize_name(Requirement(value).name) for value in extras["all"]) diff --git a/uv.lock b/uv.lock index 5cc63cb17d..e4904d5a3a 100644 --- a/uv.lock +++ b/uv.lock @@ -320,47 +320,6 @@ 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" }, ] -[[package]] -name = "blis" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.12.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d0/d0/d8cc8c9a4488a787e7fa430f6055e5bd1ddb22c340a751d9e901b82e2efe/blis-1.3.3.tar.gz", hash = "sha256:034d4560ff3cc43e8aa37e188451b0440e3261d989bb8a42ceee865607715ecd", size = 2644873, upload-time = "2025-11-17T12:28:30.511Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/db/d80daf6c060618c72acecf026410b806f620cdea62b2e72f3235d7389d05/blis-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:650f1d2b28e3c875927c63deebda463a6f9d237dff30e445bfe2127718c1a344", size = 6925724, upload-time = "2025-11-17T12:27:14.23Z" }, - { url = "https://files.pythonhosted.org/packages/06/cd/7ac854c92e33cfccc0eded48e979a9fc26a447952d07a9c7c7da7c1d6eec/blis-1.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b0d42420ddd543eec51ccb99d38364a0c0833b6895eced37127822de6ecacff", size = 1233606, upload-time = "2025-11-17T12:27:16.107Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ae/ad3165fdbc4ef6afef585686a778c72cd67fb5aa16ab2fd2f4494186705e/blis-1.3.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f0628a030d44aa71cac5973e40c9e95ec767abaaf2fd366a094b9398885f82f2", size = 2769094, upload-time = "2025-11-17T12:27:17.883Z" }, - { url = "https://files.pythonhosted.org/packages/25/d4/7b0820f139b4ea67606d01b59ba6afbee4552ce7b2fd179f2fb7908e294f/blis-1.3.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d0114cf2d8f19e0ed210f9ae92594cd0a12efa1bbbce444028b0fc365bbbb8af", size = 11300520, upload-time = "2025-11-17T12:27:20.058Z" }, - { url = "https://files.pythonhosted.org/packages/85/f3/865a4322bdbeb944744c1908e67fdabecd476613a17204956cff12d568c9/blis-1.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7e88181e9dd8430029ebaf22d41bf79e756e8c95363e9471717102c66beb4a6d", size = 2962083, upload-time = "2025-11-17T12:27:22.098Z" }, - { url = "https://files.pythonhosted.org/packages/65/a2/c2842fa1e2e6bd56eb93e41b34859a9af8b5b63669ee0442bea585d8f607/blis-1.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:62fb8c731347b0f98f5f81d19d339049e61489798738467d156c66cc329b0754", size = 14177001, upload-time = "2025-11-17T12:27:24.345Z" }, - { url = "https://files.pythonhosted.org/packages/b5/9b/3b1532f23db8bdddf3a976e9acf51e8debd94c63be5dafb8ccbab3e62935/blis-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:631836d4f335e62c30aa50a1aa0170773265c73654d296361f95180006e88c04", size = 6184429, upload-time = "2025-11-17T12:27:27.054Z" }, - { url = "https://files.pythonhosted.org/packages/a1/0a/a4c8736bc497d386b0ffc76d321f478c03f1a4725e52092f93b38beb3786/blis-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e10c8d3e892b1dbdff365b9d00e08291876fc336915bf1a5e9f188ed087e1a91", size = 6925522, upload-time = "2025-11-17T12:27:29.199Z" }, - { url = "https://files.pythonhosted.org/packages/83/5a/3437009282f23684ecd3963a8b034f9307cdd2bf4484972e5a6b096bf9ac/blis-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66e6249564f1db22e8af1e0513ff64134041fa7e03c8dd73df74db3f4d8415a7", size = 1232787, upload-time = "2025-11-17T12:27:30.996Z" }, - { url = "https://files.pythonhosted.org/packages/d1/0e/82221910d16259ce3017c1442c468a3f206a4143a96fbba9f5b5b81d62e8/blis-1.3.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7260da065958b4e5475f62f44895ef9d673b0f47dcf61b672b22b7dae1a18505", size = 2844596, upload-time = "2025-11-17T12:27:32.601Z" }, - { url = "https://files.pythonhosted.org/packages/6c/93/ab547f1a5c23e20bca16fbcf04021c32aac3f969be737ea4980509a7ca90/blis-1.3.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9327a6ca67de8ae76fe071e8584cc7f3b2e8bfadece4961d40f2826e1cda2df", size = 11377746, upload-time = "2025-11-17T12:27:35.342Z" }, - { url = "https://files.pythonhosted.org/packages/6e/a6/7733820aa62da32526287a63cd85c103b2b323b186c8ee43b7772ff7017c/blis-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c4ae70629cf302035d268858a10ca4eb6242a01b2dc8d64422f8e6dcb8a8ee74", size = 3041954, upload-time = "2025-11-17T12:27:37.479Z" }, - { url = "https://files.pythonhosted.org/packages/87/53/e39d67fd3296b649772780ca6aab081412838ecb54e0b0c6432d01626a50/blis-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45866a9027d43b93e8b59980a23c5d7358b6536fc04606286e39fdcfce1101c2", size = 14251222, upload-time = "2025-11-17T12:27:39.705Z" }, - { url = "https://files.pythonhosted.org/packages/ea/44/b749f8777b020b420bceaaf60f66432fc30cc904ca5b69640ec9cbef11ed/blis-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:27f82b8633030f8d095d2b412dffa7eb6dbc8ee43813139909a20012e54422ea", size = 6171233, upload-time = "2025-11-17T12:27:41.921Z" }, - { url = "https://files.pythonhosted.org/packages/16/d1/429cf0cf693d4c7dc2efed969bd474e315aab636e4a95f66c4ed7264912d/blis-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2a1c74e100665f8e918ebdbae2794576adf1f691680b5cdb8b29578432f623ef", size = 6929663, upload-time = "2025-11-17T12:27:44.482Z" }, - { url = "https://files.pythonhosted.org/packages/11/69/363c8df8d98b3cc97be19aad6aabb2c9c53f372490d79316bdee92d476e7/blis-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3f6c595185176ce021316263e1a1d636a3425b6c48366c1fd712d08d0b71849a", size = 1230939, upload-time = "2025-11-17T12:27:46.19Z" }, - { url = "https://files.pythonhosted.org/packages/96/2a/fbf65d906d823d839076c5150a6f8eb5ecbc5f9135e0b6510609bda1e6b7/blis-1.3.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d734b19fba0be7944f272dfa7b443b37c61f9476d9ab054a9ac53555ceadd2e0", size = 2818835, upload-time = "2025-11-17T12:27:48.167Z" }, - { url = "https://files.pythonhosted.org/packages/d5/ad/58deaa3ad856dd3cc96493e40ffd2ed043d18d4d304f85a65cde1ccbf644/blis-1.3.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ef6d6e2b599a3a2788eb6d9b443533961265aa4ec49d574ed4bb846e548dcdb", size = 11366550, upload-time = "2025-11-17T12:27:49.958Z" }, - { url = "https://files.pythonhosted.org/packages/78/82/816a7adfe1f7acc8151f01ec86ef64467a3c833932d8f19f8e06613b8a4e/blis-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8c888438ae99c500422d50698e3028b65caa8ebb44e24204d87fda2df64058f7", size = 3023686, upload-time = "2025-11-17T12:27:52.062Z" }, - { url = "https://files.pythonhosted.org/packages/1e/e2/0e93b865f648b5519360846669a35f28ee8f4e1d93d054f6850d8afbabde/blis-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8177879fd3590b5eecdd377f9deafb5dc8af6d684f065bd01553302fb3fcf9a7", size = 14250939, upload-time = "2025-11-17T12:27:53.847Z" }, - { url = "https://files.pythonhosted.org/packages/20/07/fb43edc2ff0a6a367e4a94fc39eb3b85aa1e55e24cc857af2db145ce9f0d/blis-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:f20f7ad69aaffd1ce14fe77de557b6df9b61e0c9e582f75a843715d836b5c8af", size = 6192759, upload-time = "2025-11-17T12:27:56.176Z" }, - { url = "https://files.pythonhosted.org/packages/e6/f7/d26e62d9be3d70473a63e0a5d30bae49c2fe138bebac224adddcdef8a7ce/blis-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1e647341f958421a86b028a2efe16ce19c67dba2a05f79e8f7e80b1ff45328aa", size = 6928322, upload-time = "2025-11-17T12:27:57.965Z" }, - { url = "https://files.pythonhosted.org/packages/4a/78/750d12da388f714958eb2f2fd177652323bbe7ec528365c37129edd6eb84/blis-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d563160f874abb78a57e346f07312c5323f7ad67b6370052b6b17087ef234a8e", size = 1229635, upload-time = "2025-11-17T12:28:00.118Z" }, - { url = "https://files.pythonhosted.org/packages/e8/36/eac4199c5b200a5f3e93cad197da8d26d909f218eb444c4f552647c95240/blis-1.3.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:30b8a5b90cb6cb81d1ada9ae05aa55fb8e70d9a0ae9db40d2401bb9c1c8f14c4", size = 2815650, upload-time = "2025-11-17T12:28:02.544Z" }, - { url = "https://files.pythonhosted.org/packages/bf/51/472e7b36a6bedb5242a9757e7486f702c3619eff76e256735d0c8b1679c6/blis-1.3.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9f5c53b277f6ac5b3ca30bc12ebab7ea16c8f8c36b14428abb56924213dc127", size = 11359008, upload-time = "2025-11-17T12:28:04.589Z" }, - { url = "https://files.pythonhosted.org/packages/84/da/d0dfb6d6e6321ae44df0321384c32c322bd07b15740d7422727a1a49fc5d/blis-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6297e7616c158b305c9a8a4e47ca5fc9b0785194dd96c903b1a1591a7ca21ddf", size = 3011959, upload-time = "2025-11-17T12:28:06.862Z" }, - { url = "https://files.pythonhosted.org/packages/20/c5/2b0b5e556fa0364ed671051ea078a6d6d7b979b1cfef78d64ad3ca5f0c7f/blis-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3f966ca74f89f8a33e568b9a1d71992fc9a0d29a423e047f0a212643e21b5458", size = 14232456, upload-time = "2025-11-17T12:28:08.779Z" }, - { url = "https://files.pythonhosted.org/packages/31/07/4cdc81a47bf862c0b06d91f1bc6782064e8b69ac9b5d4ff51d97e4ff03da/blis-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:7a0fc4b237a3a453bdc3c7ab48d91439fcd2d013b665c46948d9eaf9c3e45a97", size = 6192624, upload-time = "2025-11-17T12:28:14.197Z" }, -] - [[package]] name = "cachetools" version = "7.1.4" @@ -370,15 +329,6 @@ 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 = "catalogue" -version = "2.0.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/38/b4/244d58127e1cdf04cf2dc7d9566f0d24ef01d5ce21811bab088ecc62b5ea/catalogue-2.0.10.tar.gz", hash = "sha256:4f56daa940913d3f09d589c191c74e5a6d51762b3a9e37dd53b7437afd6cda15", size = 19561, upload-time = "2023-09-25T06:29:24.962Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/96/d32b941a501ab566a16358d68b6eb4e4acc373fab3c3c4d7d9e649f7b4bb/catalogue-2.0.10-py3-none-any.whl", hash = "sha256:58c2de0020aa90f4a2da7dfad161bf7b3b054c86a5f09fcedc0b2b740c109a9f", size = 17325, upload-time = "2023-09-25T06:29:23.337Z" }, -] - [[package]] name = "certifi" version = "2026.6.17" @@ -586,18 +536,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fb/e2/79c688af8b210d232694e31e59da9f6ec747bae31c3f5946e4e9b98860d5/click-8.4.2-py3-none-any.whl", hash = "sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76", size = 119243, upload-time = "2026-06-24T17:45:13.73Z" }, ] -[[package]] -name = "cloudpathlib" -version = "0.24.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/06/19/58bc6b5d7d0f81c7209b05445af477e147c486552f96665a5912211839b9/cloudpathlib-0.24.0.tar.gz", hash = "sha256:c521a984e77b47e656fe78e20a7e3e260e0ab45fc69e33ac01094227c979e34a", size = 53600, upload-time = "2026-04-30T00:54:43.265Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/5b/ba933f896d9b0b07608d575a8501e2b4e32166b60d84c430a4a7285ebe64/cloudpathlib-0.24.0-py3-none-any.whl", hash = "sha256:b1c51e2d2ec7dc4fed6538991f4aea849d6cf11a7e6b9069f86e461aa1f9b5b4", size = 63214, upload-time = "2026-04-30T00:54:42.06Z" }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -631,18 +569,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6d/c1/e419ef3723a074172b68aaa89c9f3de486ed4c2399e2dbd8113a4fdcaf9e/colorlog-6.10.1-py3-none-any.whl", hash = "sha256:2d7e8348291948af66122cff006c9f8da6255d224e7cf8e37d8de2df3bad8c9c", size = 11743, upload-time = "2025-10-16T16:14:10.512Z" }, ] -[[package]] -name = "confection" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ca/65/efd0fe8a936fc8ca2978cb7b82581fb20d901c6039e746a808f746b7647b/confection-1.3.3.tar.gz", hash = "sha256:f0f6810d567ff73993fe74d218ca5e1ffb6a44fb03f391257fc5d033546cbfaa", size = 54895, upload-time = "2026-03-24T18:45:24.331Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/e4/d66708bdf0d92fb4d49b22cdff4b10cec38aca5dcd7e81d909bb55c65cd7/confection-1.3.3-py3-none-any.whl", hash = "sha256:b9fef9ee84b237ef4611ec3eb5797b70e13063e6310ad9f15536373f5e313c82", size = 35902, upload-time = "2026-03-24T18:45:22.664Z" }, -] - [[package]] name = "coverage" version = "7.14.3" @@ -819,54 +745,6 @@ version = "0.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/55/ca/d323556e2bf9bfb63219fbb849ce61bb830cc42d1b25b91cde3815451b91/cuid-0.4.tar.gz", hash = "sha256:74eaba154916a2240405c3631acee708c263ef8fa05a86820b87d0f59f84e978", size = 4986, upload-time = "2023-03-06T00:41:12.708Z" } -[[package]] -name = "cymem" -version = "2.0.13" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/2f0fbb32535c3731b7c2974c569fb9325e0a38ed5565a08e1139a3b71e82/cymem-2.0.13.tar.gz", hash = "sha256:1c91a92ae8c7104275ac26bd4d29b08ccd3e7faff5893d3858cb6fadf1bc1588", size = 12320, upload-time = "2025-11-14T14:58:36.902Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/14/462018dd384ee1848ac9c1951534a813a325abbfc161a74e2cbcb38d2469/cymem-2.0.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8efc4f308169237aade0e82877a65a563833dec32eb7ab2326120253e0e9e918", size = 43747, upload-time = "2025-11-14T14:57:11.287Z" }, - { url = "https://files.pythonhosted.org/packages/4b/9b/c123ba65dddcd8a2bc0b3c9046766c15abe0e257c315b3040eed22cce1e2/cymem-2.0.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e03bb575a96c59bc210d7d59862747f0012696b0dac3427ce8af33c7afb3d4a2", size = 43328, upload-time = "2025-11-14T14:57:12.578Z" }, - { url = "https://files.pythonhosted.org/packages/bd/be/7b7a4cf9cd2d37e674612a86fc90b3d59bff12177f83430e62b25afaf7fc/cymem-2.0.13-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1775d3fd34cf099929b79c3e48469283642463f977af6801231f3c0e5d9c9369", size = 231539, upload-time = "2025-11-14T14:57:14.441Z" }, - { url = "https://files.pythonhosted.org/packages/79/6d/d165c38cd4caaaf60942e2cec9998b667008f2384047ccfe0b4b5f7a1ffe/cymem-2.0.13-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:84e2976e38cd663f758e40b5497fa5cd183d7c5fb0d04ce81a4b42a1ba124ff0", size = 229674, upload-time = "2025-11-14T14:57:15.685Z" }, - { url = "https://files.pythonhosted.org/packages/95/c1/af83c03a93f890ca81149561b18a4a67a9aa36a1109f15e291dd2703ab12/cymem-2.0.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed9de1b9b042f76fe5c312e4359eab58bf52ac7dfdf6887368a760410d809440", size = 229805, upload-time = "2025-11-14T14:57:17.289Z" }, - { url = "https://files.pythonhosted.org/packages/03/2d/12900758b80345d9aed5892a9d61e8a5f6abbbe5837e4def373a53cd0da2/cymem-2.0.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1366c7437a209230f4b797fae10227a8206d4021d37c9f9c0d31fd97ea4feb35", size = 234018, upload-time = "2025-11-14T14:57:18.512Z" }, - { url = "https://files.pythonhosted.org/packages/a6/8b/5fcf5430fc81098aef58cc20340e51f37b49b9d8c15766e0d5d63e7288a3/cymem-2.0.13-cp310-cp310-win_amd64.whl", hash = "sha256:7700b116524b087e0169f10f267539223b48240ef2734c3a727a9e6b4db9a671", size = 40102, upload-time = "2025-11-14T14:57:19.972Z" }, - { url = "https://files.pythonhosted.org/packages/0d/d3/cb6c83758fe399443b858faafb7096b72535621a7af7dd9a54ff0989fa14/cymem-2.0.13-cp310-cp310-win_arm64.whl", hash = "sha256:c8dbfddfe5c604974e17c6f373cedd4d25cd67f84812ede7dea12128fa0c2015", size = 36282, upload-time = "2025-11-14T14:57:21.398Z" }, - { url = "https://files.pythonhosted.org/packages/10/64/1db41f7576a6b69f70367e3c15e968fd775ba7419e12059c9966ceb826f8/cymem-2.0.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:673183466b0ff2e060d97ec5116711d44200b8f7be524323e080d215ee2d44a5", size = 43587, upload-time = "2025-11-14T14:57:22.39Z" }, - { url = "https://files.pythonhosted.org/packages/81/13/57f936fc08551323aab3f92ff6b7f4d4b89d5b4e495c870a67cb8d279757/cymem-2.0.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bee2791b3f6fc034ce41268851462bf662ff87e8947e35fb6dd0115b4644a61f", size = 43139, upload-time = "2025-11-14T14:57:23.363Z" }, - { url = "https://files.pythonhosted.org/packages/32/a6/9345754be51e0479aa387b7b6cffc289d0fd3201aaeb8dade4623abd1e02/cymem-2.0.13-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f3aee3adf16272bca81c5826eed55ba3c938add6d8c9e273f01c6b829ecfde22", size = 245063, upload-time = "2025-11-14T14:57:24.839Z" }, - { url = "https://files.pythonhosted.org/packages/d6/01/6bc654101526fa86e82bf6b05d99b2cd47c30a333cfe8622c26c0592beb2/cymem-2.0.13-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:30c4e75a3a1d809e89106b0b21803eb78e839881aa1f5b9bd27b454bc73afde3", size = 244496, upload-time = "2025-11-14T14:57:26.42Z" }, - { url = "https://files.pythonhosted.org/packages/c4/fb/853b7b021e701a1f41687f3704d5f469aeb2a4f898c3fbb8076806885955/cymem-2.0.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec99efa03cf8ec11c8906aa4d4cc0c47df393bc9095c9dd64b89b9b43e220b04", size = 243287, upload-time = "2025-11-14T14:57:27.542Z" }, - { url = "https://files.pythonhosted.org/packages/d4/2b/0e4664cafc581de2896d75000651fd2ce7094d33263f466185c28ffc96e4/cymem-2.0.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c90a6ecba994a15b17a3f45d7ec74d34081df2f73bd1b090e2adc0317e4e01b6", size = 248287, upload-time = "2025-11-14T14:57:29.055Z" }, - { url = "https://files.pythonhosted.org/packages/21/0f/f94c6950edbfc2aafb81194fc40b6cacc8e994e9359d3cb4328c5705b9b5/cymem-2.0.13-cp311-cp311-win_amd64.whl", hash = "sha256:ce821e6ba59148ed17c4567113b8683a6a0be9c9ac86f14e969919121efb61a5", size = 40116, upload-time = "2025-11-14T14:57:30.592Z" }, - { url = "https://files.pythonhosted.org/packages/00/df/2455eff6ac0381ff165db6883b311f7016e222e3dd62185517f8e8187ed0/cymem-2.0.13-cp311-cp311-win_arm64.whl", hash = "sha256:0dca715e708e545fd1d97693542378a00394b20a37779c1ae2c8bdbb43acef79", size = 36349, upload-time = "2025-11-14T14:57:31.573Z" }, - { url = "https://files.pythonhosted.org/packages/c9/52/478a2911ab5028cb710b4900d64aceba6f4f882fcb13fd8d40a456a1b6dc/cymem-2.0.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e8afbc5162a0fe14b6463e1c4e45248a1b2fe2cbcecc8a5b9e511117080da0eb", size = 43745, upload-time = "2025-11-14T14:57:32.52Z" }, - { url = "https://files.pythonhosted.org/packages/f9/71/f0f8adee945524774b16af326bd314a14a478ed369a728a22834e6785a18/cymem-2.0.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9251d889348fe79a75e9b3e4d1b5fa651fca8a64500820685d73a3acc21b6a8", size = 42927, upload-time = "2025-11-14T14:57:33.827Z" }, - { url = "https://files.pythonhosted.org/packages/62/6d/159780fe162ff715d62b809246e5fc20901cef87ca28b67d255a8d741861/cymem-2.0.13-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:742fc19764467a49ed22e56a4d2134c262d73a6c635409584ae3bf9afa092c33", size = 258346, upload-time = "2025-11-14T14:57:34.917Z" }, - { url = "https://files.pythonhosted.org/packages/eb/12/678d16f7aa1996f947bf17b8cfb917ea9c9674ef5e2bd3690c04123d5680/cymem-2.0.13-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f190a92fe46197ee64d32560eb121c2809bb843341733227f51538ce77b3410d", size = 260843, upload-time = "2025-11-14T14:57:36.503Z" }, - { url = "https://files.pythonhosted.org/packages/31/5d/0dd8c167c08cd85e70d274b7235cfe1e31b3cebc99221178eaf4bbb95c6f/cymem-2.0.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d670329ee8dbbbf241b7c08069fe3f1d3a1a3e2d69c7d05ea008a7010d826298", size = 254607, upload-time = "2025-11-14T14:57:38.036Z" }, - { url = "https://files.pythonhosted.org/packages/b7/c9/d6514a412a1160aa65db539836b3d47f9b59f6675f294ec34ae32f867c82/cymem-2.0.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a84ba3178d9128b9ffb52ce81ebab456e9fe959125b51109f5b73ebdfc6b60d6", size = 262421, upload-time = "2025-11-14T14:57:39.265Z" }, - { url = "https://files.pythonhosted.org/packages/dd/fe/3ee37d02ca4040f2fb22d34eb415198f955862b5dd47eee01df4c8f5454c/cymem-2.0.13-cp312-cp312-win_amd64.whl", hash = "sha256:2ff1c41fd59b789579fdace78aa587c5fc091991fa59458c382b116fc36e30dc", size = 40176, upload-time = "2025-11-14T14:57:40.706Z" }, - { url = "https://files.pythonhosted.org/packages/94/fb/1b681635bfd5f2274d0caa8f934b58435db6c091b97f5593738065ddb786/cymem-2.0.13-cp312-cp312-win_arm64.whl", hash = "sha256:6bbd701338df7bf408648191dff52472a9b334f71bcd31a21a41d83821050f67", size = 35959, upload-time = "2025-11-14T14:57:41.682Z" }, - { url = "https://files.pythonhosted.org/packages/ce/0f/95a4d1e3bebfdfa7829252369357cf9a764f67569328cd9221f21e2c952e/cymem-2.0.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:891fd9030293a8b652dc7fb9fdc79a910a6c76fc679cd775e6741b819ffea476", size = 43478, upload-time = "2025-11-14T14:57:42.682Z" }, - { url = "https://files.pythonhosted.org/packages/bf/a0/8fc929cc29ae466b7b4efc23ece99cbd3ea34992ccff319089c624d667fd/cymem-2.0.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:89c4889bd16513ce1644ccfe1e7c473ba7ca150f0621e66feac3a571bde09e7e", size = 42695, upload-time = "2025-11-14T14:57:43.741Z" }, - { url = "https://files.pythonhosted.org/packages/4a/b3/deeb01354ebaf384438083ffe0310209ef903db3e7ba5a8f584b06d28387/cymem-2.0.13-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:45dcaba0f48bef9cc3d8b0b92058640244a95a9f12542210b51318da97c2cf28", size = 250573, upload-time = "2025-11-14T14:57:44.81Z" }, - { url = "https://files.pythonhosted.org/packages/36/36/bc980b9a14409f3356309c45a8d88d58797d02002a9d794dd6c84e809d3a/cymem-2.0.13-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e96848faaafccc0abd631f1c5fb194eac0caee4f5a8777fdbb3e349d3a21741c", size = 254572, upload-time = "2025-11-14T14:57:46.023Z" }, - { url = "https://files.pythonhosted.org/packages/fd/dd/a12522952624685bd0f8968e26d2ed6d059c967413ce6eb52292f538f1b0/cymem-2.0.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e02d3e2c3bfeb21185d5a4a70790d9df40629a87d8d7617dc22b4e864f665fa3", size = 248060, upload-time = "2025-11-14T14:57:47.605Z" }, - { url = "https://files.pythonhosted.org/packages/08/11/5dc933ddfeb2dfea747a0b935cb965b9a7580b324d96fc5f5a1b5ff8df29/cymem-2.0.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fece5229fd5ecdcd7a0738affb8c59890e13073ae5626544e13825f26c019d3c", size = 254601, upload-time = "2025-11-14T14:57:48.861Z" }, - { url = "https://files.pythonhosted.org/packages/70/66/d23b06166864fa94e13a98e5922986ce774832936473578febce64448d75/cymem-2.0.13-cp313-cp313-win_amd64.whl", hash = "sha256:38aefeb269597c1a0c2ddf1567dd8605489b661fa0369c6406c1acd433b4c7ba", size = 40103, upload-time = "2025-11-14T14:57:50.396Z" }, - { url = "https://files.pythonhosted.org/packages/2f/9e/c7b21271ab88a21760f3afdec84d2bc09ffa9e6c8d774ad9d4f1afab0416/cymem-2.0.13-cp313-cp313-win_arm64.whl", hash = "sha256:717270dcfd8c8096b479c42708b151002ff98e434a7b6f1f916387a6c791e2ad", size = 36016, upload-time = "2025-11-14T14:57:51.611Z" }, - { url = "https://files.pythonhosted.org/packages/7f/28/d3b03427edc04ae04910edf1c24b993881c3ba93a9729a42bcbb816a1808/cymem-2.0.13-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7e1a863a7f144ffb345397813701509cfc74fc9ed360a4d92799805b4b865dd1", size = 46429, upload-time = "2025-11-14T14:57:52.582Z" }, - { url = "https://files.pythonhosted.org/packages/35/a9/7ed53e481f47ebfb922b0b42e980cec83e98ccb2137dc597ea156642440c/cymem-2.0.13-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c16cb80efc017b054f78998c6b4b013cef509c7b3d802707ce1f85a1d68361bf", size = 46205, upload-time = "2025-11-14T14:57:53.64Z" }, - { url = "https://files.pythonhosted.org/packages/61/39/a3d6ad073cf7f0fbbb8bbf09698c3c8fac11be3f791d710239a4e8dd3438/cymem-2.0.13-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0d78a27c88b26c89bd1ece247d1d5939dba05a1dae6305aad8fd8056b17ddb51", size = 296083, upload-time = "2025-11-14T14:57:55.922Z" }, - { url = "https://files.pythonhosted.org/packages/36/0c/20697c8bc19f624a595833e566f37d7bcb9167b0ce69de896eba7cfc9c2d/cymem-2.0.13-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6d36710760f817194dacb09d9fc45cb6a5062ed75e85f0ef7ad7aeeb13d80cc3", size = 286159, upload-time = "2025-11-14T14:57:57.106Z" }, - { url = "https://files.pythonhosted.org/packages/82/d4/9326e3422d1c2d2b4a8fb859bdcce80138f6ab721ddafa4cba328a505c71/cymem-2.0.13-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c8f30971cadd5dcf73bcfbbc5849b1f1e1f40db8cd846c4aa7d3b5e035c7b583", size = 288186, upload-time = "2025-11-14T14:57:58.334Z" }, - { url = "https://files.pythonhosted.org/packages/ed/bc/68da7dd749b72884dc22e898562f335002d70306069d496376e5ff3b6153/cymem-2.0.13-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:9d441d0e45798ec1fd330373bf7ffa6b795f229275f64016b6a193e6e2a51522", size = 290353, upload-time = "2025-11-14T14:58:00.562Z" }, - { url = "https://files.pythonhosted.org/packages/50/23/dbf2ad6ecd19b99b3aab6203b1a06608bbd04a09c522d836b854f2f30f73/cymem-2.0.13-cp313-cp313t-win_amd64.whl", hash = "sha256:d1c950eebb9f0f15e3ef3591313482a5a611d16fc12d545e2018cd607f40f472", size = 44764, upload-time = "2025-11-14T14:58:01.793Z" }, - { url = "https://files.pythonhosted.org/packages/54/3f/35701c13e1fc7b0895198c8b20068c569a841e0daf8e0b14d1dc0816b28f/cymem-2.0.13-cp313-cp313t-win_arm64.whl", hash = "sha256:042e8611ef862c34a97b13241f5d0da86d58aca3cecc45c533496678e75c5a1f", size = 38964, upload-time = "2025-11-14T14:58:02.87Z" }, -] - [[package]] name = "dataclasses-json" version = "0.6.7" @@ -1214,58 +1092,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-api-core" -version = "2.31.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-auth" }, - { name = "googleapis-common-protos" }, - { name = "proto-plus" }, - { name = "protobuf" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c6/22/155cadf1d49272a9cf48f3168c0f3874fa13397297e611a5ea00cd093880/google_api_core-2.31.0.tar.gz", hash = "sha256:2be84ee0f584c48e6bde1b36766e23348b361fb7e55e56135fc76ce1c397f9c2", size = 176492, upload-time = "2026-06-03T14:52:17.257Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/40/9bdbb60b03a332bd45acb8703da08bbc27d991d35286b62e42acc86d243a/google_api_core-2.31.0-py3-none-any.whl", hash = "sha256:ef79fb3784c71cbac89cbd03301ba0c8fb8ad2aa95d7f9204dd9628f7adf59ab", size = 173102, upload-time = "2026-06-03T14:51:26.729Z" }, -] - -[package.optional-dependencies] -grpc = [ - { name = "grpcio" }, - { name = "grpcio-status" }, -] - -[[package]] -name = "google-auth" -version = "2.55.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography", version = "46.0.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.13'" }, - { name = "cryptography", version = "49.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, - { name = "pyasn1-modules" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/6f/f3f4ac177c67bbee8fe8e88f2ab4f36af88c44a096e165c5217accf6e5d3/google_auth-2.55.1.tar.gz", hash = "sha256:fb2d9b730f2c9b8d326ec8d7222f21aef2ead15bf0513793d6442485d87af0a1", size = 349527, upload-time = "2026-06-25T23:39:27.182Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e8/1d/f6d3ca1ad0725f2e08a1c6915640748a52de2e66596160a4d53b010cccf0/google_auth-2.55.1-py3-none-any.whl", hash = "sha256:eada68dfd52b3b81191827601e2a0c3fa12540c818534b630ddc5355769c3995", size = 252349, upload-time = "2026-06-25T23:38:52.946Z" }, -] - -[[package]] -name = "google-cloud-language" -version = "2.21.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "google-api-core", extra = ["grpc"] }, - { name = "google-auth" }, - { name = "grpcio" }, - { name = "proto-plus" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/49/7267826b24dac0605e6109bd5324b2491d3b9896825a75c21e6c53de3c9a/google_cloud_language-2.21.0.tar.gz", hash = "sha256:30f0e69150c972455e25439a080f7b5630f20bd404dcef8a56ae503f94a4be6d", size = 186992, upload-time = "2026-06-22T23:21:38.029Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/50/3e/f567c303e8b3fa0553e97905c000579bc23fc892bf1f07700196d7ab28c8/google_cloud_language-2.21.0-py3-none-any.whl", hash = "sha256:85ca2e90c7572004502ac06733667e7ed27ec207757f1c21b584c930d8e01500", size = 174819, upload-time = "2026-06-22T23:19:24.258Z" }, -] - [[package]] name = "googleapis-common-protos" version = "1.75.0" @@ -1377,20 +1203,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0d/20/3da8bb0d637feccdc3e1e419bb511ce93651ce7d54164f95de22cc0b8b34/grpcio-1.81.1-cp313-cp313-win_amd64.whl", hash = "sha256:edb59506291b647a30884b1d51a599d605f40b20af4a7dc3d33786a47a31de60", size = 4928648, upload-time = "2026-06-11T12:46:17.823Z" }, ] -[[package]] -name = "grpcio-status" -version = "1.81.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "googleapis-common-protos" }, - { name = "grpcio" }, - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/32/26/0aa9168c87882381fd810d140c279a2490ed6aee655f0515d6f56c5ca404/grpcio_status-1.81.1.tar.gz", hash = "sha256:9389a03e746017b10f0630c064289201458f3ce01f5d7ef4b0bebc1ef6cf82ad", size = 13923, upload-time = "2026-06-11T12:58:48.636Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/5e/5abfec5f7e89d3b7993d57cfb025ca5f968a2c18656d7fcda2b6919440b9/grpcio_status-1.81.1-py3-none-any.whl", hash = "sha256:08072fa9995f4a95c647fc6f4f85e2411573d00087bcabdf30f260114338f232", size = 14638, upload-time = "2026-06-11T12:58:31.982Z" }, -] - [[package]] name = "h11" version = "0.16.0" @@ -2319,54 +2131,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, ] -[[package]] -name = "murmurhash" -version = "1.0.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/23/2e/88c147931ea9725d634840d538622e94122bceaf346233349b7b5c62964b/murmurhash-1.0.15.tar.gz", hash = "sha256:58e2b27b7847f9e2a6edf10b47a8c8dd70a4705f45dccb7bf76aeadacf56ba01", size = 13291, upload-time = "2025-11-14T09:51:15.272Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/09/3c/5e59e29fe971365d27f191a5cbf8a5fb492746e458604fe5d39810da4668/murmurhash-1.0.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f4989c16053a9a83b02c520dd00a31f0877d5fd2ab8a9b6b75ed9eba0e25c489", size = 27463, upload-time = "2025-11-14T09:49:53.158Z" }, - { url = "https://files.pythonhosted.org/packages/38/3d/ace00a9b82beaa99a8a7a52e98171cfbf13c0066d2f820e84a5d572e3bd0/murmurhash-1.0.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:899068ba3d7c371e7edd093852c634cce802fefd9aaddfcc0d2fda1d7433c7f9", size = 27714, upload-time = "2025-11-14T09:49:54.855Z" }, - { url = "https://files.pythonhosted.org/packages/10/0f/34f1c4f97424ea1bc72b1e3bdf61ac34f4c5555ec9163721f1e4cafe5b1d/murmurhash-1.0.15-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fe883982114de576c793fd1cf55945c8ee6453ad4c4785ac1a48f84e74fdc650", size = 122570, upload-time = "2025-11-14T09:49:55.977Z" }, - { url = "https://files.pythonhosted.org/packages/b9/75/0019717a16ce5a7b088fc50a3ecb513035e4196c5e569bf4a2e16bcc0414/murmurhash-1.0.15-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:342277d8d7f712d136507fb3ccdba26c076a34ca0f8d1b96f65f0daa556da2e9", size = 123194, upload-time = "2025-11-14T09:49:57.462Z" }, - { url = "https://files.pythonhosted.org/packages/7b/a4/c1c95ce60b816c2255098164e424752779269c93f5d6dceaa213346789a2/murmurhash-1.0.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc54facccb32fe1e97d6231edd4f3e2937467c35658b26aa35bbd6a87ebb7cb0", size = 122461, upload-time = "2025-11-14T09:49:58.686Z" }, - { url = "https://files.pythonhosted.org/packages/63/28/e1f79369a6e8d1a5901346ed2fd3a5c56e647d0b849044870c071cb64e1c/murmurhash-1.0.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e525bbd8e26e6b9ab1b56758a59b16c2fffd73bad2f7b8bf361c16f70ff1d980", size = 121676, upload-time = "2025-11-14T09:49:59.888Z" }, - { url = "https://files.pythonhosted.org/packages/1d/7c/e2be1f5387e5898f6551cf81c4220975858b9dbda4d471b133750945599a/murmurhash-1.0.15-cp310-cp310-win_amd64.whl", hash = "sha256:2224f30f7729717644745a6f513ea7662517dfe7b1867cf1588177f64c61df3c", size = 25156, upload-time = "2025-11-14T09:50:01.016Z" }, - { url = "https://files.pythonhosted.org/packages/74/07/0df6e1a753de68368662cbbb8f88558e2c877d3886ac12b30953fb8ed335/murmurhash-1.0.15-cp310-cp310-win_arm64.whl", hash = "sha256:8a181494b5f03ba831f9a13f2de3aab9ef591e508e57239043d65c5c592f5837", size = 23270, upload-time = "2025-11-14T09:50:01.99Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ca/77d3e69924a8eb4508bb4f0ad34e46adbeedeb93616a71080e61e53dad71/murmurhash-1.0.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f32307fb9347680bb4fe1cbef6362fb39bd994f1b59abd8c09ca174e44199081", size = 27397, upload-time = "2025-11-14T09:50:03.077Z" }, - { url = "https://files.pythonhosted.org/packages/e6/53/a936f577d35b245d47b310f29e5e9f09fcac776c8c992f1ab51a9fb0cee2/murmurhash-1.0.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:539d8405885d1d19c005f3a2313b47e8e54b0ee89915eb8dfbb430b194328e6c", size = 27692, upload-time = "2025-11-14T09:50:04.144Z" }, - { url = "https://files.pythonhosted.org/packages/4d/64/5f8cfd1fd9cbeb43fcff96672f5bd9e7e1598d1c970f808ecd915490dc20/murmurhash-1.0.15-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c4cd739a00f5a4602201b74568ddabae46ec304719d9be752fd8f534a9464b5e", size = 128396, upload-time = "2025-11-14T09:50:05.268Z" }, - { url = "https://files.pythonhosted.org/packages/ac/10/d9ce29d559a75db0d8a3f13ea12c7f541ec9de2afca38dc70418b890eedb/murmurhash-1.0.15-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:44d211bcc3ec203c47dac06f48ee871093fcbdffa6652a6cc5ea7180306680a8", size = 128687, upload-time = "2025-11-14T09:50:06.527Z" }, - { url = "https://files.pythonhosted.org/packages/48/cd/dc97ab7e68cdfa1537a56e36dbc846c5a66701cc39ecee2d4399fe61996c/murmurhash-1.0.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f9bf47101354fb1dc4b2e313192566f04ba295c28a37e2f71c692759acc1ba3c", size = 128198, upload-time = "2025-11-14T09:50:08.062Z" }, - { url = "https://files.pythonhosted.org/packages/53/73/32f2aaa22c1e4afae337106baf0c938abf36a6cc879cfee83a00461bbbf7/murmurhash-1.0.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c69b4d3bcd6233782a78907fe10b9b7a796bdc5d28060cf097d067bec280a5d", size = 127214, upload-time = "2025-11-14T09:50:09.265Z" }, - { url = "https://files.pythonhosted.org/packages/82/ed/812103a7f353eba2d83655b08205e13a38c93b4db0692f94756e1eb44516/murmurhash-1.0.15-cp311-cp311-win_amd64.whl", hash = "sha256:e43a69496342ce530bdd670264cb7c8f45490b296e4764c837ce577e3c7ebd53", size = 25241, upload-time = "2025-11-14T09:50:10.373Z" }, - { url = "https://files.pythonhosted.org/packages/eb/5f/2c511bdd28f7c24da37a00116ffd0432b65669d098f0d0260c66ac0ffdc2/murmurhash-1.0.15-cp311-cp311-win_arm64.whl", hash = "sha256:f3e99a6ee36ef5372df5f138e3d9c801420776d3641a34a49e5c2555f44edba7", size = 23216, upload-time = "2025-11-14T09:50:11.651Z" }, - { url = "https://files.pythonhosted.org/packages/b6/46/be8522d3456fdccf1b8b049c6d82e7a3c1114c4fc2cfe14b04cba4b3e701/murmurhash-1.0.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d37e3ae44746bca80b1a917c2ea625cf216913564ed43f69d2888e5df97db0cb", size = 27884, upload-time = "2025-11-14T09:50:13.133Z" }, - { url = "https://files.pythonhosted.org/packages/ed/cc/630449bf4f6178d7daf948ce46ad00b25d279065fc30abd8d706be3d87e0/murmurhash-1.0.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0861cb11039409eaf46878456b7d985ef17b6b484103a6fc367b2ecec846891d", size = 27855, upload-time = "2025-11-14T09:50:14.859Z" }, - { url = "https://files.pythonhosted.org/packages/ff/30/ea8f601a9bf44db99468696efd59eb9cff1157cd55cb586d67116697583f/murmurhash-1.0.15-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5a301decfaccfec70fe55cb01dde2a012c3014a874542eaa7cc73477bb749616", size = 134088, upload-time = "2025-11-14T09:50:15.958Z" }, - { url = "https://files.pythonhosted.org/packages/c9/de/c40ce8c0877d406691e735b8d6e9c815f36a82b499d358313db5dbe219d7/murmurhash-1.0.15-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:32c6fde7bd7e9407003370a07b5f4addacabe1556ad3dc2cac246b7a2bba3400", size = 133978, upload-time = "2025-11-14T09:50:17.572Z" }, - { url = "https://files.pythonhosted.org/packages/47/84/bd49963ecd84ebab2fe66595e2d1ed41d5e8b5153af5dc930f0bd827007c/murmurhash-1.0.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d8b43a7011540dc3c7ce66f2134df9732e2bc3bbb4a35f6458bc755e48bde26", size = 132956, upload-time = "2025-11-14T09:50:18.742Z" }, - { url = "https://files.pythonhosted.org/packages/4f/7c/2530769c545074417c862583f05f4245644599f1e9ff619b3dfe2969aafc/murmurhash-1.0.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:43bf4541892ecd95963fcd307bf1c575fc0fee1682f41c93007adee71ca2bb40", size = 134184, upload-time = "2025-11-14T09:50:19.941Z" }, - { url = "https://files.pythonhosted.org/packages/84/a4/b249b042f5afe34d14ada2dc4afc777e883c15863296756179652e081c44/murmurhash-1.0.15-cp312-cp312-win_amd64.whl", hash = "sha256:f4ac15a2089dc42e6eb0966622d42d2521590a12c92480aafecf34c085302cca", size = 25647, upload-time = "2025-11-14T09:50:21.049Z" }, - { url = "https://files.pythonhosted.org/packages/13/bf/028179259aebc18fd4ba5cae2601d1d47517427a537ab44336446431a215/murmurhash-1.0.15-cp312-cp312-win_arm64.whl", hash = "sha256:4a70ca4ae19e600d9be3da64d00710e79dde388a4d162f22078d64844d0ebdda", size = 23338, upload-time = "2025-11-14T09:50:22.359Z" }, - { url = "https://files.pythonhosted.org/packages/29/2f/ba300b5f04dae0409202d6285668b8a9d3ade43a846abee3ef611cb388d5/murmurhash-1.0.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:fe50dc70e52786759358fd1471e309b94dddfffb9320d9dfea233c7684c894ba", size = 27861, upload-time = "2025-11-14T09:50:23.804Z" }, - { url = "https://files.pythonhosted.org/packages/34/02/29c19d268e6f4ea1ed2a462c901eed1ed35b454e2cbc57da592fad663ac6/murmurhash-1.0.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1349a7c23f6092e7998ddc5bd28546cc31a595afc61e9fdb3afc423feec3d7ad", size = 27840, upload-time = "2025-11-14T09:50:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/e2/63/58e2de2b5232cd294c64092688c422196e74f9fa8b3958bdf02d33df24b9/murmurhash-1.0.15-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3ba6d05de2613535b5a9227d4ad8ef40a540465f64660d4a8800634ae10e04f", size = 133080, upload-time = "2025-11-14T09:50:26.566Z" }, - { url = "https://files.pythonhosted.org/packages/aa/9a/d13e2e9f8ba1ced06840921a50f7cece0a475453284158a3018b72679761/murmurhash-1.0.15-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fa1b70b3cc2801ab44179c65827bbd12009c68b34e9d9ce7125b6a0bd35af63c", size = 132648, upload-time = "2025-11-14T09:50:27.788Z" }, - { url = "https://files.pythonhosted.org/packages/b2/e1/47994f1813fa205c84977b0ff51ae6709f8539af052c7491a5f863d82bdc/murmurhash-1.0.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:213d710fb6f4ef3bc11abbfad0fa94a75ffb675b7dc158c123471e5de869f9af", size = 131502, upload-time = "2025-11-14T09:50:29.339Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ea/90c1fd00b4aeb704fb5e84cd666b33ffd7f245155048071ffbb51d2bb57d/murmurhash-1.0.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b65a5c4e7f5d71f7ccac2d2b60bdf7092d7976270878cfec59d5a66a533db823", size = 132736, upload-time = "2025-11-14T09:50:30.545Z" }, - { url = "https://files.pythonhosted.org/packages/00/db/da73462dbfa77f6433b128d2120ba7ba300f8c06dc4f4e022c38d240a5f5/murmurhash-1.0.15-cp313-cp313-win_amd64.whl", hash = "sha256:9aba94c5d841e1904cd110e94ceb7f49cfb60a874bbfb27e0373622998fb7c7c", size = 25682, upload-time = "2025-11-14T09:50:31.624Z" }, - { url = "https://files.pythonhosted.org/packages/bb/83/032729ef14971b938fbef41ee125fc8800020ee229bd35178b6ede8ee934/murmurhash-1.0.15-cp313-cp313-win_arm64.whl", hash = "sha256:263807eca40d08c7b702413e45cca75ecb5883aa337237dc5addb660f1483378", size = 23370, upload-time = "2025-11-14T09:50:33.264Z" }, - { url = "https://files.pythonhosted.org/packages/10/83/7547d9205e9bd2f8e5dfd0b682cc9277594f98909f228eb359489baec1df/murmurhash-1.0.15-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:694fd42a74b7ce257169d14c24aa616aa6cd4ccf8abe50eca0557e08da99d055", size = 29955, upload-time = "2025-11-14T09:50:34.488Z" }, - { url = "https://files.pythonhosted.org/packages/b7/c7/3afd5de7a5b3ae07fe2d3a3271b327ee1489c58ba2b2f2159bd31a25edb9/murmurhash-1.0.15-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a2ea4546ba426390beff3cd10db8f0152fdc9072c4f2583ec7d8aa9f3e4ac070", size = 30108, upload-time = "2025-11-14T09:50:35.53Z" }, - { url = "https://files.pythonhosted.org/packages/02/69/d6637ee67d78ebb2538c00411f28ea5c154886bbe1db16c49435a8a4ab16/murmurhash-1.0.15-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:34e5a91139c40b10f98d0b297907f5d5267b4b1b2e5dd2eb74a021824f751b98", size = 164054, upload-time = "2025-11-14T09:50:36.591Z" }, - { url = "https://files.pythonhosted.org/packages/ab/4c/89e590165b4c7da6bf941441212a721a270195332d3aacfdfdf527d466ca/murmurhash-1.0.15-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:dc35606868a5961cf42e79314ca0bddf5a400ce377b14d83192057928d6252ec", size = 168153, upload-time = "2025-11-14T09:50:37.856Z" }, - { url = "https://files.pythonhosted.org/packages/07/7a/95c42df0c21d2e413b9fcd17317a7587351daeb264dc29c6aec1fdbd26f8/murmurhash-1.0.15-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:43cc6ac3b91ca0f7a5ae9c063ba4d6c26972c97fd7c25280ecc666413e4c5535", size = 164345, upload-time = "2025-11-14T09:50:39.346Z" }, - { url = "https://files.pythonhosted.org/packages/d0/22/9d02c880a88b83bb3ce7d6a38fb727373ab78d82e5f3d8d9fc5612219f90/murmurhash-1.0.15-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:847d712136cb462f0e4bd6229ee2d9eb996d8854eb8312dff3d20c8f5181fda5", size = 161990, upload-time = "2025-11-14T09:50:40.689Z" }, - { url = "https://files.pythonhosted.org/packages/9a/e3/750232524e0dc262e8dcede6536dafc766faadd9a52f1d23746b02948ad8/murmurhash-1.0.15-cp313-cp313t-win_amd64.whl", hash = "sha256:2680851af6901dbe66cc4aa7ef8e263de47e6e1b425ae324caa571bdf18f8d58", size = 28812, upload-time = "2025-11-14T09:50:41.971Z" }, - { url = "https://files.pythonhosted.org/packages/ff/89/4ad9d215ef6ade89f27a72dc4e86b98ef1a43534cc3e6a6900a362a0bf0a/murmurhash-1.0.15-cp313-cp313t-win_arm64.whl", hash = "sha256:189a8de4d657b5da9efd66601b0636330b08262b3a55431f2379097c986995d0", size = 25398, upload-time = "2025-11-14T09:50:43.023Z" }, -] - [[package]] name = "mypy-extensions" version = "1.1.0" @@ -2404,6 +2168,7 @@ dependencies = [ { name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "onnxruntime", version = "1.23.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "onnxruntime", version = "1.27.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging" }, { name = "prompt-toolkit" }, { name = "protobuf" }, { name = "pydantic" }, @@ -2417,24 +2182,19 @@ dependencies = [ all = [ { name = "aiofiles" }, { name = "chainlit" }, - { name = "fast-langdetect" }, { name = "fastapi" }, - { name = "google-cloud-language" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, { name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, { name = "openai" }, { name = "opentelemetry-api" }, { name = "pandas" }, - { name = "presidio-analyzer", marker = "python_full_version < '3.13'" }, - { name = "presidio-anonymizer", marker = "python_full_version < '3.13'" }, { name = "starlette" }, { name = "streamlit" }, { name = "tornado" }, { name = "tqdm" }, { name = "uvicorn" }, { name = "watchdog" }, - { name = "yara-python" }, ] chat-ui = [ { name = "chainlit" }, @@ -2448,19 +2208,6 @@ eval = [ { name = "tornado" }, { name = "tqdm" }, ] -gcp = [ - { name = "google-cloud-language" }, -] -jailbreak = [ - { name = "yara-python" }, -] -multilingual = [ - { name = "fast-langdetect" }, -] -sdd = [ - { name = "presidio-analyzer", marker = "python_full_version < '3.13'" }, - { name = "presidio-anonymizer", marker = "python_full_version < '3.13'" }, -] server = [ { name = "aiofiles" }, { name = "fastapi" }, @@ -2553,13 +2300,9 @@ requires-dist = [ { name = "chainlit", marker = "extra == 'all'", specifier = ">=2.11.0,<3.0.0" }, { name = "chainlit", marker = "extra == 'chat-ui'", specifier = ">=2.11.0,<3.0.0" }, { name = "dataclasses-json", specifier = ">=0.6.7,<0.7.0" }, - { name = "fast-langdetect", marker = "extra == 'all'", specifier = ">=1.0.0" }, - { name = "fast-langdetect", marker = "extra == 'multilingual'", specifier = ">=1.0.0" }, { name = "fastapi", marker = "extra == 'all'", specifier = ">=0.103.0" }, { name = "fastapi", marker = "extra == 'server'", specifier = ">=0.103.0" }, { name = "fastembed", specifier = ">=0.2.2" }, - { name = "google-cloud-language", marker = "extra == 'all'", specifier = ">=2.14.0" }, - { name = "google-cloud-language", marker = "extra == 'gcp'", specifier = ">=2.14.0" }, { name = "httpx", specifier = ">=0.24.1" }, { name = "jinja2", specifier = ">=3.1.6" }, { name = "jsonschema", specifier = ">=4.26.0,<5.0.0" }, @@ -2580,12 +2323,9 @@ requires-dist = [ { name = "openai", marker = "extra == 'server'", specifier = ">=1.0.0,<3.0.0" }, { name = "opentelemetry-api", marker = "extra == 'all'", specifier = ">=1.27.0,<2.0.0" }, { name = "opentelemetry-api", marker = "extra == 'tracing'", specifier = ">=1.27.0,<2.0.0" }, + { name = "packaging", specifier = ">=23.0" }, { name = "pandas", marker = "extra == 'all'", specifier = ">=1.4.0,<3.0.0" }, { name = "pandas", marker = "extra == 'eval'", specifier = ">=1.4.0,<3.0.0" }, - { name = "presidio-analyzer", marker = "python_full_version < '3.13' and extra == 'all'", specifier = ">=2.2" }, - { name = "presidio-analyzer", marker = "python_full_version < '3.13' and extra == 'sdd'", specifier = ">=2.2" }, - { name = "presidio-anonymizer", marker = "python_full_version < '3.13' and extra == 'all'", specifier = ">=2.2" }, - { name = "presidio-anonymizer", marker = "python_full_version < '3.13' and extra == 'sdd'", specifier = ">=2.2" }, { name = "prompt-toolkit", specifier = ">=3.0" }, { name = "protobuf", specifier = ">=5.29.5" }, { name = "pydantic", specifier = ">=2.5,<3.0" }, @@ -2605,10 +2345,8 @@ requires-dist = [ { name = "uvicorn", marker = "extra == 'server'", specifier = ">=0.23" }, { name = "watchdog", marker = "extra == 'all'", specifier = ">=3.0.0" }, { name = "watchdog", marker = "extra == 'server'", specifier = ">=3.0.0" }, - { name = "yara-python", marker = "extra == 'all'", specifier = ">=4.5.1,<5.0.0" }, - { name = "yara-python", marker = "extra == 'jailbreak'", specifier = ">=4.5.1,<5.0.0" }, ] -provides-extras = ["tracing", "server", "sdd", "gcp", "jailbreak", "multilingual", "chat-ui", "eval", "all"] +provides-extras = ["tracing", "server", "chat-ui", "eval", "all"] [package.metadata.requires-dev] dev = [ @@ -3821,15 +3559,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, ] -[[package]] -name = "phonenumbers" -version = "9.0.33" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/37/dfc4cf24169f1a7169ebaedaf896c818f0add8603409d1e748e3085ccdc0/phonenumbers-9.0.33.tar.gz", hash = "sha256:9ab8a02b940b90c64f3866c0b25a30e567ddf7bb9836a3e11efdb0478f65fc1c", size = 2306756, upload-time = "2026-06-22T10:23:33.428Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/29/f7e30e3dbd3c7e3d9c4a55006112c04ee62b4765a31f21bcc28c253ac3f1/phonenumbers-9.0.33-py2.py3-none-any.whl", hash = "sha256:ba1d0da52711d5fdda6b2b673b2621fe80774fc5d1b2e5a6ef783396b0343186", size = 2595422, upload-time = "2026-06-22T10:23:29.925Z" }, -] - [[package]] name = "pillow" version = "12.3.0" @@ -3916,78 +3645,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/6e/4b28b62ecb6aae56769c34a8ff1d661473ec1e9519e2d5f8b2c150086b26/pre_commit-4.6.0-py2.py3-none-any.whl", hash = "sha256:e2cf246f7299edcabcf15f9b0571fdce06058527f0a06535068a86d38089f29b", size = 226472, upload-time = "2026-04-21T20:31:40.092Z" }, ] -[[package]] -name = "preshed" -version = "3.0.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cymem" }, - { name = "murmurhash" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/75/fe6b7bbd0dea530a001b0e24c331b21a0be2786e402abf3c57f5dce43d4b/preshed-3.0.13.tar.gz", hash = "sha256:d75f718bbfd97e992f7827e0fa7faf6a91bdd9c922d5baa4b50d62731396cb89", size = 18338, upload-time = "2026-03-23T08:57:31.378Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/45/7e/d55d8cdeefa78995eec15a11ae16cbd0581a0be2342527a64251fd948cef/preshed-3.0.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:42c58b07e8b431e33d0ad9922e896632453821cad8b09171b619b8c61101916f", size = 136920, upload-time = "2026-03-23T08:56:10.829Z" }, - { url = "https://files.pythonhosted.org/packages/10/bc/ee1f388a97c613e656d774b522b4ddc1cd32e984ca4eb1157c5d822e9011/preshed-3.0.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a06e27f4e5b9d7943840087828c6a0dae4a3475576d12c2e95b71abbb325a80b", size = 137576, upload-time = "2026-03-23T08:56:12.441Z" }, - { url = "https://files.pythonhosted.org/packages/a6/dd/24c5a576035df4043998e1069718dd7369e107ce9d169df2333d00461dbf/preshed-3.0.13-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b82d7a7bb63d248a6cbbfcabb4a570c993d54d964e39dc5d85c14018ba2079e", size = 780270, upload-time = "2026-03-23T08:56:14.108Z" }, - { url = "https://files.pythonhosted.org/packages/e5/ab/fb0f6808fffad96c962ce254587cae2bb7df0fda3e6d6b481ce4f60f6c2d/preshed-3.0.13-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bef84b225d226af43adfee78ce5ddede72a6155ce5292c1a41dcd1f0b9c87c30", size = 779722, upload-time = "2026-03-23T08:56:15.721Z" }, - { url = "https://files.pythonhosted.org/packages/bf/7f/c9948dde95bf965c6af2c31f0dbbc6c7e5433b5de1c85f20644edf38c78c/preshed-3.0.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:70d502e081348df207d90f347f21770ed596822bb04eb3c3b32b7281579e90c6", size = 1775435, upload-time = "2026-03-23T08:56:17.655Z" }, - { url = "https://files.pythonhosted.org/packages/d2/57/8db29ac57b981ef19d1078001aa6c2055a3eed46998c1c93f3d1fdb86106/preshed-3.0.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:985cb9b097beda76cd13c01a0499707103e8915f888fa30f8aa8324ef2cc6b08", size = 1842612, upload-time = "2026-03-23T08:56:19.755Z" }, - { url = "https://files.pythonhosted.org/packages/38/e5/ead05efc423be237fba76a3bf0eeb492e5d3801504c096c3552c517f2f72/preshed-3.0.13-cp310-cp310-win_amd64.whl", hash = "sha256:867aa73abbf4ee3b4d7662148091c33a8c039271269e3a7f1e0ca995f91995c8", size = 121951, upload-time = "2026-03-23T08:56:21.138Z" }, - { url = "https://files.pythonhosted.org/packages/aa/80/9cf7f7c208046c97d4b2765f89545a6ea8cfefbd87f0141dde61e6f098ac/preshed-3.0.13-cp310-cp310-win_arm64.whl", hash = "sha256:2b704e46cb7b88f656ef16a3e5347b36525a1c53721d327a4ba1457404101f85", size = 109604, upload-time = "2026-03-23T08:56:22.537Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d1/7bc39738388b38ff48cecbb326a9b2bb3f422bb32097be92e010f3162395/preshed-3.0.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5268c0e6fa96f50cdf87f516c2d4b32563c12706ee768e75c00e8d0098acd545", size = 136718, upload-time = "2026-03-23T08:56:23.889Z" }, - { url = "https://files.pythonhosted.org/packages/f6/65/de465b6801740140c2b5d2db6c312ca7937dcfd0442f1ae7d50dee529544/preshed-3.0.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df642547a1a94079978a0ea8f4593ab4b8d3bd43f767bef0ef64d9a214f8c4c9", size = 137261, upload-time = "2026-03-23T08:56:25.303Z" }, - { url = "https://files.pythonhosted.org/packages/89/83/478ee078746a4a413c841542caebd2ea74b659475b8bf5f2e3724b6fe655/preshed-3.0.13-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09397592d333a77f88454e72b7f1f941b2afaf040b392b9e74898dbc4648cdf5", size = 821010, upload-time = "2026-03-23T08:56:26.455Z" }, - { url = "https://files.pythonhosted.org/packages/ee/2e/1ac761e973966893cd3a0ad3256360365276e2d1e779e351448981a1156a/preshed-3.0.13-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f8e6fe0620ed0f96a246d46447055c447e071cd8222731a045c235e8a758c918", size = 823096, upload-time = "2026-03-23T08:56:28.126Z" }, - { url = "https://files.pythonhosted.org/packages/3c/51/7824cfd85dd7fe547888de20228ebd87d9acd3708206d30b82211e382d23/preshed-3.0.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:502f93f49a22788203f02d3067d4ea077a0cca3864de6a792eae12e7ce589e14", size = 1812148, upload-time = "2026-03-23T08:56:29.755Z" }, - { url = "https://files.pythonhosted.org/packages/34/48/32160a24705d56179de6af838c10a0c735c955dae5f9e4bb344750b79bc2/preshed-3.0.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:acd4d89abeca3678c5d8c89b3cd351314465bc67c7fa053d2644f8513e543386", size = 1881154, upload-time = "2026-03-23T08:56:31.49Z" }, - { url = "https://files.pythonhosted.org/packages/ed/22/0344b50f8b1ad9e3aac08099c47e1aba91c81602fd117d2673f6606ecae6/preshed-3.0.13-cp311-cp311-win_amd64.whl", hash = "sha256:de87fbabb0f37c3c92d4dd9b94fc82ab73cdab4247cdfbd57ab3926caa983919", size = 122219, upload-time = "2026-03-23T08:56:32.74Z" }, - { url = "https://files.pythonhosted.org/packages/33/c4/812eeaa568510f396e27edab01100ca71418f032fd7098b107f12e572361/preshed-3.0.13-cp311-cp311-win_arm64.whl", hash = "sha256:5e2753779832e411e93eb727f3d409c0a6b7408e5ce4dd868076d8ece48c7693", size = 109308, upload-time = "2026-03-23T08:56:33.839Z" }, - { url = "https://files.pythonhosted.org/packages/39/fb/ccff23c44c04088c248539005fcda78b9014512a34d170c5360f02ad908b/preshed-3.0.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5d14eea14bd01291388928991d7df7d60b9fd19ae970e55006eb4d29b0c1e8eb", size = 138497, upload-time = "2026-03-23T08:56:35.321Z" }, - { url = "https://files.pythonhosted.org/packages/8e/ce/cad5a8145881a771e6c0d002f2e585fc19b962f120860b54d32af5baa342/preshed-3.0.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f05b08ce92399c0655b5e0eb5a1cc1f9e295703ed3aabdfaf6538dfa8ae23d57", size = 138010, upload-time = "2026-03-23T08:56:36.399Z" }, - { url = "https://files.pythonhosted.org/packages/a7/a2/c5fed4fb3e946699259d11e4036a3cfdd8c89b3e542e3077d46781642425/preshed-3.0.13-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:62cf7f3113132891d6bba70ff547ad81c6fe50a31930bbbb8499f1d47cd122b7", size = 861498, upload-time = "2026-03-23T08:56:37.67Z" }, - { url = "https://files.pythonhosted.org/packages/51/94/8c9bc48a6ea4903f53a1a0031ce8e35687526949f25821762ef21493c007/preshed-3.0.13-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8b8de3f58043070a354477995acdd98626ce43e4193c708ebd0f694e467f5155", size = 868988, upload-time = "2026-03-23T08:56:39.324Z" }, - { url = "https://files.pythonhosted.org/packages/b6/df/ecd2f40055ff52527ca117ffbfafb888c1a3079b59fbabe03c5b8f9b7240/preshed-3.0.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:183b339956a9e1d7a4a00038a3b9587a734db9e8bd915939a49791bd1b372156", size = 1847382, upload-time = "2026-03-23T08:56:40.89Z" }, - { url = "https://files.pythonhosted.org/packages/e6/88/bdb244e40284ded3632a9f88c23bc80230bd7b2ae4a8b7f2cc91adead7a8/preshed-3.0.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e77bed56aded7cbe5d28d6bd2178bc5b13eda0e0e464dab205fb578fa915000", size = 1919236, upload-time = "2026-03-23T08:56:42.616Z" }, - { url = "https://files.pythonhosted.org/packages/a0/c9/c91ea56342e6c364fc69b444a1ac5432327857199c44032c9cc9dc4c3a23/preshed-3.0.13-cp312-cp312-win_amd64.whl", hash = "sha256:04d8f13f2986e5d11af5ac51f55ce3106c70c41b483d20ea392e6180bdd0f870", size = 122938, upload-time = "2026-03-23T08:56:44.271Z" }, - { url = "https://files.pythonhosted.org/packages/b2/0b/6a99d99619fd83b14c696e2489caed7070647488d4d3ac0b723d35db2de0/preshed-3.0.13-cp312-cp312-win_arm64.whl", hash = "sha256:19318dc1cd8cac6663c6c830bf7e0002d2de853769fb03e056774e97c21bedfd", size = 109194, upload-time = "2026-03-23T08:56:45.346Z" }, - { url = "https://files.pythonhosted.org/packages/0e/2a/401158195d6dc7f6aef0b354d74d0e95c9da124499448c2b3dbb95b71204/preshed-3.0.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0d0c14187dc0078d8a63bf190ec045a4d13e7748b6caeb557a7d575e411410b", size = 137289, upload-time = "2026-03-23T08:56:46.516Z" }, - { url = "https://files.pythonhosted.org/packages/88/8f/e20e64573988528785447a6893b2e7ab287ecfd85b3888e978b28812fd20/preshed-3.0.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7770987c2e57497cd26124a9be5f652b5b3ccd0def89859ab0da8bca6144a3de", size = 136847, upload-time = "2026-03-23T08:56:47.572Z" }, - { url = "https://files.pythonhosted.org/packages/b9/72/18168f881359c4482d312f8dc196371bdd61c1583a52b34390da4c88bbea/preshed-3.0.13-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4a7bc48220de579be6bdb0a8715482cf36e2a625a6fd5ad26c9f43485a4a23b5", size = 831478, upload-time = "2026-03-23T08:56:48.769Z" }, - { url = "https://files.pythonhosted.org/packages/fd/3a/3543476091087102775568cea9885dde3453569e9aeee365809108de572f/preshed-3.0.13-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5c8462472f790c16708306aef3a102a762bd19dfe3d2f8ee08bd5e12f51b835", size = 839913, upload-time = "2026-03-23T08:56:49.937Z" }, - { url = "https://files.pythonhosted.org/packages/cf/65/b13f01329decc44ef53cfb6b4601ba85382dcb2a4ec78d9250f03a418066/preshed-3.0.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c046736239cc8d72670749b79b526e4111839a2fc461a58545d212797649129c", size = 1816452, upload-time = "2026-03-23T08:56:51.233Z" }, - { url = "https://files.pythonhosted.org/packages/d1/c7/f1a996c6832234efd4d543041b582418d41ac480ee55c557ec9e65344637/preshed-3.0.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7c333f18e9a81c8a6de0603fd8781e17115324b117c445ca91abdf7bfb1abe49", size = 1888978, upload-time = "2026-03-23T08:56:52.591Z" }, - { url = "https://files.pythonhosted.org/packages/e3/b9/96fb71499049885ce19545903fdd38877bbc2be0da47e37c04d01f3e9f66/preshed-3.0.13-cp313-cp313-win_amd64.whl", hash = "sha256:461327f8dd36520dcf1fd55a671e0c3c2c97a2d95e22fc85faa31173f4785dda", size = 122134, upload-time = "2026-03-23T08:56:54.392Z" }, - { url = "https://files.pythonhosted.org/packages/ef/a7/32a4903019d936a2316fdd330bedddac287ac26326107d24fb76a1fbc60a/preshed-3.0.13-cp313-cp313-win_arm64.whl", hash = "sha256:35d6c5acb3ee3b12b87a551913063f0cec784055c2af16e028c19fe875f079d0", size = 108497, upload-time = "2026-03-23T08:56:55.816Z" }, -] - -[[package]] -name = "presidio-analyzer" -version = "2.2.362" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "phonenumbers" }, - { name = "pydantic" }, - { name = "pyyaml" }, - { name = "regex" }, - { name = "spacy" }, - { name = "tldextract" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/8a/d6cf4bddbb11d9c78f5c9342bbbcde4f90fe4e3c7de114a836ec4ed8a6cf/presidio_analyzer-2.2.362-py3-none-any.whl", hash = "sha256:4c36438924b1fcb4df92ea5cf2d8dc57508808e116b10923c983b8732aa07d90", size = 201084, upload-time = "2026-03-15T12:40:43.801Z" }, -] - -[[package]] -name = "presidio-anonymizer" -version = "2.2.363" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cryptography", version = "46.0.7", source = { registry = "https://pypi.org/simple" } }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e1/2b/8ce5b595ae0cee8cade7cf2d1620c63a2fe27726e5169732e604d60adfae/presidio_anonymizer-2.2.363.tar.gz", hash = "sha256:27b63f3de85507ecc732a29a63e01ac424a810aefe5b90f05ba9b558320d37ab", size = 25254, upload-time = "2026-06-28T16:09:14.089Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/60/03eb1ae29a8ee93a7227147fd9cce9e17c7088102b6dcf6ec5545e77847c/presidio_anonymizer-2.2.363-py3-none-any.whl", hash = "sha256:d689fa5b837541eeea5a772b79058a972185d560a232b83965d9384bdd0c62ad", size = 36890, upload-time = "2026-06-28T16:09:12.986Z" }, -] - [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -4094,18 +3751,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, ] -[[package]] -name = "proto-plus" -version = "1.28.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "protobuf" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c9/56/e647b0c675392d2da368da7b6f158f7368b18542fd6f7d7400a2f39de000/proto_plus-1.28.0.tar.gz", hash = "sha256:38e5696342835b08fc116f30a25665b29531cda9d5d5643e9b81fc312385abd9", size = 57221, upload-time = "2026-05-07T08:04:50.811Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/20/b122d4626976acb81132036d2ad1bb35a1a8775fceb837ec30964622516a/proto_plus-1.28.0-py3-none-any.whl", hash = "sha256:a630604310899e73c59ec302e5765c058d412b2f090b9c79c8822589f14955b8", size = 50410, upload-time = "2026-05-07T08:03:31.962Z" }, -] - [[package]] name = "protobuf" version = "7.35.1" @@ -4216,27 +3861,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 = "pycparser" version = "3.0" @@ -4772,18 +4396,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, ] -[[package]] -name = "requests-file" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3c/f8/5dc70102e4d337063452c82e1f0d95e39abfe67aa222ed8a5ddeb9df8de8/requests_file-3.0.1.tar.gz", hash = "sha256:f14243d7796c588f3521bd423c5dea2ee4cc730e54a3cac9574d78aca1272576", size = 6967, upload-time = "2025-10-20T18:56:42.279Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/d5/de8f089119205a09da657ed4784c584ede8381a0ce6821212a6d4ca47054/requests_file-3.0.1-py2.py3-none-any.whl", hash = "sha256:d0f5eb94353986d998f80ac63c7f146a307728be051d4d1cd390dbdb59c10fa2", size = 4514, upload-time = "2025-10-20T18:56:41.184Z" }, -] - [[package]] name = "requests-toolbelt" version = "1.0.0" @@ -5015,15 +4627,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" }, ] -[[package]] -name = "setuptools" -version = "81.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/1c/73e719955c59b8e424d015ab450f51c0af856ae46ea2da83eba51cc88de1/setuptools-81.0.0.tar.gz", hash = "sha256:487b53915f52501f0a79ccfd0c02c165ffe06631443a886740b91af4b7a5845a", size = 1198299, upload-time = "2026-02-06T21:10:39.601Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, -] - [[package]] name = "shellingham" version = "1.5.4" @@ -5063,18 +4666,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 = "smart-open" -version = "8.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/29/3e/79fd5fd2375a8a500b9ec2f6a0762fc1ac33e35582d4a87483a78d19408f/smart_open-8.0.0.tar.gz", hash = "sha256:5a2008d60688bd3b33c52e2ef666d3c60cf956e73e215de8c7b242cf56fdd1b2", size = 61520, upload-time = "2026-06-27T16:28:11.894Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/bd/1c92e69a1daff70118f21e18ef3a100c114f00f08b64a1074484f12d9020/smart_open-8.0.0-py3-none-any.whl", hash = "sha256:ff4f395c9e86f23e27771dc4ba756ad4bd145f181859a782c50d64168485761b", size = 73029, upload-time = "2026-06-27T16:28:10.589Z" }, -] - [[package]] name = "smmap" version = "5.0.3" @@ -5093,84 +4684,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] -[[package]] -name = "spacy" -version = "3.8.14" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "catalogue" }, - { name = "confection" }, - { name = "cymem" }, - { name = "jinja2" }, - { name = "murmurhash" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.12.*'" }, - { name = "packaging" }, - { name = "preshed" }, - { name = "pydantic" }, - { name = "requests" }, - { name = "setuptools" }, - { name = "spacy-legacy" }, - { name = "spacy-loggers" }, - { name = "srsly" }, - { name = "thinc" }, - { name = "tqdm" }, - { name = "typer" }, - { name = "wasabi" }, - { name = "weasel" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/d5/9860449c9fbed97634fb974bbf7a128ae269f5e69f3d792a9679d2354141/spacy-3.8.14-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4a06e5cc029910eaa4a3c5a0a997f07fed6a41aba46b05da9f58643bc06fe8b9", size = 6625650, upload-time = "2026-03-29T10:40:07.13Z" }, - { url = "https://files.pythonhosted.org/packages/dc/06/f896dfd0ea78093ae8221a958cd7df85a8818d58ba25c1a574aa153724e2/spacy-3.8.14-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d1bdc25a8351023b42619513bac82c861a25f45bfa4476d7e9673b6f0a177d3", size = 6449875, upload-time = "2026-03-29T10:40:09.329Z" }, - { url = "https://files.pythonhosted.org/packages/b0/ec/2b5b12396eb4cdacda26af497d6e1a798e1eceba060c6bb63d2c599c4988/spacy-3.8.14-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:93b369785413772a7121c29cafbf71320623d2e9ade5881acf81b5e3e8f1b4ae", size = 30761626, upload-time = "2026-03-29T10:40:11.736Z" }, - { url = "https://files.pythonhosted.org/packages/51/cb/8c103e81de0d9acafc30df06d0aba0d229a559154f4c7c889895d9f8f770/spacy-3.8.14-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6aa0b9308e9f4bb34ba8ff3b65df605f6d23891fe296f3e2f7b6879544637874", size = 30999811, upload-time = "2026-03-29T10:40:14.428Z" }, - { url = "https://files.pythonhosted.org/packages/b2/3a/99ff50b2b489ebd7ded9a5192dcf5e5003a3f6b785479ae1775f6e46a682/spacy-3.8.14-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cedabf5602ca1a4d5b14fc680bb7e5ac569139e1f3d780b261d373dc19f4c9d1", size = 31039385, upload-time = "2026-03-29T10:40:17.192Z" }, - { url = "https://files.pythonhosted.org/packages/04/0f/c8f7da2aa0e58874c9c5555104ee83016396a7cf5b347d97d77c6d775107/spacy-3.8.14-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc30623c581aa8f8e9763cb8625641d3fe96fa9fa95992ffb8544aaf6e3afcf3", size = 31882665, upload-time = "2026-03-29T10:40:20.232Z" }, - { url = "https://files.pythonhosted.org/packages/16/a0/a990b30ba1a09228ee49270d996be51e0b2245350631b0e4fdd655d019f1/spacy-3.8.14-cp310-cp310-win_amd64.whl", hash = "sha256:161671338396eea2455f9b9dee8e37e00f00bc2f352f6f8cf73a4cd2b708545b", size = 15359609, upload-time = "2026-03-29T10:40:23.513Z" }, - { url = "https://files.pythonhosted.org/packages/a9/8c/0ccf32d9a6b4fd8737bba33d599ddb98934399c1d523f825a4beb4bd1495/spacy-3.8.14-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c55cb123c3edfba8c252ce6ae27ffb3d7f60a53ba5e108c3534421586c5fdda", size = 6617470, upload-time = "2026-03-29T10:40:25.572Z" }, - { url = "https://files.pythonhosted.org/packages/e1/61/7f7d38e71daac7f91ffd362fb15645b6f9a68ad231e0ed6ff5c1dc6f6930/spacy-3.8.14-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab6c1ace316338dac334fc93c849994bbd717f9ebf59d2bc4158e978b2f542ee", size = 6441524, upload-time = "2026-03-29T10:40:27.648Z" }, - { url = "https://files.pythonhosted.org/packages/5c/ef/18385aa5aeb9bcb299e8074da162b24e5c8bea5aa4d1dfa3dbafb35e9d1f/spacy-3.8.14-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7bece0450cd8ab841cfa8527fcc0ce18c4454f28e3b9fca42a450803a067355b", size = 32050591, upload-time = "2026-03-29T10:40:29.704Z" }, - { url = "https://files.pythonhosted.org/packages/fa/67/5c4a65ed2cedc598ad000a2b9f45afc76bb8d17a592cc01082dffa8bbc50/spacy-3.8.14-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc5e5f2ed121d57d819d247bb59253dc320a58acbd237b85f86c2aa38cab6bd1", size = 32296467, upload-time = "2026-03-29T10:40:32.557Z" }, - { url = "https://files.pythonhosted.org/packages/03/a7/28c118879791b3a7ffa81796d22203daac428e6f75572f1b8da1539e1ac6/spacy-3.8.14-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c4e5bc5cdefe39ea139985776a2e8eae05e7ff2bf51ca1bd65247dc45feeb8e", size = 32288404, upload-time = "2026-03-29T10:40:35.583Z" }, - { url = "https://files.pythonhosted.org/packages/72/1c/32aefcea2468782fcdb994f2f96cac93dc74f6589ce01047db42d9a299a2/spacy-3.8.14-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c228f4c9ae618173334c17adb748d66b574b6594bc3575233e15cd5ad1cb26b", size = 33113476, upload-time = "2026-03-29T10:40:38.577Z" }, - { url = "https://files.pythonhosted.org/packages/86/32/fc00532eabeace451175dd9b152ddd636e8f6a42248b5d90141f98be2af5/spacy-3.8.14-cp311-cp311-win_amd64.whl", hash = "sha256:6f51d1ce8b1ba30123f6bef6e795c4bc5466608e6e8a015dc828bd21d399aa9c", size = 15359704, upload-time = "2026-03-29T10:40:41.25Z" }, - { url = "https://files.pythonhosted.org/packages/de/31/89ff6722ec91f328dc717932849c6f57249c8a9d429d8670a6c8f70e576b/spacy-3.8.14-cp311-cp311-win_arm64.whl", hash = "sha256:c0c6c9d8771cc3708e309b07310d330fc8443a6bca34f4ff20b0f22751d8faf9", size = 14717168, upload-time = "2026-03-29T10:40:43.916Z" }, - { url = "https://files.pythonhosted.org/packages/0c/78/e4f2ae19a791cae756cd0e801204953eaec4e9ab75a60ad39f671dbb8d5a/spacy-3.8.14-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:726f02c60a2c6b0029167370d22d51731172a053d29c7e2ea6190db6de3ab483", size = 6218335, upload-time = "2026-03-29T10:40:46.298Z" }, - { url = "https://files.pythonhosted.org/packages/06/df/178bbab47fa209c8baf2f1e609cbddc6b18a985200be1ceee22bd5b89beb/spacy-3.8.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e3ebe50b93f2d40e8ec3451255528bb622ccb12be39fd140bb87668ce8d1075b", size = 6033860, upload-time = "2026-03-29T10:40:47.861Z" }, - { url = "https://files.pythonhosted.org/packages/ff/e8/048d83b73b28686307bd9a60878a58de7b7b21b562ca4de8b5bd558031e9/spacy-3.8.14-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:daeb64b048f12c059997281aed53eb8776d26416dd313cf17ad6f63124b2b564", size = 32725099, upload-time = "2026-03-29T10:40:50.194Z" }, - { url = "https://files.pythonhosted.org/packages/8e/3f/1799af5f4ccc8eb7500e4a20ca301488134429dba08cda5be68ce6ab2992/spacy-3.8.14-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6d45715a24446f23b98ec3f09409a1d4111983d1d64613250ee38c3270e21853", size = 33205838, upload-time = "2026-03-29T10:40:53.029Z" }, - { url = "https://files.pythonhosted.org/packages/78/07/81ab9acd0ec64bfdd7339acfc4cf35f5fb74bbbb0b2be7e64d717c416bac/spacy-3.8.14-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1069a8be34940809f8462eb69f09a3f0ce59bf8b9cb82475f2a8e3580f50ece0", size = 32090380, upload-time = "2026-03-29T10:40:56.115Z" }, - { url = "https://files.pythonhosted.org/packages/74/a5/b081b5bd3cedb2634c23eb470b5e24c65c894c57646567f47627291c2b3f/spacy-3.8.14-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2dfa77aec7fdebac0455d8afd4ce1d92d6f868b03d507ed1976179a63db7b374", size = 32991946, upload-time = "2026-03-29T10:40:58.852Z" }, - { url = "https://files.pythonhosted.org/packages/5f/55/4371413a6dfc1fa837282a365498165f828c2f3fe018dfb35336acc869e0/spacy-3.8.14-cp312-cp312-win_amd64.whl", hash = "sha256:9def18c76a4472b326cb91a195623c9ca38a2b86999ad2df9e00b49ba8c63734", size = 14226946, upload-time = "2026-03-29T10:41:01.63Z" }, - { url = "https://files.pythonhosted.org/packages/f3/5e/12ac876017da6c1e6b72afcc3c8b309996227fd3aa15382cd3311aee21b8/spacy-3.8.14-cp312-cp312-win_arm64.whl", hash = "sha256:d6257133357e4801c9c5d011925af5439b0a015aacf3c16528aa0009982431c7", size = 13628765, upload-time = "2026-03-29T10:41:03.806Z" }, - { url = "https://files.pythonhosted.org/packages/1b/e5/822bbdfa459fee863ef2e9879a34b0ae5db7cd1e3eb76d32c766f19222e9/spacy-3.8.14-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b4f60fa8b9641a5e93e7a96db0cdd106d05d61756bf1d0ddcd1705ad347909a", size = 6202114, upload-time = "2026-03-29T10:41:06.119Z" }, - { url = "https://files.pythonhosted.org/packages/7e/de/0e512154113e1f341567f2b9341835775e4180c180221e60faedaebb2f65/spacy-3.8.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0860c57220c633ccb20468bcd64bfb0d28908990c371a8857951d093a148dc8e", size = 6015458, upload-time = "2026-03-29T10:41:07.79Z" }, - { url = "https://files.pythonhosted.org/packages/0c/4f/29c7e56afc7db07348a9e0efe0243b5eef465d5dc3d56433f164378c3fa6/spacy-3.8.14-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c24620b7dba879c69cebc51ef3b1107d4d4e44a1e0d4baa439372887d00c3fd9", size = 32510659, upload-time = "2026-03-29T10:41:09.88Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ce/cae678f664d5467016819253f5d6e52f8e68a12d8e799b651d73ec2a9a4b/spacy-3.8.14-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9699c1248d115d5825987c287a6f6acd66386ef3ebee7994ee67ba093e932c59", size = 32841057, upload-time = "2026-03-29T10:41:12.585Z" }, - { url = "https://files.pythonhosted.org/packages/04/d4/419868afd449bdd367df005932537eea66c71e97c899ba278f3124933f3c/spacy-3.8.14-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:042d799e342fdb6bb5b02a4213a95acc9116c40ed3c849bb0a8296fbe648ec22", size = 31763252, upload-time = "2026-03-29T10:41:15.569Z" }, - { url = "https://files.pythonhosted.org/packages/ec/53/df5c1fee45f200b749ba72eeb536fbb2c545fc56230324954263b2f3be00/spacy-3.8.14-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b2264294097336e86832e8663f1ab3a7215621184863c96c082ab17ee11937", size = 32717872, upload-time = "2026-03-29T10:41:18.193Z" }, - { url = "https://files.pythonhosted.org/packages/12/c2/f1882ec2f5cc9c4e73cf2132997a03c397d7ceeb5ee7f7bb878b51a16365/spacy-3.8.14-cp313-cp313-win_amd64.whl", hash = "sha256:4b6d4f20e291a7c70e37de2f246622b44a0ce82efaa710c9801c6bd599e75177", size = 14220335, upload-time = "2026-03-29T10:41:20.89Z" }, -] - -[[package]] -name = "spacy-legacy" -version = "3.0.12" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d9/79/91f9d7cc8db5642acad830dcc4b49ba65a7790152832c4eceb305e46d681/spacy-legacy-3.0.12.tar.gz", hash = "sha256:b37d6e0c9b6e1d7ca1cf5bc7152ab64a4c4671f59c85adaf7a3fcb870357a774", size = 23806, upload-time = "2023-01-23T09:04:15.104Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/55/12e842c70ff8828e34e543a2c7176dac4da006ca6901c9e8b43efab8bc6b/spacy_legacy-3.0.12-py2.py3-none-any.whl", hash = "sha256:476e3bd0d05f8c339ed60f40986c07387c0a71479245d6d0f4298dbd52cda55f", size = 29971, upload-time = "2023-01-23T09:04:13.45Z" }, -] - -[[package]] -name = "spacy-loggers" -version = "1.0.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/67/3d/926db774c9c98acf66cb4ed7faf6c377746f3e00b84b700d0868b95d0712/spacy-loggers-1.0.5.tar.gz", hash = "sha256:d60b0bdbf915a60e516cc2e653baeff946f0cfc461b452d11a4d5458c6fe5f24", size = 20811, upload-time = "2023-09-11T12:26:52.323Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/78/d1a1a026ef3af911159398c939b1509d5c36fe524c7b644f34a5146c4e16/spacy_loggers-1.0.5-py3-none-any.whl", hash = "sha256:196284c9c446cc0cdb944005384270d775fdeaf4f494d8e269466cfa497ef645", size = 22343, upload-time = "2023-09-11T12:26:50.586Z" }, -] - [[package]] name = "sqlalchemy" version = "2.0.51" @@ -5212,49 +4725,6 @@ wheels = [ { 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 = "srsly" -version = "2.5.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "catalogue" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2b/db/f794f219a6c788b881252d2536a8c4a97d2bdaadc690391e1cb53d123d71/srsly-2.5.3.tar.gz", hash = "sha256:08f98dbecbff3a31466c4ae7c833131f59d3655a0ad8ac749e6e2c149e2b0680", size = 490881, upload-time = "2026-03-23T11:56:59.865Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/67/e6d4decfb0cdc95b54c60854a1a6d1702983c39206c2b9f70f4ab18b17c8/srsly-2.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c812302a9acfe171e82f680b7ad642014cd017380b2c678441b3da4fb513c498", size = 657202, upload-time = "2026-03-23T11:55:34.938Z" }, - { url = "https://files.pythonhosted.org/packages/cc/5d/cb8b093d0836e59c152de6dfdb5db80c6408b00def0123f26d24bffde480/srsly-2.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91688edb1f49110870d2c215db2cf445f1763c14173698ead0818908c51fb2a1", size = 657951, upload-time = "2026-03-23T11:55:36.571Z" }, - { url = "https://files.pythonhosted.org/packages/71/a1/5d2fb4c6a8e0e39dd1fb23bdd8feb1f2525ce90b28946f9f58ac5d3a039c/srsly-2.5.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1fd6c35c65c4d2435ae5bfb57b59682cf9b61606318a2a761856be9d7cc2d9e3", size = 1119766, upload-time = "2026-03-23T11:55:38.351Z" }, - { url = "https://files.pythonhosted.org/packages/ff/83/0862ffac8c06ed595dd1e28f261c37956585b9cf6b9bd049f8430a4c2daf/srsly-2.5.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b9df76d5a6bbf50967589bd42df3c522dd88babea2be745a507f56b41ab40626", size = 1120674, upload-time = "2026-03-23T11:55:39.644Z" }, - { url = "https://files.pythonhosted.org/packages/f1/06/42f72bab50876a708a10e6fc026ae8c7f185507d9f27544fa4ee8567c5fd/srsly-2.5.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a595958d0b1ff6d59c2570a3f0d1c8e36ab9f89d6e1b9c96fa7eb5e1a8698510", size = 1078505, upload-time = "2026-03-23T11:55:41.299Z" }, - { url = "https://files.pythonhosted.org/packages/e5/f4/dfb86bc5c3abee267fb2f34895ea80d0159a084987a93d56ed1bf5ebefe4/srsly-2.5.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bc0ad5be2aeb9ff29c8512848d39d7c63fdd4bfbb5516bc523f5de5a77e55e6d", size = 1090635, upload-time = "2026-03-23T11:55:42.7Z" }, - { url = "https://files.pythonhosted.org/packages/2a/a6/561b46eff4477191dd649e09dd9b88afc44aad7ce204c45f4e45ad04861d/srsly-2.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:d2b8cfd8aee4d06ab335d359e4095d206102300a5e105a4b4bc69acca42427a6", size = 651653, upload-time = "2026-03-23T11:55:44.429Z" }, - { url = "https://files.pythonhosted.org/packages/dc/05/b122a1afaf8e8644d10f0203ad5174993910e6f727843089f0d48b444340/srsly-2.5.3-cp310-cp310-win_arm64.whl", hash = "sha256:c378afcb7dd7c42f426a66112496c949fc39e5883de6817d86e60afa51720ccc", size = 639118, upload-time = "2026-03-23T11:55:45.796Z" }, - { url = "https://files.pythonhosted.org/packages/9a/36/5d7bb412d52e9cca787f9bfe838b596367189b254e50bf90f234a97184bf/srsly-2.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:785a09216ac31570fb301ddb9f61ee73d1f18f8b9561f712dce0b8ac8628bc88", size = 656760, upload-time = "2026-03-23T11:55:47.155Z" }, - { url = "https://files.pythonhosted.org/packages/d6/dc/124f008cd2be3e887e972cbdeb17c5aee0f42093eca02c7cfd63bb5daf19/srsly-2.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0017c7d2a0cd9a4f1bdc00d946b45edcf90bb0e271e8f084c1ce542bf6708c32", size = 657503, upload-time = "2026-03-23T11:55:48.681Z" }, - { url = "https://files.pythonhosted.org/packages/35/8a/2c97244ebab125d55f1bfb7bb94e9572b3e819410dffd6a040eca1112350/srsly-2.5.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:66ebae2c70305987341519ec1a720072a3cb3e4b1d52ac0e9e841f4d02658d3d", size = 1139161, upload-time = "2026-03-23T11:55:50.179Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ea/ecd396188f7591d80b89665f7af9e3ae02e42683daef57033ad7993ad3f9/srsly-2.5.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4ca4a068f6e14d84113a02fcb875c6b50a6285a12938c0e7a157eb3a63c50a86", size = 1142438, upload-time = "2026-03-23T11:55:52.607Z" }, - { url = "https://files.pythonhosted.org/packages/9c/65/143e2e143c53d498ad0956f69d0e09189aa7a6e0ee6017758c285ba1ab2d/srsly-2.5.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e283fa2a8f7350fb9fb70ecdee28d59d39c92f4c7f1cc90a44d6b86db3b3a8b3", size = 1101783, upload-time = "2026-03-23T11:55:53.906Z" }, - { url = "https://files.pythonhosted.org/packages/6b/86/1392a5593de0cd3d08c2d6c071b877c84358a37f63172c4e9cb71706842d/srsly-2.5.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9ffc97e22730ea97b00f7c303ccc60b1305e786afadb2a4a46578dafa4d29da0", size = 1115876, upload-time = "2026-03-23T11:55:55.624Z" }, - { url = "https://files.pythonhosted.org/packages/d4/a5/6193aa4c08e488821538fcbce2282449e228fd2183ed67d118bb5ccd8b54/srsly-2.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:f09b551f6c3e334652831ac68c770ee4284741ce0a3895bf1ccf2a1178d66cdd", size = 651733, upload-time = "2026-03-23T11:55:56.964Z" }, - { url = "https://files.pythonhosted.org/packages/66/a8/a73181743b6d237026615ca75c3fb3e4780736f1390550a7350d0c7f1149/srsly-2.5.3-cp311-cp311-win_arm64.whl", hash = "sha256:21cf09e417d3e4f3fbf7dd337fd6d948c97abd01896b9b4cb80e81cd9778a73a", size = 639124, upload-time = "2026-03-23T11:55:58.532Z" }, - { url = "https://files.pythonhosted.org/packages/02/cc/e9f7fcec4cc92ad8bad6316c4241638b8cf7380382d4489d94ec6c436452/srsly-2.5.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:71e51c046ccbeefb86524c6b1e17574f579c6ac4dc8ea4a09437d3e8f88342d3", size = 658379, upload-time = "2026-03-23T11:55:59.85Z" }, - { url = "https://files.pythonhosted.org/packages/21/e4/fea4512e9785f58509b2cf67d993323848e583161b5fcfdc7dd9d7c1f3df/srsly-2.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f73c0db911552e94fe2016e1759d261d2f47926f68826664cada3723c87006a", size = 658513, upload-time = "2026-03-23T11:56:01.239Z" }, - { url = "https://files.pythonhosted.org/packages/20/b1/53591681b6ff2699a4f97b2d5552ba196eaa6a979b0873605f4c04b5f7ee/srsly-2.5.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c1ac27ae5f4bb9163c7d2c45fc8ec173aac3d92e32086d9472b326c5c6e570e", size = 1172265, upload-time = "2026-03-23T11:56:02.589Z" }, - { url = "https://files.pythonhosted.org/packages/4e/c9/741e29f534919a944a16da4184924b1d3404c4bf60716ab2b91be771d1e3/srsly-2.5.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:99026bcd9cbd3211cc36517400b04ca0fc5d3e412b14daf84ee6e65f67d9a2d8", size = 1180873, upload-time = "2026-03-23T11:56:03.944Z" }, - { url = "https://files.pythonhosted.org/packages/89/57/5554f786eccf78b2750d6ac63be126e1b67badec2cb409dd611cf6f8c52b/srsly-2.5.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:07d682679e639eb46ff7e6da4a92714f4d5ffe351d088ee66f221e9b1f8865bb", size = 1120437, upload-time = "2026-03-23T11:56:05.283Z" }, - { url = "https://files.pythonhosted.org/packages/eb/95/9b4f73b1be3692f86d72ccc131c8e50f26f824d5c8830a59390bcc5b60ef/srsly-2.5.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8e0542d85d6b55cf2934050d6ffcb1cd76c768dcf9572e7467002cf087bb366d", size = 1137376, upload-time = "2026-03-23T11:56:06.613Z" }, - { url = "https://files.pythonhosted.org/packages/5a/de/89ca640ca1953c4612279ce515d0af35658df3c06cdb324329bc91b4a7e1/srsly-2.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:598f1e494c18cacb978299d77125415a586417081959f8ec3f068b32d97f8933", size = 652459, upload-time = "2026-03-23T11:56:07.994Z" }, - { url = "https://files.pythonhosted.org/packages/6d/4f/7ab6d49e36d9cc72ee15746cabd116eb6f338be8a06c1882968ee9d6c7d7/srsly-2.5.3-cp312-cp312-win_arm64.whl", hash = "sha256:4b1b721cd3ad1a9b2343519aadc786a4d09d5c0666962d49852eb12d6ec3fe26", size = 638411, upload-time = "2026-03-23T11:56:09.31Z" }, - { url = "https://files.pythonhosted.org/packages/9d/5c/12901e3794f4158abc6da750725aad6c2afddb1e4227b300fe7c71f66957/srsly-2.5.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e67b6bbacbfadea5e100266d2797f2d4cec9883ea4dc84a5537673850036a8d8", size = 656750, upload-time = "2026-03-23T11:56:10.708Z" }, - { url = "https://files.pythonhosted.org/packages/04/61/181c26370995f96f56f1b64b801e3ca1e0d703fc36506ae28606d62369fb/srsly-2.5.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:348c231b4477d8fe86603131d0f166d2feac9c372704dfc4398be71cc5b6fb07", size = 656746, upload-time = "2026-03-23T11:56:12.28Z" }, - { url = "https://files.pythonhosted.org/packages/77/c6/35876c78889f8ffe11ed3521644e666c3aef20ea31527b70f47456cf35c2/srsly-2.5.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b0938c2978c91ae1ef9c1f2ba35abb86330e198fb23469e356eba311e02233ee", size = 1155762, upload-time = "2026-03-23T11:56:14.075Z" }, - { url = "https://files.pythonhosted.org/packages/3e/da/40b71ca9906c8eb8f8feb6ac11d33dad458c85a56e1de764b96d402168a0/srsly-2.5.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5f6a837954429ecbe6dcdd27390d2fb4c7d01a3f99c9ffcf9ce66b2a6dd1b738", size = 1161092, upload-time = "2026-03-23T11:56:15.778Z" }, - { url = "https://files.pythonhosted.org/packages/dc/14/c0dd30cc8b93ce8137ff4766f743c882440ce49195fffc5d50eaeef311a6/srsly-2.5.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3576c125c486ce2958c2047e8858fe3cfc9ea877adfa05203b0986f9badee355", size = 1109984, upload-time = "2026-03-23T11:56:17.056Z" }, - { url = "https://files.pythonhosted.org/packages/08/f3/34354f183d8faafc631585571224b54d1b4b67e796972c36519c074ca355/srsly-2.5.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5fb59c42922e095d1ea36085c55bc16e2adb06a7bfe57b24d381e0194ae699f2", size = 1128409, upload-time = "2026-03-23T11:56:18.761Z" }, - { url = "https://files.pythonhosted.org/packages/a4/d9/5531f8a19492060b4e76e4ab06aca6f096fb5128fe18cc813d1772daf653/srsly-2.5.3-cp313-cp313-win_amd64.whl", hash = "sha256:111805927f05f5db440aeeacb85ce43da0b19ce7b2a09567a9ef8d30f3cc4d83", size = 650820, upload-time = "2026-03-23T11:56:20.096Z" }, - { url = "https://files.pythonhosted.org/packages/8e/8a/62fb7a971eca29e12f03fb9ddacb058548c14d33e5b5675ff0f85839cc7b/srsly-2.5.3-cp313-cp313-win_arm64.whl", hash = "sha256:0f106b0a700ab56e4a7c431b0f1444009ab6cb332edc7bbf6811c2a43f4722cb", size = 637278, upload-time = "2026-03-23T11:56:21.439Z" }, -] - [[package]] name = "sse-starlette" version = "3.4.5" @@ -5345,61 +4815,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" }, ] -[[package]] -name = "thinc" -version = "8.3.13" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "blis" }, - { name = "catalogue" }, - { name = "confection" }, - { name = "cymem" }, - { name = "murmurhash" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' or python_full_version >= '3.13'" }, - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.12.*'" }, - { name = "packaging" }, - { name = "preshed" }, - { name = "pydantic" }, - { name = "setuptools" }, - { name = "srsly" }, - { name = "wasabi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/13/46/76df95f2c327f9a9cef30c1523bf285627897097163584dcf5f77b2ebce2/thinc-8.3.13.tar.gz", hash = "sha256:68e658549fc1eb3ff92aed5147fcbb9c15d6e9cc0e623b4d0998d16522ffb4f9", size = 194640, upload-time = "2026-03-23T07:22:36.41Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ad/e3/df570d55f38250d153e209d998f60e334026ea60cf9a887cffb85d7ee9bf/thinc-8.3.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:84fb50fe572a1860165f2e7a640c7cb70d43d6962366e69f643fa9a27e4a2127", size = 846996, upload-time = "2026-03-23T07:21:32.701Z" }, - { url = "https://files.pythonhosted.org/packages/ec/72/e97c9cb863ef0a645ba069c24e0981bfaedf8241ba199512ebcd64ba090a/thinc-8.3.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3dac18a0fb0a42f711c2ce9c02cbb090385aecae92089aa17b9dfd808a542013", size = 815368, upload-time = "2026-03-23T07:21:34.392Z" }, - { url = "https://files.pythonhosted.org/packages/b6/7a/9283f52b1210dc052b795e22ec739d13929b914d1289e49336bede34c4eb/thinc-8.3.13-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e08b1577a56e7315770af280aabd8fa5f2a1fb6afd1c50a4183c06e907faf558", size = 3885033, upload-time = "2026-03-23T07:21:35.772Z" }, - { url = "https://files.pythonhosted.org/packages/93/9a/aa8f2e19819c02781b282c3a9cfb57c76ff1fbe0b6deaa1ffd04dc920894/thinc-8.3.13-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:303477eb51b9b39c94a7fc7967ee8a039eca1ca37d95dcce1234c83b95b4ee9f", size = 3912947, upload-time = "2026-03-23T07:21:37.219Z" }, - { url = "https://files.pythonhosted.org/packages/51/fa/ea7c67667b8a875178bea5a42dc9c8b0622c34e7eba3d8e42874f2c4b4c1/thinc-8.3.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d7a9654f9ca362a4be7f5e590fdfee26e2e2084da9fd3306032ec037e99f2f8e", size = 4887518, upload-time = "2026-03-23T07:21:38.76Z" }, - { url = "https://files.pythonhosted.org/packages/36/44/99c391e951e3b706b9a7552ced720e9ec3bddd6707a99d53e4354ebefa45/thinc-8.3.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e1f8d13bf92ee10595c40692fd4cf8e7bbe73bd9f260107e975fd5dbee1af42b", size = 5044691, upload-time = "2026-03-23T07:21:40.257Z" }, - { url = "https://files.pythonhosted.org/packages/ce/cf/9d95fb5f12d76ad1c7570a9a38da2f2f60dba721c87630bfabaabef91bc3/thinc-8.3.13-cp310-cp310-win_amd64.whl", hash = "sha256:e7f046d8914055cad51e83ff0da1a892acb73cd58556d7c1a5d4015a3766a899", size = 1795372, upload-time = "2026-03-23T07:21:41.709Z" }, - { url = "https://files.pythonhosted.org/packages/b4/72/ca06842a007e8c794e8c59462f242cdfd6167d7cc9d0155ad004b194b015/thinc-8.3.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4565102638038a01a2193c7f5d41ccbd6233fbdcb1f1b184322a06add4f51f18", size = 844359, upload-time = "2026-03-23T07:21:43.017Z" }, - { url = "https://files.pythonhosted.org/packages/48/44/e6aef092f478d263f72eb3933b55a6f37ba97c6a0ea0a61d13fbf9bf0c19/thinc-8.3.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:859fbd9d9b16af5278da23589b4afbe2ab6b0dd615df4d3229b7c4e67cd3107e", size = 812089, upload-time = "2026-03-23T07:21:44.618Z" }, - { url = "https://files.pythonhosted.org/packages/ff/8a/9ce0424d456cd3580cc3a855b23a7ff86b81d5299fceb496a2f56f06c1c0/thinc-8.3.13-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a518d5c761a0f2341e530e867de133dc3ed814558365b2a68ec53b89c482a43f", size = 4101388, upload-time = "2026-03-23T07:21:46.135Z" }, - { url = "https://files.pythonhosted.org/packages/ad/51/ec91c0434bd9a1096ab874bbd6dc110c5089d7fc513137e6af59bd051eec/thinc-8.3.13-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:81337dfbee37f58f36c0c70f9a819dce1b32cdc13d959181e10de079621f6ac6", size = 4131972, upload-time = "2026-03-23T07:21:48.403Z" }, - { url = "https://files.pythonhosted.org/packages/ff/67/e30dea753c90cff5cb9e5feb34948fdb89a6774b84d849585b49e16a730e/thinc-8.3.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fbc0ee16edd260c6a4a9e365ff36d0a682c9e7ca6d7b985682659ef2e3e73826", size = 5101283, upload-time = "2026-03-23T07:21:49.991Z" }, - { url = "https://files.pythonhosted.org/packages/00/e9/b7544eddababa16e548b26a96fff29eeb307ce938df5fa4af9371fe8ed5d/thinc-8.3.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0355c37e40d1a9fc2a1b8e9c2e294d8586f6baa97bcac6b9002f2dddb4b82ae9", size = 5264488, upload-time = "2026-03-23T07:21:51.747Z" }, - { url = "https://files.pythonhosted.org/packages/4c/a9/49391a40d703efc0f7a451310373261835f71fd3e6e2e8cfc08ee02f78ad/thinc-8.3.13-cp311-cp311-win_amd64.whl", hash = "sha256:0a0fa13dcfe4b319c3a396432c1dbff30d3de37dbbdee559e76600ee2b9486df", size = 1795058, upload-time = "2026-03-23T07:21:53.424Z" }, - { url = "https://files.pythonhosted.org/packages/c1/31/fd5348d44beda12a3ee415cbba9ed4fd0b17ce65db1d473c38a29a8d6153/thinc-8.3.13-cp311-cp311-win_arm64.whl", hash = "sha256:cd8a2b714c061969eee65802965167a6ada1fe708d82fe176d98dcb95ebe182a", size = 1721215, upload-time = "2026-03-23T07:21:55.027Z" }, - { url = "https://files.pythonhosted.org/packages/3e/af/f7c1ebfe92eb5d27d7f2f3da67a11e2eb57bc30ab1553279af6dc65b65a8/thinc-8.3.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:77a41f66285321d20aaedaea1e87d7cd48dca6d2427bed1867ec7cba7109fc8d", size = 821097, upload-time = "2026-03-23T07:21:56.698Z" }, - { url = "https://files.pythonhosted.org/packages/45/8f/69d7338575d98df85d0b54c0f5fc277dba72587fe9ab846ecdd12a998bcb/thinc-8.3.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3710d318b4e5460cf366a6f7b5ddbefb5d39dbd4cfa408222750fdc6c27c4411", size = 791932, upload-time = "2026-03-23T07:21:58.38Z" }, - { url = "https://files.pythonhosted.org/packages/4b/a5/21d010c81e81e1589e5ccb4950e521804d13726e541e87f644c51815673b/thinc-8.3.13-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5a08c87143a6d20177652dca1ec0dc815d88216d8fc62594a57e8bc45bf5ed49", size = 3854219, upload-time = "2026-03-23T07:21:59.819Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ff/6914bf370bd1d604d89e6dfb46b97d10cd9b00d42ff8c036283e92314a8c/thinc-8.3.13-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4b5ec9ff313819e7d8667794a3559463fa89ff45aaa73e3fd8d6273b1e0d7a7f", size = 3903307, upload-time = "2026-03-23T07:22:01.652Z" }, - { url = "https://files.pythonhosted.org/packages/f3/3d/5572b47fa155fb3388c071515b74024fa17a6efd1df9406da378f0aa84ef/thinc-8.3.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5c9a48f2bc1e04f138240ed5f9b815a9141a5de26accd0f08fa0137fcefed258", size = 4836882, upload-time = "2026-03-23T07:22:03.565Z" }, - { url = "https://files.pythonhosted.org/packages/f0/f0/a8d77c7bac089697c6df302cc3c936a1ab36a4720deae889e6f1dbcbd0eb/thinc-8.3.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:79a29a44d76bd02f5ac0624268c6e42b3576ae472c791a8ae9c2d813ae789b59", size = 5033398, upload-time = "2026-03-23T07:22:05.045Z" }, - { url = "https://files.pythonhosted.org/packages/21/82/5651bb1f904d04220fc7670035ada921bf0638e2cff6444d67c12887a968/thinc-8.3.13-cp312-cp312-win_amd64.whl", hash = "sha256:ed1dc709ac4f2f03b710457889e4e02f05de51bc8456980c241d0b28798bc7cb", size = 1721248, upload-time = "2026-03-23T07:22:06.749Z" }, - { url = "https://files.pythonhosted.org/packages/94/8d/683703de021ffbe46833d722b70f49ffbbca8e5bd6876256977555d92d7d/thinc-8.3.13-cp312-cp312-win_arm64.whl", hash = "sha256:c6a049703a6011c8fe26ee41af7e70272145594140d82f79bb23de619c6a6525", size = 1645777, upload-time = "2026-03-23T07:22:08.104Z" }, - { url = "https://files.pythonhosted.org/packages/af/b9/7b46942176df459d1804a9e77b0976f7c56f3abf3ec7485d0e5f836a0382/thinc-8.3.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2811dfd8d46d8b5d3b39051b23e64006b2994a5143b1978b436938018792af8", size = 817337, upload-time = "2026-03-23T07:22:09.538Z" }, - { url = "https://files.pythonhosted.org/packages/a7/79/53085a72cd8f4fc4e6e313d05ea5aa98e870684f4a0fb318a9875fc0a964/thinc-8.3.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5593e6300cb1ebe0c0e546e9c9fb49e7c2627a0aa688795cd4f995a8b820d2ec", size = 788120, upload-time = "2026-03-23T07:22:11.215Z" }, - { url = "https://files.pythonhosted.org/packages/9e/3e/d61b462b16da95ac6885f95bb395e672040ee594833e571a6edcffd234f5/thinc-8.3.13-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f697174d3fb474966ce50b430bbafa101a6d2f7ffb559dac4b5c59389ef72d22", size = 3844666, upload-time = "2026-03-23T07:22:12.67Z" }, - { url = "https://files.pythonhosted.org/packages/78/4c/898cc654bb123734c71ec5a425c02ca34439517d01ce1c95a6563295580e/thinc-8.3.13-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9c7c5c104737b414c8c4ec578e67d78b6c859afe25cbc0684402e721415bd7f", size = 3890658, upload-time = "2026-03-23T07:22:14.668Z" }, - { url = "https://files.pythonhosted.org/packages/cd/56/1abdbf0a4ad628e8a05d6516fe0745969649d805367a3dccad8ee872981b/thinc-8.3.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a99d0e242d1ccd23f9ae6bea7cd502f8626efa65c156b91d84581d0356696c3", size = 4819933, upload-time = "2026-03-23T07:22:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/f1/22/b84dbdc6be5055bbdb2a7352e2c393f67e8593c137f1b83c82bf1e062b6e/thinc-8.3.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e676edd21a747afbe3e6b9f3fca8b962e36d146ded03b070cb0c28e2dfbe9499", size = 5018099, upload-time = "2026-03-23T07:22:18.356Z" }, - { url = "https://files.pythonhosted.org/packages/0f/a8/763cd7ba949334c9d2cddc92dadb68b344cb9546dc01b8d4a733dcaa16c1/thinc-8.3.13-cp313-cp313-win_amd64.whl", hash = "sha256:8ad40307f20e83f77af28ff5c6be0b86af7a8b251d1231c545508d2763157d8f", size = 1720309, upload-time = "2026-03-23T07:22:19.81Z" }, - { url = "https://files.pythonhosted.org/packages/f5/15/a11f7bb3cbc97dfecf32a90552f5a8f8a5c99316a99c6c17bdabf5baf256/thinc-8.3.13-cp313-cp313-win_arm64.whl", hash = "sha256:723949cab11d1925c15447928513a718276316cec6e0de28337cca0a62be0521", size = 1644606, upload-time = "2026-03-23T07:22:21.339Z" }, -] - [[package]] name = "tiktoken" version = "0.13.0" @@ -5447,21 +4862,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/4d/bc07d1f1635d4897a202acc0ae11c2886eaa7325c359ba4741b47bf8e225/tiktoken-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6c43a675ca14f6f2749ba7f12075d37456015a24b859f2517b9beb4ef30807ec", size = 873820, upload-time = "2026-05-15T04:50:59.528Z" }, ] -[[package]] -name = "tldextract" -version = "5.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "filelock" }, - { name = "idna" }, - { name = "requests" }, - { name = "requests-file" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/65/7b/644fbbb49564a6cb124a8582013315a41148dba2f72209bba14a84242bf0/tldextract-5.3.1.tar.gz", hash = "sha256:a72756ca170b2510315076383ea2993478f7da6f897eef1f4a5400735d5057fb", size = 126105, upload-time = "2025-12-28T23:58:05.532Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/42/0e49d6d0aac449ca71952ec5bae764af009754fcb2e76a5cc097543747b3/tldextract-5.3.1-py3-none-any.whl", hash = "sha256:6bfe36d518de569c572062b788e16a659ccaceffc486d243af0484e8ecf432d9", size = 105886, upload-time = "2025-12-28T23:58:04.071Z" }, -] - [[package]] name = "tokenizers" version = "0.22.2" @@ -5834,18 +5234,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/02/3623e6169bed617ed1e2d372f7c69f92ec28d54c4dfc997055c8578ec148/virtualenv-21.5.1-py3-none-any.whl", hash = "sha256:55aa670b67bbfb991b03fda39bd3276d92c419d702376e98c5df1c9989a26783", size = 4558820, upload-time = "2026-06-16T16:23:56.963Z" }, ] -[[package]] -name = "wasabi" -version = "1.1.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ac/f9/054e6e2f1071e963b5e746b48d1e3727470b2a490834d18ad92364929db3/wasabi-1.1.3.tar.gz", hash = "sha256:4bb3008f003809db0c3e28b4daf20906ea871a2bb43f9914197d540f4f2e0878", size = 30391, upload-time = "2024-05-31T16:56:18.99Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/06/7c/34330a89da55610daa5f245ddce5aab81244321101614751e7537f125133/wasabi-1.1.3-py3-none-any.whl", hash = "sha256:f76e16e8f7e79f8c4c8be49b4024ac725713ab10cd7f19350ad18a8e3f71728c", size = 27880, upload-time = "2024-05-31T16:56:16.699Z" }, -] - [[package]] name = "watchdog" version = "6.0.0" @@ -5968,26 +5356,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/42/3e5985a0a7e57de470b320c6d6a1a67c844f6737a587f3d44dd13d1819e7/wcwidth-0.8.2-py3-none-any.whl", hash = "sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85", size = 323166, upload-time = "2026-06-29T18:11:09.888Z" }, ] -[[package]] -name = "weasel" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cloudpathlib" }, - { name = "confection" }, - { name = "httpx" }, - { name = "packaging" }, - { name = "pydantic" }, - { name = "smart-open" }, - { name = "srsly" }, - { name = "typer" }, - { name = "wasabi" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ce/e5/e272bb9a045105a1fdf4b798d8086f5932a178f4d738f17a74f5c9e0ae9a/weasel-1.0.0.tar.gz", hash = "sha256:7b129b44c90cc543b760532974ca1e4eb30dad2aa2026f57bdce66354ae610fc", size = 38682, upload-time = "2026-03-20T08:10:25.266Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/07/57ebf7a6798b016c064bd0ca81b4c6a99daa4dc377b898bc7b41eb6b5af0/weasel-1.0.0-py3-none-any.whl", hash = "sha256:89518acee027f49d743126c3502d35e6dd14f5768be5c37c9af47c171b6005cc", size = 50713, upload-time = "2026-03-20T08:10:23.637Z" }, -] - [[package]] name = "websockets" version = "15.0.1"