import dataclasses from typing import Text import uuid from rasa.engine.caching import TrainingCache from rasa.engine.graph import ExecutionContext, GraphNode, GraphSchema, SchemaNode from rasa.engine.storage.resource import Resource from rasa.engine.storage.storage import ModelStorage from rasa.engine.training import fingerprinting from rasa.engine.training.components import ( PrecomputedValueProvider, FingerprintComponent, FingerprintStatus, ) from tests.engine.graph_components_test_classes import CacheableText def test_cached_component_returns_value_from_cache(default_model_storage: ModelStorage): cached_output = CacheableText("Cache me!!") node = GraphNode( node_name="cached", component_class=PrecomputedValueProvider, constructor_name="create", component_config={"output": cached_output}, fn_name="get_value", inputs={}, eager=False, model_storage=default_model_storage, resource=None, execution_context=ExecutionContext(GraphSchema({}), "1"), ) node_name, returned_output = node() assert node_name == "cached" assert returned_output.text == "Cache me!!" def test_cached_component_replace_schema_node(): schema_node = SchemaNode( needs={"i1": "first_input", "i2": "second_input"}, uses=FingerprintComponent, fn="add", constructor_name="load", config={"a": 1}, eager=False, is_input=False, resource=Resource("hello"), ) PrecomputedValueProvider.replace_schema_node(schema_node, 2) assert schema_node == SchemaNode( needs={"i1": "first_input", "i2": "second_input"}, uses=PrecomputedValueProvider, fn="get_value", constructor_name="create", config={"output": 2}, eager=False, is_input=False, resource=Resource("hello"), ) def test_fingerprint_component_replace_schema_node(temp_cache: TrainingCache): schema_node = SchemaNode( needs={"i1": "first_input", "i2": "second_input"}, uses=PrecomputedValueProvider, fn="add", constructor_name="load", config={"a": 1}, eager=False, is_input=False, resource=Resource("hello"), ) FingerprintComponent.replace_schema_node(schema_node, temp_cache) assert schema_node == SchemaNode( needs={"i1": "first_input", "i2": "second_input"}, uses=FingerprintComponent, fn="run", constructor_name="create", config={ "config_of_replaced_component": {"a": 1}, "cache": temp_cache, "graph_component_class": PrecomputedValueProvider, }, eager=True, is_input=False, resource=Resource("hello"), ) @dataclasses.dataclass class FingerprintableText: text: Text def fingerprint(self) -> Text: return self.text def test_fingerprint_component_hit( default_model_storage: ModelStorage, temp_cache: TrainingCache ): cached_output = CacheableText("Cache me!!") output_fingerprint = uuid.uuid4().hex # We generate a fingerprint key that will match the one generated by the # `FingerprintComponent`. component_config = {"x": 1} fingerprint_key = fingerprinting.calculate_fingerprint_key( graph_component_class=PrecomputedValueProvider, config=component_config, inputs={ "param_1": FingerprintableText("input_1"), "param_2": FingerprintableText("input_2"), }, ) # We cache the output using this fingerprint key. temp_cache.cache_output( fingerprint_key=fingerprint_key, output=cached_output, output_fingerprint=output_fingerprint, model_storage=default_model_storage, ) # The node inputs and config match what we used to generate the fingerprint key. node = GraphNode( node_name="fingerprint_node", component_class=FingerprintComponent, constructor_name="create", component_config={ "config_of_replaced_component": component_config, "cache": temp_cache, "graph_component_class": PrecomputedValueProvider, }, fn_name="run", inputs={"param_1": "parent_node_1", "param_2": "parent_node_2"}, eager=False, model_storage=default_model_storage, resource=None, execution_context=ExecutionContext(GraphSchema({}), "1"), ) node_name, returned_output = node( ("parent_node_1", FingerprintableText("input_1")), ("parent_node_2", FingerprintStatus(is_hit=True, output_fingerprint="input_2")), ) assert node_name == "fingerprint_node" assert returned_output.is_hit is True assert returned_output.output_fingerprint == output_fingerprint assert returned_output.output_fingerprint == returned_output.fingerprint() def test_fingerprint_component_miss( default_model_storage: ModelStorage, temp_cache: TrainingCache ): component_config = {"x": 1} node = GraphNode( node_name="fingerprint_node", component_class=FingerprintComponent, constructor_name="create", component_config={ "config_of_replaced_component": component_config, "cache": temp_cache, "graph_component_class": PrecomputedValueProvider, }, fn_name="run", inputs={"param_1": "parent_node_1", "param_2": "parent_node_2"}, eager=False, model_storage=default_model_storage, resource=None, execution_context=ExecutionContext(GraphSchema({}), "1"), ) node_name, returned_output = node( ("parent_node_1", FingerprintableText("input_1")), ("parent_node_2", FingerprintStatus(is_hit=True, output_fingerprint="input_2")), ) # As we didnt add anything to the cache, it cannot be a hit. assert node_name == "fingerprint_node" assert returned_output.is_hit is False assert returned_output.output_fingerprint is None assert returned_output.fingerprint() != returned_output.output_fingerprint assert returned_output.fingerprint() != returned_output.fingerprint()