rasa-pro 3.14.0a15__py3-none-any.whl → 3.14.0a16__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 (93) hide show
  1. rasa/builder/config.py +1 -0
  2. rasa/builder/copilot/constants.py +3 -0
  3. rasa/builder/copilot/copilot.py +127 -31
  4. rasa/builder/copilot/models.py +34 -0
  5. rasa/builder/copilot/prompts/copilot_system_prompt.jinja2 +28 -84
  6. rasa/builder/copilot/prompts/latest_user_message_context_prompt.jinja2 +61 -0
  7. rasa/builder/copilot/telemetry.py +16 -6
  8. rasa/builder/document_retrieval/models.py +3 -3
  9. rasa/builder/main.py +14 -5
  10. rasa/builder/project_generator.py +1 -3
  11. rasa/builder/service.py +8 -9
  12. rasa/builder/template_cache.py +183 -9
  13. rasa/cli/project_templates/telco/data/general/human_handoff.yml +1 -1
  14. rasa/cli/project_templates/telco/domain/general/human_handoff.yml +3 -6
  15. rasa/cli/project_templates/telco/tests/e2e_test_cases/billing/understand_bill.yml +67 -0
  16. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/bot_challenge.yml +8 -0
  17. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/feedback.yml +46 -0
  18. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/goodbye.yml +9 -0
  19. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/hello.yml +8 -0
  20. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/human_handoff.yml +35 -0
  21. rasa/cli/project_templates/telco/tests/e2e_test_cases/general/patterns.yml +23 -0
  22. rasa/cli/project_templates/telco/tests/e2e_test_cases/network/solve_internet_issue.yml +57 -0
  23. rasa/core/channels/development_inspector.py +1 -21
  24. rasa/core/channels/hangouts.py +2 -2
  25. rasa/core/channels/inspector/dist/assets/{arc-c24d8d79.js → arc-460861ce.js} +1 -1
  26. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-1b6b9f26.js → blockDiagram-38ab4fdb-16c993e0.js} +1 -1
  27. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-da91d0f9.js → c4Diagram-3d4e48cf-488337d7.js} +1 -1
  28. rasa/core/channels/inspector/dist/assets/channel-b560a3d4.js +1 -0
  29. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-6067f302.js → classDiagram-70f12bd4-b08e53a8.js} +1 -1
  30. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-705d024a.js → classDiagram-v2-f2320105-b73f5a83.js} +1 -1
  31. rasa/core/channels/inspector/dist/assets/clone-67015557.js +1 -0
  32. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-3751dffe.js → createText-2e5e7dd3-0210a219.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-7b25b4af.js → edges-e0da2a9e-28df7099.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-eb7deea8.js → erDiagram-9861fffd-9fbf4a58.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-67235ff6.js → flowDb-956e92f1-fa691f62.js} +1 -1
  36. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-34c3a16a.js → flowDiagram-66a62f08-ca907b67.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-4a070961.js +1 -0
  38. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-f1a93631.js → flowchart-elk-definition-4a651766-c10945f2.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-a68cbad1.js → ganttDiagram-c361ad54-9d49a75a.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-0b1e4a1d.js → gitGraphDiagram-72cf32ee-9aa698ac.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{graph-f3c1d212.js → graph-3ab38d50.js} +1 -1
  42. rasa/core/channels/inspector/dist/assets/{index-3862675e-34cbca30.js → index-3862675e-6edac98f.js} +1 -1
  43. rasa/core/channels/inspector/dist/assets/{index-051c5a6e.js → index-61128091.js} +41 -40
  44. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-e69960a1.js → infoDiagram-f8f76790-21baff85.js} +1 -1
  45. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-8dd3296a.js → journeyDiagram-49397b02-4a6c7e98.js} +1 -1
  46. rasa/core/channels/inspector/dist/assets/{layout-e93126bc.js → layout-4beae36e.js} +1 -1
  47. rasa/core/channels/inspector/dist/assets/{line-15eb1e26.js → line-633b638e.js} +1 -1
  48. rasa/core/channels/inspector/dist/assets/{linear-fec95d33.js → linear-22d77d65.js} +1 -1
  49. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-2557813e.js → mindmap-definition-fc14e90a-f219ef43.js} +1 -1
  50. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-40d756b1.js → pieDiagram-8a3498a8-c7e1cafb.js} +1 -1
  51. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-a48cbdcd.js → quadrantDiagram-120e2f19-045e49b4.js} +1 -1
  52. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-dc778150.js → requirementDiagram-deff3bca-22485cb9.js} +1 -1
  53. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-10026b94.js → sankeyDiagram-04a897e0-281c3da2.js} +1 -1
  54. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-3b2ed10a.js → sequenceDiagram-704730f1-a3dd10e0.js} +1 -1
  55. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-c5f3b3fb.js → stateDiagram-587899a1-61bd6eb2.js} +1 -1
  56. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-e503656b.js → stateDiagram-v2-d93cdb3a-deead491.js} +1 -1
  57. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-a683ce56.js → styles-6aaf32cf-1b10e104.js} +1 -1
  58. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-02bcdcee.js → styles-9a916d00-b1e18e58.js} +1 -1
  59. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-8e90dbb9.js → styles-c10674c1-956c3492.js} +1 -1
  60. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-7c23fc1e.js → svgDrawCommon-08f97a94-e13f753d.js} +1 -1
  61. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-c42faec8.js → timeline-definition-85554ec2-e568acd2.js} +1 -1
  62. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-5e3bb0ea.js → xychartDiagram-e933f94c-8b7e27fc.js} +1 -1
  63. rasa/core/channels/inspector/dist/index.html +1 -1
  64. rasa/core/channels/inspector/src/App.tsx +0 -7
  65. rasa/core/channels/inspector/src/components/DialogueInformation.tsx +9 -1
  66. rasa/core/channels/inspector/src/components/LatencyDisplay.tsx +63 -35
  67. rasa/core/channels/inspector/src/types.ts +32 -7
  68. rasa/core/channels/studio_chat.py +14 -40
  69. rasa/core/constants.py +6 -0
  70. rasa/core/iam_credentials_providers/__init__.py +0 -0
  71. rasa/core/iam_credentials_providers/aws_iam_credentials_providers.py +66 -0
  72. rasa/core/iam_credentials_providers/credentials_provider_protocol.py +89 -0
  73. rasa/core/processor.py +32 -0
  74. rasa/core/redis_connection_factory.py +411 -0
  75. rasa/core/tracker_stores/redis_tracker_store.py +32 -14
  76. rasa/core/tracker_stores/sql_tracker_store.py +57 -1
  77. rasa/model_manager/socket_bridge.py +1 -2
  78. rasa/shared/core/constants.py +1 -0
  79. rasa/shared/core/events.py +2 -0
  80. rasa/version.py +1 -1
  81. {rasa_pro-3.14.0a15.dist-info → rasa_pro-3.14.0a16.dist-info}/METADATA +11 -12
  82. {rasa_pro-3.14.0a15.dist-info → rasa_pro-3.14.0a16.dist-info}/RECORD +90 -77
  83. rasa/core/channels/inspector/dist/assets/channel-d2444dfd.js +0 -1
  84. rasa/core/channels/inspector/dist/assets/clone-281a0990.js +0 -1
  85. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-aa4cca3b.js +0 -1
  86. /rasa/cli/project_templates/telco/domain/billing/{domain_undertand_bill.yml → understand_bill.yml} +0 -0
  87. /rasa/cli/project_templates/telco/domain/network/{domain_reboot_router.yml → reboot_router.yml} +0 -0
  88. /rasa/cli/project_templates/telco/domain/network/{domain_reset_router.yml → reset_router.yml} +0 -0
  89. /rasa/cli/project_templates/telco/domain/network/{domain_run_speed_test.yml → run_speed_test.yml} +0 -0
  90. /rasa/cli/project_templates/telco/domain/network/{domain_solve_internet_issue.yml → solve_internet_issue.yml} +0 -0
  91. {rasa_pro-3.14.0a15.dist-info → rasa_pro-3.14.0a16.dist-info}/NOTICE +0 -0
  92. {rasa_pro-3.14.0a15.dist-info → rasa_pro-3.14.0a16.dist-info}/WHEEL +0 -0
  93. {rasa_pro-3.14.0a15.dist-info → rasa_pro-3.14.0a16.dist-info}/entry_points.txt +0 -0
@@ -2,40 +2,47 @@ import {
2
2
  Box,
3
3
  Flex,
4
4
  FlexProps,
5
- Text,
6
- useColorModeValue,
7
- Tooltip,
8
5
  Table,
9
6
  Tbody,
10
- Tr,
11
7
  Td,
8
+ Text,
9
+ Tooltip,
10
+ Tr,
11
+ useColorModeValue,
12
12
  } from '@chakra-ui/react'
13
13
  import { useOurTheme } from '../theme'
14
- import { LatencyData } from '../types'
14
+ import {
15
+ isRasaLatency,
16
+ isVoiceLatency,
17
+ RasaLatency,
18
+ VoiceLatency,
19
+ } from '../types'
15
20
 
16
- interface Props extends FlexProps {
17
- latency: LatencyData
21
+ interface MinimalDisplayProps extends FlexProps {
22
+ latency: RasaLatency
18
23
  }
19
24
 
20
25
  /**
21
26
  * Simple latency display for text-only conversations.
22
27
  * Shows a single response time value.
23
28
  */
24
- const MinimalDisplay = ({ latency, sx, ...props }: Props) => {
29
+ const MinimalDisplay = ({ latency, sx, ...props }: MinimalDisplayProps) => {
25
30
  const containerSx = {
26
31
  ...sx,
27
32
  display: 'flex',
28
33
  alignItems: 'center',
29
34
  }
30
-
35
+
31
36
  const getLatencyColor = (latency: number) => {
32
37
  if (latency < 1500) return 'green.500'
33
38
  if (latency < 2500) return 'orange.500'
34
39
  return 'red.500'
35
40
  }
36
-
37
- const value = Math.round(latency.rasa_processing_latency_ms || 0);
38
- const color = getLatencyColor(value);
41
+
42
+ const value = Math.round(
43
+ (latency.command_processor || 0) + (latency.prediction_loop || 0),
44
+ )
45
+ const color = getLatencyColor(value)
39
46
 
40
47
  return (
41
48
  <Flex sx={containerSx} {...props}>
@@ -49,11 +56,15 @@ const MinimalDisplay = ({ latency, sx, ...props }: Props) => {
49
56
  )
50
57
  }
51
58
 
59
+ interface WaterfallDisplayProps extends FlexProps {
60
+ latency: VoiceLatency
61
+ }
62
+
52
63
  /**
53
64
  * Detailed latency waterfall chart for voice conversations.
54
65
  * Displays processing times for ASR, Rasa, and TTS components.
55
66
  */
56
- const WaterfallDisplay = ({ latency, sx, ...props }: Props) => {
67
+ const WaterfallDisplay = ({ latency, sx, ...props }: WaterfallDisplayProps) => {
57
68
  const { rasaSpace } = useOurTheme()
58
69
 
59
70
  const containerSx = {
@@ -61,13 +72,13 @@ const WaterfallDisplay = ({ latency, sx, ...props }: Props) => {
61
72
  flexDirection: 'column',
62
73
  gap: rasaSpace[1],
63
74
  }
64
-
75
+
65
76
  const headerSx = {
66
77
  fontSize: 'sm',
67
78
  fontWeight: 'bold',
68
79
  color: useColorModeValue('gray.700', 'gray.300'),
69
80
  }
70
-
81
+
71
82
  const waterfallBarSx = {
72
83
  height: '24px',
73
84
  borderRadius: '4px',
@@ -75,10 +86,10 @@ const WaterfallDisplay = ({ latency, sx, ...props }: Props) => {
75
86
  border: '1px solid',
76
87
  borderColor: useColorModeValue('gray.200', 'gray.600'),
77
88
  }
78
-
89
+
79
90
  const legendTableSx = {
80
91
  size: 'sm',
81
- mt: rasaSpace[0.5]
92
+ mt: rasaSpace[0.5],
82
93
  }
83
94
 
84
95
  const getLatencyColor = (type: string) => {
@@ -95,8 +106,10 @@ const WaterfallDisplay = ({ latency, sx, ...props }: Props) => {
95
106
  const descriptions: { [key: string]: string } = {
96
107
  asr: 'Time from the first Partial Transcript event to the Final Transcript event from Speech Recognition. It also includes the time taken by the user to speak.',
97
108
  rasa: 'Time taken by Rasa to process the text from the Final Transcript event from Speech Recognition until a text response is generated.',
98
- tts_first: 'Time between the request sent to Text-to-Speech processing and the first byte of audio received by Rasa.',
99
- tts_complete: 'Time taken by Text-to-Speech to complete audio generation. It depends on the length of the text and could overlap with the Bot speaking time.'
109
+ tts_first:
110
+ 'Time between the request sent to Text-to-Speech processing and the first byte of audio received by Rasa.',
111
+ tts_complete:
112
+ 'Time taken by Text-to-Speech to complete audio generation. It depends on the length of the text and could overlap with the Bot speaking time.',
100
113
  }
101
114
  return descriptions[type] || ''
102
115
  }
@@ -151,10 +164,10 @@ const WaterfallDisplay = ({ latency, sx, ...props }: Props) => {
151
164
  )
152
165
 
153
166
  // Calculate total latency for title (Rasa + TTS First Byte)
154
- const totalDisplayLatency =
155
- (latency.rasa_processing_latency_ms || 0) +
156
- (latency.tts_first_byte_latency_ms || 0);
157
-
167
+ const totalDisplayLatency =
168
+ (latency.rasa_processing_latency_ms || 0) +
169
+ (latency.tts_first_byte_latency_ms || 0)
170
+
158
171
  return (
159
172
  <Flex sx={containerSx} {...props}>
160
173
  <Text sx={headerSx}>
@@ -241,28 +254,43 @@ const WaterfallDisplay = ({ latency, sx, ...props }: Props) => {
241
254
  )
242
255
  }
243
256
 
257
+ interface LatencyDisplayProps extends FlexProps {
258
+ voiceLatency?: any
259
+ rasaLatency?: any
260
+ }
261
+
244
262
  /**
245
263
  * Displays processing latency information for the conversation.
246
- * Shows either a detailed waterfall chart for voice conversations or
264
+ * Shows either a detailed waterfall chart for voice conversations or
247
265
  * a simpler display for text-only conversations.
248
266
  */
249
267
  export const LatencyDisplay = ({
250
268
  sx,
251
- latency,
269
+ voiceLatency,
270
+ rasaLatency,
252
271
  ...props
253
- }: Props) => {
254
- if (!latency) {
255
- console.warn('Latency data is not available')
256
- return null
272
+ }: LatencyDisplayProps) => {
273
+ // We give priority to voice latency as most comprehensive.
274
+ if (isVoiceLatency(voiceLatency)) {
275
+ return <WaterfallDisplay latency={voiceLatency} sx={sx} {...props} />
257
276
  }
258
277
 
259
- // Show waterfall if voice metrics are available, otherwise show minimal display
260
- const isVoiceMetricsAvailable =
261
- latency.asr_latency_ms && latency.tts_complete_latency_ms
278
+ // If voice latency is not available, we use rasa latency.
279
+ if (isRasaLatency(rasaLatency)) {
280
+ return <MinimalDisplay latency={rasaLatency} sx={sx} {...props} />
281
+ }
262
282
 
263
- if (isVoiceMetricsAvailable) {
264
- return <WaterfallDisplay latency={latency} sx={sx} {...props} />
283
+ // If rasa latency is not available, we are either waiting for first data from voice stream or the conversation is not started yet.
284
+ if (!rasaLatency) {
285
+ return <Text>Rasa latency data is not available yet</Text>
265
286
  }
266
287
 
267
- return <MinimalDisplay latency={latency} sx={sx} {...props} />
288
+ // If rasa latency is available but did not pass type guard, we are in an unexpected state
289
+ // and we should log an error but gracefully fallback to showing no data.
290
+ console.warn(
291
+ `Latency data has unexpected format. Raw data of rasaLatency: ${rasaLatency},
292
+ raw data of voice latency: ${voiceLatency}`,
293
+ )
294
+
295
+ return <Text>Latency data is not available yet</Text>
268
296
  }
@@ -17,7 +17,8 @@ export interface Event {
17
17
  timestamp: string
18
18
  update?: string
19
19
  parse_data?: { commands: Command[] }
20
- metadata?: { utter_action?: string }
20
+ metadata?: { utter_action?: string; execution_times?: RasaLatency }
21
+ name?: string
21
22
  }
22
23
 
23
24
  export interface Command {
@@ -42,11 +43,16 @@ export interface Stack {
42
43
  ended: boolean
43
44
  }
44
45
 
45
- export interface LatencyData {
46
- rasa_processing_latency_ms?: number
47
- asr_latency_ms?: number
48
- tts_first_byte_latency_ms?: number
49
- tts_complete_latency_ms?: number
46
+ export interface RasaLatency {
47
+ command_processor: number
48
+ prediction_loop: number
49
+ }
50
+
51
+ export interface VoiceLatency {
52
+ rasa_processing_latency_ms: number
53
+ asr_latency_ms: number
54
+ tts_first_byte_latency_ms: number
55
+ tts_complete_latency_ms: number
50
56
  }
51
57
 
52
58
  export interface Tracker {
@@ -54,7 +60,6 @@ export interface Tracker {
54
60
  slots: { [key: string]: unknown }
55
61
  events: Event[]
56
62
  stack: Stack[]
57
- latency?: LatencyData
58
63
  }
59
64
 
60
65
  export interface Flow {
@@ -96,3 +101,23 @@ interface Step {
96
101
  utter: string
97
102
  set_slots?: unknown
98
103
  }
104
+
105
+ export function isRasaLatency(obj: any): obj is RasaLatency {
106
+ return (
107
+ obj !== null &&
108
+ typeof obj === 'object' &&
109
+ typeof obj.command_processor === 'number' &&
110
+ typeof obj.prediction_loop === 'number'
111
+ )
112
+ }
113
+
114
+ export function isVoiceLatency(obj: any): obj is VoiceLatency {
115
+ return (
116
+ obj !== null &&
117
+ typeof obj === 'object' &&
118
+ typeof obj.rasa_processing_latency_ms === 'number' &&
119
+ typeof obj.asr_latency_ms === 'number' &&
120
+ typeof obj.tts_first_byte_latency_ms === 'number' &&
121
+ typeof obj.tts_complete_latency_ms === 'number'
122
+ )
123
+ }
@@ -4,7 +4,6 @@ import asyncio
4
4
  import audioop
5
5
  import base64
6
6
  import json
7
- import time
8
7
  import uuid
9
8
  from functools import partial
10
9
  from typing import (
@@ -53,9 +52,7 @@ if TYPE_CHECKING:
53
52
  structlogger = structlog.get_logger()
54
53
 
55
54
 
56
- def tracker_as_dump(
57
- tracker: "DialogueStateTracker", latency: Optional[float] = None
58
- ) -> Dict[str, Any]:
55
+ def tracker_as_dump(tracker: "DialogueStateTracker") -> Dict[str, Any]:
59
56
  """Create a dump of the tracker state."""
60
57
  from rasa.shared.core.trackers import get_trackers_for_conversation_sessions
61
58
 
@@ -66,13 +63,7 @@ def tracker_as_dump(
66
63
  else:
67
64
  last_tracker = multiple_tracker_sessions[-1]
68
65
 
69
- # TODO: this is a bug: the bridge converts this back to json, but it
70
- # should be json in the first place
71
66
  state = last_tracker.current_state(EventVerbosity.AFTER_RESTART)
72
-
73
- if latency is not None:
74
- state["latency"] = {"rasa_processing_latency_ms": latency}
75
-
76
67
  return state
77
68
 
78
69
 
@@ -227,32 +218,16 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
227
218
  def _register_tracker_update_hook(self) -> None:
228
219
  plugin_manager().register(StudioTrackerUpdatePlugin(self))
229
220
 
230
- async def on_tracker_updated(
231
- self, tracker: "DialogueStateTracker", latency: Optional[float] = None
232
- ) -> None:
221
+ async def on_tracker_updated(self, tracker: "DialogueStateTracker") -> None:
233
222
  """Triggers a tracker update notification after a change to the tracker."""
234
- await self.publish_tracker_update(
235
- tracker.sender_id, tracker_as_dump(tracker, latency)
236
- )
223
+ await self.publish_tracker_update(tracker.sender_id, tracker_as_dump(tracker))
237
224
 
238
- async def publish_tracker_update(self, sender_id: str, tracker_dump: str) -> None:
225
+ async def publish_tracker_update(
226
+ self, sender_id: str, tracker_dump: Dict[str, Any]
227
+ ) -> None:
239
228
  """Publishes a tracker update notification to the websocket."""
240
229
  await self.emit("tracker", tracker_dump, room=sender_id)
241
230
 
242
- def _record_turn_start_time(self, sender_id: Text) -> None:
243
- """Records the start time of a new turn."""
244
- self._turn_start_times[sender_id] = time.time()
245
-
246
- def _get_latency(self, sender_id: Text) -> Optional[float]:
247
- """Returns the latency of the current turn in milliseconds."""
248
- if sender_id not in self._turn_start_times:
249
- return None
250
-
251
- latency = (time.time() - self._turn_start_times[sender_id]) * 1000
252
- # The turn is over, so we can remove the start time
253
- del self._turn_start_times[sender_id]
254
- return latency
255
-
256
231
  async def on_message_proxy(
257
232
  self,
258
233
  on_new_message: Callable[[UserMessage], Awaitable[Any]],
@@ -262,7 +237,6 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
262
237
 
263
238
  Triggers a tracker update notification after processing the message.
264
239
  """
265
- self._record_turn_start_time(message.sender_id)
266
240
  try:
267
241
  await on_new_message(message)
268
242
  except Exception as e:
@@ -288,8 +262,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
288
262
  structlogger.error("studio_chat.on_message_proxy.tracker_not_found")
289
263
  return
290
264
 
291
- latency = self._get_latency(message.sender_id)
292
- await self.on_tracker_updated(tracker, latency)
265
+ await self.on_tracker_updated(tracker)
293
266
 
294
267
  async def emit_error(self, message: str, room: str, e: Exception) -> None:
295
268
  await self.emit(
@@ -389,10 +362,10 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
389
362
  call_state.is_bot_speaking = True
390
363
  return ContinueConversationAction()
391
364
 
392
- def _create_output_channel(
365
+ def create_output_channel(
393
366
  self, voice_websocket: "Websocket", tts_engine: TTSEngine
394
367
  ) -> VoiceOutputChannel:
395
- """Create a voice output channel."""
368
+ """Create a voice output channel. This is used by VoiceInputChannel."""
396
369
  return StudioVoiceOutputChannel(
397
370
  voice_websocket,
398
371
  tts_engine,
@@ -469,8 +442,9 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
469
442
  def blueprint(
470
443
  self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
471
444
  ) -> SocketBlueprint:
472
- proxied_on_message = partial(self.on_message_proxy, on_new_message)
473
- socket_blueprint = super().blueprint(proxied_on_message)
445
+ socket_blueprint = super().blueprint(
446
+ partial(self.on_message_proxy, on_new_message)
447
+ )
474
448
 
475
449
  if not self.sio_server:
476
450
  structlogger.error("studio_chat.blueprint.sio_not_initialized")
@@ -506,7 +480,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
506
480
 
507
481
  # start a voice session if requested
508
482
  if data and data.get("is_voice", False):
509
- self._start_voice_session(data["session_id"], sid, proxied_on_message)
483
+ self._start_voice_session(data["session_id"], sid, on_new_message)
510
484
 
511
485
  @self.sio_server.on(self.user_message_evt, namespace=self.namespace)
512
486
  async def handle_message(sid: Text, data: Dict) -> None:
@@ -521,7 +495,7 @@ class StudioChatInput(SocketIOInput, VoiceInputChannel):
521
495
 
522
496
  try:
523
497
  # Handle text messages
524
- await self.handle_user_message(sid, data, proxied_on_message)
498
+ await self.handle_user_message(sid, data, on_new_message)
525
499
  except Exception as e:
526
500
  structlogger.exception(
527
501
  "studio_chat.sio.handle_message.error",
rasa/core/constants.py CHANGED
@@ -112,3 +112,9 @@ ACTIVE_FLOW_METADATA_KEY = "active_flow"
112
112
  STEP_ID_METADATA_KEY = "step_id"
113
113
  KEY_IS_CALM_SYSTEM = "is_calm_system"
114
114
  KEY_IS_COEXISTENCE_ASSISTANT = "is_coexistence_assistant"
115
+
116
+ IAM_CLOUD_PROVIDER_ENV_VAR_NAME = "IAM_CLOUD_PROVIDER"
117
+ SQL_TRACKER_STORE_SSL_MODE_ENV_VAR_NAME = "SQL_TRACKER_STORE_SSL_MODE"
118
+ SQL_TRACKER_STORE_SSL_ROOT_CERTIFICATE_ENV_VAR_NAME = (
119
+ "SQL_TRACKER_STORE_SSL_ROOT_CERTIFICATE"
120
+ )
File without changes
@@ -0,0 +1,66 @@
1
+ from typing import Optional
2
+
3
+ import boto3
4
+ import structlog
5
+ from botocore.exceptions import BotoCoreError
6
+
7
+ from rasa.core.iam_credentials_providers.credentials_provider_protocol import (
8
+ IAMCredentialsProvider,
9
+ IAMCredentialsProviderInput,
10
+ SupportedServiceType,
11
+ TemporaryCredentials,
12
+ )
13
+
14
+ structlogger = structlog.get_logger(__name__)
15
+
16
+
17
+ class AWSRDSIAMCredentialsProvider(IAMCredentialsProvider):
18
+ """Generates temporary credentials for AWS RDS using IAM roles."""
19
+
20
+ def __init__(self, username: str, host: str, port: int):
21
+ """Initializes the provider."""
22
+ self.username = username
23
+ self.host = host
24
+ self.port = port
25
+
26
+ def get_credentials(self) -> TemporaryCredentials:
27
+ """Generates temporary credentials for AWS RDS."""
28
+ structlogger.debug(
29
+ "rasa.core.aws_rds_iam_credentials_provider.get_credentials",
30
+ event_info="IAM authentication for AWS RDS enabled. "
31
+ "Generating temporary auth token...",
32
+ )
33
+
34
+ try:
35
+ client = boto3.client("rds")
36
+ auth_token = client.generate_db_auth_token(
37
+ DBHostname=self.host,
38
+ Port=self.port,
39
+ DBUsername=self.username,
40
+ )
41
+ structlogger.info(
42
+ "rasa.core.aws_rds_iam_credentials_provider.generated_credentials",
43
+ event_info="Successfully generated temporary auth token for AWS RDS.",
44
+ )
45
+ return TemporaryCredentials(auth_token=auth_token)
46
+ except (BotoCoreError, ValueError) as exc:
47
+ structlogger.error(
48
+ "rasa.core.aws_rds_iam_credentials_provider.error_generating_credentials",
49
+ event_info="Failed to generate temporary auth token for AWS RDS.",
50
+ error=str(exc),
51
+ )
52
+ return TemporaryCredentials(auth_token=None)
53
+
54
+
55
+ def create_aws_iam_credentials_provider(
56
+ provider_input: "IAMCredentialsProviderInput",
57
+ ) -> Optional["IAMCredentialsProvider"]:
58
+ """Factory function to create an AWS IAM credentials provider."""
59
+ if provider_input.service_name == SupportedServiceType.TRACKER_STORE:
60
+ return AWSRDSIAMCredentialsProvider(
61
+ username=provider_input.username,
62
+ host=provider_input.host,
63
+ port=provider_input.port,
64
+ )
65
+
66
+ return None
@@ -0,0 +1,89 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from enum import Enum
5
+ from typing import Optional, Protocol, runtime_checkable
6
+
7
+ import structlog
8
+ from pydantic import BaseModel
9
+
10
+ from rasa.core.constants import IAM_CLOUD_PROVIDER_ENV_VAR_NAME
11
+
12
+ structlogger = structlog.get_logger(__name__)
13
+
14
+
15
+ class TemporaryCredentials(BaseModel):
16
+ """Dataclass storing temporary credentials."""
17
+
18
+ auth_token: Optional[str] = None
19
+ expiration: Optional[int] = None
20
+ username: Optional[str] = None
21
+ presigned_url: Optional[str] = None
22
+
23
+
24
+ @runtime_checkable
25
+ class IAMCredentialsProvider(Protocol):
26
+ """Interface for generating temporary credentials using IAM roles."""
27
+
28
+ def get_credentials(self) -> TemporaryCredentials:
29
+ """Generates temporary credentials using IAM roles."""
30
+ ...
31
+
32
+
33
+ class IAMCredentialsProviderType(Enum):
34
+ """Enum for supported IAM credentials provider types."""
35
+
36
+ AWS = "aws"
37
+
38
+
39
+ class SupportedServiceType(Enum):
40
+ """Enum for supported services using IAM credentials providers."""
41
+
42
+ TRACKER_STORE = "tracker_store"
43
+ EVENT_BROKER = "event_broker"
44
+ LOCK_STORE = "lock_store"
45
+
46
+
47
+ class IAMCredentialsProviderInput(BaseModel):
48
+ """Input data for creating an IAM credentials provider."""
49
+
50
+ service_name: SupportedServiceType
51
+ username: Optional[str] = None
52
+ host: Optional[str] = None
53
+ port: Optional[int] = None
54
+
55
+
56
+ def create_iam_credentials_provider(
57
+ provider_input: IAMCredentialsProviderInput,
58
+ ) -> Optional[IAMCredentialsProvider]:
59
+ """Factory function to create an IAM credentials provider.
60
+
61
+ Args:
62
+ provider_input: Input data for creating an IAM credentials provider.
63
+
64
+ Returns:
65
+ An instance of the specified IAM credentials provider or
66
+ None if the type is unsupported.
67
+ """
68
+ iam_cloud_provider = os.getenv(IAM_CLOUD_PROVIDER_ENV_VAR_NAME)
69
+
70
+ if iam_cloud_provider is None:
71
+ return None
72
+
73
+ try:
74
+ provider_type = IAMCredentialsProviderType(iam_cloud_provider.lower())
75
+ except ValueError:
76
+ structlogger.warning(
77
+ "rasa.core.iam_credentials_provider.create_iam_credentials_provider.unsupported_provider",
78
+ event_info=f"Unsupported IAM cloud provider: {iam_cloud_provider}",
79
+ )
80
+ return None
81
+
82
+ if provider_type == IAMCredentialsProviderType.AWS:
83
+ from rasa.core.iam_credentials_providers.aws_iam_credentials_providers import (
84
+ create_aws_iam_credentials_provider,
85
+ )
86
+
87
+ return create_aws_iam_credentials_provider(provider_input)
88
+
89
+ return None
rasa/core/processor.py CHANGED
@@ -72,6 +72,7 @@ from rasa.shared.core.constants import (
72
72
  ACTION_CORRECT_FLOW_SLOT,
73
73
  ACTION_EXTRACT_SLOTS,
74
74
  ACTION_LISTEN_NAME,
75
+ ACTION_METADATA_EXECUTION_TIME,
75
76
  ACTION_SESSION_START_NAME,
76
77
  FOLLOWUP_ACTION,
77
78
  SESSION_START_METADATA_SLOT,
@@ -207,6 +208,7 @@ class MessageProcessor:
207
208
  ) -> Optional[List[Dict[Text, Any]]]:
208
209
  """Handle a single message with this processor."""
209
210
  # preprocess message if necessary
211
+ self.time_turn_start = time.time()
210
212
  tracker = await self.log_message(message, should_save_tracker=False)
211
213
 
212
214
  if self.model_metadata.training_type == TrainingType.NLU:
@@ -1154,6 +1156,7 @@ class MessageProcessor:
1154
1156
  should_predict_another_action = True
1155
1157
 
1156
1158
  tracker = await self.run_command_processor(tracker)
1159
+ self.time_command_processor = time.time()
1157
1160
 
1158
1161
  # action loop. predicts actions until we hit action listen
1159
1162
  while should_predict_another_action and self._should_handle_message(tracker):
@@ -1403,6 +1406,34 @@ class MessageProcessor:
1403
1406
  plugin_manager().hook.after_action_executed(tracker=tracker)
1404
1407
  return self.should_predict_another_action(action.name())
1405
1408
 
1409
+ def _add_metadata_if_action_listen(
1410
+ self, action: Action, prediction: PolicyPrediction
1411
+ ) -> None:
1412
+ """Adds execution times to the ActionExecuted event metadata."""
1413
+ if not hasattr(self, "time_turn_start"):
1414
+ return
1415
+
1416
+ if not hasattr(self, "time_command_processor"):
1417
+ return
1418
+
1419
+ if not action.name() == ACTION_LISTEN_NAME:
1420
+ return
1421
+
1422
+ if prediction.action_metadata is None:
1423
+ prediction.action_metadata = {}
1424
+
1425
+ # calculate execution times
1426
+ execution_time_prediction_loop = (
1427
+ time.time() - self.time_command_processor
1428
+ ) * 1000
1429
+ execution_time_command_processor = (
1430
+ self.time_command_processor - self.time_turn_start
1431
+ ) * 1000
1432
+ prediction.action_metadata[ACTION_METADATA_EXECUTION_TIME] = {
1433
+ "command_processor": execution_time_command_processor,
1434
+ "prediction_loop": execution_time_prediction_loop,
1435
+ }
1436
+
1406
1437
  def _log_action_and_events_on_tracker(
1407
1438
  self,
1408
1439
  tracker: DialogueStateTracker,
@@ -1448,6 +1479,7 @@ class MessageProcessor:
1448
1479
  tracker.update_with_events(prediction.events)
1449
1480
 
1450
1481
  # log the action and its produced events
1482
+ self._add_metadata_if_action_listen(action, prediction)
1451
1483
  tracker.update(
1452
1484
  action.event_for_successful_execution(
1453
1485
  prediction, was_successful, error_message