rasa-pro 3.14.0a16__py3-none-any.whl → 3.14.0a18__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 (297) hide show
  1. rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +156 -105
  2. rasa/builder/copilot/telemetry.py +35 -19
  3. rasa/builder/jobs.py +15 -5
  4. rasa/builder/main.py +5 -14
  5. rasa/builder/models.py +7 -7
  6. rasa/builder/project_generator.py +130 -23
  7. rasa/builder/service.py +34 -18
  8. rasa/builder/template_cache.py +9 -184
  9. rasa/cli/project_templates/basic/README.md +23 -0
  10. rasa/cli/project_templates/basic/actions/actions.md +10 -0
  11. rasa/cli/project_templates/basic/config.yml +4 -4
  12. rasa/cli/project_templates/basic/data/data.md +5 -6
  13. rasa/cli/project_templates/basic/domain/domain.md +7 -5
  14. rasa/cli/project_templates/basic/domain/general/show_faqs.yml +1 -1
  15. rasa/cli/project_templates/basic/endpoints.yml +1 -1
  16. rasa/cli/project_templates/finance/README.md +26 -0
  17. rasa/cli/project_templates/finance/actions/__init__.py +0 -46
  18. rasa/cli/project_templates/finance/actions/accounts/check_balance.py +18 -0
  19. rasa/cli/project_templates/finance/actions/actions.md +15 -0
  20. rasa/cli/project_templates/finance/actions/{transfers/action_process_immediate_payment.py → cards/check_that_card_exists.py} +6 -3
  21. rasa/cli/project_templates/finance/actions/cards/list_cards.py +22 -0
  22. rasa/cli/project_templates/finance/actions/contacts/__init__.py +0 -0
  23. rasa/cli/project_templates/finance/actions/contacts/add_contact.py +30 -0
  24. rasa/cli/project_templates/finance/actions/contacts/list_contacts.py +22 -0
  25. rasa/cli/project_templates/finance/actions/contacts/remove_contact.py +35 -0
  26. rasa/cli/project_templates/finance/actions/db.py +117 -0
  27. rasa/cli/project_templates/finance/actions/transfers/check_transfer_funds.py +27 -0
  28. rasa/cli/project_templates/finance/actions/transfers/check_transfer_limit.py +36 -0
  29. rasa/cli/project_templates/finance/actions/transfers/execute_recurrent_payment.py +20 -0
  30. rasa/cli/project_templates/finance/actions/transfers/execute_transfer.py +45 -0
  31. rasa/cli/project_templates/finance/actions/transfers/list_transactions.py +32 -0
  32. rasa/cli/project_templates/finance/config.yml +6 -0
  33. rasa/cli/project_templates/finance/credentials.yml +7 -6
  34. rasa/cli/project_templates/finance/data/accounts/check_balance.yml +3 -4
  35. rasa/cli/project_templates/finance/data/accounts/download_statements.yml +26 -0
  36. rasa/cli/project_templates/finance/data/bills/bill_pay_reminder.yml +25 -0
  37. rasa/cli/project_templates/finance/data/cards/activate_card.yml +35 -0
  38. rasa/cli/project_templates/finance/data/cards/block_card.yml +37 -58
  39. rasa/cli/project_templates/finance/data/cards/list_cards.yml +14 -0
  40. rasa/cli/project_templates/finance/data/cards/replace_card.yml +16 -0
  41. rasa/cli/project_templates/finance/data/cards/replace_eligible_card.yml +29 -0
  42. rasa/cli/project_templates/finance/data/contacts/add_contact.yml +33 -0
  43. rasa/cli/project_templates/finance/data/contacts/list_contacts.yml +14 -0
  44. rasa/cli/project_templates/finance/data/contacts/remove_contact.yml +31 -0
  45. rasa/cli/project_templates/finance/data/data.md +14 -0
  46. rasa/cli/project_templates/finance/data/general/agent_details.yml +6 -0
  47. rasa/cli/project_templates/finance/data/general/hello.yml +1 -2
  48. rasa/cli/project_templates/finance/data/general/help.yml +2 -2
  49. rasa/cli/project_templates/finance/data/general/human_handoff.yml +1 -1
  50. rasa/cli/project_templates/finance/data/transfers/check_transfer_limit.yml +18 -0
  51. rasa/cli/project_templates/finance/data/transfers/list_transactions.yml +46 -0
  52. rasa/cli/project_templates/finance/data/transfers/move_money_between_accounts.yml +51 -0
  53. rasa/cli/project_templates/finance/data/transfers/transfer_money.yml +29 -62
  54. rasa/cli/project_templates/finance/data/transfers/transfer_money_to_a_third_party.yml +175 -0
  55. rasa/cli/project_templates/finance/db/cards.json +18 -0
  56. rasa/cli/project_templates/finance/db/contacts.json +10 -0
  57. rasa/cli/project_templates/finance/db/my_account.json +6 -0
  58. rasa/cli/project_templates/finance/db/transactions.json +22 -0
  59. rasa/cli/project_templates/finance/docs/docs.md +8 -0
  60. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/account_features/budgeting_analytics.txt +22 -0
  61. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/account_features/multi_currency_accounts.txt +19 -0
  62. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/account_features/premium_benefits.txt +19 -0
  63. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/card_management/contactless_limits.txt +16 -0
  64. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/card_management/freeze_unfreeze_card.txt +16 -0
  65. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/card_management/lost_stolen_card.txt +19 -0
  66. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/money_transfers/instant_payments.txt +19 -0
  67. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/money_transfers/international_transfers.txt +19 -0
  68. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/security_fraud/fraud_protection.txt +22 -0
  69. rasa/cli/project_templates/finance/docs/fenlo_banking_faq/security_fraud/secure_payments.txt +22 -0
  70. rasa/cli/project_templates/finance/domain/_system/patterns/pattern_session_start.yml +11 -0
  71. rasa/cli/project_templates/finance/domain/accounts/check_balance.yml +9 -5
  72. rasa/cli/project_templates/finance/domain/accounts/download_statements.yml +40 -0
  73. rasa/cli/project_templates/finance/domain/bills/bill_pay_reminder.yml +49 -0
  74. rasa/cli/project_templates/finance/domain/cards/activate_card.yml +24 -0
  75. rasa/cli/project_templates/finance/domain/cards/block_card.yml +33 -90
  76. rasa/cli/project_templates/finance/domain/cards/list_cards.yml +16 -0
  77. rasa/cli/project_templates/finance/domain/cards/replace_card.yml +43 -0
  78. rasa/cli/project_templates/finance/domain/cards/shared.yml +15 -0
  79. rasa/cli/project_templates/finance/domain/contacts/add_contact.yml +37 -0
  80. rasa/cli/project_templates/finance/domain/contacts/list_contacts.yml +16 -0
  81. rasa/cli/project_templates/finance/domain/contacts/remove_contact.yml +32 -0
  82. rasa/cli/project_templates/finance/domain/domain.md +18 -0
  83. rasa/cli/project_templates/finance/domain/general/_shared.yml +53 -0
  84. rasa/cli/project_templates/finance/domain/general/agent_details.yml +31 -0
  85. rasa/cli/project_templates/finance/domain/general/cannot_handle.yml +5 -2
  86. rasa/cli/project_templates/finance/domain/general/feedback.yml +0 -3
  87. rasa/cli/project_templates/finance/domain/general/human_handoff.yml +7 -3
  88. rasa/cli/project_templates/finance/domain/general/welcome.yml +5 -2
  89. rasa/cli/project_templates/finance/domain/transfers/check_transfer_limit.yml +32 -0
  90. rasa/cli/project_templates/finance/domain/transfers/list_transactions.yml +44 -0
  91. rasa/cli/project_templates/finance/domain/transfers/shared.yml +17 -0
  92. rasa/cli/project_templates/finance/domain/transfers/transfer_money.yml +203 -61
  93. rasa/cli/project_templates/finance/endpoints.yml +4 -3
  94. rasa/cli/project_templates/finance/prompts/rephraser_demo_personality_prompt.jinja2 +31 -12
  95. rasa/cli/project_templates/telco/README.md +25 -0
  96. rasa/cli/project_templates/telco/actions/actions.md +12 -0
  97. rasa/cli/project_templates/telco/config.yml +4 -4
  98. rasa/cli/project_templates/telco/data/data.md +11 -0
  99. rasa/cli/project_templates/telco/docs/docs.md +3 -0
  100. rasa/cli/project_templates/telco/domain/domain.md +13 -0
  101. rasa/cli/project_templates/telco/endpoints.yml +1 -1
  102. rasa/cli/project_templates/telco/prompts/rephraser_demo_personality_prompt.jinja2 +1 -1
  103. rasa/core/brokers/broker.py +1 -1
  104. rasa/core/brokers/kafka.py +52 -8
  105. rasa/core/channels/inspector/dist/assets/{arc-460861ce.js → arc-35222594.js} +1 -1
  106. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-16c993e0.js → blockDiagram-38ab4fdb-a0efbfd3.js} +1 -1
  107. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-488337d7.js → c4Diagram-3d4e48cf-0584c0f2.js} +1 -1
  108. rasa/core/channels/inspector/dist/assets/channel-8e08bed9.js +1 -0
  109. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-b08e53a8.js → classDiagram-70f12bd4-39f40dbe.js} +1 -1
  110. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-b73f5a83.js → classDiagram-v2-f2320105-1ad755f3.js} +1 -1
  111. rasa/core/channels/inspector/dist/assets/clone-78c82dea.js +1 -0
  112. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-0210a219.js → createText-2e5e7dd3-b0f4f0fe.js} +1 -1
  113. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-28df7099.js → edges-e0da2a9e-9039bff9.js} +1 -1
  114. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-9fbf4a58.js → erDiagram-9861fffd-65c9b127.js} +1 -1
  115. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-fa691f62.js → flowDb-956e92f1-4f08b38e.js} +1 -1
  116. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-ca907b67.js → flowDiagram-66a62f08-e95c362a.js} +1 -1
  117. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-2b08f601.js +1 -0
  118. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-c10945f2.js → flowchart-elk-definition-4a651766-703c3015.js} +1 -1
  119. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-9d49a75a.js → ganttDiagram-c361ad54-699328ea.js} +1 -1
  120. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-9aa698ac.js → gitGraphDiagram-72cf32ee-04cf4b05.js} +1 -1
  121. rasa/core/channels/inspector/dist/assets/{graph-3ab38d50.js → graph-ee94449e.js} +1 -1
  122. rasa/core/channels/inspector/dist/assets/{index-3862675e-6edac98f.js → index-3862675e-940162b4.js} +1 -1
  123. rasa/core/channels/inspector/dist/assets/{index-61128091.js → index-c941dcb3.js} +3 -3
  124. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-21baff85.js → infoDiagram-f8f76790-c79c2866.js} +1 -1
  125. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-4a6c7e98.js → journeyDiagram-49397b02-84489d30.js} +1 -1
  126. rasa/core/channels/inspector/dist/assets/{layout-4beae36e.js → layout-a9aa9858.js} +1 -1
  127. rasa/core/channels/inspector/dist/assets/{line-633b638e.js → line-eb73cf26.js} +1 -1
  128. rasa/core/channels/inspector/dist/assets/{linear-22d77d65.js → linear-b3399f9a.js} +1 -1
  129. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-f219ef43.js → mindmap-definition-fc14e90a-b095bf1a.js} +1 -1
  130. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-c7e1cafb.js → pieDiagram-8a3498a8-07644b66.js} +1 -1
  131. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-045e49b4.js → quadrantDiagram-120e2f19-573a3f9c.js} +1 -1
  132. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-22485cb9.js → requirementDiagram-deff3bca-d457e1e1.js} +1 -1
  133. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-281c3da2.js → sankeyDiagram-04a897e0-9d26e1a2.js} +1 -1
  134. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-a3dd10e0.js → sequenceDiagram-704730f1-3a9cde10.js} +1 -1
  135. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-61bd6eb2.js → stateDiagram-587899a1-4f3e8cec.js} +1 -1
  136. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-deead491.js → stateDiagram-v2-d93cdb3a-e617e5bf.js} +1 -1
  137. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-1b10e104.js → styles-6aaf32cf-eab30d2f.js} +1 -1
  138. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-b1e18e58.js → styles-9a916d00-09994be2.js} +1 -1
  139. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-956c3492.js → styles-c10674c1-b7110364.js} +1 -1
  140. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-e13f753d.js → svgDrawCommon-08f97a94-3ebc92ad.js} +1 -1
  141. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-e568acd2.js → timeline-definition-85554ec2-7d13d2f2.js} +1 -1
  142. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-8b7e27fc.js → xychartDiagram-e933f94c-488385e1.js} +1 -1
  143. rasa/core/channels/inspector/dist/index.html +1 -1
  144. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +14 -0
  145. rasa/core/channels/studio_chat.py +7 -0
  146. rasa/core/channels/voice_stream/asr/asr_event.py +1 -1
  147. rasa/core/channels/voice_stream/asr/azure.py +6 -3
  148. rasa/core/channels/voice_stream/asr/deepgram.py +1 -1
  149. rasa/core/channels/voice_stream/audiocodes.py +3 -0
  150. rasa/core/channels/voice_stream/browser_audio.py +53 -3
  151. rasa/core/channels/voice_stream/genesys.py +2 -1
  152. rasa/core/channels/voice_stream/jambonz.py +9 -1
  153. rasa/core/channels/voice_stream/twilio_media_streams.py +16 -0
  154. rasa/core/channels/voice_stream/voice_channel.py +61 -0
  155. rasa/core/concurrent_lock_store.py +66 -16
  156. rasa/core/iam_credentials_providers/aws_iam_credentials_providers.py +76 -1
  157. rasa/core/iam_credentials_providers/credentials_provider_protocol.py +1 -1
  158. rasa/core/lock_store.py +41 -7
  159. rasa/model_manager/warm_rasa_process.py +13 -1
  160. rasa/shared/nlu/training_data/schemas/responses.yml +3 -0
  161. rasa/version.py +1 -1
  162. {rasa_pro-3.14.0a16.dist-info → rasa_pro-3.14.0a18.dist-info}/METADATA +5 -3
  163. {rasa_pro-3.14.0a16.dist-info → rasa_pro-3.14.0a18.dist-info}/RECORD +166 -228
  164. rasa/cli/project_templates/finance/actions/accounts/action_ask_account.py +0 -47
  165. rasa/cli/project_templates/finance/actions/accounts/action_check_balance.py +0 -40
  166. rasa/cli/project_templates/finance/actions/action_session_start.py +0 -74
  167. rasa/cli/project_templates/finance/actions/cards/action_ask_card.py +0 -48
  168. rasa/cli/project_templates/finance/actions/cards/action_check_card_existence.py +0 -36
  169. rasa/cli/project_templates/finance/actions/cards/action_update_card_status.py +0 -54
  170. rasa/cli/project_templates/finance/actions/database.py +0 -277
  171. rasa/cli/project_templates/finance/actions/transfers/action_add_payee.py +0 -52
  172. rasa/cli/project_templates/finance/actions/transfers/action_ask_account_from.py +0 -51
  173. rasa/cli/project_templates/finance/actions/transfers/action_check_payee_existence.py +0 -40
  174. rasa/cli/project_templates/finance/actions/transfers/action_check_sufficient_funds.py +0 -40
  175. rasa/cli/project_templates/finance/actions/transfers/action_list_payees.py +0 -46
  176. rasa/cli/project_templates/finance/actions/transfers/action_remove_payee.py +0 -49
  177. rasa/cli/project_templates/finance/actions/transfers/action_schedule_payment.py +0 -19
  178. rasa/cli/project_templates/finance/actions/transfers/action_validate_payment_date.py +0 -36
  179. rasa/cli/project_templates/finance/csvs/accounts.csv +0 -8
  180. rasa/cli/project_templates/finance/csvs/advisors.csv +0 -7
  181. rasa/cli/project_templates/finance/csvs/appointments.csv +0 -211
  182. rasa/cli/project_templates/finance/csvs/branches.csv +0 -10
  183. rasa/cli/project_templates/finance/csvs/cards.csv +0 -11
  184. rasa/cli/project_templates/finance/csvs/payees.csv +0 -11
  185. rasa/cli/project_templates/finance/csvs/transactions.csv +0 -71
  186. rasa/cli/project_templates/finance/csvs/users.csv +0 -4
  187. rasa/cli/project_templates/finance/data/cards/select_card.yml +0 -12
  188. rasa/cli/project_templates/finance/data/general/bot_identity.yml +0 -6
  189. rasa/cli/project_templates/finance/data/system/patterns/pattern_chitchat.yml +0 -5
  190. rasa/cli/project_templates/finance/data/system/source/accounts.json +0 -51
  191. rasa/cli/project_templates/finance/data/system/source/advisors.json +0 -44
  192. rasa/cli/project_templates/finance/data/system/source/appointments.json +0 -1474
  193. rasa/cli/project_templates/finance/data/system/source/branches.json +0 -47
  194. rasa/cli/project_templates/finance/data/system/source/cards.json +0 -72
  195. rasa/cli/project_templates/finance/data/system/source/payees.json +0 -74
  196. rasa/cli/project_templates/finance/data/system/source/transactions.json +0 -492
  197. rasa/cli/project_templates/finance/data/system/source/users.json +0 -29
  198. rasa/cli/project_templates/finance/data/transfers/add_payee.yml +0 -29
  199. rasa/cli/project_templates/finance/data/transfers/list_payees.yml +0 -5
  200. rasa/cli/project_templates/finance/data/transfers/remove_payee.yml +0 -21
  201. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/consequences_of_blocking_card.txt +0 -8
  202. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/reasons_to_block_card.txt +0 -8
  203. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/recovering_from_card_fraud.txt +0 -8
  204. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/tips_for_card_security.txt +0 -8
  205. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/block_card/what_to_do_if_card_is_lost.txt +0 -8
  206. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/account_balance_security.txt +0 -7
  207. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/common_balance_inquiries.txt +0 -8
  208. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/methods_to_check_balance.txt +0 -8
  209. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/understanding_balance_updates.txt +0 -8
  210. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/check_balance/what_to_do_if_balance_is_incorrect.txt +0 -8
  211. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/benefits_of_authorised_payees.txt +0 -8
  212. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/common_issues_with_payees.txt +0 -8
  213. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/general_payee_information.txt +0 -8
  214. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/payee_management_tips.txt +0 -8
  215. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/manage_payees/understanding_payee_types.txt +0 -8
  216. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/common_transfer_errors.txt +0 -8
  217. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/fees_for_transfers.txt +0 -8
  218. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/general_transfer_information.txt +0 -8
  219. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/security_tips_for_transfers.txt +0 -8
  220. rasa/cli/project_templates/finance/docs/bank_of_rasa_faq/transfer_money/transfer_processing_times.txt +0 -8
  221. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part1.txt +0 -50
  222. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part10.txt +0 -50
  223. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part11.txt +0 -48
  224. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part12.txt +0 -50
  225. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part13.txt +0 -50
  226. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part14.txt +0 -47
  227. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part15.txt +0 -50
  228. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part16.txt +0 -50
  229. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part17.txt +0 -47
  230. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part18.txt +0 -50
  231. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part19.txt +0 -50
  232. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part2.txt +0 -50
  233. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part20.txt +0 -47
  234. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part21.txt +0 -50
  235. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part22.txt +0 -50
  236. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part23.txt +0 -47
  237. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part24.txt +0 -50
  238. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part25.txt +0 -50
  239. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part26.txt +0 -47
  240. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part27.txt +0 -50
  241. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part28.txt +0 -50
  242. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part29.txt +0 -47
  243. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part3.txt +0 -47
  244. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part30.txt +0 -50
  245. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part31.txt +0 -50
  246. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part32.txt +0 -47
  247. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part33.txt +0 -50
  248. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part34.txt +0 -50
  249. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part35.txt +0 -47
  250. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part36.txt +0 -50
  251. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part37.txt +0 -50
  252. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part38.txt +0 -47
  253. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part39.txt +0 -50
  254. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part4.txt +0 -50
  255. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part40.txt +0 -50
  256. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part41.txt +0 -47
  257. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part42.txt +0 -50
  258. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part43.txt +0 -50
  259. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part44.txt +0 -47
  260. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part45.txt +0 -50
  261. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part46.txt +0 -50
  262. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part47.txt +0 -47
  263. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part48.txt +0 -50
  264. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part49.txt +0 -50
  265. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part5.txt +0 -50
  266. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part50.txt +0 -47
  267. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part51.txt +0 -50
  268. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part52.txt +0 -50
  269. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part53.txt +0 -47
  270. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part54.txt +0 -50
  271. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part55.txt +0 -50
  272. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part56.txt +0 -47
  273. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part57.txt +0 -50
  274. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part58.txt +0 -50
  275. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part59.txt +0 -47
  276. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part6.txt +0 -47
  277. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part60.txt +0 -50
  278. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part61.txt +0 -50
  279. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part7.txt +0 -50
  280. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part8.txt +0 -50
  281. rasa/cli/project_templates/finance/docs/huggingface_alpaca_dataset/questions_part9.txt +0 -47
  282. rasa/cli/project_templates/finance/domain/cards/select_card.yml +0 -12
  283. rasa/cli/project_templates/finance/domain/general/assistant_details.yml +0 -12
  284. rasa/cli/project_templates/finance/domain/general/bot_identity.yml +0 -5
  285. rasa/cli/project_templates/finance/domain/general/defaults.yml +0 -24
  286. rasa/cli/project_templates/finance/domain/general/goodbye.yml +0 -7
  287. rasa/cli/project_templates/finance/domain/general/help.yml +0 -5
  288. rasa/cli/project_templates/finance/domain/general/utils.yml +0 -13
  289. rasa/cli/project_templates/finance/domain/transfers/add_payee.yml +0 -47
  290. rasa/cli/project_templates/finance/domain/transfers/list_payees.yml +0 -4
  291. rasa/cli/project_templates/finance/domain/transfers/remove_payee.yml +0 -16
  292. rasa/core/channels/inspector/dist/assets/channel-b560a3d4.js +0 -1
  293. rasa/core/channels/inspector/dist/assets/clone-67015557.js +0 -1
  294. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-4a070961.js +0 -1
  295. {rasa_pro-3.14.0a16.dist-info → rasa_pro-3.14.0a18.dist-info}/NOTICE +0 -0
  296. {rasa_pro-3.14.0a16.dist-info → rasa_pro-3.14.0a18.dist-info}/WHEEL +0 -0
  297. {rasa_pro-3.14.0a16.dist-info → rasa_pro-3.14.0a18.dist-info}/entry_points.txt +0 -0
@@ -15,7 +15,7 @@ class NewTranscript(ASREvent):
15
15
 
16
16
  @dataclass
17
17
  class UserIsSpeaking(ASREvent):
18
- pass
18
+ text: str
19
19
 
20
20
 
21
21
  @dataclass
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  import os
3
3
  from dataclasses import dataclass
4
- from typing import Any, AsyncIterator, Dict, Optional
4
+ from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, Optional
5
5
 
6
6
  import structlog
7
7
 
@@ -15,6 +15,9 @@ from rasa.core.channels.voice_stream.audio_bytes import HERTZ, RasaAudioBytes
15
15
  from rasa.shared.constants import AZURE_SPEECH_API_KEY_ENV_VAR
16
16
  from rasa.shared.exceptions import ConnectionException
17
17
 
18
+ if TYPE_CHECKING:
19
+ from azure.cognitiveservices.speech import SpeechRecognitionEventArgs
20
+
18
21
  logger = structlog.get_logger(__name__)
19
22
 
20
23
 
@@ -43,9 +46,9 @@ class AzureASR(ASREngine[AzureASRConfig]):
43
46
  )
44
47
  self.main_loop = asyncio.get_running_loop()
45
48
 
46
- def signal_user_is_speaking(self, event: Any) -> None:
49
+ def signal_user_is_speaking(self, event: "SpeechRecognitionEventArgs") -> None:
47
50
  """Replace the azure event with a generic is speaking event."""
48
- self.fill_queue(UserIsSpeaking())
51
+ self.fill_queue(UserIsSpeaking(event.result.text))
49
52
 
50
53
  def fill_queue(self, event: Any) -> None:
51
54
  """Either puts the event or a dedicated ASR Event into the queue."""
@@ -117,7 +117,7 @@ class DeepgramASR(ASREngine[DeepgramASRConfig]):
117
117
  self.accumulated_transcript, transcript
118
118
  )
119
119
  elif transcript:
120
- return UserIsSpeaking()
120
+ return UserIsSpeaking(transcript)
121
121
  # event that comes after utterance_end_ms of no new transcript
122
122
  elif data_type == "UtteranceEnd":
123
123
  if self.accumulated_transcript:
@@ -89,6 +89,7 @@ class AudiocodesVoiceOutputChannel(VoiceOutputChannel):
89
89
  # This is an approximation, as the bot will be sent the audio chunks next
90
90
  # which are played to the user immediately.
91
91
  call_state.is_bot_speaking = True
92
+ VoiceInputChannel._cancel_silence_timeout_watcher()
92
93
 
93
94
  async def send_intermediate_marker(self, recipient_id: str) -> None:
94
95
  """Audiocodes doesn't need intermediate markers, so do nothing."""
@@ -116,6 +117,7 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
116
117
  server_url: str,
117
118
  asr_config: Dict,
118
119
  tts_config: Dict,
120
+ interruptions: Optional[Dict[str, int]] = None,
119
121
  token: Optional[Text] = None,
120
122
  ):
121
123
  mark_as_beta_feature("Audiocodes (audiocodes_stream) Channel")
@@ -123,6 +125,7 @@ class AudiocodesVoiceInputChannel(VoiceInputChannel):
123
125
  server_url=server_url,
124
126
  asr_config=asr_config,
125
127
  tts_config=tts_config,
128
+ interruptions=interruptions,
126
129
  )
127
130
  self.token = token
128
131
 
@@ -3,7 +3,9 @@ from __future__ import annotations
3
3
  import audioop
4
4
  import base64
5
5
  import json
6
+ import os
6
7
  import uuid
8
+ import wave
7
9
  from typing import Any, Awaitable, Callable, Dict, Optional, Tuple
8
10
 
9
11
  import structlog
@@ -70,10 +72,45 @@ class BrowserAudioOutputChannel(VoiceOutputChannel):
70
72
 
71
73
  class BrowserAudioInputChannel(VoiceInputChannel):
72
74
  def __init__(
73
- self, server_url: str, asr_config: Dict[str, Any], tts_config: Dict[str, Any]
75
+ self,
76
+ server_url: str,
77
+ asr_config: Dict[str, Any],
78
+ tts_config: Dict[str, Any],
79
+ recording: bool = False,
80
+ interruptions: Optional[Dict[str, int]] = None,
74
81
  ) -> None:
75
82
  """Initializes the browser audio input channel."""
76
- super().__init__(server_url, asr_config, tts_config)
83
+ super().__init__(server_url, asr_config, tts_config, interruptions)
84
+
85
+ # For debugging, recording of user audio might be useful
86
+ # to identify audio quality issues or transcription errors
87
+ self._recording_enabled = recording
88
+ self._wav_file: Optional[wave.Wave_write] = None
89
+
90
+ def _start_recording(self, call_id: str, user_id: str) -> None:
91
+ os.makedirs("recordings", exist_ok=True)
92
+ filename = f"{user_id}_{call_id}.wav"
93
+ file_path = os.path.join("recordings", filename)
94
+
95
+ if not self._recording_enabled:
96
+ return
97
+
98
+ self._wav_file = wave.open(file_path, "wb")
99
+ self._wav_file.setnchannels(1) # Mono audio
100
+ self._wav_file.setsampwidth(4) # 32-bit audio (4 bytes)
101
+ self._wav_file.setframerate(8000) # 8kHz sample rate
102
+ logger.info("voice_channel.user_audio_recording.started", file_path=file_path)
103
+
104
+ def _append_audio_to_recording(self, audio_bytes: bytes) -> None:
105
+ if self._wav_file and self._recording_enabled:
106
+ self._wav_file.writeframes(audio_bytes)
107
+
108
+ def _stop_recording(self) -> None:
109
+ """Close the recording file if it's open."""
110
+ if self._wav_file:
111
+ self._wav_file.close()
112
+ self._wav_file = None
113
+ logger.debug("voice_channel.user_audio_recording.stopped")
77
114
 
78
115
  @classmethod
79
116
  def name(cls) -> str:
@@ -94,7 +131,7 @@ class BrowserAudioInputChannel(VoiceInputChannel):
94
131
  credentials: Optional[Dict[str, Any]],
95
132
  ) -> BrowserAudioInputChannel:
96
133
  cls.validate_basic_credentials(credentials)
97
- new_creds = repack_voice_credentials(credentials)
134
+ new_creds = repack_voice_credentials(credentials or {})
98
135
  return cls(**new_creds)
99
136
 
100
137
  def map_input_message(
@@ -105,6 +142,7 @@ class BrowserAudioInputChannel(VoiceInputChannel):
105
142
  data = json.loads(message)
106
143
  if "audio" in data:
107
144
  channel_bytes = base64.b64decode(data["audio"])
145
+ self._append_audio_to_recording(channel_bytes)
108
146
  audio_bytes = self.channel_bytes_to_rasa_audio_bytes(channel_bytes)
109
147
  return NewAudioAction(audio_bytes)
110
148
  elif "marker" in data:
@@ -120,6 +158,13 @@ class BrowserAudioInputChannel(VoiceInputChannel):
120
158
  call_state.is_bot_speaking = True
121
159
  return ContinueConversationAction()
122
160
 
161
+ async def interrupt_playback(
162
+ self, ws: Websocket, call_parameters: CallParameters
163
+ ) -> None:
164
+ """Interrupt the current playback of audio."""
165
+ logger.debug("browser_audio.interrupt_playback")
166
+ await ws.send(json.dumps({"interruptPlayback": True}))
167
+
123
168
  def create_output_channel(
124
169
  self, voice_websocket: Websocket, tts_engine: TTSEngine
125
170
  ) -> VoiceOutputChannel:
@@ -142,8 +187,13 @@ class BrowserAudioInputChannel(VoiceInputChannel):
142
187
  @blueprint.websocket("/websocket") # type: ignore
143
188
  async def handle_message(request: Request, ws: Websocket) -> None:
144
189
  try:
190
+ call_parameters = await self.collect_call_parameters(ws)
191
+ if call_parameters and call_parameters.call_id:
192
+ self._start_recording(call_parameters.call_id, "local")
145
193
  await self.run_audio_streaming(on_new_message, ws)
146
194
  except Exception as e:
147
195
  logger.error("browser_audio.handle_message.error", error=e)
196
+ finally:
197
+ self._stop_recording()
148
198
 
149
199
  return blueprint
@@ -99,10 +99,11 @@ class GenesysInputChannel(VoiceInputChannel):
99
99
  server_url: str,
100
100
  asr_config: Dict,
101
101
  tts_config: Dict,
102
+ interruptions: Optional[Dict[str, int]] = None,
102
103
  api_key: Optional[Text] = None,
103
104
  client_secret: Optional[Text] = None,
104
105
  ) -> None:
105
- super().__init__(server_url, asr_config, tts_config)
106
+ super().__init__(server_url, asr_config, tts_config, interruptions)
106
107
  self.api_key = api_key
107
108
  self.client_secret = client_secret
108
109
 
@@ -81,6 +81,7 @@ class JambonzStreamInputChannel(VoiceInputChannel):
81
81
  server_url: str,
82
82
  asr_config: Dict,
83
83
  tts_config: Dict,
84
+ interruptions: Optional[Dict[str, int]] = None,
84
85
  username: Optional[Text] = None,
85
86
  password: Optional[Text] = None,
86
87
  ) -> None:
@@ -90,7 +91,7 @@ class JambonzStreamInputChannel(VoiceInputChannel):
90
91
  username: Optional username for basic auth
91
92
  password: Optional password for basic auth
92
93
  """
93
- super().__init__(server_url, asr_config, tts_config)
94
+ super().__init__(server_url, asr_config, tts_config, interruptions)
94
95
  self.username = username
95
96
  self.password = password
96
97
 
@@ -185,6 +186,13 @@ class JambonzStreamInputChannel(VoiceInputChannel):
185
186
  self.tts_cache,
186
187
  )
187
188
 
189
+ async def interrupt_playback(
190
+ self, ws: Websocket, call_parameters: CallParameters
191
+ ) -> None:
192
+ """Interrupt the current playback of audio."""
193
+ logger.debug("jambonz.interrupt_playback")
194
+ await ws.send(json.dumps({"type": "killAudio"}))
195
+
188
196
  def blueprint(
189
197
  self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
190
198
  ) -> Blueprint:
@@ -105,6 +105,7 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
105
105
  server_url: str,
106
106
  asr_config: Dict,
107
107
  tts_config: Dict,
108
+ interruptions: Optional[Dict[str, int]] = None,
108
109
  username: Optional[Text] = None,
109
110
  password: Optional[Text] = None,
110
111
  ):
@@ -112,6 +113,7 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
112
113
  server_url=server_url,
113
114
  asr_config=asr_config,
114
115
  tts_config=tts_config,
116
+ interruptions=interruptions,
115
117
  )
116
118
  self.username = username
117
119
  self.password = password
@@ -195,6 +197,20 @@ class TwilioMediaStreamsInputChannel(VoiceInputChannel):
195
197
  self.tts_cache,
196
198
  )
197
199
 
200
+ async def interrupt_playback(
201
+ self, ws: Websocket, call_parameters: CallParameters
202
+ ) -> None:
203
+ """Interrupt the current playback of audio."""
204
+ logger.debug("twilio_media_streams.interrupt_playback")
205
+ await ws.send(
206
+ json.dumps(
207
+ {
208
+ "event": "clear",
209
+ "streamSid": call_parameters.stream_id,
210
+ }
211
+ )
212
+ )
213
+
198
214
  def blueprint(
199
215
  self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
200
216
  ) -> Blueprint:
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import copy
5
+ import string
5
6
  import time
6
7
  from dataclasses import asdict, dataclass
7
8
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, List, Optional, Tuple
@@ -53,6 +54,13 @@ from rasa.utils.io import remove_emojis
53
54
  logger = structlog.get_logger(__name__)
54
55
 
55
56
  # define constants for the voice channel
57
+ DEFAULT_INTERRUPTION_MIN_WORDS = 3
58
+
59
+
60
+ @dataclass
61
+ class InterruptionConfig:
62
+ enabled: bool = False
63
+ min_words: int = DEFAULT_INTERRUPTION_MIN_WORDS
56
64
 
57
65
 
58
66
  @dataclass
@@ -270,6 +278,10 @@ class VoiceOutputChannel(OutputChannel):
270
278
  except (WebsocketClosed, ServerError):
271
279
  call_state.connection_failed = True
272
280
 
281
+ # Is the response interruptible?
282
+ allow_interruptions = kwargs.get("allow_interruptions", True)
283
+ call_state.channel_data["allow_interruptions"] = allow_interruptions
284
+
273
285
  if cached_audio_bytes:
274
286
  audio_stream = self.chunk_audio(cached_audio_bytes)
275
287
  else:
@@ -368,6 +380,7 @@ class VoiceInputChannel(InputChannel):
368
380
  server_url: str,
369
381
  asr_config: Dict,
370
382
  tts_config: Dict,
383
+ interruptions: Optional[Dict[str, Any]] = None,
371
384
  ):
372
385
  if self.requires_voice_license:
373
386
  validate_voice_license_scope()
@@ -376,12 +389,21 @@ class VoiceInputChannel(InputChannel):
376
389
  self.asr_config = asr_config
377
390
  self.tts_config = tts_config
378
391
  self.tts_cache = TTSCache(tts_config.get("cache_size", 1000))
392
+ if interruptions:
393
+ self.interruption_config = InterruptionConfig(**interruptions)
394
+ else:
395
+ self.interruption_config = InterruptionConfig()
396
+
397
+ if self.interruption_config.enabled:
398
+ mark_as_beta_feature(f"Interruption Handling in {self.name()}")
379
399
 
380
400
  logger.info(
381
401
  "voice_channel.initialized",
402
+ name=self.name(),
382
403
  server_url=self.server_url,
383
404
  asr_config=self.asr_config,
384
405
  tts_config=self.tts_config,
406
+ interruption_config=self.interruption_config,
385
407
  )
386
408
 
387
409
  def get_sender_id(self, call_parameters: CallParameters) -> str:
@@ -463,6 +485,43 @@ class VoiceInputChannel(InputChannel):
463
485
  """Map a channel input message to a voice channel action."""
464
486
  raise NotImplementedError
465
487
 
488
+ def should_interrupt(self, e: ASREvent) -> bool:
489
+ """Determine if the current ASR event should interrupt playback.
490
+ Returns True if the bot response is interruptible
491
+ And if the user spoke more than 3 words.
492
+
493
+ Arguments:
494
+ e: The ASR event to evaluate.
495
+
496
+ Returns:
497
+ True if the event should interrupt playback, False otherwise.
498
+ """
499
+ # Are interruptions are enabled for the channel?
500
+ if not self.interruption_config.enabled:
501
+ return False
502
+
503
+ # Is the bot response interruptible?
504
+ if not call_state.channel_data.get("allow_interruptions", True):
505
+ return False
506
+
507
+ # Did the user speak more than 3 words?
508
+ min_words = self.interruption_config.min_words
509
+ if isinstance(e, UserIsSpeaking):
510
+ translator = str.maketrans("", "", string.punctuation)
511
+ words = e.text.translate(translator).split()
512
+ return len(words) >= min_words
513
+ return False
514
+
515
+ async def interrupt_playback(
516
+ self, ws: Websocket, call_parameters: CallParameters
517
+ ) -> None:
518
+ """Interrupt the current playback of audio.
519
+
520
+ This function is used for interruption handling.
521
+ As not all channels support flushing bot audio buffer,
522
+ if a channel does not implement it. It has no effect."""
523
+ pass
524
+
466
525
  async def run_audio_streaming(
467
526
  self,
468
527
  on_new_message: Callable[[UserMessage], Awaitable[Any]],
@@ -598,6 +657,8 @@ class VoiceInputChannel(InputChannel):
598
657
  call_state.user_speech_start_time = time.time()
599
658
  self._cancel_silence_timeout_watcher()
600
659
  call_state.is_user_speaking = True
660
+ if self.should_interrupt(e):
661
+ await self.interrupt_playback(voice_websocket, call_parameters)
601
662
  elif isinstance(e, UserSilence):
602
663
  output_channel = self.create_output_channel(voice_websocket, tts_engine)
603
664
  message = UserMessage(
@@ -4,6 +4,7 @@ from collections import deque
4
4
  from typing import Deque, Optional, Text
5
5
 
6
6
  import structlog
7
+ from pydantic import ValidationError
7
8
 
8
9
  from rasa.core.lock import Ticket, TicketLock
9
10
  from rasa.core.lock_store import (
@@ -12,6 +13,12 @@ from rasa.core.lock_store import (
12
13
  LockError,
13
14
  LockStore,
14
15
  )
16
+ from rasa.core.redis_connection_factory import (
17
+ DeploymentMode,
18
+ RedisConfig,
19
+ RedisConnectionFactory,
20
+ )
21
+ from rasa.shared.exceptions import RasaException
15
22
  from rasa.utils.endpoints import EndpointConfig
16
23
 
17
24
  DEFAULT_REDIS_DB = 1
@@ -74,9 +81,10 @@ class ConcurrentRedisLockStore(LockStore):
74
81
  alphanumeric.
75
82
  socket_timeout - Timeout in seconds after which an exception will be raised
76
83
  in case Redis doesn't respond within `socket_timeout` seconds.
84
+ deployment_mode - Redis deployment mode: standard, cluster, or sentinel.
85
+ endpoints - List of endpoints for cluster/sentinel mode in host:port format.
86
+ sentinel_service - Sentinel service name.
77
87
  """
78
- import redis
79
-
80
88
  host = endpoint_config.kwargs.get("host", DEFAULT_HOSTNAME)
81
89
  port = endpoint_config.kwargs.get("port", DEFAULT_PORT)
82
90
  db = endpoint_config.kwargs.get("db", DEFAULT_REDIS_DB)
@@ -90,20 +98,33 @@ class ConcurrentRedisLockStore(LockStore):
90
98
  socket_timeout = endpoint_config.kwargs.get(
91
99
  "socket_timeout", DEFAULT_SOCKET_TIMEOUT_IN_SECONDS
92
100
  )
93
-
94
- self.red = redis.StrictRedis(
95
- host=host,
96
- port=int(port),
97
- db=int(db),
98
- username=username,
99
- password=password,
100
- ssl=use_ssl,
101
- ssl_certfile=ssl_certfile,
102
- ssl_keyfile=ssl_keyfile,
103
- ssl_ca_certs=ssl_ca_certs,
104
- socket_timeout=socket_timeout,
101
+ deployment_mode = endpoint_config.kwargs.get(
102
+ "deployment_mode", DeploymentMode.STANDARD.value
105
103
  )
104
+ endpoints = endpoint_config.kwargs.get("endpoints", [])
105
+ sentinel_service = endpoint_config.kwargs.get("sentinel_service")
106
106
 
107
+ try:
108
+ redis_config = RedisConfig(
109
+ host=host,
110
+ port=port,
111
+ db=db,
112
+ username=username,
113
+ password=password,
114
+ use_ssl=use_ssl,
115
+ ssl_certfile=ssl_certfile,
116
+ ssl_keyfile=ssl_keyfile,
117
+ ssl_ca_certs=ssl_ca_certs,
118
+ socket_timeout=socket_timeout,
119
+ deployment_mode=deployment_mode,
120
+ endpoints=endpoints,
121
+ sentinel_service=sentinel_service,
122
+ )
123
+ self.red = RedisConnectionFactory.create_connection(redis_config)
124
+ except ValidationError as e:
125
+ raise RasaException(f"Invalid Redis configuration: {e}")
126
+
127
+ self.deployment_mode = deployment_mode
107
128
  self.key_prefix = DEFAULT_CONCURRENT_REDIS_LOCK_STORE_KEY_PREFIX
108
129
  if key_prefix:
109
130
  structlogger.debug(
@@ -129,6 +150,32 @@ class ConcurrentRedisLockStore(LockStore):
129
150
  ),
130
151
  )
131
152
 
153
+ def _get_keys_by_pattern(self, pattern: Text) -> list:
154
+ """Get keys by pattern, using SCAN for cluster mode and KEYS for others."""
155
+ if self.deployment_mode == DeploymentMode.CLUSTER.value:
156
+ # In cluster mode, use SCAN to get keys more reliably
157
+ keys = []
158
+ cursor = 0
159
+
160
+ while True:
161
+ try:
162
+ cursor, batch_keys = self.red.scan(cursor, match=pattern, count=100)
163
+ keys.extend(batch_keys)
164
+ if cursor == 0:
165
+ break
166
+ except Exception as e:
167
+ structlogger.warning(
168
+ "concurrent_redis_lock_store._get_keys_by_pattern.scan_interrupted",
169
+ event_info=f"SCAN interrupted in cluster mode: {e}. "
170
+ f"Returning {len(keys)} keys found so far.",
171
+ )
172
+ break
173
+ else:
174
+ # Standard and sentinel modes use KEYS
175
+ keys = self.red.keys(pattern)
176
+
177
+ return keys
178
+
132
179
  def issue_ticket(
133
180
  self, conversation_id: Text, lock_lifetime: float = LOCK_LIFETIME
134
181
  ) -> int:
@@ -157,11 +204,14 @@ class ConcurrentRedisLockStore(LockStore):
157
204
  tickets: Deque[Ticket] = deque()
158
205
 
159
206
  pattern = self.key_prefix + conversation_id + ":" + "[0-9]*"
160
- redis_keys = self.red.keys(pattern)
207
+ redis_keys = self._get_keys_by_pattern(pattern)
161
208
 
162
209
  for key in redis_keys:
163
210
  serialised_ticket = self.red.get(key)
164
211
  if serialised_ticket:
212
+ # Handle bytes to string conversion for JSON parsing
213
+ if isinstance(serialised_ticket, bytes):
214
+ serialised_ticket = serialised_ticket.decode("utf-8")
165
215
  ticket = Ticket.from_dict(json.loads(serialised_ticket))
166
216
  tickets.appendleft(ticket)
167
217
 
@@ -172,7 +222,7 @@ class ConcurrentRedisLockStore(LockStore):
172
222
  def delete_lock(self, conversation_id: Text) -> None:
173
223
  """Deletes lock for conversation ID."""
174
224
  pattern = self.key_prefix + conversation_id + ":*"
175
- redis_keys = self.red.keys(pattern)
225
+ redis_keys = self._get_keys_by_pattern(pattern)
176
226
 
177
227
  if not redis_keys:
178
228
  structlogger.debug(
@@ -1,7 +1,11 @@
1
+ import os
2
+ import threading
3
+ import time
1
4
  from typing import Optional
2
5
 
3
6
  import boto3
4
7
  import structlog
8
+ from aws_msk_iam_sasl_signer import MSKAuthTokenProvider
5
9
  from botocore.exceptions import BotoCoreError
6
10
 
7
11
  from rasa.core.iam_credentials_providers.credentials_provider_protocol import (
@@ -10,6 +14,7 @@ from rasa.core.iam_credentials_providers.credentials_provider_protocol import (
10
14
  SupportedServiceType,
11
15
  TemporaryCredentials,
12
16
  )
17
+ from rasa.shared.exceptions import ConnectionException
13
18
 
14
19
  structlogger = structlog.get_logger(__name__)
15
20
 
@@ -17,7 +22,7 @@ structlogger = structlog.get_logger(__name__)
17
22
  class AWSRDSIAMCredentialsProvider(IAMCredentialsProvider):
18
23
  """Generates temporary credentials for AWS RDS using IAM roles."""
19
24
 
20
- def __init__(self, username: str, host: str, port: int):
25
+ def __init__(self, username: str, host: str, port: int) -> None:
21
26
  """Initializes the provider."""
22
27
  self.username = username
23
28
  self.host = host
@@ -52,6 +57,73 @@ class AWSRDSIAMCredentialsProvider(IAMCredentialsProvider):
52
57
  return TemporaryCredentials(auth_token=None)
53
58
 
54
59
 
60
+ class AWSMSKafkaIAMCredentialsProvider(IAMCredentialsProvider):
61
+ """Generates temporary credentials for AWS MSK using IAM roles."""
62
+
63
+ def __init__(self) -> None:
64
+ self.region = os.getenv("AWS_DEFAULT_REGION", os.getenv("AWS_REGION"))
65
+ self._token: Optional[str] = None
66
+ self._expires_at: float = 0
67
+ self.refresh_margin_seconds = 60 # Refresh 60 seconds before expiry
68
+ # ensure thread safety when refreshing token because the
69
+ # kafka client library we use (confluent-kafka) is multithreaded
70
+ self.lock = threading.Lock()
71
+
72
+ @property
73
+ def token(self) -> Optional[str]:
74
+ return self._token
75
+
76
+ @token.setter
77
+ def token(self, value: str) -> None:
78
+ self._token = value
79
+
80
+ @property
81
+ def expires_at(self) -> float:
82
+ return self._expires_at
83
+
84
+ @expires_at.setter
85
+ def expires_at(self, value: float) -> None:
86
+ self._expires_at = value
87
+
88
+ def get_credentials(self) -> TemporaryCredentials:
89
+ """Generates temporary credentials for AWS MSK."""
90
+ with self.lock:
91
+ current_time = time.time() # Current time in seconds
92
+ if (
93
+ not self.token
94
+ or current_time >= self.expires_at - self.refresh_margin_seconds
95
+ ):
96
+ try:
97
+ auth_token, expiry_ms = MSKAuthTokenProvider.generate_auth_token(
98
+ self.region
99
+ )
100
+ structlogger.debug(
101
+ "rasa.core.aws_msk_iam_credentials_provider.get_credentials",
102
+ event_info="Successfully generated AWS IAM token for "
103
+ "Kafka authentication.",
104
+ )
105
+ self.token = auth_token
106
+ self.expires_at = int(expiry_ms) / 1000 # Convert ms to seconds
107
+ return TemporaryCredentials(
108
+ auth_token=auth_token,
109
+ expiration=self.expires_at,
110
+ )
111
+ except Exception as exc:
112
+ raise ConnectionException(
113
+ f"Failed to generate AWS IAM token "
114
+ f"for MSK authentication. Original exception: {exc}"
115
+ ) from exc
116
+ else:
117
+ structlogger.debug(
118
+ "rasa.core.aws_msk_iam_credentials_provider.get_credentials",
119
+ event_info="Using cached AWS IAM token for Kafka authentication.",
120
+ )
121
+ return TemporaryCredentials(
122
+ auth_token=self.token,
123
+ expiration=self.expires_at,
124
+ )
125
+
126
+
55
127
  def create_aws_iam_credentials_provider(
56
128
  provider_input: "IAMCredentialsProviderInput",
57
129
  ) -> Optional["IAMCredentialsProvider"]:
@@ -63,4 +135,7 @@ def create_aws_iam_credentials_provider(
63
135
  port=provider_input.port,
64
136
  )
65
137
 
138
+ if provider_input.service_name == SupportedServiceType.EVENT_BROKER:
139
+ return AWSMSKafkaIAMCredentialsProvider()
140
+
66
141
  return None
@@ -16,7 +16,7 @@ class TemporaryCredentials(BaseModel):
16
16
  """Dataclass storing temporary credentials."""
17
17
 
18
18
  auth_token: Optional[str] = None
19
- expiration: Optional[int] = None
19
+ expiration: Optional[float] = None
20
20
  username: Optional[str] = None
21
21
  presigned_url: Optional[str] = None
22
22