rasa-pro 3.13.0.dev2__py3-none-any.whl → 3.13.0.dev5__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 (173) hide show
  1. rasa/__main__.py +3 -1
  2. rasa/cli/inspect.py +8 -4
  3. rasa/cli/project_templates/default/config.yml +5 -32
  4. rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_cancels_during_a_correction.yml +1 -1
  5. rasa/cli/project_templates/{calm → default}/e2e_tests/cancelations/user_changes_mind_on_a_whim.yml +1 -1
  6. rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_handle.yml +1 -1
  7. rasa/cli/project_templates/{calm → default}/e2e_tests/corrections/user_corrects_contact_name.yml +1 -1
  8. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_adds_contact_to_their_list.yml +1 -1
  9. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_lists_contacts.yml +1 -1
  10. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact.yml +1 -1
  11. rasa/cli/project_templates/{calm → default}/e2e_tests/happy_paths/user_removes_contact_from_list.yml +1 -1
  12. rasa/cli/project_templates/default/endpoints.yml +18 -2
  13. rasa/cli/run.py +10 -6
  14. rasa/cli/scaffold.py +3 -4
  15. rasa/cli/studio/download.py +1 -1
  16. rasa/cli/studio/upload.py +0 -6
  17. rasa/cli/utils.py +7 -0
  18. rasa/core/channels/channel.py +93 -0
  19. rasa/core/channels/inspector/dist/assets/{arc-c7691751.js → arc-9f75cc3b.js} +1 -1
  20. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-ab99dff7.js → blockDiagram-38ab4fdb-7f34db23.js} +1 -1
  21. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-08c35a6b.js → c4Diagram-3d4e48cf-948bab2c.js} +1 -1
  22. rasa/core/channels/inspector/dist/assets/channel-dfa68278.js +1 -0
  23. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-9e9c71c9.js → classDiagram-70f12bd4-53b0dd0e.js} +1 -1
  24. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-15e7e2bf.js → classDiagram-v2-f2320105-fdf789e7.js} +1 -1
  25. rasa/core/channels/inspector/dist/assets/clone-edb7f119.js +1 -0
  26. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-9c105cb1.js → createText-2e5e7dd3-87c4ece5.js} +1 -1
  27. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-77e89e48.js → edges-e0da2a9e-5a8b0749.js} +1 -1
  28. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-7a011646.js → erDiagram-9861fffd-66da90e2.js} +1 -1
  29. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-b6f105ac.js → flowDb-956e92f1-10044f05.js} +1 -1
  30. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-ce4f18c2.js → flowDiagram-66a62f08-f338f66a.js} +1 -1
  31. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-65e7c670.js +1 -0
  32. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-cb5f6da4.js → flowchart-elk-definition-4a651766-b13140aa.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-e4d19e28.js → ganttDiagram-c361ad54-f2b4a55a.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-727b1c33.js → gitGraphDiagram-72cf32ee-dedc298d.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/{graph-6e2ab9a7.js → graph-4ede11ff.js} +1 -1
  36. rasa/core/channels/inspector/dist/assets/{index-3862675e-84ec700f.js → index-3862675e-65549d37.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{index-098a1a24.js → index-3a23e736.js} +142 -129
  38. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-78dda442.js → infoDiagram-f8f76790-65439671.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-f1cc6dd1.js → journeyDiagram-49397b02-56d03d98.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{layout-d98dcd0c.js → layout-dd48f7f4.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{line-838e3d82.js → line-1569ad2c.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{linear-eae72406.js → linear-48bf4935.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-c96fd84b.js → mindmap-definition-fc14e90a-688504c1.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-c936d4e2.js → pieDiagram-8a3498a8-78b6d7e6.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-b338eb8f.js → quadrantDiagram-120e2f19-048b84b3.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-c6b6c0d5.js → requirementDiagram-deff3bca-dd67f107.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-b9372e19.js → sankeyDiagram-04a897e0-8128436e.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-479e0a3f.js → sequenceDiagram-704730f1-1a0d1461.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-fd26eebc.js → stateDiagram-587899a1-46d388ed.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-3233e0ae.js → stateDiagram-v2-d93cdb3a-ea42951a.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-1fdd392b.js → styles-6aaf32cf-7427ed0c.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-6d7bfa1b.js → styles-9a916d00-ff5e5a16.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-f86aab11.js → styles-c10674c1-7b3680cf.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-e3e49d7a.js → svgDrawCommon-08f97a94-f860f2ad.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-6fe08b4d.js → timeline-definition-85554ec2-2eebf0c8.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-c2e06fd6.js → xychartDiagram-e933f94c-5d7f4e96.js} +1 -1
  57. rasa/core/channels/inspector/dist/index.html +1 -1
  58. rasa/core/channels/inspector/src/App.tsx +3 -2
  59. rasa/core/channels/inspector/src/components/Chat.tsx +23 -2
  60. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +2 -5
  61. rasa/core/channels/inspector/src/helpers/conversation.ts +16 -0
  62. rasa/core/channels/inspector/src/types.ts +1 -1
  63. rasa/core/channels/voice_ready/audiocodes.py +41 -15
  64. rasa/core/channels/voice_ready/jambonz.py +25 -5
  65. rasa/core/channels/voice_ready/jambonz_protocol.py +4 -0
  66. rasa/core/channels/voice_ready/twilio_voice.py +48 -1
  67. rasa/core/channels/voice_stream/tts/azure.py +11 -2
  68. rasa/core/channels/voice_stream/twilio_media_streams.py +101 -26
  69. rasa/core/channels/voice_stream/voice_channel.py +28 -2
  70. rasa/core/concurrent_lock_store.py +24 -10
  71. rasa/core/information_retrieval/faiss.py +7 -68
  72. rasa/core/information_retrieval/information_retrieval.py +2 -40
  73. rasa/core/information_retrieval/milvus.py +2 -7
  74. rasa/core/information_retrieval/qdrant.py +2 -7
  75. rasa/core/lock_store.py +151 -60
  76. rasa/core/nlg/contextual_response_rephraser.py +3 -0
  77. rasa/core/policies/enterprise_search_policy.py +310 -61
  78. rasa/core/policies/intentless_policy.py +3 -0
  79. rasa/dialogue_understanding/coexistence/llm_based_router.py +8 -0
  80. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -2
  81. rasa/dialogue_understanding/generator/command_parser.py +1 -1
  82. rasa/dialogue_understanding/generator/flow_retrieval.py +1 -4
  83. rasa/dialogue_understanding/generator/llm_based_command_generator.py +1 -2
  84. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +13 -0
  85. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +1 -1
  86. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -1
  87. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +2 -24
  88. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +22 -17
  89. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +27 -12
  90. rasa/dialogue_understanding_test/du_test_case.py +16 -8
  91. rasa/dialogue_understanding_test/io.py +8 -13
  92. rasa/e2e_test/utils/validation.py +3 -3
  93. rasa/engine/recipes/default_components.py +0 -2
  94. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +3 -0
  95. rasa/plugin.py +0 -3
  96. rasa/shared/constants.py +1 -0
  97. rasa/shared/core/domain.py +165 -11
  98. rasa/shared/core/flows/flow.py +155 -131
  99. rasa/shared/core/flows/flow_step.py +19 -3
  100. rasa/shared/core/flows/flow_step_links.py +15 -0
  101. rasa/shared/core/flows/flow_step_sequence.py +6 -0
  102. rasa/shared/core/flows/nlu_trigger.py +13 -0
  103. rasa/shared/core/flows/steps/action.py +7 -4
  104. rasa/shared/core/flows/steps/call.py +11 -4
  105. rasa/shared/core/flows/steps/collect.py +27 -6
  106. rasa/shared/core/flows/steps/internal.py +6 -1
  107. rasa/shared/core/flows/steps/link.py +7 -4
  108. rasa/shared/core/flows/steps/no_operation.py +7 -4
  109. rasa/shared/core/flows/steps/set_slots.py +8 -4
  110. rasa/shared/core/flows/yaml_flows_io.py +106 -5
  111. rasa/shared/importers/importer.py +8 -0
  112. rasa/shared/providers/_utils.py +83 -0
  113. rasa/shared/providers/llm/_base_litellm_client.py +6 -3
  114. rasa/shared/providers/llm/azure_openai_llm_client.py +6 -68
  115. rasa/shared/providers/router/_base_litellm_router_client.py +53 -1
  116. rasa/shared/utils/common.py +42 -0
  117. rasa/shared/utils/constants.py +3 -0
  118. rasa/shared/utils/llm.py +70 -24
  119. rasa/studio/download/domains.py +49 -0
  120. rasa/studio/download/download.py +439 -0
  121. rasa/studio/download/flows.py +359 -0
  122. rasa/studio/results_logger.py +6 -1
  123. rasa/studio/upload.py +69 -5
  124. rasa/tracing/instrumentation/attribute_extractors.py +7 -10
  125. rasa/tracing/instrumentation/instrumentation.py +12 -12
  126. rasa/utils/common.py +36 -0
  127. rasa/utils/endpoints.py +22 -1
  128. rasa/utils/licensing.py +1 -1
  129. rasa/validator.py +1 -2
  130. rasa/version.py +1 -1
  131. {rasa_pro-3.13.0.dev2.dist-info → rasa_pro-3.13.0.dev5.dist-info}/METADATA +7 -7
  132. {rasa_pro-3.13.0.dev2.dist-info → rasa_pro-3.13.0.dev5.dist-info}/RECORD +149 -166
  133. rasa/cli/project_templates/calm/config.yml +0 -10
  134. rasa/cli/project_templates/calm/credentials.yml +0 -33
  135. rasa/cli/project_templates/calm/endpoints.yml +0 -58
  136. rasa/cli/project_templates/default/actions/actions.py +0 -27
  137. rasa/cli/project_templates/default/data/nlu.yml +0 -91
  138. rasa/cli/project_templates/default/data/rules.yml +0 -13
  139. rasa/cli/project_templates/default/data/stories.yml +0 -30
  140. rasa/cli/project_templates/default/domain.yml +0 -34
  141. rasa/cli/project_templates/default/tests/test_stories.yml +0 -91
  142. rasa/core/channels/inspector/dist/assets/channel-11268142.js +0 -1
  143. rasa/core/channels/inspector/dist/assets/clone-ff7f2ce7.js +0 -1
  144. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-cba7ae20.js +0 -1
  145. rasa/document_retrieval/__init__.py +0 -0
  146. rasa/document_retrieval/constants.py +0 -32
  147. rasa/document_retrieval/document_post_processor.py +0 -351
  148. rasa/document_retrieval/document_post_processor_prompt_template.jinja2 +0 -0
  149. rasa/document_retrieval/document_retriever.py +0 -333
  150. rasa/document_retrieval/knowledge_base_connectors/__init__.py +0 -0
  151. rasa/document_retrieval/knowledge_base_connectors/api_connector.py +0 -39
  152. rasa/document_retrieval/knowledge_base_connectors/knowledge_base_connector.py +0 -34
  153. rasa/document_retrieval/knowledge_base_connectors/vector_store_connector.py +0 -226
  154. rasa/document_retrieval/query_rewriter.py +0 -234
  155. rasa/document_retrieval/query_rewriter_prompt_template.jinja2 +0 -8
  156. rasa/studio/download.py +0 -489
  157. /rasa/cli/project_templates/{calm → default}/actions/action_template.py +0 -0
  158. /rasa/cli/project_templates/{calm → default}/actions/add_contact.py +0 -0
  159. /rasa/cli/project_templates/{calm → default}/actions/db.py +0 -0
  160. /rasa/cli/project_templates/{calm → default}/actions/list_contacts.py +0 -0
  161. /rasa/cli/project_templates/{calm → default}/actions/remove_contact.py +0 -0
  162. /rasa/cli/project_templates/{calm → default}/data/flows/add_contact.yml +0 -0
  163. /rasa/cli/project_templates/{calm → default}/data/flows/list_contacts.yml +0 -0
  164. /rasa/cli/project_templates/{calm → default}/data/flows/remove_contact.yml +0 -0
  165. /rasa/cli/project_templates/{calm → default}/db/contacts.json +0 -0
  166. /rasa/cli/project_templates/{calm → default}/domain/add_contact.yml +0 -0
  167. /rasa/cli/project_templates/{calm → default}/domain/list_contacts.yml +0 -0
  168. /rasa/cli/project_templates/{calm → default}/domain/remove_contact.yml +0 -0
  169. /rasa/cli/project_templates/{calm → default}/domain/shared.yml +0 -0
  170. /rasa/{cli/project_templates/calm/actions → studio/download}/__init__.py +0 -0
  171. {rasa_pro-3.13.0.dev2.dist-info → rasa_pro-3.13.0.dev5.dist-info}/NOTICE +0 -0
  172. {rasa_pro-3.13.0.dev2.dist-info → rasa_pro-3.13.0.dev5.dist-info}/WHEEL +0 -0
  173. {rasa_pro-3.13.0.dev2.dist-info → rasa_pro-3.13.0.dev5.dist-info}/entry_points.txt +0 -0
@@ -52,6 +52,10 @@ from rasa.shared.exceptions import ProviderClientAPIException
52
52
  from rasa.shared.nlu.constants import TEXT
53
53
  from rasa.shared.nlu.training_data.message import Message
54
54
  from rasa.shared.providers.llm.llm_response import LLMResponse
55
+ from rasa.shared.utils.constants import (
56
+ LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON,
57
+ LOG_COMPONENT_SOURCE_METHOD_INIT,
58
+ )
55
59
  from rasa.shared.utils.io import deep_container_fingerprint, raise_deprecation_warning
56
60
  from rasa.shared.utils.llm import (
57
61
  allowed_values_for_slot,
@@ -330,6 +334,8 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
330
334
  return get_prompt_template(
331
335
  config.get("prompt_templates", {}).get(key, {}).get(FILE_PATH_KEY),
332
336
  default_value,
337
+ log_source_component=MultiStepLLMCommandGenerator.__name__,
338
+ log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
333
339
  )
334
340
 
335
341
  @classmethod
@@ -786,17 +792,24 @@ class MultiStepLLMCommandGenerator(LLMBasedCommandGenerator):
786
792
  @classmethod
787
793
  def fingerprint_addon(cls, config: Dict[str, Any]) -> Optional[str]:
788
794
  """Add a fingerprint for the graph."""
795
+ get_prompt_template_log_params = {
796
+ "log_source_component": MultiStepLLMCommandGenerator.__name__,
797
+ "log_source_method": LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON,
798
+ }
799
+
789
800
  handle_flows_template = get_prompt_template(
790
801
  config.get("prompt_templates", {})
791
802
  .get(HANDLE_FLOWS_KEY, {})
792
803
  .get(FILE_PATH_KEY),
793
804
  DEFAULT_HANDLE_FLOWS_TEMPLATE,
805
+ **get_prompt_template_log_params,
794
806
  )
795
807
  fill_slots_template = get_prompt_template(
796
808
  config.get("prompt_templates", {})
797
809
  .get(FILL_SLOTS_KEY, {})
798
810
  .get(FILE_PATH_KEY),
799
811
  DEFAULT_FILL_SLOTS_TEMPLATE,
812
+ **get_prompt_template_log_params,
800
813
  )
801
814
 
802
815
  llm_config = resolve_model_client_config(
@@ -57,4 +57,4 @@ Strictly adhere to the provided action types listed above.
57
57
  Focus on the last message and take it one step at a time.
58
58
  Use the previous conversation steps only to aid understanding.
59
59
 
60
- Your action list:
60
+ Your action list:
@@ -8,7 +8,7 @@ Your task is to analyze the current conversation context and generate a list of
8
8
  * `set slot slot_name slot_value`: Slot setting. For example, `set slot transfer_money_recipient Freddy`. Can be used to correct and change previously set values.
9
9
  * `cancel flow`: Cancelling the current flow.
10
10
  * `disambiguate flows flow_name1 flow_name2 ... flow_name_n`: Disambiguate which flow should be started when user input is ambiguous by listing the potential flows as options. For example, `disambiguate flows list_contacts add_contact remove_contact ...` if the user just wrote "contacts".
11
- * `search and reply`: Responding to the user's questions by supplying relevant information, such as answering FAQs or explaining services.
11
+ * `provide info`: Responding to the user's questions by supplying relevant information, such as answering FAQs or explaining services.
12
12
  * `offtopic reply`: Responding to casual or social user messages that are unrelated to any flows, engaging in friendly conversation and addressing off-topic remarks.
13
13
  * `hand over`: Handing over to a human, in case the user seems frustrated or explicitly asks to speak to one.
14
14
 
@@ -16,9 +16,7 @@ Use the following structured data:
16
16
  * `set slot slot_name slot_value`: Slot setting. For example, `set slot transfer_money_recipient Freddy`. Can be used to correct and change previously set values.
17
17
  * `cancel flow`: Cancelling the current flow.
18
18
  * `disambiguate flows flow_name1 flow_name2 ... flow_name_n`: Disambiguate which flow should be started when user input is ambiguous by listing the potential flows as options. For example, `disambiguate flows list_contacts add_contact remove_contact ...` if the user just wrote "contacts".
19
- {%- if relevant_documents.results %}
20
- * `search and reply`: Responding to the user's message by using the relevant FAQs (included in this prompt) retrieved from the knowledge base.
21
- {%- endif %}
19
+ * `provide info`: Responding to the user's questions by supplying relevant information, such as answering FAQs or explaining services.
22
20
  * `offtopic reply`: Responding to casual or social user messages that are unrelated to any flows, engaging in friendly conversation and addressing off-topic remarks.
23
21
  * `hand over`: Handing over to a human, in case the user seems frustrated or explicitly asks to speak to one.
24
22
 
@@ -29,36 +27,16 @@ Use the following structured data:
29
27
  * For categorical slots try to match the user message with allowed slot values. Use "other" if you cannot match it.
30
28
  * Set the boolean slots based on the user response. Map positive responses to `True`, and negative to `False`.
31
29
  * Extract text slot values exactly as provided by the user. Avoid assumptions, format changes, or partial extractions.
32
- * Use clarification in ambiguous cases.
33
- * Use `disambiguate flows` only when multiple flows could fit the same message (e.g., "card" could mean `block_card` or `replace_card`).
34
- {%- if relevant_documents.results %}
35
- * A user asking a question does not automatically imply that they want `search and reply`. The objective is to help them complete a business process if its possible to do so via a flow.
36
- * **Flow Priority**: If a user message can be addressed by starting a flow (even if it looks like a general question), ALWAYS start the flow first. Example: If the user says "How do I activate my card?", use `start flow activate_card` instead of `search and reply`. Only use `search and reply` if no flow matches the request.
37
- {%- endif %}
38
30
  * Only use information provided by the user.
31
+ * Use clarification in ambiguous cases.
39
32
  * Multiple flows can be started. If a user wants to digress into a second flow, you do not need to cancel the current flow.
40
33
  * Do not cancel the flow unless the user explicitly requests it.
41
34
  * Strictly adhere to the provided action format.
42
35
  * Focus on the last message and take it one step at a time.
43
36
  * Use the previous conversation steps only to aid understanding.
44
37
 
45
- {%- if relevant_documents.results %}
46
-
47
- ---
48
-
49
- ## Relevant FAQs from the knowledge base
50
- ```json
51
- {"documents":[{% for document in relevant_documents.results %}{"Q":"{{ document.text }}","A":"{{ document.metadata.answer }}"},{% endfor %}]}
52
- ```
53
-
54
- ---
55
-
56
- {% else %}
57
-
58
38
  ---
59
39
 
60
- {% endif -%}
61
-
62
40
  ## Current State
63
41
  {% if current_flow != None %}Use the following structured data:
64
42
  ```json
@@ -1,10 +1,9 @@
1
1
  import copy
2
- from typing import Any, Dict, List, Optional, Text
2
+ from typing import Any, Dict, List, Literal, Optional, Text
3
3
 
4
4
  import structlog
5
5
 
6
6
  import rasa.shared.utils.io
7
- from rasa.core.information_retrieval import SearchResultList
8
7
  from rasa.dialogue_understanding.commands import (
9
8
  CannotHandleCommand,
10
9
  Command,
@@ -39,7 +38,6 @@ from rasa.dialogue_understanding.utils import (
39
38
  add_commands_to_message_parse_data,
40
39
  add_prompt_to_message_parse_data,
41
40
  )
42
- from rasa.document_retrieval.constants import POST_PROCESSED_DOCUMENTS_KEY
43
41
  from rasa.engine.graph import ExecutionContext
44
42
  from rasa.engine.recipes.default_recipe import DefaultV1Recipe
45
43
  from rasa.engine.storage.resource import Resource
@@ -60,6 +58,10 @@ from rasa.shared.exceptions import ProviderClientAPIException
60
58
  from rasa.shared.nlu.constants import LLM_COMMANDS, LLM_PROMPT, TEXT
61
59
  from rasa.shared.nlu.training_data.message import Message
62
60
  from rasa.shared.providers.llm.llm_response import LLMResponse
61
+ from rasa.shared.utils.constants import (
62
+ LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON,
63
+ LOG_COMPONENT_SOURCE_METHOD_INIT,
64
+ )
63
65
  from rasa.shared.utils.io import deep_container_fingerprint
64
66
  from rasa.shared.utils.llm import (
65
67
  allowed_values_for_slot,
@@ -189,8 +191,8 @@ class CompactLLMCommandGenerator(LLMBasedCommandGenerator):
189
191
  )
190
192
 
191
193
  # Get the prompt template from the config or the default prompt template.
192
- self.prompt_template = self.resolve_component_prompt_template(
193
- self.config, prompt_template
194
+ self.prompt_template = self._resolve_component_prompt_template(
195
+ self.config, prompt_template, log_context=LOG_COMPONENT_SOURCE_METHOD_INIT
194
196
  )
195
197
 
196
198
  # Set the command syntax version to v2
@@ -495,10 +497,6 @@ class CompactLLMCommandGenerator(LLMBasedCommandGenerator):
495
497
  latest_user_message = sanitize_message_for_prompt(message.get(TEXT))
496
498
  current_conversation += f"\nUSER: {latest_user_message}"
497
499
 
498
- relevant_documents = SearchResultList.from_dict(
499
- message.get(POST_PROCESSED_DOCUMENTS_KEY, [])
500
- )
501
-
502
500
  inputs = {
503
501
  "available_flows": self.prepare_flows_for_template(
504
502
  startable_flows, tracker
@@ -512,7 +510,6 @@ class CompactLLMCommandGenerator(LLMBasedCommandGenerator):
512
510
  "current_slot_allowed_values": current_slot_allowed_values,
513
511
  "user_message": latest_user_message,
514
512
  "is_repeat_command_enabled": self.repeat_command_enabled,
515
- "relevant_documents": relevant_documents,
516
513
  }
517
514
 
518
515
  return self.compile_template(self.prompt_template).render(**inputs)
@@ -546,7 +543,9 @@ class CompactLLMCommandGenerator(LLMBasedCommandGenerator):
546
543
  # and update the llm config with the resolved llm config.
547
544
  _config_copy = copy.deepcopy(config)
548
545
  _config_copy[LLM_CONFIG_KEY] = llm_config
549
- prompt_template = cls.resolve_component_prompt_template(_config_copy)
546
+ prompt_template = cls._resolve_component_prompt_template(
547
+ _config_copy, log_context=LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON
548
+ )
550
549
 
551
550
  return deep_container_fingerprint(
552
551
  [prompt_template, llm_config, embedding_config]
@@ -562,20 +561,26 @@ class CompactLLMCommandGenerator(LLMBasedCommandGenerator):
562
561
  return CommandSyntaxVersion.v2
563
562
 
564
563
  @staticmethod
565
- def resolve_component_prompt_template(
566
- config: Dict[str, Any], prompt_template: Optional[str] = None
564
+ def _resolve_component_prompt_template(
565
+ config: Dict[str, Any],
566
+ prompt_template: Optional[str] = None,
567
+ log_context: Optional[Literal["init", "fingerprint_addon"]] = None,
567
568
  ) -> Optional[str]:
568
569
  """Get the prompt template from the config or the default prompt template."""
569
570
  # Get the default prompt template based on the model name.
570
571
  default_command_prompt_template = get_default_prompt_template_based_on_model(
571
- config.get(LLM_CONFIG_KEY, {}) or {},
572
- MODEL_PROMPT_MAPPER,
573
- DEFAULT_COMMAND_PROMPT_TEMPLATE_FILE_NAME,
574
- FALLBACK_COMMAND_PROMPT_TEMPLATE_FILE_NAME,
572
+ llm_config=config.get(LLM_CONFIG_KEY, {}) or {},
573
+ model_prompt_mapping=MODEL_PROMPT_MAPPER,
574
+ default_prompt_path=DEFAULT_COMMAND_PROMPT_TEMPLATE_FILE_NAME,
575
+ fallback_prompt_path=FALLBACK_COMMAND_PROMPT_TEMPLATE_FILE_NAME,
576
+ log_source_component=CompactLLMCommandGenerator.__name__,
577
+ log_source_method=log_context,
575
578
  )
576
579
 
577
580
  # Return the prompt template either from the config or the default prompt.
578
581
  return prompt_template or get_prompt_template(
579
582
  config.get(PROMPT_TEMPLATE_CONFIG_KEY),
580
583
  default_command_prompt_template,
584
+ log_source_component=CompactLLMCommandGenerator.__name__,
585
+ log_source_method=log_context,
581
586
  )
@@ -1,5 +1,5 @@
1
1
  import importlib.resources
2
- from typing import Any, Dict, Optional, Text
2
+ from typing import Any, Dict, Literal, Optional, Text
3
3
 
4
4
  import structlog
5
5
 
@@ -25,8 +25,12 @@ from rasa.shared.constants import (
25
25
  PROMPT_CONFIG_KEY,
26
26
  PROMPT_TEMPLATE_CONFIG_KEY,
27
27
  )
28
+ from rasa.shared.utils.constants import LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON
28
29
  from rasa.shared.utils.io import deep_container_fingerprint
29
- from rasa.shared.utils.llm import get_prompt_template, resolve_model_client_config
30
+ from rasa.shared.utils.llm import (
31
+ get_prompt_template,
32
+ resolve_model_client_config,
33
+ )
30
34
 
31
35
  DEFAULT_COMMAND_PROMPT_TEMPLATE = importlib.resources.read_text(
32
36
  "rasa.dialogue_understanding.generator.prompt_templates",
@@ -72,9 +76,6 @@ class SingleStepLLMCommandGenerator(CompactLLMCommandGenerator):
72
76
  "Please use the config parameter 'prompt_template' instead. "
73
77
  ),
74
78
  )
75
- self.prompt_template = self.resolve_component_prompt_template(
76
- config, prompt_template
77
- )
78
79
 
79
80
  # Set the command syntax version to v1
80
81
  CommandSyntaxManager.set_syntax_version(
@@ -95,7 +96,9 @@ class SingleStepLLMCommandGenerator(CompactLLMCommandGenerator):
95
96
  @classmethod
96
97
  def fingerprint_addon(cls: Any, config: Dict[str, Any]) -> Optional[str]:
97
98
  """Add a fingerprint for the graph."""
98
- prompt_template = cls.resolve_component_prompt_template(config)
99
+ prompt_template = cls._resolve_component_prompt_template(
100
+ config, log_context=LOG_COMPONENT_SOURCE_METHOD_FINGERPRINT_ADDON
101
+ )
99
102
  llm_config = resolve_model_client_config(
100
103
  config.get(LLM_CONFIG_KEY), SingleStepLLMCommandGenerator.__name__
101
104
  )
@@ -117,17 +120,29 @@ class SingleStepLLMCommandGenerator(CompactLLMCommandGenerator):
117
120
  return CommandSyntaxVersion.v1
118
121
 
119
122
  @staticmethod
120
- def resolve_component_prompt_template(
121
- config: Dict[str, Any], prompt_template: Optional[str] = None
123
+ def _resolve_component_prompt_template(
124
+ config: Dict[str, Any],
125
+ prompt_template: Optional[str] = None,
126
+ log_context: Optional[Literal["init", "fingerprint_addon"]] = None,
122
127
  ) -> Optional[str]:
123
128
  """Get the prompt template from the config or the default prompt template."""
124
- # Get the default prompt template based on the model name.
125
- config_prompt = (
129
+ # Case when model is being loaded
130
+ if prompt_template is not None:
131
+ return prompt_template
132
+
133
+ # The prompt can be configured in the config via the "prompt" (deprecated) or
134
+ # "prompt_template" properties
135
+ prompt_template_path = (
126
136
  config.get(PROMPT_CONFIG_KEY)
127
137
  or config.get(PROMPT_TEMPLATE_CONFIG_KEY)
128
138
  or None
129
139
  )
130
- return prompt_template or get_prompt_template(
131
- config_prompt,
140
+
141
+ # Try to load the template from the given path or fallback to the default for
142
+ # the component
143
+ return get_prompt_template(
144
+ prompt_template_path,
132
145
  DEFAULT_COMMAND_PROMPT_TEMPLATE,
146
+ log_source_component=SingleStepLLMCommandGenerator.__name__,
147
+ log_source_method=log_context,
133
148
  )
@@ -261,19 +261,27 @@ class DialogueUnderstandingTestStep(BaseModel):
261
261
  # Safely extract commands from the step.
262
262
  commands = []
263
263
  for command in step.get(KEY_COMMANDS, []):
264
+ parsed_commands = None
264
265
  try:
265
- commands.extend(
266
- parse_commands(
267
- command,
268
- flows,
269
- clarify_options_optional=True,
270
- additional_commands=custom_command_classes,
271
- default_commands_to_remove=remove_default_commands,
272
- )
266
+ parsed_commands = parse_commands(
267
+ command,
268
+ flows,
269
+ clarify_options_optional=True,
270
+ additional_commands=custom_command_classes,
271
+ default_commands_to_remove=remove_default_commands,
273
272
  )
274
273
  except (IndexError, ValueError) as e:
275
274
  raise ValueError(f"Failed to parse command '{command}': {e}") from e
276
275
 
276
+ if not parsed_commands:
277
+ raise ValueError(
278
+ f"Failed to parse command '{command}': command parser returned "
279
+ f"None. Please make sure that you are using the correct command "
280
+ f"syntax and the command arguments are valid."
281
+ )
282
+
283
+ commands.extend(parsed_commands)
284
+
277
285
  # Construct the DialogueUnderstandingTestStep
278
286
  return DialogueUnderstandingTestStep(
279
287
  actor=ACTOR_USER if ACTOR_USER in step else ACTOR_BOT,
@@ -329,19 +329,14 @@ def print_prompt(step: FailedTestStep) -> None:
329
329
  rich.print(
330
330
  f"[bold] prompt name [/bold]: {prompt_data[KEY_PROMPT_NAME]}"
331
331
  )
332
- if KEY_PROMPT_TOKENS in prompt_data:
333
- rich.print(
334
- f"[bold] prompt tokens [/bold]: {prompt_data[KEY_PROMPT_TOKENS]}" # noqa: E501
335
- )
336
- if KEY_COMPLETION_TOKENS in prompt_data:
337
- rich.print(
338
- f"[bold] completion tokens[/bold]: "
339
- f"{prompt_data[KEY_COMPLETION_TOKENS]}"
340
- )
341
- if KEY_LATENCY in prompt_data:
342
- rich.print(
343
- f"[bold] latency [/bold]: {prompt_data[KEY_LATENCY]}"
344
- )
332
+ rich.print(
333
+ f"[bold] prompt tokens [/bold]: {prompt_data[KEY_PROMPT_TOKENS]}"
334
+ )
335
+ rich.print(
336
+ f"[bold] completion tokens[/bold]: "
337
+ f"{prompt_data[KEY_COMPLETION_TOKENS]}"
338
+ )
339
+ rich.print(f"[bold] latency [/bold]: {prompt_data[KEY_LATENCY]}")
345
340
  if KEY_SYSTEM_PROMPT in prompt_data:
346
341
  rich.print(
347
342
  f"[bold] system prompt [/bold]: "
@@ -7,6 +7,7 @@ import structlog
7
7
  import rasa.shared.utils.io
8
8
  from rasa.e2e_test.constants import SCHEMA_FILE_PATH
9
9
  from rasa.e2e_test.e2e_test_case import Fixture, Metadata
10
+ from rasa.exceptions import ModelNotFound
10
11
  from rasa.shared.utils.yaml import read_schema_file
11
12
 
12
13
  if TYPE_CHECKING:
@@ -152,10 +153,9 @@ def validate_model_path(model_path: Optional[str], parameter: str, default: str)
152
153
  return model_path
153
154
 
154
155
  if model_path and not Path(model_path).exists():
155
- rasa.shared.utils.io.raise_warning(
156
+ raise ModelNotFound(
156
157
  f"The provided model path '{model_path}' could not be found. "
157
- f"Using default location '{default}' instead.",
158
- UserWarning,
158
+ "Provide an existing model path."
159
159
  )
160
160
 
161
161
  elif model_path is None:
@@ -13,7 +13,6 @@ from rasa.dialogue_understanding.generator import (
13
13
  LLMCommandGenerator,
14
14
  )
15
15
  from rasa.dialogue_understanding.generator.nlu_command_adapter import NLUCommandAdapter
16
- from rasa.document_retrieval.document_retriever import DocumentRetriever
17
16
  from rasa.nlu.classifiers.diet_classifier import DIETClassifier
18
17
  from rasa.nlu.classifiers.fallback_classifier import FallbackClassifier
19
18
  from rasa.nlu.classifiers.keyword_intent_classifier import KeywordIntentClassifier
@@ -93,5 +92,4 @@ DEFAULT_COMPONENTS = [
93
92
  FlowPolicy,
94
93
  EnterpriseSearchPolicy,
95
94
  IntentlessPolicy,
96
- DocumentRetriever,
97
95
  ]
@@ -19,6 +19,7 @@ from rasa.shared.constants import (
19
19
  )
20
20
  from rasa.shared.exceptions import ProviderClientAPIException
21
21
  from rasa.shared.providers.mappings import OPENAI_PROVIDER
22
+ from rasa.shared.utils.constants import LOG_COMPONENT_SOURCE_METHOD_INIT
22
23
  from rasa.shared.utils.llm import (
23
24
  USER,
24
25
  get_prompt_template,
@@ -54,6 +55,8 @@ class ConversationRephraser:
54
55
  self.prompt_template = get_prompt_template(
55
56
  self.config.get(PROMPT_TEMPLATE_CONFIG_KEY),
56
57
  DEFAULT_REPHRASING_PROMPT_TEMPLATE,
58
+ log_source_component=ConversationRephraser.__name__,
59
+ log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
57
60
  )
58
61
 
59
62
  @staticmethod
rasa/plugin.py CHANGED
@@ -32,11 +32,8 @@ def plugin_manager() -> pluggy.PluginManager:
32
32
 
33
33
  def init_hooks(manager: pluggy.PluginManager) -> None:
34
34
  """Initialise hooks into rasa."""
35
- import rasa.utils.licensing
36
35
  from rasa import hooks
37
36
 
38
- rasa.utils.licensing.validate_license_from_env()
39
-
40
37
  manager.register(hooks)
41
38
 
42
39
 
rasa/shared/constants.py CHANGED
@@ -238,6 +238,7 @@ EXTRA_PARAMETERS_KEY = "extra_parameters"
238
238
  MODEL_GROUP_ID_KEY = "model_group_id"
239
239
  MODEL_LIST_KEY = "model_list"
240
240
  LITELLM_PARAMS_KEY = "litellm_params"
241
+ _VALIDATE_ENVIRONMENT_MISSING_KEYS_KEY = "missing_keys"
241
242
 
242
243
  LLM_API_HEALTH_CHECK_ENV_VAR = "LLM_API_HEALTH_CHECK"
243
244
  LLM_API_HEALTH_CHECK_DEFAULT_VALUE = "false"
@@ -134,6 +134,22 @@ ALL_DOMAIN_KEYS = [
134
134
 
135
135
  PREV_PREFIX = "prev_"
136
136
 
137
+ MERGE_FUNC_MAPPING: Dict[Text, Callable[..., Any]] = {
138
+ KEY_ACTIONS: rasa.shared.utils.common.merge_lists_of_dicts,
139
+ KEY_RESPONSES: rasa.shared.utils.common.merge_dicts,
140
+ KEY_SLOTS: rasa.shared.utils.common.merge_dicts,
141
+ KEY_INTENTS: rasa.shared.utils.common.merge_lists_of_dicts,
142
+ KEY_ENTITIES: rasa.shared.utils.common.merge_lists_of_dicts,
143
+ KEY_E2E_ACTIONS: rasa.shared.utils.common.merge_lists,
144
+ KEY_FORMS: rasa.shared.utils.common.merge_dicts,
145
+ }
146
+
147
+ DICT_DATA_KEYS = [
148
+ key
149
+ for key, value in MERGE_FUNC_MAPPING.items()
150
+ if value == rasa.shared.utils.common.merge_dicts
151
+ ]
152
+
137
153
  # State is a dictionary with keys (USER, PREVIOUS_ACTION, SLOTS, ACTIVE_LOOP)
138
154
  # representing the origin of a SubState;
139
155
  # the values are SubStates, that contain the information needed for featurization
@@ -466,17 +482,7 @@ class Domain:
466
482
 
467
483
  duplicates: Dict[Text, List[Text]] = {}
468
484
 
469
- merge_func_mappings: Dict[Text, Callable[..., Any]] = {
470
- KEY_INTENTS: rasa.shared.utils.common.merge_lists_of_dicts,
471
- KEY_ENTITIES: rasa.shared.utils.common.merge_lists_of_dicts,
472
- KEY_ACTIONS: rasa.shared.utils.common.merge_lists_of_dicts,
473
- KEY_E2E_ACTIONS: rasa.shared.utils.common.merge_lists,
474
- KEY_FORMS: rasa.shared.utils.common.merge_dicts,
475
- KEY_RESPONSES: rasa.shared.utils.common.merge_dicts,
476
- KEY_SLOTS: rasa.shared.utils.common.merge_dicts,
477
- }
478
-
479
- for key, merge_func in merge_func_mappings.items():
485
+ for key, merge_func in MERGE_FUNC_MAPPING.items():
480
486
  duplicates[key] = rasa.shared.utils.common.extract_duplicates(
481
487
  combined.get(key, []), domain_dict.get(key, [])
482
488
  )
@@ -494,6 +500,74 @@ class Domain:
494
500
 
495
501
  return combined
496
502
 
503
+ def partial_merge(self, other: Domain) -> Domain:
504
+ """
505
+ Returns a new Domain with intersection-based merging:
506
+ - For each domain section only overwrite items that already exist in self.
507
+ - Brand-new items in `other` are ignored.
508
+
509
+ Args:
510
+ other: The domain to merge with.
511
+
512
+ Returns:
513
+ A new Domain object with the merged content.
514
+ """
515
+ updated_self = copy.deepcopy(self.as_dict())
516
+ other_dict = other.as_dict()
517
+
518
+ keys_to_merge = MERGE_FUNC_MAPPING.keys()
519
+ for key in keys_to_merge:
520
+ if key in DICT_DATA_KEYS:
521
+ # Merge dictionaries
522
+ self_val = updated_self.get(key, {})
523
+ other_val = other_dict.get(key, {})
524
+ updated_self[key] = rasa.shared.utils.common.partial_merge_dict(
525
+ self_val, other_val
526
+ )
527
+ else:
528
+ # Merge lists
529
+ self_val = updated_self.get(key, [])
530
+ other_val = other_dict.get(key, [])
531
+ is_same_item_fn = SAME_ITEM_FUNCTIONS.get(key, default_is_same_item)
532
+ updated_self[key] = rasa.shared.utils.common.partial_merge_list(
533
+ self_val, other_val, is_same_item_fn
534
+ )
535
+
536
+ return Domain.from_dict(updated_self)
537
+
538
+ def difference(self, other: Domain) -> Domain:
539
+ """
540
+ Returns a new Domain containing items in `self` that are NOT in `other`,
541
+ using simple equality checks for dict/list items.
542
+
543
+ Args:
544
+ other: The domain to compare with.
545
+
546
+ Returns:
547
+ A new Domain object with the difference content.
548
+ """
549
+ self_dict = self.as_dict()
550
+ other_dict = other.as_dict()
551
+
552
+ difference_dict = {}
553
+ for key in MERGE_FUNC_MAPPING.keys():
554
+ is_dict = key in DICT_DATA_KEYS
555
+ self_val = self_dict.get(key, {} if is_dict else [])
556
+ other_val = other_dict.get(key, {} if is_dict else [])
557
+
558
+ if is_dict and isinstance(self_val, dict) and isinstance(other_val, dict):
559
+ difference_dict[key] = {
560
+ k: v
561
+ for k, v in self_val.items()
562
+ if k not in other_val or v != other_val[k]
563
+ }
564
+ else:
565
+ difference_dict[key] = [
566
+ item for item in self_val if item not in other_val
567
+ ] # type: ignore[assignment]
568
+
569
+ return Domain.from_dict(difference_dict)
570
+
497
571
  def _preprocess_domain_dict(
498
572
  self,
499
573
  data: Dict,
@@ -2120,6 +2194,11 @@ class Domain:
2120
2194
  """Remove all builtin slots from the domain."""
2121
2195
  self.slots = [slot for slot in self.slots if not slot.is_builtin]
2122
2196
 
2197
+ def __eq__(self, other: object) -> bool:
2198
+ if isinstance(other, Domain):
2199
+ return self.as_dict() == other.as_dict()
2200
+ return False
2201
+
2123
2202
 
2124
2203
  def warn_about_duplicates_found_during_domain_merging(
2125
2204
  duplicates: Dict[Text, List[Text]],
@@ -2178,3 +2257,78 @@ def _validate_forms(forms: Union[Dict, List]) -> None:
2178
2257
  f"the keyword `{REQUIRED_SLOTS_KEY}` is required. "
2179
2258
  f"Please see {DOCS_URL_FORMS} for more information."
2180
2259
  )
2260
+
2261
+
2262
+ def is_same_entity(e1: Any, e2: Any) -> bool:
2263
+ """Check if two entities are the 'same' (string or dict).
2264
+
2265
+ Args:
2266
+ e1: First entity to compare.
2267
+ e2: Second entity to compare.
2268
+
2269
+ Returns:
2270
+ True if the entities are the same, False otherwise.
2271
+ """
2272
+ if isinstance(e1, str) and isinstance(e2, str):
2273
+ return e1 == e2
2274
+
2275
+ if isinstance(e1, dict) and isinstance(e2, dict):
2276
+ return (
2277
+ e1.get(ENTITY_ATTRIBUTE_TYPE) == e2.get(ENTITY_ATTRIBUTE_TYPE)
2278
+ and e1.get(ENTITY_ATTRIBUTE_ROLE) == e2.get(ENTITY_ATTRIBUTE_ROLE)
2279
+ and e1.get(ENTITY_ATTRIBUTE_GROUP) == e2.get(ENTITY_ATTRIBUTE_GROUP)
2280
+ )
2281
+
2282
+ return False
2283
+
2284
+
2285
+ def is_same_intent(i1: Any, i2: Any) -> bool:
2286
+ """Check if two intents are the 'same' (string or dict).
2287
+
2288
+ Args:
2289
+ i1: First intent to compare.
2290
+ i2: Second intent to compare.
2291
+
2292
+ Returns:
2293
+ True if the intents are the same, False otherwise.
2294
+ """
2295
+ if isinstance(i1, str) and isinstance(i2, str):
2296
+ return i1 == i2
2297
+
2298
+ if isinstance(i1, dict) and isinstance(i2, dict):
2299
+ key1, key2 = next(iter(i1.keys())), next(iter((i2.keys())))
2300
+ return key1 == key2
2301
+
2302
+ return False
2303
+
2304
+
2305
+ def is_same_action(a1: Any, a2: Any) -> bool:
2306
+ """Check if two actions are the 'same' (string or dict).
2307
+
2308
+ Args:
2309
+ a1: First action to compare.
2310
+ a2: Second action to compare.
2311
+
2312
+ Returns:
2313
+ True if the actions are the same, False otherwise.
2314
+ """
2315
+ if isinstance(a1, str) and isinstance(a2, str):
2316
+ return a1 == a2
2317
+
2318
+ if isinstance(a1, dict) and isinstance(a2, dict):
2319
+ key1, key2 = next(iter((a1.keys()))), next(iter((a2.keys())))
2320
+ return key1 == key2
2321
+
2322
+ return False
2323
+
2324
+
2325
+ def default_is_same_item(a: Any, b: Any) -> bool:
2326
+ """Fallback exact equality check if a key doesn't need special handling."""
2327
+ return a == b
2328
+
2329
+
2330
+ SAME_ITEM_FUNCTIONS: Dict[Text, Callable[[Any, Any], bool]] = {
2331
+ KEY_ENTITIES: is_same_entity,
2332
+ KEY_INTENTS: is_same_intent,
2333
+ KEY_ACTIONS: is_same_action,
2334
+ }