Skip to content

Commit

Permalink
allow components to add fingerprints of external data (#12778)
Browse files Browse the repository at this point in the history
* allow components to add fingerprints of external data
  • Loading branch information
twerkmeister authored Aug 30, 2023
1 parent ed62386 commit 347a287
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/12778.improvement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added additional method `fingerprint_addon` to the `GraphComponent` interface to allow inclusion of external data into the fingerprint calculation of a component
9 changes: 9 additions & 0 deletions data/test_classes/graph_component_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,12 @@ def not_supported_languages() -> Optional[List[Text]]:
def required_packages() -> List[Text]:
"""Any extra python dependencies required for this component to run."""
return []

@classmethod
def fingerprint_addon(cls, config: Dict[str, Any]) -> Optional[str]:
"""Adds additional data to the fingerprint calculation.
This is useful if a component uses external data that is not provided
by the graph.
"""
return None
9 changes: 9 additions & 0 deletions rasa/engine/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,15 @@ def required_packages() -> List[Text]:
"""Any extra python dependencies required for this component to run."""
return []

@classmethod
def fingerprint_addon(cls, config: Dict[str, Any]) -> Optional[str]:
"""Adds additional data to the fingerprint calculation.
This is useful if a component uses external data that is not provided
by the graph.
"""
return None


class GraphNodeHook(ABC):
"""Holds functionality to be run before and after a `GraphNode`."""
Expand Down
4 changes: 4 additions & 0 deletions rasa/engine/training/fingerprinting.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ def calculate_fingerprint_key(
"dependency_versions": dependency_versions,
}

fingerprint_addon = graph_component_class.fingerprint_addon(config)
if fingerprint_addon is not None:
fingerprint_data["addon"] = fingerprint_addon

fingerprint_key = rasa.shared.utils.io.deep_container_fingerprint(fingerprint_data)

logger.debug(
Expand Down
48 changes: 48 additions & 0 deletions tests/engine/training/test_fingerprinting.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import inspect
import os.path
import tempfile
from typing import Dict, Text, Any, Optional
from unittest.mock import Mock
from _pytest.monkeypatch import MonkeyPatch

import rasa.shared.utils.io
from rasa.core.policies.ted_policy import TEDPolicy
from rasa.engine.graph import ExecutionContext, GraphComponent
from rasa.engine.storage.resource import Resource
from rasa.engine.storage.storage import ModelStorage
from rasa.engine.training import fingerprinting
from rasa.nlu.classifiers.diet_classifier import DIETClassifier
from rasa.nlu.selectors.response_selector import ResponseSelector
Expand Down Expand Up @@ -76,3 +83,44 @@ def test_fingerprint_changes_due_to_changed_source(monkeypatch: MonkeyPatch):
assert key1 != key2

get_source_mock.assert_called_once_with(TEDPolicy)


def test_fingerprint_changes_when_external_file_changes():
tmp_file = tempfile.mktemp()

class MinimalComponent(GraphComponent):
@classmethod
def create(
cls,
config: Dict[Text, Any],
model_storage: ModelStorage,
resource: Resource,
execution_context: ExecutionContext,
) -> "MinimalComponent":
return MinimalComponent()

@classmethod
def fingerprint_addon(cls, config: Dict[str, Any]) -> Optional[str]:
if not os.path.exists(tmp_file):
return None
else:
return rasa.shared.utils.io.get_text_hash(open(tmp_file, "r").read())

with open(tmp_file, "w") as external_data:
external_data.write("This is a test.")

fingerprint_1 = fingerprinting.calculate_fingerprint_key(MinimalComponent, {}, {})

fingerprint_2 = fingerprinting.calculate_fingerprint_key(MinimalComponent, {}, {})

assert fingerprint_1 == fingerprint_2

# overwrite the original external data
with open(tmp_file, "w") as external_data:
external_data.write("This is a test for changes in external data.")

fingerprint_3 = fingerprinting.calculate_fingerprint_key(MinimalComponent, {}, {})

assert fingerprint_3 != fingerprint_1

os.remove(tmp_file)

1 comment on commit 347a287

@raoulvm
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, Thomas!

Please sign in to comment.