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,576 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import copy
5
+ import datetime
6
+ import os
7
+ import queue
8
+ import time
9
+ from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple
10
+
11
+ import structlog
12
+ from apscheduler.schedulers.background import BackgroundScheduler
13
+
14
+ import rasa.shared.core.trackers
15
+ from rasa.core.tracker_stores.tracker_store import TrackerStore
16
+ from rasa.privacy.constants import (
17
+ TEXT_KEY,
18
+ USER_CHAT_INACTIVITY_IN_MINUTES_ENV_VAR_NAME,
19
+ )
20
+ from rasa.privacy.event_broker_utils import create_event_brokers
21
+ from rasa.privacy.privacy_config import (
22
+ PrivacyConfig,
23
+ PrivacyPolicy,
24
+ validate_sensitive_slots,
25
+ )
26
+ from rasa.privacy.privacy_filter import PrivacyFilter
27
+ from rasa.shared.core.events import Event, SlotSet, UserUttered, split_events
28
+ from rasa.shared.core.trackers import DialogueStateTracker
29
+
30
+ if TYPE_CHECKING:
31
+ from asyncio import AbstractEventLoop
32
+
33
+ from rasa.core.available_endpoints import AvailableEndpoints
34
+ from rasa.core.brokers.broker import EventBroker
35
+ from rasa.shared.core.domain import Domain
36
+
37
+
38
+ structlogger = structlog.get_logger(__name__)
39
+
40
+
41
+ def wrap_async(func: Callable) -> Callable:
42
+ """Wraps a function to be used as an async job in the background scheduler."""
43
+
44
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
45
+ return asyncio.run(func(*args, **kwargs))
46
+
47
+ return wrapper
48
+
49
+
50
+ class BackgroundPrivacyManager:
51
+ """Manages privacy-related tasks in the background.
52
+
53
+ This class handles the anonymization and deletion of sensitive information
54
+ in dialogue state trackers, as well as the streaming of anonymized events
55
+ to event brokers. It uses background schedulers to periodically run these
56
+ tasks and processes trackers from a queue to ensure that sensitive information
57
+ is handled in a timely manner.
58
+ """
59
+
60
+ TRACKER_QUEUE_PROCESSING_TIMEOUT_IN_SECONDS = 2.0
61
+
62
+ def __init__(
63
+ self,
64
+ endpoints: Optional["AvailableEndpoints"],
65
+ event_loop: Optional["AbstractEventLoop"] = None,
66
+ ):
67
+ self.config = (
68
+ PrivacyConfig.from_dict(endpoints.privacy)
69
+ if endpoints and endpoints.privacy
70
+ else None
71
+ )
72
+ self.privacy_filter = (
73
+ PrivacyFilter(self.config.anonymization_rules) if self.config else None
74
+ )
75
+ self.user_chat_inactivity_in_minutes = int(
76
+ os.getenv(USER_CHAT_INACTIVITY_IN_MINUTES_ENV_VAR_NAME, 30)
77
+ )
78
+
79
+ # we recreate the tracker store here to ensure
80
+ # that this instance has no event brokers
81
+ # that could publish events during the tracker store
82
+ # background jobs
83
+ self.tracker_store = (
84
+ TrackerStore.create(endpoints.tracker_store)
85
+ if endpoints
86
+ else TrackerStore.create(None)
87
+ )
88
+ self.event_brokers: List["EventBroker"] = []
89
+ self.event_loop = event_loop
90
+
91
+ # Order of the initialisation is important
92
+ # The tracker queue must be created before the scheduler
93
+ # The can_consume_tracker_queue must be set to True before the scheduler starts
94
+ self.tracker_queue: queue.Queue = queue.Queue()
95
+
96
+ # This flag is used to stop the scheduler
97
+ self.can_consume_from_tracker_queue = True
98
+ self.background_scheduler = BackgroundScheduler()
99
+ self.background_scheduler.add_job(
100
+ self._consumer_queue, max_instances=1, id="event_broker_job"
101
+ )
102
+
103
+ self.previous_fire_time_deletion = datetime.datetime.now(
104
+ tz=datetime.timezone.utc
105
+ )
106
+ self._configure_background_scheduler()
107
+ self.background_scheduler.start()
108
+
109
+ async def initialize(
110
+ self, endpoints: Optional["AvailableEndpoints"]
111
+ ) -> BackgroundPrivacyManager:
112
+ """Initialize async attributes of the BackgroundPrivacyManager."""
113
+ event_broker_endpoints = endpoints.event_broker if endpoints else None
114
+ self.event_brokers = (
115
+ await create_event_brokers(event_broker_endpoints, self.event_loop)
116
+ if event_broker_endpoints
117
+ else []
118
+ )
119
+
120
+ return self
121
+
122
+ @classmethod
123
+ async def create_instance(
124
+ cls,
125
+ endpoints: Optional["AvailableEndpoints"],
126
+ event_loop: Optional["AbstractEventLoop"] = None,
127
+ ) -> BackgroundPrivacyManager:
128
+ """Create an instance of BackgroundPrivacyManager."""
129
+ instance = cls(endpoints, event_loop)
130
+ return await instance.initialize(endpoints)
131
+
132
+ def stop(self) -> None:
133
+ structlogger.debug("rasa.privacy_manager.stop_schedulers")
134
+ self.can_consume_from_tracker_queue = False
135
+ self.background_scheduler.shutdown(wait=False)
136
+
137
+ def run(self, tracker: "DialogueStateTracker") -> None:
138
+ self.tracker_queue.put(tracker)
139
+
140
+ def process(
141
+ self, tracker: "DialogueStateTracker", process_all: bool = False
142
+ ) -> None:
143
+ """Process the tracker to identify and anonymize sensitive information.
144
+
145
+ Args:
146
+ tracker: The tracker to process.
147
+ process_all: If True, process all events in the tracker.
148
+ """
149
+ events = self.process_events(tracker, process_all=process_all)
150
+ events_to_stream = events if events else tracker.events
151
+ self.stream_events(events_to_stream, tracker.sender_id)
152
+
153
+ def process_events(
154
+ self, tracker: DialogueStateTracker, process_all: bool = False
155
+ ) -> List[Event]:
156
+ """Anonymize tracker events."""
157
+ if (latest_message := self._get_latest_user_message(tracker)) is None:
158
+ return []
159
+
160
+ processed_events = list(tracker.events)
161
+ prior_sensitive_slot_events: List[Event] = []
162
+
163
+ if not process_all:
164
+ additional_splitting_conditions = {
165
+ TEXT_KEY: latest_message.text,
166
+ "timestamp": latest_message.timestamp,
167
+ }
168
+
169
+ resulting_events = split_events(
170
+ processed_events,
171
+ UserUttered,
172
+ additional_splitting_conditions=additional_splitting_conditions,
173
+ include_splitting_event=True,
174
+ )
175
+
176
+ processed_events = resulting_events[1]
177
+ prior_events = resulting_events[0]
178
+ prior_tracker = DialogueStateTracker.from_events(
179
+ sender_id=tracker.sender_id, evts=prior_events
180
+ )
181
+ prior_sensitive_slot_events = [
182
+ event
183
+ for event in prior_tracker.applied_events()
184
+ if isinstance(event, SlotSet)
185
+ and event.key in self.config.anonymization_rules # type: ignore[union-attr]
186
+ ]
187
+
188
+ return self.privacy_filter.anonymize( # type: ignore[union-attr]
189
+ processed_events, prior_sensitive_slot_events
190
+ )
191
+
192
+ def stream_events(
193
+ self,
194
+ anonymized_events: List[Event],
195
+ sender_id: str,
196
+ ) -> None:
197
+ """Stream anonymized events to the event broker."""
198
+ if not self.event_brokers:
199
+ structlogger.debug(
200
+ "rasa.privacy_manager.no_event_broker_configured",
201
+ )
202
+ return None
203
+
204
+ for event in anonymized_events:
205
+ body = {"sender_id": sender_id}
206
+ body.update(event.as_dict())
207
+ for broker in self.event_brokers:
208
+ broker.publish(body)
209
+
210
+ return None
211
+
212
+ def validate_sensitive_slots_in_domain(self, domain: "Domain") -> None:
213
+ """Validate the sensitive slots defined in the privacy config against the domain.""" # noqa: E501
214
+ if not self.config:
215
+ structlogger.debug(
216
+ "rasa.privacy_manager.no_sensitive_slots_configured",
217
+ )
218
+ return None
219
+
220
+ # we need to set the domain in the tracker store
221
+ # to prevent errors being raised about slots not found in the domain
222
+ # during the background jobs
223
+ self.tracker_store.domain = domain
224
+ sensitive_slots = list(self.config.anonymization_rules.keys())
225
+ return validate_sensitive_slots(sensitive_slots, domain)
226
+
227
+ def _consumer_queue(self) -> None:
228
+ while self.can_consume_from_tracker_queue:
229
+ try:
230
+ # Wait for 2 seconds for an event to be added to the queue
231
+ # If no event is added to the queue, continue
232
+ # This is done to avoid the scheduler to be stuck in the while loop
233
+ # when we want to stop the scheduler
234
+ tracker = self.tracker_queue.get(
235
+ timeout=self.TRACKER_QUEUE_PROCESSING_TIMEOUT_IN_SECONDS
236
+ )
237
+ self.process(tracker)
238
+ self.tracker_queue.task_done()
239
+ except queue.Empty:
240
+ continue
241
+
242
+ def _get_latest_user_message(
243
+ self, tracker: DialogueStateTracker
244
+ ) -> Optional[UserUttered]:
245
+ """Check if a tracker should be processed."""
246
+ if self.privacy_filter is None:
247
+ structlogger.debug(
248
+ "rasa.privacy_manager.no_privacy_rules_configured",
249
+ )
250
+ return None
251
+
252
+ latest_message = tracker.latest_message
253
+
254
+ if latest_message is None or not latest_message.text:
255
+ structlogger.debug(
256
+ "rasa.privacy_manager.no_user_message.skipping_processing",
257
+ )
258
+ return None
259
+
260
+ return latest_message
261
+
262
+ @staticmethod
263
+ def _has_session_been_anonymized(events: List[Event]) -> bool:
264
+ """Check if the session has already been anonymized."""
265
+ if not events:
266
+ return False
267
+ for event in reversed(events):
268
+ if (
269
+ hasattr(event, "anonymized_at")
270
+ and getattr(event, "anonymized_at") is not None
271
+ ):
272
+ return True
273
+
274
+ return False
275
+
276
+ async def _run_tracker_store_anonymization(self) -> None:
277
+ """Anonymize eligible tracker sessions in the tracker store."""
278
+ structlogger.info(
279
+ "rasa.privacy_manager.starting_tracker_store_anonymization",
280
+ triggered_by="anonymization_cron_job",
281
+ )
282
+
283
+ keys = await self.tracker_store.keys()
284
+ keys_copy = copy.deepcopy(list(keys))
285
+
286
+ for key in keys_copy:
287
+ full_tracker = await self.tracker_store.retrieve_full_tracker(key)
288
+
289
+ if not full_tracker:
290
+ structlogger.debug(
291
+ "rasa.privacy_manager.no_tracker_found_for_sender_id",
292
+ sender_id=key,
293
+ )
294
+ continue
295
+
296
+ processed_events, already_anonymized_events, uneligible_events = (
297
+ self._get_processed_events_after_anonymization(full_tracker)
298
+ )
299
+
300
+ if not processed_events:
301
+ structlogger.debug(
302
+ "rasa.privacy_manager.no_events_to_anonymize_for_tracker",
303
+ sender_id=key,
304
+ )
305
+ continue
306
+
307
+ all_events = (
308
+ already_anonymized_events + processed_events + uneligible_events
309
+ )
310
+ updated_tracker = DialogueStateTracker.from_events(
311
+ sender_id=key,
312
+ evts=all_events,
313
+ slots=full_tracker.slots.values(),
314
+ )
315
+ await self.tracker_store.delete(sender_id=key)
316
+ await self.tracker_store.save(updated_tracker)
317
+
318
+ structlogger.info(
319
+ "rasa.privacy_manager.saved_tracker_after_anonymization",
320
+ sender_id=key,
321
+ )
322
+
323
+ async def _run_tracker_store_deletion(self) -> None:
324
+ """Delete eligible tracker sessions from the tracker store."""
325
+ structlogger.info(
326
+ "rasa.privacy_manager.starting_tracker_store_deletion",
327
+ triggered_by="deletion_cron_job",
328
+ )
329
+ keys = await self.tracker_store.keys()
330
+
331
+ # Make a copy of the keys to avoid modifying the list while iterating
332
+ keys_copy = copy.deepcopy(list(keys))
333
+
334
+ for key in keys_copy:
335
+ full_tracker = await self.tracker_store.retrieve_full_tracker(key)
336
+
337
+ if not full_tracker:
338
+ structlogger.debug(
339
+ "rasa.privacy_manager.no_tracker_found_for_sender_id",
340
+ key=key,
341
+ )
342
+ continue
343
+
344
+ events_to_be_retained = self._get_events_to_be_retained_after_deletion(
345
+ full_tracker
346
+ )
347
+
348
+ await self.tracker_store.delete(sender_id=key)
349
+
350
+ if not events_to_be_retained:
351
+ continue
352
+
353
+ tracker = DialogueStateTracker.from_events(
354
+ sender_id=key,
355
+ evts=events_to_be_retained,
356
+ slots=full_tracker.slots.values(),
357
+ )
358
+ await self.tracker_store.save(tracker)
359
+
360
+ structlogger.info(
361
+ "rasa.privacy_manager.save_tracker_after_deletion",
362
+ key=key,
363
+ event_info="Saved tracker with events not scheduled "
364
+ "for deletion yet.",
365
+ )
366
+
367
+ async def _run_tracker_store_background_jobs_sequentially(self) -> None:
368
+ """Run the tracker store background jobs.
369
+
370
+ If both anonymization and deletion policies are configured,
371
+ we need to ensure that the background job timings do not
372
+ overlap to prevent race conditions when accessing the
373
+ tracker store.
374
+
375
+ The scheduler will run the anonymization job first,
376
+ and then the deletion job if the current time is past
377
+ the next scheduled time for deletion.
378
+ """
379
+ await self._run_tracker_store_anonymization()
380
+
381
+ now = datetime.datetime.now(tz=datetime.timezone.utc)
382
+ next_fire_time = (
383
+ self.config.tracker_store_settings.deletion_policy.cron.get_next_fire_time( # type: ignore[union-attr]
384
+ self.previous_fire_time_deletion,
385
+ now=now,
386
+ )
387
+ )
388
+
389
+ if next_fire_time and now >= next_fire_time:
390
+ await self._run_tracker_store_deletion()
391
+ self.previous_fire_time_deletion = next_fire_time
392
+
393
+ return None
394
+
395
+ def _add_anonymization_job(self) -> None:
396
+ wrapped_anonymization = wrap_async(self._run_tracker_store_anonymization)
397
+ self.background_scheduler.add_job(
398
+ wrapped_anonymization,
399
+ trigger=self.config.tracker_store_settings.anonymization_policy.cron, # type: ignore[union-attr]
400
+ max_instances=1,
401
+ id="anonymization_cron_job",
402
+ )
403
+
404
+ def _add_deletion_job(self) -> None:
405
+ wrapped_deletion = wrap_async(self._run_tracker_store_deletion)
406
+ self.background_scheduler.add_job(
407
+ wrapped_deletion,
408
+ trigger=self.config.tracker_store_settings.deletion_policy.cron, # type: ignore[union-attr]
409
+ max_instances=1,
410
+ id="deletion_cron_job",
411
+ )
412
+
413
+ def _add_sequential_job(self) -> None:
414
+ sequential_dispatcher = wrap_async(
415
+ self._run_tracker_store_background_jobs_sequentially
416
+ )
417
+ self.background_scheduler.add_job(
418
+ sequential_dispatcher,
419
+ trigger=self.config.tracker_store_settings.anonymization_policy.cron, # type: ignore[union-attr]
420
+ max_instances=1,
421
+ id="anonymization_and_deletion_cron_job",
422
+ )
423
+
424
+ def _configure_background_scheduler(self) -> None:
425
+ """Configure the background scheduler."""
426
+ tracker_store_settings_configured = (
427
+ self.config is not None and self.config.tracker_store_settings is not None
428
+ )
429
+ anonymization_policy = (
430
+ self.config.tracker_store_settings.anonymization_policy # type: ignore[union-attr]
431
+ if tracker_store_settings_configured
432
+ else None
433
+ )
434
+ deletion_policy = (
435
+ self.config.tracker_store_settings.deletion_policy # type: ignore[union-attr]
436
+ if tracker_store_settings_configured
437
+ else None
438
+ )
439
+
440
+ if (
441
+ tracker_store_settings_configured
442
+ and anonymization_policy is not None
443
+ and deletion_policy is not None
444
+ ):
445
+ next_fire_time_anonymization = get_next_fire_time(anonymization_policy)
446
+ next_fire_time_deletion = get_next_fire_time(deletion_policy)
447
+
448
+ # If both anonymization and deletion policies are configured
449
+ # to start on the same date,
450
+ # we need to run them sequentially to avoid race conditions
451
+ if (
452
+ next_fire_time_anonymization is not None
453
+ and next_fire_time_deletion is not None
454
+ and next_fire_time_anonymization.date()
455
+ == next_fire_time_deletion.date()
456
+ ):
457
+ self._add_sequential_job()
458
+ else:
459
+ self._add_anonymization_job()
460
+ self._add_deletion_job()
461
+
462
+ elif tracker_store_settings_configured and anonymization_policy is not None:
463
+ self._add_anonymization_job()
464
+
465
+ elif tracker_store_settings_configured and deletion_policy is not None:
466
+ self._add_deletion_job()
467
+
468
+ def _get_processed_events_after_anonymization(
469
+ self,
470
+ full_tracker: DialogueStateTracker,
471
+ ) -> Tuple[List[Event], List[Event], List[Event]]:
472
+ """Get processed events after anonymization job."""
473
+ multiple_tracker_sessions = (
474
+ rasa.shared.core.trackers.get_trackers_for_conversation_sessions(
475
+ full_tracker
476
+ )
477
+ )
478
+
479
+ processed_events = []
480
+ already_anonymized_events = []
481
+ uneligible_events = []
482
+
483
+ for session in multiple_tracker_sessions:
484
+ has_session_been_anonymized = self._has_session_been_anonymized(
485
+ list(session.events)
486
+ )
487
+
488
+ if has_session_been_anonymized:
489
+ structlogger.debug(
490
+ "rasa.privacy_manager.session_already_anonymized",
491
+ session_id=session.sender_id,
492
+ )
493
+ already_anonymized_events.extend(list(session.events))
494
+ continue
495
+
496
+ current_time = time.time()
497
+
498
+ last_event_timestamp = (
499
+ str(datetime.datetime.fromtimestamp(session.events[-1].timestamp))
500
+ if session.events
501
+ else "N/A"
502
+ )
503
+
504
+ if session.events and current_time - session.events[-1].timestamp > (
505
+ self.user_chat_inactivity_in_minutes * 60
506
+ + self.config.tracker_store_settings.anonymization_policy.min_after_session_end # type: ignore[union-attr] # noqa: E501
507
+ * 60
508
+ ):
509
+ structlogger.info(
510
+ "rasa.privacy_manager.anonymizing_tracker_session",
511
+ key=session.sender_id,
512
+ last_event_timestamp=last_event_timestamp,
513
+ triggered_by="anonymization_cron_job",
514
+ )
515
+ tracker = DialogueStateTracker.from_events(
516
+ session.sender_id, session.events
517
+ )
518
+ events = self.process_events(tracker, process_all=True)
519
+ processed_events.extend(events)
520
+ else:
521
+ # If the session is not valid for anonymization,
522
+ # we still want to write them back to the tracker store
523
+ events = list(session.events)
524
+ uneligible_events.extend(events)
525
+ structlogger.debug(
526
+ "rasa.privacy_manager.session_not_valid_for_anonymization",
527
+ key=session.sender_id,
528
+ session_id=session.sender_id,
529
+ last_event_timestamp=last_event_timestamp,
530
+ )
531
+ return processed_events, already_anonymized_events, uneligible_events
532
+
533
+ def _get_events_to_be_retained_after_deletion(
534
+ self, full_tracker: DialogueStateTracker
535
+ ) -> List[Event]:
536
+ """Get the events to be retained after deletion."""
537
+ multiple_tracker_sessions = (
538
+ rasa.shared.core.trackers.get_trackers_for_conversation_sessions(
539
+ full_tracker
540
+ )
541
+ )
542
+ events_to_be_retained: List[Event] = []
543
+ for session in multiple_tracker_sessions:
544
+ current_time = time.time()
545
+ if session.events and (
546
+ current_time - session.events[-1].timestamp
547
+ <= (
548
+ self.user_chat_inactivity_in_minutes * 60
549
+ + self.config.tracker_store_settings.deletion_policy.min_after_session_end # type: ignore[union-attr] # noqa: E501
550
+ * 60
551
+ )
552
+ ):
553
+ events_to_be_retained.extend(session.events)
554
+ else:
555
+ last_event_timestamp = (
556
+ str(datetime.datetime.fromtimestamp(session.events[-1].timestamp))
557
+ if session.events
558
+ else "N/A"
559
+ )
560
+
561
+ structlogger.info(
562
+ "rasa.privacy_manager.tracker_session_scheduled_for_deletion",
563
+ key=full_tracker.sender_id,
564
+ last_event_timestamp=last_event_timestamp,
565
+ triggered_by="deletion_cron_job",
566
+ )
567
+
568
+ return events_to_be_retained
569
+
570
+
571
+ def get_next_fire_time(
572
+ privacy_policy: PrivacyPolicy,
573
+ ) -> Optional[datetime.datetime]:
574
+ """Get the next fire time for the privacy policy."""
575
+ now = datetime.datetime.now(tz=datetime.timezone.utc)
576
+ return privacy_policy.cron.get_next_fire_time(None, now=now)
rasa/server.py CHANGED
@@ -44,6 +44,7 @@ import rasa.utils.endpoints
44
44
  import rasa.utils.io
45
45
  from rasa.constants import MINIMUM_COMPATIBLE_VERSION
46
46
  from rasa.core.agent import Agent
47
+ from rasa.core.available_endpoints import AvailableEndpoints
47
48
  from rasa.core.channels.channel import (
48
49
  CollectingOutputChannel,
49
50
  OutputChannel,
@@ -52,7 +53,6 @@ from rasa.core.channels.channel import (
52
53
  from rasa.core.constants import DEFAULT_RESPONSE_TIMEOUT
53
54
  from rasa.core.persistor import parse_remote_storage
54
55
  from rasa.core.test import test
55
- from rasa.core.utils import AvailableEndpoints
56
56
  from rasa.nlu.emulators.emulator import Emulator
57
57
  from rasa.nlu.emulators.no_emulator import NoEmulator
58
58
  from rasa.nlu.test import CVEvaluationResult
@@ -75,7 +75,9 @@ from rasa.shared.core.training_data.story_writer.yaml_story_writer import (
75
75
  YAMLStoryWriter,
76
76
  )
77
77
  from rasa.shared.exceptions import RasaException, YamlException
78
- from rasa.shared.importers.importer import TrainingDataImporter
78
+ from rasa.shared.importers.importer import (
79
+ TrainingDataImporter,
80
+ )
79
81
  from rasa.shared.nlu.training_data.formats import RasaYAMLReader
80
82
  from rasa.shared.utils.schemas.events import EVENTS_SCHEMA
81
83
  from rasa.shared.utils.yaml import validate_training_data
@@ -797,6 +799,25 @@ def create_app(
797
799
  f"An unexpected error occurred. Error: {e}",
798
800
  )
799
801
 
802
+ @app.delete("/conversations/<conversation_id:path>/tracker")
803
+ @requires_auth(app, auth_token)
804
+ @ensure_loaded_agent(app)
805
+ @ensure_conversation_exists()
806
+ async def delete_tracker(request: Request, conversation_id: Text) -> HTTPResponse:
807
+ """Delete a conversation's tracker."""
808
+ try:
809
+ async with app.ctx.agent.lock_store.lock(conversation_id):
810
+ await app.ctx.agent.tracker_store.delete(conversation_id)
811
+ logger.info(f"Tracker for conversation '{conversation_id}' deleted.")
812
+ return response.empty(status=HTTPStatus.NO_CONTENT)
813
+ except Exception as e:
814
+ logger.debug(traceback.format_exc())
815
+ raise ErrorResponse(
816
+ HTTPStatus.INTERNAL_SERVER_ERROR,
817
+ "ConversationError",
818
+ f"An unexpected error occurred. Error: {e}",
819
+ )
820
+
800
821
  @app.post("/conversations/<conversation_id:path>/tracker/events")
801
822
  @requires_auth(app, auth_token)
802
823
  @ensure_loaded_agent(app)
rasa/shared/constants.py CHANGED
@@ -144,6 +144,7 @@ DEFAULT_ACTIONS_PATH = "actions"
144
144
  DEFAULT_MODELS_PATH = "models"
145
145
  DEFAULT_CONVERTED_DATA_PATH = "converted_data"
146
146
  DEFAULT_DATA_PATH = "data"
147
+ DEFAULT_PROMPTS_PATH = "prompts"
147
148
  DEFAULT_RESULTS_PATH = "results"
148
149
  DEFAULT_NLU_RESULTS_PATH = "nlu_comparison_results"
149
150
  DEFAULT_CORE_SUBDIRECTORY_NAME = "core"
@@ -197,7 +198,10 @@ PROVIDER_CONFIG_KEY = "provider"
197
198
  REQUEST_TIMEOUT_CONFIG_KEY = "request_timeout" # deprecated
198
199
  TIMEOUT_CONFIG_KEY = "timeout"
199
200
 
201
+ LOGIT_BIAS_CONFIG_KEY = "logit_bias"
202
+ MAX_RETRIES_CONFIG_KEY = "max_retries"
200
203
  TEMPERATURE_CONFIG_KEY = "temperature"
204
+ MAX_COMPLETION_TOKENS_CONFIG_KEY = "max_completion_tokens"
201
205
  MAX_TOKENS_CONFIG_KEY = "max_tokens"
202
206
 
203
207
  DEPLOYMENT_NAME_CONFIG_KEY = "deployment_name"
@@ -292,6 +296,7 @@ CONTEXT = "context"
292
296
 
293
297
  RASA_PATTERN_INTERNAL_ERROR = "pattern_internal_error"
294
298
  RASA_PATTERN_HUMAN_HANDOFF = "pattern_human_handoff"
299
+ RASA_PATTERN_CHITCHAT = "pattern_chitchat"
295
300
 
296
301
  RASA_INTERNAL_ERROR_PREFIX = "rasa_internal_error_"
297
302
  RASA_PATTERN_INTERNAL_ERROR_DEFAULT = RASA_INTERNAL_ERROR_PREFIX + "default"
@@ -311,6 +316,9 @@ RASA_PATTERN_CANNOT_HANDLE_NOT_SUPPORTED = (
311
316
  RASA_PATTERN_CANNOT_HANDLE_INVALID_INTENT = (
312
317
  RASA_PATTERN_CANNOT_HANDLE_PREFIX + "invalid_intent"
313
318
  )
319
+ RASA_PATTERN_CANNOT_HANDLE_NO_RELEVANT_ANSWER = (
320
+ RASA_PATTERN_CANNOT_HANDLE_PREFIX + "no_relevant_answer"
321
+ )
314
322
 
315
323
  ROUTE_TO_CALM_SLOT = "route_session_to_calm"
316
324
 
@@ -339,3 +347,13 @@ ROLE_SYSTEM = "system"
339
347
  # Used for key values in ValidateSlotPatternFlowStackFrame
340
348
  REFILL_UTTER = "refill_utter"
341
349
  REJECTIONS = "rejections"
350
+
351
+ # Constants for extractive search FAQ parsing (QA pairs from input documents)
352
+ FAQ_DOCUMENT_METADATA_TITLE = "title"
353
+ FAQ_DOCUMENT_METADATA_ANSWER = "answer"
354
+ FAQ_DOCUMENT_METADATA_TYPE = "type"
355
+ DOCUMENT_TYPE_FAQ = "faq"
356
+ FAQ_INPUT_DATA_QUESTION_LINE_PREFIX = "Q:"
357
+ FAQ_INPUT_DATA_ANSWER_LINE_PREFIX = "A:"
358
+ FAQ_DOCUMENT_ENTRY_SEPARATOR = "\n\n"
359
+ FAQ_DOCUMENT_LINE_SEPARATOR = "\n"
@@ -1,4 +1,3 @@
1
- import copy
2
1
  import re
3
2
  from typing import List, Optional
4
3
 
@@ -35,9 +34,7 @@ class CommandPayloadReader:
35
34
 
36
35
  matches = CommandPayloadReader.find_matches(user_text)
37
36
  if not matches:
38
- structlogger.warning(
39
- "message.parsing.failed", user_text=copy.deepcopy(user_text)
40
- )
37
+ structlogger.warning("message.parsing.failed")
41
38
  return message
42
39
 
43
40
  return CommandPayloadReader.extract_commands_from_pattern_matches(
@@ -110,7 +107,6 @@ class CommandPayloadReader:
110
107
  if user_text.count("=") > MAX_NUMBER_OF_SLOTS:
111
108
  structlogger.warning(
112
109
  "too.many.slots",
113
- user_text=copy.deepcopy(user_text),
114
110
  slot_limit=10,
115
111
  )
116
112
  return True