rasa-pro 3.13.0.dev7__py3-none-any.whl → 3.13.0.dev9__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 (215) 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 -1
  5. rasa/cli/evaluate.py +1 -1
  6. rasa/cli/export.py +1 -1
  7. rasa/cli/llm_fine_tuning.py +12 -11
  8. rasa/cli/project_templates/defaults.py +133 -0
  9. rasa/cli/run.py +1 -1
  10. rasa/cli/studio/link.py +53 -0
  11. rasa/cli/studio/pull.py +78 -0
  12. rasa/cli/studio/push.py +78 -0
  13. rasa/cli/studio/studio.py +12 -0
  14. rasa/cli/studio/upload.py +8 -0
  15. rasa/cli/train.py +1 -1
  16. rasa/cli/utils.py +1 -1
  17. rasa/cli/x.py +1 -1
  18. rasa/constants.py +2 -0
  19. rasa/core/__init__.py +0 -16
  20. rasa/core/actions/action.py +5 -1
  21. rasa/core/actions/action_repeat_bot_messages.py +18 -22
  22. rasa/core/actions/action_run_slot_rejections.py +0 -1
  23. rasa/core/agent.py +16 -1
  24. rasa/core/available_endpoints.py +146 -0
  25. rasa/core/brokers/pika.py +1 -2
  26. rasa/core/channels/botframework.py +2 -2
  27. rasa/core/channels/channel.py +2 -2
  28. rasa/core/channels/development_inspector.py +1 -1
  29. rasa/core/channels/facebook.py +1 -4
  30. rasa/core/channels/hangouts.py +8 -5
  31. rasa/core/channels/inspector/README.md +3 -3
  32. rasa/core/channels/inspector/dist/assets/{arc-c4b064fc.js → arc-02053cc1.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-215b5026.js → blockDiagram-38ab4fdb-008b6289.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-2b54a0a3.js → c4Diagram-3d4e48cf-fb2597be.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/channel-078dada8.js +1 -0
  36. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-daacea5f.js → classDiagram-70f12bd4-7f847e00.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-930d4dc2.js → classDiagram-v2-f2320105-ba1d689b.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/clone-5b4516de.js +1 -0
  39. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-83c206ba.js → createText-2e5e7dd3-dd8e67c4.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-b0eb01d0.js → edges-e0da2a9e-10784939.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-17586500.js → erDiagram-9861fffd-24947ae6.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-be2a1776.js → flowDb-956e92f1-a9ced505.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-c2120ebd.js → flowDiagram-66a62f08-afda9c7c.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-f9613071.js +1 -0
  45. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-a6ab5c48.js → flowchart-elk-definition-4a651766-6ef530b8.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-ef613457.js → ganttDiagram-c361ad54-0c7dd39a.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-d59185b3.js → gitGraphDiagram-72cf32ee-b57239d6.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{graph-0f155405.js → graph-9ed57cec.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{index-3862675e-d5f1d1b7.js → index-3862675e-233090de.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{index-47737d3a.js → index-72184470.js} +3 -3
  51. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-b07d141f.js → infoDiagram-f8f76790-aa116649.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-1936d429.js → journeyDiagram-49397b02-e51877cc.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{layout-dde8d0f3.js → layout-3ca3798c.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{line-0c2c7ee0.js → line-26ee10d3.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{linear-35dd89a4.js → linear-aedded32.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-56192851.js → mindmap-definition-fc14e90a-d8957261.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-fc21ed78.js → pieDiagram-8a3498a8-d771f885.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-25e98518.js → quadrantDiagram-120e2f19-09fdf50c.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-546ff1f5.js → requirementDiagram-deff3bca-9f0af02e.js} +1 -1
  60. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-02d8b82d.js → sankeyDiagram-04a897e0-84415b37.js} +1 -1
  61. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3ca5a92e.js → sequenceDiagram-704730f1-8dec4055.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-128ea07c.js → stateDiagram-587899a1-c5431d07.js} +1 -1
  63. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-95f290af.js → stateDiagram-v2-d93cdb3a-274e77d9.js} +1 -1
  64. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-4984898a.js → styles-6aaf32cf-e364a1d7.js} +1 -1
  65. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1bf266ba.js → styles-9a916d00-0dae36f6.js} +1 -1
  66. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-60521c63.js → styles-c10674c1-c4641675.js} +1 -1
  67. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-a25b6e12.js → svgDrawCommon-08f97a94-831fe9a1.js} +1 -1
  68. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-0fc086bf.js → timeline-definition-85554ec2-c3304b3a.js} +1 -1
  69. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-44ee592e.js → xychartDiagram-e933f94c-da799369.js} +1 -1
  70. rasa/core/channels/inspector/dist/index.html +1 -1
  71. rasa/core/channels/inspector/src/components/RecruitmentPanel.tsx +1 -1
  72. rasa/core/channels/mattermost.py +1 -1
  73. rasa/core/channels/rasa_chat.py +2 -4
  74. rasa/core/channels/rest.py +5 -4
  75. rasa/core/channels/socketio.py +56 -41
  76. rasa/core/channels/studio_chat.py +314 -10
  77. rasa/core/channels/vier_cvg.py +1 -2
  78. rasa/core/channels/voice_ready/audiocodes.py +2 -9
  79. rasa/core/channels/voice_stream/audiocodes.py +8 -5
  80. rasa/core/channels/voice_stream/browser_audio.py +1 -1
  81. rasa/core/channels/voice_stream/genesys.py +2 -2
  82. rasa/core/channels/voice_stream/tts/__init__.py +8 -0
  83. rasa/core/channels/voice_stream/twilio_media_streams.py +10 -5
  84. rasa/core/channels/voice_stream/voice_channel.py +39 -23
  85. rasa/core/http_interpreter.py +3 -7
  86. rasa/core/information_retrieval/faiss.py +18 -11
  87. rasa/core/information_retrieval/ingestion/__init__.py +0 -0
  88. rasa/core/information_retrieval/ingestion/faq_parser.py +158 -0
  89. rasa/core/jobs.py +2 -1
  90. rasa/core/nlg/contextual_response_rephraser.py +44 -10
  91. rasa/core/nlg/generator.py +0 -1
  92. rasa/core/nlg/interpolator.py +2 -3
  93. rasa/core/nlg/summarize.py +39 -5
  94. rasa/core/policies/enterprise_search_policy.py +262 -62
  95. rasa/core/policies/enterprise_search_prompt_with_relevancy_check_and_citation_template.jinja2 +63 -0
  96. rasa/core/policies/flow_policy.py +1 -1
  97. rasa/core/policies/flows/flow_executor.py +96 -17
  98. rasa/core/policies/intentless_policy.py +56 -17
  99. rasa/core/processor.py +104 -51
  100. rasa/core/run.py +33 -11
  101. rasa/core/tracker_stores/tracker_store.py +1 -1
  102. rasa/core/training/interactive.py +1 -1
  103. rasa/core/utils.py +24 -97
  104. rasa/dialogue_understanding/coexistence/intent_based_router.py +2 -1
  105. rasa/dialogue_understanding/coexistence/llm_based_router.py +9 -6
  106. rasa/dialogue_understanding/commands/can_not_handle_command.py +2 -0
  107. rasa/dialogue_understanding/commands/cancel_flow_command.py +5 -1
  108. rasa/dialogue_understanding/commands/chit_chat_answer_command.py +2 -0
  109. rasa/dialogue_understanding/commands/clarify_command.py +5 -1
  110. rasa/dialogue_understanding/commands/command_syntax_manager.py +1 -0
  111. rasa/dialogue_understanding/commands/correct_slots_command.py +1 -3
  112. rasa/dialogue_understanding/commands/human_handoff_command.py +2 -0
  113. rasa/dialogue_understanding/commands/knowledge_answer_command.py +4 -2
  114. rasa/dialogue_understanding/commands/repeat_bot_messages_command.py +2 -0
  115. rasa/dialogue_understanding/commands/set_slot_command.py +11 -1
  116. rasa/dialogue_understanding/commands/skip_question_command.py +2 -0
  117. rasa/dialogue_understanding/commands/start_flow_command.py +4 -0
  118. rasa/dialogue_understanding/commands/utils.py +26 -2
  119. rasa/dialogue_understanding/generator/__init__.py +7 -1
  120. rasa/dialogue_understanding/generator/command_generator.py +4 -2
  121. rasa/dialogue_understanding/generator/command_parser.py +2 -2
  122. rasa/dialogue_understanding/generator/command_parser_validator.py +63 -0
  123. rasa/dialogue_understanding/generator/nlu_command_adapter.py +2 -2
  124. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v2_gpt_4o_2024_11_20_template.jinja2 +12 -33
  125. rasa/dialogue_understanding/generator/prompt_templates/command_prompt_v3_gpt_4o_2024_11_20_template.jinja2 +78 -0
  126. rasa/dialogue_understanding/generator/single_step/compact_llm_command_generator.py +26 -461
  127. rasa/dialogue_understanding/generator/single_step/search_ready_llm_command_generator.py +147 -0
  128. rasa/dialogue_understanding/generator/single_step/single_step_based_llm_command_generator.py +477 -0
  129. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +11 -64
  130. rasa/dialogue_understanding/patterns/cancel.py +1 -2
  131. rasa/dialogue_understanding/patterns/clarify.py +1 -1
  132. rasa/dialogue_understanding/patterns/correction.py +2 -2
  133. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +37 -25
  134. rasa/dialogue_understanding/patterns/domain_for_patterns.py +190 -0
  135. rasa/dialogue_understanding/processor/command_processor.py +6 -7
  136. rasa/dialogue_understanding/processor/command_processor_component.py +3 -3
  137. rasa/dialogue_understanding/stack/frames/flow_stack_frame.py +17 -4
  138. rasa/dialogue_understanding/stack/utils.py +3 -1
  139. rasa/dialogue_understanding/utils.py +68 -12
  140. rasa/dialogue_understanding_test/du_test_case.py +1 -1
  141. rasa/dialogue_understanding_test/du_test_runner.py +4 -22
  142. rasa/dialogue_understanding_test/test_case_simulation/test_case_tracker_simulator.py +2 -6
  143. rasa/e2e_test/e2e_test_runner.py +1 -1
  144. rasa/engine/constants.py +1 -1
  145. rasa/engine/graph.py +2 -2
  146. rasa/engine/recipes/default_recipe.py +26 -2
  147. rasa/engine/validation.py +3 -2
  148. rasa/hooks.py +0 -28
  149. rasa/llm_fine_tuning/annotation_module.py +39 -9
  150. rasa/llm_fine_tuning/conversations.py +3 -0
  151. rasa/llm_fine_tuning/llm_data_preparation_module.py +66 -49
  152. rasa/llm_fine_tuning/paraphrasing/conversation_rephraser.py +1 -5
  153. rasa/llm_fine_tuning/paraphrasing/rephrase_validator.py +52 -44
  154. rasa/llm_fine_tuning/paraphrasing_module.py +10 -12
  155. rasa/llm_fine_tuning/storage.py +4 -4
  156. rasa/llm_fine_tuning/utils.py +63 -1
  157. rasa/model_manager/model_api.py +88 -0
  158. rasa/model_manager/trainer_service.py +4 -4
  159. rasa/plugin.py +1 -11
  160. rasa/privacy/__init__.py +0 -0
  161. rasa/privacy/constants.py +83 -0
  162. rasa/privacy/event_broker_utils.py +77 -0
  163. rasa/privacy/privacy_config.py +281 -0
  164. rasa/privacy/privacy_config_schema.json +86 -0
  165. rasa/privacy/privacy_filter.py +340 -0
  166. rasa/privacy/privacy_manager.py +576 -0
  167. rasa/server.py +23 -2
  168. rasa/shared/constants.py +14 -0
  169. rasa/shared/core/command_payload_reader.py +1 -5
  170. rasa/shared/core/constants.py +4 -3
  171. rasa/shared/core/domain.py +7 -0
  172. rasa/shared/core/events.py +38 -10
  173. rasa/shared/core/flows/flow.py +1 -2
  174. rasa/shared/core/flows/flows_yaml_schema.json +3 -0
  175. rasa/shared/core/flows/steps/collect.py +46 -2
  176. rasa/shared/core/flows/validation.py +16 -3
  177. rasa/shared/core/slots.py +28 -0
  178. rasa/shared/core/training_data/story_reader/yaml_story_reader.py +1 -4
  179. rasa/shared/exceptions.py +4 -0
  180. rasa/shared/utils/common.py +1 -1
  181. rasa/shared/utils/llm.py +191 -6
  182. rasa/shared/utils/yaml.py +32 -0
  183. rasa/studio/data_handler.py +3 -3
  184. rasa/studio/download/download.py +37 -60
  185. rasa/studio/download/flows.py +23 -31
  186. rasa/studio/link.py +200 -0
  187. rasa/studio/pull.py +94 -0
  188. rasa/studio/push.py +131 -0
  189. rasa/studio/upload.py +117 -67
  190. rasa/telemetry.py +82 -25
  191. rasa/tracing/config.py +3 -4
  192. rasa/tracing/constants.py +19 -1
  193. rasa/tracing/instrumentation/attribute_extractors.py +10 -2
  194. rasa/tracing/instrumentation/instrumentation.py +53 -2
  195. rasa/tracing/instrumentation/metrics.py +98 -15
  196. rasa/tracing/metric_instrument_provider.py +75 -3
  197. rasa/utils/common.py +1 -27
  198. rasa/utils/log_utils.py +1 -45
  199. rasa/validator.py +2 -8
  200. rasa/version.py +1 -1
  201. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/METADATA +7 -8
  202. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/RECORD +205 -189
  203. rasa/anonymization/__init__.py +0 -2
  204. rasa/anonymization/anonymisation_rule_yaml_reader.py +0 -91
  205. rasa/anonymization/anonymization_pipeline.py +0 -286
  206. rasa/anonymization/anonymization_rule_executor.py +0 -266
  207. rasa/anonymization/anonymization_rule_orchestrator.py +0 -119
  208. rasa/anonymization/schemas/config.yml +0 -47
  209. rasa/anonymization/utils.py +0 -118
  210. rasa/core/channels/inspector/dist/assets/channel-3730f5fd.js +0 -1
  211. rasa/core/channels/inspector/dist/assets/clone-e847561e.js +0 -1
  212. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-efbbfe00.js +0 -1
  213. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/NOTICE +0 -0
  214. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/WHEEL +0 -0
  215. {rasa_pro-3.13.0.dev7.dist-info → rasa_pro-3.13.0.dev9.dist-info}/entry_points.txt +0 -0
@@ -14,7 +14,7 @@ from sanic import ( # type: ignore[attr-defined]
14
14
  response,
15
15
  )
16
16
 
17
- from rasa.core.channels import InputChannel, UserMessage
17
+ from rasa.core.channels import UserMessage
18
18
  from rasa.core.channels.channel import (
19
19
  create_auth_requested_response_provider,
20
20
  requires_basic_auth,
@@ -102,16 +102,22 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
102
102
  server_url: str,
103
103
  asr_config: Dict,
104
104
  tts_config: Dict,
105
- monitor_silence: bool = False,
106
105
  username: Optional[Text] = None,
107
106
  password: Optional[Text] = None,
108
107
  ):
109
- super().__init__(server_url, asr_config, tts_config, monitor_silence)
108
+ super().__init__(
109
+ server_url=server_url,
110
+ asr_config=asr_config,
111
+ tts_config=tts_config,
112
+ )
110
113
  self.username = username
111
114
  self.password = password
112
115
 
113
116
  @classmethod
114
- def from_credentials(cls, credentials: Optional[Dict[str, Any]]) -> InputChannel:
117
+ def from_credentials(
118
+ cls,
119
+ credentials: Optional[Dict[str, Any]],
120
+ ) -> VoiceInputChannel:
115
121
  credentials = credentials or {}
116
122
 
117
123
  username = credentials.get("username")
@@ -126,7 +132,6 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
126
132
  credentials["server_url"],
127
133
  credentials["asr"],
128
134
  credentials["tts"],
129
- credentials.get("monitor_silence", False),
130
135
  username=username,
131
136
  password=password,
132
137
  )
@@ -31,8 +31,10 @@ from rasa.core.channels.voice_stream.tts.azure import AzureTTS
31
31
  from rasa.core.channels.voice_stream.tts.cartesia import CartesiaTTS
32
32
  from rasa.core.channels.voice_stream.tts.tts_cache import TTSCache
33
33
  from rasa.core.channels.voice_stream.tts.tts_engine import TTSEngine, TTSError
34
- from rasa.core.channels.voice_stream.util import generate_silence
35
- from rasa.shared.core.constants import SLOT_SILENCE_TIMEOUT
34
+ from rasa.core.channels.voice_stream.util import (
35
+ generate_silence,
36
+ )
37
+ from rasa.shared.core.constants import SILENCE_TIMEOUT_SLOT
36
38
  from rasa.shared.utils.cli import print_error_and_exit
37
39
  from rasa.shared.utils.common import (
38
40
  class_from_module_path,
@@ -171,8 +173,12 @@ class VoiceOutputChannel(OutputChannel):
171
173
  def update_silence_timeout(self) -> None:
172
174
  """Updates the silence timeout for the session."""
173
175
  if self.tracker_state:
174
- call_state.silence_timeout = ( # type: ignore[attr-defined]
175
- self.tracker_state["slots"][SLOT_SILENCE_TIMEOUT]
176
+ call_state.silence_timeout = self.tracker_state["slots"][ # type: ignore[attr-defined]
177
+ SILENCE_TIMEOUT_SLOT
178
+ ]
179
+ logger.debug(
180
+ "voice_channel.silence_timeout_updated",
181
+ silence_timeout=call_state.silence_timeout,
176
182
  )
177
183
 
178
184
  async def send_text_with_buttons(
@@ -280,26 +286,34 @@ class VoiceOutputChannel(OutputChannel):
280
286
 
281
287
 
282
288
  class VoiceInputChannel(InputChannel):
289
+ # All children of this class require a voice license to be used.
290
+ requires_voice_license = True
291
+
283
292
  def __init__(
284
293
  self,
285
294
  server_url: str,
286
295
  asr_config: Dict,
287
296
  tts_config: Dict,
288
- monitor_silence: bool = False,
289
297
  ):
290
- validate_voice_license_scope()
298
+ if self.requires_voice_license:
299
+ validate_voice_license_scope()
300
+
291
301
  self.server_url = server_url
292
302
  self.asr_config = asr_config
293
303
  self.tts_config = tts_config
294
- self.monitor_silence = monitor_silence
295
304
  self.tts_cache = TTSCache(tts_config.get("cache_size", 1000))
296
305
 
306
+ logger.info(
307
+ "voice_channel.initialized",
308
+ server_url=self.server_url,
309
+ asr_config=self.asr_config,
310
+ tts_config=self.tts_config,
311
+ )
312
+
297
313
  async def monitor_silence_timeout(self, asr_event_queue: asyncio.Queue) -> None:
298
314
  timeout = call_state.silence_timeout
299
315
  if not timeout:
300
316
  return
301
- if not self.monitor_silence:
302
- return
303
317
  logger.debug("voice_channel.silence_timeout_watch_started", timeout=timeout)
304
318
  await asyncio.sleep(timeout)
305
319
  await asr_event_queue.put(UserSilence())
@@ -314,13 +328,15 @@ class VoiceInputChannel(InputChannel):
314
328
  call_state.silence_timeout_watcher = None # type: ignore[attr-defined]
315
329
 
316
330
  @classmethod
317
- def from_credentials(cls, credentials: Optional[Dict[str, Any]]) -> InputChannel:
331
+ def from_credentials(
332
+ cls,
333
+ credentials: Optional[Dict[str, Any]],
334
+ ) -> InputChannel:
318
335
  credentials = credentials or {}
319
336
  return cls(
320
337
  credentials["server_url"],
321
338
  credentials["asr"],
322
339
  credentials["tts"],
323
- credentials.get("monitor_silence", False),
324
340
  )
325
341
 
326
342
  def channel_bytes_to_rasa_audio_bytes(self, input_bytes: bytes) -> RasaAudioBytes:
@@ -340,9 +356,9 @@ class VoiceInputChannel(InputChannel):
340
356
  ) -> None:
341
357
  output_channel = self.create_output_channel(channel_websocket, tts_engine)
342
358
  message = UserMessage(
343
- USER_CONVERSATION_SESSION_START,
344
- output_channel,
345
- call_parameters.stream_id,
359
+ text=USER_CONVERSATION_SESSION_START,
360
+ output_channel=output_channel,
361
+ sender_id=call_parameters.stream_id,
346
362
  input_channel=self.name(),
347
363
  metadata=asdict(call_parameters),
348
364
  )
@@ -377,17 +393,17 @@ class VoiceInputChannel(InputChannel):
377
393
 
378
394
  async def consume_audio_bytes() -> None:
379
395
  async for message in channel_websocket:
380
- is_bot_speaking_before = call_state.is_bot_speaking
396
+ was_bot_speaking_before = call_state.is_bot_speaking
381
397
  channel_action = self.map_input_message(message, channel_websocket)
382
398
  is_bot_speaking_after = call_state.is_bot_speaking
383
399
 
384
- if not is_bot_speaking_before and is_bot_speaking_after:
400
+ if not was_bot_speaking_before and is_bot_speaking_after:
385
401
  logger.debug("voice_channel.bot_started_speaking")
386
402
  # relevant when the bot speaks multiple messages in one turn
387
403
  self._cancel_silence_timeout_watcher()
388
404
 
389
405
  # we just stopped speaking, starting a watcher for silence timeout
390
- if is_bot_speaking_before and not is_bot_speaking_after:
406
+ if was_bot_speaking_before and not is_bot_speaking_after:
391
407
  logger.debug("voice_channel.bot_stopped_speaking")
392
408
  self._cancel_silence_timeout_watcher()
393
409
  call_state.silence_timeout_watcher = ( # type: ignore[attr-defined]
@@ -458,9 +474,9 @@ class VoiceInputChannel(InputChannel):
458
474
  call_state.is_user_speaking = False # type: ignore[attr-defined]
459
475
  output_channel = self.create_output_channel(voice_websocket, tts_engine)
460
476
  message = UserMessage(
461
- e.text,
462
- output_channel,
463
- call_parameters.stream_id,
477
+ text=e.text,
478
+ output_channel=output_channel,
479
+ sender_id=call_parameters.stream_id,
464
480
  input_channel=self.name(),
465
481
  metadata=asdict(call_parameters),
466
482
  )
@@ -471,9 +487,9 @@ class VoiceInputChannel(InputChannel):
471
487
  elif isinstance(e, UserSilence):
472
488
  output_channel = self.create_output_channel(voice_websocket, tts_engine)
473
489
  message = UserMessage(
474
- USER_CONVERSATION_SILENCE_TIMEOUT,
475
- output_channel,
476
- call_parameters.stream_id,
490
+ text=USER_CONVERSATION_SILENCE_TIMEOUT,
491
+ output_channel=output_channel,
492
+ sender_id=call_parameters.stream_id,
477
493
  input_channel=self.name(),
478
494
  metadata=asdict(call_parameters),
479
495
  )
@@ -1,4 +1,3 @@
1
- import copy
2
1
  import logging
3
2
  from typing import Any, Dict, Optional, Text
4
3
 
@@ -49,7 +48,6 @@ class RasaNLUHttpInterpreter:
49
48
  if not self.endpoint_config or self.endpoint_config.url is None:
50
49
  structlogger.error(
51
50
  "http.parse.text",
52
- text=copy.deepcopy(text),
53
51
  event_info="No rasa NLU server specified!",
54
52
  )
55
53
  return None
@@ -71,18 +69,16 @@ class RasaNLUHttpInterpreter:
71
69
  if resp.status == 200:
72
70
  return await resp.json()
73
71
  else:
74
- response_text = await resp.text()
75
72
  structlogger.error(
76
73
  "http.parse.text.failure",
77
- text=copy.deepcopy(text),
78
- response_text=copy.deepcopy(response_text),
74
+ event_info="Failed to parse text",
79
75
  )
80
76
  return None
81
- except Exception: # skipcq: PYL-W0703
77
+ except Exception as e: # skipcq: PYL-W0703
82
78
  # need to catch all possible exceptions when doing http requests
83
79
  # (timeouts, value errors, parser errors, ...)
84
80
  structlogger.exception(
85
81
  "http.parse.text.exception",
86
- text=copy.deepcopy(text),
82
+ event_info=f"Exception occurred while parsing text. Error: {e}",
87
83
  )
88
84
  return None
@@ -12,6 +12,7 @@ from rasa.core.information_retrieval import (
12
12
  InformationRetrievalException,
13
13
  SearchResultList,
14
14
  )
15
+ from rasa.core.information_retrieval.ingestion.faq_parser import _format_faq_documents
15
16
  from rasa.utils.endpoints import EndpointConfig
16
17
  from rasa.utils.ml_utils import persist_faiss_vector_store
17
18
 
@@ -31,10 +32,12 @@ class FAISS_Store(InformationRetrieval):
31
32
  index_path: str,
32
33
  docs_folder: Optional[str],
33
34
  create_index: Optional[bool] = False,
35
+ parse_as_faq_pairs: Optional[bool] = False,
34
36
  ):
35
37
  """Initializes the FAISS Store."""
36
38
  self.chunk_size = 1000
37
39
  self.chunk_overlap = 20
40
+ self.parse_as_faq_pairs = parse_as_faq_pairs
38
41
 
39
42
  path = Path(index_path) / "documents_faiss"
40
43
  if create_index:
@@ -86,21 +89,25 @@ class FAISS_Store(InformationRetrieval):
86
89
  if not docs_folder:
87
90
  raise ValueError("parameter `docs_folder` needs to be specified")
88
91
 
89
- docs = self.load_documents(docs_folder)
90
- splitter = RecursiveCharacterTextSplitter(
91
- chunk_size=self.chunk_size,
92
- chunk_overlap=self.chunk_overlap,
93
- length_function=len,
94
- )
95
- doc_chunks = splitter.split_documents(docs)
92
+ documents = self.load_documents(docs_folder)
93
+
94
+ if not self.parse_as_faq_pairs:
95
+ splitter = RecursiveCharacterTextSplitter(
96
+ chunk_size=self.chunk_size,
97
+ chunk_overlap=self.chunk_overlap,
98
+ length_function=len,
99
+ )
100
+ parsed_documents = splitter.split_documents(documents)
101
+ else:
102
+ parsed_documents = _format_faq_documents(documents)
96
103
 
97
104
  logger.info(
98
105
  "information_retrieval.faiss_store._create_document_index",
99
- len_chunks=len(doc_chunks),
106
+ len_chunks=len(parsed_documents),
100
107
  )
101
- if doc_chunks:
102
- texts = [chunk.page_content for chunk in doc_chunks]
103
- metadatas = [chunk.metadata for chunk in doc_chunks]
108
+ if parsed_documents:
109
+ texts = [document.page_content for document in parsed_documents]
110
+ metadatas = [document.metadata for document in parsed_documents]
104
111
  return FAISS.from_texts(texts, embedding, metadatas=metadatas, ids=None)
105
112
  else:
106
113
  raise ValueError(f"No documents found at '{docs_folder}'.")
File without changes
@@ -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,7 +5,10 @@ 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,
11
14
  MAX_COMPLETION_TOKENS_CONFIG_KEY,
@@ -14,6 +17,7 @@ from rasa.shared.constants import (
14
17
  MODEL_NAME_CONFIG_KEY,
15
18
  OPENAI_PROVIDER,
16
19
  PROMPT_CONFIG_KEY,
20
+ PROMPT_TEMPLATE_CONFIG_KEY,
17
21
  PROVIDER_CONFIG_KEY,
18
22
  TEMPERATURE_CONFIG_KEY,
19
23
  TIMEOUT_CONFIG_KEY,
@@ -35,6 +39,7 @@ from rasa.shared.utils.llm import (
35
39
  DEFAULT_OPENAI_GENERATE_MODEL_NAME,
36
40
  DEFAULT_OPENAI_MAX_GENERATED_TOKENS,
37
41
  USER,
42
+ check_prompt_config_keys_and_warn_if_deprecated,
38
43
  combine_custom_and_default_config,
39
44
  get_prompt_template,
40
45
  llm_factory,
@@ -55,6 +60,7 @@ RESPONSE_SUMMARISE_CONVERSATION_KEY = "summarize_conversation"
55
60
  DEFAULT_REPHRASE_ALL = False
56
61
  DEFAULT_SUMMARIZE_HISTORY = True
57
62
  DEFAULT_MAX_HISTORICAL_TURNS = 5
63
+ DEFAULT_COUNT_MULTIPLE_UTTERANCES_AS_SINGLE_TURN = True
58
64
 
59
65
  DEFAULT_LLM_CONFIG = {
60
66
  PROVIDER_CONFIG_KEY: OPENAI_PROVIDER,
@@ -72,6 +78,7 @@ its meaning. Use simple {{language}}.
72
78
  Context / previous conversation with the user:
73
79
  {{history}}
74
80
 
81
+ Last user message:
75
82
  {{current_input}}
76
83
 
77
84
  Suggested AI Response: {{suggested_response}}
@@ -105,8 +112,15 @@ class ContextualResponseRephraser(
105
112
  super().__init__(domain.responses)
106
113
 
107
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
+
108
121
  self.prompt_template = get_prompt_template(
109
- 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),
110
124
  DEFAULT_RESPONSE_VARIATION_PROMPT_TEMPLATE,
111
125
  log_source_component=ContextualResponseRephraser.__name__,
112
126
  log_source_method=LOG_COMPONENT_SOURCE_METHOD_INIT,
@@ -124,6 +138,11 @@ class ContextualResponseRephraser(
124
138
  "max_historical_turns", DEFAULT_MAX_HISTORICAL_TURNS
125
139
  )
126
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
+
127
146
  self.llm_config = resolve_model_client_config(
128
147
  self.nlg_endpoint.kwargs.get(LLM_CONFIG_KEY),
129
148
  ContextualResponseRephraser.__name__,
@@ -260,8 +279,16 @@ class ContextualResponseRephraser(
260
279
  Returns:
261
280
  The history for the prompt.
262
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
+ )
263
288
  llm = llm_factory(self.llm_config, DEFAULT_LLM_CONFIG)
264
- 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
+ )
265
292
 
266
293
  async def rephrase(
267
294
  self,
@@ -283,19 +310,26 @@ class ContextualResponseRephraser(
283
310
 
284
311
  prompt_template_text = self._template_for_response_rephrasing(response)
285
312
 
286
- # Retrieve inputs for the dynamic prompt
287
- latest_message = self._last_message_if_human(tracker)
288
- 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
+ )
289
318
 
290
319
  # Only summarise conversation history if flagged
291
320
  if self.summarize_history:
292
321
  history = await self._create_history(tracker)
293
322
  else:
294
- # 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
+ )
295
329
  max_turns = max(self.max_historical_turns, 1)
296
- history = tracker_as_readable_transcript(tracker, max_turns=max_turns)
297
- # the history already contains the current input
298
- current_input = ""
330
+ history = tracker_as_readable_transcript(
331
+ tracker, max_turns=max_turns, turns_wrapper=turns_wrapper
332
+ )
299
333
 
300
334
  prompt = Template(prompt_template_text).render(
301
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