rasa-pro 3.8.16__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 (644) hide show
  1. README.md +380 -0
  2. rasa/__init__.py +10 -0
  3. rasa/__main__.py +151 -0
  4. rasa/anonymization/__init__.py +2 -0
  5. rasa/anonymization/anonymisation_rule_yaml_reader.py +91 -0
  6. rasa/anonymization/anonymization_pipeline.py +287 -0
  7. rasa/anonymization/anonymization_rule_executor.py +260 -0
  8. rasa/anonymization/anonymization_rule_orchestrator.py +120 -0
  9. rasa/anonymization/schemas/config.yml +47 -0
  10. rasa/anonymization/utils.py +117 -0
  11. rasa/api.py +146 -0
  12. rasa/cli/__init__.py +5 -0
  13. rasa/cli/arguments/__init__.py +0 -0
  14. rasa/cli/arguments/data.py +81 -0
  15. rasa/cli/arguments/default_arguments.py +165 -0
  16. rasa/cli/arguments/evaluate.py +65 -0
  17. rasa/cli/arguments/export.py +51 -0
  18. rasa/cli/arguments/interactive.py +74 -0
  19. rasa/cli/arguments/run.py +204 -0
  20. rasa/cli/arguments/shell.py +13 -0
  21. rasa/cli/arguments/test.py +211 -0
  22. rasa/cli/arguments/train.py +263 -0
  23. rasa/cli/arguments/visualize.py +34 -0
  24. rasa/cli/arguments/x.py +30 -0
  25. rasa/cli/data.py +292 -0
  26. rasa/cli/e2e_test.py +566 -0
  27. rasa/cli/evaluate.py +222 -0
  28. rasa/cli/export.py +251 -0
  29. rasa/cli/inspect.py +63 -0
  30. rasa/cli/interactive.py +164 -0
  31. rasa/cli/license.py +65 -0
  32. rasa/cli/markers.py +78 -0
  33. rasa/cli/project_templates/__init__.py +0 -0
  34. rasa/cli/project_templates/calm/actions/__init__.py +0 -0
  35. rasa/cli/project_templates/calm/actions/action_template.py +27 -0
  36. rasa/cli/project_templates/calm/actions/add_contact.py +30 -0
  37. rasa/cli/project_templates/calm/actions/db.py +57 -0
  38. rasa/cli/project_templates/calm/actions/list_contacts.py +22 -0
  39. rasa/cli/project_templates/calm/actions/remove_contact.py +35 -0
  40. rasa/cli/project_templates/calm/config.yml +12 -0
  41. rasa/cli/project_templates/calm/credentials.yml +33 -0
  42. rasa/cli/project_templates/calm/data/flows/add_contact.yml +31 -0
  43. rasa/cli/project_templates/calm/data/flows/list_contacts.yml +14 -0
  44. rasa/cli/project_templates/calm/data/flows/remove_contact.yml +29 -0
  45. rasa/cli/project_templates/calm/db/contacts.json +10 -0
  46. rasa/cli/project_templates/calm/domain/add_contact.yml +33 -0
  47. rasa/cli/project_templates/calm/domain/list_contacts.yml +14 -0
  48. rasa/cli/project_templates/calm/domain/remove_contact.yml +31 -0
  49. rasa/cli/project_templates/calm/domain/shared.yml +5 -0
  50. rasa/cli/project_templates/calm/e2e_tests/cancelations/user_cancels_during_a_correction.yml +16 -0
  51. rasa/cli/project_templates/calm/e2e_tests/cancelations/user_changes_mind_on_a_whim.yml +7 -0
  52. rasa/cli/project_templates/calm/e2e_tests/corrections/user_corrects_contact_handle.yml +20 -0
  53. rasa/cli/project_templates/calm/e2e_tests/corrections/user_corrects_contact_name.yml +19 -0
  54. rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_adds_contact_to_their_list.yml +15 -0
  55. rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_lists_contacts.yml +5 -0
  56. rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_removes_contact.yml +11 -0
  57. rasa/cli/project_templates/calm/e2e_tests/happy_paths/user_removes_contact_from_list.yml +12 -0
  58. rasa/cli/project_templates/calm/endpoints.yml +45 -0
  59. rasa/cli/project_templates/default/actions/__init__.py +0 -0
  60. rasa/cli/project_templates/default/actions/actions.py +27 -0
  61. rasa/cli/project_templates/default/config.yml +44 -0
  62. rasa/cli/project_templates/default/credentials.yml +33 -0
  63. rasa/cli/project_templates/default/data/nlu.yml +91 -0
  64. rasa/cli/project_templates/default/data/rules.yml +13 -0
  65. rasa/cli/project_templates/default/data/stories.yml +30 -0
  66. rasa/cli/project_templates/default/domain.yml +34 -0
  67. rasa/cli/project_templates/default/endpoints.yml +42 -0
  68. rasa/cli/project_templates/default/tests/test_stories.yml +91 -0
  69. rasa/cli/project_templates/tutorial/actions.py +22 -0
  70. rasa/cli/project_templates/tutorial/config.yml +11 -0
  71. rasa/cli/project_templates/tutorial/credentials.yml +33 -0
  72. rasa/cli/project_templates/tutorial/data/flows.yml +8 -0
  73. rasa/cli/project_templates/tutorial/domain.yml +17 -0
  74. rasa/cli/project_templates/tutorial/endpoints.yml +45 -0
  75. rasa/cli/run.py +136 -0
  76. rasa/cli/scaffold.py +268 -0
  77. rasa/cli/shell.py +141 -0
  78. rasa/cli/studio/__init__.py +0 -0
  79. rasa/cli/studio/download.py +51 -0
  80. rasa/cli/studio/studio.py +110 -0
  81. rasa/cli/studio/train.py +59 -0
  82. rasa/cli/studio/upload.py +85 -0
  83. rasa/cli/telemetry.py +90 -0
  84. rasa/cli/test.py +280 -0
  85. rasa/cli/train.py +260 -0
  86. rasa/cli/utils.py +453 -0
  87. rasa/cli/visualize.py +40 -0
  88. rasa/cli/x.py +205 -0
  89. rasa/constants.py +37 -0
  90. rasa/core/__init__.py +17 -0
  91. rasa/core/actions/__init__.py +0 -0
  92. rasa/core/actions/action.py +1450 -0
  93. rasa/core/actions/action_clean_stack.py +59 -0
  94. rasa/core/actions/action_run_slot_rejections.py +207 -0
  95. rasa/core/actions/action_trigger_chitchat.py +31 -0
  96. rasa/core/actions/action_trigger_flow.py +109 -0
  97. rasa/core/actions/action_trigger_search.py +31 -0
  98. rasa/core/actions/constants.py +2 -0
  99. rasa/core/actions/forms.py +737 -0
  100. rasa/core/actions/loops.py +111 -0
  101. rasa/core/actions/two_stage_fallback.py +186 -0
  102. rasa/core/agent.py +557 -0
  103. rasa/core/auth_retry_tracker_store.py +122 -0
  104. rasa/core/brokers/__init__.py +0 -0
  105. rasa/core/brokers/broker.py +126 -0
  106. rasa/core/brokers/file.py +58 -0
  107. rasa/core/brokers/kafka.py +322 -0
  108. rasa/core/brokers/pika.py +387 -0
  109. rasa/core/brokers/sql.py +86 -0
  110. rasa/core/channels/__init__.py +55 -0
  111. rasa/core/channels/audiocodes.py +463 -0
  112. rasa/core/channels/botframework.py +339 -0
  113. rasa/core/channels/callback.py +85 -0
  114. rasa/core/channels/channel.py +419 -0
  115. rasa/core/channels/console.py +243 -0
  116. rasa/core/channels/development_inspector.py +93 -0
  117. rasa/core/channels/facebook.py +422 -0
  118. rasa/core/channels/hangouts.py +335 -0
  119. rasa/core/channels/inspector/.eslintrc.cjs +25 -0
  120. rasa/core/channels/inspector/.gitignore +23 -0
  121. rasa/core/channels/inspector/README.md +54 -0
  122. rasa/core/channels/inspector/assets/favicon.ico +0 -0
  123. rasa/core/channels/inspector/assets/rasa-chat.js +2 -0
  124. rasa/core/channels/inspector/custom.d.ts +3 -0
  125. rasa/core/channels/inspector/dist/assets/arc-5623b6dc.js +1 -0
  126. rasa/core/channels/inspector/dist/assets/array-9f3ba611.js +1 -0
  127. rasa/core/channels/inspector/dist/assets/c4Diagram-d0fbc5ce-685c106a.js +10 -0
  128. rasa/core/channels/inspector/dist/assets/classDiagram-936ed81e-8cbed007.js +2 -0
  129. rasa/core/channels/inspector/dist/assets/classDiagram-v2-c3cb15f1-5889cf12.js +2 -0
  130. rasa/core/channels/inspector/dist/assets/createText-62fc7601-24c249d7.js +7 -0
  131. rasa/core/channels/inspector/dist/assets/edges-f2ad444c-7dd06a75.js +4 -0
  132. rasa/core/channels/inspector/dist/assets/erDiagram-9d236eb7-62c1e54c.js +51 -0
  133. rasa/core/channels/inspector/dist/assets/flowDb-1972c806-ce49b86f.js +6 -0
  134. rasa/core/channels/inspector/dist/assets/flowDiagram-7ea5b25a-4067e48f.js +4 -0
  135. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-85583a23.js +1 -0
  136. rasa/core/channels/inspector/dist/assets/flowchart-elk-definition-abe16c3d-59fe4051.js +139 -0
  137. rasa/core/channels/inspector/dist/assets/ganttDiagram-9b5ea136-47e3a43b.js +266 -0
  138. rasa/core/channels/inspector/dist/assets/gitGraphDiagram-99d0ae7c-5a2ac0d9.js +70 -0
  139. rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-128cfa44.ttf +0 -0
  140. rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-21dbcb97.woff +0 -0
  141. rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-222b5e26.svg +329 -0
  142. rasa/core/channels/inspector/dist/assets/ibm-plex-mono-v4-latin-regular-9ad89b2a.woff2 +0 -0
  143. rasa/core/channels/inspector/dist/assets/index-268a75c0.js +1040 -0
  144. rasa/core/channels/inspector/dist/assets/index-2c4b9a3b-dfb8efc4.js +1 -0
  145. rasa/core/channels/inspector/dist/assets/index-3ee28881.css +1 -0
  146. rasa/core/channels/inspector/dist/assets/infoDiagram-736b4530-b0c470f2.js +7 -0
  147. rasa/core/channels/inspector/dist/assets/init-77b53fdd.js +1 -0
  148. rasa/core/channels/inspector/dist/assets/journeyDiagram-df861f2b-2edb829a.js +139 -0
  149. rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-60c05ee4.woff +0 -0
  150. rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-8335d9b8.svg +438 -0
  151. rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-9cc39c75.ttf +0 -0
  152. rasa/core/channels/inspector/dist/assets/lato-v14-latin-700-ead13ccf.woff2 +0 -0
  153. rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-16705655.woff2 +0 -0
  154. rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-5aeb07f9.woff +0 -0
  155. rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-9c459044.ttf +0 -0
  156. rasa/core/channels/inspector/dist/assets/lato-v14-latin-regular-9e2898a4.svg +435 -0
  157. rasa/core/channels/inspector/dist/assets/layout-b6873d69.js +1 -0
  158. rasa/core/channels/inspector/dist/assets/line-1efc5781.js +1 -0
  159. rasa/core/channels/inspector/dist/assets/linear-661e9b94.js +1 -0
  160. rasa/core/channels/inspector/dist/assets/mindmap-definition-beec6740-2d2e727f.js +109 -0
  161. rasa/core/channels/inspector/dist/assets/ordinal-ba9b4969.js +1 -0
  162. rasa/core/channels/inspector/dist/assets/path-53f90ab3.js +1 -0
  163. rasa/core/channels/inspector/dist/assets/pieDiagram-dbbf0591-9d3ea93d.js +35 -0
  164. rasa/core/channels/inspector/dist/assets/quadrantDiagram-4d7f4fd6-06a178a2.js +7 -0
  165. rasa/core/channels/inspector/dist/assets/requirementDiagram-6fc4c22a-0bfedffc.js +52 -0
  166. rasa/core/channels/inspector/dist/assets/sankeyDiagram-8f13d901-d76d0a04.js +8 -0
  167. rasa/core/channels/inspector/dist/assets/sequenceDiagram-b655622a-37bb4341.js +122 -0
  168. rasa/core/channels/inspector/dist/assets/stateDiagram-59f0c015-f52f7f57.js +1 -0
  169. rasa/core/channels/inspector/dist/assets/stateDiagram-v2-2b26beab-4a986a20.js +1 -0
  170. rasa/core/channels/inspector/dist/assets/styles-080da4f6-7dd9ae12.js +110 -0
  171. rasa/core/channels/inspector/dist/assets/styles-3dcbcfbf-46e1ca14.js +159 -0
  172. rasa/core/channels/inspector/dist/assets/styles-9c745c82-4a97439a.js +207 -0
  173. rasa/core/channels/inspector/dist/assets/svgDrawCommon-4835440b-823917a3.js +1 -0
  174. rasa/core/channels/inspector/dist/assets/timeline-definition-5b62e21b-9ea72896.js +61 -0
  175. rasa/core/channels/inspector/dist/assets/xychartDiagram-2b33534f-b631a8b6.js +7 -0
  176. rasa/core/channels/inspector/dist/index.html +39 -0
  177. rasa/core/channels/inspector/index.html +37 -0
  178. rasa/core/channels/inspector/jest.config.ts +13 -0
  179. rasa/core/channels/inspector/package.json +48 -0
  180. rasa/core/channels/inspector/setupTests.ts +2 -0
  181. rasa/core/channels/inspector/src/App.tsx +170 -0
  182. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +97 -0
  183. rasa/core/channels/inspector/src/components/DialogueInformation.tsx +187 -0
  184. rasa/core/channels/inspector/src/components/DialogueStack.tsx +151 -0
  185. rasa/core/channels/inspector/src/components/ExpandIcon.tsx +16 -0
  186. rasa/core/channels/inspector/src/components/FullscreenButton.tsx +45 -0
  187. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +19 -0
  188. rasa/core/channels/inspector/src/components/NoActiveFlow.tsx +21 -0
  189. rasa/core/channels/inspector/src/components/RasaLogo.tsx +32 -0
  190. rasa/core/channels/inspector/src/components/SaraDiagrams.tsx +39 -0
  191. rasa/core/channels/inspector/src/components/Slots.tsx +91 -0
  192. rasa/core/channels/inspector/src/components/Welcome.tsx +54 -0
  193. rasa/core/channels/inspector/src/helpers/formatters.test.ts +385 -0
  194. rasa/core/channels/inspector/src/helpers/formatters.ts +239 -0
  195. rasa/core/channels/inspector/src/helpers/utils.ts +42 -0
  196. rasa/core/channels/inspector/src/main.tsx +13 -0
  197. rasa/core/channels/inspector/src/theme/Button/Button.ts +29 -0
  198. rasa/core/channels/inspector/src/theme/Heading/Heading.ts +31 -0
  199. rasa/core/channels/inspector/src/theme/Input/Input.ts +27 -0
  200. rasa/core/channels/inspector/src/theme/Link/Link.ts +10 -0
  201. rasa/core/channels/inspector/src/theme/Modal/Modal.ts +47 -0
  202. rasa/core/channels/inspector/src/theme/Table/Table.tsx +38 -0
  203. rasa/core/channels/inspector/src/theme/Tooltip/Tooltip.ts +12 -0
  204. rasa/core/channels/inspector/src/theme/base/breakpoints.ts +8 -0
  205. rasa/core/channels/inspector/src/theme/base/colors.ts +88 -0
  206. rasa/core/channels/inspector/src/theme/base/fonts/fontFaces.css +29 -0
  207. rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.eot +0 -0
  208. rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.svg +329 -0
  209. rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.ttf +0 -0
  210. rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.woff +0 -0
  211. rasa/core/channels/inspector/src/theme/base/fonts/ibm-plex-mono-v4-latin/ibm-plex-mono-v4-latin-regular.woff2 +0 -0
  212. rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.eot +0 -0
  213. rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.svg +438 -0
  214. rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.ttf +0 -0
  215. rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.woff +0 -0
  216. rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-700.woff2 +0 -0
  217. rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.eot +0 -0
  218. rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.svg +435 -0
  219. rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.ttf +0 -0
  220. rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.woff +0 -0
  221. rasa/core/channels/inspector/src/theme/base/fonts/lato-v14-latin/lato-v14-latin-regular.woff2 +0 -0
  222. rasa/core/channels/inspector/src/theme/base/radii.ts +9 -0
  223. rasa/core/channels/inspector/src/theme/base/shadows.ts +7 -0
  224. rasa/core/channels/inspector/src/theme/base/sizes.ts +7 -0
  225. rasa/core/channels/inspector/src/theme/base/space.ts +15 -0
  226. rasa/core/channels/inspector/src/theme/base/styles.ts +13 -0
  227. rasa/core/channels/inspector/src/theme/base/typography.ts +24 -0
  228. rasa/core/channels/inspector/src/theme/base/zIndices.ts +19 -0
  229. rasa/core/channels/inspector/src/theme/index.ts +101 -0
  230. rasa/core/channels/inspector/src/types.ts +64 -0
  231. rasa/core/channels/inspector/src/vite-env.d.ts +1 -0
  232. rasa/core/channels/inspector/tests/__mocks__/fileMock.ts +1 -0
  233. rasa/core/channels/inspector/tests/__mocks__/matchMedia.ts +16 -0
  234. rasa/core/channels/inspector/tests/__mocks__/styleMock.ts +1 -0
  235. rasa/core/channels/inspector/tests/renderWithProviders.tsx +14 -0
  236. rasa/core/channels/inspector/tsconfig.json +26 -0
  237. rasa/core/channels/inspector/tsconfig.node.json +10 -0
  238. rasa/core/channels/inspector/vite.config.ts +8 -0
  239. rasa/core/channels/inspector/yarn.lock +6156 -0
  240. rasa/core/channels/mattermost.py +229 -0
  241. rasa/core/channels/rasa_chat.py +126 -0
  242. rasa/core/channels/rest.py +210 -0
  243. rasa/core/channels/rocketchat.py +175 -0
  244. rasa/core/channels/slack.py +620 -0
  245. rasa/core/channels/socketio.py +274 -0
  246. rasa/core/channels/telegram.py +298 -0
  247. rasa/core/channels/twilio.py +169 -0
  248. rasa/core/channels/twilio_voice.py +367 -0
  249. rasa/core/channels/vier_cvg.py +374 -0
  250. rasa/core/channels/webexteams.py +135 -0
  251. rasa/core/concurrent_lock_store.py +210 -0
  252. rasa/core/constants.py +107 -0
  253. rasa/core/evaluation/__init__.py +0 -0
  254. rasa/core/evaluation/marker.py +267 -0
  255. rasa/core/evaluation/marker_base.py +925 -0
  256. rasa/core/evaluation/marker_stats.py +294 -0
  257. rasa/core/evaluation/marker_tracker_loader.py +103 -0
  258. rasa/core/exceptions.py +29 -0
  259. rasa/core/exporter.py +284 -0
  260. rasa/core/featurizers/__init__.py +0 -0
  261. rasa/core/featurizers/precomputation.py +410 -0
  262. rasa/core/featurizers/single_state_featurizer.py +402 -0
  263. rasa/core/featurizers/tracker_featurizers.py +1172 -0
  264. rasa/core/http_interpreter.py +89 -0
  265. rasa/core/information_retrieval/__init__.py +0 -0
  266. rasa/core/information_retrieval/faiss.py +116 -0
  267. rasa/core/information_retrieval/information_retrieval.py +72 -0
  268. rasa/core/information_retrieval/milvus.py +59 -0
  269. rasa/core/information_retrieval/qdrant.py +102 -0
  270. rasa/core/jobs.py +63 -0
  271. rasa/core/lock.py +139 -0
  272. rasa/core/lock_store.py +344 -0
  273. rasa/core/migrate.py +404 -0
  274. rasa/core/nlg/__init__.py +3 -0
  275. rasa/core/nlg/callback.py +147 -0
  276. rasa/core/nlg/contextual_response_rephraser.py +270 -0
  277. rasa/core/nlg/generator.py +230 -0
  278. rasa/core/nlg/interpolator.py +143 -0
  279. rasa/core/nlg/response.py +155 -0
  280. rasa/core/nlg/summarize.py +69 -0
  281. rasa/core/policies/__init__.py +0 -0
  282. rasa/core/policies/ensemble.py +329 -0
  283. rasa/core/policies/enterprise_search_policy.py +717 -0
  284. rasa/core/policies/enterprise_search_prompt_template.jinja2 +62 -0
  285. rasa/core/policies/flow_policy.py +205 -0
  286. rasa/core/policies/flows/__init__.py +0 -0
  287. rasa/core/policies/flows/flow_exceptions.py +44 -0
  288. rasa/core/policies/flows/flow_executor.py +582 -0
  289. rasa/core/policies/flows/flow_step_result.py +43 -0
  290. rasa/core/policies/intentless_policy.py +924 -0
  291. rasa/core/policies/intentless_prompt_template.jinja2 +22 -0
  292. rasa/core/policies/memoization.py +538 -0
  293. rasa/core/policies/policy.py +716 -0
  294. rasa/core/policies/rule_policy.py +1276 -0
  295. rasa/core/policies/ted_policy.py +2146 -0
  296. rasa/core/policies/unexpected_intent_policy.py +1015 -0
  297. rasa/core/processor.py +1331 -0
  298. rasa/core/run.py +315 -0
  299. rasa/core/secrets_manager/__init__.py +0 -0
  300. rasa/core/secrets_manager/constants.py +32 -0
  301. rasa/core/secrets_manager/endpoints.py +391 -0
  302. rasa/core/secrets_manager/factory.py +233 -0
  303. rasa/core/secrets_manager/secret_manager.py +262 -0
  304. rasa/core/secrets_manager/vault.py +576 -0
  305. rasa/core/test.py +1337 -0
  306. rasa/core/tracker_store.py +1664 -0
  307. rasa/core/train.py +107 -0
  308. rasa/core/training/__init__.py +89 -0
  309. rasa/core/training/converters/__init__.py +0 -0
  310. rasa/core/training/converters/responses_prefix_converter.py +119 -0
  311. rasa/core/training/interactive.py +1742 -0
  312. rasa/core/training/story_conflict.py +381 -0
  313. rasa/core/training/training.py +93 -0
  314. rasa/core/utils.py +344 -0
  315. rasa/core/visualize.py +70 -0
  316. rasa/dialogue_understanding/__init__.py +0 -0
  317. rasa/dialogue_understanding/coexistence/__init__.py +0 -0
  318. rasa/dialogue_understanding/coexistence/constants.py +4 -0
  319. rasa/dialogue_understanding/coexistence/intent_based_router.py +189 -0
  320. rasa/dialogue_understanding/coexistence/llm_based_router.py +261 -0
  321. rasa/dialogue_understanding/coexistence/router_template.jinja2 +12 -0
  322. rasa/dialogue_understanding/commands/__init__.py +45 -0
  323. rasa/dialogue_understanding/commands/can_not_handle_command.py +61 -0
  324. rasa/dialogue_understanding/commands/cancel_flow_command.py +116 -0
  325. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +48 -0
  326. rasa/dialogue_understanding/commands/clarify_command.py +77 -0
  327. rasa/dialogue_understanding/commands/command.py +85 -0
  328. rasa/dialogue_understanding/commands/correct_slots_command.py +288 -0
  329. rasa/dialogue_understanding/commands/error_command.py +67 -0
  330. rasa/dialogue_understanding/commands/free_form_answer_command.py +9 -0
  331. rasa/dialogue_understanding/commands/handle_code_change_command.py +64 -0
  332. rasa/dialogue_understanding/commands/human_handoff_command.py +57 -0
  333. rasa/dialogue_understanding/commands/knowledge_answer_command.py +48 -0
  334. rasa/dialogue_understanding/commands/noop_command.py +45 -0
  335. rasa/dialogue_understanding/commands/set_slot_command.py +125 -0
  336. rasa/dialogue_understanding/commands/skip_question_command.py +66 -0
  337. rasa/dialogue_understanding/commands/start_flow_command.py +98 -0
  338. rasa/dialogue_understanding/generator/__init__.py +6 -0
  339. rasa/dialogue_understanding/generator/command_generator.py +257 -0
  340. rasa/dialogue_understanding/generator/command_prompt_template.jinja2 +57 -0
  341. rasa/dialogue_understanding/generator/flow_document_template.jinja2 +4 -0
  342. rasa/dialogue_understanding/generator/flow_retrieval.py +410 -0
  343. rasa/dialogue_understanding/generator/llm_command_generator.py +637 -0
  344. rasa/dialogue_understanding/generator/nlu_command_adapter.py +157 -0
  345. rasa/dialogue_understanding/patterns/__init__.py +0 -0
  346. rasa/dialogue_understanding/patterns/cancel.py +111 -0
  347. rasa/dialogue_understanding/patterns/cannot_handle.py +43 -0
  348. rasa/dialogue_understanding/patterns/chitchat.py +37 -0
  349. rasa/dialogue_understanding/patterns/clarify.py +97 -0
  350. rasa/dialogue_understanding/patterns/code_change.py +41 -0
  351. rasa/dialogue_understanding/patterns/collect_information.py +90 -0
  352. rasa/dialogue_understanding/patterns/completed.py +40 -0
  353. rasa/dialogue_understanding/patterns/continue_interrupted.py +42 -0
  354. rasa/dialogue_understanding/patterns/correction.py +278 -0
  355. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +243 -0
  356. rasa/dialogue_understanding/patterns/human_handoff.py +37 -0
  357. rasa/dialogue_understanding/patterns/internal_error.py +47 -0
  358. rasa/dialogue_understanding/patterns/search.py +37 -0
  359. rasa/dialogue_understanding/patterns/skip_question.py +38 -0
  360. rasa/dialogue_understanding/processor/__init__.py +0 -0
  361. rasa/dialogue_understanding/processor/command_processor.py +578 -0
  362. rasa/dialogue_understanding/processor/command_processor_component.py +39 -0
  363. rasa/dialogue_understanding/stack/__init__.py +0 -0
  364. rasa/dialogue_understanding/stack/dialogue_stack.py +178 -0
  365. rasa/dialogue_understanding/stack/frames/__init__.py +19 -0
  366. rasa/dialogue_understanding/stack/frames/chit_chat_frame.py +27 -0
  367. rasa/dialogue_understanding/stack/frames/dialogue_stack_frame.py +137 -0
  368. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +157 -0
  369. rasa/dialogue_understanding/stack/frames/pattern_frame.py +10 -0
  370. rasa/dialogue_understanding/stack/frames/search_frame.py +27 -0
  371. rasa/dialogue_understanding/stack/utils.py +211 -0
  372. rasa/e2e_test/__init__.py +0 -0
  373. rasa/e2e_test/constants.py +10 -0
  374. rasa/e2e_test/e2e_test_case.py +322 -0
  375. rasa/e2e_test/e2e_test_result.py +34 -0
  376. rasa/e2e_test/e2e_test_runner.py +659 -0
  377. rasa/e2e_test/e2e_test_schema.yml +67 -0
  378. rasa/engine/__init__.py +0 -0
  379. rasa/engine/caching.py +464 -0
  380. rasa/engine/constants.py +17 -0
  381. rasa/engine/exceptions.py +14 -0
  382. rasa/engine/graph.py +625 -0
  383. rasa/engine/loader.py +36 -0
  384. rasa/engine/recipes/__init__.py +0 -0
  385. rasa/engine/recipes/config_files/default_config.yml +44 -0
  386. rasa/engine/recipes/default_components.py +99 -0
  387. rasa/engine/recipes/default_recipe.py +1252 -0
  388. rasa/engine/recipes/graph_recipe.py +79 -0
  389. rasa/engine/recipes/recipe.py +93 -0
  390. rasa/engine/runner/__init__.py +0 -0
  391. rasa/engine/runner/dask.py +256 -0
  392. rasa/engine/runner/interface.py +49 -0
  393. rasa/engine/storage/__init__.py +0 -0
  394. rasa/engine/storage/local_model_storage.py +248 -0
  395. rasa/engine/storage/resource.py +110 -0
  396. rasa/engine/storage/storage.py +203 -0
  397. rasa/engine/training/__init__.py +0 -0
  398. rasa/engine/training/components.py +176 -0
  399. rasa/engine/training/fingerprinting.py +64 -0
  400. rasa/engine/training/graph_trainer.py +256 -0
  401. rasa/engine/training/hooks.py +164 -0
  402. rasa/engine/validation.py +839 -0
  403. rasa/env.py +5 -0
  404. rasa/exceptions.py +69 -0
  405. rasa/graph_components/__init__.py +0 -0
  406. rasa/graph_components/converters/__init__.py +0 -0
  407. rasa/graph_components/converters/nlu_message_converter.py +48 -0
  408. rasa/graph_components/providers/__init__.py +0 -0
  409. rasa/graph_components/providers/domain_for_core_training_provider.py +87 -0
  410. rasa/graph_components/providers/domain_provider.py +71 -0
  411. rasa/graph_components/providers/flows_provider.py +74 -0
  412. rasa/graph_components/providers/forms_provider.py +44 -0
  413. rasa/graph_components/providers/nlu_training_data_provider.py +56 -0
  414. rasa/graph_components/providers/responses_provider.py +44 -0
  415. rasa/graph_components/providers/rule_only_provider.py +49 -0
  416. rasa/graph_components/providers/story_graph_provider.py +43 -0
  417. rasa/graph_components/providers/training_tracker_provider.py +55 -0
  418. rasa/graph_components/validators/__init__.py +0 -0
  419. rasa/graph_components/validators/default_recipe_validator.py +552 -0
  420. rasa/graph_components/validators/finetuning_validator.py +302 -0
  421. rasa/hooks.py +113 -0
  422. rasa/jupyter.py +63 -0
  423. rasa/keys +1 -0
  424. rasa/markers/__init__.py +0 -0
  425. rasa/markers/marker.py +269 -0
  426. rasa/markers/marker_base.py +828 -0
  427. rasa/markers/upload.py +74 -0
  428. rasa/markers/validate.py +21 -0
  429. rasa/model.py +118 -0
  430. rasa/model_testing.py +457 -0
  431. rasa/model_training.py +535 -0
  432. rasa/nlu/__init__.py +7 -0
  433. rasa/nlu/classifiers/__init__.py +3 -0
  434. rasa/nlu/classifiers/classifier.py +5 -0
  435. rasa/nlu/classifiers/diet_classifier.py +1874 -0
  436. rasa/nlu/classifiers/fallback_classifier.py +192 -0
  437. rasa/nlu/classifiers/keyword_intent_classifier.py +188 -0
  438. rasa/nlu/classifiers/llm_intent_classifier.py +519 -0
  439. rasa/nlu/classifiers/logistic_regression_classifier.py +240 -0
  440. rasa/nlu/classifiers/mitie_intent_classifier.py +156 -0
  441. rasa/nlu/classifiers/regex_message_handler.py +56 -0
  442. rasa/nlu/classifiers/sklearn_intent_classifier.py +309 -0
  443. rasa/nlu/constants.py +77 -0
  444. rasa/nlu/convert.py +40 -0
  445. rasa/nlu/emulators/__init__.py +0 -0
  446. rasa/nlu/emulators/dialogflow.py +55 -0
  447. rasa/nlu/emulators/emulator.py +49 -0
  448. rasa/nlu/emulators/luis.py +86 -0
  449. rasa/nlu/emulators/no_emulator.py +10 -0
  450. rasa/nlu/emulators/wit.py +56 -0
  451. rasa/nlu/extractors/__init__.py +0 -0
  452. rasa/nlu/extractors/crf_entity_extractor.py +672 -0
  453. rasa/nlu/extractors/duckling_entity_extractor.py +206 -0
  454. rasa/nlu/extractors/entity_synonyms.py +178 -0
  455. rasa/nlu/extractors/extractor.py +470 -0
  456. rasa/nlu/extractors/mitie_entity_extractor.py +293 -0
  457. rasa/nlu/extractors/regex_entity_extractor.py +220 -0
  458. rasa/nlu/extractors/spacy_entity_extractor.py +95 -0
  459. rasa/nlu/featurizers/__init__.py +0 -0
  460. rasa/nlu/featurizers/dense_featurizer/__init__.py +0 -0
  461. rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +449 -0
  462. rasa/nlu/featurizers/dense_featurizer/dense_featurizer.py +57 -0
  463. rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +772 -0
  464. rasa/nlu/featurizers/dense_featurizer/mitie_featurizer.py +170 -0
  465. rasa/nlu/featurizers/dense_featurizer/spacy_featurizer.py +132 -0
  466. rasa/nlu/featurizers/featurizer.py +89 -0
  467. rasa/nlu/featurizers/sparse_featurizer/__init__.py +0 -0
  468. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +840 -0
  469. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +539 -0
  470. rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +269 -0
  471. rasa/nlu/featurizers/sparse_featurizer/sparse_featurizer.py +9 -0
  472. rasa/nlu/model.py +24 -0
  473. rasa/nlu/persistor.py +240 -0
  474. rasa/nlu/run.py +27 -0
  475. rasa/nlu/selectors/__init__.py +0 -0
  476. rasa/nlu/selectors/response_selector.py +990 -0
  477. rasa/nlu/test.py +1943 -0
  478. rasa/nlu/tokenizers/__init__.py +0 -0
  479. rasa/nlu/tokenizers/jieba_tokenizer.py +148 -0
  480. rasa/nlu/tokenizers/mitie_tokenizer.py +75 -0
  481. rasa/nlu/tokenizers/spacy_tokenizer.py +72 -0
  482. rasa/nlu/tokenizers/tokenizer.py +239 -0
  483. rasa/nlu/tokenizers/whitespace_tokenizer.py +106 -0
  484. rasa/nlu/utils/__init__.py +35 -0
  485. rasa/nlu/utils/bilou_utils.py +462 -0
  486. rasa/nlu/utils/hugging_face/__init__.py +0 -0
  487. rasa/nlu/utils/hugging_face/registry.py +108 -0
  488. rasa/nlu/utils/hugging_face/transformers_pre_post_processors.py +311 -0
  489. rasa/nlu/utils/mitie_utils.py +113 -0
  490. rasa/nlu/utils/pattern_utils.py +168 -0
  491. rasa/nlu/utils/spacy_utils.py +312 -0
  492. rasa/plugin.py +90 -0
  493. rasa/server.py +1536 -0
  494. rasa/shared/__init__.py +0 -0
  495. rasa/shared/constants.py +181 -0
  496. rasa/shared/core/__init__.py +0 -0
  497. rasa/shared/core/constants.py +168 -0
  498. rasa/shared/core/conversation.py +46 -0
  499. rasa/shared/core/domain.py +2106 -0
  500. rasa/shared/core/events.py +2507 -0
  501. rasa/shared/core/flows/__init__.py +7 -0
  502. rasa/shared/core/flows/flow.py +353 -0
  503. rasa/shared/core/flows/flow_step.py +146 -0
  504. rasa/shared/core/flows/flow_step_links.py +319 -0
  505. rasa/shared/core/flows/flow_step_sequence.py +70 -0
  506. rasa/shared/core/flows/flows_list.py +211 -0
  507. rasa/shared/core/flows/flows_yaml_schema.json +217 -0
  508. rasa/shared/core/flows/nlu_trigger.py +117 -0
  509. rasa/shared/core/flows/steps/__init__.py +24 -0
  510. rasa/shared/core/flows/steps/action.py +51 -0
  511. rasa/shared/core/flows/steps/call.py +64 -0
  512. rasa/shared/core/flows/steps/collect.py +112 -0
  513. rasa/shared/core/flows/steps/constants.py +5 -0
  514. rasa/shared/core/flows/steps/continuation.py +36 -0
  515. rasa/shared/core/flows/steps/end.py +22 -0
  516. rasa/shared/core/flows/steps/internal.py +44 -0
  517. rasa/shared/core/flows/steps/link.py +51 -0
  518. rasa/shared/core/flows/steps/no_operation.py +48 -0
  519. rasa/shared/core/flows/steps/set_slots.py +50 -0
  520. rasa/shared/core/flows/steps/start.py +30 -0
  521. rasa/shared/core/flows/validation.py +527 -0
  522. rasa/shared/core/flows/yaml_flows_io.py +278 -0
  523. rasa/shared/core/generator.py +907 -0
  524. rasa/shared/core/slot_mappings.py +235 -0
  525. rasa/shared/core/slots.py +647 -0
  526. rasa/shared/core/trackers.py +1159 -0
  527. rasa/shared/core/training_data/__init__.py +0 -0
  528. rasa/shared/core/training_data/loading.py +90 -0
  529. rasa/shared/core/training_data/story_reader/__init__.py +0 -0
  530. rasa/shared/core/training_data/story_reader/story_reader.py +129 -0
  531. rasa/shared/core/training_data/story_reader/story_step_builder.py +168 -0
  532. rasa/shared/core/training_data/story_reader/yaml_story_reader.py +888 -0
  533. rasa/shared/core/training_data/story_writer/__init__.py +0 -0
  534. rasa/shared/core/training_data/story_writer/story_writer.py +76 -0
  535. rasa/shared/core/training_data/story_writer/yaml_story_writer.py +442 -0
  536. rasa/shared/core/training_data/structures.py +838 -0
  537. rasa/shared/core/training_data/visualization.html +146 -0
  538. rasa/shared/core/training_data/visualization.py +603 -0
  539. rasa/shared/data.py +192 -0
  540. rasa/shared/engine/__init__.py +0 -0
  541. rasa/shared/engine/caching.py +26 -0
  542. rasa/shared/exceptions.py +129 -0
  543. rasa/shared/importers/__init__.py +0 -0
  544. rasa/shared/importers/importer.py +705 -0
  545. rasa/shared/importers/multi_project.py +203 -0
  546. rasa/shared/importers/rasa.py +100 -0
  547. rasa/shared/importers/utils.py +34 -0
  548. rasa/shared/nlu/__init__.py +0 -0
  549. rasa/shared/nlu/constants.py +45 -0
  550. rasa/shared/nlu/interpreter.py +10 -0
  551. rasa/shared/nlu/training_data/__init__.py +0 -0
  552. rasa/shared/nlu/training_data/entities_parser.py +209 -0
  553. rasa/shared/nlu/training_data/features.py +374 -0
  554. rasa/shared/nlu/training_data/formats/__init__.py +10 -0
  555. rasa/shared/nlu/training_data/formats/dialogflow.py +162 -0
  556. rasa/shared/nlu/training_data/formats/luis.py +87 -0
  557. rasa/shared/nlu/training_data/formats/rasa.py +135 -0
  558. rasa/shared/nlu/training_data/formats/rasa_yaml.py +605 -0
  559. rasa/shared/nlu/training_data/formats/readerwriter.py +245 -0
  560. rasa/shared/nlu/training_data/formats/wit.py +52 -0
  561. rasa/shared/nlu/training_data/loading.py +137 -0
  562. rasa/shared/nlu/training_data/lookup_tables_parser.py +30 -0
  563. rasa/shared/nlu/training_data/message.py +477 -0
  564. rasa/shared/nlu/training_data/schemas/__init__.py +0 -0
  565. rasa/shared/nlu/training_data/schemas/data_schema.py +85 -0
  566. rasa/shared/nlu/training_data/schemas/nlu.yml +53 -0
  567. rasa/shared/nlu/training_data/schemas/responses.yml +70 -0
  568. rasa/shared/nlu/training_data/synonyms_parser.py +42 -0
  569. rasa/shared/nlu/training_data/training_data.py +732 -0
  570. rasa/shared/nlu/training_data/util.py +223 -0
  571. rasa/shared/providers/__init__.py +0 -0
  572. rasa/shared/providers/openai/__init__.py +0 -0
  573. rasa/shared/providers/openai/clients.py +43 -0
  574. rasa/shared/providers/openai/session_handler.py +110 -0
  575. rasa/shared/utils/__init__.py +0 -0
  576. rasa/shared/utils/cli.py +72 -0
  577. rasa/shared/utils/common.py +308 -0
  578. rasa/shared/utils/constants.py +1 -0
  579. rasa/shared/utils/io.py +403 -0
  580. rasa/shared/utils/llm.py +405 -0
  581. rasa/shared/utils/pykwalify_extensions.py +26 -0
  582. rasa/shared/utils/schemas/__init__.py +0 -0
  583. rasa/shared/utils/schemas/config.yml +2 -0
  584. rasa/shared/utils/schemas/domain.yml +142 -0
  585. rasa/shared/utils/schemas/events.py +212 -0
  586. rasa/shared/utils/schemas/model_config.yml +46 -0
  587. rasa/shared/utils/schemas/stories.yml +173 -0
  588. rasa/shared/utils/yaml.py +777 -0
  589. rasa/studio/__init__.py +0 -0
  590. rasa/studio/auth.py +252 -0
  591. rasa/studio/config.py +127 -0
  592. rasa/studio/constants.py +16 -0
  593. rasa/studio/data_handler.py +352 -0
  594. rasa/studio/download.py +350 -0
  595. rasa/studio/train.py +136 -0
  596. rasa/studio/upload.py +408 -0
  597. rasa/telemetry.py +1583 -0
  598. rasa/tracing/__init__.py +0 -0
  599. rasa/tracing/config.py +338 -0
  600. rasa/tracing/constants.py +38 -0
  601. rasa/tracing/instrumentation/__init__.py +0 -0
  602. rasa/tracing/instrumentation/attribute_extractors.py +663 -0
  603. rasa/tracing/instrumentation/instrumentation.py +939 -0
  604. rasa/tracing/instrumentation/intentless_policy_instrumentation.py +142 -0
  605. rasa/tracing/instrumentation/metrics.py +206 -0
  606. rasa/tracing/metric_instrument_provider.py +125 -0
  607. rasa/utils/__init__.py +0 -0
  608. rasa/utils/beta.py +83 -0
  609. rasa/utils/cli.py +27 -0
  610. rasa/utils/common.py +635 -0
  611. rasa/utils/converter.py +53 -0
  612. rasa/utils/endpoints.py +303 -0
  613. rasa/utils/io.py +326 -0
  614. rasa/utils/licensing.py +319 -0
  615. rasa/utils/log_utils.py +174 -0
  616. rasa/utils/mapper.py +210 -0
  617. rasa/utils/ml_utils.py +145 -0
  618. rasa/utils/plotting.py +362 -0
  619. rasa/utils/singleton.py +23 -0
  620. rasa/utils/tensorflow/__init__.py +0 -0
  621. rasa/utils/tensorflow/callback.py +112 -0
  622. rasa/utils/tensorflow/constants.py +116 -0
  623. rasa/utils/tensorflow/crf.py +492 -0
  624. rasa/utils/tensorflow/data_generator.py +440 -0
  625. rasa/utils/tensorflow/environment.py +161 -0
  626. rasa/utils/tensorflow/exceptions.py +5 -0
  627. rasa/utils/tensorflow/layers.py +1565 -0
  628. rasa/utils/tensorflow/layers_utils.py +113 -0
  629. rasa/utils/tensorflow/metrics.py +281 -0
  630. rasa/utils/tensorflow/model_data.py +991 -0
  631. rasa/utils/tensorflow/model_data_utils.py +500 -0
  632. rasa/utils/tensorflow/models.py +936 -0
  633. rasa/utils/tensorflow/rasa_layers.py +1094 -0
  634. rasa/utils/tensorflow/transformer.py +640 -0
  635. rasa/utils/tensorflow/types.py +6 -0
  636. rasa/utils/train_utils.py +572 -0
  637. rasa/utils/yaml.py +54 -0
  638. rasa/validator.py +1035 -0
  639. rasa/version.py +3 -0
  640. rasa_pro-3.8.16.dist-info/METADATA +528 -0
  641. rasa_pro-3.8.16.dist-info/NOTICE +5 -0
  642. rasa_pro-3.8.16.dist-info/RECORD +644 -0
  643. rasa_pro-3.8.16.dist-info/WHEEL +4 -0
  644. rasa_pro-3.8.16.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,1276 @@
1
+ from __future__ import annotations
2
+ import copy
3
+ import functools
4
+ import logging
5
+ import structlog
6
+ from typing import Any, List, DefaultDict, Dict, Text, Optional, Set, Tuple, cast
7
+
8
+ from tqdm import tqdm
9
+ import numpy as np
10
+ import json
11
+ from collections import defaultdict
12
+
13
+ from rasa.engine.graph import ExecutionContext
14
+ from rasa.engine.recipes.default_recipe import DefaultV1Recipe
15
+ from rasa.engine.storage.resource import Resource
16
+ from rasa.engine.storage.storage import ModelStorage
17
+ from rasa.shared.constants import DOCS_URL_RULES
18
+ from rasa.shared.exceptions import RasaException
19
+ import rasa.shared.utils.io
20
+ from rasa.shared.core.events import LoopInterrupted, UserUttered, ActionExecuted
21
+ from rasa.core.featurizers.tracker_featurizers import TrackerFeaturizer
22
+ from rasa.core.policies.memoization import MemoizationPolicy
23
+ from rasa.core.policies.policy import SupportedData, PolicyPrediction
24
+ from rasa.shared.core.trackers import (
25
+ DialogueStateTracker,
26
+ get_active_loop_name,
27
+ is_prev_action_listen_in_state,
28
+ )
29
+ from rasa.shared.core.generator import TrackerWithCachedStates
30
+ from rasa.core.constants import (
31
+ DEFAULT_CORE_FALLBACK_THRESHOLD,
32
+ RULE_POLICY_PRIORITY,
33
+ POLICY_PRIORITY,
34
+ POLICY_MAX_HISTORY,
35
+ )
36
+ from rasa.shared.core.constants import (
37
+ USER_INTENT_RESTART,
38
+ USER_INTENT_BACK,
39
+ USER_INTENT_SESSION_START,
40
+ ACTION_LISTEN_NAME,
41
+ ACTION_RESTART_NAME,
42
+ ACTION_SESSION_START_NAME,
43
+ ACTION_DEFAULT_FALLBACK_NAME,
44
+ ACTION_BACK_NAME,
45
+ RULE_SNIPPET_ACTION_NAME,
46
+ SHOULD_NOT_BE_SET,
47
+ PREVIOUS_ACTION,
48
+ LOOP_NAME,
49
+ SLOTS,
50
+ ACTIVE_LOOP,
51
+ RULE_ONLY_SLOTS,
52
+ RULE_ONLY_LOOPS,
53
+ )
54
+ from rasa.shared.core.domain import InvalidDomain, State, Domain
55
+ from rasa.shared.nlu.constants import ACTION_NAME, INTENT_NAME_KEY
56
+ import rasa.core.test
57
+ from rasa.core.training.training import create_action_fingerprints, ActionFingerprint
58
+
59
+ logger = logging.getLogger(__name__)
60
+ structlogger = structlog.get_logger()
61
+
62
+
63
+ # These are Rasa Open Source default actions and overrule everything at any time.
64
+ DEFAULT_ACTION_MAPPINGS = {
65
+ USER_INTENT_RESTART: ACTION_RESTART_NAME,
66
+ USER_INTENT_BACK: ACTION_BACK_NAME,
67
+ USER_INTENT_SESSION_START: ACTION_SESSION_START_NAME,
68
+ }
69
+
70
+ RULES = "rules"
71
+ RULES_FOR_LOOP_UNHAPPY_PATH = "rules_for_loop_unhappy_path"
72
+ RULES_NOT_IN_STORIES = "rules_not_in_stories"
73
+
74
+ LOOP_WAS_INTERRUPTED = "loop_was_interrupted"
75
+ DO_NOT_PREDICT_LOOP_ACTION = "do_not_predict_loop_action"
76
+
77
+ DEFAULT_RULES = "predicting default action with intent "
78
+ LOOP_RULES = "handling active loops and forms - "
79
+ LOOP_RULES_SEPARATOR = " - "
80
+
81
+
82
+ class InvalidRule(RasaException):
83
+ """Exception that can be raised when rules are not valid."""
84
+
85
+ def __init__(self, message: Text) -> None:
86
+ super().__init__()
87
+ self.message = message
88
+
89
+ def __str__(self) -> Text:
90
+ return self.message + (
91
+ f"\nYou can find more information about the usage of "
92
+ f"rules at {DOCS_URL_RULES}. "
93
+ )
94
+
95
+
96
+ @DefaultV1Recipe.register(
97
+ DefaultV1Recipe.ComponentType.POLICY_WITHOUT_END_TO_END_SUPPORT, is_trainable=True
98
+ )
99
+ class RulePolicy(MemoizationPolicy):
100
+ """Policy which handles all the rules."""
101
+
102
+ # rules use explicit json strings
103
+ ENABLE_FEATURE_STRING_COMPRESSION = False
104
+
105
+ # number of user inputs that is allowed in case rules are restricted
106
+ ALLOWED_NUMBER_OF_USER_INPUTS = 1
107
+
108
+ @staticmethod
109
+ def supported_data() -> SupportedData:
110
+ """The type of data supported by this policy.
111
+
112
+ Returns:
113
+ The data type supported by this policy (ML and rule data).
114
+ """
115
+ return SupportedData.ML_AND_RULE_DATA
116
+
117
+ @staticmethod
118
+ def get_default_config() -> Dict[Text, Any]:
119
+ """Returns the default config (see parent class for full docstring)."""
120
+ return {
121
+ # Priority of the policy which is used if multiple policies predict
122
+ # actions with the same confidence.
123
+ POLICY_PRIORITY: RULE_POLICY_PRIORITY,
124
+ # Confidence of the prediction if no rule matched and de-facto
125
+ # threshold for a core fallback.
126
+ "core_fallback_threshold": DEFAULT_CORE_FALLBACK_THRESHOLD,
127
+ # Name of the action which should be predicted if no rule matched.
128
+ "core_fallback_action_name": ACTION_DEFAULT_FALLBACK_NAME,
129
+ # If `True` `core_fallback_action_name` is predicted in case no rule
130
+ # matched.
131
+ "enable_fallback_prediction": True,
132
+ # If `True` rules are restricted to contain a maximum of 1
133
+ # user message. This is used to avoid that users build a state machine
134
+ # using the rules.
135
+ "restrict_rules": True,
136
+ # Whether to check for contradictions between rules and stories
137
+ "check_for_contradictions": True,
138
+ # the policy will use the confidence of NLU on the latest
139
+ # user message to set the confidence of the action
140
+ "use_nlu_confidence_as_score": False,
141
+ }
142
+
143
+ def __init__(
144
+ self,
145
+ config: Dict[Text, Any],
146
+ model_storage: ModelStorage,
147
+ resource: Resource,
148
+ execution_context: ExecutionContext,
149
+ featurizer: Optional[TrackerFeaturizer] = None,
150
+ lookup: Optional[Dict] = None,
151
+ ) -> None:
152
+ """Initializes the policy."""
153
+ # max history is set to `None` in order to capture any lengths of rule stories
154
+ config[POLICY_MAX_HISTORY] = None
155
+
156
+ super().__init__(
157
+ config, model_storage, resource, execution_context, featurizer, lookup
158
+ )
159
+
160
+ self._fallback_action_name = config["core_fallback_action_name"]
161
+ self._enable_fallback_prediction = config["enable_fallback_prediction"]
162
+ self._check_for_contradictions = config["check_for_contradictions"]
163
+
164
+ self._rules_sources: DefaultDict[Text, List[Tuple[Text, Text]]] = defaultdict(
165
+ list
166
+ )
167
+
168
+ @classmethod
169
+ def raise_if_incompatible_with_domain(
170
+ cls, config: Dict[Text, Any], domain: Domain
171
+ ) -> None:
172
+ """Checks whether the domains action names match the configured fallback.
173
+
174
+ Args:
175
+ config: configuration of a `RulePolicy`
176
+ domain: a domain
177
+ Raises:
178
+ `InvalidDomain` if this policy is incompatible with the domain
179
+ """
180
+ fallback_action_name = config.get("core_fallback_action_name", None)
181
+ if (
182
+ fallback_action_name
183
+ and fallback_action_name not in domain.action_names_or_texts
184
+ ):
185
+ raise InvalidDomain(
186
+ f"The fallback action '{fallback_action_name}' which was "
187
+ f"configured for the {RulePolicy.__name__} must be "
188
+ f"present in the domain."
189
+ )
190
+
191
+ @staticmethod
192
+ def _is_rule_snippet_state(state: State) -> bool:
193
+ prev_action_name = state.get(PREVIOUS_ACTION, {}).get(ACTION_NAME)
194
+ return prev_action_name == RULE_SNIPPET_ACTION_NAME
195
+
196
+ def _create_feature_key(self, states: List[State]) -> Optional[Text]:
197
+ new_states: List[State] = []
198
+ for state in reversed(states):
199
+ if self._is_rule_snippet_state(state):
200
+ # remove all states before RULE_SNIPPET_ACTION_NAME
201
+ break
202
+ new_states.insert(0, state)
203
+
204
+ if not new_states:
205
+ return None
206
+
207
+ # we sort keys to make sure that the same states
208
+ # represented as dictionaries have the same json strings
209
+ return json.dumps(new_states, sort_keys=True)
210
+
211
+ @staticmethod
212
+ def _states_for_unhappy_loop_predictions(states: List[State]) -> List[State]:
213
+ """Modifies the states to create feature keys for loop unhappy path conditions.
214
+
215
+ Args:
216
+ states: a representation of a tracker
217
+ as a list of dictionaries containing features
218
+
219
+ Returns:
220
+ modified states
221
+ """
222
+ # leave only last 2 dialogue turns to
223
+ # - capture previous meaningful action before action_listen
224
+ # - ignore previous intent
225
+ if len(states) == 1 or not states[-2].get(PREVIOUS_ACTION):
226
+ return [states[-1]]
227
+ else:
228
+ return [{PREVIOUS_ACTION: states[-2][PREVIOUS_ACTION]}, states[-1]]
229
+
230
+ @staticmethod
231
+ def _remove_rule_snippet_predictions(lookup: Dict[Text, Text]) -> Dict[Text, Text]:
232
+ # Delete rules if it would predict the RULE_SNIPPET_ACTION_NAME action
233
+ return {
234
+ feature_key: action
235
+ for feature_key, action in lookup.items()
236
+ if action != RULE_SNIPPET_ACTION_NAME
237
+ }
238
+
239
+ def _create_loop_unhappy_lookup_from_states(
240
+ self,
241
+ trackers_as_states: List[List[State]],
242
+ trackers_as_actions: List[List[Text]],
243
+ ) -> Dict[Text, Text]:
244
+ """Creates lookup dictionary from the tracker represented as states.
245
+
246
+ Args:
247
+ trackers_as_states: representation of the trackers as a list of states
248
+ trackers_as_actions: representation of the trackers as a list of actions
249
+
250
+ Returns:
251
+ lookup dictionary
252
+ """
253
+ lookup = {}
254
+ for states, actions in zip(trackers_as_states, trackers_as_actions):
255
+ action = actions[0]
256
+ active_loop = get_active_loop_name(states[-1])
257
+ # even if there are two identical feature keys
258
+ # their loop will be the same
259
+ if not active_loop:
260
+ continue
261
+
262
+ states = self._states_for_unhappy_loop_predictions(states)
263
+ feature_key = self._create_feature_key(states)
264
+ if not feature_key:
265
+ continue
266
+
267
+ # Since rule snippets and stories inside the loop contain
268
+ # only unhappy paths, notify the loop that
269
+ # it was predicted after an answer to a different question and
270
+ # therefore it should not validate user input
271
+ if (
272
+ # loop is predicted after action_listen in unhappy path,
273
+ # therefore no validation is needed
274
+ is_prev_action_listen_in_state(states[-1])
275
+ and action == active_loop
276
+ ):
277
+ lookup[feature_key] = LOOP_WAS_INTERRUPTED
278
+ elif (
279
+ # some action other than active_loop is predicted in unhappy path,
280
+ # therefore active_loop shouldn't be predicted by the rule
281
+ not is_prev_action_listen_in_state(states[-1])
282
+ and action != active_loop
283
+ ):
284
+ lookup[feature_key] = DO_NOT_PREDICT_LOOP_ACTION
285
+ return lookup
286
+
287
+ def _check_rule_restriction(
288
+ self, rule_trackers: List[TrackerWithCachedStates]
289
+ ) -> None:
290
+ rules_exceeding_max_user_turns = []
291
+ for tracker in rule_trackers:
292
+ number_of_user_uttered = sum(
293
+ isinstance(event, UserUttered) for event in tracker.events
294
+ )
295
+ if number_of_user_uttered > self.ALLOWED_NUMBER_OF_USER_INPUTS:
296
+ rules_exceeding_max_user_turns.append(tracker.sender_id)
297
+
298
+ if rules_exceeding_max_user_turns:
299
+ raise InvalidRule(
300
+ f"Found rules '{', '.join(rules_exceeding_max_user_turns)}' "
301
+ f"that contain more than {self.ALLOWED_NUMBER_OF_USER_INPUTS} "
302
+ f"user message. Rules are not meant to hardcode a state machine. "
303
+ f"Please use stories for these cases."
304
+ )
305
+
306
+ @staticmethod
307
+ def _expected_but_missing_slots(
308
+ fingerprint: ActionFingerprint, state: State
309
+ ) -> Set[Text]:
310
+ expected_slots = set(fingerprint.slots)
311
+ current_slots = set(state.get(SLOTS, {}).keys())
312
+ # report all slots that are expected but aren't set in current slots
313
+ return expected_slots.difference(current_slots)
314
+
315
+ @staticmethod
316
+ def _check_active_loops_fingerprint(
317
+ fingerprint: ActionFingerprint, state: State
318
+ ) -> Set[Optional[Text]]:
319
+ expected_active_loops = set(fingerprint.active_loop)
320
+ # we don't use tracker.active_loop_name
321
+ # because we need to keep should_not_be_set
322
+ current_active_loop = state.get(ACTIVE_LOOP, {}).get(LOOP_NAME)
323
+ if current_active_loop in expected_active_loops:
324
+ # one of expected active loops is set
325
+ return set()
326
+
327
+ return expected_active_loops
328
+
329
+ @staticmethod
330
+ def _error_messages_from_fingerprints(
331
+ action_name: Text,
332
+ missing_fingerprint_slots: Set[Text],
333
+ fingerprint_active_loops: Set[Text],
334
+ rule_name: Text,
335
+ ) -> List[Text]:
336
+ error_messages = []
337
+ if action_name and missing_fingerprint_slots:
338
+ error_messages.append(
339
+ f"- the action '{action_name}' in rule '{rule_name}' does not set some "
340
+ f"of the slots that it sets in other rules. Slots not set in rule "
341
+ f"'{rule_name}': '{', '.join(missing_fingerprint_slots)}'. Please "
342
+ f"update the rule with an appropriate slot or if it is the last action "
343
+ f"add 'wait_for_user_input: false' after this action."
344
+ )
345
+ if action_name and fingerprint_active_loops:
346
+ # substitute `SHOULD_NOT_BE_SET` with `null` so that users
347
+ # know what to put in their rules
348
+ fingerprint_active_loops = set(
349
+ "null" if active_loop == SHOULD_NOT_BE_SET else active_loop
350
+ for active_loop in fingerprint_active_loops
351
+ )
352
+ # add action_name to active loop so that users
353
+ # know what to put in their rules
354
+ fingerprint_active_loops.add(action_name)
355
+
356
+ error_messages.append(
357
+ f"- the form '{action_name}' in rule '{rule_name}' does not set "
358
+ f"the 'active_loop', that it sets in other rules: "
359
+ f"'{', '.join(fingerprint_active_loops)}'. Please update the rule with "
360
+ f"the appropriate 'active loop' property or if it is the last action "
361
+ f"add 'wait_for_user_input: false' after this action."
362
+ )
363
+ return error_messages
364
+
365
+ def _check_for_incomplete_rules(
366
+ self, rule_trackers: List[TrackerWithCachedStates], domain: Domain
367
+ ) -> None:
368
+ logger.debug("Started checking if some rules are incomplete.")
369
+ # we need to use only fingerprints from rules
370
+ rule_fingerprints = create_action_fingerprints(rule_trackers, domain)
371
+ if not rule_fingerprints:
372
+ return
373
+
374
+ error_messages: List[Text] = []
375
+ for tracker in rule_trackers:
376
+ states = tracker.past_states(domain)
377
+ # the last action is always action listen
378
+ action_names = [
379
+ state.get(PREVIOUS_ACTION, {}).get(ACTION_NAME) for state in states[1:]
380
+ ] + [ACTION_LISTEN_NAME]
381
+
382
+ for state, action_name in zip(states, action_names):
383
+ previous_action_name = state.get(PREVIOUS_ACTION, {}).get(ACTION_NAME)
384
+ fingerprint = rule_fingerprints.get(previous_action_name)
385
+ if (
386
+ not previous_action_name
387
+ or not fingerprint
388
+ or action_name == RULE_SNIPPET_ACTION_NAME
389
+ or previous_action_name == RULE_SNIPPET_ACTION_NAME
390
+ ):
391
+ # do not check fingerprints for rule snippet action
392
+ # and don't raise if fingerprints are not satisfied
393
+ # for a previous action if current action is rule snippet action
394
+ continue
395
+
396
+ missing_expected_slots = self._expected_but_missing_slots(
397
+ fingerprint, state
398
+ )
399
+ expected_active_loops = self._check_active_loops_fingerprint(
400
+ fingerprint, state
401
+ )
402
+ error_messages.extend(
403
+ self._error_messages_from_fingerprints(
404
+ previous_action_name,
405
+ missing_expected_slots,
406
+ expected_active_loops,
407
+ tracker.sender_id,
408
+ )
409
+ )
410
+
411
+ if error_messages:
412
+ error_text = "\n".join(error_messages)
413
+ raise InvalidRule(
414
+ f"\nIncomplete rules found🚨\n\n{error_text}\n"
415
+ f"Please note that if some slots or active loops should not be set "
416
+ f"during prediction you need to explicitly set them to 'null' in the "
417
+ f"rules."
418
+ )
419
+
420
+ logger.debug("Found no incompletions in rules.")
421
+
422
+ @staticmethod
423
+ def _get_slots_loops_from_states(
424
+ trackers_as_states: List[List[State]],
425
+ ) -> Tuple[Set[Text], Set[Text]]:
426
+ slots = set()
427
+ loops = set()
428
+ for states in trackers_as_states:
429
+ for state in states:
430
+ slots.update(set(state.get(SLOTS, {}).keys()))
431
+ # FIXME: ideally we have better annotation for State, TypedDict
432
+ # could work but support in mypy is very limited. Dataclass are
433
+ # another option
434
+ active_loop = cast(Text, state.get(ACTIVE_LOOP, {}).get(LOOP_NAME))
435
+ if active_loop:
436
+ loops.add(active_loop)
437
+ return slots, loops
438
+
439
+ def _find_rule_only_slots_loops(
440
+ self,
441
+ rule_trackers_as_states: List[List[State]],
442
+ story_trackers_as_states: List[List[State]],
443
+ ) -> Tuple[List[Text], List[Text]]:
444
+ rule_slots, rule_loops = self._get_slots_loops_from_states(
445
+ rule_trackers_as_states
446
+ )
447
+ story_slots, story_loops = self._get_slots_loops_from_states(
448
+ story_trackers_as_states
449
+ )
450
+
451
+ # set is not json serializable, so convert to list
452
+ return (
453
+ list(rule_slots - story_slots - {SHOULD_NOT_BE_SET}),
454
+ list(rule_loops - story_loops - {SHOULD_NOT_BE_SET}),
455
+ )
456
+
457
+ def _predict_next_action(
458
+ self, tracker: TrackerWithCachedStates, domain: Domain
459
+ ) -> Tuple[Optional[Text], Optional[Text]]:
460
+ prediction, prediction_source = self._predict(tracker, domain)
461
+ probabilities = prediction.probabilities
462
+ # do not raise an error if RulePolicy didn't predict anything for stories;
463
+ # however for rules RulePolicy should always predict an action
464
+ predicted_action_name = None
465
+ if (
466
+ probabilities != self._default_predictions(domain)
467
+ or tracker.is_rule_tracker
468
+ ):
469
+ predicted_action_name = domain.action_names_or_texts[
470
+ np.argmax(probabilities)
471
+ ]
472
+
473
+ return predicted_action_name, prediction_source
474
+
475
+ def _predicted_action_name(
476
+ self, tracker: TrackerWithCachedStates, domain: Domain, gold_action_name: Text
477
+ ) -> Tuple[Optional[Text], Optional[Text]]:
478
+ predicted_action_name, prediction_source = self._predict_next_action(
479
+ tracker, domain
480
+ )
481
+ # if there is an active_loop,
482
+ # RulePolicy will always predict active_loop first,
483
+ # but inside loop unhappy path there might be another action
484
+ if (
485
+ tracker.active_loop_name
486
+ and predicted_action_name != gold_action_name
487
+ and predicted_action_name == tracker.active_loop_name
488
+ ):
489
+ rasa.core.test.emulate_loop_rejection(tracker)
490
+ predicted_action_name, prediction_source = self._predict_next_action(
491
+ tracker, domain
492
+ )
493
+
494
+ return predicted_action_name, prediction_source
495
+
496
+ def _collect_sources(
497
+ self,
498
+ tracker: TrackerWithCachedStates,
499
+ predicted_action_name: Optional[Text],
500
+ gold_action_name: Optional[Text],
501
+ prediction_source: Text,
502
+ ) -> None:
503
+ # we need to remember which action should be predicted by the rule
504
+ # in order to correctly output the names of the contradicting rules
505
+ rule_name = tracker.sender_id
506
+
507
+ if prediction_source is not None and (
508
+ prediction_source.startswith(DEFAULT_RULES)
509
+ or prediction_source.startswith(LOOP_RULES)
510
+ ):
511
+ # the real gold action contradict the one in the rules in this case
512
+ gold_action_name = predicted_action_name
513
+ rule_name = prediction_source
514
+
515
+ self._rules_sources[prediction_source].append((rule_name, gold_action_name))
516
+
517
+ @staticmethod
518
+ def _default_sources() -> Set[Text]:
519
+ return {
520
+ DEFAULT_RULES + default_intent
521
+ for default_intent in DEFAULT_ACTION_MAPPINGS.keys()
522
+ }
523
+
524
+ @staticmethod
525
+ def _handling_loop_sources(domain: Domain) -> Set[Text]:
526
+ loop_sources = set()
527
+ for loop_name in domain.form_names:
528
+ loop_sources.add(LOOP_RULES + loop_name)
529
+ loop_sources.add(
530
+ LOOP_RULES + loop_name + LOOP_RULES_SEPARATOR + ACTION_LISTEN_NAME
531
+ )
532
+ return loop_sources
533
+
534
+ def _should_delete(
535
+ self,
536
+ prediction_source: Text,
537
+ tracker: TrackerWithCachedStates,
538
+ predicted_action_name: Text,
539
+ ) -> bool:
540
+ """Checks whether this contradiction is due to action, intent pair.
541
+
542
+ Args:
543
+ prediction_source: the states that result in the prediction
544
+ tracker: the tracker that raises the contradiction
545
+ predicted_action_name: the action that was predicted
546
+
547
+ Returns:
548
+ true if the contradiction is a result of an action, intent pair in the rule.
549
+ """
550
+ if (
551
+ # only apply to contradicting story, not rule
552
+ tracker.is_rule_tracker
553
+ # only apply for prediction after unpredictable action
554
+ or prediction_source.count(PREVIOUS_ACTION) > 1
555
+ # only apply for prediction of action_listen
556
+ or predicted_action_name != ACTION_LISTEN_NAME
557
+ ):
558
+ return False
559
+ for source in self.lookup[RULES]:
560
+ # remove rule only if another action is predicted after action_listen
561
+ if (
562
+ source.startswith(prediction_source[:-2])
563
+ and not prediction_source == source
564
+ ):
565
+ return True
566
+ return False
567
+
568
+ def _check_prediction(
569
+ self,
570
+ tracker: TrackerWithCachedStates,
571
+ predicted_action_name: Optional[Text],
572
+ gold_action_name: Text,
573
+ prediction_source: Optional[Text],
574
+ ) -> List[Text]:
575
+ # FIXME: `predicted_action_name` and `prediction_source` are
576
+ # either None together or defined together. This could be improved
577
+ # by better typing in this class, but requires some refactoring
578
+ if (
579
+ not predicted_action_name
580
+ or not prediction_source
581
+ or predicted_action_name == gold_action_name
582
+ ):
583
+ return []
584
+
585
+ if self._should_delete(prediction_source, tracker, predicted_action_name):
586
+ self.lookup[RULES].pop(prediction_source)
587
+ return []
588
+
589
+ tracker_type = "rule" if tracker.is_rule_tracker else "story"
590
+ contradicting_rules = {
591
+ rule_name
592
+ for rule_name, action_name in self._rules_sources[prediction_source]
593
+ if action_name != gold_action_name
594
+ }
595
+
596
+ if not contradicting_rules:
597
+ return []
598
+
599
+ error_message = (
600
+ f"- the prediction of the action '{gold_action_name}' in {tracker_type} "
601
+ f"'{tracker.sender_id}' "
602
+ f"is contradicting with rule(s) '{', '.join(contradicting_rules)}'"
603
+ )
604
+ # outputting predicted action 'action_default_fallback' is confusing
605
+ if predicted_action_name != self._fallback_action_name:
606
+ error_message += f" which predicted action '{predicted_action_name}'"
607
+
608
+ return [error_message + "."]
609
+
610
+ def _run_prediction_on_trackers(
611
+ self,
612
+ trackers: List[TrackerWithCachedStates],
613
+ domain: Domain,
614
+ collect_sources: bool,
615
+ ) -> Tuple[List[Text], Set[Optional[Text]]]:
616
+ if collect_sources:
617
+ self._rules_sources = defaultdict(list)
618
+
619
+ error_messages = []
620
+ rules_used_in_stories = set()
621
+ pbar = tqdm(
622
+ trackers,
623
+ desc="Processed trackers",
624
+ disable=rasa.shared.utils.io.is_logging_disabled(),
625
+ )
626
+ for tracker in pbar:
627
+ running_tracker = tracker.init_copy()
628
+ running_tracker.sender_id = tracker.sender_id
629
+ # the first action is always unpredictable
630
+ next_action_is_unpredictable = True
631
+ for event in tracker.applied_events(True):
632
+ if not isinstance(event, ActionExecuted):
633
+ running_tracker.update(event)
634
+ continue
635
+
636
+ if event.action_name == RULE_SNIPPET_ACTION_NAME:
637
+ # notify that the action after RULE_SNIPPET_ACTION_NAME is
638
+ # unpredictable
639
+ next_action_is_unpredictable = True
640
+ running_tracker.update(event)
641
+ continue
642
+
643
+ # do not run prediction on unpredictable actions
644
+ if next_action_is_unpredictable or event.unpredictable:
645
+ next_action_is_unpredictable = False # reset unpredictability
646
+ running_tracker.update(event)
647
+ continue
648
+
649
+ gold_action_name = event.action_name or event.action_text
650
+ predicted_action_name, prediction_source = self._predicted_action_name(
651
+ running_tracker, domain, gold_action_name
652
+ )
653
+ if collect_sources:
654
+ if prediction_source:
655
+ self._collect_sources(
656
+ running_tracker,
657
+ predicted_action_name,
658
+ gold_action_name,
659
+ prediction_source,
660
+ )
661
+ else:
662
+ # to be able to remove only rules turns from the dialogue history
663
+ # for ML policies,
664
+ # we need to know which rules were used in ML trackers
665
+ if (
666
+ not tracker.is_rule_tracker
667
+ and predicted_action_name == gold_action_name
668
+ ):
669
+ rules_used_in_stories.add(prediction_source)
670
+
671
+ error_messages += self._check_prediction(
672
+ running_tracker,
673
+ predicted_action_name,
674
+ gold_action_name,
675
+ prediction_source,
676
+ )
677
+
678
+ running_tracker.update(event)
679
+
680
+ return error_messages, rules_used_in_stories
681
+
682
+ def _collect_rule_sources(
683
+ self, rule_trackers: List[TrackerWithCachedStates], domain: Domain
684
+ ) -> None:
685
+ self._run_prediction_on_trackers(rule_trackers, domain, collect_sources=True)
686
+
687
+ def _find_contradicting_and_used_in_stories_rules(
688
+ self, trackers: List[TrackerWithCachedStates], domain: Domain
689
+ ) -> Tuple[List[Text], Set[Optional[Text]]]:
690
+ return self._run_prediction_on_trackers(trackers, domain, collect_sources=False)
691
+
692
+ def _analyze_rules(
693
+ self,
694
+ rule_trackers: List[TrackerWithCachedStates],
695
+ all_trackers: List[TrackerWithCachedStates],
696
+ domain: Domain,
697
+ ) -> List[Text]:
698
+ """Analyzes learned rules by running prediction on training trackers.
699
+
700
+ This method collects error messages for contradicting rules
701
+ and creates the lookup for rules that are not present in the stories.
702
+
703
+ Args:
704
+ rule_trackers: The list of the rule trackers.
705
+ all_trackers: The list of all trackers.
706
+ domain: The domain.
707
+
708
+ Returns:
709
+ Rules that are not present in the stories.
710
+ """
711
+ logger.debug("Started checking rules and stories for contradictions.")
712
+ # during training we run `predict_action_probabilities` to check for
713
+ # contradicting rules.
714
+ # We silent prediction debug to avoid too many logs during these checks.
715
+ logger_level = logger.level
716
+ logger.setLevel(logging.WARNING)
717
+
718
+ # we need to run prediction on rule trackers twice, because we need to collect
719
+ # the information about which rule snippets contributed to the learned rules
720
+ self._collect_rule_sources(rule_trackers, domain)
721
+ (
722
+ error_messages,
723
+ rules_used_in_stories,
724
+ ) = self._find_contradicting_and_used_in_stories_rules(all_trackers, domain)
725
+
726
+ logger.setLevel(logger_level) # reset logger level
727
+ if error_messages:
728
+ error_text = "\n".join(error_messages)
729
+ raise InvalidRule(
730
+ f"\nContradicting rules or stories found 🚨\n\n{error_text}\n"
731
+ f"Please update your stories and rules so that they don't contradict "
732
+ f"each other."
733
+ )
734
+
735
+ logger.debug("Found no contradicting rules.")
736
+ all_rules = (
737
+ set(self._rules_sources.keys())
738
+ | self._default_sources()
739
+ | self._handling_loop_sources(domain)
740
+ )
741
+ # set is not json serializable, so convert to list
742
+ return list(all_rules - rules_used_in_stories)
743
+
744
+ def _create_lookup_from_trackers(
745
+ self,
746
+ rule_trackers: List[TrackerWithCachedStates],
747
+ story_trackers: List[TrackerWithCachedStates],
748
+ domain: Domain,
749
+ ) -> None:
750
+ (
751
+ rule_trackers_as_states,
752
+ rule_trackers_as_actions,
753
+ ) = self.featurizer.training_states_and_labels(
754
+ rule_trackers, domain, omit_unset_slots=True
755
+ )
756
+
757
+ rules_lookup = self._create_lookup_from_states(
758
+ rule_trackers_as_states, rule_trackers_as_actions
759
+ )
760
+ self.lookup[RULES] = self._remove_rule_snippet_predictions(rules_lookup)
761
+
762
+ (
763
+ story_trackers_as_states,
764
+ story_trackers_as_actions,
765
+ ) = self.featurizer.training_states_and_labels(story_trackers, domain)
766
+
767
+ if self._check_for_contradictions:
768
+ (
769
+ self.lookup[RULE_ONLY_SLOTS],
770
+ self.lookup[RULE_ONLY_LOOPS],
771
+ ) = self._find_rule_only_slots_loops(
772
+ rule_trackers_as_states, story_trackers_as_states
773
+ )
774
+
775
+ # use all trackers to find negative rules in unhappy paths
776
+ trackers_as_states = rule_trackers_as_states + story_trackers_as_states
777
+ trackers_as_actions = rule_trackers_as_actions + story_trackers_as_actions
778
+
779
+ # negative rules are not anti-rules, they are auxiliary to actual rules
780
+ self.lookup[
781
+ RULES_FOR_LOOP_UNHAPPY_PATH
782
+ ] = self._create_loop_unhappy_lookup_from_states(
783
+ trackers_as_states, trackers_as_actions
784
+ )
785
+
786
+ def train(
787
+ self,
788
+ training_trackers: List[TrackerWithCachedStates],
789
+ domain: Domain,
790
+ **kwargs: Any,
791
+ ) -> Resource:
792
+ """Trains the policy on given training trackers.
793
+
794
+ Args:
795
+ training_trackers: The list of the trackers.
796
+ domain: The domain.
797
+ **kwargs: Additional arguments.
798
+
799
+ Returns:
800
+ The resource which can be used to load the trained policy.
801
+ """
802
+ self.raise_if_incompatible_with_domain(self.config, domain)
803
+
804
+ # only consider original trackers (no augmented ones)
805
+ training_trackers = [
806
+ t for t in training_trackers if not getattr(t, "is_augmented", False)
807
+ ]
808
+ # trackers from rule-based training data
809
+ rule_trackers = [t for t in training_trackers if t.is_rule_tracker]
810
+ if self.config["restrict_rules"]:
811
+ self._check_rule_restriction(rule_trackers)
812
+ if self._check_for_contradictions:
813
+ self._check_for_incomplete_rules(rule_trackers, domain)
814
+
815
+ # trackers from ML-based training data
816
+ story_trackers = [t for t in training_trackers if not t.is_rule_tracker]
817
+
818
+ self._create_lookup_from_trackers(rule_trackers, story_trackers, domain)
819
+
820
+ # make this configurable because checking might take a lot of time
821
+ if self._check_for_contradictions:
822
+ # using trackers here might not be the most efficient way, however
823
+ # it allows us to directly test `predict_action_probabilities` method
824
+ self.lookup[RULES_NOT_IN_STORIES] = self._analyze_rules(
825
+ rule_trackers, training_trackers, domain
826
+ )
827
+
828
+ logger.debug(f"Memorized '{len(self.lookup[RULES])}' unique rules.")
829
+
830
+ self.persist()
831
+
832
+ return self._resource
833
+
834
+ @staticmethod
835
+ def _does_rule_match_state(rule_state: State, conversation_state: State) -> bool:
836
+ for state_type, rule_sub_state in rule_state.items():
837
+ conversation_sub_state = conversation_state.get(state_type, {})
838
+ for key, value_from_rules in rule_sub_state.items():
839
+ if isinstance(value_from_rules, list):
840
+ # json dumps and loads tuples as lists,
841
+ # so we need to convert them back
842
+ value_from_rules = tuple(value_from_rules)
843
+ value_from_conversation = conversation_sub_state.get(key)
844
+ if (
845
+ # value should be set, therefore
846
+ # check whether it is the same as in the state
847
+ value_from_rules
848
+ and value_from_rules != SHOULD_NOT_BE_SET
849
+ and value_from_conversation != value_from_rules
850
+ ) or (
851
+ # value shouldn't be set, therefore
852
+ # it should be None or non existent in the state
853
+ value_from_rules == SHOULD_NOT_BE_SET
854
+ and value_from_conversation
855
+ # during training `SHOULD_NOT_BE_SET` is provided. Hence, we also
856
+ # have to check for the value of the slot state
857
+ and value_from_conversation != SHOULD_NOT_BE_SET
858
+ ):
859
+ return False
860
+
861
+ return True
862
+
863
+ @staticmethod
864
+ # This function is called a lot (e.g. for checking contradictions) so we cache
865
+ # its results.
866
+ @functools.lru_cache(maxsize=1000)
867
+ def _rule_key_to_state(rule_key: Text) -> List[State]:
868
+ return json.loads(rule_key)
869
+
870
+ def _is_rule_applicable(
871
+ self, rule_key: Text, turn_index: int, conversation_state: State
872
+ ) -> bool:
873
+ """Checks if rule is satisfied with current state at turn.
874
+
875
+ Args:
876
+ rule_key: the textual representation of learned rule
877
+ turn_index: index of a current dialogue turn
878
+ conversation_state: the state that corresponds to turn_index
879
+
880
+ Returns:
881
+ a boolean that says whether the rule is applicable to current state
882
+ """
883
+ # turn_index goes back in time
884
+ reversed_rule_states = list(reversed(self._rule_key_to_state(rule_key)))
885
+
886
+ # the rule must be applicable because we got (without any applicability issues)
887
+ # further in the conversation history than the rule's length
888
+ if turn_index >= len(reversed_rule_states):
889
+ return True
890
+
891
+ # a state has previous action if and only if it is not a conversation start
892
+ # state
893
+ current_previous_action = conversation_state.get(PREVIOUS_ACTION)
894
+ rule_previous_action = reversed_rule_states[turn_index].get(PREVIOUS_ACTION)
895
+
896
+ # current conversation state and rule state are conversation starters.
897
+ # any slots with initial_value set will necessarily be in both states and don't
898
+ # need to be checked.
899
+ if not rule_previous_action and not current_previous_action:
900
+ return True
901
+
902
+ # current rule state is a conversation starter (due to conversation_start: true)
903
+ # but current conversation state is not.
904
+ # or
905
+ # current conversation state is a starter
906
+ # but current rule state is not.
907
+ if not rule_previous_action or not current_previous_action:
908
+ return False
909
+
910
+ # check: current rule state features are present in current conversation state
911
+ return self._does_rule_match_state(
912
+ reversed_rule_states[turn_index], conversation_state
913
+ )
914
+
915
+ def _get_possible_keys(
916
+ self, lookup: Dict[Text, Text], states: List[State]
917
+ ) -> Set[Text]:
918
+ possible_keys = set(lookup.keys())
919
+ for i, state in enumerate(reversed(states)):
920
+ # find rule keys that correspond to current state
921
+ possible_keys = set(
922
+ filter(
923
+ lambda _key: self._is_rule_applicable(_key, i, state), possible_keys
924
+ )
925
+ )
926
+ return possible_keys
927
+
928
+ @staticmethod
929
+ def _find_action_from_default_actions(
930
+ tracker: DialogueStateTracker,
931
+ ) -> Tuple[Optional[Text], Optional[Text]]:
932
+ if (
933
+ not tracker.latest_action_name == ACTION_LISTEN_NAME
934
+ or not tracker.latest_message
935
+ ):
936
+ return None, None
937
+
938
+ intent_name = tracker.latest_message.intent.get(INTENT_NAME_KEY)
939
+ if intent_name is None:
940
+ return None, None
941
+
942
+ default_action_name = DEFAULT_ACTION_MAPPINGS.get(intent_name)
943
+ if default_action_name is None:
944
+ return None, None
945
+
946
+ logger.debug(f"Predicted default action '{default_action_name}'.")
947
+ return (
948
+ default_action_name,
949
+ # create prediction source that corresponds to one of
950
+ # default prediction sources in `_default_sources()`
951
+ DEFAULT_RULES + intent_name,
952
+ )
953
+
954
+ @staticmethod
955
+ def _find_action_from_loop_happy_path(
956
+ tracker: DialogueStateTracker,
957
+ ) -> Tuple[Optional[Text], Optional[Text]]:
958
+
959
+ active_loop_name = tracker.active_loop_name
960
+ if active_loop_name is None:
961
+ return None, None
962
+
963
+ active_loop_rejected = tracker.is_active_loop_rejected
964
+ should_predict_loop = (
965
+ not active_loop_rejected
966
+ and tracker.latest_action
967
+ and tracker.latest_action.get(ACTION_NAME) != active_loop_name
968
+ )
969
+ should_predict_listen = (
970
+ not active_loop_rejected and tracker.latest_action_name == active_loop_name
971
+ )
972
+
973
+ if should_predict_loop:
974
+ logger.debug(f"Predicted loop '{active_loop_name}'.")
975
+ return active_loop_name, LOOP_RULES + active_loop_name
976
+
977
+ # predict `action_listen` if loop action was run successfully
978
+ if should_predict_listen:
979
+ logger.debug(
980
+ f"Predicted '{ACTION_LISTEN_NAME}' after loop '{active_loop_name}'."
981
+ )
982
+ return (
983
+ ACTION_LISTEN_NAME,
984
+ (
985
+ f"{LOOP_RULES}{active_loop_name}"
986
+ f"{LOOP_RULES_SEPARATOR}{ACTION_LISTEN_NAME}"
987
+ ),
988
+ )
989
+
990
+ return None, None
991
+
992
+ def _find_action_from_rules(
993
+ self,
994
+ tracker: DialogueStateTracker,
995
+ domain: Domain,
996
+ use_text_for_last_user_input: bool,
997
+ ) -> Tuple[Optional[Text], Optional[Text], bool]:
998
+ """Predicts the next action based on the memoized rules.
999
+
1000
+ Args:
1001
+ tracker: The current conversation tracker.
1002
+ domain: The domain of the current model.
1003
+ use_text_for_last_user_input: `True` if text of last user message
1004
+ should be used for the prediction. `False` if intent should be used.
1005
+
1006
+ Returns:
1007
+ A tuple of the predicted action name or text (or `None` if no matching rule
1008
+ was found), a description of the matching rule, and `True` if a loop action
1009
+ was predicted after the loop has been in an unhappy path before.
1010
+ """
1011
+ if (
1012
+ use_text_for_last_user_input
1013
+ and not tracker.latest_action_name == ACTION_LISTEN_NAME
1014
+ ):
1015
+ # make text prediction only directly after user utterance
1016
+ # because we've otherwise already decided whether to use
1017
+ # the text or the intent
1018
+ return None, None, False
1019
+
1020
+ states = self._prediction_states(
1021
+ tracker,
1022
+ domain,
1023
+ use_text_for_last_user_input,
1024
+ rule_only_data=self._get_rule_only_data(),
1025
+ )
1026
+
1027
+ current_states = self.format_tracker_states(states)
1028
+ structlogger.debug(
1029
+ "rule_policy.actions.find", current_states=copy.deepcopy(current_states)
1030
+ )
1031
+
1032
+ # Tracks if we are returning after an unhappy loop path. If this becomes `True`
1033
+ # the policy returns an event which notifies the loop action that it
1034
+ # is returning after an unhappy path. For example, the `FormAction` uses this
1035
+ # to skip the validation of slots for its first execution after an unhappy path.
1036
+ returning_from_unhappy_path = False
1037
+
1038
+ rule_keys = self._get_possible_keys(self.lookup[RULES], states)
1039
+ predicted_action_name = None
1040
+ best_rule_key = ""
1041
+ if rule_keys:
1042
+ # if there are several rules,
1043
+ # it should mean that some rule is a subset of another rule
1044
+ # therefore we pick a rule of maximum length
1045
+ best_rule_key = max(rule_keys, key=len)
1046
+ predicted_action_name = self.lookup[RULES].get(best_rule_key)
1047
+
1048
+ active_loop_name = tracker.active_loop_name
1049
+ if active_loop_name:
1050
+ # find rules for unhappy path of the loop
1051
+ loop_unhappy_keys = self._get_possible_keys(
1052
+ self.lookup[RULES_FOR_LOOP_UNHAPPY_PATH], states
1053
+ )
1054
+ # there could be several unhappy path conditions
1055
+ unhappy_path_conditions = [
1056
+ self.lookup[RULES_FOR_LOOP_UNHAPPY_PATH].get(key)
1057
+ for key in loop_unhappy_keys
1058
+ ]
1059
+
1060
+ # Check if a rule that predicted action_listen
1061
+ # was applied inside the loop.
1062
+ # Rules might not explicitly switch back to the loop.
1063
+ # Hence, we have to take care of that.
1064
+ predicted_listen_from_general_rule = (
1065
+ predicted_action_name == ACTION_LISTEN_NAME
1066
+ and not get_active_loop_name(self._rule_key_to_state(best_rule_key)[-1])
1067
+ )
1068
+ if predicted_listen_from_general_rule:
1069
+ if DO_NOT_PREDICT_LOOP_ACTION not in unhappy_path_conditions:
1070
+ # negative rules don't contain a key that corresponds to
1071
+ # the fact that active_loop shouldn't be predicted
1072
+ logger.debug(
1073
+ f"Predicted loop '{active_loop_name}' by overwriting "
1074
+ f"'{ACTION_LISTEN_NAME}' predicted by general rule."
1075
+ )
1076
+ return (
1077
+ active_loop_name,
1078
+ best_rule_key,
1079
+ returning_from_unhappy_path,
1080
+ )
1081
+
1082
+ # do not predict anything
1083
+ predicted_action_name = None
1084
+
1085
+ if LOOP_WAS_INTERRUPTED in unhappy_path_conditions:
1086
+ logger.debug(
1087
+ "Returning from unhappy path. Loop will be notified that "
1088
+ "it was interrupted."
1089
+ )
1090
+ returning_from_unhappy_path = True
1091
+
1092
+ if predicted_action_name is not None:
1093
+ logger.debug(
1094
+ f"There is a rule for the next action '{predicted_action_name}'."
1095
+ )
1096
+ else:
1097
+ logger.debug("There is no applicable rule.")
1098
+
1099
+ # if we didn't predict anything from the rules, then the feature key created
1100
+ # from states can be used as an indicator that this state will lead to fallback
1101
+ return (
1102
+ predicted_action_name,
1103
+ best_rule_key or self._create_feature_key(states),
1104
+ returning_from_unhappy_path,
1105
+ )
1106
+
1107
+ async def predict_action_probabilities(
1108
+ self,
1109
+ tracker: DialogueStateTracker,
1110
+ domain: Domain,
1111
+ rule_only_data: Optional[Dict[Text, Any]] = None,
1112
+ **kwargs: Any,
1113
+ ) -> PolicyPrediction:
1114
+ """Predicts the next action (see parent class for more information)."""
1115
+ if self.should_abstain_in_coexistence(tracker, False):
1116
+ # don't use self._default_predictions as this might have a different
1117
+ # probability for the fallback action, and we want to have all probabilities
1118
+ # set to 0.0
1119
+ return self._prediction(super()._default_predictions(domain))
1120
+
1121
+ prediction, _ = self._predict(tracker, domain)
1122
+ return prediction
1123
+
1124
+ def _predict(
1125
+ self, tracker: DialogueStateTracker, domain: Domain
1126
+ ) -> Tuple[PolicyPrediction, Optional[Text]]:
1127
+ (
1128
+ rules_action_name_from_text,
1129
+ prediction_source_from_text,
1130
+ returning_from_unhappy_path_from_text,
1131
+ ) = self._find_action_from_rules(
1132
+ tracker, domain, use_text_for_last_user_input=True
1133
+ )
1134
+
1135
+ # Rasa Open Source default actions overrule anything. If users want to achieve
1136
+ # the same, they need to write a rule or make sure that their loop rejects
1137
+ # accordingly.
1138
+ (
1139
+ default_action_name,
1140
+ default_prediction_source,
1141
+ ) = self._find_action_from_default_actions(tracker)
1142
+
1143
+ # text has priority over intents including default,
1144
+ # however loop happy path has priority over rules prediction
1145
+ if default_action_name and not rules_action_name_from_text:
1146
+ return (
1147
+ self._rule_prediction(
1148
+ self._prediction_result(default_action_name, tracker, domain),
1149
+ default_prediction_source,
1150
+ ),
1151
+ default_prediction_source,
1152
+ )
1153
+
1154
+ # A loop has priority over any other rule except defaults.
1155
+ # The rules or any other prediction will be applied only if a loop was rejected.
1156
+ # If we are in a loop, and the loop didn't run previously or rejected, we can
1157
+ # simply force predict the loop.
1158
+ (
1159
+ loop_happy_path_action_name,
1160
+ loop_happy_path_prediction_source,
1161
+ ) = self._find_action_from_loop_happy_path(tracker)
1162
+ if loop_happy_path_action_name:
1163
+ # this prediction doesn't use user input
1164
+ # and happy user input anyhow should be ignored during featurization
1165
+ return (
1166
+ self._rule_prediction(
1167
+ self._prediction_result(
1168
+ loop_happy_path_action_name, tracker, domain
1169
+ ),
1170
+ loop_happy_path_prediction_source,
1171
+ is_no_user_prediction=True,
1172
+ ),
1173
+ loop_happy_path_prediction_source,
1174
+ )
1175
+
1176
+ # predict rules from text first
1177
+ if rules_action_name_from_text:
1178
+ return (
1179
+ self._rule_prediction(
1180
+ self._prediction_result(
1181
+ rules_action_name_from_text, tracker, domain
1182
+ ),
1183
+ prediction_source_from_text,
1184
+ returning_from_unhappy_path=returning_from_unhappy_path_from_text,
1185
+ is_end_to_end_prediction=True,
1186
+ ),
1187
+ prediction_source_from_text,
1188
+ )
1189
+
1190
+ (
1191
+ rules_action_name_from_intent,
1192
+ # we want to remember the source even if rules didn't predict any action
1193
+ prediction_source_from_intent,
1194
+ returning_from_unhappy_path_from_intent,
1195
+ ) = self._find_action_from_rules(
1196
+ tracker, domain, use_text_for_last_user_input=False
1197
+ )
1198
+ if rules_action_name_from_intent:
1199
+ probabilities = self._prediction_result(
1200
+ rules_action_name_from_intent, tracker, domain
1201
+ )
1202
+ else:
1203
+ probabilities = self._default_predictions(domain)
1204
+
1205
+ return (
1206
+ self._rule_prediction(
1207
+ probabilities,
1208
+ prediction_source_from_intent,
1209
+ returning_from_unhappy_path=(
1210
+ # returning_from_unhappy_path is a negative condition,
1211
+ # so `or` should be applied
1212
+ returning_from_unhappy_path_from_text
1213
+ or returning_from_unhappy_path_from_intent
1214
+ ),
1215
+ is_end_to_end_prediction=False,
1216
+ ),
1217
+ prediction_source_from_intent,
1218
+ )
1219
+
1220
+ def _rule_prediction(
1221
+ self,
1222
+ probabilities: List[float],
1223
+ prediction_source: Text,
1224
+ returning_from_unhappy_path: bool = False,
1225
+ is_end_to_end_prediction: bool = False,
1226
+ is_no_user_prediction: bool = False,
1227
+ ) -> PolicyPrediction:
1228
+ return PolicyPrediction(
1229
+ probabilities,
1230
+ self.__class__.__name__,
1231
+ self.priority,
1232
+ events=[LoopInterrupted(True)] if returning_from_unhappy_path else [],
1233
+ is_end_to_end_prediction=is_end_to_end_prediction,
1234
+ is_no_user_prediction=is_no_user_prediction,
1235
+ hide_rule_turn=(
1236
+ True
1237
+ if prediction_source in self.lookup.get(RULES_NOT_IN_STORIES, [])
1238
+ else False
1239
+ ),
1240
+ )
1241
+
1242
+ def _default_predictions(self, domain: Domain) -> List[float]:
1243
+ result = super()._default_predictions(domain)
1244
+
1245
+ if self._enable_fallback_prediction:
1246
+ result[domain.index_for_action(self._fallback_action_name)] = self.config[
1247
+ "core_fallback_threshold"
1248
+ ]
1249
+
1250
+ return result
1251
+
1252
+ def persist(self) -> None:
1253
+ """Persists trained `RulePolicy`."""
1254
+ super().persist()
1255
+ with self._model_storage.write_to(self._resource) as directory:
1256
+ rule_only_data = self._get_rule_only_data()
1257
+ rasa.shared.utils.io.dump_obj_as_json_to_file(
1258
+ directory / "rule_only_data.json", rule_only_data
1259
+ )
1260
+
1261
+ def _metadata(self) -> Dict[Text, Any]:
1262
+ return {"lookup": self.lookup}
1263
+
1264
+ @classmethod
1265
+ def _metadata_filename(cls) -> Text:
1266
+ return "rule_policy.json"
1267
+
1268
+ def _get_rule_only_data(self) -> Dict[Text, Any]:
1269
+ """Gets the slots and loops that are used only in rule data.
1270
+
1271
+ Returns:
1272
+ Slots and loops that are used only in rule data.
1273
+ """
1274
+ return {
1275
+ key: self.lookup.get(key, []) for key in [RULE_ONLY_SLOTS, RULE_ONLY_LOOPS]
1276
+ }