rasa-pro 3.13.0.dev20250612__py3-none-any.whl → 3.13.0rc1__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 (252) hide show
  1. rasa/__main__.py +0 -3
  2. rasa/api.py +1 -1
  3. rasa/cli/dialogue_understanding_test.py +1 -1
  4. rasa/cli/e2e_test.py +1 -8
  5. rasa/cli/evaluate.py +1 -1
  6. rasa/cli/export.py +3 -1
  7. rasa/cli/llm_fine_tuning.py +12 -11
  8. rasa/cli/project_templates/defaults.py +133 -0
  9. rasa/cli/project_templates/tutorial/config.yml +1 -1
  10. rasa/cli/project_templates/tutorial/endpoints.yml +1 -1
  11. rasa/cli/run.py +1 -1
  12. rasa/cli/studio/download.py +1 -23
  13. rasa/cli/studio/link.py +52 -0
  14. rasa/cli/studio/pull.py +79 -0
  15. rasa/cli/studio/push.py +78 -0
  16. rasa/cli/studio/studio.py +12 -0
  17. rasa/cli/studio/train.py +0 -1
  18. rasa/cli/studio/upload.py +8 -0
  19. rasa/cli/train.py +1 -1
  20. rasa/cli/utils.py +1 -1
  21. rasa/cli/x.py +1 -1
  22. rasa/constants.py +2 -0
  23. rasa/core/__init__.py +0 -16
  24. rasa/core/actions/action.py +5 -1
  25. rasa/core/actions/action_repeat_bot_messages.py +18 -22
  26. rasa/core/actions/action_run_slot_rejections.py +0 -1
  27. rasa/core/agent.py +16 -1
  28. rasa/core/available_endpoints.py +146 -0
  29. rasa/core/brokers/pika.py +1 -2
  30. rasa/core/channels/__init__.py +2 -0
  31. rasa/core/channels/botframework.py +2 -2
  32. rasa/core/channels/channel.py +2 -2
  33. rasa/core/channels/development_inspector.py +1 -1
  34. rasa/core/channels/facebook.py +1 -4
  35. rasa/core/channels/hangouts.py +8 -5
  36. rasa/core/channels/inspector/README.md +3 -3
  37. rasa/core/channels/inspector/dist/assets/{arc-c4b064fc.js → arc-371401b1.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-215b5026.js → blockDiagram-38ab4fdb-3f126156.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-2b54a0a3.js → c4Diagram-3d4e48cf-12f22eb7.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/channel-f1efda17.js +1 -0
  41. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-daacea5f.js → classDiagram-70f12bd4-03b1d386.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-930d4dc2.js → classDiagram-v2-f2320105-84f69d63.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/clone-fdf164e2.js +1 -0
  44. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-83c206ba.js → createText-2e5e7dd3-ca47fd38.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-b0eb01d0.js → edges-e0da2a9e-f837ca8a.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-17586500.js → erDiagram-9861fffd-8717ac54.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-be2a1776.js → flowDb-956e92f1-94f38b83.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-c2120ebd.js → flowDiagram-66a62f08-b616f9fb.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-7d7a1629.js +1 -0
  50. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-a6ab5c48.js → flowchart-elk-definition-4a651766-f5d24bb8.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-ef613457.js → ganttDiagram-c361ad54-b43ba8d9.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-d59185b3.js → gitGraphDiagram-72cf32ee-c3aafaa5.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{graph-0f155405.js → graph-0d0a2c10.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{index-3862675e-d5f1d1b7.js → index-3862675e-58ea0305.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{index-47737d3a.js → index-cce6f8a1.js} +3 -3
  56. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-b07d141f.js → infoDiagram-f8f76790-b8f60461.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-1936d429.js → journeyDiagram-49397b02-95be5545.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{layout-dde8d0f3.js → layout-da885b9b.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/{line-0c2c7ee0.js → line-f1c817d3.js} +1 -1
  60. rasa/core/channels/inspector/dist/assets/{linear-35dd89a4.js → linear-d42801e6.js} +1 -1
  61. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-56192851.js → mindmap-definition-fc14e90a-a38923a6.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-fc21ed78.js → pieDiagram-8a3498a8-ca6e71e9.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-25e98518.js → quadrantDiagram-120e2f19-b290dae9.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-546ff1f5.js → requirementDiagram-deff3bca-03f02ceb.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-02d8b82d.js → sankeyDiagram-04a897e0-c49eee40.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3ca5a92e.js → sequenceDiagram-704730f1-b2cd6a3d.js} +1 -1
  67. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-128ea07c.js → stateDiagram-587899a1-e53a2028.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-95f290af.js → stateDiagram-v2-d93cdb3a-e1982a03.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-4984898a.js → styles-6aaf32cf-d0226ca5.js} +1 -1
  70. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1bf266ba.js → styles-9a916d00-0e21dc00.js} +1 -1
  71. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-60521c63.js → styles-c10674c1-9588494e.js} +1 -1
  72. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-a25b6e12.js → svgDrawCommon-08f97a94-be478d4f.js} +1 -1
  73. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-0fc086bf.js → timeline-definition-85554ec2-74631749.js} +1 -1
  74. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-44ee592e.js → xychartDiagram-e933f94c-a043552f.js} +1 -1
  75. rasa/core/channels/inspector/dist/index.html +1 -1
  76. rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +1 -1
  77. rasa/core/channels/mattermost.py +1 -1
  78. rasa/core/channels/rasa_chat.py +2 -4
  79. rasa/core/channels/rest.py +5 -4
  80. rasa/core/channels/socketio.py +56 -41
  81. rasa/core/channels/studio_chat.py +314 -10
  82. rasa/core/channels/vier_cvg.py +1 -2
  83. rasa/core/channels/voice_ready/audiocodes.py +2 -9
  84. rasa/core/channels/voice_stream/asr/azure.py +9 -0
  85. rasa/core/channels/voice_stream/audiocodes.py +8 -5
  86. rasa/core/channels/voice_stream/browser_audio.py +1 -1
  87. rasa/core/channels/voice_stream/genesys.py +2 -2
  88. rasa/core/channels/voice_stream/jambonz.py +166 -0
  89. rasa/core/channels/voice_stream/tts/__init__.py +8 -0
  90. rasa/core/channels/voice_stream/twilio_media_streams.py +17 -5
  91. rasa/core/channels/voice_stream/voice_channel.py +44 -24
  92. rasa/core/exporter.py +36 -0
  93. rasa/core/http_interpreter.py +3 -7
  94. rasa/core/information_retrieval/faiss.py +18 -11
  95. rasa/core/information_retrieval/ingestion/faq_parser.py +158 -0
  96. rasa/core/jobs.py +2 -1
  97. rasa/core/nlg/contextual_response_rephraser.py +48 -12
  98. rasa/core/nlg/generator.py +0 -1
  99. rasa/core/nlg/interpolator.py +2 -3
  100. rasa/core/nlg/summarize.py +39 -5
  101. rasa/core/policies/enterprise_search_policy.py +298 -184
  102. rasa/core/policies/enterprise_search_policy_config.py +241 -0
  103. rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +64 -0
  104. rasa/core/policies/flow_policy.py +1 -1
  105. rasa/core/policies/flows/flow_executor.py +96 -17
  106. rasa/core/policies/intentless_policy.py +71 -26
  107. rasa/core/processor.py +104 -51
  108. rasa/core/run.py +33 -11
  109. rasa/core/tracker_stores/tracker_store.py +1 -1
  110. rasa/core/training/interactive.py +1 -1
  111. rasa/core/utils.py +35 -99
  112. rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
  113. rasa/dialogue_understanding/coexistence/llm_based_router.py +13 -17
  114. rasa/dialogue_understanding/commands/__init__.py +4 -0
  115. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
  116. rasa/dialogue_understanding/commands/cancel_flow_command.py +6 -2
  117. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
  118. rasa/dialogue_understanding/commands/clarify_command.py +7 -3
  119. rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
  120. rasa/dialogue_understanding/commands/correct_slots_command.py +5 -6
  121. rasa/dialogue_understanding/commands/error_command.py +1 -1
  122. rasa/dialogue_understanding/commands/human_handoff_command.py +3 -3
  123. rasa/dialogue_understanding/commands/knowledge_answer_command.py +2 -0
  124. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
  125. rasa/dialogue_understanding/commands/set_slot_command.py +15 -5
  126. rasa/dialogue_understanding/commands/skip_question_command.py +3 -3
  127. rasa/dialogue_understanding/commands/start_flow_command.py +7 -3
  128. rasa/dialogue_understanding/commands/utils.py +26 -2
  129. rasa/dialogue_understanding/generator/__init__.py +7 -1
  130. rasa/dialogue_understanding/generator/command_generator.py +15 -3
  131. rasa/dialogue_understanding/generator/command_parser.py +2 -2
  132. rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
  133. rasa/dialogue_understanding/generator/constants.py +2 -2
  134. rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
  135. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_template.jinja2 +0 -2
  136. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_claude_3_5_sonnet_20240620_template.jinja2 +1 -0
  137. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +1 -0
  138. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_claude_3_5_sonnet_20240620_template.jinja2 +79 -0
  139. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +79 -0
  140. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +28 -463
  141. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
  142. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +461 -0
  143. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +11 -64
  144. rasa/dialogue_understanding/patterns/cancel.py +1 -2
  145. rasa/dialogue_understanding/patterns/clarify.py +1 -1
  146. rasa/dialogue_understanding/patterns/correction.py +2 -2
  147. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +37 -25
  148. rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
  149. rasa/dialogue_understanding/processor/command_processor.py +11 -12
  150. rasa/dialogue_understanding/processor/command_processor_component.py +3 -3
  151. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
  152. rasa/dialogue_understanding/stack/utils.py +3 -1
  153. rasa/dialogue_understanding/utils.py +68 -12
  154. rasa/dialogue_understanding_test/du_test_case.py +1 -1
  155. rasa/dialogue_understanding_test/du_test_runner.py +4 -22
  156. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
  157. rasa/e2e_test/e2e_test_coverage_report.py +1 -1
  158. rasa/e2e_test/e2e_test_runner.py +1 -1
  159. rasa/engine/constants.py +1 -1
  160. rasa/engine/graph.py +2 -2
  161. rasa/engine/recipes/default_recipe.py +26 -2
  162. rasa/engine/validation.py +3 -2
  163. rasa/hooks.py +0 -28
  164. rasa/llm_fine_tuning/annotation_module.py +39 -9
  165. rasa/llm_fine_tuning/conversations.py +3 -0
  166. rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
  167. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +5 -7
  168. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
  169. rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
  170. rasa/llm_fine_tuning/storage.py +4 -4
  171. rasa/llm_fine_tuning/utils.py +63 -1
  172. rasa/model_manager/model_api.py +88 -0
  173. rasa/model_manager/trainer_service.py +4 -4
  174. rasa/plugin.py +1 -11
  175. rasa/privacy/__init__.py +0 -0
  176. rasa/privacy/constants.py +83 -0
  177. rasa/privacy/event_broker_utils.py +77 -0
  178. rasa/privacy/privacy_config.py +281 -0
  179. rasa/privacy/privacy_config_schema.json +86 -0
  180. rasa/privacy/privacy_filter.py +340 -0
  181. rasa/privacy/privacy_manager.py +576 -0
  182. rasa/server.py +23 -2
  183. rasa/shared/constants.py +18 -0
  184. rasa/shared/core/command_payload_reader.py +1 -5
  185. rasa/shared/core/constants.py +4 -3
  186. rasa/shared/core/domain.py +7 -0
  187. rasa/shared/core/events.py +38 -10
  188. rasa/shared/core/flows/constants.py +2 -0
  189. rasa/shared/core/flows/flow.py +127 -14
  190. rasa/shared/core/flows/flows_list.py +18 -1
  191. rasa/shared/core/flows/flows_yaml_schema.json +3 -0
  192. rasa/shared/core/flows/steps/collect.py +46 -2
  193. rasa/shared/core/flows/steps/link.py +7 -2
  194. rasa/shared/core/flows/validation.py +25 -5
  195. rasa/shared/core/slots.py +28 -0
  196. rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
  197. rasa/shared/exceptions.py +4 -0
  198. rasa/shared/providers/_configs/azure_openai_client_config.py +6 -2
  199. rasa/shared/providers/_configs/default_litellm_client_config.py +1 -1
  200. rasa/shared/providers/_configs/huggingface_local_embedding_client_config.py +1 -1
  201. rasa/shared/providers/_configs/openai_client_config.py +5 -1
  202. rasa/shared/providers/_configs/rasa_llm_client_config.py +1 -1
  203. rasa/shared/providers/_configs/self_hosted_llm_client_config.py +1 -1
  204. rasa/shared/providers/_configs/utils.py +0 -99
  205. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +3 -0
  206. rasa/shared/providers/llm/_base_litellm_client.py +5 -2
  207. rasa/shared/utils/common.py +1 -1
  208. rasa/shared/utils/configs.py +110 -0
  209. rasa/shared/utils/constants.py +0 -3
  210. rasa/shared/utils/llm.py +195 -9
  211. rasa/shared/utils/pykwalify_extensions.py +0 -9
  212. rasa/shared/utils/yaml.py +32 -0
  213. rasa/studio/constants.py +1 -0
  214. rasa/studio/data_handler.py +11 -4
  215. rasa/studio/download.py +167 -0
  216. rasa/studio/link.py +200 -0
  217. rasa/studio/prompts.py +223 -0
  218. rasa/studio/pull/__init__.py +0 -0
  219. rasa/studio/{download/flows.py → pull/data.py} +23 -160
  220. rasa/studio/{download → pull}/domains.py +1 -1
  221. rasa/studio/pull/pull.py +235 -0
  222. rasa/studio/push.py +136 -0
  223. rasa/studio/train.py +1 -1
  224. rasa/studio/upload.py +117 -67
  225. rasa/telemetry.py +82 -25
  226. rasa/tracing/config.py +3 -4
  227. rasa/tracing/constants.py +19 -1
  228. rasa/tracing/instrumentation/attribute_extractors.py +30 -8
  229. rasa/tracing/instrumentation/instrumentation.py +53 -2
  230. rasa/tracing/instrumentation/metrics.py +98 -15
  231. rasa/tracing/metric_instrument_provider.py +75 -3
  232. rasa/utils/common.py +7 -22
  233. rasa/utils/log_utils.py +1 -45
  234. rasa/validator.py +2 -8
  235. rasa/version.py +1 -1
  236. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/METADATA +8 -9
  237. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/RECORD +241 -220
  238. rasa/anonymization/__init__.py +0 -2
  239. rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
  240. rasa/anonymization/anonymization_pipeline.py +0 -286
  241. rasa/anonymization/anonymization_rule_executor.py +0 -266
  242. rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
  243. rasa/anonymization/schemas/config.yml +0 -47
  244. rasa/anonymization/utils.py +0 -118
  245. rasa/core/channels/inspector/dist/assets/channel-3730f5fd.js +0 -1
  246. rasa/core/channels/inspector/dist/assets/clone-e847561e.js +0 -1
  247. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-efbbfe00.js +0 -1
  248. rasa/studio/download/download.py +0 -439
  249. /rasa/{studio/download → core/information_retrieval/ingestion}/__init__.py +0 -0
  250. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/NOTICE +0 -0
  251. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/WHEEL +0 -0
  252. {rasa_pro-3.13.0.dev20250612.dist-info → rasa_pro-3.13.0rc1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,158 @@
1
+ """Utilities for parsing FAQ-style documents (Q/A pairs) used in extractive search."""
2
+
3
+ import re
4
+ from collections import defaultdict
5
+ from typing import TYPE_CHECKING, List
6
+
7
+ import structlog
8
+
9
+ from rasa.shared.constants import (
10
+ DOCUMENT_TYPE_FAQ,
11
+ FAQ_DOCUMENT_ENTRY_SEPARATOR,
12
+ FAQ_DOCUMENT_LINE_SEPARATOR,
13
+ FAQ_DOCUMENT_METADATA_ANSWER,
14
+ FAQ_DOCUMENT_METADATA_TITLE,
15
+ FAQ_DOCUMENT_METADATA_TYPE,
16
+ FAQ_INPUT_DATA_ANSWER_LINE_PREFIX,
17
+ FAQ_INPUT_DATA_QUESTION_LINE_PREFIX,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ from langchain.schema import Document
22
+
23
+ _FAQ_PAIR_PATTERN = re.compile(
24
+ rf"{re.escape(FAQ_INPUT_DATA_QUESTION_LINE_PREFIX)}\s*"
25
+ rf"(?P<question>.*?)\s*{FAQ_DOCUMENT_LINE_SEPARATOR}\s*"
26
+ rf"{re.escape(FAQ_INPUT_DATA_ANSWER_LINE_PREFIX)}\s*"
27
+ rf"(?P<answer>.*)",
28
+ re.DOTALL,
29
+ )
30
+
31
+
32
+ structlogger = structlog.get_logger()
33
+
34
+
35
+ def _format_faq_documents(documents: List["Document"]) -> List["Document"]:
36
+ """Splits each loaded file into individual FAQs.
37
+
38
+ Args:
39
+ documents: Documents representing whole files containing FAQs.
40
+
41
+ Returns:
42
+ List of Document objects, each containing a separate FAQ.
43
+
44
+ Examples:
45
+ An example of a file containing FAQs:
46
+
47
+ Q: Who is Finley?
48
+ A: Finley is your smart assistant for the FinX App. You can add him to your
49
+ favorite messenger and tell him what you need help with.
50
+
51
+ Q: How does Finley work?
52
+ A: Finley is powered by the latest chatbot technology leveraging a unique
53
+ interplay of large language models and secure logic.
54
+
55
+ More details in documentation: https://rasa.com/docs/reference/config/policies/extractive-search/
56
+ """
57
+ structured_faqs = []
58
+ from langchain.schema import Document
59
+
60
+ for document in documents:
61
+ chunks = document.page_content.strip().split(FAQ_DOCUMENT_ENTRY_SEPARATOR)
62
+
63
+ for chunk in chunks:
64
+ match = _FAQ_PAIR_PATTERN.match(chunk.strip())
65
+
66
+ if not match:
67
+ structlogger.warning(
68
+ "faq_parser.format_faq_documents.invalid_chunk_skipped",
69
+ event_info=(
70
+ "Chunk does not match expected QA format. "
71
+ "Please refer to the documentation: "
72
+ "https://rasa.com/docs/reference/config/"
73
+ "policies/extractive-search/"
74
+ ),
75
+ chunk_preview=chunk[:100],
76
+ )
77
+ continue
78
+
79
+ question = match.group("question").strip()
80
+ answer = match.group("answer").strip()
81
+ title = _sanitize_title(question)
82
+
83
+ formatted_document = Document(
84
+ page_content=question,
85
+ metadata={
86
+ FAQ_DOCUMENT_METADATA_TITLE: title,
87
+ FAQ_DOCUMENT_METADATA_TYPE: DOCUMENT_TYPE_FAQ,
88
+ FAQ_DOCUMENT_METADATA_ANSWER: answer,
89
+ },
90
+ )
91
+
92
+ structured_faqs.append(formatted_document)
93
+
94
+ structlogger.debug(
95
+ "faq_parser.format_faq_documents.parsed_chunk",
96
+ event_info="Parsed chunk.",
97
+ title=title,
98
+ question=question,
99
+ answer=answer,
100
+ parsed_chunk_preview=chunk[:100],
101
+ )
102
+
103
+ structlogger.debug(
104
+ "faq_parser.format_faq_documents.parsed_chunks",
105
+ event_info=(
106
+ f"Retrieved {len(structured_faqs)} FAQ pair(s)"
107
+ f"from {len(documents)} document(s)."
108
+ ),
109
+ num_structured_faqs=len(structured_faqs),
110
+ num_documents=len(documents),
111
+ )
112
+ _check_and_parsed_faq_documents_for_duplicates(structured_faqs)
113
+ return structured_faqs
114
+
115
+
116
+ def _sanitize_title(title: str) -> str:
117
+ title = title.lower()
118
+ # Remove all whitespaces with "_"
119
+ title = re.sub(r"\s+", "_", title)
120
+ # Remove all non alpha-numeric characters
121
+ title = re.sub(r"[^\w]", "", title)
122
+ # Collapse multiple "_"
123
+ title = re.sub(r"_+", "_", title)
124
+ # Clean up edges
125
+ return title.strip("_")
126
+
127
+
128
+ def _check_and_parsed_faq_documents_for_duplicates(documents: List["Document"]) -> None:
129
+ seen_qa_pairs = set()
130
+ seen_questions: defaultdict = defaultdict(list)
131
+
132
+ for doc in documents:
133
+ question = doc.page_content.strip()
134
+ answer = doc.metadata.get(FAQ_DOCUMENT_METADATA_ANSWER, "").strip()
135
+
136
+ if not question or not answer:
137
+ continue
138
+
139
+ if (question, answer) in seen_qa_pairs:
140
+ structlogger.warning(
141
+ "faq_parser.duplicate_qa_pair_found",
142
+ event_info="Duplicate QA pair found.",
143
+ question=question,
144
+ answer_preview=answer,
145
+ )
146
+ continue
147
+
148
+ if question in seen_questions and seen_questions[question] != answer:
149
+ structlogger.warning(
150
+ "faq_parser.inconsistent_answer",
151
+ event_info="Duplicate question with different answer found.",
152
+ question=question,
153
+ previous_answers=seen_questions[question],
154
+ new_answer=answer,
155
+ )
156
+
157
+ seen_qa_pairs.add((question, answer))
158
+ seen_questions[question].append(answer)
rasa/core/jobs.py CHANGED
@@ -1,12 +1,13 @@
1
1
  import asyncio
2
2
  import logging
3
+ from typing import Optional
3
4
 
4
5
  from apscheduler.schedulers.asyncio import AsyncIOScheduler
5
6
  from pytz import UnknownTimeZoneError, utc
6
7
 
7
8
  import rasa.shared.utils.io
8
9
 
9
- __scheduler = None
10
+ __scheduler: Optional[AsyncIOScheduler] = None
10
11
 
11
12
  logger = logging.getLogger(__name__)
12
13
 
@@ -5,15 +5,21 @@ from jinja2 import Template
5
5
 
6
6
  from rasa import telemetry
7
7
  from rasa.core.nlg.response import TemplatedNaturalLanguageGenerator
8
- from rasa.core.nlg.summarize import summarize_conversation
8
+ from rasa.core.nlg.summarize import (
9
+ _count_multiple_utterances_as_single_turn,
10
+ summarize_conversation,
11
+ )
9
12
  from rasa.shared.constants import (
10
13
  LLM_CONFIG_KEY,
14
+ MAX_COMPLETION_TOKENS_CONFIG_KEY,
11
15
  MODEL_CONFIG_KEY,
12
16
  MODEL_GROUP_ID_CONFIG_KEY,
13
17
  MODEL_NAME_CONFIG_KEY,
14
18
  OPENAI_PROVIDER,
15
19
  PROMPT_CONFIG_KEY,
20
+ PROMPT_TEMPLATE_CONFIG_KEY,
16
21
  PROVIDER_CONFIG_KEY,
22
+ TEMPERATURE_CONFIG_KEY,
17
23
  TIMEOUT_CONFIG_KEY,
18
24
  )
19
25
  from rasa.shared.core.domain import KEY_RESPONSES_TEXT, Domain
@@ -33,6 +39,7 @@ from rasa.shared.utils.llm import (
33
39
  DEFAULT_OPENAI_GENERATE_MODEL_NAME,
34
40
  DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
35
41
  USER,
42
+ check_prompt_config_keys_and_warn_if_deprecated,
36
43
  combine_custom_and_default_config,
37
44
  get_prompt_template,
38
45
  llm_factory,
@@ -53,12 +60,13 @@ RESPONSE_SUMMARISE_CONVERSATION_KEY = "summarize_conversation"
53
60
  DEFAULT_REPHRASE_ALL = False
54
61
  DEFAULT_SUMMARIZE_HISTORY = True
55
62
  DEFAULT_MAX_HISTORICAL_TURNS = 5
63
+ DEFAULT_COUNT_MULTIPLE_UTTERANCES_AS_SINGLE_TURN = True
56
64
 
57
65
  DEFAULT_LLM_CONFIG = {
58
66
  PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
59
67
  MODEL_CONFIG_KEY: DEFAULT_OPENAI_GENERATE_MODEL_NAME,
60
- "temperature": 0.3,
61
- "max_tokens": DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
68
+ TEMPERATURE_CONFIG_KEY: 0.3,
69
+ MAX_COMPLETION_TOKENS_CONFIG_KEY: DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
62
70
  TIMEOUT_CONFIG_KEY: 5,
63
71
  }
64
72
 
@@ -70,6 +78,7 @@ its meaning. Use simple {{language}}.
70
78
  Context / previous conversation with the user:
71
79
  {{history}}
72
80
 
81
+ Last user message:
73
82
  {{current_input}}
74
83
 
75
84
  Suggested AI Response: {{suggested_response}}
@@ -103,8 +112,15 @@ class ContextualResponseRephraser(
103
112
  super().__init__(domain.responses)
104
113
 
105
114
  self.nlg_endpoint = endpoint_config
115
+
116
+ # Warn if the prompt config key is used to set the prompt template
117
+ check_prompt_config_keys_and_warn_if_deprecated(
118
+ self.nlg_endpoint.kwargs, "contextual_response_rephraser"
119
+ )
120
+
106
121
  self.prompt_template = get_prompt_template(
107
- self.nlg_endpoint.kwargs.get(PROMPT_CONFIG_KEY),
122
+ self.nlg_endpoint.kwargs.get(PROMPT_TEMPLATE_CONFIG_KEY)
123
+ or self.nlg_endpoint.kwargs.get(PROMPT_CONFIG_KEY),
108
124
  DEFAULT_RESPONSE_VARIATION_PROMPT_TEMPLATE,
109
125
  log_source_component=ContextualResponseRephraser.__name__,
110
126
  log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
@@ -122,6 +138,11 @@ class ContextualResponseRephraser(
122
138
  "max_historical_turns", DEFAULT_MAX_HISTORICAL_TURNS
123
139
  )
124
140
 
141
+ self.count_multiple_utterances_as_single_turn = self.nlg_endpoint.kwargs.get(
142
+ "count_multiple_utterances_as_single_turn",
143
+ DEFAULT_COUNT_MULTIPLE_UTTERANCES_AS_SINGLE_TURN,
144
+ )
145
+
125
146
  self.llm_config = resolve_model_client_config(
126
147
  self.nlg_endpoint.kwargs.get(LLM_CONFIG_KEY),
127
148
  ContextualResponseRephraser.__name__,
@@ -258,8 +279,16 @@ class ContextualResponseRephraser(
258
279
  Returns:
259
280
  The history for the prompt.
260
281
  """
282
+ # Count multiple utterances by bot/user as single turn in conversation history
283
+ turns_wrapper = (
284
+ _count_multiple_utterances_as_single_turn
285
+ if self.count_multiple_utterances_as_single_turn
286
+ else None
287
+ )
261
288
  llm = llm_factory(self.llm_config, DEFAULT_LLM_CONFIG)
262
- return await summarize_conversation(tracker, llm, max_turns=5)
289
+ return await summarize_conversation(
290
+ tracker, llm, max_turns=5, turns_wrapper=turns_wrapper
291
+ )
263
292
 
264
293
  async def rephrase(
265
294
  self,
@@ -281,19 +310,26 @@ class ContextualResponseRephraser(
281
310
 
282
311
  prompt_template_text = self._template_for_response_rephrasing(response)
283
312
 
284
- # Retrieve inputs for the dynamic prompt
285
- latest_message = self._last_message_if_human(tracker)
286
- current_input = f"{USER}: {latest_message}" if latest_message else ""
313
+ # Last user message (=current input) should always be in prompt if available
314
+ last_message_by_user = getattr(tracker.latest_message, "text", "")
315
+ current_input = (
316
+ f"{USER}: {last_message_by_user}" if last_message_by_user else ""
317
+ )
287
318
 
288
319
  # Only summarise conversation history if flagged
289
320
  if self.summarize_history:
290
321
  history = await self._create_history(tracker)
291
322
  else:
292
- # make sure the transcript/history contains the last user utterance
323
+ # Count multiple utterances by bot/user as single turn
324
+ turns_wrapper = (
325
+ _count_multiple_utterances_as_single_turn
326
+ if self.count_multiple_utterances_as_single_turn
327
+ else None
328
+ )
293
329
  max_turns = max(self.max_historical_turns, 1)
294
- history = tracker_as_readable_transcript(tracker, max_turns=max_turns)
295
- # the history already contains the current input
296
- current_input = ""
330
+ history = tracker_as_readable_transcript(
331
+ tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
332
+ )
297
333
 
298
334
  prompt = Template(prompt_template_text).render(
299
335
  history=history,
@@ -292,7 +292,6 @@ def _evaluate_predicate(constraint: str, filled_slots: Dict[Text, Any]) -> bool:
292
292
  structlogger.error(
293
293
  "rasa.core.nlg.generator.evaluate_conditional_response_predicate.error",
294
294
  predicate=constraint,
295
- document=document,
296
295
  error=str(e),
297
296
  )
298
297
  return False
@@ -1,4 +1,3 @@
1
- import copy
2
1
  import logging
3
2
  import re
4
3
  from typing import Any, Dict, List, Text, Union
@@ -70,9 +69,9 @@ def interpolate_format_template(response: Text, values: Dict[Text, Text]) -> Tex
70
69
  )
71
70
  structlogger.exception(
72
71
  "interpolator.interpolate.text",
73
- response=copy.deepcopy(response),
74
72
  placeholder_key=e.args[0],
75
73
  event_info=event_info,
74
+ error=str(e),
76
75
  )
77
76
  return response
78
77
 
@@ -98,9 +97,9 @@ def interpolate_jinja_template(response: Text, values: Dict[Text, Any]) -> Text:
98
97
  )
99
98
  structlogger.exception(
100
99
  "interpolator.interpolate.text",
101
- response=copy.deepcopy(response),
102
100
  placeholder_key=e.args[0],
103
101
  event_info=event_info,
102
+ error=str(e),
104
103
  )
105
104
  return response
106
105
 
@@ -1,4 +1,5 @@
1
- from typing import Optional
1
+ from itertools import groupby
2
+ from typing import Callable, List, Optional
2
3
 
3
4
  import structlog
4
5
  from jinja2 import Template
@@ -23,20 +24,49 @@ SUMMARY_PROMPT_TEMPLATE = Template(_DEFAULT_SUMMARIZER_TEMPLATE)
23
24
  MAX_TURNS_DEFAULT = 20
24
25
 
25
26
 
27
+ def _count_multiple_utterances_as_single_turn(transcript: List[str]) -> List[str]:
28
+ """Counts multiple utterances as a single turn.
29
+
30
+ Args:
31
+ transcript: the lines of the transcript
32
+
33
+ Returns:
34
+ transcript: with multiple utterances counted as a single turn
35
+ """
36
+ if not transcript:
37
+ return []
38
+
39
+ def get_speaker_label(line: str) -> str:
40
+ return line.partition(": ")[0] if ": " in line else ""
41
+
42
+ modified_transcript = [
43
+ f"{speaker}: {' '.join(line.partition(': ')[2] for line in group)}"
44
+ for speaker, group in groupby(transcript, key=get_speaker_label)
45
+ if speaker
46
+ ]
47
+
48
+ return modified_transcript
49
+
50
+
26
51
  def _create_summarization_prompt(
27
- tracker: DialogueStateTracker, max_turns: Optional[int]
52
+ tracker: DialogueStateTracker,
53
+ max_turns: Optional[int],
54
+ turns_wrapper: Optional[Callable[[List[str]], List[str]]],
28
55
  ) -> str:
29
56
  """Creates an LLM prompt to summarize the conversation in the tracker.
30
57
 
31
58
  Args:
32
59
  tracker: tracker of the conversation to be summarized
33
60
  max_turns: maximum number of turns to summarize
61
+ turns_wrapper: optional function to wrap the turns
34
62
 
35
63
 
36
64
  Returns:
37
65
  The prompt to summarize the conversation.
38
66
  """
39
- transcript = tracker_as_readable_transcript(tracker, max_turns=max_turns)
67
+ transcript = tracker_as_readable_transcript(
68
+ tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
69
+ )
40
70
  return SUMMARY_PROMPT_TEMPLATE.render(
41
71
  conversation=transcript,
42
72
  )
@@ -46,6 +76,7 @@ async def summarize_conversation(
46
76
  tracker: DialogueStateTracker,
47
77
  llm: LLMClient,
48
78
  max_turns: Optional[int] = MAX_TURNS_DEFAULT,
79
+ turns_wrapper: Optional[Callable[[List[str]], List[str]]] = None,
49
80
  ) -> str:
50
81
  """Summarizes the dialogue using the LLM.
51
82
 
@@ -53,11 +84,12 @@ async def summarize_conversation(
53
84
  tracker: the tracker to summarize
54
85
  llm: the LLM to use for summarization
55
86
  max_turns: maximum number of turns to summarize
87
+ turns_wrapper: optional function to wrap the turns
56
88
 
57
89
  Returns:
58
90
  The summary of the dialogue.
59
91
  """
60
- prompt = _create_summarization_prompt(tracker, max_turns)
92
+ prompt = _create_summarization_prompt(tracker, max_turns, turns_wrapper)
61
93
  try:
62
94
  llm_response = await llm.acompletion(prompt)
63
95
  summarization = llm_response.choices[0].strip()
@@ -66,6 +98,8 @@ async def summarize_conversation(
66
98
  )
67
99
  return summarization
68
100
  except Exception as e:
69
- transcript = tracker_as_readable_transcript(tracker, max_turns=max_turns)
101
+ transcript = tracker_as_readable_transcript(
102
+ tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
103
+ )
70
104
  structlogger.error("summarization.error", error=e)
71
105
  return transcript