rasa-pro 3.12.21__py3-none-any.whl → 3.13.0__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 (354) hide show
  1. rasa/__main__.py +3 -4
  2. rasa/api.py +1 -1
  3. rasa/cli/dialogue_understanding_test.py +1 -1
  4. rasa/cli/e2e_test.py +1 -8
  5. rasa/cli/evaluate.py +2 -2
  6. rasa/cli/export.py +5 -3
  7. rasa/cli/inspect.py +7 -0
  8. rasa/cli/llm_fine_tuning.py +1 -1
  9. rasa/cli/project_templates/default/config.yml +5 -32
  10. rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_cancels_during_a_correction.yml +1 -1
  11. rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_changes_mind_on_a_whim.yml +1 -1
  12. rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_handle.yml +1 -1
  13. rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_name.yml +1 -1
  14. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_adds_contact_to_their_list.yml +1 -1
  15. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_lists_contacts.yml +1 -1
  16. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact.yml +1 -1
  17. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact_from_list.yml +1 -1
  18. rasa/cli/project_templates/default/endpoints.yml +18 -2
  19. rasa/cli/project_templates/defaults.py +133 -0
  20. rasa/cli/project_templates/tutorial/config.yml +1 -1
  21. rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
  22. rasa/cli/run.py +1 -1
  23. rasa/cli/scaffold.py +2 -3
  24. rasa/cli/shell.py +6 -1
  25. rasa/cli/studio/download.py +0 -22
  26. rasa/cli/studio/link.py +36 -0
  27. rasa/cli/studio/pull.py +79 -0
  28. rasa/cli/studio/push.py +78 -0
  29. rasa/cli/studio/studio.py +12 -0
  30. rasa/cli/studio/train.py +1 -5
  31. rasa/cli/studio/upload.py +6 -4
  32. rasa/cli/train.py +5 -1
  33. rasa/cli/utils.py +1 -1
  34. rasa/cli/x.py +1 -1
  35. rasa/constants.py +2 -0
  36. rasa/core/__init__.py +0 -16
  37. rasa/core/actions/action.py +43 -29
  38. rasa/core/actions/action_repeat_bot_messages.py +18 -22
  39. rasa/core/actions/action_run_slot_rejections.py +1 -2
  40. rasa/core/agent.py +24 -3
  41. rasa/core/available_endpoints.py +146 -0
  42. rasa/core/brokers/kafka.py +4 -0
  43. rasa/core/brokers/pika.py +5 -2
  44. rasa/core/brokers/sql.py +1 -1
  45. rasa/core/channels/__init__.py +3 -0
  46. rasa/core/channels/botframework.py +2 -2
  47. rasa/core/channels/channel.py +2 -2
  48. rasa/core/channels/development_inspector.py +1 -1
  49. rasa/core/channels/facebook.py +1 -4
  50. rasa/core/channels/hangouts.py +8 -5
  51. rasa/core/channels/inspector/.eslintrc.cjs +12 -6
  52. rasa/core/channels/inspector/.prettierrc +5 -0
  53. rasa/core/channels/inspector/README.md +11 -5
  54. rasa/core/channels/inspector/dist/assets/{arc-9f75cc3b.js → arc-371401b1.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-7f34db23.js → blockDiagram-38ab4fdb-3f126156.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-948bab2c.js → c4Diagram-3d4e48cf-12f22eb7.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/channel-f1efda17.js +1 -0
  58. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-53b0dd0e.js → classDiagram-70f12bd4-03b1d386.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-fdf789e7.js → classDiagram-v2-f2320105-84f69d63.js} +1 -1
  60. rasa/core/channels/inspector/dist/assets/clone-fdf164e2.js +1 -0
  61. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-87c4ece5.js → createText-2e5e7dd3-ca47fd38.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-5a8b0749.js → edges-e0da2a9e-f837ca8a.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-66da90e2.js → erDiagram-9861fffd-8717ac54.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-10044f05.js → flowDb-956e92f1-94f38b83.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-f338f66a.js → flowDiagram-66a62f08-b616f9fb.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-7d7a1629.js +1 -0
  67. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-b13140aa.js → flowchart-elk-definition-4a651766-f5d24bb8.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-f2b4a55a.js → ganttDiagram-c361ad54-b43ba8d9.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-dedc298d.js → gitGraphDiagram-72cf32ee-c3aafaa5.js} +1 -1
  70. rasa/core/channels/inspector/dist/assets/{graph-4ede11ff.js → graph-0d0a2c10.js} +1 -1
  71. rasa/core/channels/inspector/dist/assets/{index-3862675e-65549d37.js → index-3862675e-58ea0305.js} +1 -1
  72. rasa/core/channels/inspector/dist/assets/{index-3a23e736.js → index-cce6f8a1.js} +123 -123
  73. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-65439671.js → infoDiagram-f8f76790-b8f60461.js} +1 -1
  74. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-56d03d98.js → journeyDiagram-49397b02-95be5545.js} +1 -1
  75. rasa/core/channels/inspector/dist/assets/{layout-dd48f7f4.js → layout-da885b9b.js} +1 -1
  76. rasa/core/channels/inspector/dist/assets/{line-1569ad2c.js → line-f1c817d3.js} +1 -1
  77. rasa/core/channels/inspector/dist/assets/{linear-48bf4935.js → linear-d42801e6.js} +1 -1
  78. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-688504c1.js → mindmap-definition-fc14e90a-a38923a6.js} +1 -1
  79. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-78b6d7e6.js → pieDiagram-8a3498a8-ca6e71e9.js} +1 -1
  80. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-048b84b3.js → quadrantDiagram-120e2f19-b290dae9.js} +1 -1
  81. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-dd67f107.js → requirementDiagram-deff3bca-03f02ceb.js} +1 -1
  82. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-8128436e.js → sankeyDiagram-04a897e0-c49eee40.js} +1 -1
  83. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-1a0d1461.js → sequenceDiagram-704730f1-b2cd6a3d.js} +1 -1
  84. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-46d388ed.js → stateDiagram-587899a1-e53a2028.js} +1 -1
  85. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-ea42951a.js → stateDiagram-v2-d93cdb3a-e1982a03.js} +1 -1
  86. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-7427ed0c.js → styles-6aaf32cf-d0226ca5.js} +1 -1
  87. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-ff5e5a16.js → styles-9a916d00-0e21dc00.js} +1 -1
  88. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-7b3680cf.js → styles-c10674c1-9588494e.js} +1 -1
  89. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-f860f2ad.js → svgDrawCommon-08f97a94-be478d4f.js} +1 -1
  90. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-2eebf0c8.js → timeline-definition-85554ec2-74631749.js} +1 -1
  91. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-5d7f4e96.js → xychartDiagram-e933f94c-a043552f.js} +1 -1
  92. rasa/core/channels/inspector/dist/index.html +1 -1
  93. rasa/core/channels/inspector/package.json +3 -1
  94. rasa/core/channels/inspector/src/App.tsx +91 -90
  95. rasa/core/channels/inspector/src/components/Chat.tsx +45 -41
  96. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +40 -40
  97. rasa/core/channels/inspector/src/components/DialogueInformation.tsx +57 -57
  98. rasa/core/channels/inspector/src/components/DialogueStack.tsx +36 -27
  99. rasa/core/channels/inspector/src/components/ExpandIcon.tsx +4 -4
  100. rasa/core/channels/inspector/src/components/FullscreenButton.tsx +7 -7
  101. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +28 -12
  102. rasa/core/channels/inspector/src/components/NoActiveFlow.tsx +9 -9
  103. rasa/core/channels/inspector/src/components/RasaLogo.tsx +5 -5
  104. rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +55 -60
  105. rasa/core/channels/inspector/src/components/SaraDiagrams.tsx +5 -5
  106. rasa/core/channels/inspector/src/components/Slots.tsx +22 -22
  107. rasa/core/channels/inspector/src/components/Welcome.tsx +28 -31
  108. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +245 -0
  109. rasa/core/channels/inspector/src/helpers/audio/microphone-processor.js +12 -0
  110. rasa/core/channels/inspector/src/helpers/audio/playback-processor.js +36 -0
  111. rasa/core/channels/inspector/src/helpers/conversation.ts +7 -7
  112. rasa/core/channels/inspector/src/helpers/formatters.test.ts +181 -181
  113. rasa/core/channels/inspector/src/helpers/formatters.ts +111 -111
  114. rasa/core/channels/inspector/src/helpers/utils.ts +78 -61
  115. rasa/core/channels/inspector/src/main.tsx +8 -8
  116. rasa/core/channels/inspector/src/theme/Button/Button.ts +8 -8
  117. rasa/core/channels/inspector/src/theme/Heading/Heading.ts +7 -7
  118. rasa/core/channels/inspector/src/theme/Input/Input.ts +9 -9
  119. rasa/core/channels/inspector/src/theme/Link/Link.ts +6 -6
  120. rasa/core/channels/inspector/src/theme/Modal/Modal.ts +13 -13
  121. rasa/core/channels/inspector/src/theme/Table/Table.tsx +10 -10
  122. rasa/core/channels/inspector/src/theme/Tooltip/Tooltip.ts +5 -5
  123. rasa/core/channels/inspector/src/theme/base/breakpoints.ts +7 -7
  124. rasa/core/channels/inspector/src/theme/base/colors.ts +64 -64
  125. rasa/core/channels/inspector/src/theme/base/fonts/fontFaces.css +21 -18
  126. rasa/core/channels/inspector/src/theme/base/radii.ts +8 -8
  127. rasa/core/channels/inspector/src/theme/base/shadows.ts +5 -5
  128. rasa/core/channels/inspector/src/theme/base/sizes.ts +5 -5
  129. rasa/core/channels/inspector/src/theme/base/space.ts +12 -12
  130. rasa/core/channels/inspector/src/theme/base/styles.ts +5 -5
  131. rasa/core/channels/inspector/src/theme/base/typography.ts +12 -12
  132. rasa/core/channels/inspector/src/theme/base/zIndices.ts +3 -3
  133. rasa/core/channels/inspector/src/theme/index.ts +38 -38
  134. rasa/core/channels/inspector/src/types.ts +56 -50
  135. rasa/core/channels/inspector/yarn.lock +5 -0
  136. rasa/core/channels/mattermost.py +1 -1
  137. rasa/core/channels/rasa_chat.py +2 -4
  138. rasa/core/channels/rest.py +5 -4
  139. rasa/core/channels/socketio.py +56 -41
  140. rasa/core/channels/studio_chat.py +329 -68
  141. rasa/core/channels/vier_cvg.py +1 -2
  142. rasa/core/channels/voice_ready/audiocodes.py +4 -11
  143. rasa/core/channels/voice_ready/jambonz.py +5 -6
  144. rasa/core/channels/voice_ready/twilio_voice.py +13 -12
  145. rasa/core/channels/voice_ready/utils.py +22 -0
  146. rasa/core/channels/voice_stream/audiocodes.py +13 -16
  147. rasa/core/channels/voice_stream/browser_audio.py +1 -1
  148. rasa/core/channels/voice_stream/genesys.py +37 -18
  149. rasa/core/channels/voice_stream/jambonz.py +232 -0
  150. rasa/core/channels/voice_stream/tts/__init__.py +8 -0
  151. rasa/core/channels/voice_stream/twilio_media_streams.py +15 -12
  152. rasa/core/channels/voice_stream/voice_channel.py +71 -27
  153. rasa/core/concurrent_lock_store.py +24 -10
  154. rasa/core/evaluation/marker_tracker_loader.py +1 -1
  155. rasa/core/exporter.py +37 -1
  156. rasa/core/http_interpreter.py +3 -7
  157. rasa/core/information_retrieval/faiss.py +18 -11
  158. rasa/core/information_retrieval/ingestion/faq_parser.py +158 -0
  159. rasa/core/jobs.py +2 -1
  160. rasa/core/lock_store.py +151 -60
  161. rasa/core/nlg/contextual_response_rephraser.py +17 -7
  162. rasa/core/nlg/generator.py +5 -22
  163. rasa/core/nlg/interpolator.py +2 -3
  164. rasa/core/nlg/response.py +6 -43
  165. rasa/core/nlg/summarize.py +1 -1
  166. rasa/core/nlg/translate.py +0 -8
  167. rasa/core/policies/enterprise_search_policy.py +305 -189
  168. rasa/core/policies/enterprise_search_policy_config.py +241 -0
  169. rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +67 -0
  170. rasa/core/policies/flow_policy.py +1 -1
  171. rasa/core/policies/flows/flow_executor.py +102 -17
  172. rasa/core/policies/intentless_policy.py +56 -17
  173. rasa/core/processor.py +70 -49
  174. rasa/core/run.py +33 -11
  175. rasa/core/tracker_stores/__init__.py +0 -0
  176. rasa/core/{auth_retry_tracker_store.py → tracker_stores/auth_retry_tracker_store.py} +66 -1
  177. rasa/core/tracker_stores/dynamo_tracker_store.py +256 -0
  178. rasa/core/tracker_stores/mongo_tracker_store.py +223 -0
  179. rasa/core/tracker_stores/redis_tracker_store.py +252 -0
  180. rasa/core/tracker_stores/sql_tracker_store.py +582 -0
  181. rasa/core/tracker_stores/tracker_store.py +839 -0
  182. rasa/core/training/interactive.py +1 -1
  183. rasa/core/utils.py +24 -95
  184. rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
  185. rasa/dialogue_understanding/coexistence/llm_based_router.py +13 -11
  186. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
  187. rasa/dialogue_understanding/commands/cancel_flow_command.py +3 -1
  188. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
  189. rasa/dialogue_understanding/commands/clarify_command.py +6 -2
  190. rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
  191. rasa/dialogue_understanding/commands/correct_slots_command.py +5 -6
  192. rasa/dialogue_understanding/commands/error_command.py +1 -1
  193. rasa/dialogue_understanding/commands/human_handoff_command.py +3 -3
  194. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -0
  195. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
  196. rasa/dialogue_understanding/commands/set_slot_command.py +8 -4
  197. rasa/dialogue_understanding/commands/skip_question_command.py +3 -3
  198. rasa/dialogue_understanding/commands/start_flow_command.py +7 -3
  199. rasa/dialogue_understanding/generator/__init__.py +7 -1
  200. rasa/dialogue_understanding/generator/command_generator.py +4 -2
  201. rasa/dialogue_understanding/generator/command_parser.py +2 -2
  202. rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
  203. rasa/dialogue_understanding/generator/llm_based_command_generator.py +1 -2
  204. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +3 -2
  205. rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
  206. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +0 -2
  207. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -0
  208. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +1 -0
  209. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +79 -0
  210. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +79 -0
  211. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +26 -461
  212. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
  213. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +461 -0
  214. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +20 -64
  215. rasa/dialogue_understanding/patterns/cancel.py +1 -2
  216. rasa/dialogue_understanding/patterns/clarify.py +1 -1
  217. rasa/dialogue_understanding/patterns/correction.py +2 -2
  218. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +42 -27
  219. rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
  220. rasa/dialogue_understanding/processor/command_processor.py +6 -7
  221. rasa/dialogue_understanding_test/command_metric_calculation.py +7 -40
  222. rasa/dialogue_understanding_test/command_metrics.py +38 -0
  223. rasa/dialogue_understanding_test/du_test_case.py +58 -25
  224. rasa/dialogue_understanding_test/du_test_result.py +228 -132
  225. rasa/dialogue_understanding_test/du_test_runner.py +11 -2
  226. rasa/dialogue_understanding_test/du_test_schema.yml +3 -3
  227. rasa/dialogue_understanding_test/io.py +35 -8
  228. rasa/e2e_test/constants.py +1 -1
  229. rasa/e2e_test/e2e_test_runner.py +1 -1
  230. rasa/e2e_test/e2e_test_schema.yml +3 -3
  231. rasa/engine/constants.py +1 -1
  232. rasa/engine/graph.py +2 -2
  233. rasa/engine/recipes/default_recipe.py +1 -1
  234. rasa/engine/validation.py +3 -2
  235. rasa/hooks.py +2 -30
  236. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +2 -6
  237. rasa/model_manager/model_api.py +89 -1
  238. rasa/model_manager/runner_service.py +20 -4
  239. rasa/model_manager/socket_bridge.py +0 -7
  240. rasa/model_manager/trainer_service.py +10 -4
  241. rasa/plugin.py +2 -15
  242. rasa/privacy/__init__.py +0 -0
  243. rasa/privacy/constants.py +83 -0
  244. rasa/privacy/event_broker_utils.py +77 -0
  245. rasa/privacy/privacy_config.py +281 -0
  246. rasa/privacy/privacy_config_schema.json +86 -0
  247. rasa/privacy/privacy_filter.py +393 -0
  248. rasa/privacy/privacy_manager.py +594 -0
  249. rasa/server.py +23 -2
  250. rasa/shared/constants.py +17 -0
  251. rasa/shared/core/command_payload_reader.py +1 -5
  252. rasa/shared/core/constants.py +4 -3
  253. rasa/shared/core/domain.py +172 -11
  254. rasa/shared/core/events.py +100 -6
  255. rasa/shared/core/flows/flow.py +30 -5
  256. rasa/shared/core/flows/flow_step.py +19 -3
  257. rasa/shared/core/flows/flow_step_links.py +15 -0
  258. rasa/shared/core/flows/flow_step_sequence.py +6 -0
  259. rasa/shared/core/flows/flows_yaml_schema.json +3 -0
  260. rasa/shared/core/flows/nlu_trigger.py +13 -0
  261. rasa/shared/core/flows/steps/action.py +7 -4
  262. rasa/shared/core/flows/steps/call.py +11 -4
  263. rasa/shared/core/flows/steps/collect.py +71 -6
  264. rasa/shared/core/flows/steps/internal.py +6 -1
  265. rasa/shared/core/flows/steps/link.py +7 -4
  266. rasa/shared/core/flows/steps/no_operation.py +7 -4
  267. rasa/shared/core/flows/steps/set_slots.py +8 -4
  268. rasa/shared/core/flows/validation.py +25 -5
  269. rasa/shared/core/flows/yaml_flows_io.py +106 -5
  270. rasa/shared/core/slots.py +29 -1
  271. rasa/shared/core/trackers.py +21 -10
  272. rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
  273. rasa/shared/importers/importer.py +8 -0
  274. rasa/shared/providers/_configs/azure_openai_client_config.py +2 -2
  275. rasa/shared/providers/_configs/default_litellm_client_config.py +1 -1
  276. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +1 -1
  277. rasa/shared/providers/_configs/openai_client_config.py +1 -1
  278. rasa/shared/providers/_configs/rasa_llm_client_config.py +1 -1
  279. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -1
  280. rasa/shared/providers/_configs/utils.py +0 -99
  281. rasa/shared/providers/llm/default_litellm_llm_client.py +2 -2
  282. rasa/shared/utils/common.py +43 -1
  283. rasa/shared/utils/configs.py +110 -0
  284. rasa/shared/utils/constants.py +0 -3
  285. rasa/shared/utils/llm.py +245 -8
  286. rasa/shared/utils/pykwalify_extensions.py +0 -9
  287. rasa/shared/utils/yaml.py +32 -0
  288. rasa/studio/constants.py +1 -0
  289. rasa/studio/data_handler.py +33 -12
  290. rasa/studio/download.py +117 -435
  291. rasa/studio/link.py +211 -0
  292. rasa/studio/prompts.py +221 -0
  293. rasa/studio/pull/__init__.py +0 -0
  294. rasa/studio/pull/data.py +222 -0
  295. rasa/studio/pull/domains.py +60 -0
  296. rasa/studio/pull/pull.py +239 -0
  297. rasa/studio/push.py +138 -0
  298. rasa/studio/results_logger.py +6 -1
  299. rasa/studio/train.py +1 -1
  300. rasa/studio/upload.py +243 -72
  301. rasa/studio/utils.py +33 -0
  302. rasa/telemetry.py +83 -26
  303. rasa/tracing/config.py +4 -5
  304. rasa/tracing/constants.py +19 -1
  305. rasa/tracing/instrumentation/attribute_extractors.py +68 -16
  306. rasa/tracing/instrumentation/instrumentation.py +54 -3
  307. rasa/tracing/instrumentation/metrics.py +98 -15
  308. rasa/tracing/metric_instrument_provider.py +75 -3
  309. rasa/utils/common.py +43 -22
  310. rasa/utils/endpoints.py +22 -1
  311. rasa/utils/licensing.py +2 -3
  312. rasa/utils/log_utils.py +1 -45
  313. rasa/validator.py +2 -8
  314. rasa/version.py +1 -1
  315. {rasa_pro-3.12.21.dist-info → rasa_pro-3.13.0.dist-info}/METADATA +13 -14
  316. {rasa_pro-3.12.21.dist-info → rasa_pro-3.13.0.dist-info}/RECORD +333 -309
  317. rasa/anonymization/__init__.py +0 -2
  318. rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
  319. rasa/anonymization/anonymization_pipeline.py +0 -286
  320. rasa/anonymization/anonymization_rule_executor.py +0 -266
  321. rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
  322. rasa/anonymization/schemas/config.yml +0 -47
  323. rasa/anonymization/utils.py +0 -118
  324. rasa/cli/project_templates/calm/config.yml +0 -10
  325. rasa/cli/project_templates/calm/credentials.yml +0 -33
  326. rasa/cli/project_templates/calm/endpoints.yml +0 -58
  327. rasa/cli/project_templates/default/actions/actions.py +0 -27
  328. rasa/cli/project_templates/default/data/nlu.yml +0 -91
  329. rasa/cli/project_templates/default/data/rules.yml +0 -13
  330. rasa/cli/project_templates/default/data/stories.yml +0 -30
  331. rasa/cli/project_templates/default/domain.yml +0 -34
  332. rasa/cli/project_templates/default/tests/test_stories.yml +0 -91
  333. rasa/core/channels/inspector/dist/assets/channel-dfa68278.js +0 -1
  334. rasa/core/channels/inspector/dist/assets/clone-edb7f119.js +0 -1
  335. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-65e7c670.js +0 -1
  336. rasa/core/channels/inspector/src/helpers/audiostream.ts +0 -191
  337. rasa/core/tracker_store.py +0 -1792
  338. /rasa/cli/project_templates/{calm → default}/actions/action_template.py +0 -0
  339. /rasa/cli/project_templates/{calm → default}/actions/add_contact.py +0 -0
  340. /rasa/cli/project_templates/{calm → default}/actions/db.py +0 -0
  341. /rasa/cli/project_templates/{calm → default}/actions/list_contacts.py +0 -0
  342. /rasa/cli/project_templates/{calm → default}/actions/remove_contact.py +0 -0
  343. /rasa/cli/project_templates/{calm → default}/data/flows/add_contact.yml +0 -0
  344. /rasa/cli/project_templates/{calm → default}/data/flows/list_contacts.yml +0 -0
  345. /rasa/cli/project_templates/{calm → default}/data/flows/remove_contact.yml +0 -0
  346. /rasa/cli/project_templates/{calm → default}/db/contacts.json +0 -0
  347. /rasa/cli/project_templates/{calm → default}/domain/add_contact.yml +0 -0
  348. /rasa/cli/project_templates/{calm → default}/domain/list_contacts.yml +0 -0
  349. /rasa/cli/project_templates/{calm → default}/domain/remove_contact.yml +0 -0
  350. /rasa/cli/project_templates/{calm → default}/domain/shared.yml +0 -0
  351. /rasa/{cli/project_templates/calm/actions → core/information_retrieval/ingestion}/__init__.py +0 -0
  352. {rasa_pro-3.12.21.dist-info → rasa_pro-3.13.0.dist-info}/NOTICE +0 -0
  353. {rasa_pro-3.12.21.dist-info → rasa_pro-3.13.0.dist-info}/WHEEL +0 -0
  354. {rasa_pro-3.12.21.dist-info → rasa_pro-3.13.0.dist-info}/entry_points.txt +0 -0
rasa/core/processor.py CHANGED
@@ -12,7 +12,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Text, Tuple, Union
12
12
  import structlog
13
13
 
14
14
  import rasa.core.actions.action
15
- import rasa.core.tracker_store
15
+ import rasa.core.tracker_stores.tracker_store
16
16
  import rasa.core.utils
17
17
  import rasa.shared.core.trackers
18
18
  import rasa.shared.utils.io
@@ -75,8 +75,8 @@ from rasa.shared.core.constants import (
75
75
  ACTION_SESSION_START_NAME,
76
76
  FOLLOWUP_ACTION,
77
77
  SESSION_START_METADATA_SLOT,
78
+ SILENCE_TIMEOUT_SLOT,
78
79
  SLOT_CONSECUTIVE_SILENCE_TIMEOUTS,
79
- SLOT_SILENCE_TIMEOUT,
80
80
  USER_INTENT_RESTART,
81
81
  USER_INTENT_SILENCE_TIMEOUT,
82
82
  SetSlotExtractor,
@@ -112,7 +112,8 @@ from rasa.utils.common import TempDirectoryPath, get_temp_dir_name
112
112
  from rasa.utils.endpoints import EndpointConfig
113
113
 
114
114
  if TYPE_CHECKING:
115
- from rasa.core.utils import AvailableEndpoints
115
+ from rasa.core.available_endpoints import AvailableEndpoints
116
+ from rasa.privacy.privacy_manager import BackgroundPrivacyManager
116
117
 
117
118
  logger = logging.getLogger(__name__)
118
119
  structlogger = structlog.get_logger()
@@ -129,7 +130,7 @@ class MessageProcessor:
129
130
  def __init__(
130
131
  self,
131
132
  model_path: Union[Text, Path],
132
- tracker_store: rasa.core.tracker_store.TrackerStore,
133
+ tracker_store: rasa.core.tracker_stores.tracker_store.TrackerStore,
133
134
  lock_store: LockStore,
134
135
  generator: NaturalLanguageGenerator,
135
136
  action_endpoint: Optional[EndpointConfig] = None,
@@ -138,6 +139,7 @@ class MessageProcessor:
138
139
  on_circuit_break: Optional[LambdaType] = None,
139
140
  http_interpreter: Optional[RasaNLUHttpInterpreter] = None,
140
141
  endpoints: Optional["AvailableEndpoints"] = None,
142
+ privacy_manager: Optional["BackgroundPrivacyManager"] = None,
141
143
  ) -> None:
142
144
  """Initializes a `MessageProcessor`."""
143
145
  self.nlg = generator
@@ -167,6 +169,9 @@ class MessageProcessor:
167
169
  self.model_path = Path(model_path)
168
170
  self.domain = self.model_metadata.domain
169
171
  self.http_interpreter = http_interpreter
172
+ self.privacy_manager = privacy_manager
173
+ if self.privacy_manager is not None:
174
+ self.privacy_manager.validate_sensitive_slots_in_domain(self.domain)
170
175
 
171
176
  @staticmethod
172
177
  def _load_model(
@@ -216,15 +221,36 @@ class MessageProcessor:
216
221
 
217
222
  await self._run_prediction_loop(message.output_channel, tracker)
218
223
 
219
- await self.run_anonymization_pipeline(tracker)
220
-
221
224
  await self.save_tracker(tracker)
222
225
 
226
+ self.trigger_anonymization(tracker)
227
+
223
228
  if isinstance(message.output_channel, CollectingOutputChannel):
224
229
  return message.output_channel.messages
225
230
 
226
231
  return None
227
232
 
233
+ def trigger_anonymization(self, tracker: DialogueStateTracker) -> None:
234
+ if self.privacy_manager is None:
235
+ structlogger.debug(
236
+ "processor.trigger_anonymization.skipping.pii_management_not_enabled",
237
+ )
238
+ return None
239
+
240
+ if not self.privacy_manager.event_brokers:
241
+ structlogger.debug(
242
+ "processor.trigger_anonymization.skipping.no_event_brokers",
243
+ )
244
+ return None
245
+
246
+ structlogger.info(
247
+ "rasa.core.processor.trigger_anonymization",
248
+ sender_id=tracker.sender_id,
249
+ event_info="Triggering anonymization for publishing anonymized "
250
+ "events to the event broker.",
251
+ )
252
+ return self.privacy_manager.run(tracker)
253
+
228
254
  async def run_action_extract_slots(
229
255
  self,
230
256
  output_channel: OutputChannel,
@@ -265,26 +291,6 @@ class MessageProcessor:
265
291
 
266
292
  return tracker
267
293
 
268
- async def run_anonymization_pipeline(self, tracker: DialogueStateTracker) -> None:
269
- """Run the anonymization pipeline on the new tracker events.
270
-
271
- Args:
272
- tracker: A tracker representing a conversation state.
273
- """
274
- anonymization_pipeline = plugin_manager().hook.get_anonymization_pipeline()
275
- if anonymization_pipeline is None:
276
- return None
277
-
278
- old_tracker = await self.tracker_store.retrieve(tracker.sender_id)
279
- new_events = rasa.shared.core.trackers.TrackerEventDiffEngine.event_difference(
280
- old_tracker, tracker
281
- )
282
-
283
- for event in new_events:
284
- body = {"sender_id": tracker.sender_id}
285
- body.update(event.as_dict())
286
- anonymization_pipeline.run(body)
287
-
288
294
  async def predict_next_for_sender_id(
289
295
  self, sender_id: Text
290
296
  ) -> Optional[Dict[Text, Any]]:
@@ -822,28 +828,8 @@ class MessageProcessor:
822
828
  )
823
829
 
824
830
  self._check_for_unseen_features(parse_data)
825
- # resetting timeouts variables whenever something that is not a timeout occurs
826
- if (
827
- parse_data.get(INTENT, {}).get(INTENT_NAME_KEY)
828
- != USER_INTENT_SILENCE_TIMEOUT
829
- and tracker
830
- ):
831
- if (
832
- SLOT_CONSECUTIVE_SILENCE_TIMEOUTS in tracker.slots
833
- and tracker.slots[SLOT_CONSECUTIVE_SILENCE_TIMEOUTS].value != 0.0
834
- ):
835
- tracker.update(SlotSet(SLOT_CONSECUTIVE_SILENCE_TIMEOUTS, 0.0))
836
- if (
837
- SLOT_SILENCE_TIMEOUT in tracker.slots
838
- and tracker.slots[SLOT_SILENCE_TIMEOUT].value
839
- != tracker.slots[SLOT_SILENCE_TIMEOUT].initial_value
840
- ):
841
- tracker.update(
842
- SlotSet(
843
- SLOT_SILENCE_TIMEOUT,
844
- tracker.slots[SLOT_SILENCE_TIMEOUT].initial_value,
845
- )
846
- )
831
+
832
+ self._initialise_consecutive_silence_timeout_slots(parse_data, tracker)
847
833
 
848
834
  return parse_data
849
835
 
@@ -1068,7 +1054,14 @@ class MessageProcessor:
1068
1054
 
1069
1055
  @staticmethod
1070
1056
  def _should_handle_message(tracker: DialogueStateTracker) -> bool:
1071
- return not tracker.is_paused() or (
1057
+ return not tracker.is_paused() or MessageProcessor._last_user_intent_is_restart(
1058
+ tracker
1059
+ )
1060
+
1061
+ @staticmethod
1062
+ def _last_user_intent_is_restart(tracker: DialogueStateTracker) -> bool:
1063
+ """Check if the last user intent is a restart intent."""
1064
+ return (
1072
1065
  tracker.latest_message is not None
1073
1066
  and tracker.latest_message.intent.get(INTENT_NAME_KEY)
1074
1067
  == USER_INTENT_RESTART
@@ -1615,3 +1608,31 @@ class MessageProcessor:
1615
1608
  )
1616
1609
 
1617
1610
  return tracker, validate_frames
1611
+
1612
+ @staticmethod
1613
+ def _initialise_consecutive_silence_timeout_slots(
1614
+ parse_data: Dict[str, Any],
1615
+ tracker: DialogueStateTracker,
1616
+ ) -> None:
1617
+ # resetting timeouts variables whenever something that is not a timeout occurs
1618
+ if (
1619
+ parse_data.get(INTENT, {}).get(INTENT_NAME_KEY)
1620
+ != USER_INTENT_SILENCE_TIMEOUT
1621
+ and tracker
1622
+ ):
1623
+ if (
1624
+ SLOT_CONSECUTIVE_SILENCE_TIMEOUTS in tracker.slots
1625
+ and tracker.slots[SLOT_CONSECUTIVE_SILENCE_TIMEOUTS].value != 0.0
1626
+ ):
1627
+ tracker.update(SlotSet(SLOT_CONSECUTIVE_SILENCE_TIMEOUTS, 0.0))
1628
+ if (
1629
+ SILENCE_TIMEOUT_SLOT in tracker.slots
1630
+ and tracker.slots[SILENCE_TIMEOUT_SLOT].value
1631
+ != tracker.slots[SILENCE_TIMEOUT_SLOT].initial_value
1632
+ ):
1633
+ tracker.update(
1634
+ SlotSet(
1635
+ SILENCE_TIMEOUT_SLOT,
1636
+ tracker.slots[SILENCE_TIMEOUT_SLOT].initial_value,
1637
+ )
1638
+ )
rasa/core/run.py CHANGED
@@ -30,12 +30,11 @@ from rasa import server, telemetry
30
30
  from rasa.constants import ENV_SANIC_BACKLOG
31
31
  from rasa.core import agent, channels, constants
32
32
  from rasa.core.agent import Agent
33
+ from rasa.core.available_endpoints import AvailableEndpoints
33
34
  from rasa.core.channels import console
34
35
  from rasa.core.channels.channel import InputChannel
35
36
  from rasa.core.channels.development_inspector import DevelopmentInspectProxy
36
37
  from rasa.core.persistor import StorageType
37
- from rasa.core.utils import AvailableEndpoints
38
- from rasa.plugin import plugin_manager
39
38
  from rasa.shared.exceptions import RasaException
40
39
  from rasa.shared.utils.yaml import read_config_file
41
40
  from rasa.utils import licensing
@@ -45,7 +44,7 @@ logger = logging.getLogger() # get the root logger
45
44
 
46
45
  def create_http_input_channels(
47
46
  channel: Optional[Text], credentials_file: Optional[Text]
48
- ) -> List["InputChannel"]:
47
+ ) -> List[InputChannel]:
49
48
  """Instantiate the chosen input channel."""
50
49
  if credentials_file:
51
50
  all_credentials = read_config_file(credentials_file)
@@ -59,22 +58,45 @@ def create_http_input_channels(
59
58
  "To connect to all given channels, omit the '--connector' "
60
59
  "argument.".format(channel)
61
60
  )
62
- return [_create_single_channel(channel, all_credentials.get(channel))]
61
+ return [
62
+ _create_single_channel(
63
+ channel,
64
+ all_credentials.get(channel),
65
+ )
66
+ ]
63
67
  else:
64
68
  return [_create_single_channel(c, k) for c, k in all_credentials.items()]
65
69
 
66
70
 
67
- def _create_single_channel(channel: Text, credentials: Dict[Text, Any]) -> Any:
71
+ def _create_single_channel(
72
+ channel: Text,
73
+ credentials: Optional[Dict[Text, Any]],
74
+ ) -> Any:
75
+ """Create a single input channel based on the channel name and credentials.
76
+
77
+ Args:
78
+ channel: The name of the input channel to create.
79
+ credentials: The credentials for the input channel.
80
+
81
+ Returns:
82
+ An instance of the input channel class.
83
+
84
+ Raises:
85
+ RasaException: If the channel class cannot be found or instantiated.
86
+ """
68
87
  from rasa.core.channels import BUILTIN_CHANNELS
69
88
 
70
89
  if channel in BUILTIN_CHANNELS:
71
- return BUILTIN_CHANNELS[channel].from_credentials(credentials)
90
+ channel_class = BUILTIN_CHANNELS[channel]
91
+
92
+ return channel_class.from_credentials(credentials)
72
93
  else:
73
94
  # try to load channel based on class name
74
95
  try:
75
96
  input_channel_class = rasa.shared.utils.common.class_from_module_path(
76
97
  channel
77
98
  )
99
+
78
100
  return input_channel_class.from_credentials(credentials)
79
101
  except (AttributeError, ImportError):
80
102
  raise RasaException(
@@ -108,7 +130,7 @@ def _is_apple_silicon_system() -> bool:
108
130
 
109
131
 
110
132
  def configure_app(
111
- input_channels: Optional[List["InputChannel"]] = None,
133
+ input_channels: Optional[List[InputChannel]] = None,
112
134
  cors: Optional[Union[Text, List[Text], None]] = None,
113
135
  auth_token: Optional[Text] = None,
114
136
  enable_api: bool = True,
@@ -190,10 +212,6 @@ def configure_app(
190
212
  logger.info("Killing Sanic server now.")
191
213
  running_app.stop() # kill the sanic server
192
214
 
193
- @app.after_server_stop
194
- async def after_server_stop(running_app: Sanic) -> None:
195
- plugin_manager().hook.after_server_stop()
196
-
197
215
  if server_listeners:
198
216
  for listener, event in server_listeners:
199
217
  app.register_listener(listener, event)
@@ -346,3 +364,7 @@ async def close_resources(app: Sanic, _: AbstractEventLoop) -> None:
346
364
  event_broker = current_agent.tracker_store.event_broker
347
365
  if event_broker:
348
366
  await event_broker.close()
367
+
368
+ privacy_manager = current_agent.privacy_manager
369
+ if privacy_manager:
370
+ privacy_manager.stop()
File without changes
@@ -3,7 +3,7 @@ from typing import Iterable, Optional, Text
3
3
 
4
4
  from rasa.core.brokers.broker import EventBroker
5
5
  from rasa.core.secrets_manager.secret_manager import EndpointResolver
6
- from rasa.core.tracker_store import TrackerStore, create_tracker_store
6
+ from rasa.core.tracker_stores.tracker_store import TrackerStore, create_tracker_store
7
7
  from rasa.shared.core.domain import Domain
8
8
  from rasa.shared.core.trackers import DialogueStateTracker
9
9
  from rasa.utils.endpoints import EndpointConfig
@@ -92,6 +92,29 @@ class AuthRetryTrackerStore(TrackerStore):
92
92
  )
93
93
  return None
94
94
 
95
+ async def retrieve_full_tracker(
96
+ self, sender_id: Text
97
+ ) -> Optional["DialogueStateTracker"]:
98
+ """Retries retrieving the full tracker if it fails."""
99
+ # add + 1 to retries because the retries are additional to the first attempt
100
+ for _ in range(self.retries + 1):
101
+ try:
102
+ return await self._tracker_store.retrieve_full_tracker(sender_id)
103
+ except Exception as e:
104
+ logger.warning(
105
+ f"Failed to retrieve full tracker for {sender_id}. Retrying...",
106
+ exc_info=e,
107
+ )
108
+ self._tracker_store = self.recreate_tracker_store(
109
+ self.domain, self.event_broker
110
+ )
111
+
112
+ logger.error(
113
+ f"Failed to retrieve full tracker for {sender_id} "
114
+ f"after {self.retries} retries."
115
+ )
116
+ return None
117
+
95
118
  async def save(self, tracker: "DialogueStateTracker") -> None:
96
119
  """Retries saving the tracker if it fails."""
97
120
  # add + 1 to retries because the retries are additional to the first attempt
@@ -119,3 +142,45 @@ class AuthRetryTrackerStore(TrackerStore):
119
142
  """Recreate tracker store with updated credentials."""
120
143
  endpoint_config = EndpointResolver.update_config(self.endpoint_config)
121
144
  return create_tracker_store(endpoint_config, domain, event_broker)
145
+
146
+ async def delete(self, sender_id: str) -> None:
147
+ """Retries deleting the tracker for the given sender_id."""
148
+ # add + 1 to retries because the retries are additional to the first attempt
149
+ for _ in range(self.retries + 1):
150
+ try:
151
+ await self._tracker_store.delete(sender_id)
152
+ break
153
+ except Exception as e:
154
+ logger.warning(
155
+ f"Failed to delete tracker for {sender_id}. Retrying...",
156
+ exc_info=e,
157
+ )
158
+ self._tracker_store = self.recreate_tracker_store(
159
+ self.domain, self.event_broker
160
+ )
161
+ else:
162
+ logger.error(
163
+ f"Failed to delete tracker for {sender_id} "
164
+ f"after {self.retries} retries."
165
+ )
166
+
167
+ async def update(self, tracker: DialogueStateTracker) -> None:
168
+ """Retries replacing the tracker if it fails."""
169
+ # add + 1 to retries because the retries are additional to the first attempt
170
+ for _ in range(self.retries + 1):
171
+ try:
172
+ await self._tracker_store.update(tracker)
173
+ break
174
+ except Exception as e:
175
+ logger.warning(
176
+ f"Failed to replace tracker for {tracker.sender_id}. Retrying...",
177
+ exc_info=e,
178
+ )
179
+ self._tracker_store = self.recreate_tracker_store(
180
+ self.domain, self.event_broker
181
+ )
182
+ else:
183
+ logger.error(
184
+ f"Failed to replace tracker for {tracker.sender_id} "
185
+ f"after {self.retries} retries."
186
+ )
@@ -0,0 +1,256 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import TYPE_CHECKING, Any, Dict, Iterable, Optional, Text
5
+
6
+ import structlog
7
+ from boto3.dynamodb.conditions import Key
8
+
9
+ import rasa.utils
10
+ from rasa.constants import DEFAULT_SANIC_WORKERS, ENV_SANIC_WORKERS
11
+ from rasa.core.tracker_stores.tracker_store import (
12
+ SerializedTrackerAsDict,
13
+ TrackerStore,
14
+ )
15
+ from rasa.shared.core.domain import Domain
16
+ from rasa.shared.core.trackers import DialogueStateTracker
17
+ from rasa.shared.exceptions import RasaException
18
+ from rasa.utils.endpoints import EndpointConfig
19
+
20
+ structlogger = structlog.get_logger(__name__)
21
+
22
+ if TYPE_CHECKING:
23
+ import boto3.resources.factory.dynamodb.Table
24
+
25
+
26
+ class DynamoTrackerStore(TrackerStore, SerializedTrackerAsDict):
27
+ """Stores conversation history in DynamoDB."""
28
+
29
+ def __init__(
30
+ self,
31
+ domain: Domain,
32
+ table_name: Text = "states",
33
+ region: Text = "us-east-1",
34
+ event_broker: Optional[EndpointConfig] = None,
35
+ **kwargs: Dict[Text, Any],
36
+ ) -> None:
37
+ """Initialize `DynamoTrackerStore`.
38
+
39
+ Args:
40
+ domain: Domain associated with this tracker store.
41
+ table_name: The name of the DynamoDB table, does not need to be present a
42
+ priori.
43
+ region: The name of the region associated with the client.
44
+ A client is associated with a single region.
45
+ event_broker: An event broker used to publish events.
46
+ kwargs: Additional kwargs.
47
+ """
48
+ import boto3
49
+
50
+ self.client = boto3.client("dynamodb", region_name=region)
51
+ self.region = region
52
+ self.table_name = table_name
53
+ self.db = self.get_or_create_table(table_name)
54
+ super().__init__(domain, event_broker, **kwargs)
55
+
56
+ def get_or_create_table(
57
+ self, table_name: Text
58
+ ) -> "boto3.resources.factory.dynamodb.Table":
59
+ """Returns table or creates one if the table name is not in the table list."""
60
+ import boto3
61
+
62
+ dynamo = boto3.resource("dynamodb", region_name=self.region)
63
+ try:
64
+ self.client.describe_table(TableName=table_name)
65
+ except self.client.exceptions.ResourceNotFoundException:
66
+ sanic_workers_count = int(
67
+ os.environ.get(ENV_SANIC_WORKERS, DEFAULT_SANIC_WORKERS)
68
+ )
69
+
70
+ if sanic_workers_count > 1:
71
+ structlogger.error(
72
+ "dynamo_tracker_store.table_creation_not_supported_in_multi_worker_mode",
73
+ event_info=(
74
+ "DynamoDB table creation is not "
75
+ "supported in multi-worker mode. "
76
+ "Table should already exist.",
77
+ ),
78
+ )
79
+ raise RasaException(
80
+ "DynamoDB table creation is not supported in "
81
+ "case of multiple sanic workers. To create the table either "
82
+ "run Rasa with a single worker or create the table manually."
83
+ "Here are the defaults which can be used to "
84
+ "create the table manually: "
85
+ f"Table name: {table_name}, Primary key: sender_id, "
86
+ f"key type `HASH`, attribute type `S` (String), "
87
+ "Provisioned throughput: Read capacity units: 5, "
88
+ "Write capacity units: 5"
89
+ )
90
+
91
+ table = dynamo.create_table(
92
+ TableName=self.table_name,
93
+ KeySchema=[{"AttributeName": "sender_id", "KeyType": "HASH"}],
94
+ AttributeDefinitions=[
95
+ {"AttributeName": "sender_id", "AttributeType": "S"}
96
+ ],
97
+ ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
98
+ )
99
+
100
+ # Wait until the table exists.
101
+ table.meta.client.get_waiter("table_exists").wait(TableName=table_name)
102
+ else:
103
+ table = dynamo.Table(table_name)
104
+
105
+ return table
106
+
107
+ async def save(self, tracker: DialogueStateTracker) -> None:
108
+ """Saves the current conversation state."""
109
+ await self.stream_events(tracker)
110
+ serialized = self.serialise_tracker(tracker)
111
+
112
+ full_tracker = await self.retrieve_full_tracker(tracker.sender_id)
113
+ if full_tracker is None:
114
+ self.db.put_item(Item=serialized)
115
+ return None
116
+
117
+ # return the latest events since the last user message
118
+ new_tracker = DialogueStateTracker.from_dict(
119
+ serialized["sender_id"], events_as_dict=serialized["events"]
120
+ )
121
+ new_events = new_tracker.get_last_turn_events()
122
+ new_serialized_events = [event.as_dict() for event in new_events]
123
+
124
+ # we need to save the full tracker if it is a new tracker
125
+ # without events following a user message
126
+ if not new_serialized_events:
127
+ self.db.put_item(Item=serialized)
128
+ return None
129
+
130
+ # append new events to the existing tracker
131
+ self.db.update_item(
132
+ Key={"sender_id": tracker.sender_id},
133
+ UpdateExpression="SET events = list_append(if_not_exists(events, :empty_list), :events)", # noqa: E501
134
+ ExpressionAttributeValues={
135
+ ":events": new_serialized_events,
136
+ ":empty_list": [],
137
+ },
138
+ ReturnValues="UPDATED_NEW",
139
+ )
140
+ return None
141
+
142
+ async def delete(self, sender_id: Text) -> None:
143
+ """Delete tracker for the given sender_id."""
144
+ if not await self.exists(sender_id):
145
+ structlogger.info(
146
+ "dynamo_tracker_store.delete.no_tracker_for_sender_id",
147
+ event_info=f"Could not find tracker for conversation ID '{sender_id}'.",
148
+ )
149
+ return None
150
+
151
+ self.db.delete_item(
152
+ Key={"sender_id": sender_id},
153
+ ConditionExpression="attribute_exists(sender_id)",
154
+ )
155
+ structlogger.info(
156
+ "dynamo_tracker_store.delete.deleted_tracker",
157
+ sender_id=sender_id,
158
+ )
159
+
160
+ @staticmethod
161
+ def serialise_tracker(
162
+ tracker: "DialogueStateTracker",
163
+ ) -> Dict:
164
+ """Serializes the tracker, returns object with decimal types.
165
+
166
+ DynamoDB cannot store `float`s, so we'll convert them to `Decimal`s.
167
+ """
168
+ return rasa.utils.json_utils.replace_floats_with_decimals(
169
+ SerializedTrackerAsDict.serialise_tracker(tracker)
170
+ )
171
+
172
+ async def retrieve(self, sender_id: Text) -> Optional[DialogueStateTracker]:
173
+ """Retrieve dialogues for a sender_id in reverse-chronological order.
174
+
175
+ Based on the session_date sort key.
176
+ """
177
+ return await self._retrieve(sender_id, fetch_all_sessions=False)
178
+
179
+ async def retrieve_full_tracker(
180
+ self, sender_id: Text
181
+ ) -> Optional[DialogueStateTracker]:
182
+ """Retrieves tracker for all conversation sessions.
183
+
184
+ Args:
185
+ sender_id: Conversation ID to fetch the tracker for.
186
+ """
187
+ return await self._retrieve(sender_id, fetch_all_sessions=True)
188
+
189
+ async def _retrieve(
190
+ self, sender_id: Text, fetch_all_sessions: bool
191
+ ) -> Optional[DialogueStateTracker]:
192
+ """Returns tracker matching sender_id.
193
+
194
+ Args:
195
+ sender_id: Conversation ID to fetch the tracker for.
196
+ fetch_all_sessions: Whether to fetch all sessions or only the last one.
197
+ """
198
+ dialogues = self.db.query(
199
+ KeyConditionExpression=Key("sender_id").eq(sender_id),
200
+ ScanIndexForward=False,
201
+ )["Items"]
202
+
203
+ if not dialogues:
204
+ return None
205
+
206
+ events_with_floats = []
207
+ for dialogue in dialogues:
208
+ if dialogue.get("events"):
209
+ events = rasa.utils.json_utils.replace_decimals_with_floats(
210
+ dialogue["events"]
211
+ )
212
+ events_with_floats.extend(events)
213
+
214
+ if self.domain is None:
215
+ slots = []
216
+ else:
217
+ slots = self.domain.slots
218
+
219
+ tracker = DialogueStateTracker.from_dict(sender_id, events_with_floats, slots)
220
+
221
+ if fetch_all_sessions:
222
+ return tracker
223
+
224
+ # only return the last session
225
+ multiple_tracker_sessions = (
226
+ rasa.shared.core.trackers.get_trackers_for_conversation_sessions(tracker)
227
+ )
228
+
229
+ if len(multiple_tracker_sessions) <= 1:
230
+ return tracker
231
+
232
+ return multiple_tracker_sessions[-1]
233
+
234
+ async def keys(self) -> Iterable[Text]:
235
+ """Returns sender_ids of the `DynamoTrackerStore`."""
236
+ response = self.db.scan(ProjectionExpression="sender_id")
237
+ sender_ids = [i["sender_id"] for i in response["Items"]]
238
+
239
+ while response.get("LastEvaluatedKey"):
240
+ response = self.db.scan(
241
+ ProjectionExpression="sender_id",
242
+ ExclusiveStartKey=response["LastEvaluatedKey"],
243
+ )
244
+ sender_ids.extend([i["sender_id"] for i in response["Items"]])
245
+
246
+ return sender_ids
247
+
248
+ async def update(self, tracker: DialogueStateTracker) -> None:
249
+ """Overwrites the tracker for the given sender_id."""
250
+ serialized = self.serialise_tracker(tracker)
251
+ self.db.put_item(Item=serialized)
252
+
253
+ structlogger.info(
254
+ "dynamo_tracker_store.replace.replaced_tracker",
255
+ sender_id=tracker.sender_id,
256
+ )