diff --git a/deploy/Dockerfile b/deploy/Dockerfile index d2799692f..e9c9ff5d2 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -76,7 +76,7 @@ COPY deploy/entrypoint.py deploy/start_web.py ./deploy/ # import-fail at runtime. Syncing with the dev group installs those deps from the # frozen lock (still pinned, no re-resolve). TODO(deploy): move the source # packages to a dedicated non-dev group to keep test-only tooling out of the image. -RUN uv sync --frozen --extra s3 +RUN uv sync --frozen --extra pii --extra s3 # Install workspace packages (without CLI for base). The root install does not # need workspace source overrides because dependencies were synced above; avoid @@ -90,11 +90,13 @@ RUN uv pip install --no-sources --no-deps -e . \ && uv pip install --no-deps -e ./frontends/aiq_api \ && uv pip install "psycopg[binary]>=3.0.0" -RUN /app/.venv/bin/python -c "import aiq_api; import knowledge_layer; print('✓ Base packages installed')" +RUN /app/.venv/bin/python -c \ + "import asyncio; import aiq_api; import knowledge_layer; import presidio_analyzer; import presidio_anonymizer; import spacy; from nemoguardrails.library.sensitive_data_detection.actions import mask_sensitive_data; from nemoguardrails.rails.llm.config import RailsConfig; config = RailsConfig.from_content(yaml_content='rails:\n config:\n sensitive_data_detection:\n output:\n entities:\n - EMAIL_ADDRESS\n'); assert spacy.util.is_package('en_core_web_lg'); assert asyncio.run(mask_sensitive_data(source='output', text='Contact customer@example.com', config=config)) == 'Contact '; print('✓ Base packages and PII runtime installed')" +# Keep immutable application assets root-owned; only runtime data is writable by the service user. RUN chmod +x /app/deploy/start_web.py \ && mkdir -p /app/data \ - && chown -R 1000:1000 /app + && chown 1000:1000 /app/data # ============================================================================= # Stage 2: Development (includes CLI) diff --git a/docs/source/customization/guardrails.md b/docs/source/customization/guardrails.md index 67cae83e6..17a749842 100644 --- a/docs/source/customization/guardrails.md +++ b/docs/source/customization/guardrails.md @@ -34,8 +34,20 @@ At each configured boundary, guardrails can make one of three decisions: | Modify | Replace the selected input or output field with the modified content returned by the rail. | | Block | Return the configured refusal response instead of continuing with the blocked content. | -Input-rail evaluation exceptions are caught, logged, and converted to the middleware refusal response. Output-rail -evaluation exceptions are not converted to a refusal; they propagate and fail the invocation. +Input- and output-rail evaluation exceptions are caught, logged, and converted to the middleware refusal response. +Output failures preserve the intercepted response schema and do not return the original unfiltered output. + +Buffered output streams are evaluated as one logical assistant response before any chunk is emitted. This includes +streams that mix raw strings and structured response chunks. Modified output is redistributed across the buffered +chunks, terminal workflow outcomes are synchronized with every rewritten structured chunk, and blocked or failed +streams emit only a safe refusal. + +## PII Runtime Dependencies + +The built-in `sensitive_data_detection` action requires Presidio, spaCy, and a compatible spaCy language model. These +large dependencies are available through the `pii` project extra rather than the base Python package. Install AI-Q with +`--extra pii` when running PII rails. The release Docker image includes this extra and verifies during the build that the +Presidio analyzer and anonymizer import, `en_core_web_lg` is installed, and email analysis succeeds. ## Configuration Shape diff --git a/mcp/tests/test_config_and_packaging.py b/mcp/tests/test_config_and_packaging.py index 92035fc04..d47ae83dc 100644 --- a/mcp/tests/test_config_and_packaging.py +++ b/mcp/tests/test_config_and_packaging.py @@ -150,6 +150,16 @@ def test_root_workspace_excludes_the_independent_mcp_project() -> None: if "registry" in source: assert source == {"registry": "https://pypi.org/simple"} continue + if "url" in source: + assert package["name"] == "en-core-web-lg" + assert package["version"] == "3.8.0" + assert source == { + "url": ( + "https://github.com/explosion/spacy-models/releases/download/" + "en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl" + ) + } + continue editable = source.get("editable") assert isinstance(editable, str) assert not Path(editable).is_absolute() diff --git a/mcp/uv.lock b/mcp/uv.lock index 966eac399..d38df1001 100644 --- a/mcp/uv.lock +++ b/mcp/uv.lock @@ -198,6 +198,7 @@ requires-dist = [ { name = "asyncpg", specifier = ">=0.29.0" }, { name = "boto3", marker = "extra == 's3'", specifier = ">=1.35.0,<2" }, { name = "deepagents", specifier = ">=0.6.5" }, + { name = "en-core-web-lg", marker = "extra == 'pii'", url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl" }, { name = "knowledge-layer", extras = ["all"], editable = "../sources/knowledge_layer" }, { name = "langchain-modal", specifier = "==0.0.5" }, { name = "langgraph-checkpoint-postgres", specifier = ">=3.0.0" }, @@ -209,6 +210,7 @@ requires-dist = [ { name = "nvidia-nat-eval", specifier = "==1.8.0" }, { name = "nvidia-nat-profiler", specifier = "==1.8.0" }, { name = "nvidia-nat-redis", specifier = "==1.8.0" }, + { name = "nvidia-nat-security", extras = ["defense"], marker = "extra == 'pii'", specifier = "==1.8.0" }, { name = "nvidia-nat-security", extras = ["guardrails"], specifier = "==1.8.0" }, { name = "nvidia-sphinx-theme", marker = "extra == 'docs'", specifier = ">=0.0.9.post1" }, { name = "pillow", specifier = ">=12.2.0,<13" }, @@ -231,7 +233,7 @@ requires-dist = [ { name = "sphinxcontrib-mermaid", marker = "extra == 'docs'", specifier = ">=2.0.1" }, { name = "yapf", marker = "extra == 'dev'", specifier = ">=0.40.0" }, ] -provides-extras = ["s3", "dev", "docs", "viz"] +provides-extras = ["pii", "s3", "dev", "docs", "viz"] [package.metadata.requires-dev] dev = [ diff --git a/pyproject.toml b/pyproject.toml index 5fcb0643f..a71f2a472 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,10 @@ dependencies = [ ] [project.optional-dependencies] +pii = [ + "nvidia-nat-security[defense]==1.8.0", + "en-core-web-lg @ https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl", +] s3 = [ "boto3>=1.35.0,<2", ] diff --git a/src/aiq_agent/guardrails/deep_agent/middleware.py b/src/aiq_agent/guardrails/deep_agent/middleware.py index 243991d68..c190d7760 100644 --- a/src/aiq_agent/guardrails/deep_agent/middleware.py +++ b/src/aiq_agent/guardrails/deep_agent/middleware.py @@ -25,6 +25,7 @@ from aiq_agent.agents.deep_researcher.models import DeepResearchAgentState from aiq_agent.guardrails.deep_agent.config import DeepAgentGuardrailsConfig +from aiq_agent.guardrails.interface.middleware import _GUARDRAILS_FAILURE_REFUSAL from aiq_agent.guardrails.interface.middleware import GuardrailsMixin from nat.builder.builder import Builder from nat.middleware.middleware import InvocationContext @@ -134,6 +135,14 @@ def _on_post_invoke_blocked( """Replace blocked deep-agent output content with the refusal.""" return super()._on_post_invoke_blocked(context, block_message, original_output) + def _build_emergency_output_refusal( + self, + context: InvocationContext, + original_output: object, + ) -> DeepResearchAgentState: + """Return a minimal refusal state without traversing protected output.""" + return DeepResearchAgentState(messages=[AIMessage(content=_GUARDRAILS_FAILURE_REFUSAL)]) + def _iter_targets_at_path(self, value: Any, path: str) -> Iterator[tuple[str, Callable[[str], None]]]: """Yield the latest deep-agent message for the inherited field-selection hook.""" selected_message = self._extract_latest_message_text(value, path.split(".")) diff --git a/src/aiq_agent/guardrails/interface/middleware.py b/src/aiq_agent/guardrails/interface/middleware.py index 710ea4e3c..af4c1c34a 100644 --- a/src/aiq_agent/guardrails/interface/middleware.py +++ b/src/aiq_agent/guardrails/interface/middleware.py @@ -49,8 +49,11 @@ async def pre_invoke(self, context: InvocationContext) -> InvocationContext | No """Run input rails and adapt blocked outputs for the intercepted boundary.""" try: result = await super().pre_invoke(context) - except Exception: - logger.exception("Input Guardrails failed while evaluating selected fields; refusing request") + except Exception as exc: + logger.error( + "Input Guardrails failed while evaluating selected fields; refusing request; error_type=%s", + type(exc).__name__, + ) context.output = self._on_pre_invoke_blocked(context, _GUARDRAILS_FAILURE_REFUSAL) return context @@ -67,7 +70,20 @@ def _on_pre_invoke_blocked(self, context: InvocationContext, block_message: str) async def post_invoke(self, context: InvocationContext) -> InvocationContext | None: """Run output rails and adapt blocked outputs for the intercepted boundary.""" - return await super().post_invoke(context) + try: + result = await super().post_invoke(context) + if result is not None: + self._synchronize_terminal_output(context) + return result + except Exception as exc: + logger.error( + "Output Guardrails failed while evaluating selected fields for function %s; " + "refusing response; error_type=%s", + context.function_context.name, + type(exc).__name__, + ) + context.output = self._refuse_output_safely(context, context.output) + return context async def function_middleware_stream( self, @@ -107,7 +123,18 @@ async def function_middleware_stream( yield ctx.output return - output, blocked = await self._apply_output_rails_to_structured_stream(ctx, buffered) + try: + output, blocked = await self._apply_output_rails_to_structured_stream(ctx, buffered) + except Exception as exc: + logger.error( + "Output Guardrails failed while evaluating buffered structured output for function %s; " + "refusing response; error_type=%s", + context.name, + type(exc).__name__, + ) + ctx.output = self._refuse_output_safely(ctx, buffered[0]) + yield ctx.output + return if blocked: yield output return @@ -134,7 +161,7 @@ async def _apply_output_rails_to_structured_stream( input_text = getattr(raw, "input_message", None) or (raw if isinstance(raw, str) else str(raw)) paths = self._resolve_guarded_targets_for_phase(context.function_context.name, "post_invoke") - selections = self._structured_stream_output_selections(buffered, paths) + selections = self._stream_output_selections(buffered, paths) if not selections: return buffered[0], False @@ -161,20 +188,30 @@ async def _apply_output_rails_to_structured_stream( selections[0][1](result_text) for _text, apply_to_field in selections[1:]: apply_to_field("") + self._synchronize_buffered_outputs(buffered, paths) return buffered[0], False - def _structured_stream_output_selections( + def _stream_output_selections( self, buffered: list[object], paths: list[str], ) -> list[tuple[str, Callable[[str], None]]]: - """Return selected text fields from buffered structured stream chunks.""" + """Return writable text selections from string and structured stream chunks.""" selections: list[tuple[str, Callable[[str], None]]] = [] - for chunk in buffered: - selections.extend(self._gather_guardrail_inputs(chunk, paths, lambda _value: None)) + for index, chunk in enumerate(buffered): + if isinstance(chunk, str): + selections.append((chunk, lambda value, index=index: buffered.__setitem__(index, value))) + else: + selections.extend(self._gather_guardrail_inputs(chunk, paths, lambda _value: None)) return selections + def _synchronize_buffered_outputs(self, buffered: list[object], paths: list[str]) -> None: + """Let a concrete boundary align auxiliary fields with rewritten public output.""" + + def _synchronize_terminal_output(self, context: InvocationContext) -> None: + """Let a concrete boundary align auxiliary fields with guarded public output.""" + def on_post_invoke_blocked(self, context: InvocationContext, block_message: str) -> object: """Adapt blocked output before the intercepted result is returned.""" return self._on_post_invoke_blocked(context, block_message, context.output) @@ -188,7 +225,32 @@ def _on_post_invoke_blocked( """Adapt output-rail block output for the intercepted boundary.""" if not isinstance(original_output, str): paths = self._resolve_guarded_targets_for_phase(context.function_context.name, "post_invoke") + modified = False for _text, apply_to_field in self._gather_guardrail_inputs(original_output, paths, lambda _value: None): apply_to_field(block_message) + modified = True + if modified: + self._synchronize_blocked_output(original_output, block_message) return original_output + return self._build_emergency_output_refusal(context, original_output) return block_message + + def _synchronize_blocked_output(self, output: object, block_message: str) -> None: + """Let a concrete boundary align auxiliary fields with a blocked public output.""" + + def _refuse_output_safely(self, context: InvocationContext, original_output: object) -> object: + """Adapt a refusal without allowing a failing target traversal to escape.""" + try: + return self._on_post_invoke_blocked(context, _GUARDRAILS_FAILURE_REFUSAL, original_output) + except Exception as exc: + logger.error( + "Output Guardrails failed while adapting refusal for function %s; " + "using emergency refusal; error_type=%s", + context.function_context.name, + type(exc).__name__, + ) + return self._build_emergency_output_refusal(context, original_output) + + def _build_emergency_output_refusal(self, context: InvocationContext, original_output: object) -> object: + """Return the traversal-independent refusal for this intercepted boundary.""" + return _GUARDRAILS_FAILURE_REFUSAL diff --git a/src/aiq_agent/guardrails/shallow_agent/middleware.py b/src/aiq_agent/guardrails/shallow_agent/middleware.py index 7bed7ea96..639b38cfb 100644 --- a/src/aiq_agent/guardrails/shallow_agent/middleware.py +++ b/src/aiq_agent/guardrails/shallow_agent/middleware.py @@ -24,6 +24,7 @@ from langchain_core.messages import AIMessage from aiq_agent.agents.shallow_researcher.models import ShallowResearchAgentState +from aiq_agent.guardrails.interface.middleware import _GUARDRAILS_FAILURE_REFUSAL from aiq_agent.guardrails.interface.middleware import GuardrailsMixin from aiq_agent.guardrails.shallow_agent.config import ShallowAgentGuardrailsConfig from nat.builder.builder import Builder @@ -134,6 +135,14 @@ def _on_post_invoke_blocked( """Replace blocked shallow-agent output content with the refusal.""" return super()._on_post_invoke_blocked(context, block_message, original_output) + def _build_emergency_output_refusal( + self, + context: InvocationContext, + original_output: object, + ) -> ShallowResearchAgentState: + """Return a minimal refusal state without traversing protected output.""" + return ShallowResearchAgentState(messages=[AIMessage(content=_GUARDRAILS_FAILURE_REFUSAL)]) + def _iter_targets_at_path(self, value: Any, path: str) -> Iterator[tuple[str, Callable[[str], None]]]: """Yield the latest shallow-agent message for the inherited field-selection hook.""" selected_message = self._extract_latest_message_text(value, path.split(".")) diff --git a/src/aiq_agent/guardrails/workflow/middleware.py b/src/aiq_agent/guardrails/workflow/middleware.py index f06a941ee..414b4089d 100644 --- a/src/aiq_agent/guardrails/workflow/middleware.py +++ b/src/aiq_agent/guardrails/workflow/middleware.py @@ -26,14 +26,16 @@ from nemoguardrails.rails.llm.options import GenerationOptions from nemoguardrails.rails.llm.options import GenerationResponse +from aiq_agent.agents.chat_researcher.models.result import ChatResearcherResponse +from aiq_agent.agents.chat_researcher.models.result import WorkflowSuccess from aiq_agent.agents.chat_researcher.utils import _extract_context_from_text from aiq_agent.agents.chat_researcher.utils import _is_user_role +from aiq_agent.common import _create_chat_response from aiq_agent.guardrails.interface.middleware import _GUARDRAILS_FAILURE_REFUSAL from aiq_agent.guardrails.interface.middleware import GuardrailsMixin from aiq_agent.guardrails.workflow.config import WorkflowGuardrailsConfig from nat.builder.builder import Builder from nat.middleware.middleware import InvocationContext -from nat.plugins.security.middleware.guardrails.nemo_guardrails_middleware import GuardrailsMiddleware logger = logging.getLogger(__name__) @@ -65,11 +67,11 @@ async def pre_invoke(self, context: InvocationContext) -> InvocationContext | No input: Any = context.modified_args[0] - targets = self._extract_guardrail_targets_for_rewrite(input) - if not targets: - return None - try: + targets = self._extract_guardrail_targets_for_rewrite(input) + if not targets: + return None + await self.bind_llms_to_rail() modified = False @@ -98,14 +100,56 @@ async def pre_invoke(self, context: InvocationContext) -> InvocationContext | No modified = True return context if modified else None - except Exception: - logger.exception("Workflow input Guardrails failed while evaluating query text; refusing request") + except Exception as exc: + logger.error( + "Workflow input Guardrails failed while evaluating query text; refusing request; error_type=%s", + type(exc).__name__, + ) context.output = self._on_pre_invoke_blocked(context, _GUARDRAILS_FAILURE_REFUSAL) return context async def post_invoke(self, context: InvocationContext) -> InvocationContext | None: """Run output rails for the configured workflow result fields.""" - return await GuardrailsMiddleware.post_invoke(self, context) + return await super().post_invoke(context) + + def _build_emergency_output_refusal(self, context: InvocationContext, original_output: object) -> object: + """Return a fresh refusal preserving the workflow response schema.""" + model = getattr(original_output, "model", None) + response_id = getattr(original_output, "id", "guardrails_refusal") + response = _create_chat_response(_GUARDRAILS_FAILURE_REFUSAL, response_id=response_id, model=model) + if isinstance(original_output, ChatResearcherResponse): + return ChatResearcherResponse( + **response.model_dump(), + workflow_outcome=WorkflowSuccess(result=_GUARDRAILS_FAILURE_REFUSAL), + ) + return response + + @staticmethod + def _replace_terminal_output_text(output: object, guarded_text: str) -> None: + """Keep a workflow's successful terminal result aligned with guarded public output.""" + workflow_outcome = getattr(output, "workflow_outcome", None) + if getattr(workflow_outcome, "status", None) == "success": + setattr(workflow_outcome, "result", guarded_text) + + def _synchronize_blocked_output(self, output: object, block_message: str) -> None: + """Synchronize a blocked workflow's terminal outcome.""" + self._replace_terminal_output_text(output, block_message) + + def _synchronize_buffered_outputs(self, buffered: list[object], paths: list[str]) -> None: + """Synchronize every rewritten structured stream chunk's terminal outcome.""" + for chunk in buffered: + if isinstance(chunk, str): + continue + for text, _apply_to_field in self._gather_guardrail_inputs(chunk, paths, lambda _value: None): + self._replace_terminal_output_text(chunk, text) + break + + def _synchronize_terminal_output(self, context: InvocationContext) -> None: + """Copy guarded workflow output into its successful terminal outcome.""" + paths = self._resolve_guarded_targets_for_phase(context.function_context.name, "post_invoke") + for text, _apply_to_field in self._gather_guardrail_inputs(context.output, paths, lambda _value: None): + self._replace_terminal_output_text(context.output, text) + return def _extract_guardrail_target(self, raw_input: object) -> str | None: """Extract the normalized user query text from a raw workflow input.""" @@ -119,15 +163,7 @@ def _extract_guardrail_targets_for_rewrite( raw_input: object, ) -> list[tuple[str, Callable[[str], object]]]: """Extract guardrail targets that each map to one writable string leaf.""" - try: - targets = self._extract_guardrail_targets_for_rewrite_unchecked(raw_input) - except Exception: - logger.exception( - "Workflow input Guardrails could not extract query text from input type %s; continuing without rails", - type(raw_input).__name__, - ) - return [] - + targets = self._extract_guardrail_targets_for_rewrite_unchecked(raw_input) targets = [(text, replace_text) for text, replace_text in targets if text] if not targets: logger.warning( diff --git a/tests/aiq_agent/guardrails/test_deep_agent_guardrails.py b/tests/aiq_agent/guardrails/test_deep_agent_guardrails.py index 40b227a68..0ddbbb896 100644 --- a/tests/aiq_agent/guardrails/test_deep_agent_guardrails.py +++ b/tests/aiq_agent/guardrails/test_deep_agent_guardrails.py @@ -19,9 +19,11 @@ Guardrails field selection to target configured deep-agent message content. """ +import logging from collections.abc import Callable from types import SimpleNamespace from unittest.mock import AsyncMock +from unittest.mock import Mock import pytest from langchain_core.messages import AIMessage @@ -378,3 +380,63 @@ async def block_on_output_message(*, messages: list[dict[str, str]], **_kwargs: assert output_text not in [message.content for message in context.output.messages] guardrails._llm_rails.generate_async.assert_awaited_once() assert guardrails._llm_rails.generate_async.await_args.kwargs["messages"][-1]["content"] == output_text + + +@pytest.mark.asyncio +@pytest.mark.parametrize("failure_point", ["bind", "evaluate"]) +async def test_post_invoke_refuses_when_output_rail_fails( + guardrails: _DeepAgentGuardrails, + caplog: pytest.LogCaptureFixture, + failure_point: str, +): + """An output-rail runtime failure replaces deep-agent output with a refusal.""" + user_text = "Please summarize this issue." + output_text = "Please follow up with customer@example.com about this issue." + output = DeepResearchAgentState(messages=[HumanMessage(content=user_text), AIMessage(content=output_text)]) + rail_error = RuntimeError("rail backend failed") + + caplog.set_level(logging.ERROR, logger="aiq_agent.guardrails.interface.middleware") + guardrails.bind_llms_to_rail = AsyncMock(side_effect=rail_error if failure_point == "bind" else None) + guardrails._llm_rails = SimpleNamespace( + generate_async=AsyncMock(side_effect=rail_error if failure_point == "evaluate" else None) + ) + context = _post_invoke_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert context.output is output + assert output.messages[0].content == user_text + assert output.messages[1].content == _GUARDRAILS_FAILURE_REFUSAL + assert output_text not in output.messages[1].content + assert "Output Guardrails failed while evaluating selected fields" in caplog.text + assert "rail backend failed" not in caplog.text + if failure_point == "evaluate": + guardrails._llm_rails.generate_async.assert_awaited_once() + assert guardrails._llm_rails.generate_async.await_args.kwargs["messages"][-1]["content"] == output_text + else: + guardrails._llm_rails.generate_async.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_post_invoke_uses_schema_safe_emergency_refusal( + guardrails: _DeepAgentGuardrails, + caplog: pytest.LogCaptureFixture, +): + """Traversal failure returns a minimal deep-agent state without protected output.""" + output_text = "Contact customer@example.com" + log_sentinel = "traversal failed for jane.doe@example.com" + output = DeepResearchAgentState(messages=[AIMessage(content=output_text)]) + + caplog.set_level(logging.ERROR, logger="aiq_agent.guardrails.interface.middleware") + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._gather_guardrail_inputs = Mock(side_effect=RuntimeError(log_sentinel)) + context = _post_invoke_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert isinstance(context.output, DeepResearchAgentState) + assert [message.content for message in context.output.messages] == [_GUARDRAILS_FAILURE_REFUSAL] + assert output_text not in context.output.model_dump_json() + assert log_sentinel not in caplog.text diff --git a/tests/aiq_agent/guardrails/test_shallow_agent_guardrails.py b/tests/aiq_agent/guardrails/test_shallow_agent_guardrails.py index b98ad80a9..1502ee00a 100644 --- a/tests/aiq_agent/guardrails/test_shallow_agent_guardrails.py +++ b/tests/aiq_agent/guardrails/test_shallow_agent_guardrails.py @@ -19,9 +19,11 @@ Guardrails field selection to target configured shallow-agent message content. """ +import logging from collections.abc import Callable from types import SimpleNamespace from unittest.mock import AsyncMock +from unittest.mock import Mock import pytest from langchain_core.messages import AIMessage @@ -390,3 +392,63 @@ async def block_on_output_message(*, messages: list[dict[str, str]], **_kwargs: assert output_text not in [message.content for message in context.output.messages] guardrails._llm_rails.generate_async.assert_awaited_once() assert guardrails._llm_rails.generate_async.await_args.kwargs["messages"][-1]["content"] == output_text + + +@pytest.mark.asyncio +@pytest.mark.parametrize("failure_point", ["bind", "evaluate"]) +async def test_post_invoke_refuses_when_output_rail_fails( + guardrails: _ShallowAgentGuardrails, + caplog: pytest.LogCaptureFixture, + failure_point: str, +): + """An output-rail runtime failure replaces shallow-agent output with a refusal.""" + user_text = "Please summarize this issue." + output_text = "Please follow up with customer@example.com about this issue." + output = ShallowResearchAgentState(messages=[HumanMessage(content=user_text), AIMessage(content=output_text)]) + rail_error = RuntimeError("rail backend failed") + + caplog.set_level(logging.ERROR, logger="aiq_agent.guardrails.interface.middleware") + guardrails.bind_llms_to_rail = AsyncMock(side_effect=rail_error if failure_point == "bind" else None) + guardrails._llm_rails = SimpleNamespace( + generate_async=AsyncMock(side_effect=rail_error if failure_point == "evaluate" else None) + ) + context = _post_invoke_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert context.output is output + assert output.messages[0].content == user_text + assert output.messages[1].content == _GUARDRAILS_FAILURE_REFUSAL + assert output_text not in output.messages[1].content + assert "Output Guardrails failed while evaluating selected fields" in caplog.text + assert "rail backend failed" not in caplog.text + if failure_point == "evaluate": + guardrails._llm_rails.generate_async.assert_awaited_once() + assert guardrails._llm_rails.generate_async.await_args.kwargs["messages"][-1]["content"] == output_text + else: + guardrails._llm_rails.generate_async.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_post_invoke_uses_schema_safe_emergency_refusal( + guardrails: _ShallowAgentGuardrails, + caplog: pytest.LogCaptureFixture, +): + """Traversal failure returns a minimal shallow-agent state without protected output.""" + output_text = "Contact customer@example.com" + log_sentinel = "traversal failed for jane.doe@example.com" + output = ShallowResearchAgentState(messages=[AIMessage(content=output_text)]) + + caplog.set_level(logging.ERROR, logger="aiq_agent.guardrails.interface.middleware") + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._gather_guardrail_inputs = Mock(side_effect=RuntimeError(log_sentinel)) + context = _post_invoke_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert isinstance(context.output, ShallowResearchAgentState) + assert [message.content for message in context.output.messages] == [_GUARDRAILS_FAILURE_REFUSAL] + assert output_text not in context.output.model_dump_json() + assert log_sentinel not in caplog.text diff --git a/tests/aiq_agent/guardrails/test_workflow_guardrails.py b/tests/aiq_agent/guardrails/test_workflow_guardrails.py index 3632e5b61..840252a34 100644 --- a/tests/aiq_agent/guardrails/test_workflow_guardrails.py +++ b/tests/aiq_agent/guardrails/test_workflow_guardrails.py @@ -26,9 +26,12 @@ from collections.abc import Callable from types import SimpleNamespace from unittest.mock import AsyncMock +from unittest.mock import Mock import pytest +from aiq_agent.agents.chat_researcher.models.result import ChatResearcherResponse +from aiq_agent.agents.chat_researcher.models.result import WorkflowSuccess from aiq_agent.common import _create_chat_response from aiq_agent.guardrails.dynamic_field_selection import FunctionFieldSelection from aiq_agent.guardrails.interface.middleware import _GUARDRAILS_FAILURE_REFUSAL @@ -70,6 +73,11 @@ def _workflow_response(content: str): return _create_chat_response(content, response_id="research_response", model=_TEST_WORKFLOW_FUNCTION) +def _workflow_response_with_outcome(content: str) -> ChatResearcherResponse: + response = _workflow_response(content) + return ChatResearcherResponse(**response.model_dump(), workflow_outcome=WorkflowSuccess(result=content)) + + def _rail_response( response: object, *, @@ -549,6 +557,46 @@ async def test_pre_invoke_refuses_when_rail_evaluation_fails( call_next.assert_not_awaited() +@pytest.mark.asyncio +async def test_pre_invoke_refuses_when_input_target_traversal_fails( + guardrails: _WorkflowGuardrails, + caplog: pytest.LogCaptureFixture, +): + """An input traversal failure refuses instead of running the workflow unguarded.""" + + class RaisingInput: + @property + def messages(self) -> list[object]: + raise RuntimeError("input traversal failed for jane.doe@example.com") + + caplog.set_level(logging.ERROR, logger="aiq_agent.guardrails.workflow.middleware") + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._llm_rails = SimpleNamespace(generate_async=AsyncMock()) + call_next = AsyncMock(return_value="workflow result") + + result = await guardrails.function_middleware_invoke( + RaisingInput(), + call_next=call_next, + context=FunctionMiddlewareContext( + name=_TEST_WORKFLOW_FUNCTION, + config=None, + description=None, + input_schema=None, + single_output_schema=type(None), + stream_output_schema=type(None), + ), + ) + + assert result == _GUARDRAILS_FAILURE_REFUSAL + call_next.assert_not_awaited() + guardrails.bind_llms_to_rail.assert_not_awaited() + guardrails._llm_rails.generate_async.assert_not_awaited() + assert "Workflow input Guardrails failed while evaluating query text" in caplog.text + assert "error_type=RuntimeError" in caplog.text + assert "input traversal failed" not in caplog.text + assert "jane.doe@example.com" not in caplog.text + + @pytest.mark.asyncio async def test_post_invoke_passes_when_rail_passes(guardrails: _WorkflowGuardrails): """A passing output rail leaves configured ChatResponse message content unchanged.""" @@ -632,6 +680,217 @@ async def test_post_invoke_blocks_when_rail_blocks(guardrails: _WorkflowGuardrai assert guardrails._llm_rails.generate_async.await_args.kwargs["messages"][-1]["content"] == output_text +@pytest.mark.asyncio +@pytest.mark.parametrize("failure_point", ["bind", "evaluate"]) +async def test_post_invoke_refuses_when_output_rail_fails( + guardrails: _WorkflowGuardrails, + caplog: pytest.LogCaptureFixture, + failure_point: str, +): + """An output-rail runtime failure returns a shaped refusal without leaking output.""" + output_text = "Please follow up with customer@example.com about this issue." + output = _workflow_response(output_text) + rail_error = RuntimeError("rail backend failed") + + caplog.set_level(logging.ERROR, logger="aiq_agent.guardrails.interface.middleware") + guardrails.bind_llms_to_rail = AsyncMock(side_effect=rail_error if failure_point == "bind" else None) + guardrails._llm_rails = SimpleNamespace( + generate_async=AsyncMock(side_effect=rail_error if failure_point == "evaluate" else None) + ) + context = _workflow_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert context.output is output + assert output.choices[0].message.content == _GUARDRAILS_FAILURE_REFUSAL + assert output_text not in output.choices[0].message.content + assert "rail backend failed" not in output.choices[0].message.content + assert "Output Guardrails failed while evaluating selected fields" in caplog.text + assert "rail backend failed" not in caplog.text + + +@pytest.mark.asyncio +async def test_post_invoke_failure_replaces_every_selected_output(guardrails: _WorkflowGuardrails): + """A failure after one successful target cannot leave later selected output unfiltered.""" + first_text = "First safe result." + second_text = "Contact customer@example.com" + output = _workflow_response(first_text) + second_choice = _workflow_response(second_text).choices[0] + second_choice.index = 1 + output.choices.append(second_choice) + + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._llm_rails = SimpleNamespace( + generate_async=AsyncMock( + side_effect=[ + _rail_response( + [{"role": "assistant", "content": first_text}], + rail_name="mask sensitive data on output", + ), + RuntimeError("rail backend failed"), + ] + ) + ) + context = _workflow_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert [choice.message.content for choice in output.choices] == [ + _GUARDRAILS_FAILURE_REFUSAL, + _GUARDRAILS_FAILURE_REFUSAL, + ] + assert second_text not in output.model_dump_json() + + +@pytest.mark.asyncio +async def test_post_invoke_contains_target_gathering_failure( + guardrails: _WorkflowGuardrails, + caplog: pytest.LogCaptureFixture, +): + """A target traversal failure cannot escape again while adapting the refusal.""" + output = _workflow_response_with_outcome("Contact customer@example.com") + + caplog.set_level(logging.ERROR, logger="aiq_agent.guardrails.interface.middleware") + guardrails.bind_llms_to_rail = AsyncMock() + log_sentinel = "target traversal failed for jane.doe@example.com" + guardrails._gather_guardrail_inputs = Mock(side_effect=RuntimeError(log_sentinel)) + context = _workflow_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert isinstance(context.output, type(output)) + assert context.output.choices[0].message.content == _GUARDRAILS_FAILURE_REFUSAL + assert isinstance(context.output.workflow_outcome, WorkflowSuccess) + assert context.output.workflow_outcome.result == _GUARDRAILS_FAILURE_REFUSAL + assert "customer@example.com" not in context.output.model_dump_json() + assert "Output Guardrails failed while adapting refusal" in caplog.text + assert log_sentinel not in caplog.text + + +@pytest.mark.asyncio +async def test_post_invoke_uses_schema_safe_emergency_refusal_for_chat_response( + guardrails: _WorkflowGuardrails, +): + """Traversal failure preserves a workflow ChatResponse without terminal metadata.""" + output_text = "Contact customer@example.com" + output = _workflow_response(output_text) + + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._gather_guardrail_inputs = Mock(side_effect=RuntimeError("target traversal failed")) + context = _workflow_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert isinstance(context.output, type(output)) + assert context.output.choices[0].message.content == _GUARDRAILS_FAILURE_REFUSAL + assert output_text not in context.output.model_dump_json() + + +@pytest.mark.asyncio +async def test_post_invoke_uses_schema_safe_emergency_refusal_when_no_target_can_be_adapted( + guardrails: _WorkflowGuardrails, +): + """An empty traversal result cannot degrade a workflow response to a scalar refusal.""" + output_text = "Contact customer@example.com" + output = _workflow_response(output_text) + + guardrails.bind_llms_to_rail = AsyncMock(side_effect=RuntimeError("rail failed")) + guardrails._gather_guardrail_inputs = Mock(return_value=[]) + context = _workflow_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert isinstance(context.output, type(output)) + assert context.output.choices[0].message.content == _GUARDRAILS_FAILURE_REFUSAL + assert output_text not in context.output.model_dump_json() + + +@pytest.mark.asyncio +async def test_post_invoke_contains_terminal_synchronization_failure( + guardrails: _WorkflowGuardrails, + caplog: pytest.LogCaptureFixture, +): + """A terminal synchronization failure returns a shaped refusal without leaking output.""" + output_text = "Contact customer@example.com" + masked_output = "Contact " + output = _workflow_response_with_outcome(output_text) + log_sentinel = "synchronization failed for jane.doe@example.com" + + caplog.set_level(logging.ERROR, logger="aiq_agent.guardrails.interface.middleware") + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._llm_rails = SimpleNamespace( + generate_async=AsyncMock( + return_value=_rail_response( + [{"role": "assistant", "content": masked_output}], + rail_name="mask sensitive data on output", + ) + ) + ) + guardrails._synchronize_terminal_output = Mock(side_effect=RuntimeError(log_sentinel)) + context = _workflow_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert isinstance(context.output, ChatResearcherResponse) + assert context.output.choices[0].message.content == _GUARDRAILS_FAILURE_REFUSAL + assert isinstance(context.output.workflow_outcome, WorkflowSuccess) + assert context.output.workflow_outcome.result == _GUARDRAILS_FAILURE_REFUSAL + assert output_text not in context.output.model_dump_json() + assert log_sentinel not in caplog.text + + +@pytest.mark.asyncio +async def test_post_invoke_refusal_replaces_terminal_workflow_result(guardrails: _WorkflowGuardrails): + """Guarded workflow output cannot remain available through the terminal outcome.""" + output_text = "Please follow up with customer@example.com about this issue." + output = _workflow_response_with_outcome(output_text) + + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._llm_rails = SimpleNamespace(generate_async=AsyncMock(side_effect=RuntimeError("rail backend failed"))) + context = _workflow_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert output.choices[0].message.content == _GUARDRAILS_FAILURE_REFUSAL + assert isinstance(output.workflow_outcome, WorkflowSuccess) + assert output.workflow_outcome.result == _GUARDRAILS_FAILURE_REFUSAL + assert output_text not in output.model_dump_json() + + +@pytest.mark.asyncio +async def test_post_invoke_masking_replaces_terminal_workflow_result(guardrails: _WorkflowGuardrails): + """Masked workflow output cannot remain unfiltered in the terminal outcome.""" + output_text = "Contact jane.doe@example.com" + masked_output = "Contact " + output = _workflow_response_with_outcome(output_text) + + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._llm_rails = SimpleNamespace( + generate_async=AsyncMock( + return_value=_rail_response( + [{"role": "assistant", "content": masked_output}], + rail_name="mask sensitive data on output", + ) + ) + ) + context = _workflow_context(output) + + result = await guardrails.post_invoke(context) + + assert result is context + assert output.choices[0].message.content == masked_output + assert isinstance(output.workflow_outcome, WorkflowSuccess) + assert output.workflow_outcome.result == masked_output + assert output_text not in output.model_dump_json() + + @pytest.mark.asyncio async def test_stream_middleware_preserves_structured_output_chunks(guardrails: _WorkflowGuardrails): """Streaming Guardrails evaluate selected fields without stringifying structured chunks.""" @@ -678,6 +937,197 @@ async def call_next(*_args, **_kwargs): assert guardrails._llm_rails.generate_async.await_args.kwargs["messages"][-1]["content"] == output_text +@pytest.mark.asyncio +async def test_stream_middleware_synchronizes_every_structured_terminal_outcome( + guardrails: _WorkflowGuardrails, +): + """Masking a multi-chunk stream removes raw output from every serialized chunk.""" + first_output = _workflow_response_with_outcome("Contact jane.doe@") + second_output = _workflow_response_with_outcome("example.com") + masked_output = "Contact " + + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._llm_rails = SimpleNamespace( + generate_async=AsyncMock( + side_effect=[ + _rail_response("hello", rail_name="detect sensitive data on input"), + _rail_response( + [{"role": "assistant", "content": masked_output}], + rail_name="mask sensitive data on output", + ), + ] + ) + ) + + async def call_next(*_args, **_kwargs): + yield first_output + yield second_output + + results = [ + item + async for item in guardrails.function_middleware_stream( + "hello", + call_next=call_next, + context=FunctionMiddlewareContext( + name=_TEST_WORKFLOW_FUNCTION, + config=None, + description=None, + input_schema=None, + single_output_schema=type(None), + stream_output_schema=type(None), + ), + ) + ] + + serialized = json.dumps([item.model_dump(mode="json") for item in results]) + assert [item.choices[0].message.content for item in results] == [masked_output, ""] + assert [item.workflow_outcome.result for item in results] == [masked_output, ""] + assert "jane.doe@example.com" not in serialized + assert "jane.doe@" not in serialized + assert "example.com" not in serialized + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("structured_first", "rail_response", "expected"), + [ + pytest.param(False, "The result is safe.", ["The result ", "is safe."], id="pass-string-first"), + pytest.param(False, "The result is masked.", ["The result is masked.", ""], id="modify-string-first"), + pytest.param(True, "The result is safe. tail", ["The result is safe.", " tail"], id="pass-structured-first"), + pytest.param(True, "The result is masked.", ["The result is masked.", ""], id="modify-structured-first"), + ], +) +async def test_stream_middleware_guards_mixed_string_and_structured_output( + guardrails: _WorkflowGuardrails, + structured_first: bool, + rail_response: str, + expected: list[str], +): + """Mixed string and structured chunks are evaluated as one guarded logical output.""" + structured_text = "The result is safe." if structured_first else "is safe." + raw_text = " tail" if structured_first else "The result " + structured_output = _workflow_response_with_outcome(structured_text) + + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._llm_rails = SimpleNamespace( + generate_async=AsyncMock( + side_effect=[ + _rail_response("hello", rail_name="detect sensitive data on input"), + _rail_response( + [{"role": "assistant", "content": rail_response}], + rail_name="mask sensitive data on output", + ), + ] + ) + ) + + async def call_next(*_args, **_kwargs): + for chunk in [structured_output, raw_text] if structured_first else [raw_text, structured_output]: + yield chunk + + results = [ + item + async for item in guardrails.function_middleware_stream( + "hello", + call_next=call_next, + context=FunctionMiddlewareContext( + name=_TEST_WORKFLOW_FUNCTION, + config=None, + description=None, + input_schema=None, + single_output_schema=type(None), + stream_output_schema=type(None), + ), + ) + ] + + if structured_first: + assert results[0].choices[0].message.content == expected[0] + assert results[0].workflow_outcome.result == expected[0] + assert results[1] == expected[1] + logical_output = "The result is safe. tail" + else: + assert results[0] == expected[0] + assert results[1].choices[0].message.content == expected[1] + assert results[1].workflow_outcome.result == expected[1] + logical_output = "The result is safe." + assert guardrails._llm_rails.generate_async.await_args.kwargs["messages"][-1]["content"] == logical_output + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("failure_mode", "structured_first"), + [ + pytest.param("block", False, id="block-string-first"), + pytest.param("exception", False, id="exception-string-first"), + pytest.param("block", True, id="block-structured-first"), + pytest.param("exception", True, id="exception-structured-first"), + ], +) +async def test_stream_middleware_refuses_mixed_output_without_emitting_raw_chunks( + guardrails: _WorkflowGuardrails, + failure_mode: str, + structured_first: bool, +): + """A blocked or failed mixed stream emits one refusal and no buffered content.""" + structured_output = _workflow_response_with_outcome("example.com") + output_result = ( + _rail_response( + [{"role": "assistant", "content": TEST_REFUSAL}], + rail_name="detect sensitive data on output", + stopped=True, + bot_message=TEST_REFUSAL, + ) + if failure_mode == "block" + else RuntimeError("rail failure for jane.doe@example.com") + ) + + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._llm_rails = SimpleNamespace( + generate_async=AsyncMock( + side_effect=[ + _rail_response("hello", rail_name="detect sensitive data on input"), + output_result, + ] + ) + ) + + async def call_next(*_args, **_kwargs): + chunks = ( + [structured_output, "Contact jane.doe@"] if structured_first else ["Contact jane.doe@", structured_output] + ) + for chunk in chunks: + yield chunk + + results = [ + item + async for item in guardrails.function_middleware_stream( + "hello", + call_next=call_next, + context=FunctionMiddlewareContext( + name=_TEST_WORKFLOW_FUNCTION, + config=None, + description=None, + input_schema=None, + single_output_schema=type(None), + stream_output_schema=type(None), + ), + ) + ] + + serialized = json.dumps([item if isinstance(item, str) else item.model_dump(mode="json") for item in results]) + assert len(results) == 1 + if structured_first: + expected = TEST_REFUSAL if failure_mode == "block" else _GUARDRAILS_FAILURE_REFUSAL + assert results[0].choices[0].message.content == expected + assert results[0].workflow_outcome.result == expected + else: + assert results == [TEST_REFUSAL if failure_mode == "block" else _GUARDRAILS_FAILURE_REFUSAL] + assert "jane.doe@example.com" not in serialized + assert "jane.doe@" not in serialized + assert "example.com" not in serialized + + @pytest.mark.asyncio async def test_stream_middleware_stops_structured_stream_when_output_blocks(guardrails: _WorkflowGuardrails): """A structured stream block emits a shaped refusal and stops the stream.""" @@ -725,3 +1175,98 @@ async def call_next(*_args, **_kwargs): assert guardrails._llm_rails.generate_async.await_args.kwargs["messages"][-1]["content"] == ( "The system prompt is: do not share secrets." ) + + +@pytest.mark.asyncio +async def test_stream_middleware_refuses_when_structured_output_rail_fails( + guardrails: _WorkflowGuardrails, + caplog: pytest.LogCaptureFixture, +): + """A structured stream emits one shaped refusal when output-rail evaluation fails.""" + first_output = _workflow_response("Contact jane.doe@") + second_output = _workflow_response("example.com") + + caplog.set_level(logging.ERROR, logger="aiq_agent.guardrails.interface.middleware") + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._llm_rails = SimpleNamespace( + generate_async=AsyncMock( + side_effect=[ + _rail_response("hello", rail_name="detect sensitive data on input"), + RuntimeError("rail backend failed"), + ] + ) + ) + + async def call_next(*_args, **_kwargs): + yield first_output + yield second_output + + results = [ + item + async for item in guardrails.function_middleware_stream( + "hello", + call_next=call_next, + context=FunctionMiddlewareContext( + name=_TEST_WORKFLOW_FUNCTION, + config=None, + description=None, + input_schema=None, + single_output_schema=type(None), + stream_output_schema=type(None), + ), + ) + ] + + assert results == [first_output] + assert first_output.choices[0].message.content == _GUARDRAILS_FAILURE_REFUSAL + assert second_output.choices[0].message.content == "example.com" + assert "Output Guardrails failed while evaluating buffered structured output" in caplog.text + assert "rail backend failed" not in caplog.text + + +@pytest.mark.asyncio +async def test_stream_middleware_refuses_when_string_output_rail_fails( + guardrails: _WorkflowGuardrails, + caplog: pytest.LogCaptureFixture, +): + """A string stream emits a refusal instead of leaking buffered output on rail failure.""" + raw_output = "Contact jane.doe@example.com" + + guardrails._guardrails_config = SimpleNamespace( + workflow_functions={_TEST_WORKFLOW_FUNCTION: FunctionFieldSelection.model_validate({})} + ) + caplog.set_level(logging.ERROR, logger="aiq_agent.guardrails.interface.middleware") + guardrails.bind_llms_to_rail = AsyncMock() + guardrails._llm_rails = SimpleNamespace( + generate_async=AsyncMock( + side_effect=[ + _rail_response("hello", rail_name="detect sensitive data on input"), + RuntimeError("rail backend failed"), + ] + ) + ) + + async def call_next(*_args, **_kwargs): + yield "Contact jane.doe@" + yield "example.com" + + results = [ + item + async for item in guardrails.function_middleware_stream( + "hello", + call_next=call_next, + context=FunctionMiddlewareContext( + name=_TEST_WORKFLOW_FUNCTION, + config=None, + description=None, + input_schema=None, + single_output_schema=type(None), + stream_output_schema=type(None), + ), + ) + ] + + assert results == [_GUARDRAILS_FAILURE_REFUSAL] + assert raw_output not in results + assert "Output Guardrails failed while evaluating selected fields" in caplog.text + assert "rail backend failed" not in caplog.text diff --git a/uv.lock b/uv.lock index 95d8442c4..c9aa59eea 100644 --- a/uv.lock +++ b/uv.lock @@ -241,6 +241,10 @@ docs = [ { name = "sphinx-rtd-theme" }, { name = "sphinxcontrib-mermaid" }, ] +pii = [ + { name = "en-core-web-lg" }, + { name = "nvidia-nat-security", extra = ["defense"] }, +] s3 = [ { name = "boto3" }, ] @@ -279,6 +283,7 @@ requires-dist = [ { name = "asyncpg", specifier = ">=0.29.0" }, { name = "boto3", marker = "extra == 's3'", specifier = ">=1.35.0,<2" }, { name = "deepagents", specifier = ">=0.6.5" }, + { name = "en-core-web-lg", marker = "extra == 'pii'", url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl" }, { name = "knowledge-layer", extras = ["all"], editable = "sources/knowledge_layer" }, { name = "langchain-modal", specifier = "==0.0.5" }, { name = "langgraph-checkpoint-postgres", specifier = ">=3.0.0" }, @@ -290,6 +295,7 @@ requires-dist = [ { name = "nvidia-nat-eval", specifier = "==1.8.0" }, { name = "nvidia-nat-profiler", specifier = "==1.8.0" }, { name = "nvidia-nat-redis", specifier = "==1.8.0" }, + { name = "nvidia-nat-security", extras = ["defense"], marker = "extra == 'pii'", specifier = "==1.8.0" }, { name = "nvidia-nat-security", extras = ["guardrails"], specifier = "==1.8.0" }, { name = "nvidia-sphinx-theme", marker = "extra == 'docs'", specifier = ">=0.0.9.post1" }, { name = "pillow", specifier = ">=12.2.0,<13" }, @@ -312,7 +318,7 @@ requires-dist = [ { name = "sphinxcontrib-mermaid", marker = "extra == 'docs'", specifier = ">=2.0.1" }, { name = "yapf", marker = "extra == 'dev'", specifier = ">=0.40.0" }, ] -provides-extras = ["s3", "dev", "docs", "viz"] +provides-extras = ["pii", "s3", "dev", "docs", "viz"] [package.metadata.requires-dev] dev = [ @@ -747,6 +753,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9", size = 109924, upload-time = "2026-06-07T16:44:21.566Z" }, ] +[[package]] +name = "blis" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, +] +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/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 = "boto3" version = "1.43.25" @@ -865,6 +903,15 @@ 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 = "cbor2" version = "6.1.2" @@ -1083,6 +1130,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" }, ] +[[package]] +name = "cloudpathlib" +version = "0.24.0" +source = { registry = "https://pypi.org/simple" } +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 = "cloudpickle" version = "3.1.2" @@ -1101,6 +1157,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "confection" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +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 = "contourpy" version = "1.3.3" @@ -1341,6 +1406,46 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, ] +[[package]] +name = "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/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 = "dask" version = "2026.3.0" @@ -1559,6 +1664,14 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922, upload-time = "2025-05-17T13:52:36.463Z" }, ] +[[package]] +name = "en-core-web-lg" +version = "3.8.0" +source = { url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl" } +wheels = [ + { url = "https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl", hash = "sha256:293e9547a655b25499198ab15a525b05b9407a75f10255e405e8c3854329ab63" }, +] + [[package]] name = "events" version = "0.5" @@ -3797,6 +3910,46 @@ 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/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" version = "2.1.0" @@ -4257,6 +4410,12 @@ wheels = [ ] [package.optional-dependencies] +defense = [ + { name = "presidio-analyzer" }, + { name = "presidio-anonymizer" }, + { name = "spacy" }, + { name = "typer" }, +] guardrails = [ { name = "dataclasses-json" }, { name = "nemoguardrails" }, @@ -4743,6 +4902,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8b/c8/cdbc975f5b634e249cfa6597e37c50f3078412474f21c015e508bfbfe3c3/pdfplumber-0.11.9-py3-none-any.whl", hash = "sha256:33ec5580959ba524e9100138746e090879504c42955df1b8a997604dd326c443", size = 60045, upload-time = "2026-01-05T08:10:27.512Z" }, ] +[[package]] +name = "phonenumbers" +version = "9.0.34" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/c3/e154829a50679c38ae28ec9c4f151f2c425db5e70fd445266e76f6d6cd65/phonenumbers-9.0.34.tar.gz", hash = "sha256:00751c75d1166485ca80ce02ec15b6a61a2628e9b313381579330bc70c934075", size = 2306776, upload-time = "2026-07-03T06:30:37.358Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/0a/3a7980f3b071dde9a297d85cf6b18ba4bc2e5024e1c453b3da8a1dc14268/phonenumbers-9.0.34-py2.py3-none-any.whl", hash = "sha256:1221bf8e65bd2c02770226488af806d4636814bc997104d3a1f7de6ed6410bd2", size = 2595344, upload-time = "2026-07-03T06:30:33.913Z" }, +] + [[package]] name = "pillow" version = "12.2.0" @@ -4904,6 +5072,73 @@ dependencies = [ ] sdist = { url = "https://files.pythonhosted.org/packages/c5/b0/e66e9f6e07a0b37aa0f5703c46f54bafbdf65dfba63994247676b19076c4/prefixspan-0.5.2.tar.gz", hash = "sha256:8fbd2f94b3a7f4399d04f9bd6aa214b830fb7828799c472cd43dda10b03f671c", size = 10404, upload-time = "2018-09-29T06:12:27.708Z" } +[[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/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.363" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "numpy" }, + { name = "phonenumbers" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "regex" }, + { name = "spacy" }, + { name = "tldextract" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/c7/c88c469f06cbf44d00a3af773ea8baea7f6ee045ba39933f4a54993854f8/presidio_analyzer-2.2.363.tar.gz", hash = "sha256:f93eae598710e747e54d1a79d3a3b892caea91d46e87a32becb340fb064bf497", size = 149406, upload-time = "2026-06-28T16:09:10.613Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/17/1ff9fbf2cbecb9bf0bf86615aadb4641e29d9712cb6ca51aaeda481f5add/presidio_analyzer-2.2.363-py3-none-any.whl", hash = "sha256:84203d1148a87620ab80004cf31775d5868b965395f2754aa0a27c8d308e7dfb", size = 259041, upload-time = "2026-06-28T16:09:08.993Z" }, +] + +[[package]] +name = "presidio-anonymizer" +version = "2.2.363" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, +] +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 = "primp" version = "1.3.1" @@ -5834,6 +6069,18 @@ 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-oauthlib" version = "2.0.0" @@ -6117,6 +6364,18 @@ 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.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/53/9c513747547fd595d5c143259129ea8b9c3ea2f6b7bb9dcea2b1966ded3c/smart_open-8.0.1.tar.gz", hash = "sha256:18b1c4496003c6902be17c15f032b5c319f307c89c6ae9e6b028b508bed8b2cf", size = 61882, upload-time = "2026-07-15T13:56:10.492Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/96/325b8c507ccecc50421fecc0345a502ee6e4a44785af3c4e6ecbadad624a/smart_open-8.0.1-py3-none-any.whl", hash = "sha256:3e97f90e92a952cb57863dfe132082c400a52eeeb27c067692fb51dbcc5b0089", size = 73504, upload-time = "2026-07-15T13:56:09.033Z" }, +] + [[package]] name = "sniffio" version = "1.3.1" @@ -6162,6 +6421,77 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304, upload-time = "2026-05-24T13:55:55.406Z" }, ] +[[package]] +name = "spacy" +version = "3.8.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "catalogue" }, + { name = "confection" }, + { name = "cymem" }, + { name = "jinja2" }, + { name = "murmurhash" }, + { name = "numpy" }, + { 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" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/d7/1924f32272f50d2f13275f16d6f869fcec47b2b676b64f91fa206bd30ef4/spacy-3.8.13.tar.gz", hash = "sha256:eb7c03c2bb16593c34d4c91974118f0931c6e6969dbfe895b1a026c6714176cf", size = 1328016, upload-time = "2026-03-23T17:44:32.042Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/a8/21f5b9c3998c7a9cdd28715248e489ebd9300b1d349d5220166b951c3df4/spacy-3.8.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3fb3ec2a78a72bb87ca1cc22ad9fea2d37a3d88ec68ee6b4a02c967f35c8b0cb", size = 6617511, upload-time = "2026-03-23T17:42:17.689Z" }, + { url = "https://files.pythonhosted.org/packages/0c/24/0da6c637b3d25a37db605f836e53226cae65f035e865d2909ddf66b89185/spacy-3.8.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c7d719ff171aad00a9aac31e7c7f6e1871be08b0be50c362885c632826d41ce", size = 6441498, upload-time = "2026-03-23T17:42:20.516Z" }, + { url = "https://files.pythonhosted.org/packages/17/64/1eeb854dc194dde91582eedf523d38c8caa209fce45c79b175e3e7a00b9f/spacy-3.8.13-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:95c2bf7d0afbf699df89cca5431527dff4950a823fd093680618057aa2affff8", size = 32050555, upload-time = "2026-03-23T17:42:25.13Z" }, + { url = "https://files.pythonhosted.org/packages/05/5b/7de5aae98f0a4547b9e44fd41e3f5820c199d67c78148782a08df83d19a8/spacy-3.8.13-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b118c9ca4250cba8a39c7fc73641820086d476dafb54b5d1f306a3f60098d3cd", size = 32296475, upload-time = "2026-03-23T17:42:30.151Z" }, + { url = "https://files.pythonhosted.org/packages/96/33/624d0025097b6b26a2c6d1b3dd7e24dec5da495e8c2a0f379bcc3ec26d15/spacy-3.8.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:53a6ff574a4505339375e31ba3280ff7235e82e120377614210750fa66cb63df", size = 32288426, upload-time = "2026-03-23T17:42:35.418Z" }, + { url = "https://files.pythonhosted.org/packages/c2/be/93eec7f8266a6d96fb5229d4588d64bb6dfa037604b5121a703cad35debb/spacy-3.8.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cf07faad25a301efbd90da1343c1f62e35185d064c5e087bd0d11a5a6b3358fe", size = 33113494, upload-time = "2026-03-23T17:42:41.053Z" }, + { url = "https://files.pythonhosted.org/packages/26/3d/0bd7d780642635e662b32fcd530ed5bed1844dbf977bc5eed43efefa034c/spacy-3.8.13-cp311-cp311-win_amd64.whl", hash = "sha256:740cafd3cb2331c48057f6689ed6be5652d35db92aa2980f32f3d78a9aba6300", size = 15359637, upload-time = "2026-03-23T17:42:45.168Z" }, + { url = "https://files.pythonhosted.org/packages/be/78/11896d5b5987cd37ae498ef1ce795e0dd551f984fffa9cf6a7887655617e/spacy-3.8.13-cp311-cp311-win_arm64.whl", hash = "sha256:189bdd05cfd66fd3d1f79449d38ba4e2d0270743f762db1304617ccad9dd321c", size = 14717010, upload-time = "2026-03-23T17:42:48.915Z" }, + { url = "https://files.pythonhosted.org/packages/d8/e1/e8f6dc7fc00582b8dcbf40fac5bce6c0ff366cf6db212decdacfaf13e584/spacy-3.8.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dd8ab0deefe9d2c7dccce1be1e65660a32cfc3cc114d0aa222ff52ae11be58ba", size = 6218311, upload-time = "2026-03-23T17:42:51.938Z" }, + { url = "https://files.pythonhosted.org/packages/31/fe/517de9e25a6ec469738c9e886c15cd96649b338c6ae475b9b3e4c4f5e03d/spacy-3.8.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:eab059bc668336d0034f3f69aed6b661b626c174e97cc1b52296d0d923c24405", size = 6033843, upload-time = "2026-03-23T17:42:54.133Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a8/9a33c69f772f5c32a57c9ef7e692293b558e7bad202264d5586ab087fd29/spacy-3.8.13-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:96a8e141fd7ff749fa6693c9c72187f802d334bcadf48b6b9d6ca9bff315ca73", size = 32725183, upload-time = "2026-03-23T17:42:58.847Z" }, + { url = "https://files.pythonhosted.org/packages/ba/cf/dcc0ea61270cb23a46d3efc437fe760e7a9c3cabcfaa770591329d1430f2/spacy-3.8.13-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:32c16f5be8c8006197554da3647d83311fb89bb31bb417d38f0d1da585877b0d", size = 33205816, upload-time = "2026-03-23T17:43:03.717Z" }, + { url = "https://files.pythonhosted.org/packages/90/6b/ce94ba2a166add3376aa2c976cb1a34d6387e9dc61103cfbe9192675d0b6/spacy-3.8.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7402154eae9d2c34d097f3f7f2bf3ed4ce6ef9d44e3035af15ccc7f357d122cb", size = 32090348, upload-time = "2026-03-23T17:43:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4d/20/df5076a162bda36b04cdfa9cd544ac2be4b47aed957b3922e4c8f75dc25a/spacy-3.8.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:879f467578569db73b5811aa0f0b9a3b3fb81a125c2f7a433dd5626bcaca53f6", size = 32991911, upload-time = "2026-03-23T17:43:14.543Z" }, + { url = "https://files.pythonhosted.org/packages/41/17/316dfdf5f8d1dd33a205e4f5e7190b67db73802180f7d08c8b306eb44375/spacy-3.8.13-cp312-cp312-win_amd64.whl", hash = "sha256:a0c849b5c9cee5a930a5e8a63d0b07e095d4e3d371af6a70c496efa1d0851257", size = 14226861, upload-time = "2026-03-23T17:43:18.122Z" }, + { url = "https://files.pythonhosted.org/packages/be/a8/a794d5bbf7bc2df6770df2b14cd6811420d0e10fe81830920bf0e891c19a/spacy-3.8.13-cp312-cp312-win_arm64.whl", hash = "sha256:5d7f660f64c7d09778600b27b881a367075fe792cb5cc6932737aeda71a9aa09", size = 13628855, upload-time = "2026-03-23T17:43:22.162Z" }, + { url = "https://files.pythonhosted.org/packages/a7/2d/dc15440fa58c12bab25cbd368bbf3b754eb11d1d3cc37f3aee47caa63695/spacy-3.8.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6db8903cf3fd8b40db9dab669c1e823a8884100f223dc8ec63c3744ff505d5fd", size = 6202080, upload-time = "2026-03-23T17:43:25.044Z" }, + { url = "https://files.pythonhosted.org/packages/1b/27/6d723fa0c3c9872d7b8c3fb1c76edef65c1db2de441ee87d119128cb82f7/spacy-3.8.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c26258bfed2f3bf5563c5994ee752ea8662bbac70924d28fbea4d1c10df48fd0", size = 6015435, upload-time = "2026-03-23T17:43:28.281Z" }, + { url = "https://files.pythonhosted.org/packages/c0/36/d13f36204290e7753ce849106fb732a1a1ff6c1af0a200f2c2f493a56e60/spacy-3.8.13-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a5f02eab8e6e8e9e790b1e8da05b70b3b1d4b1fb10dc4f44203da96939fdb977", size = 32510675, upload-time = "2026-03-23T17:43:32.657Z" }, + { url = "https://files.pythonhosted.org/packages/19/37/f2a0c986bc2d59b18547d5ffaabf57fd9d88e129ad7df5ebc9ccdc91e643/spacy-3.8.13-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:17b14f28d83fe85390f420f89760ae36d515f3e0a236f8266c46335549806e3a", size = 32841103, upload-time = "2026-03-23T17:43:38.139Z" }, + { url = "https://files.pythonhosted.org/packages/37/31/b4a86cc013e4ac3c3c290ecade748c4623709a78db8a368ee0e152931c0b/spacy-3.8.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:10fffb387e6afeba582e01efd9b5e0c5431ac323af134f4a71a749255182f4c8", size = 31763223, upload-time = "2026-03-23T17:43:42.823Z" }, + { url = "https://files.pythonhosted.org/packages/4f/fa/b3a406bdc2636aaa315b8539b88f262e78ca391afc4083e3e9806953b24a/spacy-3.8.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:500f6eb9e4711e75fc07d517962dbfb553ba144ccf1b1e8ffc71b014c9ad91b2", size = 32717863, upload-time = "2026-03-23T17:43:47.939Z" }, + { url = "https://files.pythonhosted.org/packages/d9/97/11ed2a980e7225b0a1a4041cb1cba44f40bd83682a08e450dd6171f054e4/spacy-3.8.13-cp313-cp313-win_amd64.whl", hash = "sha256:1e679a8c5dd86c564a1185d894b1b8e50b52fa81bee518120fc2f86349d1879c", size = 14220413, upload-time = "2026-03-23T17:43:51.922Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e2/06633e779486f62b3ec7ddeeb4accc10fb9a356b59f5bed3771dfb89931b/spacy-3.8.13-cp313-cp313-win_arm64.whl", hash = "sha256:c086bff909d73be892b4eb6883070dd3e328592b8933f0059a6569c8c7904928", size = 13619060, upload-time = "2026-03-23T17:43:55.449Z" }, +] + +[[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 = "sphinx" version = "9.0.4" @@ -6170,23 +6500,23 @@ resolution-markers = [ "python_full_version < '3.12'", ] dependencies = [ - { name = "alabaster", marker = "python_full_version < '3.12'" }, - { name = "babel", marker = "python_full_version < '3.12'" }, - { name = "colorama", marker = "python_full_version < '3.12' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version < '3.12'" }, - { name = "imagesize", marker = "python_full_version < '3.12'" }, - { name = "jinja2", marker = "python_full_version < '3.12'" }, - { name = "packaging", marker = "python_full_version < '3.12'" }, - { name = "pygments", marker = "python_full_version < '3.12'" }, - { name = "requests", marker = "python_full_version < '3.12'" }, - { name = "roman-numerals", marker = "python_full_version < '3.12'" }, - { name = "snowballstemmer", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.12'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.12'" }, + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "roman-numerals" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" } wheels = [ @@ -6202,23 +6532,23 @@ resolution-markers = [ "python_full_version == '3.12.*'", ] dependencies = [ - { name = "alabaster", marker = "python_full_version >= '3.12'" }, - { name = "babel", marker = "python_full_version >= '3.12'" }, - { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version >= '3.12'" }, - { name = "imagesize", marker = "python_full_version >= '3.12'" }, - { name = "jinja2", marker = "python_full_version >= '3.12'" }, - { name = "packaging", marker = "python_full_version >= '3.12'" }, - { name = "pygments", marker = "python_full_version >= '3.12'" }, - { name = "requests", marker = "python_full_version >= '3.12'" }, - { name = "roman-numerals", marker = "python_full_version >= '3.12'" }, - { name = "snowballstemmer", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.12'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.12'" }, + { name = "alabaster" }, + { name = "babel" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "docutils" }, + { name = "imagesize" }, + { name = "jinja2" }, + { name = "packaging" }, + { name = "pygments" }, + { name = "requests" }, + { name = "roman-numerals" }, + { name = "snowballstemmer" }, + { name = "sphinxcontrib-applehelp" }, + { name = "sphinxcontrib-devhelp" }, + { name = "sphinxcontrib-htmlhelp" }, + { name = "sphinxcontrib-jsmath" }, + { name = "sphinxcontrib-qthelp" }, + { name = "sphinxcontrib-serializinghtml" }, ] sdist = { url = "https://files.pythonhosted.org/packages/cd/bd/f08eb0f4eed5c83f1ba2a3bd18f7745a2b1525fad70660a1c00224ec468a/sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb", size = 8718324, upload-time = "2025-12-31T15:09:27.646Z" } wheels = [ @@ -6429,6 +6759,41 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/42/89/81b2907cda14e566b9bf215e2ad82fc9b349edf07d2010756ffdb902f328/sqlite_vec-0.1.9-py3-none-win_amd64.whl", hash = "sha256:4a28dc12fa4b53d7b1dced22da2488fade444e96b5d16fd2d698cd670675cf32", size = 292804, upload-time = "2026-03-31T08:02:36.035Z" }, ] +[[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/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.4" @@ -6520,6 +6885,52 @@ 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" }, + { 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/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 = "threadpoolctl" version = "3.6.0" @@ -6578,6 +6989,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ce/34/d50e338631baaf65ec5396e70085e5de0b52b24b28db1ffbc1c6e82190dc/tinytag-2.2.1-py3-none-any.whl", hash = "sha256:ed8b1e6d25367937e3321e054f4974f9abfde1a3e0a538824c87da377130c2b6", size = 32927, upload-time = "2026-03-15T18:47:59.613Z" }, ] +[[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.23.1" @@ -6690,17 +7116,17 @@ wheels = [ [[package]] name = "typer" -version = "0.26.7" +version = "0.25.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "click" }, { name = "rich" }, { name = "shellingham" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5e/ed/ef06584ccdd5c410df0837951ecd7e15d9a6144ea1bd4c73cecab1a89891/typer-0.26.7.tar.gz", hash = "sha256:e314a34c617e419c091b2830dda3ea1f257134ff593061a8f5b9717ab8dddb3a", size = 201709, upload-time = "2026-06-03T07:18:06.843Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/51/9aed62104cea109b820bbd6c14245af756112017d309da813ef107d42e7e/typer-0.25.1.tar.gz", hash = "sha256:9616eb8853a09ffeabab1698952f33c6f29ffdbceb4eaeecf571880e8d7664cc", size = 122276, upload-time = "2026-04-30T19:32:16.964Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/25/2201973529af2c954de0bb725323c3aaed6d7f0ceee8f550dec9185df013/typer-0.26.7-py3-none-any.whl", hash = "sha256:5c87cfbc5d34491c5346ebf49c23e18d56ccb863268d3a8d592b26087c2f5e58", size = 122456, upload-time = "2026-06-03T07:18:05.732Z" }, + { url = "https://files.pythonhosted.org/packages/3f/f9/2b3ff4e56e5fa7debfaf9eb135d0da96f3e9a1d5b27222223c7296336e5f/typer-0.25.1-py3-none-any.whl", hash = "sha256:75caa44ed46a03fb2dab8808753ffacdbfea88495e74c85a28c5eefcf5f39c89", size = 58409, upload-time = "2026-04-30T19:32:18.271Z" }, ] [[package]] @@ -6922,6 +7348,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bf/c4/557dc082be035381b85fdb2b74e21d3d21b57750b74f2b47a32f3a639ff9/virtualenv-21.4.2-py3-none-any.whl", hash = "sha256:854210ca524a1a4d0d744734f4acbc721c3ffe163b85bbf5d56d14d5ae2f0fae", size = 7594079, upload-time = "2026-05-31T17:01:20.735Z" }, ] +[[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" @@ -7038,6 +7476,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bd/6e/95b0e537de1f4d4301f76f944642c6da50d1511cc7b3d64dc418a66c7509/wcwidth-0.8.1-py3-none-any.whl", hash = "sha256:f453740b1e4a4f3291faa37944c555d71056c4da08d59809b307ef4feba695c8", size = 323092, upload-time = "2026-06-08T05:57:21.413Z" }, ] +[[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 = "websocket-client" version = "1.9.0"