rasa-pro 3.13.0.dev5__py3-none-any.whl → 3.13.0.dev8__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.

Files changed (266) hide show
  1. rasa/__main__.py +0 -3
  2. rasa/api.py +5 -1
  3. rasa/cli/arguments/default_arguments.py +13 -1
  4. rasa/cli/arguments/train.py +2 -0
  5. rasa/cli/dialogue_understanding_test.py +1 -1
  6. rasa/cli/e2e_test.py +1 -1
  7. rasa/cli/evaluate.py +2 -2
  8. rasa/cli/export.py +3 -3
  9. rasa/cli/llm_fine_tuning.py +12 -11
  10. rasa/cli/project_templates/defaults.py +133 -0
  11. rasa/cli/run.py +1 -1
  12. rasa/cli/studio/link.py +53 -0
  13. rasa/cli/studio/pull.py +78 -0
  14. rasa/cli/studio/push.py +78 -0
  15. rasa/cli/studio/studio.py +12 -0
  16. rasa/cli/studio/upload.py +8 -0
  17. rasa/cli/train.py +2 -1
  18. rasa/cli/utils.py +1 -1
  19. rasa/cli/x.py +1 -1
  20. rasa/constants.py +4 -0
  21. rasa/core/__init__.py +0 -16
  22. rasa/core/actions/action.py +5 -1
  23. rasa/core/actions/action_repeat_bot_messages.py +18 -22
  24. rasa/core/actions/action_run_slot_rejections.py +0 -1
  25. rasa/core/agent.py +18 -3
  26. rasa/core/available_endpoints.py +146 -0
  27. rasa/core/brokers/kafka.py +4 -0
  28. rasa/core/brokers/pika.py +5 -2
  29. rasa/core/brokers/sql.py +1 -1
  30. rasa/core/channels/botframework.py +2 -2
  31. rasa/core/channels/channel.py +2 -2
  32. rasa/core/channels/hangouts.py +8 -5
  33. rasa/core/channels/inspector/.eslintrc.cjs +12 -6
  34. rasa/core/channels/inspector/.prettierrc +5 -0
  35. rasa/core/channels/inspector/README.md +10 -4
  36. rasa/core/channels/inspector/dist/assets/{arc-9f75cc3b.js → arc-c4b064fc.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-7f34db23.js → blockDiagram-38ab4fdb-215b5026.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-948bab2c.js → c4Diagram-3d4e48cf-2b54a0a3.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/channel-3730f5fd.js +1 -0
  40. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-53b0dd0e.js → classDiagram-70f12bd4-daacea5f.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-fdf789e7.js → classDiagram-v2-f2320105-930d4dc2.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/clone-e847561e.js +1 -0
  43. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-87c4ece5.js → createText-2e5e7dd3-83c206ba.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-5a8b0749.js → edges-e0da2a9e-b0eb01d0.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-66da90e2.js → erDiagram-9861fffd-17586500.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-10044f05.js → flowDb-956e92f1-be2a1776.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-f338f66a.js → flowDiagram-66a62f08-c2120ebd.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-efbbfe00.js +1 -0
  49. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-b13140aa.js → flowchart-elk-definition-4a651766-a6ab5c48.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-f2b4a55a.js → ganttDiagram-c361ad54-ef613457.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-dedc298d.js → gitGraphDiagram-72cf32ee-d59185b3.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{graph-4ede11ff.js → graph-0f155405.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{index-3862675e-65549d37.js → index-3862675e-d5f1d1b7.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{index-3a23e736.js → index-47737d3a.js} +123 -123
  55. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-65439671.js → infoDiagram-f8f76790-b07d141f.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-56d03d98.js → journeyDiagram-49397b02-1936d429.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{layout-dd48f7f4.js → layout-dde8d0f3.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{line-1569ad2c.js → line-0c2c7ee0.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/{linear-48bf4935.js → linear-35dd89a4.js} +1 -1
  60. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-688504c1.js → mindmap-definition-fc14e90a-56192851.js} +1 -1
  61. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-78b6d7e6.js → pieDiagram-8a3498a8-fc21ed78.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-048b84b3.js → quadrantDiagram-120e2f19-25e98518.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-dd67f107.js → requirementDiagram-deff3bca-546ff1f5.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-8128436e.js → sankeyDiagram-04a897e0-02d8b82d.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-1a0d1461.js → sequenceDiagram-704730f1-3ca5a92e.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-46d388ed.js → stateDiagram-587899a1-128ea07c.js} +1 -1
  67. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-ea42951a.js → stateDiagram-v2-d93cdb3a-95f290af.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-7427ed0c.js → styles-6aaf32cf-4984898a.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-ff5e5a16.js → styles-9a916d00-1bf266ba.js} +1 -1
  70. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-7b3680cf.js → styles-c10674c1-60521c63.js} +1 -1
  71. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-f860f2ad.js → svgDrawCommon-08f97a94-a25b6e12.js} +1 -1
  72. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-2eebf0c8.js → timeline-definition-85554ec2-0fc086bf.js} +1 -1
  73. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-5d7f4e96.js → xychartDiagram-e933f94c-44ee592e.js} +1 -1
  74. rasa/core/channels/inspector/dist/index.html +1 -1
  75. rasa/core/channels/inspector/package.json +3 -1
  76. rasa/core/channels/inspector/src/App.tsx +91 -90
  77. rasa/core/channels/inspector/src/components/Chat.tsx +45 -41
  78. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +40 -40
  79. rasa/core/channels/inspector/src/components/DialogueInformation.tsx +57 -57
  80. rasa/core/channels/inspector/src/components/DialogueStack.tsx +36 -27
  81. rasa/core/channels/inspector/src/components/ExpandIcon.tsx +4 -4
  82. rasa/core/channels/inspector/src/components/FullscreenButton.tsx +7 -7
  83. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +28 -12
  84. rasa/core/channels/inspector/src/components/NoActiveFlow.tsx +9 -9
  85. rasa/core/channels/inspector/src/components/RasaLogo.tsx +5 -5
  86. rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +55 -60
  87. rasa/core/channels/inspector/src/components/SaraDiagrams.tsx +5 -5
  88. rasa/core/channels/inspector/src/components/Slots.tsx +22 -22
  89. rasa/core/channels/inspector/src/components/Welcome.tsx +28 -31
  90. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +245 -0
  91. rasa/core/channels/inspector/src/helpers/audio/microphone-processor.js +12 -0
  92. rasa/core/channels/inspector/src/helpers/audio/playback-processor.js +36 -0
  93. rasa/core/channels/inspector/src/helpers/conversation.ts +7 -7
  94. rasa/core/channels/inspector/src/helpers/formatters.test.ts +181 -181
  95. rasa/core/channels/inspector/src/helpers/formatters.ts +111 -111
  96. rasa/core/channels/inspector/src/helpers/utils.ts +78 -61
  97. rasa/core/channels/inspector/src/main.tsx +8 -8
  98. rasa/core/channels/inspector/src/theme/Button/Button.ts +8 -8
  99. rasa/core/channels/inspector/src/theme/Heading/Heading.ts +7 -7
  100. rasa/core/channels/inspector/src/theme/Input/Input.ts +9 -9
  101. rasa/core/channels/inspector/src/theme/Link/Link.ts +6 -6
  102. rasa/core/channels/inspector/src/theme/Modal/Modal.ts +13 -13
  103. rasa/core/channels/inspector/src/theme/Table/Table.tsx +10 -10
  104. rasa/core/channels/inspector/src/theme/Tooltip/Tooltip.ts +5 -5
  105. rasa/core/channels/inspector/src/theme/base/breakpoints.ts +7 -7
  106. rasa/core/channels/inspector/src/theme/base/colors.ts +64 -64
  107. rasa/core/channels/inspector/src/theme/base/fonts/fontFaces.css +21 -18
  108. rasa/core/channels/inspector/src/theme/base/radii.ts +8 -8
  109. rasa/core/channels/inspector/src/theme/base/shadows.ts +5 -5
  110. rasa/core/channels/inspector/src/theme/base/sizes.ts +5 -5
  111. rasa/core/channels/inspector/src/theme/base/space.ts +12 -12
  112. rasa/core/channels/inspector/src/theme/base/styles.ts +5 -5
  113. rasa/core/channels/inspector/src/theme/base/typography.ts +12 -12
  114. rasa/core/channels/inspector/src/theme/base/zIndices.ts +3 -3
  115. rasa/core/channels/inspector/src/theme/index.ts +38 -38
  116. rasa/core/channels/inspector/src/types.ts +56 -50
  117. rasa/core/channels/inspector/yarn.lock +5 -0
  118. rasa/core/channels/mattermost.py +1 -1
  119. rasa/core/channels/rasa_chat.py +2 -4
  120. rasa/core/channels/rest.py +5 -4
  121. rasa/core/channels/studio_chat.py +3 -2
  122. rasa/core/channels/vier_cvg.py +1 -2
  123. rasa/core/channels/voice_ready/audiocodes.py +35 -25
  124. rasa/core/channels/voice_stream/audiocodes.py +7 -4
  125. rasa/core/channels/voice_stream/genesys.py +2 -2
  126. rasa/core/channels/voice_stream/twilio_media_streams.py +10 -5
  127. rasa/core/channels/voice_stream/voice_channel.py +33 -22
  128. rasa/core/evaluation/marker_tracker_loader.py +1 -1
  129. rasa/core/exporter.py +1 -1
  130. rasa/core/http_interpreter.py +3 -7
  131. rasa/core/jobs.py +2 -1
  132. rasa/core/nlg/contextual_response_rephraser.py +38 -11
  133. rasa/core/nlg/generator.py +0 -1
  134. rasa/core/nlg/interpolator.py +2 -3
  135. rasa/core/nlg/summarize.py +40 -6
  136. rasa/core/persistor.py +55 -20
  137. rasa/core/policies/enterprise_search_policy.py +290 -66
  138. rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +63 -0
  139. rasa/core/policies/flow_policy.py +1 -1
  140. rasa/core/policies/flows/flow_executor.py +96 -17
  141. rasa/core/policies/intentless_policy.py +24 -16
  142. rasa/core/processor.py +106 -53
  143. rasa/core/run.py +40 -13
  144. rasa/core/tracker_stores/__init__.py +0 -0
  145. rasa/core/{auth_retry_tracker_store.py → tracker_stores/auth_retry_tracker_store.py} +5 -1
  146. rasa/core/tracker_stores/dynamo_tracker_store.py +218 -0
  147. rasa/core/tracker_stores/mongo_tracker_store.py +206 -0
  148. rasa/core/tracker_stores/redis_tracker_store.py +219 -0
  149. rasa/core/tracker_stores/sql_tracker_store.py +555 -0
  150. rasa/core/tracker_stores/tracker_store.py +805 -0
  151. rasa/core/training/interactive.py +1 -1
  152. rasa/core/utils.py +24 -91
  153. rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
  154. rasa/dialogue_understanding/coexistence/llm_based_router.py +8 -3
  155. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
  156. rasa/dialogue_understanding/commands/cancel_flow_command.py +2 -0
  157. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
  158. rasa/dialogue_understanding/commands/clarify_command.py +6 -2
  159. rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
  160. rasa/dialogue_understanding/commands/human_handoff_command.py +2 -0
  161. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -0
  162. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
  163. rasa/dialogue_understanding/commands/set_slot_command.py +11 -1
  164. rasa/dialogue_understanding/commands/skip_question_command.py +2 -0
  165. rasa/dialogue_understanding/commands/start_flow_command.py +4 -0
  166. rasa/dialogue_understanding/commands/utils.py +26 -2
  167. rasa/dialogue_understanding/generator/__init__.py +7 -1
  168. rasa/dialogue_understanding/generator/command_generator.py +4 -2
  169. rasa/dialogue_understanding/generator/command_parser.py +2 -2
  170. rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
  171. rasa/dialogue_understanding/generator/constants.py +2 -2
  172. rasa/dialogue_understanding/generator/llm_based_command_generator.py +1 -1
  173. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +78 -0
  174. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +28 -463
  175. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
  176. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +477 -0
  177. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +8 -58
  178. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +37 -25
  179. rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
  180. rasa/dialogue_understanding/processor/command_processor.py +3 -3
  181. rasa/dialogue_understanding/processor/command_processor_component.py +3 -3
  182. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
  183. rasa/dialogue_understanding/utils.py +68 -12
  184. rasa/dialogue_understanding_test/du_test_case.py +1 -1
  185. rasa/dialogue_understanding_test/du_test_runner.py +4 -22
  186. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
  187. rasa/e2e_test/e2e_test_runner.py +1 -1
  188. rasa/engine/constants.py +1 -1
  189. rasa/engine/recipes/default_recipe.py +26 -2
  190. rasa/engine/validation.py +3 -2
  191. rasa/hooks.py +2 -30
  192. rasa/keys +1 -0
  193. rasa/llm_fine_tuning/annotation_module.py +39 -9
  194. rasa/llm_fine_tuning/conversations.py +3 -0
  195. rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
  196. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +4 -2
  197. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
  198. rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
  199. rasa/llm_fine_tuning/storage.py +4 -4
  200. rasa/llm_fine_tuning/utils.py +63 -1
  201. rasa/model_manager/config.py +3 -1
  202. rasa/model_manager/model_api.py +89 -2
  203. rasa/model_manager/runner_service.py +8 -4
  204. rasa/model_manager/trainer_service.py +5 -4
  205. rasa/model_training.py +12 -3
  206. rasa/nlu/extractors/crf_entity_extractor.py +66 -16
  207. rasa/plugin.py +2 -12
  208. rasa/privacy/__init__.py +0 -0
  209. rasa/privacy/constants.py +83 -0
  210. rasa/privacy/event_broker_utils.py +77 -0
  211. rasa/privacy/privacy_config.py +281 -0
  212. rasa/privacy/privacy_config_schema.json +86 -0
  213. rasa/privacy/privacy_filter.py +340 -0
  214. rasa/privacy/privacy_manager.py +576 -0
  215. rasa/server.py +29 -4
  216. rasa/shared/constants.py +6 -0
  217. rasa/shared/core/constants.py +4 -3
  218. rasa/shared/core/domain.py +7 -0
  219. rasa/shared/core/events.py +99 -3
  220. rasa/shared/core/flows/flow.py +1 -2
  221. rasa/shared/core/flows/flows_yaml_schema.json +3 -0
  222. rasa/shared/core/flows/steps/collect.py +46 -2
  223. rasa/shared/core/slots.py +28 -0
  224. rasa/shared/exceptions.py +4 -0
  225. rasa/shared/providers/_configs/azure_openai_client_config.py +4 -0
  226. rasa/shared/providers/_configs/openai_client_config.py +4 -0
  227. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +3 -0
  228. rasa/shared/providers/llm/_base_litellm_client.py +5 -2
  229. rasa/shared/utils/llm.py +161 -6
  230. rasa/shared/utils/yaml.py +32 -0
  231. rasa/studio/data_handler.py +3 -3
  232. rasa/studio/download/download.py +37 -60
  233. rasa/studio/download/flows.py +23 -31
  234. rasa/studio/link.py +200 -0
  235. rasa/studio/pull.py +94 -0
  236. rasa/studio/push.py +131 -0
  237. rasa/studio/upload.py +117 -67
  238. rasa/telemetry.py +84 -27
  239. rasa/tracing/config.py +4 -5
  240. rasa/tracing/constants.py +19 -1
  241. rasa/tracing/instrumentation/attribute_extractors.py +11 -3
  242. rasa/tracing/instrumentation/instrumentation.py +54 -3
  243. rasa/tracing/instrumentation/metrics.py +98 -15
  244. rasa/tracing/metric_instrument_provider.py +75 -3
  245. rasa/utils/common.py +1 -27
  246. rasa/utils/licensing.py +1 -2
  247. rasa/utils/log_utils.py +1 -45
  248. rasa/validator.py +2 -8
  249. rasa/version.py +1 -1
  250. {rasa_pro-3.13.0.dev5.dist-info → rasa_pro-3.13.0.dev8.dist-info}/METADATA +8 -9
  251. {rasa_pro-3.13.0.dev5.dist-info → rasa_pro-3.13.0.dev8.dist-info}/RECORD +254 -231
  252. rasa/anonymization/__init__.py +0 -2
  253. rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
  254. rasa/anonymization/anonymization_pipeline.py +0 -286
  255. rasa/anonymization/anonymization_rule_executor.py +0 -266
  256. rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
  257. rasa/anonymization/schemas/config.yml +0 -47
  258. rasa/anonymization/utils.py +0 -118
  259. rasa/core/channels/inspector/dist/assets/channel-dfa68278.js +0 -1
  260. rasa/core/channels/inspector/dist/assets/clone-edb7f119.js +0 -1
  261. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-65e7c670.js +0 -1
  262. rasa/core/channels/inspector/src/helpers/audiostream.ts +0 -191
  263. rasa/core/tracker_store.py +0 -1792
  264. {rasa_pro-3.13.0.dev5.dist-info → rasa_pro-3.13.0.dev8.dist-info}/NOTICE +0 -0
  265. {rasa_pro-3.13.0.dev5.dist-info → rasa_pro-3.13.0.dev8.dist-info}/WHEEL +0 -0
  266. {rasa_pro-3.13.0.dev5.dist-info → rasa_pro-3.13.0.dev8.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,340 @@
1
+ import copy
2
+ import datetime
3
+ import json
4
+ import os
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ import structlog
8
+
9
+ from rasa.privacy.constants import (
10
+ DEFAULT_PII_MODEL,
11
+ ENTITIES_KEY,
12
+ ENTITY_LABEL_KEY,
13
+ GLINER_LABELS,
14
+ GLINER_MODEL_PATH_ENV_VAR_NAME,
15
+ HUGGINGFACE_CACHE_DIR_ENV_VAR_NAME,
16
+ TEXT_KEY,
17
+ VALUE_KEY,
18
+ )
19
+ from rasa.privacy.privacy_config import AnonymizationMethod, AnonymizationType
20
+ from rasa.shared.core.events import BotUttered, Event, SlotSet, UserUttered
21
+
22
+ structlogger = structlog.get_logger(__name__)
23
+
24
+
25
+ class PrivacyFilter:
26
+ """A class to anonymise sensitive information."""
27
+
28
+ def __init__(self, anonymization_rules: Dict[str, AnonymizationMethod]) -> None:
29
+ """Initialise the PrivacyFilter."""
30
+ self.anonymization_rules = anonymization_rules
31
+ self.labels = GLINER_LABELS
32
+ self.model = self._load_gliner_model()
33
+
34
+ def anonymize(
35
+ self, events: List[Event], prior_sensitive_slot_events: List[Event]
36
+ ) -> List[Event]:
37
+ """Anonymize sensitive information in the events of the current turn.
38
+
39
+ The order of priority for PII detection is:
40
+ - firstly, the slot-based approach i.e. identify any defined slots in
41
+ the anonymization rules that could have been set in this turn and
42
+ anonymise the plaintext slot values in all 3 event types
43
+ (UserUttered, BotUttered, SlotSet)
44
+ - secondly, the GLiNER model based approach i.e. identify any PII entities
45
+ and anonymise the text in UserUttered events or values of
46
+ SlotSet events that fill from_text slots.
47
+ """
48
+ anonymized_events: List[Event] = []
49
+ anonymized_slots = self._anonymize_sensitive_slots(
50
+ (events + prior_sensitive_slot_events)
51
+ )
52
+
53
+ for event in events:
54
+ anonymized_event = self._anonymize_event(event, anonymized_slots)
55
+ anonymized_events.append(anonymized_event)
56
+
57
+ return anonymized_events
58
+
59
+ @staticmethod
60
+ def _load_gliner_model() -> Optional[Any]:
61
+ """Load the GLiNER model for PII detection."""
62
+ local_model_path = os.getenv(GLINER_MODEL_PATH_ENV_VAR_NAME)
63
+ cache_dir = os.getenv(HUGGINGFACE_CACHE_DIR_ENV_VAR_NAME)
64
+ model_path = local_model_path or DEFAULT_PII_MODEL
65
+
66
+ try:
67
+ from gliner import GLiNER
68
+
69
+ return GLiNER.from_pretrained(
70
+ model_path,
71
+ cache_dir=cache_dir,
72
+ )
73
+ except ImportError:
74
+ structlogger.warning(
75
+ "rasa.privacy.privacy_filter.gliner_import_error",
76
+ event_info="Optional GLiNER library is not installed. "
77
+ "Please install it if you wish to use additional "
78
+ "PII detection to the slot based approach.",
79
+ )
80
+ return None
81
+
82
+ def _anonymize_sensitive_slots(self, events: List[Event]) -> Dict[str, SlotSet]:
83
+ """Identify and anonymize sensitive slot events.
84
+
85
+ Returns a dictionary where the keys represent a concatenation of the slot key
86
+ and its original value, and the values are the anonymized SlotSet events.
87
+ """
88
+ sensitive_slots = self._find_sensitive_slots(events)
89
+
90
+ if not sensitive_slots:
91
+ structlogger.debug("rasa.privacy.privacy_filter.no_sensitive_slots_found")
92
+ return {}
93
+
94
+ anonymized_slots = {}
95
+ for slot in sensitive_slots:
96
+ slot_value = (
97
+ slot.value if isinstance(slot.value, str) else json.dumps(slot.value)
98
+ )
99
+ anonymized_slots[f"{slot.key}:{slot_value}"] = (
100
+ self._anonymize_sensitive_slot_event(slot)
101
+ )
102
+
103
+ return anonymized_slots
104
+
105
+ def _find_sensitive_slots(self, processed_events: List[Event]) -> List[SlotSet]:
106
+ """Find all slot events that contain sensitive information.
107
+
108
+ These sensitive slots are defined in the anonymization rules and
109
+ have a non-empty value.
110
+ """
111
+ return [
112
+ copy.deepcopy(slot_event)
113
+ for slot_event in processed_events
114
+ if isinstance(slot_event, SlotSet)
115
+ and slot_event.key in self.anonymization_rules
116
+ and bool(slot_event.value)
117
+ ]
118
+
119
+ def _anonymize_sensitive_slot_event(
120
+ self,
121
+ slot_event: SlotSet,
122
+ ) -> SlotSet:
123
+ """Anonymize the sensitive slot event if it contains sensitive information.
124
+
125
+ A sensitive slot event is defined as a SlotSet event that has a key
126
+ in the anonymization rules and a non-empty value.
127
+ """
128
+ slot_value = slot_event.value
129
+ if not bool(slot_value):
130
+ return slot_event
131
+
132
+ anonymized_value = self._anonymize_value(slot_event)
133
+ slot_event.value = anonymized_value
134
+
135
+ return slot_event
136
+
137
+ def _anonymize_event(
138
+ self, event: Event, anonymized_slots: Dict[str, SlotSet]
139
+ ) -> Event:
140
+ if isinstance(event, SlotSet):
141
+ return self._anonymize_slot_event(event, anonymized_slots)
142
+ elif isinstance(event, UserUttered):
143
+ return self._anonymize_user_event(event, anonymized_slots)
144
+ elif isinstance(event, BotUttered):
145
+ return self._anonymize_bot_event(event, anonymized_slots)
146
+ else:
147
+ return event
148
+
149
+ def _anonymize_slot_event(
150
+ self,
151
+ event: SlotSet,
152
+ anonymized_slots: Dict[str, SlotSet],
153
+ ) -> SlotSet:
154
+ """Anonymize the slot event if it contains sensitive information."""
155
+ event_value = (
156
+ event.value if isinstance(event.value, str) else json.dumps(event.value)
157
+ )
158
+ # obtain the anonymized slot event, otherwise return the original event
159
+ slot_event = anonymized_slots.get(f"{event.key}:{event_value}", event)
160
+
161
+ # apply the edge case anonymization
162
+ slot_value = (
163
+ slot_event.value
164
+ if isinstance(slot_event.value, str)
165
+ else json.dumps(slot_event.value)
166
+ )
167
+ anonymized_value = self._anonymize_edge_cases(slot_value, anonymized_slots)
168
+
169
+ slot_event.value = (
170
+ anonymized_value
171
+ if isinstance(slot_event.value, str)
172
+ else json.loads(anonymized_value)
173
+ )
174
+ slot_event.anonymized_at = datetime.datetime.now(datetime.timezone.utc)
175
+ return slot_event
176
+
177
+ def _anonymize_user_event(
178
+ self,
179
+ user_event: UserUttered,
180
+ anonymized_slots: Dict[str, SlotSet],
181
+ ) -> UserUttered:
182
+ """Anonymize the user event if it contains sensitive information."""
183
+ if not user_event.text:
184
+ structlogger.debug(
185
+ "rasa.privacy.privacy_filter.user_event_no_text",
186
+ )
187
+ return user_event
188
+
189
+ original_parse_data: Dict[str, Any] = (
190
+ copy.deepcopy(user_event.parse_data) if user_event.parse_data else {}
191
+ )
192
+ anonymized_parse_data: Dict[str, Any] = {}
193
+
194
+ for key, slot in anonymized_slots.items():
195
+ original_slot_value = key.split(":", 1)[1]
196
+ anonymized_text = user_event.text.replace(original_slot_value, slot.value)
197
+ user_event.text = anonymized_text
198
+
199
+ anonymized_parse_data[TEXT_KEY] = anonymized_text
200
+ for entity in original_parse_data.get(ENTITIES_KEY, []):
201
+ entity_value = entity[VALUE_KEY]
202
+ if entity_value == original_slot_value:
203
+ anonymized_entities: List[Dict[str, Any]] = (
204
+ anonymized_parse_data.get(ENTITIES_KEY, [])
205
+ )
206
+ anonymized_entities.append({**entity, VALUE_KEY: slot.value})
207
+ anonymized_parse_data[ENTITIES_KEY] = anonymized_entities
208
+
209
+ user_event.parse_data = anonymized_parse_data # type: ignore[assignment]
210
+ user_event.text = self._anonymize_edge_cases(user_event.text, anonymized_slots)
211
+ # cover the edge case anonymization for the parse data text field
212
+ parse_data_text = user_event.parse_data.get(TEXT_KEY, "")
213
+ user_event.parse_data[TEXT_KEY] = self._anonymize_edge_cases( # type: ignore[literal-required]
214
+ parse_data_text, anonymized_slots
215
+ )
216
+
217
+ user_event.anonymized_at = datetime.datetime.now(datetime.timezone.utc)
218
+
219
+ return user_event
220
+
221
+ def _anonymize_bot_event(
222
+ self,
223
+ bot_event: BotUttered,
224
+ anonymized_slots: Dict[str, SlotSet],
225
+ ) -> BotUttered:
226
+ """Anonymize the bot event if it contains sensitive information."""
227
+ if not bot_event.text:
228
+ structlogger.debug(
229
+ "rasa.privacy.privacy_filter.bot_event_no_text",
230
+ )
231
+ return bot_event
232
+
233
+ for key, slot in anonymized_slots.items():
234
+ original_slot_value = key.split(":", 1)[1]
235
+ anonymized_text = bot_event.text.replace(original_slot_value, slot.value)
236
+ bot_event.text = anonymized_text
237
+
238
+ bot_event.text = self._anonymize_edge_cases(bot_event.text, anonymized_slots)
239
+ bot_event.anonymized_at = datetime.datetime.now(datetime.timezone.utc)
240
+ return bot_event
241
+
242
+ def _anonymize_value(self, slot: SlotSet) -> str:
243
+ """Anonymize the given slot value using the specified anonymization method."""
244
+ slot_name = slot.key
245
+ slot_value = slot.value
246
+ anonymization_method = self.anonymization_rules[slot_name]
247
+
248
+ if anonymization_method.method_type == AnonymizationType.REDACT:
249
+ return self._redact(slot_value, anonymization_method)
250
+
251
+ if anonymization_method.method_type == AnonymizationType.MASK:
252
+ return self._mask(slot_name)
253
+
254
+ # we won't reach this case, because the json schema specifies
255
+ # the allowed methods, this is to satisfy the type checker
256
+ return ""
257
+
258
+ @staticmethod
259
+ def _redact(slot_value: Any, anonymization_method: AnonymizationMethod) -> str:
260
+ """Redact the given slot value using the specified anonymization method."""
261
+ if anonymization_method.keep_left is not None:
262
+ left_part = slot_value[: anonymization_method.keep_left]
263
+ else:
264
+ left_part = ""
265
+
266
+ if anonymization_method.keep_right is not None:
267
+ right_part = slot_value[-anonymization_method.keep_right :]
268
+ else:
269
+ right_part = ""
270
+
271
+ return (
272
+ left_part
273
+ + anonymization_method.redaction_char
274
+ * (len(slot_value) - len(left_part) - len(right_part))
275
+ + right_part
276
+ )
277
+
278
+ @staticmethod
279
+ def _mask(slot_name: str) -> str:
280
+ """Mask the given slot value using the slot name."""
281
+ return f"[{slot_name.upper()}]"
282
+
283
+ @staticmethod
284
+ def _strip_square_brackets(string: str) -> str:
285
+ """Strip square brackets from the start and end of the string if present."""
286
+ if len(string) >= 2 and string[0] == "[" and string[-1] == "]":
287
+ return string[1:-1]
288
+ return string
289
+
290
+ def _anonymize_edge_cases(
291
+ self, text: str, anonymized_slots: Dict[str, SlotSet]
292
+ ) -> str:
293
+ """Anonymize edge cases in the text using GLiNER model.
294
+
295
+ This method is used to detect PII entities in the text that are not
296
+ covered by the slot-based anonymization rules. For example, when
297
+ the user message contains PII entities that are not defined as slots,
298
+ or when the slot is filled from a text input that could contain multiple
299
+ PII entities, such as a from_text slot.
300
+
301
+ This method uses the GLiNER model to predict entities in the text
302
+ and replaces them with masked values.
303
+ If the GLiNER model is not loaded, it will skip this step and return
304
+ the original text.
305
+ """
306
+ if self.model is None:
307
+ structlogger.debug(
308
+ "rasa.privacy.privacy_filter.gliner_model_not_loaded",
309
+ event_info="GLiNER model is not loaded, skipping PII detection.",
310
+ )
311
+ return text
312
+
313
+ entities = self.model.predict_entities(text, self.labels, threshold=0.85)
314
+
315
+ all_anonymized_slot_values = [
316
+ self._strip_square_brackets(str(slot.value))
317
+ for slot in anonymized_slots.values()
318
+ ]
319
+
320
+ for entity in entities:
321
+ structlogger.debug(
322
+ "rasa.privacy.privacy_filter.pii_entity_found",
323
+ entity=entity[ENTITY_LABEL_KEY],
324
+ )
325
+
326
+ entity_value = entity[TEXT_KEY]
327
+
328
+ if entity_value in all_anonymized_slot_values:
329
+ # the entity that was found is already anonymized,
330
+ # we shouldn't override the already anonymized value
331
+ # with a masked value
332
+ structlogger.debug(
333
+ "rasa.privacy.privacy_filter.pii_entity_already_anonymized",
334
+ entity=entity[ENTITY_LABEL_KEY],
335
+ )
336
+ continue
337
+
338
+ text = text.replace(entity_value, self._mask(entity[ENTITY_LABEL_KEY]))
339
+
340
+ return text