rasa-pro 3.8.18__py3-none-any.whl → 3.9.14__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 (278) hide show
  1. README.md +6 -42
  2. rasa/__main__.py +14 -9
  3. rasa/anonymization/anonymization_pipeline.py +0 -1
  4. rasa/anonymization/anonymization_rule_executor.py +3 -3
  5. rasa/anonymization/utils.py +4 -3
  6. rasa/api.py +2 -2
  7. rasa/cli/arguments/default_arguments.py +1 -1
  8. rasa/cli/arguments/run.py +2 -2
  9. rasa/cli/arguments/test.py +1 -1
  10. rasa/cli/arguments/train.py +10 -10
  11. rasa/cli/e2e_test.py +27 -7
  12. rasa/cli/export.py +0 -1
  13. rasa/cli/license.py +3 -3
  14. rasa/cli/project_templates/calm/actions/action_template.py +1 -1
  15. rasa/cli/project_templates/calm/config.yml +1 -1
  16. rasa/cli/project_templates/calm/credentials.yml +1 -1
  17. rasa/cli/project_templates/calm/data/flows/add_contact.yml +1 -1
  18. rasa/cli/project_templates/calm/data/flows/remove_contact.yml +1 -1
  19. rasa/cli/project_templates/calm/domain/add_contact.yml +8 -2
  20. rasa/cli/project_templates/calm/domain/list_contacts.yml +3 -0
  21. rasa/cli/project_templates/calm/domain/remove_contact.yml +9 -2
  22. rasa/cli/project_templates/calm/domain/shared.yml +5 -0
  23. rasa/cli/project_templates/calm/endpoints.yml +4 -4
  24. rasa/cli/project_templates/default/actions/actions.py +1 -1
  25. rasa/cli/project_templates/default/config.yml +5 -5
  26. rasa/cli/project_templates/default/credentials.yml +1 -1
  27. rasa/cli/project_templates/default/endpoints.yml +4 -4
  28. rasa/cli/project_templates/default/tests/test_stories.yml +1 -1
  29. rasa/cli/project_templates/tutorial/config.yml +1 -1
  30. rasa/cli/project_templates/tutorial/credentials.yml +1 -1
  31. rasa/cli/project_templates/tutorial/data/patterns.yml +6 -0
  32. rasa/cli/project_templates/tutorial/domain.yml +4 -0
  33. rasa/cli/project_templates/tutorial/endpoints.yml +6 -6
  34. rasa/cli/run.py +0 -1
  35. rasa/cli/scaffold.py +3 -2
  36. rasa/cli/studio/download.py +11 -0
  37. rasa/cli/studio/studio.py +180 -24
  38. rasa/cli/studio/upload.py +0 -8
  39. rasa/cli/telemetry.py +18 -6
  40. rasa/cli/utils.py +21 -10
  41. rasa/cli/x.py +3 -2
  42. rasa/constants.py +1 -1
  43. rasa/core/actions/action.py +90 -315
  44. rasa/core/actions/action_exceptions.py +24 -0
  45. rasa/core/actions/constants.py +3 -0
  46. rasa/core/actions/custom_action_executor.py +188 -0
  47. rasa/core/actions/forms.py +11 -7
  48. rasa/core/actions/grpc_custom_action_executor.py +251 -0
  49. rasa/core/actions/http_custom_action_executor.py +140 -0
  50. rasa/core/actions/loops.py +3 -0
  51. rasa/core/actions/two_stage_fallback.py +1 -1
  52. rasa/core/agent.py +2 -4
  53. rasa/core/brokers/pika.py +1 -2
  54. rasa/core/channels/audiocodes.py +1 -1
  55. rasa/core/channels/botframework.py +0 -1
  56. rasa/core/channels/callback.py +0 -1
  57. rasa/core/channels/console.py +6 -8
  58. rasa/core/channels/development_inspector.py +1 -1
  59. rasa/core/channels/facebook.py +0 -3
  60. rasa/core/channels/hangouts.py +0 -6
  61. rasa/core/channels/inspector/dist/assets/{arc-5623b6dc.js → arc-b6e548fe.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-685c106a.js → c4Diagram-d0fbc5ce-fa03ac9e.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-8cbed007.js → classDiagram-936ed81e-ee67392a.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-5889cf12.js → classDiagram-v2-c3cb15f1-9b283fae.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{createText-62fc7601-24c249d7.js → createText-62fc7601-8b6fcc2a.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-7dd06a75.js → edges-f2ad444c-22e77f4f.js} +1 -1
  67. rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-62c1e54c.js → erDiagram-9d236eb7-60ffc87f.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-ce49b86f.js → flowDb-1972c806-9dd802e4.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-4067e48f.js → flowDiagram-7ea5b25a-5fa1912f.js} +1 -1
  70. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-1844e5a5.js +1 -0
  71. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-59fe4051.js → flowchart-elk-definition-abe16c3d-622a1fd2.js} +1 -1
  72. rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-47e3a43b.js → ganttDiagram-9b5ea136-e285a63a.js} +1 -1
  73. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-5a2ac0d9.js → gitGraphDiagram-99d0ae7c-f237bdca.js} +1 -1
  74. rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-dfb8efc4.js → index-2c4b9a3b-4b03d70e.js} +1 -1
  75. rasa/core/channels/inspector/dist/assets/{index-268a75c0.js → index-a5d3e69d.js} +4 -4
  76. rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-b0c470f2.js → infoDiagram-736b4530-72a0fa5f.js} +1 -1
  77. rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-2edb829a.js → journeyDiagram-df861f2b-82218c41.js} +1 -1
  78. rasa/core/channels/inspector/dist/assets/{layout-b6873d69.js → layout-78cff630.js} +1 -1
  79. rasa/core/channels/inspector/dist/assets/{line-1efc5781.js → line-5038b469.js} +1 -1
  80. rasa/core/channels/inspector/dist/assets/{linear-661e9b94.js → linear-c4fc4098.js} +1 -1
  81. rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-2d2e727f.js → mindmap-definition-beec6740-c33c8ea6.js} +1 -1
  82. rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-9d3ea93d.js → pieDiagram-dbbf0591-a8d03059.js} +1 -1
  83. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-06a178a2.js → quadrantDiagram-4d7f4fd6-6a0e56b2.js} +1 -1
  84. rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-0bfedffc.js → requirementDiagram-6fc4c22a-2dc7c7bd.js} +1 -1
  85. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-d76d0a04.js → sankeyDiagram-8f13d901-2360fe39.js} +1 -1
  86. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-37bb4341.js → sequenceDiagram-b655622a-41b9f9ad.js} +1 -1
  87. rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-f52f7f57.js → stateDiagram-59f0c015-0aad326f.js} +1 -1
  88. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-4a986a20.js → stateDiagram-v2-2b26beab-9847d984.js} +1 -1
  89. rasa/core/channels/inspector/dist/assets/{styles-080da4f6-7dd9ae12.js → styles-080da4f6-564d890e.js} +1 -1
  90. rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-46e1ca14.js → styles-3dcbcfbf-38957613.js} +1 -1
  91. rasa/core/channels/inspector/dist/assets/{styles-9c745c82-4a97439a.js → styles-9c745c82-f0fc6921.js} +1 -1
  92. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-823917a3.js → svgDrawCommon-4835440b-ef3c5a77.js} +1 -1
  93. rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-9ea72896.js → timeline-definition-5b62e21b-bf3e91c1.js} +1 -1
  94. rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-b631a8b6.js → xychartDiagram-2b33534f-4d4026c0.js} +1 -1
  95. rasa/core/channels/inspector/dist/index.html +1 -1
  96. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +10 -0
  97. rasa/core/channels/inspector/src/helpers/formatters.test.ts +4 -7
  98. rasa/core/channels/inspector/src/helpers/formatters.ts +3 -2
  99. rasa/core/channels/rest.py +36 -21
  100. rasa/core/channels/rocketchat.py +0 -1
  101. rasa/core/channels/socketio.py +1 -1
  102. rasa/core/channels/telegram.py +3 -3
  103. rasa/core/channels/webexteams.py +0 -1
  104. rasa/core/concurrent_lock_store.py +1 -1
  105. rasa/core/evaluation/marker_base.py +1 -3
  106. rasa/core/evaluation/marker_stats.py +1 -2
  107. rasa/core/featurizers/single_state_featurizer.py +3 -26
  108. rasa/core/featurizers/tracker_featurizers.py +18 -122
  109. rasa/core/information_retrieval/__init__.py +7 -0
  110. rasa/core/information_retrieval/faiss.py +9 -4
  111. rasa/core/information_retrieval/information_retrieval.py +64 -7
  112. rasa/core/information_retrieval/milvus.py +7 -14
  113. rasa/core/information_retrieval/qdrant.py +8 -15
  114. rasa/core/lock_store.py +0 -1
  115. rasa/core/migrate.py +1 -2
  116. rasa/core/nlg/callback.py +3 -4
  117. rasa/core/policies/enterprise_search_policy.py +86 -22
  118. rasa/core/policies/enterprise_search_prompt_template.jinja2 +4 -41
  119. rasa/core/policies/enterprise_search_prompt_with_citation_template.jinja2 +60 -0
  120. rasa/core/policies/flows/flow_executor.py +104 -2
  121. rasa/core/policies/intentless_policy.py +7 -9
  122. rasa/core/policies/memoization.py +3 -3
  123. rasa/core/policies/policy.py +18 -9
  124. rasa/core/policies/rule_policy.py +8 -11
  125. rasa/core/policies/ted_policy.py +61 -88
  126. rasa/core/policies/unexpected_intent_policy.py +8 -17
  127. rasa/core/processor.py +136 -47
  128. rasa/core/run.py +41 -25
  129. rasa/core/secrets_manager/endpoints.py +2 -2
  130. rasa/core/secrets_manager/vault.py +6 -8
  131. rasa/core/test.py +3 -5
  132. rasa/core/tracker_store.py +49 -14
  133. rasa/core/train.py +1 -3
  134. rasa/core/training/interactive.py +9 -6
  135. rasa/core/utils.py +5 -10
  136. rasa/dialogue_understanding/coexistence/intent_based_router.py +11 -4
  137. rasa/dialogue_understanding/coexistence/llm_based_router.py +2 -3
  138. rasa/dialogue_understanding/commands/__init__.py +4 -0
  139. rasa/dialogue_understanding/commands/can_not_handle_command.py +9 -0
  140. rasa/dialogue_understanding/commands/cancel_flow_command.py +9 -0
  141. rasa/dialogue_understanding/commands/change_flow_command.py +38 -0
  142. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +9 -0
  143. rasa/dialogue_understanding/commands/clarify_command.py +9 -0
  144. rasa/dialogue_understanding/commands/correct_slots_command.py +9 -0
  145. rasa/dialogue_understanding/commands/error_command.py +12 -0
  146. rasa/dialogue_understanding/commands/handle_code_change_command.py +9 -0
  147. rasa/dialogue_understanding/commands/human_handoff_command.py +9 -0
  148. rasa/dialogue_understanding/commands/knowledge_answer_command.py +9 -0
  149. rasa/dialogue_understanding/commands/noop_command.py +9 -0
  150. rasa/dialogue_understanding/commands/set_slot_command.py +34 -3
  151. rasa/dialogue_understanding/commands/skip_question_command.py +9 -0
  152. rasa/dialogue_understanding/commands/start_flow_command.py +9 -0
  153. rasa/dialogue_understanding/generator/__init__.py +16 -1
  154. rasa/dialogue_understanding/generator/command_generator.py +92 -6
  155. rasa/dialogue_understanding/generator/constants.py +18 -0
  156. rasa/dialogue_understanding/generator/flow_retrieval.py +7 -5
  157. rasa/dialogue_understanding/generator/llm_based_command_generator.py +467 -0
  158. rasa/dialogue_understanding/generator/llm_command_generator.py +39 -609
  159. rasa/dialogue_understanding/generator/multi_step/__init__.py +0 -0
  160. rasa/dialogue_understanding/generator/multi_step/fill_slots_prompt.jinja2 +62 -0
  161. rasa/dialogue_understanding/generator/multi_step/handle_flows_prompt.jinja2 +38 -0
  162. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +827 -0
  163. rasa/dialogue_understanding/generator/nlu_command_adapter.py +69 -8
  164. rasa/dialogue_understanding/generator/single_step/__init__.py +0 -0
  165. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +345 -0
  166. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +44 -39
  167. rasa/dialogue_understanding/processor/command_processor.py +111 -3
  168. rasa/e2e_test/constants.py +1 -0
  169. rasa/e2e_test/e2e_test_case.py +44 -0
  170. rasa/e2e_test/e2e_test_runner.py +114 -11
  171. rasa/e2e_test/e2e_test_schema.yml +18 -0
  172. rasa/engine/caching.py +0 -1
  173. rasa/engine/graph.py +18 -6
  174. rasa/engine/recipes/config_files/default_config.yml +3 -3
  175. rasa/engine/recipes/default_components.py +1 -1
  176. rasa/engine/recipes/default_recipe.py +4 -5
  177. rasa/engine/recipes/recipe.py +1 -1
  178. rasa/engine/runner/dask.py +3 -9
  179. rasa/engine/storage/local_model_storage.py +0 -2
  180. rasa/engine/validation.py +179 -145
  181. rasa/exceptions.py +2 -2
  182. rasa/graph_components/validators/default_recipe_validator.py +3 -5
  183. rasa/hooks.py +0 -1
  184. rasa/model.py +1 -1
  185. rasa/model_training.py +1 -0
  186. rasa/nlu/classifiers/diet_classifier.py +33 -52
  187. rasa/nlu/classifiers/logistic_regression_classifier.py +9 -22
  188. rasa/nlu/classifiers/sklearn_intent_classifier.py +16 -37
  189. rasa/nlu/extractors/crf_entity_extractor.py +54 -97
  190. rasa/nlu/extractors/duckling_entity_extractor.py +1 -1
  191. rasa/nlu/featurizers/dense_featurizer/convert_featurizer.py +1 -5
  192. rasa/nlu/featurizers/dense_featurizer/lm_featurizer.py +0 -4
  193. rasa/nlu/featurizers/featurizer.py +1 -1
  194. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +18 -49
  195. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +26 -64
  196. rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +3 -5
  197. rasa/nlu/persistor.py +68 -26
  198. rasa/nlu/selectors/response_selector.py +7 -10
  199. rasa/nlu/test.py +0 -3
  200. rasa/nlu/utils/hugging_face/registry.py +1 -1
  201. rasa/nlu/utils/spacy_utils.py +1 -3
  202. rasa/server.py +22 -7
  203. rasa/shared/constants.py +12 -1
  204. rasa/shared/core/command_payload_reader.py +109 -0
  205. rasa/shared/core/constants.py +4 -5
  206. rasa/shared/core/domain.py +57 -56
  207. rasa/shared/core/events.py +4 -7
  208. rasa/shared/core/flows/flow.py +9 -0
  209. rasa/shared/core/flows/flows_list.py +12 -0
  210. rasa/shared/core/flows/steps/action.py +7 -2
  211. rasa/shared/core/generator.py +12 -11
  212. rasa/shared/core/slot_mappings.py +315 -24
  213. rasa/shared/core/slots.py +4 -2
  214. rasa/shared/core/trackers.py +32 -14
  215. rasa/shared/core/training_data/loading.py +0 -1
  216. rasa/shared/core/training_data/story_reader/story_reader.py +3 -3
  217. rasa/shared/core/training_data/story_reader/yaml_story_reader.py +11 -11
  218. rasa/shared/core/training_data/story_writer/yaml_story_writer.py +5 -3
  219. rasa/shared/core/training_data/structures.py +1 -1
  220. rasa/shared/core/training_data/visualization.py +1 -1
  221. rasa/shared/data.py +58 -1
  222. rasa/shared/exceptions.py +36 -2
  223. rasa/shared/importers/importer.py +1 -2
  224. rasa/shared/importers/rasa.py +0 -1
  225. rasa/shared/nlu/constants.py +2 -0
  226. rasa/shared/nlu/training_data/entities_parser.py +1 -2
  227. rasa/shared/nlu/training_data/features.py +2 -120
  228. rasa/shared/nlu/training_data/formats/dialogflow.py +3 -2
  229. rasa/shared/nlu/training_data/formats/rasa_yaml.py +3 -5
  230. rasa/shared/nlu/training_data/formats/readerwriter.py +0 -1
  231. rasa/shared/nlu/training_data/message.py +13 -0
  232. rasa/shared/nlu/training_data/training_data.py +0 -2
  233. rasa/shared/providers/openai/session_handler.py +2 -2
  234. rasa/shared/utils/constants.py +3 -0
  235. rasa/shared/utils/io.py +11 -1
  236. rasa/shared/utils/llm.py +1 -2
  237. rasa/shared/utils/pykwalify_extensions.py +1 -0
  238. rasa/shared/utils/schemas/domain.yml +3 -0
  239. rasa/shared/utils/yaml.py +44 -35
  240. rasa/studio/auth.py +26 -10
  241. rasa/studio/constants.py +2 -0
  242. rasa/studio/data_handler.py +114 -107
  243. rasa/studio/download.py +160 -27
  244. rasa/studio/results_logger.py +137 -0
  245. rasa/studio/train.py +6 -7
  246. rasa/studio/upload.py +159 -134
  247. rasa/telemetry.py +188 -34
  248. rasa/tracing/config.py +18 -3
  249. rasa/tracing/constants.py +26 -2
  250. rasa/tracing/instrumentation/attribute_extractors.py +50 -41
  251. rasa/tracing/instrumentation/instrumentation.py +290 -44
  252. rasa/tracing/instrumentation/intentless_policy_instrumentation.py +7 -5
  253. rasa/tracing/instrumentation/metrics.py +109 -21
  254. rasa/tracing/metric_instrument_provider.py +83 -3
  255. rasa/utils/cli.py +2 -1
  256. rasa/utils/common.py +1 -1
  257. rasa/utils/endpoints.py +1 -2
  258. rasa/utils/io.py +72 -6
  259. rasa/utils/licensing.py +246 -31
  260. rasa/utils/ml_utils.py +1 -1
  261. rasa/utils/tensorflow/data_generator.py +1 -1
  262. rasa/utils/tensorflow/environment.py +1 -1
  263. rasa/utils/tensorflow/model_data.py +201 -12
  264. rasa/utils/tensorflow/model_data_utils.py +499 -500
  265. rasa/utils/tensorflow/models.py +5 -6
  266. rasa/utils/tensorflow/rasa_layers.py +15 -15
  267. rasa/utils/train_utils.py +1 -1
  268. rasa/utils/url_tools.py +53 -0
  269. rasa/validator.py +305 -3
  270. rasa/version.py +1 -1
  271. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.14.dist-info}/METADATA +25 -61
  272. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.14.dist-info}/RECORD +276 -259
  273. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-85583a23.js +0 -1
  274. rasa/utils/tensorflow/feature_array.py +0 -370
  275. /rasa/dialogue_understanding/generator/{command_prompt_template.jinja2 → single_step/command_prompt_template.jinja2} +0 -0
  276. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.14.dist-info}/NOTICE +0 -0
  277. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.14.dist-info}/WHEEL +0 -0
  278. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.14.dist-info}/entry_points.txt +0 -0
@@ -2,20 +2,27 @@ from typing import Dict, Text, Any, Optional, List
2
2
 
3
3
  import structlog
4
4
 
5
+
5
6
  from rasa.dialogue_understanding.commands import (
6
7
  Command,
7
8
  StartFlowCommand,
8
9
  SetSlotCommand,
9
10
  )
11
+ from rasa.dialogue_understanding.commands.set_slot_command import SetSlotExtractor
10
12
  from rasa.dialogue_understanding.generator import CommandGenerator
11
13
  from rasa.engine.graph import GraphComponent, ExecutionContext
12
14
  from rasa.engine.recipes.default_recipe import DefaultV1Recipe
13
15
  from rasa.engine.storage.resource import Resource
14
16
  from rasa.engine.storage.storage import ModelStorage
15
17
  from rasa.shared.constants import ROUTE_TO_CALM_SLOT
18
+ from rasa.shared.core.domain import Domain
16
19
  from rasa.shared.core.flows.flows_list import FlowsList
20
+ from rasa.shared.core.slot_mappings import (
21
+ SlotFillingManager,
22
+ extract_slot_value,
23
+ )
17
24
  from rasa.shared.core.trackers import DialogueStateTracker
18
- from rasa.shared.nlu.constants import INTENT
25
+ from rasa.shared.nlu.constants import ENTITIES, INTENT
19
26
  from rasa.shared.nlu.training_data.message import Message
20
27
  from rasa.shared.nlu.training_data.training_data import TrainingData
21
28
 
@@ -76,6 +83,7 @@ class NLUCommandAdapter(GraphComponent, CommandGenerator):
76
83
  message: Message,
77
84
  flows: FlowsList,
78
85
  tracker: Optional[DialogueStateTracker] = None,
86
+ **kwargs: Any,
79
87
  ) -> List[Command]:
80
88
  """Creates commands using the predicted intents.
81
89
 
@@ -83,6 +91,7 @@ class NLUCommandAdapter(GraphComponent, CommandGenerator):
83
91
  message: The message from the user.
84
92
  flows: The flows available to the user.
85
93
  tracker: The tracker containing the current state of the conversation.
94
+ **kwargs: Keyword arguments for forward compatibility.
86
95
 
87
96
  Returns:
88
97
  The commands triggered by NLU.
@@ -91,9 +100,18 @@ class NLUCommandAdapter(GraphComponent, CommandGenerator):
91
100
  # cannot do anything if there are no flows or no tracker
92
101
  return []
93
102
 
94
- commands = self.convert_nlu_to_commands(message, tracker, flows)
103
+ domain = kwargs.get("domain", None)
104
+ commands = self.convert_nlu_to_commands(message, tracker, flows, domain)
105
+
106
+ commands_contain_start_flow = any(
107
+ isinstance(command, StartFlowCommand) for command in commands
108
+ )
95
109
 
96
- if commands and len(commands) >= 1 and tracker.has_coexistence_routing_slot:
110
+ if (
111
+ commands
112
+ and commands_contain_start_flow
113
+ and tracker.has_coexistence_routing_slot
114
+ ):
97
115
  # if the nlu command adapter will start a flow and the coexistence feature
98
116
  # is used, make sure to set the routing slot
99
117
  commands += [SetSlotCommand(ROUTE_TO_CALM_SLOT, True)]
@@ -121,15 +139,22 @@ class NLUCommandAdapter(GraphComponent, CommandGenerator):
121
139
 
122
140
  @staticmethod
123
141
  def convert_nlu_to_commands(
124
- message: Message, tracker: DialogueStateTracker, flows: FlowsList
142
+ message: Message,
143
+ tracker: DialogueStateTracker,
144
+ flows: FlowsList,
145
+ domain: Optional[Domain] = None,
125
146
  ) -> List[Command]:
126
147
  """Converts the predicted intent to a command."""
127
148
  if tracker is None or flows.is_empty():
128
149
  # cannot do anything if there are no flows or no tracker
129
150
  return []
130
151
 
131
- if not message.get(INTENT) or not message.get(INTENT)["name"]:
132
- # if the message does not have an intent set,
152
+ if not (
153
+ message.get(INTENT)
154
+ or message.get(INTENT, {}).get("name")
155
+ or message.get(ENTITIES)
156
+ ):
157
+ # if the message does not have an intent or entities set
133
158
  # no commands can be predicted
134
159
  return []
135
160
 
@@ -139,8 +164,6 @@ class NLUCommandAdapter(GraphComponent, CommandGenerator):
139
164
  if flow.nlu_triggers and flow.nlu_triggers.is_triggered(message):
140
165
  commands.append(StartFlowCommand(flow.id))
141
166
 
142
- structlogger.info("nlu_command_adapter.predict_commands", commands=commands)
143
-
144
167
  # there should be just one flow that can be triggered by the predicted intent
145
168
  # this is checked when loading the flows
146
169
  # however we just doublecheck here and return the first command if there are
@@ -154,4 +177,42 @@ class NLUCommandAdapter(GraphComponent, CommandGenerator):
154
177
  )
155
178
  commands = [commands[0]]
156
179
 
180
+ set_slot_commands = _issue_set_slot_commands(message, tracker, flows, domain)
181
+ commands.extend(set_slot_commands)
182
+
183
+ structlogger.info("nlu_command_adapter.predict_commands", commands=commands)
184
+
157
185
  return commands
186
+
187
+
188
+ def _issue_set_slot_commands(
189
+ message: Message,
190
+ tracker: DialogueStateTracker,
191
+ flows: FlowsList,
192
+ domain: Optional[Domain] = None,
193
+ ) -> List[Command]:
194
+ """Issue SetSlotCommand for each slot that can be filled with NLU properties."""
195
+ commands: List[Command] = []
196
+ domain = domain if domain else Domain.empty()
197
+ slot_filling_manager = SlotFillingManager(domain, tracker, message)
198
+ available_slot_names = flows.available_slot_names()
199
+
200
+ for _, slot in tracker.slots.items():
201
+ # if a slot is not collected in available flows,
202
+ # it means that it is not a slot that can be filled by CALM,
203
+ # so we skip it
204
+ if slot.name not in available_slot_names:
205
+ structlogger.debug("nlu_command_adapter.skip_slot", slot=slot.name)
206
+ continue
207
+
208
+ slot_value, is_extracted = extract_slot_value(slot, slot_filling_manager)
209
+ if is_extracted:
210
+ commands.append(
211
+ SetSlotCommand(
212
+ name=slot.name,
213
+ value=slot_value,
214
+ extractor=SetSlotExtractor.NLU.value,
215
+ )
216
+ )
217
+
218
+ return commands
@@ -0,0 +1,345 @@
1
+ import importlib.resources
2
+ import re
3
+ import structlog
4
+ from typing import Dict, Any, List, Optional, Text
5
+
6
+ import rasa.shared.utils.io
7
+ from rasa.dialogue_understanding.commands import (
8
+ Command,
9
+ ErrorCommand,
10
+ SetSlotCommand,
11
+ CancelFlowCommand,
12
+ HumanHandoffCommand,
13
+ ChitChatAnswerCommand,
14
+ SkipQuestionCommand,
15
+ KnowledgeAnswerCommand,
16
+ ClarifyCommand,
17
+ CannotHandleCommand,
18
+ )
19
+ from rasa.dialogue_understanding.generator.llm_based_command_generator import (
20
+ LLMBasedCommandGenerator,
21
+ )
22
+ from rasa.dialogue_understanding.generator.constants import (
23
+ LLM_CONFIG_KEY,
24
+ USER_INPUT_CONFIG_KEY,
25
+ FLOW_RETRIEVAL_KEY,
26
+ )
27
+ from rasa.dialogue_understanding.generator.flow_retrieval import (
28
+ FlowRetrieval,
29
+ )
30
+ from rasa.dialogue_understanding.stack.utils import top_flow_frame
31
+ from rasa.engine.graph import ExecutionContext
32
+ from rasa.engine.recipes.default_recipe import DefaultV1Recipe
33
+ from rasa.engine.storage.resource import Resource
34
+ from rasa.engine.storage.storage import ModelStorage
35
+ from rasa.shared.constants import ROUTE_TO_CALM_SLOT
36
+ from rasa.shared.core.flows import FlowsList
37
+ from rasa.shared.core.trackers import DialogueStateTracker
38
+ from rasa.shared.nlu.constants import TEXT
39
+ from rasa.shared.nlu.training_data.message import Message
40
+ from rasa.shared.exceptions import ProviderClientAPIException
41
+ from rasa.shared.utils.io import deep_container_fingerprint
42
+ from rasa.shared.utils.llm import (
43
+ get_prompt_template,
44
+ tracker_as_readable_transcript,
45
+ sanitize_message_for_prompt,
46
+ )
47
+ from rasa.utils.log_utils import log_llm
48
+
49
+ COMMAND_PROMPT_FILE_NAME = "command_prompt.jinja2"
50
+
51
+ DEFAULT_COMMAND_PROMPT_TEMPLATE = importlib.resources.read_text(
52
+ "rasa.dialogue_understanding.generator.single_step",
53
+ "command_prompt_template.jinja2",
54
+ )
55
+
56
+ structlogger = structlog.get_logger()
57
+
58
+
59
+ @DefaultV1Recipe.register(
60
+ [
61
+ DefaultV1Recipe.ComponentType.COMMAND_GENERATOR,
62
+ ],
63
+ is_trainable=True,
64
+ )
65
+ class SingleStepLLMCommandGenerator(LLMBasedCommandGenerator):
66
+ """A single step LLM-based command generator."""
67
+
68
+ def __init__(
69
+ self,
70
+ config: Dict[str, Any],
71
+ model_storage: ModelStorage,
72
+ resource: Resource,
73
+ prompt_template: Optional[Text] = None,
74
+ **kwargs: Any,
75
+ ) -> None:
76
+ super().__init__(
77
+ config,
78
+ model_storage,
79
+ resource,
80
+ prompt_template=prompt_template,
81
+ **kwargs,
82
+ )
83
+
84
+ # Set the prompt template
85
+ if config.get("prompt"):
86
+ structlogger.warning(
87
+ "single_step_llm_command_generator.init",
88
+ event_info=(
89
+ "The config parameter 'prompt' is deprecated "
90
+ "and will be removed in Rasa 4.0.0. "
91
+ "Please use the config parameter 'prompt_template' instead. "
92
+ ),
93
+ )
94
+ config_prompt = config.get("prompt") or config.get("prompt_template") or None
95
+ self.prompt_template = prompt_template or get_prompt_template(
96
+ config_prompt,
97
+ DEFAULT_COMMAND_PROMPT_TEMPLATE,
98
+ )
99
+
100
+ self.trace_prompt_tokens = self.config.get("trace_prompt_tokens", False)
101
+
102
+ ### Implementations of LLMBasedCommandGenerator parent
103
+ @staticmethod
104
+ def get_default_config() -> Dict[str, Any]:
105
+ """The component's default config (see parent class for full docstring)."""
106
+ return {
107
+ "prompt": None, # Legacy
108
+ "prompt_template": None,
109
+ USER_INPUT_CONFIG_KEY: None,
110
+ LLM_CONFIG_KEY: None,
111
+ FLOW_RETRIEVAL_KEY: FlowRetrieval.get_default_config(),
112
+ }
113
+
114
+ @classmethod
115
+ def load(
116
+ cls: Any,
117
+ config: Dict[str, Any],
118
+ model_storage: ModelStorage,
119
+ resource: Resource,
120
+ execution_context: ExecutionContext,
121
+ **kwargs: Any,
122
+ ) -> "SingleStepLLMCommandGenerator":
123
+ """Loads trained component (see parent class for full docstring)."""
124
+ # load prompt template from the model storage.
125
+ prompt_template = cls.load_prompt_template_from_model_storage(
126
+ model_storage, resource, COMMAND_PROMPT_FILE_NAME
127
+ )
128
+ # init base command generator
129
+ command_generator = cls(config, model_storage, resource, prompt_template)
130
+ # load flow retrieval if enabled
131
+ if command_generator.enabled_flow_retrieval:
132
+ command_generator.flow_retrieval = cls.load_flow_retrival(
133
+ command_generator.config, model_storage, resource
134
+ )
135
+ return command_generator
136
+
137
+ def persist(self) -> None:
138
+ """Persist this component to disk for future loading."""
139
+ # persist prompt template
140
+ with self._model_storage.write_to(self._resource) as path:
141
+ rasa.shared.utils.io.write_text_file(
142
+ self.prompt_template, path / COMMAND_PROMPT_FILE_NAME
143
+ )
144
+ # persist flow retrieval
145
+ if self.flow_retrieval is not None:
146
+ self.flow_retrieval.persist()
147
+
148
+ async def predict_commands(
149
+ self,
150
+ message: Message,
151
+ flows: FlowsList,
152
+ tracker: Optional[DialogueStateTracker] = None,
153
+ **kwargs: Any,
154
+ ) -> List[Command]:
155
+ """Predict commands using the LLM.
156
+
157
+ Args:
158
+ message: The message from the user.
159
+ flows: The flows available to the user.
160
+ tracker: The tracker containing the current state of the conversation.
161
+ **kwargs: Keyword arguments for forward compatibility.
162
+
163
+ Returns:
164
+ The commands generated by the llm.
165
+ """
166
+ if tracker is None or flows.is_empty():
167
+ # cannot do anything if there are no flows or no tracker
168
+ return []
169
+
170
+ # retrieve flows
171
+ try:
172
+ filtered_flows = await self.filter_flows(message, flows, tracker)
173
+ except ProviderClientAPIException:
174
+ return [ErrorCommand()]
175
+
176
+ flow_prompt = self.render_template(message, tracker, filtered_flows, flows)
177
+ log_llm(
178
+ logger=structlogger,
179
+ log_module="SingleStepLLMCommandGenerator",
180
+ log_event="llm_command_generator.predict_commands.prompt_rendered",
181
+ prompt=flow_prompt,
182
+ )
183
+
184
+ try:
185
+ action_list = await self.invoke_llm(flow_prompt)
186
+ # The check for 'None' maintains compatibility with older versions
187
+ # of LLMCommandGenerator. In previous implementations, 'invoke_llm'
188
+ # might return 'None' to indicate a failure to generate actions.
189
+ if action_list is None:
190
+ return [ErrorCommand()]
191
+ except ProviderClientAPIException:
192
+ return [ErrorCommand()]
193
+
194
+ log_llm(
195
+ logger=structlogger,
196
+ log_module="SingleStepLLMCommandGenerator",
197
+ log_event="llm_command_generator.predict_commands.actions_generated",
198
+ action_list=action_list,
199
+ )
200
+
201
+ commands = self.parse_commands(action_list, tracker, flows)
202
+
203
+ if not commands:
204
+ # no commands are parsed or there's an invalid command
205
+ commands = [CannotHandleCommand()]
206
+ else:
207
+ # if the LLM command generator predicted valid commands and the
208
+ # coexistence feature is used, set the routing slot
209
+ if tracker.has_coexistence_routing_slot:
210
+ commands += [SetSlotCommand(ROUTE_TO_CALM_SLOT, True)]
211
+
212
+ log_llm(
213
+ logger=structlogger,
214
+ log_module="SingleStepLLMCommandGenerator",
215
+ log_event="llm_command_generator.predict_commands.finished",
216
+ commands=commands,
217
+ )
218
+ return commands
219
+
220
+ @classmethod
221
+ def parse_commands(
222
+ cls, actions: Optional[str], tracker: DialogueStateTracker, flows: FlowsList
223
+ ) -> List[Command]:
224
+ """Parse the actions returned by the llm into intent and entities.
225
+
226
+ Args:
227
+ actions: The actions returned by the llm.
228
+ tracker: The tracker containing the current state of the conversation.
229
+ flows: the list of flows
230
+
231
+ Returns:
232
+ The parsed commands.
233
+ """
234
+ if actions is None:
235
+ return []
236
+
237
+ commands: List[Command] = []
238
+
239
+ slot_set_re = re.compile(r"""SetSlot\(([a-zA-Z_][a-zA-Z0-9_-]*?), ?(.*)\)""")
240
+ start_flow_re = re.compile(r"StartFlow\(([a-zA-Z0-9_-]+?)\)")
241
+ cancel_flow_re = re.compile(r"CancelFlow\(\)")
242
+ chitchat_re = re.compile(r"ChitChat\(\)")
243
+ skip_question_re = re.compile(r"SkipQuestion\(\)")
244
+ knowledge_re = re.compile(r"SearchAndReply\(\)")
245
+ humand_handoff_re = re.compile(r"HumanHandoff\(\)")
246
+ clarify_re = re.compile(r"Clarify\(([a-zA-Z0-9_, ]+)\)")
247
+
248
+ for action in actions.strip().splitlines():
249
+ if match := slot_set_re.search(action):
250
+ slot_name = match.group(1).strip()
251
+ slot_value = cls.clean_extracted_value(match.group(2))
252
+ # error case where the llm tries to start a flow using a slot set
253
+ if slot_name == "flow_name":
254
+ commands.extend(cls.start_flow_by_name(slot_value, flows))
255
+ else:
256
+ typed_slot_value = cls.get_nullable_slot_value(slot_value)
257
+ commands.append(
258
+ SetSlotCommand(name=slot_name, value=typed_slot_value)
259
+ )
260
+ elif match := start_flow_re.search(action):
261
+ flow_name = match.group(1).strip()
262
+ commands.extend(cls.start_flow_by_name(flow_name, flows))
263
+ elif cancel_flow_re.search(action):
264
+ commands.append(CancelFlowCommand())
265
+ elif chitchat_re.search(action):
266
+ commands.append(ChitChatAnswerCommand())
267
+ elif skip_question_re.search(action):
268
+ commands.append(SkipQuestionCommand())
269
+ elif knowledge_re.search(action):
270
+ commands.append(KnowledgeAnswerCommand())
271
+ elif humand_handoff_re.search(action):
272
+ commands.append(HumanHandoffCommand())
273
+ elif match := clarify_re.search(action):
274
+ options = sorted([opt.strip() for opt in match.group(1).split(",")])
275
+ valid_options = [
276
+ flow for flow in options if flow in flows.user_flow_ids
277
+ ]
278
+ if len(set(valid_options)) == 1:
279
+ commands.extend(cls.start_flow_by_name(valid_options[0], flows))
280
+ elif len(valid_options) > 1:
281
+ commands.append(ClarifyCommand(valid_options))
282
+
283
+ return commands
284
+
285
+ @classmethod
286
+ def fingerprint_addon(cls: Any, config: Dict[str, Any]) -> Optional[str]:
287
+ """Add a fingerprint of the knowledge base for the graph."""
288
+ config_prompt = config.get("prompt") or config.get("prompt_template") or None
289
+ prompt_template = get_prompt_template(
290
+ config_prompt,
291
+ DEFAULT_COMMAND_PROMPT_TEMPLATE,
292
+ )
293
+ return deep_container_fingerprint(prompt_template)
294
+
295
+ ### Helper methods
296
+ def render_template(
297
+ self,
298
+ message: Message,
299
+ tracker: DialogueStateTracker,
300
+ startable_flows: FlowsList,
301
+ all_flows: FlowsList,
302
+ ) -> str:
303
+ """Render the jinja template to create the prompt for the LLM.
304
+
305
+ Args:
306
+ message: The current message from the user.
307
+ tracker: The tracker containing the current state of the conversation.
308
+ startable_flows: The flows startable at this point in time by the user.
309
+ all_flows: all flows present in the assistant
310
+
311
+ Returns:
312
+ The rendered prompt template.
313
+ """
314
+ # need to make this distinction here because current step of the
315
+ # top_calling_frame would be the call step, but we need the collect step from
316
+ # the called frame. If no call is active calling and called frame are the same.
317
+ top_calling_frame = top_flow_frame(tracker.stack)
318
+ top_called_frame = top_flow_frame(tracker.stack, ignore_call_frames=False)
319
+
320
+ top_flow = top_calling_frame.flow(all_flows) if top_calling_frame else None
321
+ current_step = top_called_frame.step(all_flows) if top_called_frame else None
322
+
323
+ flow_slots = self.prepare_current_flow_slots_for_template(
324
+ top_flow, current_step, tracker
325
+ )
326
+ current_slot, current_slot_description = self.prepare_current_slot_for_template(
327
+ current_step
328
+ )
329
+ current_conversation = tracker_as_readable_transcript(tracker)
330
+ latest_user_message = sanitize_message_for_prompt(message.get(TEXT))
331
+ current_conversation += f"\nUSER: {latest_user_message}"
332
+
333
+ inputs = {
334
+ "available_flows": self.prepare_flows_for_template(
335
+ startable_flows, tracker
336
+ ),
337
+ "current_conversation": current_conversation,
338
+ "flow_slots": flow_slots,
339
+ "current_flow": top_flow.id if top_flow is not None else None,
340
+ "current_slot": current_slot,
341
+ "current_slot_description": current_slot_description,
342
+ "user_message": latest_user_message,
343
+ }
344
+
345
+ return self.compile_template(self.prompt_template).render(**inputs)