项目文件夹

文件
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

556 行
17 KiB
Python

import dataclasses
import logging
import shutil
import uuid
from pathlib import Path
from typing import Dict, Text, Optional, Any, Callable
from unittest.mock import Mock
import pytest
from _pytest.logging import LogCaptureFixture
from _pytest.monkeypatch import MonkeyPatch
from sqlalchemy.exc import OperationalError
import rasa.shared.utils.io
import rasa.shared.utils.common
from rasa.engine.caching import (
LocalTrainingCache,
CACHE_LOCATION_ENV,
DEFAULT_CACHE_NAME,
CACHE_SIZE_ENV,
CACHE_DB_NAME_ENV,
TrainingCache,
)
import tests.conftest
from rasa.engine.storage.local_model_storage import LocalModelStorage
from rasa.engine.storage.resource import Resource
from rasa.engine.storage.storage import ModelStorage
@dataclasses.dataclass
class TestCacheableOutput:
value: Dict
size_in_mb: int = 0
cache_dir: Optional[Path] = dataclasses.field(default=None, compare=False)
def to_cache(self, directory: Path, model_storage: ModelStorage) -> None:
rasa.shared.utils.io.dump_obj_as_json_to_file(
directory / "cached.json", self.value
)
# Can be used to create cache results of desired size
if self.size_in_mb:
tests.conftest.create_test_file_with_size(directory, self.size_in_mb)
@classmethod
def from_cache(
cls,
node_name: Text,
directory: Path,
model_storage: ModelStorage,
output_fingerprint: Text,
) -> "TestCacheableOutput":
value = rasa.shared.utils.io.read_json_file(directory / "cached.json")
return cls(value, cache_dir=directory)
def test_cache_output(temp_cache: TrainingCache, default_model_storage: ModelStorage):
fingerprint_key = uuid.uuid4().hex
output = TestCacheableOutput({"something to cache": "dasdaasda"})
output_fingerprint = uuid.uuid4().hex
temp_cache.cache_output(
fingerprint_key, output, output_fingerprint, default_model_storage
)
assert (
temp_cache.get_cached_output_fingerprint(fingerprint_key) == output_fingerprint
)
assert (
temp_cache.get_cached_result(
output_fingerprint, "some_node", default_model_storage
)
== output
)
def test_get_cached_result_with_miss(
temp_cache: TrainingCache, default_model_storage: ModelStorage
):
# Cache something
temp_cache.cache_output(
uuid.uuid4().hex,
TestCacheableOutput({"something to cache": "dasdaasda"}),
uuid.uuid4().hex,
default_model_storage,
)
assert (
temp_cache.get_cached_result(
uuid.uuid4().hex, "some node", default_model_storage
)
is None
)
assert temp_cache.get_cached_output_fingerprint(uuid.uuid4().hex) is None
def test_get_cached_result_when_result_no_longer_available(
tmp_path: Path,
local_cache_creator: Callable[..., LocalTrainingCache],
default_model_storage: ModelStorage,
):
cache = local_cache_creator(tmp_path)
output = TestCacheableOutput({"something to cache": "dasdaasda"})
output_fingerprint = uuid.uuid4().hex
cache.cache_output(
uuid.uuid4().hex, output, output_fingerprint, default_model_storage
)
# Pretend something deleted the cache in between
for path in tmp_path.glob("*"):
if path.is_dir():
shutil.rmtree(path)
assert (
cache.get_cached_result(output_fingerprint, "some_node", default_model_storage)
is None
)
def test_cache_creates_location_if_missing(
tmp_path: Path, local_cache_creator: Callable[..., LocalTrainingCache]
):
cache_location = tmp_path / "directory does not exist yet"
_ = local_cache_creator(cache_location)
assert cache_location.is_dir()
def test_caching_something_which_is_not_cacheable(
temp_cache: TrainingCache, default_model_storage: ModelStorage
):
# Cache something
fingerprint_key = uuid.uuid4().hex
output_fingerprint_key = uuid.uuid4().hex
temp_cache.cache_output(
fingerprint_key, None, output_fingerprint_key, default_model_storage
)
# Output fingerprint was saved
assert (
temp_cache.get_cached_output_fingerprint(fingerprint_key)
== output_fingerprint_key
)
# But it's not stored to disk
assert (
temp_cache.get_cached_result(
output_fingerprint_key, "some_node", default_model_storage
)
is None
)
@pytest.mark.parametrize(
"initial_output_fingerprint, second_output_fingerprint",
[("same same same", "same same same"), ("first output", "second output")],
)
def test_cache_again(
temp_cache: TrainingCache,
default_model_storage: ModelStorage,
initial_output_fingerprint: Text,
second_output_fingerprint: Text,
):
# Cache something
fingerprint_key = uuid.uuid4().hex
temp_cache.cache_output(
fingerprint_key, None, initial_output_fingerprint, default_model_storage
)
# Pretend we are caching the same fingerprint again
# Note that it can't happen that we cache a `Cacheable` result twice as we would
# have replaced the component with a `PrecomputedValueProvider` otherwise
temp_cache.cache_output(
fingerprint_key, None, second_output_fingerprint, default_model_storage
)
assert (
temp_cache.get_cached_output_fingerprint(fingerprint_key)
== second_output_fingerprint
)
def test_caching_cacheable_fails(
tmp_path: Path,
caplog: LogCaptureFixture,
temp_cache: TrainingCache,
default_model_storage: ModelStorage,
):
fingerprint_key = uuid.uuid4().hex
# `tmp_path` is not a dict and will hence fail to be cached
# noinspection PyTypeChecker
output = TestCacheableOutput(tmp_path)
output_fingerprint = uuid.uuid4().hex
with caplog.at_level(logging.ERROR):
temp_cache.cache_output(
fingerprint_key, output, output_fingerprint, default_model_storage
)
caplog_error_records = list(
filter(
lambda x: "failed to send traces to Datadog Agent" not in x[2],
caplog.record_tuples,
)
)
assert len(caplog_error_records) == 1
assert (
temp_cache.get_cached_output_fingerprint(fingerprint_key) == output_fingerprint
)
assert (
temp_cache.get_cached_result(
output_fingerprint, "some_node", default_model_storage
)
is None
)
@pytest.mark.parametrize(
"cached_module", [Mock(side_effect=ValueError()), Mock(return_value=Dict)]
)
def test_restore_cached_output_with_invalid_module(
temp_cache: TrainingCache,
default_model_storage: ModelStorage,
monkeypatch: MonkeyPatch,
cached_module: Any,
):
output = TestCacheableOutput({"something to cache": "dasdaasda"})
output_fingerprint = uuid.uuid4().hex
temp_cache.cache_output(
uuid.uuid4().hex, output, output_fingerprint, default_model_storage
)
monkeypatch.setattr(
rasa.shared.utils.common, "class_from_module_path", cached_module
)
assert (
temp_cache.get_cached_result(
output_fingerprint, "some_node", default_model_storage
)
is None
)
def test_removing_no_longer_compatible_cache_entries(
tmp_path: Path,
monkeypatch: MonkeyPatch,
local_cache_creator: Callable[..., LocalTrainingCache],
default_model_storage: ModelStorage,
):
cache = local_cache_creator(tmp_path)
# Cache an entry including serialized output which will be incompatible later
fingerprint_key1 = uuid.uuid4().hex
output1 = TestCacheableOutput({"something to cache": "dasdaasda"})
output_fingerprint1 = uuid.uuid4().hex
cache.cache_output(
fingerprint_key1, output1, output_fingerprint1, default_model_storage
)
# Cache an entry without serialized output which will be incompatible later
fingerprint_key2 = uuid.uuid4().hex
output_fingerprint2 = uuid.uuid4().hex
cache.cache_output(
fingerprint_key2, None, output_fingerprint2, default_model_storage
)
# Cache a second entry with a newer Rasa version
monkeypatch.setattr(rasa, "__version__", "99999.9.5")
fingerprint_key3 = uuid.uuid4().hex
output3 = TestCacheableOutput({"something to cache2": "dasdaasda"})
output_fingerprint3 = uuid.uuid4().hex
cache.cache_output(
fingerprint_key3, output3, output_fingerprint3, default_model_storage
)
# Pretend we updated Rasa Open Source to a no longer compatible version
monkeypatch.setattr(rasa.engine.caching, "MINIMUM_COMPATIBLE_VERSION", "99999.8.10")
cache_run_by_future_rasa = LocalTrainingCache()
# Cached fingerprints can no longer be retrieved
assert (
cache_run_by_future_rasa.get_cached_output_fingerprint(fingerprint_key1) is None
)
assert (
cache_run_by_future_rasa.get_cached_output_fingerprint(fingerprint_key2) is None
)
assert (
cache_run_by_future_rasa.get_cached_result(
output_fingerprint1, "some_node", default_model_storage
)
is None
)
assert (
cache_run_by_future_rasa.get_cached_result(
output_fingerprint2, "some_node", default_model_storage
)
is None
)
# Entry 3 wasn't deleted from cache as it's still compatible
assert (
cache_run_by_future_rasa.get_cached_output_fingerprint(fingerprint_key3)
== output_fingerprint3
)
restored = cache_run_by_future_rasa.get_cached_result(
output_fingerprint3, "some_node", default_model_storage
)
assert isinstance(restored, TestCacheableOutput)
assert restored == output3
# Cached output of no longer compatible stuff was deleted from disk
assert set(tmp_path.glob("*")) == {
tmp_path / DEFAULT_CACHE_NAME,
restored.cache_dir,
}
def test_skip_caching_if_cache_size_is_zero(
tmp_path: Path, monkeypatch: MonkeyPatch, default_model_storage: ModelStorage
):
cache_location = tmp_path / "cache"
monkeypatch.setenv(CACHE_LOCATION_ENV, str(cache_location))
# Disable cache
monkeypatch.setenv(CACHE_SIZE_ENV, "0")
cache = LocalTrainingCache()
# Cache something
fingerprint_key1 = uuid.uuid4().hex
output1 = TestCacheableOutput({"something to cache": "dasdaasda"})
output_fingerprint1 = uuid.uuid4().hex
cache.cache_output(
fingerprint_key1, output1, output_fingerprint1, default_model_storage
)
# not even the database and no subdirectory was created ⛔️
assert list(tmp_path.glob("*")) == []
assert cache.get_cached_output_fingerprint(fingerprint_key1) is None
assert (
cache.get_cached_result(output_fingerprint1, "some_node", default_model_storage)
is None
)
def test_skip_caching_if_result_exceeds_max_size(
tmp_path: Path, monkeypatch: MonkeyPatch, default_model_storage: ModelStorage
):
monkeypatch.setenv(CACHE_LOCATION_ENV, str(tmp_path))
# Pretend we have a cache of size "1"
monkeypatch.setenv(CACHE_SIZE_ENV, "1")
cache = LocalTrainingCache()
# Cache something
fingerprint_key1 = uuid.uuid4().hex
output1 = TestCacheableOutput({"something to cache": "dasdaasda"}, size_in_mb=2)
output_fingerprint1 = uuid.uuid4().hex
cache.cache_output(
fingerprint_key1, output1, output_fingerprint1, default_model_storage
)
assert cache.get_cached_output_fingerprint(fingerprint_key1) == output_fingerprint1
assert (
cache.get_cached_result(output_fingerprint1, "some_node", default_model_storage)
is None
)
def test_delete_using_lru_if_cache_exceeds_size(
tmp_path: Path, monkeypatch: MonkeyPatch, default_model_storage: ModelStorage
):
monkeypatch.setenv(CACHE_LOCATION_ENV, str(tmp_path))
# Pretend we have a cache of certain size
monkeypatch.setenv(CACHE_SIZE_ENV, "5")
cache = LocalTrainingCache()
# Cache an item
fingerprint_key1 = uuid.uuid4().hex
output1 = TestCacheableOutput({"something to cache": "dasdaasda"}, size_in_mb=2)
output_fingerprint1 = uuid.uuid4().hex
cache.cache_output(
fingerprint_key1, output1, output_fingerprint1, default_model_storage
)
# Cache an non cacheable item to spice it up 🔥
fingerprint_key2 = uuid.uuid4().hex
output2 = TestCacheableOutput(None)
output_fingerprint2 = uuid.uuid4().hex
cache.cache_output(
fingerprint_key2, output2, output_fingerprint2, default_model_storage
)
# Cache another item
fingerprint_key3 = uuid.uuid4().hex
output3 = TestCacheableOutput({"something to cache": "dasdaasda"}, size_in_mb=2)
output_fingerprint3 = uuid.uuid4().hex
cache.cache_output(
fingerprint_key3, output3, output_fingerprint3, default_model_storage
)
# Assert both are there
for output_fingerprint in [output_fingerprint1, output_fingerprint2]:
assert cache.get_cached_result(
output_fingerprint, "some_node", default_model_storage
)
# Checkout the first item as this updates `last_used` and hence affects LRU
cache.get_cached_output_fingerprint(fingerprint_key1)
# Now store something which requires a deletion
fingerprint_key4 = uuid.uuid4().hex
output4 = TestCacheableOutput({"something to cache": "dasdaasda"}, size_in_mb=2)
output_fingerprint4 = uuid.uuid4().hex
cache.cache_output(
fingerprint_key4, output4, output_fingerprint4, default_model_storage
)
# Assert cached result 1 and 3 are there
for output_fingerprint in [output_fingerprint1, output_fingerprint4]:
assert cache.get_cached_result(
output_fingerprint, "some_node", default_model_storage
)
# Cached result 2 and 3 were deleted
assert cache.get_cached_output_fingerprint(fingerprint_key2) is None
assert (
cache.get_cached_result(output_fingerprint3, "some_node", default_model_storage)
is None
)
def test_cache_exceeds_size_but_not_in_database(
tmp_path: Path, monkeypatch: MonkeyPatch, default_model_storage: ModelStorage
):
monkeypatch.setenv(CACHE_LOCATION_ENV, str(tmp_path))
max_cache_size = 5
# Pretend we have a cache of size `max_cached_size`
monkeypatch.setenv(CACHE_SIZE_ENV, str(max_cache_size))
cache = LocalTrainingCache()
# Fill cache with something which is not in the cache metadata
sub_dir = cache._cache_location / "some dir"
sub_dir.mkdir()
# one subdirectory which needs deletion
tests.conftest.create_test_file_with_size(sub_dir, max_cache_size)
# one file which needs deletion
test_file = tests.conftest.create_test_file_with_size(
cache._cache_location, max_cache_size
)
# Cache an item
fingerprint_key = uuid.uuid4().hex
output = TestCacheableOutput({"something to cache": "dasdaasda"}, size_in_mb=2)
output_fingerprint = uuid.uuid4().hex
cache.cache_output(
fingerprint_key, output, output_fingerprint, default_model_storage
)
assert cache.get_cached_output_fingerprint(fingerprint_key) == output_fingerprint
assert cache.get_cached_result(
output_fingerprint, "some_node", default_model_storage
)
assert not sub_dir.is_dir()
assert not test_file.is_file()
def test_clean_up_of_cached_result_if_database_fails(
tmp_path: Path,
monkeypatch: MonkeyPatch,
default_model_storage: ModelStorage,
local_cache_creator: Callable[..., LocalTrainingCache],
):
database_name = "test.db"
monkeypatch.setenv(CACHE_DB_NAME_ENV, database_name)
cache = local_cache_creator(tmp_path)
# Deleting the database will cause an error when caching the result
(tmp_path / database_name).unlink()
# Cache an item
fingerprint_key = uuid.uuid4().hex
output = TestCacheableOutput({"something to cache": "dasdaasda"}, size_in_mb=2)
output_fingerprint = uuid.uuid4().hex
with pytest.raises(OperationalError):
cache.cache_output(
fingerprint_key, output, output_fingerprint, default_model_storage
)
assert list(tmp_path.glob("*")) == [tmp_path / database_name]
def test_resource_with_model_storage(
default_model_storage: ModelStorage, tmp_path: Path, temp_cache: TrainingCache
):
node_name = "some node"
resource = Resource(node_name)
test_filename = "persisted_model.json"
test_content = {"epochs": 500}
with default_model_storage.write_to(resource) as temporary_directory:
rasa.shared.utils.io.dump_obj_as_json_to_file(
temporary_directory / test_filename, test_content
)
test_fingerprint_key = uuid.uuid4().hex
test_output_fingerprint_key = uuid.uuid4().hex
temp_cache.cache_output(
test_fingerprint_key,
resource,
test_output_fingerprint_key,
default_model_storage,
)
new_model_storage_location = tmp_path / "new_model_storage"
new_model_storage_location.mkdir()
new_model_storage = LocalModelStorage(new_model_storage_location)
restored_resource = temp_cache.get_cached_result(
test_output_fingerprint_key, node_name, new_model_storage
)
assert isinstance(restored_resource, Resource)
assert restored_resource == restored_resource
with new_model_storage.read_from(restored_resource) as temporary_directory:
cached_content = rasa.shared.utils.io.read_json_file(
temporary_directory / test_filename
)
assert cached_content == test_content