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
@@ -1,71 +1,16 @@
1
- import importlib.resources
2
- import re
3
- from typing import Dict, Any, List, Optional, Tuple, Union, Text
4
- from functools import lru_cache
5
- import rasa.shared.utils.io
1
+ from typing import Dict, Any, Optional, Text
2
+
6
3
  import structlog
7
- from jinja2 import Template
8
- from rasa.dialogue_understanding.commands import (
9
- Command,
10
- ErrorCommand,
11
- SetSlotCommand,
12
- CancelFlowCommand,
13
- StartFlowCommand,
14
- HumanHandoffCommand,
15
- ChitChatAnswerCommand,
16
- SkipQuestionCommand,
17
- KnowledgeAnswerCommand,
18
- ClarifyCommand,
19
- CannotHandleCommand,
4
+ from deprecated import deprecated # type: ignore[import]
5
+
6
+ from rasa.dialogue_understanding.generator.single_step.single_step_llm_command_generator import ( # noqa: E501
7
+ SingleStepLLMCommandGenerator,
20
8
  )
21
- from rasa.dialogue_understanding.generator import CommandGenerator
22
- from rasa.dialogue_understanding.generator.flow_retrieval import FlowRetrieval
23
- from rasa.dialogue_understanding.stack.utils import top_flow_frame
24
- from rasa.engine.graph import GraphComponent, ExecutionContext
25
9
  from rasa.engine.recipes.default_recipe import DefaultV1Recipe
26
10
  from rasa.engine.storage.resource import Resource
27
11
  from rasa.engine.storage.storage import ModelStorage
28
- from rasa.shared.constants import ROUTE_TO_CALM_SLOT
29
- from rasa.shared.core.domain import Domain
30
- from rasa.shared.core.flows import FlowStep, Flow, FlowsList
31
- from rasa.shared.core.flows.steps.collect import CollectInformationFlowStep
32
- from rasa.shared.core.trackers import DialogueStateTracker
33
- from rasa.shared.exceptions import FileIOException
34
- from rasa.shared.nlu.constants import FLOWS_IN_PROMPT
35
- from rasa.shared.nlu.constants import TEXT
36
- from rasa.shared.nlu.training_data.message import Message
37
- from rasa.shared.nlu.training_data.training_data import TrainingData
38
- from rasa.shared.utils.io import deep_container_fingerprint
39
- from rasa.shared.utils.llm import (
40
- DEFAULT_OPENAI_CHAT_MODEL_NAME_ADVANCED,
41
- DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
42
- get_prompt_template,
43
- llm_factory,
44
- tracker_as_readable_transcript,
45
- sanitize_message_for_prompt,
46
- allowed_values_for_slot,
47
- )
48
- from rasa.utils.log_utils import log_llm
49
-
50
- COMMAND_PROMPT_FILE_NAME = "command_prompt.jinja2"
51
-
52
- DEFAULT_COMMAND_PROMPT_TEMPLATE = importlib.resources.read_text(
53
- "rasa.dialogue_understanding.generator", "command_prompt_template.jinja2"
54
- )
55
-
56
- DEFAULT_LLM_CONFIG = {
57
- "_type": "openai",
58
- "request_timeout": 7,
59
- "temperature": 0.0,
60
- "model_name": DEFAULT_OPENAI_CHAT_MODEL_NAME_ADVANCED,
61
- "max_tokens": DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
62
- }
63
-
64
- LLM_CONFIG_KEY = "llm"
65
- USER_INPUT_CONFIG_KEY = "user_input"
66
-
67
- FLOW_RETRIEVAL_KEY = "flow_retrieval"
68
- FLOW_RETRIEVAL_ACTIVE_KEY = "active"
12
+ from rasa.shared.exceptions import ProviderClientAPIException
13
+ from rasa.shared.utils.io import raise_deprecation_warning
69
14
 
70
15
  structlogger = structlog.get_logger()
71
16
 
@@ -76,562 +21,47 @@ structlogger = structlog.get_logger()
76
21
  ],
77
22
  is_trainable=True,
78
23
  )
79
- class LLMCommandGenerator(GraphComponent, CommandGenerator):
80
- """An LLM-based command generator."""
81
-
82
- @staticmethod
83
- def get_default_config() -> Dict[str, Any]:
84
- """The component's default config (see parent class for full docstring)."""
85
- return {
86
- "prompt": None,
87
- USER_INPUT_CONFIG_KEY: None,
88
- LLM_CONFIG_KEY: None,
89
- FLOW_RETRIEVAL_KEY: FlowRetrieval.get_default_config(),
90
- }
91
-
24
+ @deprecated(
25
+ reason=(
26
+ "The LLMCommandGenerator is deprecated and will be removed in Rasa 4.0.0. "
27
+ "Please use use SingleStepLLMCommandGenerator instead."
28
+ )
29
+ )
30
+ class LLMCommandGenerator(SingleStepLLMCommandGenerator):
92
31
  def __init__(
93
32
  self,
94
33
  config: Dict[str, Any],
95
34
  model_storage: ModelStorage,
96
35
  resource: Resource,
97
36
  prompt_template: Optional[Text] = None,
98
- ) -> None:
99
- super().__init__(config)
100
- self.config = {**self.get_default_config(), **config}
101
-
102
- self.prompt_template = prompt_template or get_prompt_template(
103
- config.get("prompt"),
104
- DEFAULT_COMMAND_PROMPT_TEMPLATE,
105
- )
106
-
107
- self._model_storage = model_storage
108
- self._resource = resource
109
- self.trace_prompt_tokens = self.config.get("trace_prompt_tokens", False)
110
-
111
- self.flow_retrieval: Optional[FlowRetrieval]
112
-
113
- if self.enabled_flow_retrieval:
114
- self.flow_retrieval = FlowRetrieval(
115
- self.config[FLOW_RETRIEVAL_KEY], model_storage, resource
116
- )
117
- structlogger.info("llm_command_generator.flow_retrieval.enabled")
118
- else:
119
- self.flow_retrieval = None
120
- structlogger.warn(
121
- "llm_command_generator.flow_retrieval.disabled",
122
- event_info=(
123
- "Disabling flow retrieval can cause issues when there are a "
124
- "large number of flows to be included in the prompt. For more"
125
- "information see:\n"
126
- "https://rasa.com/docs/rasa-pro/concepts/dialogue-understanding#how-the-llmcommandgenerator-works"
127
- ),
128
- )
129
-
130
- @property
131
- def enabled_flow_retrieval(self) -> bool:
132
- return self.config[FLOW_RETRIEVAL_KEY].get(FLOW_RETRIEVAL_ACTIVE_KEY, True)
133
-
134
- @lru_cache
135
- def _compile_template(self, template: str) -> Template:
136
- """Compile the prompt template.
137
-
138
- Compiling the template is an expensive operation,
139
- so we cache the result."""
140
- return Template(template)
141
-
142
- @classmethod
143
- def create(
144
- cls,
145
- config: Dict[str, Any],
146
- model_storage: ModelStorage,
147
- resource: Resource,
148
- execution_context: ExecutionContext,
149
- ) -> "LLMCommandGenerator":
150
- """Creates a new untrained component (see parent class for full docstring)."""
151
- return cls(config, model_storage, resource)
152
-
153
- @classmethod
154
- def load(
155
- cls,
156
- config: Dict[str, Any],
157
- model_storage: ModelStorage,
158
- resource: Resource,
159
- execution_context: ExecutionContext,
160
37
  **kwargs: Any,
161
- ) -> "LLMCommandGenerator":
162
- """Loads trained component (see parent class for full docstring)."""
163
- # load prompt template from the model storage.
164
- prompt_template = cls._load_prompt_template_from_model_storage(
165
- model_storage, resource
166
- )
167
- # init base command generator
168
- command_generator = cls(config, model_storage, resource, prompt_template)
169
- # load flow retrieval if enabled
170
- if command_generator.enabled_flow_retrieval:
171
- command_generator.flow_retrieval = cls._load_flow_retrival(
172
- command_generator.config, model_storage, resource
173
- )
174
- return command_generator
175
-
176
- @classmethod
177
- def _load_prompt_template_from_model_storage(
178
- cls, model_storage: ModelStorage, resource: Resource
179
- ) -> Optional[Text]:
180
- try:
181
- with model_storage.read_from(resource) as path:
182
- return rasa.shared.utils.io.read_file(path / COMMAND_PROMPT_FILE_NAME)
183
- except (FileNotFoundError, FileIOException) as e:
184
- structlogger.warning(
185
- "llm_command_generator.load_prompt_template.failed",
186
- error=e,
187
- resource=resource.name,
188
- )
189
- return None
190
-
191
- @classmethod
192
- def _load_flow_retrival(
193
- cls, config: Dict[Text, Any], model_storage: ModelStorage, resource: Resource
194
- ) -> Optional[FlowRetrieval]:
195
- enable_flow_retrieval = config.get(FLOW_RETRIEVAL_KEY, {}).get(
196
- FLOW_RETRIEVAL_ACTIVE_KEY, True
197
- )
198
- if enable_flow_retrieval:
199
- return FlowRetrieval.load(
200
- config=config.get(FLOW_RETRIEVAL_KEY),
201
- model_storage=model_storage,
202
- resource=resource,
203
- )
204
- return None
205
-
206
- def train(
207
- self, training_data: TrainingData, flows: FlowsList, domain: Domain
208
- ) -> Resource:
209
- """Train the llm command generator. Stores all flows into a vector store."""
210
- # flow retrieval is populated with only user-defined flows
211
- try:
212
- if self.flow_retrieval is not None:
213
- self.flow_retrieval.populate(flows.user_flows, domain)
214
- except Exception as e:
215
- structlogger.error(
216
- "llm_command_generator.train.failed",
217
- event_info=("Flow retrieval store isinaccessible."),
218
- error=e,
219
- )
220
- raise
221
- self.persist()
222
- return self._resource
223
-
224
- def persist(self) -> None:
225
- """Persist this component to disk for future loading."""
226
- # persist prompt template
227
- with self._model_storage.write_to(self._resource) as path:
228
- rasa.shared.utils.io.write_text_file(
229
- self.prompt_template, path / COMMAND_PROMPT_FILE_NAME
230
- )
231
- # persist flow retrieval
232
- if self.flow_retrieval is not None:
233
- self.flow_retrieval.persist()
234
-
235
- async def predict_commands(
236
- self,
237
- message: Message,
238
- flows: FlowsList,
239
- tracker: Optional[DialogueStateTracker] = None,
240
- ) -> List[Command]:
241
- """Predict commands using the LLM.
242
-
243
- Args:
244
- message: The message from the user.
245
- flows: The flows available to the user.
246
- tracker: The tracker containing the current state of the conversation.
247
-
248
- Returns:
249
- The commands generated by the llm.
250
- """
251
- if tracker is None or flows.is_empty():
252
- # cannot do anything if there are no flows or no tracker
253
- return []
254
-
255
- commands: List[Command]
256
-
257
- # retrieve flows
258
- try:
259
- # If the flow retrieval is disabled, use the all the provided flows.
260
- filtered_flows = (
261
- await self.flow_retrieval.filter_flows(tracker, message, flows)
262
- if self.flow_retrieval is not None
263
- else flows
264
- )
265
- # Filter flows based on current context (tracker and message)
266
- # to identify which flows LLM can potentially start.
267
- filtered_flows = tracker.get_startable_flows(filtered_flows)
268
- except Exception:
269
- # e.g. in case of API problems (are being logged by the flow retrieval)
270
- commands = [ErrorCommand()]
271
- # if coexistence feature is used, set the routing slot
272
- if tracker.has_coexistence_routing_slot:
273
- commands += [SetSlotCommand(ROUTE_TO_CALM_SLOT, True)]
274
- return commands
275
-
276
- # add the filtered flows to the message for evaluation purposes
277
- message.set(
278
- FLOWS_IN_PROMPT, list(filtered_flows.user_flow_ids), add_to_output=True
279
- )
280
- log_llm(
281
- logger=structlogger,
282
- log_module="LLMCommandGenerator",
283
- log_event="llm_command_generator.predict_commands.filtered_flows",
284
- message=message.data[TEXT],
285
- enabled_flow_retrieval=self.flow_retrieval is not None,
286
- relevant_flows=list(filtered_flows.user_flow_ids),
287
- )
288
-
289
- flow_prompt = self.render_template(message, tracker, filtered_flows, flows)
290
- log_llm(
291
- logger=structlogger,
292
- log_module="LLMCommandGenerator",
293
- log_event="llm_command_generator.predict_commands.prompt_rendered",
294
- prompt=flow_prompt,
295
- )
296
-
297
- action_list = await self._generate_action_list_using_llm(flow_prompt)
298
- log_llm(
299
- logger=structlogger,
300
- log_module="LLMCommandGenerator",
301
- log_event="llm_command_generator.predict_commands.actions_generated",
302
- action_list=action_list,
38
+ ) -> None:
39
+ raise_deprecation_warning(
40
+ message="`LLMCommandGenerator` has been renamed "
41
+ "to `SingleStepLLMCommandGenerator`."
42
+ "Support for the former name `LLMCommandGenerator` "
43
+ "will be removed in Rasa `4.0.0`."
44
+ "Please modify your assistant's configuration to "
45
+ "use `SingleStepLLMCommandGenerator` instead."
303
46
  )
304
47
 
305
- if action_list is None:
306
- # if action_list is None, we couldn't get any response from the LLM
307
- commands = [ErrorCommand()]
308
- else:
309
- commands = self.parse_commands(action_list, tracker, flows)
310
- if not commands:
311
- # no commands are parsed or there's an invalid command
312
- commands = [CannotHandleCommand()]
313
-
314
- # if coexistence feature is used, set the routing slot
315
- if tracker.has_coexistence_routing_slot:
316
- commands += [SetSlotCommand(ROUTE_TO_CALM_SLOT, True)]
317
-
318
- log_llm(
319
- logger=structlogger,
320
- log_module="LLMCommandGenerator",
321
- log_event="llm_command_generator.predict_commands.finished",
322
- commands=commands,
48
+ super().__init__(
49
+ config,
50
+ model_storage,
51
+ resource,
52
+ prompt_template=prompt_template,
53
+ **kwargs,
323
54
  )
324
- return commands
325
-
326
- def render_template(
327
- self,
328
- message: Message,
329
- tracker: DialogueStateTracker,
330
- startable_flows: FlowsList,
331
- all_flows: FlowsList,
332
- ) -> str:
333
- """Render the jinja template to create the prompt for the LLM.
334
-
335
- Args:
336
- message: The current message from the user.
337
- tracker: The tracker containing the current state of the conversation.
338
- startable_flows: The flows startable at this point in time by the user.
339
- all_flows: all flows present in the assistant
340
-
341
- Returns:
342
- The rendered prompt template.
343
- """
344
- # need to make this distinction here because current step of the
345
- # top_calling_frame would be the call step, but we need the collect step from
346
- # the called frame. If no call is active calling and called frame are the same.
347
- top_calling_frame = top_flow_frame(tracker.stack)
348
- top_called_frame = top_flow_frame(tracker.stack, ignore_call_frames=False)
349
-
350
- top_flow = top_calling_frame.flow(all_flows) if top_calling_frame else None
351
- current_step = top_called_frame.step(all_flows) if top_called_frame else None
352
-
353
- flow_slots = self.prepare_current_flow_slots_for_template(
354
- top_flow, current_step, tracker
355
- )
356
- current_slot, current_slot_description = self.prepare_current_slot_for_template(
357
- current_step
358
- )
359
- current_conversation = tracker_as_readable_transcript(tracker)
360
- latest_user_message = sanitize_message_for_prompt(message.get(TEXT))
361
- current_conversation += f"\nUSER: {latest_user_message}"
362
-
363
- inputs = {
364
- "available_flows": self.prepare_flows_for_template(
365
- startable_flows, tracker
366
- ),
367
- "current_conversation": current_conversation,
368
- "flow_slots": flow_slots,
369
- "current_flow": top_flow.id if top_flow is not None else None,
370
- "current_slot": current_slot,
371
- "current_slot_description": current_slot_description,
372
- "user_message": latest_user_message,
373
- }
374
-
375
- return self._compile_template(self.prompt_template).render(**inputs)
376
-
377
- async def _generate_action_list_using_llm(self, prompt: str) -> Optional[str]:
378
- """Use LLM to generate a response.
379
-
380
- Args:
381
- prompt: The prompt to send to the LLM.
382
-
383
- Returns:
384
- The generated text.
385
- """
386
- llm = llm_factory(self.config.get(LLM_CONFIG_KEY), DEFAULT_LLM_CONFIG)
387
55
 
56
+ async def invoke_llm(self, prompt: Text) -> Optional[Text]:
388
57
  try:
389
- return await llm.apredict(prompt)
390
- except Exception as e:
391
- # unfortunately, langchain does not wrap LLM exceptions which means
392
- # we have to catch all exceptions here
393
- structlogger.error("llm_command_generator.llm.error", error=e)
58
+ return await super().invoke_llm(prompt)
59
+ except ProviderClientAPIException:
60
+ # Returning 'None' in case of a ProviderClientAPIException ensures
61
+ # backward compatibility with previous versions. In earlier
62
+ # implementations, 'invoke_llm' was expected to return 'None' when
63
+ # encountering API-related issues. This practice is maintained here
64
+ # to prevent breaking changes in how exceptions are handled and
65
+ # propagated in existing integrations, where callers might not yet
66
+ # be prepared to manage exceptions directly.
394
67
  return None
395
-
396
- @classmethod
397
- def parse_commands(
398
- cls, actions: Optional[str], tracker: DialogueStateTracker, flows: FlowsList
399
- ) -> List[Command]:
400
- """Parse the actions returned by the llm into intent and entities.
401
-
402
- Args:
403
- actions: The actions returned by the llm.
404
- tracker: The tracker containing the current state of the conversation.
405
- flows: the list of flows
406
-
407
- Returns:
408
- The parsed commands.
409
- """
410
- if actions is None:
411
- return []
412
-
413
- commands: List[Command] = []
414
-
415
- slot_set_re = re.compile(r"""SetSlot\(([a-zA-Z_][a-zA-Z0-9_-]*?), ?(.*)\)""")
416
- start_flow_re = re.compile(r"StartFlow\(([a-zA-Z0-9_-]+?)\)")
417
- cancel_flow_re = re.compile(r"CancelFlow\(\)")
418
- chitchat_re = re.compile(r"ChitChat\(\)")
419
- skip_question_re = re.compile(r"SkipQuestion\(\)")
420
- knowledge_re = re.compile(r"SearchAndReply\(\)")
421
- humand_handoff_re = re.compile(r"HumanHandoff\(\)")
422
- clarify_re = re.compile(r"Clarify\(([a-zA-Z0-9_, ]+)\)")
423
-
424
- for action in actions.strip().splitlines():
425
- if match := slot_set_re.search(action):
426
- slot_name = match.group(1).strip()
427
- slot_value = cls.clean_extracted_value(match.group(2))
428
- # error case where the llm tries to start a flow using a slot set
429
- if slot_name == "flow_name":
430
- commands.extend(cls.start_flow_by_name(slot_value, flows))
431
- else:
432
- typed_slot_value = cls.get_nullable_slot_value(slot_value)
433
- commands.append(
434
- SetSlotCommand(name=slot_name, value=typed_slot_value)
435
- )
436
- elif match := start_flow_re.search(action):
437
- flow_name = match.group(1).strip()
438
- commands.extend(cls.start_flow_by_name(flow_name, flows))
439
- elif cancel_flow_re.search(action):
440
- commands.append(CancelFlowCommand())
441
- elif chitchat_re.search(action):
442
- commands.append(ChitChatAnswerCommand())
443
- elif skip_question_re.search(action):
444
- commands.append(SkipQuestionCommand())
445
- elif knowledge_re.search(action):
446
- commands.append(KnowledgeAnswerCommand())
447
- elif humand_handoff_re.search(action):
448
- commands.append(HumanHandoffCommand())
449
- elif match := clarify_re.search(action):
450
- options = sorted([opt.strip() for opt in match.group(1).split(",")])
451
- valid_options = [
452
- flow for flow in options if flow in flows.user_flow_ids
453
- ]
454
- if len(valid_options) >= 1:
455
- commands.append(ClarifyCommand(valid_options))
456
-
457
- return commands
458
-
459
- @staticmethod
460
- def start_flow_by_name(flow_name: str, flows: FlowsList) -> List[Command]:
461
- """Start a flow by name.
462
-
463
- If the flow does not exist, no command is returned.
464
- """
465
- if flow_name in flows.user_flow_ids:
466
- return [StartFlowCommand(flow=flow_name)]
467
- else:
468
- structlogger.debug(
469
- "llm_command_generator.flow.start_invalid_flow_id", flow=flow_name
470
- )
471
- return []
472
-
473
- @staticmethod
474
- def is_none_value(value: str) -> bool:
475
- """Check if the value is a none value."""
476
- return value in {
477
- "[missing information]",
478
- "[missing]",
479
- "None",
480
- "undefined",
481
- "null",
482
- }
483
-
484
- @staticmethod
485
- def clean_extracted_value(value: str) -> str:
486
- """Clean up the extracted value from the llm."""
487
- # replace any combination of single quotes, double quotes, and spaces
488
- # from the beginning and end of the string
489
- return value.strip("'\" ")
490
-
491
- @classmethod
492
- def get_nullable_slot_value(cls, slot_value: str) -> Union[str, None]:
493
- """Get the slot value or None if the value is a none value.
494
-
495
- Args:
496
- slot_value: the value to coerce
497
-
498
- Returns:
499
- The slot value or None if the value is a none value.
500
- """
501
- return slot_value if not cls.is_none_value(slot_value) else None
502
-
503
- def prepare_flows_for_template(
504
- self, flows: FlowsList, tracker: DialogueStateTracker
505
- ) -> List[Dict[str, Any]]:
506
- """Format data on available flows for insertion into the prompt template.
507
-
508
- Args:
509
- flows: The flows available to the user.
510
- tracker: The tracker containing the current state of the conversation.
511
-
512
- Returns:
513
- The inputs for the prompt template.
514
- """
515
- result = []
516
- for flow in flows.user_flows:
517
- slots_with_info = [
518
- {
519
- "name": q.collect,
520
- "description": q.description,
521
- "allowed_values": allowed_values_for_slot(tracker.slots[q.collect]),
522
- }
523
- for q in flow.get_collect_steps()
524
- if self.is_extractable(q, tracker)
525
- ]
526
- result.append(
527
- {
528
- "name": flow.id,
529
- "description": flow.description,
530
- "slots": slots_with_info,
531
- }
532
- )
533
- return result
534
-
535
- @staticmethod
536
- def is_extractable(
537
- collect_step: CollectInformationFlowStep,
538
- tracker: DialogueStateTracker,
539
- current_step: Optional[FlowStep] = None,
540
- ) -> bool:
541
- """Check if the `collect` can be filled.
542
-
543
- A collect slot can only be filled if the slot exist
544
- and either the collect has been asked already or the
545
- slot has been filled already.
546
-
547
- Args:
548
- collect_step: The collect_information step.
549
- tracker: The tracker containing the current state of the conversation.
550
- current_step: The current step in the flow.
551
-
552
- Returns:
553
- `True` if the slot can be filled, `False` otherwise.
554
- """
555
- slot = tracker.slots.get(collect_step.collect)
556
- if slot is None:
557
- return False
558
-
559
- return (
560
- # we can fill because this is a slot that can be filled ahead of time
561
- not collect_step.ask_before_filling
562
- # we can fill because the slot has been filled already
563
- or slot.has_been_set
564
- # we can fill because the is currently getting asked
565
- or (
566
- current_step is not None
567
- and isinstance(current_step, CollectInformationFlowStep)
568
- and current_step.collect == collect_step.collect
569
- )
570
- )
571
-
572
- @staticmethod
573
- def get_slot_value(tracker: DialogueStateTracker, slot_name: str) -> str:
574
- """Get the slot value from the tracker.
575
-
576
- Args:
577
- tracker: The tracker containing the current state of the conversation.
578
- slot_name: The name of the slot.
579
-
580
- Returns:
581
- The slot value as a string.
582
- """
583
- slot_value = tracker.get_slot(slot_name)
584
- if slot_value is None:
585
- return "undefined"
586
- else:
587
- return str(slot_value)
588
-
589
- def prepare_current_flow_slots_for_template(
590
- self, top_flow: Flow, current_step: FlowStep, tracker: DialogueStateTracker
591
- ) -> List[Dict[str, Any]]:
592
- """Prepare the current flow slots for the template.
593
-
594
- Args:
595
- top_flow: The top flow.
596
- current_step: The current step in the flow.
597
- tracker: The tracker containing the current state of the conversation.
598
-
599
- Returns:
600
- The slots with values, types, allowed values and a description.
601
- """
602
- if top_flow is not None:
603
- flow_slots = [
604
- {
605
- "name": collect_step.collect,
606
- "value": self.get_slot_value(tracker, collect_step.collect),
607
- "type": tracker.slots[collect_step.collect].type_name,
608
- "allowed_values": allowed_values_for_slot(
609
- tracker.slots[collect_step.collect]
610
- ),
611
- "description": collect_step.description,
612
- }
613
- for collect_step in top_flow.get_collect_steps()
614
- if self.is_extractable(collect_step, tracker, current_step)
615
- ]
616
- else:
617
- flow_slots = []
618
- return flow_slots
619
-
620
- def prepare_current_slot_for_template(
621
- self, current_step: FlowStep
622
- ) -> Tuple[Union[str, None], Union[str, None]]:
623
- """Prepare the current slot for the template."""
624
- return (
625
- (current_step.collect, current_step.description)
626
- if isinstance(current_step, CollectInformationFlowStep)
627
- else (None, None)
628
- )
629
-
630
- @classmethod
631
- def fingerprint_addon(cls, config: Dict[str, Any]) -> Optional[str]:
632
- """Add a fingerprint of the knowledge base for the graph."""
633
- prompt_template = get_prompt_template(
634
- config.get("prompt"),
635
- DEFAULT_COMMAND_PROMPT_TEMPLATE,
636
- )
637
- return deep_container_fingerprint(prompt_template)