Files
wehub-resource-sync dc6079821b
Docs Tests / Check for file changes (push) Has been cancelled
Docs Tests / Test Documentation (push) Has been cancelled
Docs Tests / Documentation Linting Checks (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-policies) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.10) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.8) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.9) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.10) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.8) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.9) (push) Has been cancelled
Continuous Integration / Check for file changes (push) Has been cancelled
Continuous Integration / Wait for docs tests (push) Has been cancelled
Continuous Integration / Code Quality (push) Has been cancelled
Continuous Integration / Check for changelog (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Upload coverage reports to codeclimate (push) Has been cancelled
Continuous Integration / Run Non-Sequential Integration Tests (push) Has been cancelled
Continuous Integration / Run Broker Integration Tests (push) Has been cancelled
Continuous Integration / Run Sequential Integration Tests (push) Has been cancelled
Continuous Integration / Build Docker base images and setup environment (push) Has been cancelled
Continuous Integration / Build Docker (default) (push) Has been cancelled
Continuous Integration / Build Docker (full) (push) Has been cancelled
Continuous Integration / Build Docker (mitie-en) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-de) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-en) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-it) (push) Has been cancelled
Continuous Integration / Deploy to PyPI (push) Has been cancelled
Continuous Integration / Notify Slack & Publish Release Notes (push) Has been cancelled
Publish Documentation / Evaluate release tag (push) Has been cancelled
Publish Documentation / Prebuild Docs (push) Has been cancelled
Publish Documentation / Preview Docs (push) Has been cancelled
Publish Documentation / Check for file changes (push) Has been cancelled
Publish Documentation / Publish Docs (push) Has been cancelled
Automatic PR Merger / mergepal (push) Has been cancelled
CI Github Actions / Run Tests (push) Has been cancelled
Semgrep / Semgrep Workflow Security Scan (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:47 +08:00

313 lines
12 KiB
Python

from __future__ import annotations
import dataclasses
import typing
import logging
from typing import Any, Dict, List, Optional, Text, Tuple
from rasa.engine.graph import ExecutionContext, GraphComponent
from rasa.engine.recipes.default_recipe import DefaultV1Recipe
from rasa.engine.storage.resource import Resource
from rasa.engine.storage.storage import ModelStorage
from rasa.nlu.constants import DENSE_FEATURIZABLE_ATTRIBUTES, SPACY_DOCS
from rasa.shared.nlu.training_data.message import Message
from rasa.shared.nlu.training_data.training_data import TrainingData
from rasa.nlu.model import InvalidModelError
from rasa.shared.constants import DOCS_URL_COMPONENTS
logger = logging.getLogger(__name__)
if typing.TYPE_CHECKING:
from spacy.language import Language
from spacy.tokens import Doc
@dataclasses.dataclass
class SpacyModel:
"""Wraps `SpacyNLP` output to make it fingerprintable."""
model: Language
model_name: Text
def fingerprint(self) -> Text:
"""Fingerprints the model name.
Use a static fingerprint as we assume this only changes if the model name
changes and want to avoid investigating the model in greater detail for now.
Returns:
Fingerprint for model.
"""
return str(self.model_name)
@DefaultV1Recipe.register(
[
DefaultV1Recipe.ComponentType.MODEL_LOADER,
DefaultV1Recipe.ComponentType.MESSAGE_FEATURIZER,
],
is_trainable=False,
model_from="SpacyNLP",
)
class SpacyNLP(GraphComponent):
"""Component which provides the common loaded SpaCy model to others.
This is used to avoid loading the SpaCy model multiple times. Instead the Spacy
model is only loaded once and then shared by depending components.
"""
def __init__(self, model: SpacyModel, config: Dict[Text, Any]) -> None:
"""Initializes a `SpacyNLP`."""
self._model = model
self._config = config
@staticmethod
def get_default_config() -> Dict[Text, Any]:
"""Default config."""
return {
# when retrieving word vectors, this will decide if the casing
# of the word is relevant. E.g. `hello` and `Hello` will
# retrieve the same vector, if set to `False`. For some
# applications and models it makes sense to differentiate
# between these two words, therefore setting this to `True`.
"case_sensitive": False
}
@staticmethod
def load_model(spacy_model_name: Text) -> SpacyModel:
"""Try loading the model, catching the OSError if missing."""
import spacy
if not spacy_model_name:
raise InvalidModelError(
f"Missing model configuration for `SpacyNLP` in `config.yml`.\n"
f"You must pass a model to the `SpacyNLP` component explicitly.\n"
f"For example:\n"
f"- name: SpacyNLP\n"
f" model: en_core_web_md\n"
f"More information can be found on {DOCS_URL_COMPONENTS}#spacynlp"
)
try:
language = spacy.load(spacy_model_name, disable=["parser"])
spacy_runtime_version = spacy.about.__version__
spacy_model_info = spacy.info(spacy_model_name)
spacy_model_version_req = (
spacy_model_info.get("spacy_version")
if isinstance(spacy_model_info, dict)
else ""
)
if not spacy.util.is_compatible_version(
spacy_runtime_version, spacy_model_version_req
):
raise InvalidModelError(
f"The specified model - {spacy_model_name} requires a spaCy "
f"runtime version {spacy_model_version_req} and is not compatible "
f"with the current spaCy runtime version {spacy_runtime_version}"
)
return SpacyModel(model=language, model_name=spacy_model_name)
except OSError:
raise InvalidModelError(
f"Please confirm that {spacy_model_name} is an available spaCy model. "
f"You need to download one upfront. For example:\n"
f"python -m spacy download en_core_web_md\n"
f"More information can be found on {DOCS_URL_COMPONENTS}#spacynlp"
)
@staticmethod
def required_packages() -> List[Text]:
"""Lists required dependencies (see parent class for full docstring)."""
return ["spacy"]
@classmethod
def create(
cls,
config: Dict[Text, Any],
model_storage: ModelStorage,
resource: Resource,
execution_context: ExecutionContext,
) -> SpacyNLP:
"""Creates component (see parent class for full docstring)."""
spacy_model_name = config.get("model")
logger.info(f"Trying to load SpaCy model with name '{spacy_model_name}'.")
model = cls.load_model(spacy_model_name)
cls.ensure_proper_language_model(model.model)
return cls(model, {**cls.get_default_config(), **config})
@staticmethod
def ensure_proper_language_model(nlp: Optional[Language]) -> None:
"""Checks if the SpaCy language model is properly loaded.
Raises an exception if the model is invalid.
"""
if nlp is None:
raise Exception(
"Failed to load SpaCy language model. "
"Loading the model returned 'None'."
)
if nlp.path is None:
# Spacy sets the path to `None` if
# it did not load the model from disk.
# In this case `nlp` is an unusable stub.
raise Exception(
f"Failed to load SpaCy language model for "
f"lang '{nlp.lang}'. Make sure you have downloaded the "
f"correct model (https://spacy.io/docs/usage/)."
""
)
def provide(self) -> SpacyModel:
"""Provides the loaded SpaCy model."""
return self._model
def _doc_for_text(self, model: Language, text: Text) -> Doc:
"""Makes a SpaCy doc object from a string of text."""
return model(self._preprocess_text(text))
def _preprocess_text(self, text: Optional[Text]) -> Text:
"""Processes the text before it is handled by SpaCy."""
if text is None:
# converted to empty string so that it can still be passed to spacy.
# Another option could be to neglect tokenization of the attribute of
# this example, but since we are processing in batch mode, it would
# get complex to collect all processed and neglected examples.
text = ""
if self._config.get("case_sensitive"):
return text
else:
return text.lower()
def _get_text(self, example: Dict[Text, Any], attribute: Text) -> Text:
return self._preprocess_text(example.get(attribute))
@staticmethod
def _merge_content_lists(
indexed_training_samples: List[Tuple[int, Text]],
doc_lists: List[Tuple[int, Doc]],
) -> List[Tuple[int, Doc]]:
"""Merge lists with processed Docs back into their original order."""
dct = dict(indexed_training_samples)
dct.update(doc_lists)
return sorted(dct.items())
@staticmethod
def _filter_training_samples_by_content(
indexed_training_samples: List[Tuple[int, Text]]
) -> Tuple[List[Tuple[int, Text]], List[Tuple[int, Text]]]:
"""Separates empty training samples from content bearing ones."""
docs_to_pipe = list(
filter(
lambda training_sample: training_sample[1] != "",
indexed_training_samples,
)
)
empty_docs = list(
filter(
lambda training_sample: training_sample[1] == "",
indexed_training_samples,
)
)
return docs_to_pipe, empty_docs
@staticmethod
def _process_content_bearing_samples(
model: Language, samples_to_pipe: List[Tuple[int, Text]]
) -> List[Tuple[int, Doc]]:
"""Sends content bearing training samples to SpaCy's pipe."""
docs = [
(to_pipe_sample[0], doc)
for to_pipe_sample, doc in zip(
samples_to_pipe,
[
doc
for doc in model.pipe(
[txt for _, txt in samples_to_pipe], batch_size=50
)
],
)
]
return docs
@staticmethod
def _process_non_content_bearing_samples(
model: Language, empty_samples: List[Tuple[int, Text]]
) -> List[Tuple[int, Doc]]:
"""Creates empty Doc-objects from zero-lengthed training samples strings."""
from spacy.tokens import Doc
n_docs = [
(empty_sample[0], doc)
for empty_sample, doc in zip(
empty_samples, [Doc(model.vocab) for doc in empty_samples]
)
]
return n_docs
def _docs_for_training_data(
self, model: Language, training_data: TrainingData
) -> Dict[Text, List[Any]]:
attribute_docs = {}
for attribute in DENSE_FEATURIZABLE_ATTRIBUTES:
texts = [
self._get_text(e, attribute) for e in training_data.training_examples
]
# Index and freeze indices of the training samples for preserving the order
# after processing the data.
indexed_training_samples = [(idx, text) for idx, text in enumerate(texts)]
samples_to_pipe, empty_samples = self._filter_training_samples_by_content(
indexed_training_samples
)
content_bearing_docs = self._process_content_bearing_samples(
model, samples_to_pipe
)
non_content_bearing_docs = self._process_non_content_bearing_samples(
model, empty_samples
)
attribute_document_list = self._merge_content_lists(
indexed_training_samples,
content_bearing_docs + non_content_bearing_docs,
)
# Since we only need the training samples strings,
# we create a list to get them out of the tuple.
attribute_docs[attribute] = [doc for _, doc in attribute_document_list]
return attribute_docs
def process_training_data(
self, training_data: TrainingData, model: SpacyModel
) -> TrainingData:
"""Adds SpaCy tokens and features to training data messages."""
attribute_docs = self._docs_for_training_data(model.model, training_data)
for attribute in DENSE_FEATURIZABLE_ATTRIBUTES:
for idx, example in enumerate(training_data.training_examples):
example_attribute_doc = attribute_docs[attribute][idx]
if len(example_attribute_doc):
# If length is 0, that means the initial text feature
# was None and was replaced by ''
# in preprocess method
example.set(SPACY_DOCS[attribute], example_attribute_doc)
return training_data
def process(self, messages: List[Message], model: SpacyModel) -> List[Message]:
"""Adds SpaCy tokens and features to messages."""
for message in messages:
for attribute in DENSE_FEATURIZABLE_ATTRIBUTES:
if message.get(attribute):
message.set(
SPACY_DOCS[attribute],
self._doc_for_text(model.model, message.get(attribute)),
)
return messages