rasa-pro 3.8.18__py3-none-any.whl → 3.9.15__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 +38 -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 +36 -31
  167. rasa/dialogue_understanding/processor/command_processor.py +112 -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.15.dist-info}/METADATA +25 -61
  272. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.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.15.dist-info}/NOTICE +0 -0
  277. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/WHEEL +0 -0
  278. {rasa_pro-3.8.18.dist-info → rasa_pro-3.9.15.dist-info}/entry_points.txt +0 -0
@@ -3,8 +3,17 @@ from typing import Any, Dict, List, Optional, Text
3
3
 
4
4
  import structlog
5
5
 
6
- from rasa.dialogue_understanding.commands import Command, StartFlowCommand, ErrorCommand
6
+ from rasa.dialogue_understanding.commands import (
7
+ Command,
8
+ SetSlotCommand,
9
+ StartFlowCommand,
10
+ ErrorCommand,
11
+ )
12
+ from rasa.dialogue_understanding.commands.set_slot_command import SetSlotExtractor
13
+ from rasa.shared.core.constants import SlotMappingType
14
+ from rasa.shared.core.domain import Domain
7
15
  from rasa.shared.core.flows import FlowsList
16
+ from rasa.shared.core.slot_mappings import SlotFillingManager
8
17
  from rasa.shared.core.trackers import DialogueStateTracker
9
18
  from rasa.shared.nlu.training_data.message import Message
10
19
  from rasa.shared.nlu.constants import COMMANDS, TEXT
@@ -53,6 +62,7 @@ class CommandGenerator:
53
62
  messages: List[Message],
54
63
  flows: FlowsList,
55
64
  tracker: Optional[DialogueStateTracker] = None,
65
+ domain: Optional[Domain] = None,
56
66
  ) -> List[Message]:
57
67
  """Process a list of messages. For each message predict commands.
58
68
 
@@ -63,6 +73,7 @@ class CommandGenerator:
63
73
  messages: The messages to process.
64
74
  tracker: The tracker containing the conversation history up to now.
65
75
  flows: The flows to use for command prediction.
76
+ domain: The domain.
66
77
 
67
78
  Returns:
68
79
  The processed messages (usually this is just one during prediction).
@@ -78,14 +89,13 @@ class CommandGenerator:
78
89
  )
79
90
 
80
91
  for message in messages:
81
-
82
92
  if message.get(COMMANDS):
83
93
  # do not overwrite commands if they are already present
84
94
  # i.e. another command generator already predicted commands
85
95
  continue
86
96
 
87
97
  commands = await self._evaluate_and_predict(
88
- message, available_flows, tracker
98
+ message, available_flows, tracker, domain
89
99
  )
90
100
  # Double check commands for guarded flows. Unlikely but the llm could
91
101
  # have predicted a command for a flow that is not in the startable
@@ -93,6 +103,9 @@ class CommandGenerator:
93
103
  commands = self._check_commands_against_startable_flows(
94
104
  commands, startable_flows
95
105
  )
106
+ commands = self._check_commands_against_slot_mappings(
107
+ commands, tracker, domain
108
+ )
96
109
  commands_dicts = [command.as_dict() for command in commands]
97
110
  message.set(COMMANDS, commands_dicts, add_to_output=True)
98
111
 
@@ -139,6 +152,7 @@ class CommandGenerator:
139
152
  message: Message,
140
153
  startable_flows: FlowsList,
141
154
  tracker: Optional[DialogueStateTracker] = None,
155
+ domain: Optional[Domain] = None,
142
156
  ) -> List[Command]:
143
157
  """Evaluates message for errors and predicts commands if no errors are found.
144
158
 
@@ -156,7 +170,9 @@ class CommandGenerator:
156
170
 
157
171
  # if no errors, try predicting commands
158
172
  try:
159
- return await self.predict_commands(message, startable_flows, tracker)
173
+ return await self.predict_commands(
174
+ message, startable_flows, tracker, domain=domain
175
+ )
160
176
  except NotImplementedError:
161
177
  raise
162
178
  except Exception as e:
@@ -168,6 +184,7 @@ class CommandGenerator:
168
184
  message: Message,
169
185
  flows: FlowsList,
170
186
  tracker: Optional[DialogueStateTracker] = None,
187
+ **kwargs: Any,
171
188
  ) -> List[Command]:
172
189
  """Predict commands for a single message.
173
190
 
@@ -175,7 +192,7 @@ class CommandGenerator:
175
192
  message: The message to predict commands for.
176
193
  flows: The flows to use for command prediction.
177
194
  tracker: The tracker containing the conversation history up to now.
178
-
195
+ **kwargs: Keyword arguments for forward compatibility.
179
196
  Returns:
180
197
  The predicted commands.
181
198
  """
@@ -188,7 +205,7 @@ class CommandGenerator:
188
205
 
189
206
  Args:
190
207
  commands: The commands to check.
191
- startable_flows: The flows which have their starting conditions statisfied.
208
+ startable_flows: The flows which have their starting conditions satisfied.
192
209
 
193
210
  Returns:
194
211
  The commands that are startable.
@@ -255,3 +272,72 @@ class CommandGenerator:
255
272
  def check_if_message_is_empty(self, message: Message) -> bool:
256
273
  """Checks if the given message is empty or whitespace-only."""
257
274
  return len(message.get(TEXT, "").strip()) == 0
275
+
276
+ @staticmethod
277
+ def _check_commands_against_slot_mappings(
278
+ commands: List[Command],
279
+ tracker: DialogueStateTracker,
280
+ domain: Optional[Domain] = None,
281
+ ) -> List[Command]:
282
+ """Check if the LLM-issued slot commands are fillable.
283
+
284
+ The LLM-issued slot commands are fillable if the slot
285
+ mappings are satisfied.
286
+ """
287
+ if not domain:
288
+ return commands
289
+
290
+ llm_fillable_slot_names = [
291
+ command.name
292
+ for command in commands
293
+ if isinstance(command, SetSlotCommand)
294
+ and command.extractor == SetSlotExtractor.LLM.value
295
+ ]
296
+
297
+ if not llm_fillable_slot_names:
298
+ return commands
299
+
300
+ llm_fillable_slots = [
301
+ slot for slot in domain.slots if slot.name in llm_fillable_slot_names
302
+ ]
303
+
304
+ slot_filling_manager = SlotFillingManager(domain, tracker)
305
+ slots_to_be_removed = []
306
+
307
+ structlogger.debug(
308
+ "command_processor.check_commands_against_slot_mappings.active_flow",
309
+ active_flow=tracker.active_flow,
310
+ )
311
+
312
+ for slot in llm_fillable_slots:
313
+ should_fill_slot = False
314
+ for mapping in slot.mappings:
315
+ mapping_type = SlotMappingType(mapping.get("type"))
316
+
317
+ should_fill_slot = slot_filling_manager.should_fill_slot(
318
+ slot.name, mapping_type, mapping
319
+ )
320
+
321
+ if should_fill_slot:
322
+ break
323
+
324
+ if not should_fill_slot:
325
+ structlogger.debug(
326
+ "command_processor.check_commands_against_slot_mappings.slot_not_fillable",
327
+ slot_name=slot.name,
328
+ )
329
+ slots_to_be_removed.append(slot.name)
330
+
331
+ if not slots_to_be_removed:
332
+ return commands
333
+
334
+ filtered_commands = [
335
+ command
336
+ for command in commands
337
+ if not (
338
+ isinstance(command, SetSlotCommand)
339
+ and command.name in slots_to_be_removed
340
+ )
341
+ ]
342
+
343
+ return filtered_commands
@@ -0,0 +1,18 @@
1
+ from rasa.shared.utils.llm import (
2
+ DEFAULT_OPENAI_CHAT_MODEL_NAME_ADVANCED,
3
+ DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
4
+ )
5
+
6
+ DEFAULT_LLM_CONFIG = {
7
+ "_type": "openai",
8
+ "request_timeout": 7,
9
+ "temperature": 0.0,
10
+ "model_name": DEFAULT_OPENAI_CHAT_MODEL_NAME_ADVANCED,
11
+ "max_tokens": DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
12
+ }
13
+
14
+ LLM_CONFIG_KEY = "llm"
15
+ USER_INPUT_CONFIG_KEY = "user_input"
16
+
17
+ FLOW_RETRIEVAL_KEY = "flow_retrieval"
18
+ FLOW_RETRIEVAL_ACTIVE_KEY = "active"
@@ -34,6 +34,7 @@ from rasa.shared.core.flows import FlowsList
34
34
  from rasa.shared.core.trackers import DialogueStateTracker
35
35
  from rasa.shared.nlu.constants import TEXT, FLOWS_FROM_SEMANTIC_SEARCH
36
36
  from rasa.shared.nlu.training_data.message import Message
37
+ from rasa.shared.exceptions import ProviderClientAPIException
37
38
  from rasa.shared.utils.llm import (
38
39
  tracker_as_readable_transcript,
39
40
  embedder_factory,
@@ -93,11 +94,10 @@ class FlowRetrieval:
93
94
 
94
95
  @classmethod
95
96
  def validate_config(cls, config: Dict[Text, Any]) -> Dict[Text, Any]:
96
-
97
97
  if config[MAX_FLOWS_FROM_SEMANTIC_SEARCH_KEY] < 0:
98
- config[
99
- MAX_FLOWS_FROM_SEMANTIC_SEARCH_KEY
100
- ] = DEFAULT_MAX_FLOWS_FROM_SEMANTIC_SEARCH
98
+ config[MAX_FLOWS_FROM_SEMANTIC_SEARCH_KEY] = (
99
+ DEFAULT_MAX_FLOWS_FROM_SEMANTIC_SEARCH
100
+ )
101
101
  structlogger.error(
102
102
  f"flow_retrieval.validate_config.{MAX_FLOWS_FROM_SEMANTIC_SEARCH_KEY}.set_as_negative",
103
103
  event_info=(
@@ -407,4 +407,6 @@ class FlowRetrieval:
407
407
  error=e,
408
408
  query=query,
409
409
  )
410
- raise
410
+ raise ProviderClientAPIException(
411
+ message="Cannot fetch flows from vector store", original_exception=e
412
+ )
@@ -0,0 +1,467 @@
1
+ from abc import ABC, abstractmethod
2
+ from functools import lru_cache
3
+ from typing import Dict, Any, List, Optional, Tuple, Union, Text
4
+
5
+ import structlog
6
+ from jinja2 import Template
7
+
8
+ import rasa.shared.utils.io
9
+ from rasa.dialogue_understanding.commands import (
10
+ Command,
11
+ StartFlowCommand,
12
+ )
13
+ from rasa.dialogue_understanding.generator import CommandGenerator
14
+ from rasa.dialogue_understanding.generator.constants import (
15
+ DEFAULT_LLM_CONFIG,
16
+ LLM_CONFIG_KEY,
17
+ FLOW_RETRIEVAL_KEY,
18
+ FLOW_RETRIEVAL_ACTIVE_KEY,
19
+ )
20
+ from rasa.dialogue_understanding.generator.flow_retrieval import FlowRetrieval
21
+ from rasa.engine.graph import GraphComponent, ExecutionContext
22
+ from rasa.engine.recipes.default_recipe import DefaultV1Recipe
23
+ from rasa.engine.storage.resource import Resource
24
+ from rasa.engine.storage.storage import ModelStorage
25
+ from rasa.shared.core.domain import Domain
26
+ from rasa.shared.core.flows import FlowStep, Flow, FlowsList
27
+ from rasa.shared.core.flows.steps.collect import CollectInformationFlowStep
28
+ from rasa.shared.core.trackers import DialogueStateTracker
29
+ from rasa.shared.exceptions import FileIOException
30
+ from rasa.shared.exceptions import ProviderClientAPIException
31
+ from rasa.shared.nlu.constants import FLOWS_IN_PROMPT
32
+ from rasa.shared.nlu.training_data.message import Message
33
+ from rasa.shared.nlu.training_data.training_data import TrainingData
34
+ from rasa.shared.utils.llm import (
35
+ llm_factory,
36
+ allowed_values_for_slot,
37
+ )
38
+ from rasa.utils.log_utils import log_llm
39
+
40
+ structlogger = structlog.get_logger()
41
+
42
+
43
+ @DefaultV1Recipe.register(
44
+ [
45
+ DefaultV1Recipe.ComponentType.COMMAND_GENERATOR,
46
+ ],
47
+ is_trainable=True,
48
+ )
49
+ class LLMBasedCommandGenerator(GraphComponent, CommandGenerator, ABC):
50
+ """An abstract class defining interface and common functionality
51
+ of an LLM-based command generators.
52
+ """
53
+
54
+ def __init__(
55
+ self,
56
+ config: Dict[str, Any],
57
+ model_storage: ModelStorage,
58
+ resource: Resource,
59
+ **kwargs: Any,
60
+ ) -> None:
61
+ super().__init__(config)
62
+ self.config = {**self.get_default_config(), **config}
63
+ self._model_storage = model_storage
64
+ self._resource = resource
65
+ self.flow_retrieval: Optional[FlowRetrieval]
66
+
67
+ if self.enabled_flow_retrieval:
68
+ self.flow_retrieval = FlowRetrieval(
69
+ self.config[FLOW_RETRIEVAL_KEY], model_storage, resource
70
+ )
71
+ structlogger.info("llm_based_command_generator.flow_retrieval.enabled")
72
+ else:
73
+ self.flow_retrieval = None
74
+ structlogger.warn(
75
+ "llm_based_command_generator.flow_retrieval.disabled",
76
+ event_info=(
77
+ "Disabling flow retrieval can cause issues when there are a "
78
+ "large number of flows to be included in the prompt. For more"
79
+ "information see:\n"
80
+ "https://rasa.com/docs/rasa-pro/concepts/dialogue-understanding#how-the-llmcommandgenerator-works"
81
+ ),
82
+ )
83
+
84
+ ### Abstract methods
85
+ @staticmethod
86
+ @abstractmethod
87
+ def get_default_config() -> Dict[str, Any]:
88
+ """The component's default config."""
89
+ pass
90
+
91
+ @classmethod
92
+ @abstractmethod
93
+ def load(
94
+ cls: Any,
95
+ config: Dict[str, Any],
96
+ model_storage: ModelStorage,
97
+ resource: Resource,
98
+ execution_context: ExecutionContext,
99
+ **kwargs: Any,
100
+ ) -> "LLMBasedCommandGenerator":
101
+ """Loads trained component (see parent class for full docstring)."""
102
+ pass
103
+
104
+ @abstractmethod
105
+ def persist(self) -> None:
106
+ """Persist the component to disk for future loading."""
107
+ pass
108
+
109
+ @abstractmethod
110
+ async def predict_commands(
111
+ self,
112
+ message: Message,
113
+ flows: FlowsList,
114
+ tracker: Optional[DialogueStateTracker] = None,
115
+ **kwargs: Any,
116
+ ) -> List[Command]:
117
+ """Predict commands using the LLM.
118
+
119
+ Args:
120
+ message: The message from the user.
121
+ flows: The flows available to the user.
122
+ tracker: The tracker containing the current state of the conversation.
123
+ **kwargs: Keyword arguments for forward compatibility.
124
+
125
+ Returns:
126
+ The commands generated by the llm.
127
+ """
128
+ pass
129
+
130
+ @abstractmethod
131
+ def parse_commands(
132
+ cls, actions: Optional[str], tracker: DialogueStateTracker, flows: FlowsList
133
+ ) -> List[Command]:
134
+ """Parse the actions returned by the llm into intent and entities.
135
+
136
+ Args:
137
+ actions: The actions returned by the llm.
138
+ tracker: The tracker containing the current state of the conversation.
139
+ flows: the list of flows
140
+
141
+ Returns:
142
+ The parsed commands.
143
+ """
144
+ pass
145
+
146
+ @classmethod
147
+ @abstractmethod
148
+ def fingerprint_addon(cls: Any, config: Dict[str, Any]) -> Optional[str]:
149
+ """Add a fingerprint of the knowledge base for the graph."""
150
+ pass
151
+
152
+ ### Shared implementations of GraphComponent parent
153
+ @classmethod
154
+ def create(
155
+ cls,
156
+ config: Dict[str, Any],
157
+ model_storage: ModelStorage,
158
+ resource: Resource,
159
+ execution_context: ExecutionContext,
160
+ ) -> "LLMBasedCommandGenerator":
161
+ """Creates a new untrained component (see parent class for full docstring)."""
162
+ return cls(config, model_storage, resource)
163
+
164
+ def train(
165
+ self, training_data: TrainingData, flows: FlowsList, domain: Domain
166
+ ) -> Resource:
167
+ """Train the llm based command generator. Stores all flows into a vector
168
+ store.
169
+ """
170
+ # flow retrieval is populated with only user-defined flows
171
+ try:
172
+ if self.flow_retrieval is not None and not flows.is_empty():
173
+ self.flow_retrieval.populate(flows.user_flows, domain)
174
+ except Exception as e:
175
+ structlogger.error(
176
+ "llm_based_command_generator.train.failed",
177
+ event_info=("Flow retrieval store isinaccessible."),
178
+ error=e,
179
+ )
180
+ raise
181
+ self.persist()
182
+ return self._resource
183
+
184
+ ### Helper methods
185
+ @property
186
+ def enabled_flow_retrieval(self) -> bool:
187
+ return self.config[FLOW_RETRIEVAL_KEY].get(FLOW_RETRIEVAL_ACTIVE_KEY, True)
188
+
189
+ @lru_cache
190
+ def compile_template(self, template: str) -> Template:
191
+ """Compile the prompt template.
192
+
193
+ Compiling the template is an expensive operation,
194
+ so we cache the result.
195
+ """
196
+ return Template(template)
197
+
198
+ @classmethod
199
+ def load_prompt_template_from_model_storage(
200
+ cls,
201
+ model_storage: ModelStorage,
202
+ resource: Resource,
203
+ prompt_template_file_name: str,
204
+ ) -> Optional[Text]:
205
+ try:
206
+ with model_storage.read_from(resource) as path:
207
+ return rasa.shared.utils.io.read_file(path / prompt_template_file_name)
208
+ except (FileNotFoundError, FileIOException) as e:
209
+ structlogger.warning(
210
+ "llm_based_command_generator.load_prompt_template.failed",
211
+ error=e,
212
+ resource=resource.name,
213
+ )
214
+ return None
215
+
216
+ @classmethod
217
+ def load_flow_retrival(
218
+ cls, config: Dict[Text, Any], model_storage: ModelStorage, resource: Resource
219
+ ) -> Optional[FlowRetrieval]:
220
+ """Load the FlowRetrieval component if it is enabled in the configuration."""
221
+ enable_flow_retrieval = config.get(FLOW_RETRIEVAL_KEY, {}).get(
222
+ FLOW_RETRIEVAL_ACTIVE_KEY, True
223
+ )
224
+ if enable_flow_retrieval:
225
+ return FlowRetrieval.load(
226
+ config=config.get(FLOW_RETRIEVAL_KEY),
227
+ model_storage=model_storage,
228
+ resource=resource,
229
+ )
230
+ return None
231
+
232
+ async def filter_flows(
233
+ self,
234
+ message: Message,
235
+ flows: FlowsList,
236
+ tracker: Optional[DialogueStateTracker] = None,
237
+ ) -> FlowsList:
238
+ """Filters the available flows.
239
+
240
+ Args:
241
+ message: The message from the user.
242
+ flows: The flows available to the user.
243
+ tracker: The tracker containing the current state of the conversation.
244
+
245
+ Returns:
246
+ Filtered list of flows.
247
+ """
248
+ # If the flow retrieval is disabled, use the all the provided flows.
249
+ filtered_flows = (
250
+ await self.flow_retrieval.filter_flows(tracker, message, flows)
251
+ if self.flow_retrieval is not None
252
+ else flows
253
+ )
254
+ # Filter flows based on current context (tracker and message)
255
+ # to identify which flows LLM can potentially start.
256
+ if tracker:
257
+ filtered_flows = tracker.get_startable_flows(filtered_flows)
258
+ else:
259
+ filtered_flows = filtered_flows
260
+
261
+ # add the filtered flows to the message for evaluation purposes
262
+ message.set(
263
+ FLOWS_IN_PROMPT, list(filtered_flows.user_flow_ids), add_to_output=True
264
+ )
265
+ log_llm(
266
+ logger=structlogger,
267
+ log_module="LLMBasedCommandGenerator",
268
+ log_event="llm_based_command_generator.predict_commands.filtered_flows",
269
+ message=message.data,
270
+ enabled_flow_retrieval=self.flow_retrieval is not None,
271
+ relevant_flows=list(filtered_flows.user_flow_ids),
272
+ )
273
+ return filtered_flows
274
+
275
+ async def invoke_llm(self, prompt: Text) -> Optional[Text]:
276
+ """Use LLM to generate a response.
277
+
278
+ Args:
279
+ prompt: The prompt to send to the LLM.
280
+
281
+ Returns:
282
+ The generated text.
283
+
284
+ Raises:
285
+ ProviderClientAPIException if an error during API call.
286
+ """
287
+ llm = llm_factory(self.config.get(LLM_CONFIG_KEY), DEFAULT_LLM_CONFIG)
288
+ try:
289
+ return await llm.apredict(prompt)
290
+ except Exception as e:
291
+ # unfortunately, langchain does not wrap LLM exceptions which means
292
+ # we have to catch all exceptions here
293
+ structlogger.error("llm_based_command_generator.llm.error", error=e)
294
+ raise ProviderClientAPIException(
295
+ message="LLM call exception", original_exception=e
296
+ )
297
+
298
+ @staticmethod
299
+ def start_flow_by_name(flow_name: str, flows: FlowsList) -> List[Command]:
300
+ """Start a flow by name.
301
+
302
+ If the flow does not exist, no command is returned.
303
+ """
304
+ if flow_name in flows.user_flow_ids:
305
+ return [StartFlowCommand(flow=flow_name)]
306
+ else:
307
+ structlogger.debug(
308
+ "llm_command_generator.flow.start_invalid_flow_id", flow=flow_name
309
+ )
310
+ return []
311
+
312
+ @staticmethod
313
+ def is_none_value(value: str) -> bool:
314
+ """Check if the value is a none value."""
315
+ return value in {
316
+ "[missing information]",
317
+ "[missing]",
318
+ "None",
319
+ "undefined",
320
+ "null",
321
+ }
322
+
323
+ @staticmethod
324
+ def clean_extracted_value(value: str) -> str:
325
+ """Clean up the extracted value from the llm."""
326
+ # replace any combination of single quotes, double quotes, and spaces
327
+ # from the beginning and end of the string
328
+ return value.strip("'\" ")
329
+
330
+ @classmethod
331
+ def get_nullable_slot_value(cls, slot_value: str) -> Union[str, None]:
332
+ """Get the slot value or None if the value is a none value.
333
+
334
+ Args:
335
+ slot_value: the value to coerce
336
+
337
+ Returns:
338
+ The slot value or None if the value is a none value.
339
+ """
340
+ return slot_value if not cls.is_none_value(slot_value) else None
341
+
342
+ def prepare_flows_for_template(
343
+ self, flows: FlowsList, tracker: DialogueStateTracker
344
+ ) -> List[Dict[str, Any]]:
345
+ """Format data on available flows for insertion into the prompt template.
346
+
347
+ Args:
348
+ flows: The flows available to the user.
349
+ tracker: The tracker containing the current state of the conversation.
350
+
351
+ Returns:
352
+ The inputs for the prompt template.
353
+ """
354
+ result = []
355
+ for flow in flows.user_flows:
356
+ slots_with_info = [
357
+ {
358
+ "name": q.collect,
359
+ "description": q.description,
360
+ "allowed_values": allowed_values_for_slot(tracker.slots[q.collect]),
361
+ }
362
+ for q in flow.get_collect_steps()
363
+ if self.is_extractable(q, tracker)
364
+ ]
365
+ result.append(
366
+ {
367
+ "name": flow.id,
368
+ "description": flow.description,
369
+ "slots": slots_with_info,
370
+ }
371
+ )
372
+ return result
373
+
374
+ @staticmethod
375
+ def is_extractable(
376
+ collect_step: CollectInformationFlowStep,
377
+ tracker: DialogueStateTracker,
378
+ current_step: Optional[FlowStep] = None,
379
+ ) -> bool:
380
+ """Check if the `collect` can be filled.
381
+
382
+ A collect slot can only be filled if the slot exist
383
+ and either the collect has been asked already or the
384
+ slot has been filled already.
385
+
386
+ Args:
387
+ collect_step: The collect_information step.
388
+ tracker: The tracker containing the current state of the conversation.
389
+ current_step: The current step in the flow.
390
+
391
+ Returns:
392
+ `True` if the slot can be filled, `False` otherwise.
393
+ """
394
+ slot = tracker.slots.get(collect_step.collect)
395
+ if slot is None:
396
+ return False
397
+
398
+ return (
399
+ # we can fill because this is a slot that can be filled ahead of time
400
+ not collect_step.ask_before_filling
401
+ # we can fill because the slot has been filled already
402
+ or slot.has_been_set
403
+ # we can fill because the is currently getting asked
404
+ or (
405
+ current_step is not None
406
+ and isinstance(current_step, CollectInformationFlowStep)
407
+ and current_step.collect == collect_step.collect
408
+ )
409
+ )
410
+
411
+ @staticmethod
412
+ def get_slot_value(tracker: DialogueStateTracker, slot_name: str) -> str:
413
+ """Get the slot value from the tracker.
414
+
415
+ Args:
416
+ tracker: The tracker containing the current state of the conversation.
417
+ slot_name: The name of the slot.
418
+
419
+ Returns:
420
+ The slot value as a string.
421
+ """
422
+ slot_value = tracker.get_slot(slot_name)
423
+ if slot_value is None:
424
+ return "undefined"
425
+ else:
426
+ return str(slot_value)
427
+
428
+ def prepare_current_flow_slots_for_template(
429
+ self, top_flow: Flow, current_step: FlowStep, tracker: DialogueStateTracker
430
+ ) -> List[Dict[str, Any]]:
431
+ """Prepare the current flow slots for the template.
432
+
433
+ Args:
434
+ top_flow: The top flow.
435
+ current_step: The current step in the flow.
436
+ tracker: The tracker containing the current state of the conversation.
437
+
438
+ Returns:
439
+ The slots with values, types, allowed values and a description.
440
+ """
441
+ if top_flow is not None:
442
+ flow_slots = [
443
+ {
444
+ "name": collect_step.collect,
445
+ "value": self.get_slot_value(tracker, collect_step.collect),
446
+ "type": tracker.slots[collect_step.collect].type_name,
447
+ "allowed_values": allowed_values_for_slot(
448
+ tracker.slots[collect_step.collect]
449
+ ),
450
+ "description": collect_step.description,
451
+ }
452
+ for collect_step in top_flow.get_collect_steps()
453
+ if self.is_extractable(collect_step, tracker, current_step)
454
+ ]
455
+ else:
456
+ flow_slots = []
457
+ return flow_slots
458
+
459
+ def prepare_current_slot_for_template(
460
+ self, current_step: FlowStep
461
+ ) -> Tuple[Union[str, None], Union[str, None]]:
462
+ """Prepare the current slot for the template."""
463
+ return (
464
+ (current_step.collect, current_step.description)
465
+ if isinstance(current_step, CollectInformationFlowStep)
466
+ else (None, None)
467
+ )