rasa-pro 3.10.16__py3-none-any.whl → 3.11.0a1__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 (185) hide show
  1. README.md +396 -17
  2. rasa/api.py +9 -3
  3. rasa/cli/arguments/default_arguments.py +23 -2
  4. rasa/cli/arguments/run.py +15 -0
  5. rasa/cli/arguments/train.py +3 -9
  6. rasa/cli/e2e_test.py +1 -1
  7. rasa/cli/evaluate.py +1 -1
  8. rasa/cli/inspect.py +8 -4
  9. rasa/cli/llm_fine_tuning.py +12 -15
  10. rasa/cli/run.py +8 -1
  11. rasa/cli/studio/studio.py +8 -18
  12. rasa/cli/train.py +11 -53
  13. rasa/cli/utils.py +8 -10
  14. rasa/cli/x.py +1 -1
  15. rasa/constants.py +1 -1
  16. rasa/core/actions/action.py +2 -0
  17. rasa/core/actions/action_hangup.py +29 -0
  18. rasa/core/agent.py +2 -2
  19. rasa/core/brokers/kafka.py +3 -1
  20. rasa/core/brokers/pika.py +3 -1
  21. rasa/core/channels/__init__.py +8 -6
  22. rasa/core/channels/channel.py +21 -4
  23. rasa/core/channels/development_inspector.py +143 -46
  24. rasa/core/channels/inspector/README.md +1 -1
  25. rasa/core/channels/inspector/dist/assets/{arc-b6e548fe.js → arc-86942a71.js} +1 -1
  26. rasa/core/channels/inspector/dist/assets/{c4Diagram-d0fbc5ce-fa03ac9e.js → c4Diagram-d0fbc5ce-b0290676.js} +1 -1
  27. rasa/core/channels/inspector/dist/assets/{classDiagram-936ed81e-ee67392a.js → classDiagram-936ed81e-f6405f6e.js} +1 -1
  28. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-c3cb15f1-9b283fae.js → classDiagram-v2-c3cb15f1-ef61ac77.js} +1 -1
  29. rasa/core/channels/inspector/dist/assets/{createText-62fc7601-8b6fcc2a.js → createText-62fc7601-f0411e58.js} +1 -1
  30. rasa/core/channels/inspector/dist/assets/{edges-f2ad444c-22e77f4f.js → edges-f2ad444c-7dcc4f3b.js} +1 -1
  31. rasa/core/channels/inspector/dist/assets/{erDiagram-9d236eb7-60ffc87f.js → erDiagram-9d236eb7-e0c092d7.js} +1 -1
  32. rasa/core/channels/inspector/dist/assets/{flowDb-1972c806-9dd802e4.js → flowDb-1972c806-fba2e3ce.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{flowDiagram-7ea5b25a-5fa1912f.js → flowDiagram-7ea5b25a-7a70b71a.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-24a5f41a.js +1 -0
  35. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-abe16c3d-622a1fd2.js → flowchart-elk-definition-abe16c3d-00a59b68.js} +1 -1
  36. rasa/core/channels/inspector/dist/assets/{ganttDiagram-9b5ea136-e285a63a.js → ganttDiagram-9b5ea136-293c91fa.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-99d0ae7c-f237bdca.js → gitGraphDiagram-99d0ae7c-07b2d68c.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{index-2c4b9a3b-4b03d70e.js → index-2c4b9a3b-bc959fbd.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/index-3a8a5a28.js +1317 -0
  40. rasa/core/channels/inspector/dist/assets/{infoDiagram-736b4530-72a0fa5f.js → infoDiagram-736b4530-4a350f72.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{journeyDiagram-df861f2b-82218c41.js → journeyDiagram-df861f2b-af464fb7.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{layout-78cff630.js → layout-0071f036.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{line-5038b469.js → line-2f73cc83.js} +1 -1
  44. rasa/core/channels/inspector/dist/assets/{linear-c4fc4098.js → linear-f014b4cc.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{mindmap-definition-beec6740-c33c8ea6.js → mindmap-definition-beec6740-d2426fb6.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{pieDiagram-dbbf0591-a8d03059.js → pieDiagram-dbbf0591-776f01a2.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-4d7f4fd6-6a0e56b2.js → quadrantDiagram-4d7f4fd6-82e00b57.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{requirementDiagram-6fc4c22a-2dc7c7bd.js → requirementDiagram-6fc4c22a-ea13c6bb.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-8f13d901-2360fe39.js → sankeyDiagram-8f13d901-1feca7e9.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-b655622a-41b9f9ad.js → sequenceDiagram-b655622a-070c61d2.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{stateDiagram-59f0c015-0aad326f.js → stateDiagram-59f0c015-24f46263.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-2b26beab-9847d984.js → stateDiagram-v2-2b26beab-c9056051.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{styles-080da4f6-564d890e.js → styles-080da4f6-08abc34a.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{styles-3dcbcfbf-38957613.js → styles-3dcbcfbf-bc74c25a.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{styles-9c745c82-f0fc6921.js → styles-9c745c82-4e5d66de.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-4835440b-ef3c5a77.js → svgDrawCommon-4835440b-849c4517.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{timeline-definition-5b62e21b-bf3e91c1.js → timeline-definition-5b62e21b-d0fb1598.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{xychartDiagram-2b33534f-4d4026c0.js → xychartDiagram-2b33534f-04d115e2.js} +1 -1
  59. rasa/core/channels/inspector/dist/index.html +18 -17
  60. rasa/core/channels/inspector/index.html +17 -16
  61. rasa/core/channels/inspector/package.json +5 -1
  62. rasa/core/channels/inspector/src/App.tsx +117 -67
  63. rasa/core/channels/inspector/src/components/Chat.tsx +95 -0
  64. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +11 -10
  65. rasa/core/channels/inspector/src/components/DialogueStack.tsx +10 -25
  66. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +1 -1
  67. rasa/core/channels/inspector/src/helpers/formatters.test.ts +10 -0
  68. rasa/core/channels/inspector/src/helpers/formatters.ts +107 -41
  69. rasa/core/channels/inspector/src/helpers/utils.ts +92 -7
  70. rasa/core/channels/inspector/src/types.ts +21 -1
  71. rasa/core/channels/inspector/yarn.lock +94 -1
  72. rasa/core/channels/rest.py +51 -46
  73. rasa/core/channels/socketio.py +22 -0
  74. rasa/core/channels/{audiocodes.py → voice_ready/audiocodes.py} +110 -68
  75. rasa/core/channels/{voice_aware → voice_ready}/jambonz.py +11 -4
  76. rasa/core/channels/{voice_aware → voice_ready}/jambonz_protocol.py +57 -5
  77. rasa/core/channels/{twilio_voice.py → voice_ready/twilio_voice.py} +58 -7
  78. rasa/core/channels/{voice_aware → voice_ready}/utils.py +16 -0
  79. rasa/core/channels/voice_stream/asr/__init__.py +0 -0
  80. rasa/core/channels/voice_stream/asr/asr_engine.py +71 -0
  81. rasa/core/channels/voice_stream/asr/asr_event.py +13 -0
  82. rasa/core/channels/voice_stream/asr/deepgram.py +77 -0
  83. rasa/core/channels/voice_stream/audio_bytes.py +7 -0
  84. rasa/core/channels/voice_stream/tts/__init__.py +0 -0
  85. rasa/core/channels/voice_stream/tts/azure.py +100 -0
  86. rasa/core/channels/voice_stream/tts/cartesia.py +114 -0
  87. rasa/core/channels/voice_stream/tts/tts_cache.py +27 -0
  88. rasa/core/channels/voice_stream/tts/tts_engine.py +48 -0
  89. rasa/core/channels/voice_stream/twilio_media_streams.py +164 -0
  90. rasa/core/channels/voice_stream/util.py +57 -0
  91. rasa/core/channels/voice_stream/voice_channel.py +247 -0
  92. rasa/core/featurizers/single_state_featurizer.py +1 -22
  93. rasa/core/featurizers/tracker_featurizers.py +18 -115
  94. rasa/core/nlg/contextual_response_rephraser.py +11 -2
  95. rasa/{nlu → core}/persistor.py +16 -38
  96. rasa/core/policies/enterprise_search_policy.py +12 -15
  97. rasa/core/policies/flows/flow_executor.py +8 -18
  98. rasa/core/policies/intentless_policy.py +10 -15
  99. rasa/core/policies/ted_policy.py +33 -58
  100. rasa/core/policies/unexpected_intent_policy.py +7 -15
  101. rasa/core/processor.py +13 -64
  102. rasa/core/run.py +11 -1
  103. rasa/core/secrets_manager/constants.py +4 -0
  104. rasa/core/secrets_manager/factory.py +8 -0
  105. rasa/core/secrets_manager/vault.py +11 -1
  106. rasa/core/training/interactive.py +1 -1
  107. rasa/core/utils.py +1 -11
  108. rasa/dialogue_understanding/coexistence/llm_based_router.py +10 -10
  109. rasa/dialogue_understanding/commands/__init__.py +2 -0
  110. rasa/dialogue_understanding/commands/change_flow_command.py +0 -6
  111. rasa/dialogue_understanding/commands/session_end_command.py +61 -0
  112. rasa/dialogue_understanding/generator/flow_retrieval.py +0 -7
  113. rasa/dialogue_understanding/generator/llm_based_command_generator.py +12 -3
  114. rasa/dialogue_understanding/generator/llm_command_generator.py +1 -1
  115. rasa/dialogue_understanding/generator/multi_step/multi_step_llm_command_generator.py +3 -28
  116. rasa/dialogue_understanding/generator/nlu_command_adapter.py +1 -19
  117. rasa/dialogue_understanding/generator/single_step/single_step_llm_command_generator.py +4 -37
  118. rasa/e2e_test/aggregate_test_stats_calculator.py +1 -11
  119. rasa/e2e_test/assertions.py +6 -48
  120. rasa/e2e_test/e2e_test_runner.py +6 -9
  121. rasa/e2e_test/utils/e2e_yaml_utils.py +1 -1
  122. rasa/e2e_test/utils/io.py +1 -3
  123. rasa/engine/graph.py +3 -10
  124. rasa/engine/recipes/config_files/default_config.yml +0 -3
  125. rasa/engine/recipes/default_recipe.py +0 -1
  126. rasa/engine/recipes/graph_recipe.py +0 -1
  127. rasa/engine/runner/dask.py +2 -2
  128. rasa/engine/storage/local_model_storage.py +12 -42
  129. rasa/engine/storage/storage.py +1 -5
  130. rasa/engine/validation.py +1 -78
  131. rasa/keys +1 -0
  132. rasa/model_training.py +13 -16
  133. rasa/nlu/classifiers/diet_classifier.py +25 -38
  134. rasa/nlu/classifiers/logistic_regression_classifier.py +9 -22
  135. rasa/nlu/classifiers/sklearn_intent_classifier.py +16 -37
  136. rasa/nlu/extractors/crf_entity_extractor.py +50 -93
  137. rasa/nlu/featurizers/sparse_featurizer/count_vectors_featurizer.py +16 -45
  138. rasa/nlu/featurizers/sparse_featurizer/lexical_syntactic_featurizer.py +17 -52
  139. rasa/nlu/featurizers/sparse_featurizer/regex_featurizer.py +3 -5
  140. rasa/server.py +1 -1
  141. rasa/shared/constants.py +3 -12
  142. rasa/shared/core/constants.py +4 -0
  143. rasa/shared/core/domain.py +101 -47
  144. rasa/shared/core/events.py +29 -0
  145. rasa/shared/core/flows/flows_list.py +20 -11
  146. rasa/shared/core/flows/validation.py +25 -0
  147. rasa/shared/core/flows/yaml_flows_io.py +3 -24
  148. rasa/shared/importers/importer.py +40 -39
  149. rasa/shared/importers/multi_project.py +23 -11
  150. rasa/shared/importers/rasa.py +7 -2
  151. rasa/shared/importers/remote_importer.py +196 -0
  152. rasa/shared/importers/utils.py +3 -1
  153. rasa/shared/nlu/training_data/features.py +2 -120
  154. rasa/shared/nlu/training_data/training_data.py +18 -19
  155. rasa/shared/providers/_configs/azure_openai_client_config.py +3 -5
  156. rasa/shared/providers/embedding/_base_litellm_embedding_client.py +1 -6
  157. rasa/shared/providers/llm/_base_litellm_client.py +11 -31
  158. rasa/shared/providers/llm/self_hosted_llm_client.py +3 -15
  159. rasa/shared/utils/common.py +3 -22
  160. rasa/shared/utils/io.py +0 -1
  161. rasa/shared/utils/llm.py +30 -27
  162. rasa/shared/utils/schemas/events.py +2 -0
  163. rasa/shared/utils/schemas/model_config.yml +0 -10
  164. rasa/shared/utils/yaml.py +44 -0
  165. rasa/studio/auth.py +5 -3
  166. rasa/studio/config.py +4 -13
  167. rasa/studio/constants.py +0 -1
  168. rasa/studio/data_handler.py +3 -10
  169. rasa/studio/upload.py +8 -17
  170. rasa/tracing/instrumentation/attribute_extractors.py +1 -1
  171. rasa/utils/io.py +66 -0
  172. rasa/utils/tensorflow/model_data.py +193 -2
  173. rasa/validator.py +0 -12
  174. rasa/version.py +1 -1
  175. rasa_pro-3.11.0a1.dist-info/METADATA +576 -0
  176. {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/RECORD +181 -164
  177. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-855bc5b3-1844e5a5.js +0 -1
  178. rasa/core/channels/inspector/dist/assets/index-a5d3e69d.js +0 -1040
  179. rasa/utils/tensorflow/feature_array.py +0 -366
  180. rasa_pro-3.10.16.dist-info/METADATA +0 -196
  181. /rasa/core/channels/{voice_aware → voice_ready}/__init__.py +0 -0
  182. /rasa/core/channels/{voice_native → voice_stream}/__init__.py +0 -0
  183. {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/NOTICE +0 -0
  184. {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/WHEEL +0 -0
  185. {rasa_pro-3.10.16.dist-info → rasa_pro-3.11.0a1.dist-info}/entry_points.txt +0 -0
@@ -1674,6 +1674,23 @@
1674
1674
  "@jridgewell/resolve-uri" "^3.1.0"
1675
1675
  "@jridgewell/sourcemap-codec" "^1.4.14"
1676
1676
 
1677
+ "@lit-labs/react@^2.1.3":
1678
+ version "2.1.3"
1679
+ resolved "https://registry.yarnpkg.com/@lit-labs/react/-/react-2.1.3.tgz#5fca408c435f0b6297fc1314471dc2426206d413"
1680
+ integrity sha512-OD9h2JynerBQUMNzb563jiVpxfvPF0HjQkKY2mx0lpVYvD7F+rtJpOGz6ek+6ufMidV3i+MPT9SX62OKWHFrQg==
1681
+ dependencies:
1682
+ "@lit/react" "^1.0.3"
1683
+
1684
+ "@lit/react@^1.0.3":
1685
+ version "1.0.5"
1686
+ resolved "https://registry.yarnpkg.com/@lit/react/-/react-1.0.5.tgz#9c53a8d719f91ef7edca0bdd68f5589ea579ffc1"
1687
+ integrity sha512-RSHhrcuSMa4vzhqiTenzXvtQ6QDq3hSPsnHHO3jaPmmvVFeoNNm4DHoQ0zLdKAUvY3wP3tTENSUf7xpyVfrDEA==
1688
+
1689
+ "@microsoft/fetch-event-source@^2.0.1":
1690
+ version "2.0.1"
1691
+ resolved "https://registry.yarnpkg.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz#9ceecc94b49fbaa15666e38ae8587f64acce007d"
1692
+ integrity sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==
1693
+
1677
1694
  "@nodelib/fs.scandir@2.1.5":
1678
1695
  version "2.1.5"
1679
1696
  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -2215,7 +2232,7 @@ arg@^4.1.0:
2215
2232
  resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
2216
2233
  integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
2217
2234
 
2218
- argparse@^1.0.7:
2235
+ argparse@^1.0.10, argparse@^1.0.7:
2219
2236
  version "1.0.10"
2220
2237
  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
2221
2238
  integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
@@ -2266,6 +2283,13 @@ asynckit@^0.4.0:
2266
2283
  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
2267
2284
  integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
2268
2285
 
2286
+ autolinker@^3.11.0:
2287
+ version "3.16.2"
2288
+ resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-3.16.2.tgz#6bb4f32432fc111b65659336863e653973bfbcc9"
2289
+ integrity sha512-JiYl7j2Z19F9NdTmirENSUUIIL/9MytEWtmzhfmsKPCp9E+G35Y0UNCMoM9tFigxT59qSc8Ml2dlZXOCVTYwuA==
2290
+ dependencies:
2291
+ tslib "^2.3.0"
2292
+
2269
2293
  available-typed-arrays@^1.0.5:
2270
2294
  version "1.0.5"
2271
2295
  resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
@@ -2999,11 +3023,33 @@ decode-named-character-reference@^1.0.0:
2999
3023
  dependencies:
3000
3024
  character-entities "^2.0.0"
3001
3025
 
3026
+ decode-uri-component@^0.4.1:
3027
+ version "0.4.1"
3028
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz#2ac4859663c704be22bf7db760a1494a49ab2cc5"
3029
+ integrity sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==
3030
+
3002
3031
  dedent@^1.0.0:
3003
3032
  version "1.5.1"
3004
3033
  resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff"
3005
3034
  integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==
3006
3035
 
3036
+ deep-chat-react@2.0.1:
3037
+ version "2.0.1"
3038
+ resolved "https://registry.yarnpkg.com/deep-chat-react/-/deep-chat-react-2.0.1.tgz#a8590a708787c478ab0bd469f1afddca3b65a62e"
3039
+ integrity sha512-oq3ztyhwgPemvJ+LAP2Zbb8rBFty41TlxtCE6bpzGx60mv3tEXd9DvmcI+CQWPDcYRonJLu0tMNxKVwTlY3Ybg==
3040
+ dependencies:
3041
+ "@lit-labs/react" "^2.1.3"
3042
+ deep-chat "2.0.1"
3043
+
3044
+ deep-chat@2.0.1:
3045
+ version "2.0.1"
3046
+ resolved "https://registry.yarnpkg.com/deep-chat/-/deep-chat-2.0.1.tgz#a3892a994e312f60f6fea38d8b63818a93e32dc2"
3047
+ integrity sha512-dLzEvRah+lr+Z6MZq/XSkuIJu2jyd38QVdqMeXGy2WKJQGmBnFZOK/m+F+hsLm9nz2MSfnMYXxGitQI4SNzBcQ==
3048
+ dependencies:
3049
+ "@microsoft/fetch-event-source" "^2.0.1"
3050
+ remarkable "^2.0.1"
3051
+ speech-to-element "^0.1.66"
3052
+
3007
3053
  deep-equal@^2.0.5:
3008
3054
  version "2.2.2"
3009
3055
  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.2.tgz#9b2635da569a13ba8e1cc159c2f744071b115daa"
@@ -3444,6 +3490,11 @@ fill-range@^7.0.1:
3444
3490
  dependencies:
3445
3491
  to-regex-range "^5.0.1"
3446
3492
 
3493
+ filter-obj@^5.1.0:
3494
+ version "5.1.0"
3495
+ resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-5.1.0.tgz#5bd89676000a713d7db2e197f660274428e524ed"
3496
+ integrity sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==
3497
+
3447
3498
  find-root@^1.1.0:
3448
3499
  version "1.1.0"
3449
3500
  resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
@@ -3780,6 +3831,11 @@ ignore@^5.2.0, ignore@^5.2.4:
3780
3831
  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
3781
3832
  integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
3782
3833
 
3834
+ immutable-json-patch@^6.0.1:
3835
+ version "6.0.1"
3836
+ resolved "https://registry.yarnpkg.com/immutable-json-patch/-/immutable-json-patch-6.0.1.tgz#b60b609633e97bb2bc7c74726ff607729e61fd5b"
3837
+ integrity sha512-BHL/cXMjwFZlTOffiWNdY8ZTvNyYLrutCnWxrcKPHr5FqpAb6vsO6WWSPnVSys3+DruFN6lhHJJPHi8uELQL5g==
3838
+
3783
3839
  import-fresh@^3.2.1:
3784
3840
  version "3.3.0"
3785
3841
  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
@@ -5301,6 +5357,15 @@ pure-rand@^6.0.0:
5301
5357
  resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7"
5302
5358
  integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==
5303
5359
 
5360
+ query-string@^9.0.0:
5361
+ version "9.0.0"
5362
+ resolved "https://registry.yarnpkg.com/query-string/-/query-string-9.0.0.tgz#1fe177cd95545600f0deab93f5fb02fd4e3e7273"
5363
+ integrity sha512-4EWwcRGsO2H+yzq6ddHcVqkCQ2EFUSfDMEjF8ryp8ReymyZhIuaFRGLomeOQLkrzacMHoyky2HW0Qe30UbzkKw==
5364
+ dependencies:
5365
+ decode-uri-component "^0.4.1"
5366
+ filter-obj "^5.1.0"
5367
+ split-on-first "^3.0.0"
5368
+
5304
5369
  querystringify@^2.1.1:
5305
5370
  version "2.2.0"
5306
5371
  resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
@@ -5402,6 +5467,11 @@ react-syntax-highlighter@15.5.0:
5402
5467
  prismjs "^1.27.0"
5403
5468
  refractor "^3.6.0"
5404
5469
 
5470
+ react-use-websocket@^4.8.1:
5471
+ version "4.8.1"
5472
+ resolved "https://registry.yarnpkg.com/react-use-websocket/-/react-use-websocket-4.8.1.tgz#be06a0bc956c6d56391a29cbe2caa6ae8edb5615"
5473
+ integrity sha512-FTXuG5O+LFozmu1BRfrzl7UIQngECvGJmL7BHsK4TYXuVt+mCizVA8lT0hGSIF0Z0TedF7bOo1nRzOUdginhDw==
5474
+
5405
5475
  react@18.2.0:
5406
5476
  version "18.2.0"
5407
5477
  resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
@@ -5440,6 +5510,14 @@ regexp.prototype.flags@^1.5.0:
5440
5510
  define-properties "^1.2.0"
5441
5511
  set-function-name "^2.0.0"
5442
5512
 
5513
+ remarkable@^2.0.1:
5514
+ version "2.0.1"
5515
+ resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-2.0.1.tgz#280ae6627384dfb13d98ee3995627ca550a12f31"
5516
+ integrity sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==
5517
+ dependencies:
5518
+ argparse "^1.0.10"
5519
+ autolinker "^3.11.0"
5520
+
5443
5521
  require-directory@^2.1.1:
5444
5522
  version "2.1.1"
5445
5523
  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -5638,6 +5716,16 @@ space-separated-tokens@^1.0.0:
5638
5716
  resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899"
5639
5717
  integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==
5640
5718
 
5719
+ speech-to-element@^0.1.66:
5720
+ version "0.1.66"
5721
+ resolved "https://registry.yarnpkg.com/speech-to-element/-/speech-to-element-0.1.66.tgz#911279115fa72bd997a92fab428ed857062e4ba0"
5722
+ integrity sha512-tHDZ8ttFCsXtQLgWHDDVVTGuiCGWqkxRoIGLygBbU0DUsod2Ho89fws7OeCHpGOeDL9s5rBVpEqj45BsEIKy+Q==
5723
+
5724
+ split-on-first@^3.0.0:
5725
+ version "3.0.0"
5726
+ resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-3.0.0.tgz#f04959c9ea8101b9b0bbf35a61b9ebea784a23e7"
5727
+ integrity sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==
5728
+
5641
5729
  sprintf-js@~1.0.2:
5642
5730
  version "1.0.3"
5643
5731
  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@@ -5855,6 +5943,11 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0:
5855
5943
  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
5856
5944
  integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
5857
5945
 
5946
+ tslib@^2.3.0:
5947
+ version "2.6.3"
5948
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
5949
+ integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==
5950
+
5858
5951
  type-check@^0.4.0, type-check@~0.4.0:
5859
5952
  version "0.4.0"
5860
5953
  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@@ -9,7 +9,6 @@ import structlog
9
9
  from asyncio import Queue, CancelledError
10
10
  from sanic import Blueprint, response
11
11
  from sanic.request import Request
12
- from sanic.response import HTTPResponse, ResponseStream
13
12
  from typing import (
14
13
  Text,
15
14
  Dict,
@@ -20,6 +19,7 @@ from typing import (
20
19
  NoReturn,
21
20
  Union,
22
21
  )
22
+ from sanic.response import HTTPResponse, ResponseStream, BaseHTTPResponse
23
23
 
24
24
  import rasa.utils.endpoints
25
25
  from rasa.core.channels.channel import (
@@ -128,6 +128,54 @@ class RestInput(InputChannel):
128
128
 
129
129
  await task
130
130
 
131
+ async def receive_messages(
132
+ self, request: Request, on_new_message: Callable[[UserMessage], Awaitable[None]]
133
+ ) -> Union[BaseHTTPResponse, ResponseStream]:
134
+ sender_id = await self._extract_sender(request)
135
+ text = self._extract_message(request)
136
+ should_use_stream = rasa.utils.endpoints.bool_arg(
137
+ request, "stream", default=False
138
+ )
139
+ input_channel = self._extract_input_channel(request)
140
+ metadata = self.get_metadata(request)
141
+
142
+ if should_use_stream:
143
+ return ResponseStream(
144
+ partial(
145
+ self.stream_response,
146
+ on_new_message,
147
+ text,
148
+ sender_id,
149
+ input_channel,
150
+ metadata,
151
+ ),
152
+ content_type="text/event-stream",
153
+ )
154
+ else:
155
+ collector = CollectingOutputChannel()
156
+ # noinspection PyBroadException
157
+ try:
158
+ await on_new_message(
159
+ UserMessage(
160
+ text,
161
+ collector,
162
+ sender_id,
163
+ input_channel=input_channel,
164
+ metadata=metadata,
165
+ headers=request.headers,
166
+ )
167
+ )
168
+ except CancelledError:
169
+ structlogger.error(
170
+ "rest.message.received.timeout", text=copy.deepcopy(text)
171
+ )
172
+ except Exception:
173
+ structlogger.exception(
174
+ "rest.message.received.failure", text=copy.deepcopy(text)
175
+ )
176
+
177
+ return response.json(collector.messages)
178
+
131
179
  def blueprint(
132
180
  self, on_new_message: Callable[[UserMessage], Awaitable[None]]
133
181
  ) -> Blueprint:
@@ -149,51 +197,8 @@ class RestInput(InputChannel):
149
197
  return response.json({"status": "ok"})
150
198
 
151
199
  @custom_webhook.route("/webhook", methods=["POST"])
152
- async def receive(request: Request) -> Union[ResponseStream, HTTPResponse]:
153
- sender_id = await self._extract_sender(request)
154
- text = self._extract_message(request)
155
- should_use_stream = rasa.utils.endpoints.bool_arg(
156
- request, "stream", default=False
157
- )
158
- input_channel = self._extract_input_channel(request)
159
- metadata = self.get_metadata(request)
160
-
161
- if should_use_stream:
162
- return ResponseStream(
163
- partial(
164
- self.stream_response,
165
- on_new_message,
166
- text,
167
- sender_id,
168
- input_channel,
169
- metadata,
170
- ),
171
- content_type="text/event-stream",
172
- )
173
- else:
174
- collector = CollectingOutputChannel()
175
- # noinspection PyBroadException
176
- try:
177
- await on_new_message(
178
- UserMessage(
179
- text,
180
- collector,
181
- sender_id,
182
- input_channel=input_channel,
183
- metadata=metadata,
184
- headers=request.headers,
185
- )
186
- )
187
- except CancelledError:
188
- structlogger.error(
189
- "rest.message.received.timeout", text=copy.deepcopy(text)
190
- )
191
- except Exception:
192
- structlogger.exception(
193
- "rest.message.received.failure", text=copy.deepcopy(text)
194
- )
195
-
196
- return response.json(collector.messages)
200
+ async def receive(request: Request) -> Union[ResponseStream, BaseHTTPResponse]:
201
+ return await self.receive_messages(request, on_new_message)
197
202
 
198
203
  return custom_webhook
199
204
 
@@ -49,9 +49,31 @@ class SocketIOOutput(OutputChannel):
49
49
  def __init__(self, sio: AsyncServer, bot_message_evt: Text) -> None:
50
50
  self.sio = sio
51
51
  self.bot_message_evt = bot_message_evt
52
+ self.last_event_timestamp = (
53
+ -1
54
+ ) # Initialize with -1 to send all events on first message
55
+
56
+ def _get_new_events(self) -> List[Dict[Text, Any]]:
57
+ """Get events that are newer than the last sent event."""
58
+ events = self.tracker_state.get("events", [])
59
+ new_events = [
60
+ event for event in events if event["timestamp"] > self.last_event_timestamp
61
+ ]
62
+ if new_events:
63
+ self.last_event_timestamp = new_events[-1]["timestamp"]
64
+ return new_events
52
65
 
53
66
  async def _send_message(self, socket_id: Text, response: Any) -> None:
54
67
  """Sends a message to the recipient using the bot event."""
68
+ # send tracker state (contains stack, slots and more)
69
+ await self.sio.emit("tracker_state", self.tracker_state, room=socket_id)
70
+
71
+ # send new events
72
+ new_events = self._get_new_events()
73
+ if new_events:
74
+ await self.sio.emit("rasa_events", new_events, room=socket_id)
75
+
76
+ # send bot response
55
77
  await self.sio.emit(self.bot_message_evt, response, room=socket_id)
56
78
 
57
79
  async def send_text_message(
@@ -1,16 +1,20 @@
1
1
  import copy
2
- import datetime
2
+ from datetime import datetime, timezone, timedelta
3
3
  import json
4
- import logging
5
4
  import uuid
6
5
  from typing import Any, Awaitable, Callable, Dict, List, Optional, Text, Union
6
+ from dataclasses import asdict
7
7
 
8
8
  import structlog
9
9
  from jsonschema import ValidationError, validate
10
10
  from rasa.core import jobs
11
11
  from rasa.core.channels.channel import InputChannel, OutputChannel, UserMessage
12
- from rasa.core.channels.voice_aware.utils import validate_voice_license_scope
12
+ from rasa.core.channels.voice_ready.utils import (
13
+ validate_voice_license_scope,
14
+ CallParameters,
15
+ )
13
16
  from rasa.shared.constants import INTENT_MESSAGE_PREFIX
17
+ from rasa.shared.core.constants import USER_INTENT_SESSION_START
14
18
  from rasa.shared.exceptions import RasaException
15
19
  from sanic import Blueprint, response
16
20
  from sanic.exceptions import NotFound, SanicException, ServerError
@@ -18,15 +22,37 @@ from sanic.request import Request
18
22
  from sanic.response import HTTPResponse
19
23
 
20
24
 
21
- logger = logging.getLogger(__name__)
22
25
  structlogger = structlog.get_logger()
23
26
 
24
27
  CHANNEL_NAME = "audiocodes"
25
28
  KEEP_ALIVE_SECONDS = 120
26
29
  KEEP_ALIVE_EXPIRATION_FACTOR = 1.5
27
-
28
-
29
- class Unauthorized(SanicException):
30
+ EVENT_START = "start"
31
+ EVENT_DTMF = "DTMF"
32
+ ACTIVITY_MESSAGE = "message"
33
+ ACTIVITY_EVENT = "event"
34
+ INFO_UNKNOWN = "unknown"
35
+ ACTIVITY_ID_KEY = "id"
36
+ CREDENTIALS_TOKEN_KEY = "token"
37
+ CREDENTIALS_USE_WEBSOCKET_KEY = "use_websocket"
38
+ CREDENTIALS_KEEP_ALIVE_KEY = "keep_alive"
39
+ CREDENTIALS_KEEP_ALIVE_EXPIRATION_FACTOR_KEY = "keep_alive_expiration_factor"
40
+ CLEANUP_INTERVAL_MINUTES = 10
41
+
42
+
43
+ def map_call_params(parameters: Dict[Text, Any]) -> CallParameters:
44
+ """Map the Audiocodes parameters to the CallParameters dataclass."""
45
+ return CallParameters(
46
+ call_id=parameters.get("vaigConversationId"),
47
+ user_phone=parameters.get("callee"),
48
+ bot_phone=parameters.get("caller"),
49
+ user_name=parameters.get("callerDisplayName"),
50
+ user_host=parameters.get("callerHost"),
51
+ bot_host=parameters.get("calleeHost"),
52
+ )
53
+
54
+
55
+ class HttpUnauthorized(SanicException):
30
56
  """**Status**: 401 Not Authorized."""
31
57
 
32
58
  status_code = 401
@@ -41,30 +67,44 @@ class Conversation:
41
67
  self.update()
42
68
 
43
69
  def update(self) -> None:
44
- self.last_activity: datetime.datetime = datetime.datetime.utcnow()
70
+ """Update the last activity time."""
71
+ self.last_activity: datetime = datetime.now(timezone.utc)
45
72
 
46
73
  @staticmethod
47
74
  def get_metadata(activity: Dict[Text, Any]) -> Optional[Dict[Text, Any]]:
75
+ """Get metadata from the activity."""
48
76
  return activity.get("parameters")
49
77
 
50
78
  @staticmethod
51
79
  def _handle_event(event: Dict[Text, Any]) -> Text:
52
- text = f'{INTENT_MESSAGE_PREFIX}vaig_event_{event["name"]}'
53
- event_params = {}
54
- if "parameters" in event:
55
- event_params.update(event["parameters"])
56
- if "value" in event:
57
- event_params.update({"value": event["value"]})
58
- if len(event_params) > 0:
59
- text += json.dumps(event_params)
80
+ """Handle start and DTMF event and return the corresponding text."""
81
+ structlogger.debug("audiocodes.handle.event", event_payload=event)
82
+ if "name" not in event:
83
+ structlogger.warning(
84
+ "audiocodes.handle.event.no_name_key", event_payload=event
85
+ )
86
+ return ""
87
+
88
+ if event["name"] == EVENT_START:
89
+ text = f"{INTENT_MESSAGE_PREFIX}{USER_INTENT_SESSION_START}"
90
+ event_params = asdict(map_call_params(event["parameters"]))
91
+ elif event["name"] == EVENT_DTMF:
92
+ text = f"{INTENT_MESSAGE_PREFIX}vaig_event_DTMF"
93
+ event_params = {"value": event["value"]}
94
+ else:
95
+ structlogger.warning(
96
+ "audiocodes.handle.event.unknown_event", event_payload=event
97
+ )
98
+ return ""
99
+
100
+ text += json.dumps(event_params)
60
101
  return text
61
102
 
62
- def is_active_conversation(
63
- self, now: datetime.datetime, delta: datetime.timedelta
64
- ) -> bool:
103
+ def is_active_conversation(self, now: datetime, delta: timedelta) -> bool:
104
+ """Check if the conversation is active."""
65
105
  if now - self.last_activity > delta:
66
- logger.warning(
67
- f"Conversation {self.conversation_id} is invalid due to inactivity"
106
+ structlogger.warning(
107
+ "audiocodes.conversation.inactive", conversation=self.conversation_id
68
108
  )
69
109
  return False
70
110
  return True
@@ -75,24 +115,25 @@ class Conversation:
75
115
  output_channel: OutputChannel,
76
116
  on_new_message: Callable[[UserMessage], Awaitable[Any]],
77
117
  ) -> None:
78
- logger.debug("(handle_activities) --- Activities:")
118
+ """Handle activities sent by Audiocodes."""
119
+ structlogger.debug("audiocodes.handle.activities")
79
120
  for activity in message["activities"]:
80
121
  text = None
81
- if activity["id"] in self.activity_ids:
82
- logger.warning(
83
- "Got activity that already handled. Activity ID:"
84
- f' {activity["id"]}'
122
+ if activity[ACTIVITY_ID_KEY] in self.activity_ids:
123
+ structlogger.warning(
124
+ "audiocodes.handle.activities.duplicate_activity",
125
+ activity_id=activity[ACTIVITY_ID_KEY],
85
126
  )
86
127
  continue
87
- self.activity_ids.append(activity["id"])
88
- if activity["type"] == "message":
128
+ self.activity_ids.append(activity[ACTIVITY_ID_KEY])
129
+ if activity["type"] == ACTIVITY_MESSAGE:
89
130
  text = activity["text"]
90
- elif activity["type"] == "event":
131
+ elif activity["type"] == ACTIVITY_EVENT:
91
132
  text = self._handle_event(activity)
92
133
  else:
93
- logger.warning(
94
- "Received an activity from audiocodes that we can not "
95
- f"handle. Activity: {activity}"
134
+ structlogger.warning(
135
+ "audiocodes.handle.activities.unknown_activity_type",
136
+ activity=activity,
96
137
  )
97
138
  if not text:
98
139
  continue
@@ -111,13 +152,14 @@ class Conversation:
111
152
  elif isinstance(user_msg.text, str):
112
153
  anonymized_info = user_msg.text
113
154
  else:
114
- anonymized_info = "unknown"
155
+ anonymized_info = INFO_UNKNOWN
115
156
 
116
157
  structlogger.exception(
117
158
  "audiocodes.handle.activities.failure",
118
159
  user_message=copy.deepcopy(anonymized_info),
160
+ error=e,
161
+ exc_info=True,
119
162
  )
120
- logger.debug(e, exc_info=True)
121
163
 
122
164
  await output_channel.send_custom_json(
123
165
  self.conversation_id,
@@ -158,11 +200,12 @@ class AudiocodesInput(InputChannel):
158
200
  raise RasaException(f"Invalid credentials: {e.message}")
159
201
 
160
202
  return cls(
161
- credentials.get("token", ""),
162
- credentials.get("use_websocket", True),
163
- credentials.get("keep_alive", KEEP_ALIVE_SECONDS),
203
+ credentials.get(CREDENTIALS_TOKEN_KEY, ""),
204
+ credentials.get(CREDENTIALS_USE_WEBSOCKET_KEY, True),
205
+ credentials.get(CREDENTIALS_KEEP_ALIVE_KEY, KEEP_ALIVE_SECONDS),
164
206
  credentials.get(
165
- "keep_alive_expiration_factor", KEEP_ALIVE_EXPIRATION_FACTOR
207
+ CREDENTIALS_KEEP_ALIVE_EXPIRATION_FACTOR_KEY,
208
+ KEEP_ALIVE_EXPIRATION_FACTOR,
166
209
  ),
167
210
  )
168
211
 
@@ -185,12 +228,12 @@ class AudiocodesInput(InputChannel):
185
228
  if self.scheduler_job:
186
229
  self.scheduler_job.remove()
187
230
  self.scheduler_job = (await jobs.scheduler()).add_job(
188
- self.clean_old_conversations, "interval", minutes=10
231
+ self.clean_old_conversations, "interval", minutes=CLEANUP_INTERVAL_MINUTES
189
232
  )
190
233
 
191
234
  def _check_token(self, token: Optional[Text]) -> None:
192
235
  if not token:
193
- raise Unauthorized("Authentication token required.")
236
+ raise HttpUnauthorized("Authentication token required.")
194
237
 
195
238
  def _get_conversation(
196
239
  self, token: Optional[Text], conversation_id: Text
@@ -203,14 +246,11 @@ class AudiocodesInput(InputChannel):
203
246
  return conversation
204
247
 
205
248
  def clean_old_conversations(self) -> None:
206
- logger.debug(
207
- "Performing clean old conversations, current number:"
208
- f" {len(self.conversations)}"
209
- )
210
- now = datetime.datetime.utcnow()
211
- delta = datetime.timedelta(
212
- seconds=self.keep_alive * self.keep_alive_expiration_factor
249
+ structlogger.debug(
250
+ "audiocodes.clean_old_conversations", current_number=len(self.conversations)
213
251
  )
252
+ now = datetime.now(timezone.utc)
253
+ delta = timedelta(seconds=self.keep_alive * self.keep_alive_expiration_factor)
214
254
  self.conversations = {
215
255
  k: v
216
256
  for k, v in self.conversations.items()
@@ -221,9 +261,8 @@ class AudiocodesInput(InputChannel):
221
261
  conversation_id = body["conversation"]
222
262
  if conversation_id in self.conversations:
223
263
  raise ServerError("Conversation already exists")
224
- logger.debug(
225
- "(handle_start_conversation) --- New Conversation has arrived."
226
- f" Conversation: {conversation_id}"
264
+ structlogger.debug(
265
+ "audiocodes.handle_start_conversation", conversation=conversation_id
227
266
  )
228
267
  self.conversations[conversation_id] = Conversation(conversation_id)
229
268
  urls = {
@@ -248,16 +287,15 @@ class AudiocodesInput(InputChannel):
248
287
  """Triggered on new websocket connection."""
249
288
  if self.use_websocket is False:
250
289
  raise ConnectionRefusedError("websocket is unavailable")
251
- logger.debug(
252
- "(new_client_connection) --- New client is trying to connect."
253
- f" Conversation: {conversation_id}"
290
+ structlogger.debug(
291
+ "audiocodes.new_client_connection", conversation=conversation_id
254
292
  )
255
293
  conversation = self._get_conversation(request.token, conversation_id)
256
294
  if conversation:
257
295
  if conversation.ws:
258
- logger.debug(
259
- "(new_client_connection) --- The client was already connected."
260
- f" Conversation: {conversation_id}"
296
+ structlogger.debug(
297
+ "audiocodes.new_client_connection.already_connected",
298
+ conversation=conversation_id,
261
299
  )
262
300
  else:
263
301
  conversation.ws = ws
@@ -265,11 +303,9 @@ class AudiocodesInput(InputChannel):
265
303
  try:
266
304
  await ws.recv()
267
305
  except Exception:
268
- logger.debug(
269
- (
270
- "(new_client_connection) --- Websocket was closed by client: "
271
- f"{conversation_id}"
272
- )
306
+ structlogger.warning(
307
+ "audiocodes.new_client_connection.closed",
308
+ conversation=conversation_id,
273
309
  )
274
310
  if conversation:
275
311
  conversation.ws = None
@@ -308,10 +344,7 @@ class AudiocodesInput(InputChannel):
308
344
  Example of payload:
309
345
  {"conversation": <conversation_id>, "activities": List[Activity]}.
310
346
  """
311
- logger.debug(
312
- "(on_activities) --- New activities from the user. Conversation: "
313
- f"{conversation_id}"
314
- )
347
+ structlogger.debug("audiocodes.on_activities", conversation=conversation_id)
315
348
  conversation = self._get_conversation(request.token, conversation_id)
316
349
  if conversation is None:
317
350
  return response.json({})
@@ -353,13 +386,18 @@ class AudiocodesInput(InputChannel):
353
386
  reason = json.dumps({"reason": request.json.get("reason")})
354
387
  await on_new_message(
355
388
  UserMessage(
356
- text=f"{INTENT_MESSAGE_PREFIX}vaig_event_end{reason}",
389
+ text=f"{INTENT_MESSAGE_PREFIX}session_end",
357
390
  output_channel=None,
358
391
  sender_id=conversation_id,
392
+ metadata=reason,
359
393
  )
360
394
  )
361
395
  del self.conversations[conversation_id]
362
- logger.debug("(disconnect) --- Conversation was deleted")
396
+ structlogger.debug(
397
+ "audiocodes.disconnect",
398
+ conversation=conversation_id,
399
+ request=request.json,
400
+ )
363
401
  return response.json({})
364
402
 
365
403
  @ac_webhook.route("/conversation/<conversation_id>/keepalive", methods=["POST"])
@@ -397,7 +435,7 @@ class AudiocodesOutput(OutputChannel):
397
435
  )
398
436
  message.update(
399
437
  {
400
- "timestamp": datetime.datetime.utcnow().isoformat("T")[:-3] + "Z",
438
+ "timestamp": datetime.now(timezone.utc).isoformat("T")[:-3] + "Z",
401
439
  "id": str(uuid.uuid4()),
402
440
  }
403
441
  )
@@ -429,6 +467,10 @@ class AudiocodesOutput(OutputChannel):
429
467
  """Send an activity."""
430
468
  await self.add_message(json_message)
431
469
 
470
+ async def hangup(self, recipient_id: Text, **kwargs: Any) -> None:
471
+ """Indicate that the conversation should be ended."""
472
+ await self.add_message({"type": "event", "name": "hangup"})
473
+
432
474
 
433
475
  class WebsocketOutput(AudiocodesOutput):
434
476
  def __init__(self, ws: Any, conversation_id: Text) -> None: