rasa-pro 3.8.17__py3-none-any.whl → 3.8.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rasa-pro might be problematic. Click here for more details.

README.md CHANGED
@@ -236,6 +236,39 @@ To check the types execute
236
236
  make types
237
237
  ```
238
238
 
239
+ ### Backporting
240
+
241
+ In order to port changes to `main` and across release branches, we use the `backport` workflow located at
242
+ the `.github/workflows/backport.yml` path.
243
+ This workflow is triggered by the `backport-to-<release-branch>` label applied to a PR, for example `backport-to-3.8.x`.
244
+ Current available target branches are `main` and maintained release branches.
245
+
246
+ When a PR gets labelled `backport-to-<release-branch>`, a PR is opened by the `backport-github-action` as soon as the
247
+ source PR gets closed (by merging). If you want to close the PR without merging changes, make sure to remove the `backport-to-<release-branch>` label.
248
+
249
+ The PR author which the action assigns to the backporting PR has to resolve any conflicts before approving and merging.
250
+ Release PRs should also be labelled with `backport-to-main` to backport the `CHANGELOG.md` updates to `main`.
251
+ Backporting version updates should be accepted to the `main` branch from the latest release branch only.
252
+
253
+ Here are some guidelines to follow when backporting changes and resolving conflicts:
254
+
255
+ a) for conflicts in `version.py`: accept only the version from the latest release branch. Do not merge version changes
256
+ from earlier release branches into `main` because this could cause issues when trying to make the next minor release.
257
+
258
+ b) for conflicts in `pyproject.toml`: if related to the `rasa-pro` version, accept only the latest release branch;
259
+ if related to other dependencies, accept `main` or whichever is the higher upgrade (main usually has the updated
260
+ dependencies because we only do housekeeping on `main`, apart from vulnerability updates). Be mindful of dependencies that
261
+ are removed from `main` but still exist in former release branches (for example `langchain`).
262
+
263
+ c) for conflicts in `poetry.lock`: accept changes which were already present on the target branch, then run
264
+ `poetry lock --no-update` so that the lock file contains your changes from `pyproject.toml` too.
265
+
266
+ d) for conflicts in `CHANGELOG.md`: Manually place the changelog in their allocated section (e.g. 3.8.10 will go under the
267
+ 3.8 section with the other releases, rather than go at the top of the file)
268
+
269
+ If the backporting workflow fails, you are encouraged to cherry-pick the commits manually and create a PR to
270
+ the target branch. Alternatively, you can install the backporting CLI tool as described [here](https://github.com/sorenlouv/backport?tab=readme-ov-file#install).
271
+
239
272
  ## Releases
240
273
  Rasa has implemented robust policies governing version naming, as well as release pace for major, minor, and patch releases.
241
274
 
@@ -318,9 +351,12 @@ Releasing a new version is quite simple, as the packages are build and distribut
318
351
  9. If however an error occurs in the build, then we should see a failure message automatically posted in the company's Slack (`dev-tribe` channel) like this [one](https://rasa-hq.slack.com/archives/C01M5TAHDHA/p1701444735622919)
319
352
  (In this case do the following checks):
320
353
  - Check the workflows in [Github Actions](https://github.com/RasaHQ/rasa-private/actions) and make sure that the merged PR of the current release is completed successfully. To easily find your PR you can use the filters `event: push` and `branch: <version number>` (example on release 2.4 you can see [here](https://github.com/RasaHQ/rasa/actions/runs/643344876))
321
- - If the workflow is not completed, then try to re run the workflow in case that solves the problem
354
+ - If the workflow is not completed, then try to re-run the workflow in case that solves the problem
322
355
  - If the problem persists, check also the log files and try to find the root cause of the issue
323
356
  - If you still cannot resolve the error, contact the infrastructure team by providing any helpful information from your investigation
357
+ 10. If the release is successful, add the newly created release branch to the backporting configuration in the `.backportrc.json` file to
358
+ the `targetBranchesChoices` list. This is necessary for the backporting workflow to work correctly with new release branches.
359
+
324
360
 
325
361
  ### Cutting a Patch release
326
362
 
rasa/constants.py CHANGED
@@ -18,7 +18,7 @@ CONFIG_TELEMETRY_ID = "rasa_user_id"
18
18
  CONFIG_TELEMETRY_ENABLED = "enabled"
19
19
  CONFIG_TELEMETRY_DATE = "date"
20
20
 
21
- MINIMUM_COMPATIBLE_VERSION = "3.7.0"
21
+ MINIMUM_COMPATIBLE_VERSION = "3.8.18"
22
22
 
23
23
  GLOBAL_USER_CONFIG_PATH = os.path.expanduser("~/.config/rasa/global.yml")
24
24
 
@@ -1,7 +1,8 @@
1
1
  import logging
2
+ from typing import List, Optional, Dict, Text, Set, Any
3
+
2
4
  import numpy as np
3
5
  import scipy.sparse
4
- from typing import List, Optional, Dict, Text, Set, Any
5
6
 
6
7
  from rasa.core.featurizers.precomputation import MessageContainerForCoreFeaturization
7
8
  from rasa.nlu.extractors.extractor import EntityTagSpec
@@ -362,6 +363,26 @@ class SingleStateFeaturizer:
362
363
  for action in domain.action_names_or_texts
363
364
  ]
364
365
 
366
+ def to_dict(self) -> Dict[str, Any]:
367
+ return {
368
+ "action_texts": self.action_texts,
369
+ "entity_tag_specs": self.entity_tag_specs,
370
+ "feature_states": self._default_feature_states,
371
+ }
372
+
373
+ @classmethod
374
+ def create_from_dict(
375
+ cls, data: Dict[str, Any]
376
+ ) -> Optional["SingleStateFeaturizer"]:
377
+ if not data:
378
+ return None
379
+
380
+ featurizer = SingleStateFeaturizer()
381
+ featurizer.action_texts = data["action_texts"]
382
+ featurizer._default_feature_states = data["feature_states"]
383
+ featurizer.entity_tag_specs = data["entity_tag_specs"]
384
+ return featurizer
385
+
365
386
 
366
387
  class IntentTokenizerSingleStateFeaturizer(SingleStateFeaturizer):
367
388
  """A SingleStateFeaturizer for use with policies that predict intent labels."""
@@ -1,11 +1,9 @@
1
1
  from __future__ import annotations
2
- from pathlib import Path
3
- from collections import defaultdict
4
- from abc import abstractmethod
5
- import jsonpickle
6
- import logging
7
2
 
8
- from tqdm import tqdm
3
+ import logging
4
+ from abc import abstractmethod
5
+ from collections import defaultdict
6
+ from pathlib import Path
9
7
  from typing import (
10
8
  Tuple,
11
9
  List,
@@ -18,25 +16,30 @@ from typing import (
18
16
  Set,
19
17
  DefaultDict,
20
18
  cast,
19
+ Type,
20
+ Callable,
21
+ ClassVar,
21
22
  )
23
+
22
24
  import numpy as np
25
+ from tqdm import tqdm
23
26
 
24
- from rasa.core.featurizers.single_state_featurizer import SingleStateFeaturizer
25
- from rasa.core.featurizers.precomputation import MessageContainerForCoreFeaturization
26
- from rasa.core.exceptions import InvalidTrackerFeaturizerUsageError
27
27
  import rasa.shared.core.trackers
28
28
  import rasa.shared.utils.io
29
- from rasa.shared.nlu.constants import TEXT, INTENT, ENTITIES, ACTION_NAME
30
- from rasa.shared.nlu.training_data.features import Features
31
- from rasa.shared.core.trackers import DialogueStateTracker
32
- from rasa.shared.core.domain import State, Domain
33
- from rasa.shared.core.events import Event, ActionExecuted, UserUttered
29
+ from rasa.core.exceptions import InvalidTrackerFeaturizerUsageError
30
+ from rasa.core.featurizers.precomputation import MessageContainerForCoreFeaturization
31
+ from rasa.core.featurizers.single_state_featurizer import SingleStateFeaturizer
34
32
  from rasa.shared.core.constants import (
35
33
  USER,
36
34
  ACTION_UNLIKELY_INTENT_NAME,
37
35
  PREVIOUS_ACTION,
38
36
  )
37
+ from rasa.shared.core.domain import State, Domain
38
+ from rasa.shared.core.events import Event, ActionExecuted, UserUttered
39
+ from rasa.shared.core.trackers import DialogueStateTracker
39
40
  from rasa.shared.exceptions import RasaException
41
+ from rasa.shared.nlu.constants import TEXT, INTENT, ENTITIES, ACTION_NAME
42
+ from rasa.shared.nlu.training_data.features import Features
40
43
  from rasa.utils.tensorflow.constants import LABEL_PAD_ID
41
44
  from rasa.utils.tensorflow.model_data import ragged_array_to_ndarray
42
45
 
@@ -64,6 +67,10 @@ class InvalidStory(RasaException):
64
67
  class TrackerFeaturizer:
65
68
  """Base class for actual tracker featurizers."""
66
69
 
70
+ # Class registry to store all subclasses
71
+ _registry: ClassVar[Dict[str, Type["TrackerFeaturizer"]]] = {}
72
+ _featurizer_type: str = "TrackerFeaturizer"
73
+
67
74
  def __init__(
68
75
  self, state_featurizer: Optional[SingleStateFeaturizer] = None
69
76
  ) -> None:
@@ -74,6 +81,36 @@ class TrackerFeaturizer:
74
81
  """
75
82
  self.state_featurizer = state_featurizer
76
83
 
84
+ @classmethod
85
+ def register(cls, featurizer_type: str) -> Callable:
86
+ """Decorator to register featurizer subclasses."""
87
+
88
+ def wrapper(subclass: Type["TrackerFeaturizer"]) -> Type["TrackerFeaturizer"]:
89
+ cls._registry[featurizer_type] = subclass
90
+ # Store the type identifier in the class for serialization
91
+ subclass._featurizer_type = featurizer_type
92
+ return subclass
93
+
94
+ return wrapper
95
+
96
+ @classmethod
97
+ def from_dict(cls, data: Dict[str, Any]) -> "TrackerFeaturizer":
98
+ """Create featurizer instance from dictionary."""
99
+ featurizer_type = data.pop("type")
100
+
101
+ if featurizer_type not in cls._registry:
102
+ raise ValueError(f"Unknown featurizer type: {featurizer_type}")
103
+
104
+ # Get the correct subclass and instantiate it
105
+ subclass = cls._registry[featurizer_type]
106
+ return subclass.create_from_dict(data)
107
+
108
+ @classmethod
109
+ @abstractmethod
110
+ def create_from_dict(cls, data: Dict[str, Any]) -> "TrackerFeaturizer":
111
+ """Each subclass must implement its own creation from dict method."""
112
+ pass
113
+
77
114
  @staticmethod
78
115
  def _create_states(
79
116
  tracker: DialogueStateTracker,
@@ -465,9 +502,7 @@ class TrackerFeaturizer:
465
502
  self.state_featurizer.entity_tag_specs = []
466
503
 
467
504
  # noinspection PyTypeChecker
468
- rasa.shared.utils.io.write_text_file(
469
- str(jsonpickle.encode(self)), featurizer_file
470
- )
505
+ rasa.shared.utils.io.dump_obj_as_json_to_file(featurizer_file, self.to_dict())
471
506
 
472
507
  @staticmethod
473
508
  def load(path: Union[Text, Path]) -> Optional[TrackerFeaturizer]:
@@ -481,7 +516,17 @@ class TrackerFeaturizer:
481
516
  """
482
517
  featurizer_file = Path(path) / FEATURIZER_FILE
483
518
  if featurizer_file.is_file():
484
- return jsonpickle.decode(rasa.shared.utils.io.read_file(featurizer_file))
519
+ data = rasa.shared.utils.io.read_json_file(featurizer_file)
520
+
521
+ if "type" not in data:
522
+ logger.error(
523
+ f"Couldn't load featurizer for policy. "
524
+ f"File '{featurizer_file}' does not contain all "
525
+ f"necessary information. 'type' is missing."
526
+ )
527
+ return None
528
+
529
+ return TrackerFeaturizer.from_dict(data)
485
530
 
486
531
  logger.error(
487
532
  f"Couldn't load featurizer for policy. "
@@ -508,7 +553,16 @@ class TrackerFeaturizer:
508
553
  )
509
554
  ]
510
555
 
556
+ def to_dict(self) -> Dict[str, Any]:
557
+ return {
558
+ "type": self.__class__._featurizer_type,
559
+ "state_featurizer": (
560
+ self.state_featurizer.to_dict() if self.state_featurizer else None
561
+ ),
562
+ }
563
+
511
564
 
565
+ @TrackerFeaturizer.register("FullDialogueTrackerFeaturizer")
512
566
  class FullDialogueTrackerFeaturizer(TrackerFeaturizer):
513
567
  """Creates full dialogue training data for time distributed architectures.
514
568
 
@@ -646,7 +700,20 @@ class FullDialogueTrackerFeaturizer(TrackerFeaturizer):
646
700
 
647
701
  return trackers_as_states
648
702
 
703
+ def to_dict(self) -> Dict[str, Any]:
704
+ return super().to_dict()
649
705
 
706
+ @classmethod
707
+ def create_from_dict(cls, data: Dict[str, Any]) -> "FullDialogueTrackerFeaturizer":
708
+ state_featurizer = SingleStateFeaturizer.create_from_dict(
709
+ data["state_featurizer"]
710
+ )
711
+ return cls(
712
+ state_featurizer,
713
+ )
714
+
715
+
716
+ @TrackerFeaturizer.register("MaxHistoryTrackerFeaturizer")
650
717
  class MaxHistoryTrackerFeaturizer(TrackerFeaturizer):
651
718
  """Truncates the tracker history into `max_history` long sequences.
652
719
 
@@ -887,7 +954,25 @@ class MaxHistoryTrackerFeaturizer(TrackerFeaturizer):
887
954
 
888
955
  return trackers_as_states
889
956
 
957
+ def to_dict(self) -> Dict[str, Any]:
958
+ data = super().to_dict()
959
+ data.update(
960
+ {
961
+ "remove_duplicates": self.remove_duplicates,
962
+ "max_history": self.max_history,
963
+ }
964
+ )
965
+ return data
966
+
967
+ @classmethod
968
+ def create_from_dict(cls, data: Dict[str, Any]) -> "MaxHistoryTrackerFeaturizer":
969
+ state_featurizer = SingleStateFeaturizer.create_from_dict(
970
+ data["state_featurizer"]
971
+ )
972
+ return cls(state_featurizer, data["max_history"], data["remove_duplicates"])
890
973
 
974
+
975
+ @TrackerFeaturizer.register("IntentMaxHistoryTrackerFeaturizer")
891
976
  class IntentMaxHistoryTrackerFeaturizer(MaxHistoryTrackerFeaturizer):
892
977
  """Truncates the tracker history into `max_history` long sequences.
893
978
 
@@ -1166,6 +1251,18 @@ class IntentMaxHistoryTrackerFeaturizer(MaxHistoryTrackerFeaturizer):
1166
1251
 
1167
1252
  return trackers_as_states
1168
1253
 
1254
+ def to_dict(self) -> Dict[str, Any]:
1255
+ return super().to_dict()
1256
+
1257
+ @classmethod
1258
+ def create_from_dict(
1259
+ cls, data: Dict[str, Any]
1260
+ ) -> "IntentMaxHistoryTrackerFeaturizer":
1261
+ state_featurizer = SingleStateFeaturizer.create_from_dict(
1262
+ data["state_featurizer"]
1263
+ )
1264
+ return cls(state_featurizer, data["max_history"], data["remove_duplicates"])
1265
+
1169
1266
 
1170
1267
  def _is_prev_action_unlikely_intent_in_state(state: State) -> bool:
1171
1268
  prev_action_name = state.get(PREVIOUS_ACTION, {}).get(ACTION_NAME)
@@ -1,15 +1,15 @@
1
1
  from __future__ import annotations
2
- import logging
3
2
 
4
- from rasa.engine.recipes.default_recipe import DefaultV1Recipe
3
+ import logging
5
4
  from pathlib import Path
6
5
  from collections import defaultdict
7
6
  import contextlib
7
+ from typing import Any, List, Optional, Text, Dict, Tuple, Union, Type
8
8
 
9
9
  import numpy as np
10
10
  import tensorflow as tf
11
- from typing import Any, List, Optional, Text, Dict, Tuple, Union, Type
12
11
 
12
+ from rasa.engine.recipes.default_recipe import DefaultV1Recipe
13
13
  from rasa.engine.graph import ExecutionContext
14
14
  from rasa.engine.storage.resource import Resource
15
15
  from rasa.engine.storage.storage import ModelStorage
@@ -49,18 +49,22 @@ from rasa.shared.core.generator import TrackerWithCachedStates
49
49
  from rasa.shared.core.events import EntitiesAdded, Event
50
50
  from rasa.shared.core.domain import Domain
51
51
  from rasa.shared.nlu.training_data.message import Message
52
- from rasa.shared.nlu.training_data.features import Features
52
+ from rasa.shared.nlu.training_data.features import (
53
+ Features,
54
+ save_features,
55
+ load_features,
56
+ )
53
57
  import rasa.shared.utils.io
54
58
  import rasa.utils.io
55
59
  from rasa.utils import train_utils
56
- from rasa.utils.tensorflow.models import RasaModel, TransformerRasaModel
57
- from rasa.utils.tensorflow import rasa_layers
58
- from rasa.utils.tensorflow.model_data import (
59
- RasaModelData,
60
- FeatureSignature,
60
+ from rasa.utils.tensorflow.feature_array import (
61
61
  FeatureArray,
62
- Data,
62
+ serialize_nested_feature_arrays,
63
+ deserialize_nested_feature_arrays,
63
64
  )
65
+ from rasa.utils.tensorflow.models import RasaModel, TransformerRasaModel
66
+ from rasa.utils.tensorflow import rasa_layers
67
+ from rasa.utils.tensorflow.model_data import RasaModelData, FeatureSignature, Data
64
68
  from rasa.utils.tensorflow.model_data_utils import convert_to_data_format
65
69
  from rasa.utils.tensorflow.constants import (
66
70
  LABEL,
@@ -961,22 +965,32 @@ class TEDPolicy(Policy):
961
965
  model_path: Path where model is to be persisted
962
966
  """
963
967
  model_filename = self._metadata_filename()
964
- rasa.utils.io.json_pickle(
965
- model_path / f"{model_filename}.priority.pkl", self.priority
966
- )
967
- rasa.utils.io.pickle_dump(
968
- model_path / f"{model_filename}.meta.pkl", self.config
968
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
969
+ model_path / f"{model_filename}.priority.json", self.priority
969
970
  )
970
- rasa.utils.io.pickle_dump(
971
- model_path / f"{model_filename}.data_example.pkl", self.data_example
971
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
972
+ model_path / f"{model_filename}.meta.json", self.config
972
973
  )
973
- rasa.utils.io.pickle_dump(
974
- model_path / f"{model_filename}.fake_features.pkl", self.fake_features
974
+ # save data example
975
+ serialize_nested_feature_arrays(
976
+ self.data_example,
977
+ str(model_path / f"{model_filename}.data_example.st"),
978
+ str(model_path / f"{model_filename}.data_example_metadata.json"),
975
979
  )
976
- rasa.utils.io.pickle_dump(
977
- model_path / f"{model_filename}.label_data.pkl",
980
+ # save label data
981
+ serialize_nested_feature_arrays(
978
982
  dict(self._label_data.data) if self._label_data is not None else {},
983
+ str(model_path / f"{model_filename}.label_data.st"),
984
+ str(model_path / f"{model_filename}.label_data_metadata.json"),
985
+ )
986
+ # save fake features
987
+ metadata = save_features(
988
+ self.fake_features, str(model_path / f"{model_filename}.fake_features.st")
989
+ )
990
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
991
+ model_path / f"{model_filename}.fake_features_metadata.json", metadata
979
992
  )
993
+
980
994
  entity_tag_specs = (
981
995
  [tag_spec._asdict() for tag_spec in self._entity_tag_specs]
982
996
  if self._entity_tag_specs
@@ -994,18 +1008,29 @@ class TEDPolicy(Policy):
994
1008
  model_path: Path where model is to be persisted.
995
1009
  """
996
1010
  tf_model_file = model_path / f"{cls._metadata_filename()}.tf_model"
997
- loaded_data = rasa.utils.io.pickle_load(
998
- model_path / f"{cls._metadata_filename()}.data_example.pkl"
1011
+
1012
+ # load data example
1013
+ loaded_data = deserialize_nested_feature_arrays(
1014
+ str(model_path / f"{cls._metadata_filename()}.data_example.st"),
1015
+ str(model_path / f"{cls._metadata_filename()}.data_example_metadata.json"),
999
1016
  )
1000
- label_data = rasa.utils.io.pickle_load(
1001
- model_path / f"{cls._metadata_filename()}.label_data.pkl"
1017
+ # load label data
1018
+ loaded_label_data = deserialize_nested_feature_arrays(
1019
+ str(model_path / f"{cls._metadata_filename()}.label_data.st"),
1020
+ str(model_path / f"{cls._metadata_filename()}.label_data_metadata.json"),
1002
1021
  )
1003
- fake_features = rasa.utils.io.pickle_load(
1004
- model_path / f"{cls._metadata_filename()}.fake_features.pkl"
1022
+ label_data = RasaModelData(data=loaded_label_data)
1023
+
1024
+ # load fake features
1025
+ metadata = rasa.shared.utils.io.read_json_file(
1026
+ model_path / f"{cls._metadata_filename()}.fake_features_metadata.json"
1005
1027
  )
1006
- label_data = RasaModelData(data=label_data)
1007
- priority = rasa.utils.io.json_unpickle(
1008
- model_path / f"{cls._metadata_filename()}.priority.pkl"
1028
+ fake_features = load_features(
1029
+ str(model_path / f"{cls._metadata_filename()}.fake_features.st"), metadata
1030
+ )
1031
+
1032
+ priority = rasa.shared.utils.io.read_json_file(
1033
+ model_path / f"{cls._metadata_filename()}.priority.json"
1009
1034
  )
1010
1035
  entity_tag_specs = rasa.shared.utils.io.read_json_file(
1011
1036
  model_path / f"{cls._metadata_filename()}.entity_tag_specs.json"
@@ -1023,8 +1048,8 @@ class TEDPolicy(Policy):
1023
1048
  )
1024
1049
  for tag_spec in entity_tag_specs
1025
1050
  ]
1026
- model_config = rasa.utils.io.pickle_load(
1027
- model_path / f"{cls._metadata_filename()}.meta.pkl"
1051
+ model_config = rasa.shared.utils.io.read_json_file(
1052
+ model_path / f"{cls._metadata_filename()}.meta.json"
1028
1053
  )
1029
1054
 
1030
1055
  return {
@@ -1070,7 +1095,7 @@ class TEDPolicy(Policy):
1070
1095
  ) -> TEDPolicy:
1071
1096
  featurizer = TrackerFeaturizer.load(model_path)
1072
1097
 
1073
- if not (model_path / f"{cls._metadata_filename()}.data_example.pkl").is_file():
1098
+ if not (model_path / f"{cls._metadata_filename()}.data_example.st").is_file():
1074
1099
  return cls(
1075
1100
  config,
1076
1101
  model_storage,
@@ -5,6 +5,7 @@ from typing import Any, List, Optional, Text, Dict, Type, Union
5
5
 
6
6
  import numpy as np
7
7
  import tensorflow as tf
8
+
8
9
  import rasa.utils.common
9
10
  from rasa.engine.graph import ExecutionContext
10
11
  from rasa.engine.recipes.default_recipe import DefaultV1Recipe
@@ -16,6 +17,7 @@ from rasa.shared.core.domain import Domain
16
17
  from rasa.shared.core.trackers import DialogueStateTracker
17
18
  from rasa.shared.core.constants import SLOTS, ACTIVE_LOOP, ACTION_UNLIKELY_INTENT_NAME
18
19
  from rasa.shared.core.events import UserUttered, ActionExecuted
20
+ import rasa.shared.utils.io
19
21
  from rasa.shared.nlu.constants import (
20
22
  INTENT,
21
23
  TEXT,
@@ -103,8 +105,6 @@ from rasa.utils.tensorflow.constants import (
103
105
  )
104
106
  from rasa.utils.tensorflow import layers
105
107
  from rasa.utils.tensorflow.model_data import RasaModelData, FeatureArray, Data
106
-
107
- import rasa.utils.io as io_utils
108
108
  from rasa.core.exceptions import RasaCoreException
109
109
  from rasa.shared.utils import common
110
110
 
@@ -882,9 +882,12 @@ class UnexpecTEDIntentPolicy(TEDPolicy):
882
882
  model_path: Path where model is to be persisted
883
883
  """
884
884
  super().persist_model_utilities(model_path)
885
- io_utils.pickle_dump(
886
- model_path / f"{self._metadata_filename()}.label_quantiles.pkl",
887
- self.label_quantiles,
885
+
886
+ from safetensors.numpy import save_file
887
+
888
+ save_file(
889
+ {str(k): np.array(v) for k, v in self.label_quantiles.items()},
890
+ model_path / f"{self._metadata_filename()}.label_quantiles.st",
888
891
  )
889
892
 
890
893
  @classmethod
@@ -895,9 +898,14 @@ class UnexpecTEDIntentPolicy(TEDPolicy):
895
898
  model_path: Path where model is to be persisted.
896
899
  """
897
900
  model_utilties = super()._load_model_utilities(model_path)
898
- label_quantiles = io_utils.pickle_load(
899
- model_path / f"{cls._metadata_filename()}.label_quantiles.pkl"
901
+
902
+ from safetensors.numpy import load_file
903
+
904
+ loaded_label_quantiles = load_file(
905
+ model_path / f"{cls._metadata_filename()}.label_quantiles.st"
900
906
  )
907
+ label_quantiles = {int(k): list(v) for k, v in loaded_label_quantiles.items()}
908
+
901
909
  model_utilties.update({"label_quantiles": label_quantiles})
902
910
  return model_utilties
903
911
 
@@ -1,18 +1,17 @@
1
1
  from __future__ import annotations
2
+
2
3
  import copy
3
4
  import logging
4
5
  from collections import defaultdict
5
6
  from pathlib import Path
6
-
7
- from rasa.exceptions import ModelNotFound
8
- from rasa.nlu.featurizers.featurizer import Featurizer
7
+ from typing import Any, Dict, List, Optional, Text, Tuple, Union, TypeVar, Type
9
8
 
10
9
  import numpy as np
11
10
  import scipy.sparse
12
11
  import tensorflow as tf
13
12
 
14
- from typing import Any, Dict, List, Optional, Text, Tuple, Union, TypeVar, Type
15
-
13
+ from rasa.exceptions import ModelNotFound
14
+ from rasa.nlu.featurizers.featurizer import Featurizer
16
15
  from rasa.engine.graph import ExecutionContext, GraphComponent
17
16
  from rasa.engine.recipes.default_recipe import DefaultV1Recipe
18
17
  from rasa.engine.storage.resource import Resource
@@ -20,18 +19,21 @@ from rasa.engine.storage.storage import ModelStorage
20
19
  from rasa.nlu.extractors.extractor import EntityExtractorMixin
21
20
  from rasa.nlu.classifiers.classifier import IntentClassifier
22
21
  import rasa.shared.utils.io
23
- import rasa.utils.io as io_utils
24
22
  import rasa.nlu.utils.bilou_utils as bilou_utils
25
23
  from rasa.shared.constants import DIAGNOSTIC_DATA
26
24
  from rasa.nlu.extractors.extractor import EntityTagSpec
27
25
  from rasa.nlu.classifiers import LABEL_RANKING_LENGTH
28
26
  from rasa.utils import train_utils
29
27
  from rasa.utils.tensorflow import rasa_layers
28
+ from rasa.utils.tensorflow.feature_array import (
29
+ FeatureArray,
30
+ serialize_nested_feature_arrays,
31
+ deserialize_nested_feature_arrays,
32
+ )
30
33
  from rasa.utils.tensorflow.models import RasaModel, TransformerRasaModel
31
34
  from rasa.utils.tensorflow.model_data import (
32
35
  RasaModelData,
33
36
  FeatureSignature,
34
- FeatureArray,
35
37
  )
36
38
  from rasa.nlu.constants import TOKENS_NAMES, DEFAULT_TRANSFORMER_SIZE
37
39
  from rasa.shared.nlu.constants import (
@@ -118,7 +120,6 @@ LABEL_SUB_KEY = IDS
118
120
 
119
121
  POSSIBLE_TAGS = [ENTITY_ATTRIBUTE_TYPE, ENTITY_ATTRIBUTE_ROLE, ENTITY_ATTRIBUTE_GROUP]
120
122
 
121
-
122
123
  DIETClassifierT = TypeVar("DIETClassifierT", bound="DIETClassifier")
123
124
 
124
125
 
@@ -1085,18 +1086,24 @@ class DIETClassifier(GraphComponent, IntentClassifier, EntityExtractorMixin):
1085
1086
 
1086
1087
  self.model.save(str(tf_model_file))
1087
1088
 
1088
- io_utils.pickle_dump(
1089
- model_path / f"{file_name}.data_example.pkl", self._data_example
1090
- )
1091
- io_utils.pickle_dump(
1092
- model_path / f"{file_name}.sparse_feature_sizes.pkl",
1093
- self._sparse_feature_sizes,
1089
+ # save data example
1090
+ serialize_nested_feature_arrays(
1091
+ self._data_example,
1092
+ model_path / f"{file_name}.data_example.st",
1093
+ model_path / f"{file_name}.data_example_metadata.json",
1094
1094
  )
1095
- io_utils.pickle_dump(
1096
- model_path / f"{file_name}.label_data.pkl",
1095
+ # save label data
1096
+ serialize_nested_feature_arrays(
1097
1097
  dict(self._label_data.data) if self._label_data is not None else {},
1098
+ model_path / f"{file_name}.label_data.st",
1099
+ model_path / f"{file_name}.label_data_metadata.json",
1098
1100
  )
1099
- io_utils.json_pickle(
1101
+
1102
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
1103
+ model_path / f"{file_name}.sparse_feature_sizes.json",
1104
+ self._sparse_feature_sizes,
1105
+ )
1106
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
1100
1107
  model_path / f"{file_name}.index_label_id_mapping.json",
1101
1108
  self.index_label_id_mapping,
1102
1109
  )
@@ -1185,15 +1192,22 @@ class DIETClassifier(GraphComponent, IntentClassifier, EntityExtractorMixin):
1185
1192
  ]:
1186
1193
  file_name = cls.__name__
1187
1194
 
1188
- data_example = io_utils.pickle_load(
1189
- model_path / f"{file_name}.data_example.pkl"
1195
+ # load data example
1196
+ data_example = deserialize_nested_feature_arrays(
1197
+ str(model_path / f"{file_name}.data_example.st"),
1198
+ str(model_path / f"{file_name}.data_example_metadata.json"),
1190
1199
  )
1191
- label_data = io_utils.pickle_load(model_path / f"{file_name}.label_data.pkl")
1192
- label_data = RasaModelData(data=label_data)
1193
- sparse_feature_sizes = io_utils.pickle_load(
1194
- model_path / f"{file_name}.sparse_feature_sizes.pkl"
1200
+ # load label data
1201
+ loaded_label_data = deserialize_nested_feature_arrays(
1202
+ str(model_path / f"{file_name}.label_data.st"),
1203
+ str(model_path / f"{file_name}.label_data_metadata.json"),
1204
+ )
1205
+ label_data = RasaModelData(data=loaded_label_data)
1206
+
1207
+ sparse_feature_sizes = rasa.shared.utils.io.read_json_file(
1208
+ model_path / f"{file_name}.sparse_feature_sizes.json"
1195
1209
  )
1196
- index_label_id_mapping = io_utils.json_unpickle(
1210
+ index_label_id_mapping = rasa.shared.utils.io.read_json_file(
1197
1211
  model_path / f"{file_name}.index_label_id_mapping.json"
1198
1212
  )
1199
1213
  entity_tag_specs = rasa.shared.utils.io.read_json_file(
@@ -1213,7 +1227,6 @@ class DIETClassifier(GraphComponent, IntentClassifier, EntityExtractorMixin):
1213
1227
  for tag_spec in entity_tag_specs
1214
1228
  ]
1215
1229
 
1216
- # jsonpickle converts dictionary keys to strings
1217
1230
  index_label_id_mapping = {
1218
1231
  int(key): value for key, value in index_label_id_mapping.items()
1219
1232
  }