rasa-pro 3.12.22__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.22.dist-info → rasa_pro-3.13.0.dist-info}/METADATA +11 -12
  316. {rasa_pro-3.12.22.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.22.dist-info → rasa_pro-3.13.0.dist-info}/NOTICE +0 -0
  353. {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/WHEEL +0 -0
  354. {rasa_pro-3.12.22.dist-info → rasa_pro-3.13.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,839 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from inspect import isawaitable, iscoroutinefunction
5
+ from typing import (
6
+ Any,
7
+ Callable,
8
+ Dict,
9
+ Generic,
10
+ Iterable,
11
+ List,
12
+ Optional,
13
+ Text,
14
+ TypeVar,
15
+ Union,
16
+ )
17
+
18
+ import structlog
19
+
20
+ import rasa.shared.utils.cli
21
+ import rasa.shared.utils.common
22
+ import rasa.shared.utils.io
23
+ import rasa.utils.json_utils
24
+ from rasa.core.brokers.broker import EventBroker
25
+ from rasa.plugin import plugin_manager
26
+ from rasa.shared.core.constants import ACTION_LISTEN_NAME
27
+ from rasa.shared.core.conversation import Dialogue
28
+ from rasa.shared.core.domain import Domain
29
+ from rasa.shared.core.events import Event
30
+ from rasa.shared.core.trackers import (
31
+ ActionExecuted,
32
+ DialogueStateTracker,
33
+ TrackerEventDiffEngine,
34
+ )
35
+ from rasa.shared.exceptions import ConnectionException, RasaException
36
+ from rasa.utils.endpoints import EndpointConfig
37
+
38
+ structlogger = structlog.get_logger(__name__)
39
+
40
+
41
+ def check_if_tracker_store_async(tracker_store: TrackerStore) -> bool:
42
+ """Evaluates if a tracker store object is async based on implementation of methods.
43
+
44
+ :param tracker_store: tracker store object we're evaluating
45
+ :return: if the tracker store correctly implements all async methods
46
+ """
47
+ return all(
48
+ iscoroutinefunction(getattr(tracker_store, method))
49
+ for method in _get_async_tracker_store_methods()
50
+ )
51
+
52
+
53
+ def _get_async_tracker_store_methods() -> List[str]:
54
+ return [
55
+ attribute
56
+ for attribute in dir(TrackerStore)
57
+ if iscoroutinefunction(getattr(TrackerStore, attribute))
58
+ ]
59
+
60
+
61
+ class TrackerDeserialisationException(RasaException):
62
+ """Raised when an error is encountered while deserialising a tracker."""
63
+
64
+
65
+ SerializationType = TypeVar("SerializationType")
66
+
67
+
68
+ class SerializedTrackerRepresentation(Generic[SerializationType]):
69
+ """Mixin class for specifying different serialization methods per tracker store."""
70
+
71
+ @staticmethod
72
+ def serialise_tracker(tracker: DialogueStateTracker) -> SerializationType:
73
+ """Requires implementation to return representation of tracker."""
74
+ raise NotImplementedError()
75
+
76
+
77
+ class SerializedTrackerAsText(SerializedTrackerRepresentation[Text]):
78
+ """Mixin class that returns the serialized tracker as string."""
79
+
80
+ @staticmethod
81
+ def serialise_tracker(tracker: DialogueStateTracker) -> Text:
82
+ """Serializes the tracker, returns representation of the tracker."""
83
+ dialogue = tracker.as_dialogue()
84
+
85
+ return json.dumps(dialogue.as_dict())
86
+
87
+
88
+ class SerializedTrackerAsDict(SerializedTrackerRepresentation[Dict]):
89
+ """Mixin class that returns the serialized tracker as dictionary."""
90
+
91
+ @staticmethod
92
+ def serialise_tracker(tracker: DialogueStateTracker) -> Dict:
93
+ """Serializes the tracker, returns representation of the tracker."""
94
+ d = tracker.as_dialogue().as_dict()
95
+ d.update({"sender_id": tracker.sender_id})
96
+ return d
97
+
98
+
99
+ class TrackerStore:
100
+ """Represents common behavior and interface for all `TrackerStore`s."""
101
+
102
+ def __init__(
103
+ self,
104
+ domain: Optional[Domain],
105
+ event_broker: Optional[EventBroker] = None,
106
+ **kwargs: Dict[Text, Any],
107
+ ) -> None:
108
+ """Create a TrackerStore.
109
+
110
+ Args:
111
+ domain: The `Domain` to initialize the `DialogueStateTracker`.
112
+ event_broker: An event broker to publish any new events to another
113
+ destination.
114
+ kwargs: Additional kwargs.
115
+ """
116
+ self._domain = domain or Domain.empty()
117
+ self.event_broker = event_broker
118
+ self.max_event_history: Optional[int] = None
119
+
120
+ @staticmethod
121
+ def create(
122
+ obj: Union[TrackerStore, EndpointConfig, None],
123
+ domain: Optional[Domain] = None,
124
+ event_broker: Optional[EventBroker] = None,
125
+ ) -> TrackerStore:
126
+ """Factory to create a tracker store."""
127
+ if isinstance(obj, TrackerStore):
128
+ return obj
129
+
130
+ import pymongo.errors
131
+ import sqlalchemy.exc
132
+ from botocore.exceptions import BotoCoreError, ClientError
133
+
134
+ try:
135
+ _tracker_store = plugin_manager().hook.create_tracker_store(
136
+ endpoint_config=obj,
137
+ domain=domain,
138
+ event_broker=event_broker,
139
+ )
140
+
141
+ tracker_store = (
142
+ _tracker_store
143
+ if _tracker_store
144
+ else create_tracker_store(obj, domain, event_broker)
145
+ )
146
+
147
+ return tracker_store
148
+ except (
149
+ BotoCoreError,
150
+ ClientError,
151
+ pymongo.errors.ConnectionFailure,
152
+ sqlalchemy.exc.OperationalError,
153
+ ConnectionError,
154
+ pymongo.errors.OperationFailure,
155
+ ) as error:
156
+ raise ConnectionException(
157
+ "Cannot connect to tracker store." + str(error)
158
+ ) from error
159
+
160
+ async def get_or_create_tracker(
161
+ self,
162
+ sender_id: Text,
163
+ max_event_history: Optional[int] = None,
164
+ append_action_listen: bool = True,
165
+ ) -> "DialogueStateTracker":
166
+ """Returns tracker or creates one if the retrieval returns None.
167
+
168
+ Args:
169
+ sender_id: Conversation ID associated with the requested tracker.
170
+ max_event_history: Value to update the tracker store's max event history to.
171
+ append_action_listen: Whether or not to append an initial `action_listen`.
172
+ """
173
+ self.max_event_history = max_event_history
174
+
175
+ tracker = await self.retrieve(sender_id)
176
+
177
+ if tracker is None:
178
+ tracker = await self.create_tracker(
179
+ sender_id, append_action_listen=append_action_listen
180
+ )
181
+
182
+ return tracker
183
+
184
+ def init_tracker(self, sender_id: Text) -> "DialogueStateTracker":
185
+ """Returns a Dialogue State Tracker."""
186
+ return DialogueStateTracker(
187
+ sender_id,
188
+ self.domain.slots,
189
+ max_event_history=self.max_event_history,
190
+ )
191
+
192
+ async def create_tracker(
193
+ self, sender_id: Text, append_action_listen: bool = True
194
+ ) -> DialogueStateTracker:
195
+ """Creates a new tracker for `sender_id`.
196
+
197
+ The tracker begins with a `SessionStarted` event and is initially listening.
198
+
199
+ Args:
200
+ sender_id: Conversation ID associated with the tracker.
201
+ append_action_listen: Whether or not to append an initial `action_listen`.
202
+
203
+ Returns:
204
+ The newly created tracker for `sender_id`.
205
+ """
206
+ tracker = self.init_tracker(sender_id)
207
+
208
+ if append_action_listen:
209
+ tracker.update(ActionExecuted(ACTION_LISTEN_NAME))
210
+
211
+ await self.save(tracker)
212
+
213
+ return tracker
214
+
215
+ async def save(self, tracker: DialogueStateTracker) -> None:
216
+ """Save method that will be overridden by specific tracker."""
217
+ raise NotImplementedError()
218
+
219
+ async def exists(self, conversation_id: Text) -> bool:
220
+ """Checks if tracker exists for the specified ID.
221
+
222
+ This method may be overridden by the specific tracker store for
223
+ faster implementations.
224
+
225
+ Args:
226
+ conversation_id: Conversation ID to check if the tracker exists.
227
+
228
+ Returns:
229
+ `True` if the tracker exists, `False` otherwise.
230
+ """
231
+ return await self.retrieve(conversation_id) is not None
232
+
233
+ async def retrieve(self, sender_id: Text) -> Optional[DialogueStateTracker]:
234
+ """Retrieves tracker for the latest conversation session.
235
+
236
+ This method will be overridden by the specific tracker store.
237
+
238
+ Args:
239
+ sender_id: Conversation ID to fetch the tracker for.
240
+
241
+ Returns:
242
+ Tracker containing events from the latest conversation sessions.
243
+ """
244
+ raise NotImplementedError()
245
+
246
+ async def delete(self, sender_id: str) -> None:
247
+ """Delete tracker for the given sender_id.
248
+
249
+ Args:
250
+ sender_id: Conversation ID to delete the tracker for.
251
+ """
252
+ raise NotImplementedError()
253
+
254
+ async def update(self, tracker: DialogueStateTracker) -> None:
255
+ """Replace an existing tracker with a new one.
256
+
257
+ Args:
258
+ tracker: The tracker to update.
259
+ """
260
+ raise NotImplementedError()
261
+
262
+ async def retrieve_full_tracker(
263
+ self, conversation_id: Text
264
+ ) -> Optional[DialogueStateTracker]:
265
+ """Retrieve method for fetching all tracker events.
266
+
267
+ Fetches events across conversation sessions. The default implementation
268
+ uses `self.retrieve()`.
269
+
270
+ Args:
271
+ conversation_id: The conversation ID to retrieve the tracker for.
272
+
273
+ Returns:
274
+ The fetch tracker containing all events across session starts.
275
+ """
276
+ return await self.retrieve(conversation_id)
277
+
278
+ async def get_or_create_full_tracker(
279
+ self,
280
+ sender_id: Text,
281
+ append_action_listen: bool = True,
282
+ ) -> "DialogueStateTracker":
283
+ """Returns tracker or creates one if the retrieval returns None.
284
+
285
+ Args:
286
+ sender_id: Conversation ID associated with the requested tracker.
287
+ append_action_listen: Whether to append an initial `action_listen`.
288
+
289
+ Returns:
290
+ The tracker for the conversation ID.
291
+ """
292
+ tracker = await self.retrieve_full_tracker(sender_id)
293
+
294
+ if tracker is None:
295
+ tracker = await self.create_tracker(
296
+ sender_id, append_action_listen=append_action_listen
297
+ )
298
+
299
+ return tracker
300
+
301
+ async def stream_events(self, tracker: DialogueStateTracker) -> None:
302
+ """Streams events to a message broker."""
303
+ if self.event_broker is None:
304
+ structlogger.debug(
305
+ "tracker_store.stream_events.no_broker_configured",
306
+ event_info="No event broker configured. Skipping streaming events.",
307
+ )
308
+ return None
309
+
310
+ if (
311
+ hasattr(self.event_broker, "stream_pii")
312
+ and not self.event_broker.stream_pii
313
+ ):
314
+ # If the event broker is configured to not stream un-anonymized events,
315
+ # skip streaming
316
+ structlogger.debug(
317
+ "tracker_store.stream_events.no_streaming",
318
+ event_info="Un-anonymized events will not be published "
319
+ "to the event broker.",
320
+ )
321
+ return None
322
+
323
+ old_tracker = await self.retrieve(tracker.sender_id)
324
+ new_events = TrackerEventDiffEngine.event_difference(old_tracker, tracker)
325
+
326
+ await self._stream_new_events(self.event_broker, new_events, tracker.sender_id)
327
+
328
+ async def _stream_new_events(
329
+ self,
330
+ event_broker: EventBroker,
331
+ new_events: List[Event],
332
+ sender_id: Text,
333
+ ) -> None:
334
+ """Publishes new tracker events to a message broker."""
335
+ for event in new_events:
336
+ body = {"sender_id": sender_id}
337
+ body.update(event.as_dict())
338
+ event_broker.publish(body)
339
+
340
+ async def keys(self) -> Iterable[Text]:
341
+ """Returns the set of values for the tracker store's primary key."""
342
+ raise NotImplementedError()
343
+
344
+ async def count_conversations(self, after_timestamp: float = 0.0) -> int:
345
+ """Returns the number of conversations that have occurred after a timestamp.
346
+
347
+ By default, this method returns the number of conversations that
348
+ have occurred after the Unix epoch (i.e. timestamp 0). A conversation
349
+ is considered to have occurred after a timestamp if at least one event
350
+ happened after that timestamp.
351
+ """
352
+ tracker_keys = await self.keys()
353
+
354
+ conversation_count = 0
355
+ for key in tracker_keys:
356
+ tracker = await self.retrieve(key)
357
+ if tracker is None or not tracker.events:
358
+ continue
359
+
360
+ last_event = tracker.events[-1]
361
+ if last_event.timestamp >= after_timestamp:
362
+ conversation_count += 1
363
+
364
+ return conversation_count
365
+
366
+ def deserialise_tracker(
367
+ self, sender_id: Text, serialised_tracker: Union[Text, bytes]
368
+ ) -> Optional[DialogueStateTracker]:
369
+ """Deserializes the tracker and returns it."""
370
+ tracker = self.init_tracker(sender_id)
371
+
372
+ try:
373
+ dialogue = Dialogue.from_parameters(json.loads(serialised_tracker))
374
+ except UnicodeDecodeError as e:
375
+ raise TrackerDeserialisationException(
376
+ "Tracker cannot be deserialised. "
377
+ "Trackers must be serialised as json. "
378
+ "Support for deserialising pickled trackers has been removed."
379
+ ) from e
380
+
381
+ tracker.recreate_from_dialogue(dialogue)
382
+
383
+ return tracker
384
+
385
+ @property
386
+ def domain(self) -> Domain:
387
+ """Returns the domain of the tracker store."""
388
+ return self._domain
389
+
390
+ @domain.setter
391
+ def domain(self, domain: Optional[Domain]) -> None:
392
+ self._domain = domain or Domain.empty()
393
+
394
+
395
+ class InMemoryTrackerStore(TrackerStore, SerializedTrackerAsText):
396
+ """Stores conversation history in memory."""
397
+
398
+ def __init__(
399
+ self,
400
+ domain: Domain,
401
+ event_broker: Optional[EventBroker] = None,
402
+ **kwargs: Dict[Text, Any],
403
+ ) -> None:
404
+ """Initializes the tracker store."""
405
+ self.store: Dict[Text, Text] = {}
406
+ super().__init__(domain, event_broker, **kwargs)
407
+
408
+ async def save(self, tracker: DialogueStateTracker) -> None:
409
+ """Updates and saves the current conversation state."""
410
+ await self.stream_events(tracker)
411
+ serialised = InMemoryTrackerStore.serialise_tracker(tracker)
412
+ self.store[tracker.sender_id] = serialised
413
+
414
+ async def delete(self, sender_id: Text) -> None:
415
+ """Delete tracker for the given sender_id."""
416
+ if sender_id not in self.store:
417
+ structlogger.info(
418
+ "in_memory_tracker_store.delete.no_tracker_for_sender_id",
419
+ event_info=f"Could not find tracker for conversation ID '{sender_id}'.",
420
+ )
421
+ return None
422
+
423
+ del self.store[sender_id]
424
+
425
+ structlogger.info(
426
+ "in_memory_tracker_store.delete.deleted_tracker",
427
+ sender_id=sender_id,
428
+ )
429
+
430
+ async def retrieve(self, sender_id: Text) -> Optional[DialogueStateTracker]:
431
+ """Returns tracker matching sender_id."""
432
+ return await self._retrieve(sender_id, fetch_all_sessions=False)
433
+
434
+ async def keys(self) -> Iterable[Text]:
435
+ """Returns sender_ids of the Tracker Store in memory."""
436
+ return self.store.keys()
437
+
438
+ async def retrieve_full_tracker(
439
+ self, sender_id: Text
440
+ ) -> Optional[DialogueStateTracker]:
441
+ """Returns tracker matching sender_id.
442
+
443
+ Args:
444
+ sender_id: Conversation ID to fetch the tracker for.
445
+ """
446
+ return await self._retrieve(sender_id, fetch_all_sessions=True)
447
+
448
+ async def _retrieve(
449
+ self, sender_id: Text, fetch_all_sessions: bool
450
+ ) -> Optional[DialogueStateTracker]:
451
+ """Returns tracker matching sender_id.
452
+
453
+ Args:
454
+ sender_id: Conversation ID to fetch the tracker for.
455
+ fetch_all_sessions: Whether to fetch all sessions or only the last one.
456
+ """
457
+ if sender_id not in self.store:
458
+ structlogger.debug(
459
+ "in_memory_tracker_store.retrieve.no_tracker_for_sender_id",
460
+ event_info=f"Could not find tracker for conversation ID '{sender_id}'.",
461
+ )
462
+ return None
463
+
464
+ tracker = self.deserialise_tracker(sender_id, self.store[sender_id])
465
+
466
+ if not tracker:
467
+ structlogger.debug(
468
+ "in_memory_tracker_store.retrieve.failed_to_deserialize_tracker",
469
+ event_info=(
470
+ f"Could not deserialize tracker "
471
+ f"for conversation ID '{sender_id}'.",
472
+ ),
473
+ )
474
+ return None
475
+
476
+ if fetch_all_sessions:
477
+ return tracker
478
+
479
+ # only return the last session
480
+ multiple_tracker_sessions = (
481
+ rasa.shared.core.trackers.get_trackers_for_conversation_sessions(tracker)
482
+ )
483
+
484
+ if len(multiple_tracker_sessions) <= 1:
485
+ return tracker
486
+
487
+ return multiple_tracker_sessions[-1]
488
+
489
+ async def update(self, tracker: DialogueStateTracker) -> None:
490
+ """Replace an existing tracker with a new one.
491
+
492
+ Args:
493
+ tracker: The tracker to update.
494
+ """
495
+ await self.save(tracker)
496
+
497
+
498
+ def validate_port(port: Any) -> Optional[int]:
499
+ """Ensure that port can be converted to integer.
500
+
501
+ Raises:
502
+ RasaException if port cannot be cast to integer.
503
+ """
504
+ if port is not None and not isinstance(port, int):
505
+ try:
506
+ port = int(port)
507
+ except ValueError as e:
508
+ raise RasaException(f"The port '{port}' cannot be cast to integer.") from e
509
+
510
+ return port
511
+
512
+
513
+ class FailSafeTrackerStore(TrackerStore):
514
+ """Tracker store wrapper.
515
+
516
+ Allows a fallback to a different tracker store in case of errors.
517
+ """
518
+
519
+ def __init__(
520
+ self,
521
+ tracker_store: TrackerStore,
522
+ on_tracker_store_error: Optional[Callable[[Exception], None]] = None,
523
+ fallback_tracker_store: Optional[TrackerStore] = None,
524
+ ) -> None:
525
+ """Create a `FailSafeTrackerStore`.
526
+
527
+ Args:
528
+ tracker_store: Primary tracker store.
529
+ on_tracker_store_error: Callback which is called when there is an error
530
+ in the primary tracker store.
531
+ fallback_tracker_store: Fallback tracker store.
532
+ """
533
+ self._fallback_tracker_store: Optional[TrackerStore] = fallback_tracker_store
534
+ self._tracker_store = tracker_store
535
+ self._on_tracker_store_error = on_tracker_store_error
536
+
537
+ super().__init__(tracker_store.domain, tracker_store.event_broker)
538
+
539
+ @property
540
+ def domain(self) -> Domain:
541
+ """Returns the domain of the primary tracker store."""
542
+ return self._tracker_store.domain
543
+
544
+ @domain.setter
545
+ def domain(self, domain: Domain) -> None:
546
+ self._tracker_store.domain = domain
547
+
548
+ if self._fallback_tracker_store:
549
+ self._fallback_tracker_store.domain = domain
550
+
551
+ @property
552
+ def fallback_tracker_store(self) -> TrackerStore:
553
+ """Returns the fallback tracker store."""
554
+ if not self._fallback_tracker_store:
555
+ self._fallback_tracker_store = InMemoryTrackerStore(
556
+ self._tracker_store.domain, self._tracker_store.event_broker
557
+ )
558
+
559
+ return self._fallback_tracker_store
560
+
561
+ def on_tracker_store_error(self, error: Exception) -> None:
562
+ """Calls the callback when there is an error in the primary tracker store."""
563
+ if self._on_tracker_store_error:
564
+ self._on_tracker_store_error(error)
565
+ else:
566
+ structlogger.error(
567
+ "fail_safe_tracker_store.tracker_store_error",
568
+ event_info=(
569
+ f"Error happened when trying to save conversation tracker to "
570
+ f"'{self._tracker_store.__class__.__name__}'. Falling back to use "
571
+ f"the '{InMemoryTrackerStore.__name__}'. Please "
572
+ f"investigate the following error: {error}."
573
+ ),
574
+ )
575
+
576
+ async def retrieve(self, sender_id: Text) -> Optional[DialogueStateTracker]:
577
+ """Calls `retrieve` method of primary tracker store."""
578
+ try:
579
+ return await self._tracker_store.retrieve(sender_id)
580
+ except Exception as e:
581
+ self.on_tracker_store_retrieve_error(e)
582
+ return None
583
+
584
+ async def keys(self) -> Iterable[Text]:
585
+ """Calls `keys` method of primary tracker store."""
586
+ try:
587
+ return await self._tracker_store.keys()
588
+ except Exception as e:
589
+ self.on_tracker_store_error(e)
590
+ return []
591
+
592
+ async def save(self, tracker: DialogueStateTracker) -> None:
593
+ """Calls `save` method of primary tracker store."""
594
+ try:
595
+ await self._tracker_store.save(tracker)
596
+ except Exception as e:
597
+ self.on_tracker_store_error(e)
598
+ await self.fallback_tracker_store.save(tracker)
599
+
600
+ async def delete(self, sender_id: Text) -> None:
601
+ """Delete tracker for the given sender_id."""
602
+ try:
603
+ await self._tracker_store.delete(sender_id)
604
+ except Exception as e:
605
+ self.on_tracker_store_error(e)
606
+ await self.fallback_tracker_store.delete(sender_id)
607
+
608
+ async def update(self, tracker: DialogueStateTracker) -> None:
609
+ """Replace an existing tracker with a new one."""
610
+ try:
611
+ await self._tracker_store.update(tracker)
612
+ except Exception as e:
613
+ self.on_tracker_store_error(e)
614
+ await self.fallback_tracker_store.update(tracker)
615
+
616
+ async def retrieve_full_tracker(
617
+ self, sender_id: Text
618
+ ) -> Optional[DialogueStateTracker]:
619
+ """Calls `retrieve_full_tracker` method of primary tracker store.
620
+
621
+ Args:
622
+ sender_id: The sender id of the tracker to retrieve.
623
+ """
624
+ try:
625
+ return await self._tracker_store.retrieve_full_tracker(sender_id)
626
+ except Exception as e:
627
+ self.on_tracker_store_retrieve_error(e)
628
+ return None
629
+
630
+ def on_tracker_store_retrieve_error(self, error: Exception) -> None:
631
+ """Calls `_on_tracker_store_error` callable attribute if set.
632
+
633
+ Otherwise, logs the error.
634
+
635
+ Args:
636
+ error: The error that occurred.
637
+ """
638
+ if self._on_tracker_store_error:
639
+ self._on_tracker_store_error(error)
640
+ else:
641
+ structlogger.error(
642
+ "fail_safe_tracker_store.tracker_store_retrieve_error",
643
+ event_info=(
644
+ f"Error happened when trying to retrieve conversation tracker from "
645
+ f"'{self._tracker_store.__class__.__name__}'. Falling back to use "
646
+ f"the '{InMemoryTrackerStore.__name__}'."
647
+ ),
648
+ exec_info=error,
649
+ )
650
+
651
+
652
+ def _create_from_endpoint_config(
653
+ endpoint_config: Optional[EndpointConfig] = None,
654
+ domain: Optional[Domain] = None,
655
+ event_broker: Optional[EventBroker] = None,
656
+ ) -> TrackerStore:
657
+ """Given an endpoint configuration, create a proper tracker store object."""
658
+ from rasa.core.tracker_stores.dynamo_tracker_store import DynamoTrackerStore
659
+ from rasa.core.tracker_stores.mongo_tracker_store import MongoTrackerStore
660
+ from rasa.core.tracker_stores.redis_tracker_store import (
661
+ RedisTrackerStore,
662
+ )
663
+ from rasa.core.tracker_stores.sql_tracker_store import SQLTrackerStore
664
+
665
+ domain = domain or Domain.empty()
666
+
667
+ if endpoint_config is None or endpoint_config.type is None:
668
+ # default tracker store if no type is set
669
+ tracker_store: TrackerStore = InMemoryTrackerStore(domain, event_broker)
670
+ elif endpoint_config.type.lower() == "redis":
671
+ tracker_store = RedisTrackerStore(
672
+ domain=domain,
673
+ host=endpoint_config.url,
674
+ event_broker=event_broker,
675
+ **endpoint_config.kwargs,
676
+ )
677
+ elif endpoint_config.type.lower() == "mongod":
678
+ tracker_store = MongoTrackerStore(
679
+ domain=domain,
680
+ host=endpoint_config.url,
681
+ event_broker=event_broker,
682
+ **endpoint_config.kwargs,
683
+ )
684
+ elif endpoint_config.type.lower() == "sql":
685
+ tracker_store = SQLTrackerStore(
686
+ domain=domain,
687
+ host=endpoint_config.url,
688
+ event_broker=event_broker,
689
+ **endpoint_config.kwargs,
690
+ )
691
+ elif endpoint_config.type.lower() == "dynamo":
692
+ tracker_store = DynamoTrackerStore(
693
+ domain=domain, event_broker=event_broker, **endpoint_config.kwargs
694
+ )
695
+ else:
696
+ tracker_store = _load_from_module_name_in_endpoint_config(
697
+ domain, endpoint_config, event_broker
698
+ )
699
+
700
+ structlogger.debug(
701
+ "tracker_store.create_tracker_store_from_endpoint_config",
702
+ event_info=f"Connected to {tracker_store.__class__.__name__}.",
703
+ )
704
+
705
+ return tracker_store
706
+
707
+
708
+ def _load_from_module_name_in_endpoint_config(
709
+ domain: Domain, store: EndpointConfig, event_broker: Optional[EventBroker] = None
710
+ ) -> TrackerStore:
711
+ """Initializes a custom tracker.
712
+
713
+ Defaults to the InMemoryTrackerStore if the module path can not be found.
714
+
715
+ Args:
716
+ domain: defines the universe in which the assistant operates
717
+ store: the specific tracker store
718
+ event_broker: an event broker to publish events
719
+
720
+ Returns:
721
+ a tracker store from a specified type in a stores endpoint configuration
722
+ """
723
+ try:
724
+ tracker_store_class = rasa.shared.utils.common.class_from_module_path(
725
+ store.type
726
+ )
727
+
728
+ return tracker_store_class(
729
+ host=store.url, domain=domain, event_broker=event_broker, **store.kwargs
730
+ )
731
+ except (AttributeError, ImportError):
732
+ rasa.shared.utils.io.raise_warning(
733
+ f"Tracker store with type '{store.type}' not found. "
734
+ f"Using `InMemoryTrackerStore` instead."
735
+ )
736
+ return InMemoryTrackerStore(domain)
737
+
738
+
739
+ def create_tracker_store(
740
+ endpoint_config: Optional[EndpointConfig],
741
+ domain: Optional[Domain] = None,
742
+ event_broker: Optional[EventBroker] = None,
743
+ ) -> TrackerStore:
744
+ """Creates a tracker store based on the current configuration."""
745
+ tracker_store = _create_from_endpoint_config(endpoint_config, domain, event_broker)
746
+
747
+ if not check_if_tracker_store_async(tracker_store):
748
+ rasa.shared.utils.io.raise_deprecation_warning(
749
+ f"Tracker store implementation "
750
+ f"{tracker_store.__class__.__name__} "
751
+ f"is not asynchronous. Non-asynchronous tracker stores "
752
+ f"are currently deprecated and will be removed in 4.0. "
753
+ f"Please make the following methods async: "
754
+ f"{_get_async_tracker_store_methods()}"
755
+ )
756
+ tracker_store = AwaitableTrackerStore(tracker_store)
757
+
758
+ return tracker_store
759
+
760
+
761
+ class AwaitableTrackerStore(TrackerStore):
762
+ """Wraps a tracker store so it can be implemented with async overrides."""
763
+
764
+ def __init__(
765
+ self,
766
+ tracker_store: TrackerStore,
767
+ ) -> None:
768
+ """Create a `AwaitableTrackerStore`.
769
+
770
+ Args:
771
+ tracker_store: the wrapped tracker store.
772
+ """
773
+ self._tracker_store = tracker_store
774
+
775
+ super().__init__(tracker_store.domain, tracker_store.event_broker)
776
+
777
+ @property
778
+ def domain(self) -> Domain:
779
+ """Returns the domain of the primary tracker store."""
780
+ return self._tracker_store.domain
781
+
782
+ @domain.setter
783
+ def domain(self, domain: Optional[Domain]) -> None:
784
+ """Setter method to modify the wrapped tracker store's domain field."""
785
+ self._tracker_store.domain = domain or Domain.empty()
786
+
787
+ @staticmethod
788
+ def create(
789
+ obj: Union[TrackerStore, EndpointConfig, None],
790
+ domain: Optional[Domain] = None,
791
+ event_broker: Optional[EventBroker] = None,
792
+ ) -> TrackerStore:
793
+ """Wrapper to call `create` method of primary tracker store."""
794
+ if isinstance(obj, TrackerStore):
795
+ return AwaitableTrackerStore(obj)
796
+ elif isinstance(obj, EndpointConfig):
797
+ return AwaitableTrackerStore(_create_from_endpoint_config(obj))
798
+ else:
799
+ raise ValueError(
800
+ f"{type(obj).__name__} supplied "
801
+ f"but expected object of type {TrackerStore.__name__} or "
802
+ f"of type {EndpointConfig.__name__}."
803
+ )
804
+
805
+ async def retrieve(self, sender_id: Text) -> Optional[DialogueStateTracker]:
806
+ """Wrapper to call `retrieve` method of primary tracker store."""
807
+ result = self._tracker_store.retrieve(sender_id)
808
+ return (
809
+ await result if isawaitable(result) else result # type: ignore[return-value, misc]
810
+ )
811
+
812
+ async def keys(self) -> Iterable[Text]:
813
+ """Wrapper to call `keys` method of primary tracker store."""
814
+ result = self._tracker_store.keys()
815
+ return await result if isawaitable(result) else result
816
+
817
+ async def save(self, tracker: DialogueStateTracker) -> None:
818
+ """Wrapper to call `save` method of primary tracker store."""
819
+ result = self._tracker_store.save(tracker)
820
+ return await result if isawaitable(result) else result
821
+
822
+ async def delete(self, sender_id: Text) -> None:
823
+ """Delete tracker for the given sender_id."""
824
+ result = self._tracker_store.delete(sender_id)
825
+ return await result if isawaitable(result) else result
826
+
827
+ async def update(self, tracker: DialogueStateTracker) -> None:
828
+ """Replace an existing tracker with a new one."""
829
+ result = self._tracker_store.update(tracker)
830
+ return await result if isawaitable(result) else result
831
+
832
+ async def retrieve_full_tracker(
833
+ self, conversation_id: Text
834
+ ) -> Optional[DialogueStateTracker]:
835
+ """Wrapper to call `retrieve_full_tracker` method of primary tracker store."""
836
+ result = self._tracker_store.retrieve_full_tracker(conversation_id)
837
+ return (
838
+ await result if isawaitable(result) else result # type: ignore[return-value, misc]
839
+ )