rasa-pro 3.14.0.dev20250731__py3-none-any.whl → 3.14.0.dev20250825__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 (79) hide show
  1. rasa/core/channels/channel.py +4 -3
  2. rasa/core/channels/constants.py +3 -0
  3. rasa/core/channels/development_inspector.py +48 -15
  4. rasa/core/channels/inspector/dist/assets/{arc-0b11fe30.js → arc-1ddec37b.js} +1 -1
  5. rasa/core/channels/inspector/dist/assets/{blockDiagram-38ab4fdb-9eef30a7.js → blockDiagram-38ab4fdb-18af387c.js} +1 -1
  6. rasa/core/channels/inspector/dist/assets/{c4Diagram-3d4e48cf-03e94f28.js → c4Diagram-3d4e48cf-250127a3.js} +1 -1
  7. rasa/core/channels/inspector/dist/assets/channel-59f6d54b.js +1 -0
  8. rasa/core/channels/inspector/dist/assets/{classDiagram-70f12bd4-95c09eba.js → classDiagram-70f12bd4-c3388b34.js} +1 -1
  9. rasa/core/channels/inspector/dist/assets/{classDiagram-v2-f2320105-38e8446c.js → classDiagram-v2-f2320105-9c893a82.js} +1 -1
  10. rasa/core/channels/inspector/dist/assets/clone-26177ddb.js +1 -0
  11. rasa/core/channels/inspector/dist/assets/{createText-2e5e7dd3-57dc3038.js → createText-2e5e7dd3-c111213b.js} +1 -1
  12. rasa/core/channels/inspector/dist/assets/{edges-e0da2a9e-4bac0545.js → edges-e0da2a9e-812a729d.js} +1 -1
  13. rasa/core/channels/inspector/dist/assets/{erDiagram-9861fffd-81795c90.js → erDiagram-9861fffd-fd5051bc.js} +1 -1
  14. rasa/core/channels/inspector/dist/assets/{flowDb-956e92f1-89489ae6.js → flowDb-956e92f1-3287ac02.js} +1 -1
  15. rasa/core/channels/inspector/dist/assets/{flowDiagram-66a62f08-cd152627.js → flowDiagram-66a62f08-692fb0b2.js} +1 -1
  16. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-29c03f5a.js +1 -0
  17. rasa/core/channels/inspector/dist/assets/{flowchart-elk-definition-4a651766-3da369bc.js → flowchart-elk-definition-4a651766-008376f1.js} +1 -1
  18. rasa/core/channels/inspector/dist/assets/{ganttDiagram-c361ad54-85ec16f8.js → ganttDiagram-c361ad54-df330a69.js} +1 -1
  19. rasa/core/channels/inspector/dist/assets/{gitGraphDiagram-72cf32ee-495bc140.js → gitGraphDiagram-72cf32ee-e03676fb.js} +1 -1
  20. rasa/core/channels/inspector/dist/assets/{graph-1ec4d266.js → graph-46fad2ba.js} +1 -1
  21. rasa/core/channels/inspector/dist/assets/{index-3862675e-0a0e97c9.js → index-3862675e-a484ac55.js} +1 -1
  22. rasa/core/channels/inspector/dist/assets/{index-c804b295.js → index-a003633f.js} +164 -164
  23. rasa/core/channels/inspector/dist/assets/{infoDiagram-f8f76790-4d54bcde.js → infoDiagram-f8f76790-3f9e6ec2.js} +1 -1
  24. rasa/core/channels/inspector/dist/assets/{journeyDiagram-49397b02-dc097114.js → journeyDiagram-49397b02-79f72383.js} +1 -1
  25. rasa/core/channels/inspector/dist/assets/{layout-1a08981e.js → layout-aad098e5.js} +1 -1
  26. rasa/core/channels/inspector/dist/assets/{line-95f7f1d3.js → line-219ab7ae.js} +1 -1
  27. rasa/core/channels/inspector/dist/assets/{linear-97e69543.js → linear-2cddbe62.js} +1 -1
  28. rasa/core/channels/inspector/dist/assets/{mindmap-definition-fc14e90a-8c71ff03.js → mindmap-definition-fc14e90a-1d41ed99.js} +1 -1
  29. rasa/core/channels/inspector/dist/assets/{pieDiagram-8a3498a8-f14c71c7.js → pieDiagram-8a3498a8-cc496ee8.js} +1 -1
  30. rasa/core/channels/inspector/dist/assets/{quadrantDiagram-120e2f19-f1d3c9ff.js → quadrantDiagram-120e2f19-84d32884.js} +1 -1
  31. rasa/core/channels/inspector/dist/assets/{requirementDiagram-deff3bca-bfa2412f.js → requirementDiagram-deff3bca-c0deb984.js} +1 -1
  32. rasa/core/channels/inspector/dist/assets/{sankeyDiagram-04a897e0-53f2c97b.js → sankeyDiagram-04a897e0-b9d7fd62.js} +1 -1
  33. rasa/core/channels/inspector/dist/assets/{sequenceDiagram-704730f1-319d7c0e.js → sequenceDiagram-704730f1-7d517565.js} +1 -1
  34. rasa/core/channels/inspector/dist/assets/{stateDiagram-587899a1-76a09418.js → stateDiagram-587899a1-98ef9b27.js} +1 -1
  35. rasa/core/channels/inspector/dist/assets/{stateDiagram-v2-d93cdb3a-a67f15d4.js → stateDiagram-v2-d93cdb3a-cee70748.js} +1 -1
  36. rasa/core/channels/inspector/dist/assets/{styles-6aaf32cf-0654e7c3.js → styles-6aaf32cf-3f9d1c96.js} +1 -1
  37. rasa/core/channels/inspector/dist/assets/{styles-9a916d00-1394bb9d.js → styles-9a916d00-67471923.js} +1 -1
  38. rasa/core/channels/inspector/dist/assets/{styles-c10674c1-e4c5bdae.js → styles-c10674c1-bd093fb7.js} +1 -1
  39. rasa/core/channels/inspector/dist/assets/{svgDrawCommon-08f97a94-50957104.js → svgDrawCommon-08f97a94-675794e8.js} +1 -1
  40. rasa/core/channels/inspector/dist/assets/{timeline-definition-85554ec2-b0885a6a.js → timeline-definition-85554ec2-0ac67617.js} +1 -1
  41. rasa/core/channels/inspector/dist/assets/{xychartDiagram-e933f94c-79e6541a.js → xychartDiagram-e933f94c-c018dc37.js} +1 -1
  42. rasa/core/channels/inspector/dist/index.html +2 -2
  43. rasa/core/channels/inspector/index.html +1 -1
  44. rasa/core/channels/inspector/src/App.tsx +53 -7
  45. rasa/core/channels/inspector/src/components/Chat.tsx +3 -2
  46. rasa/core/channels/inspector/src/components/DiagramFlow.tsx +1 -1
  47. rasa/core/channels/inspector/src/components/LatencyDisplay.tsx +268 -0
  48. rasa/core/channels/inspector/src/components/LoadingSpinner.tsx +6 -2
  49. rasa/core/channels/inspector/src/helpers/audio/audiostream.ts +8 -3
  50. rasa/core/channels/inspector/src/types.ts +8 -0
  51. rasa/core/channels/socketio.py +212 -51
  52. rasa/core/channels/studio_chat.py +77 -31
  53. rasa/core/channels/voice_stream/audiocodes.py +2 -2
  54. rasa/core/channels/voice_stream/browser_audio.py +20 -3
  55. rasa/core/channels/voice_stream/call_state.py +13 -2
  56. rasa/core/channels/voice_stream/genesys.py +2 -2
  57. rasa/core/channels/voice_stream/jambonz.py +2 -2
  58. rasa/core/channels/voice_stream/twilio_media_streams.py +2 -2
  59. rasa/core/channels/voice_stream/voice_channel.py +88 -16
  60. rasa/core/nlg/contextual_response_rephraser.py +13 -2
  61. rasa/core/run.py +13 -3
  62. rasa/dialogue_understanding/patterns/default_flows_for_patterns.yml +1 -1
  63. rasa/dialogue_understanding/processor/command_processor.py +27 -11
  64. rasa/model_manager/model_api.py +3 -3
  65. rasa/model_manager/socket_bridge.py +21 -16
  66. rasa/shared/providers/_utils.py +60 -44
  67. rasa/shared/providers/embedding/default_litellm_embedding_client.py +2 -0
  68. rasa/shared/providers/llm/default_litellm_llm_client.py +2 -0
  69. rasa/studio/upload.py +7 -4
  70. rasa/studio/utils.py +33 -22
  71. rasa/version.py +1 -1
  72. {rasa_pro-3.14.0.dev20250731.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/METADATA +6 -6
  73. {rasa_pro-3.14.0.dev20250731.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/RECORD +76 -74
  74. rasa/core/channels/inspector/dist/assets/channel-51d02e9e.js +0 -1
  75. rasa/core/channels/inspector/dist/assets/clone-cc738fa6.js +0 -1
  76. rasa/core/channels/inspector/dist/assets/flowDiagram-v2-96b9c2cf-0c716443.js +0 -1
  77. {rasa_pro-3.14.0.dev20250731.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/NOTICE +0 -0
  78. {rasa_pro-3.14.0.dev20250731.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/WHEEL +0 -0
  79. {rasa_pro-3.14.0.dev20250731.dist-info → rasa_pro-3.14.0.dev20250825.dist-info}/entry_points.txt +0 -0
@@ -5,7 +5,7 @@ import {
5
5
  useColorModeValue,
6
6
  useToast,
7
7
  } from '@chakra-ui/react'
8
- import { useEffect, useState } from 'react'
8
+ import { useEffect, useState, useCallback } from 'react'
9
9
  import axios from 'axios'
10
10
  import { useOurTheme } from './theme'
11
11
  import { Welcome } from './components/Welcome'
@@ -23,6 +23,7 @@ import {
23
23
  } from './helpers/utils'
24
24
  import queryString from 'query-string'
25
25
  import { Chat } from './components/Chat'
26
+ import { LatencyDisplay } from './components/LatencyDisplay'
26
27
  import useWebSocket, { ReadyState } from 'react-use-websocket'
27
28
 
28
29
  export function App() {
@@ -35,6 +36,7 @@ export function App() {
35
36
  const [story, setStory] = useState<string>('')
36
37
  const [stack, setStack] = useState<Stack[]>([])
37
38
  const [frame, setFrame] = useState<SelectedStack | undefined>(undefined)
39
+ const [latency, setLatency] = useState<any>(null)
38
40
 
39
41
  // State to control the visibility of the RecruitmentPanel
40
42
  const [showRecruitmentPanel, setShowRecruitmentPanel] = useState(true)
@@ -127,6 +129,13 @@ export function App() {
127
129
  !rasaChatSessionId ||
128
130
  lastJsonMessage?.sender_id === rasaChatSessionId
129
131
  ) {
132
+ if (lastJsonMessage.latency) {
133
+ console.log('Latency update:', lastJsonMessage.latency)
134
+ setLatency((prevLatency: any) => ({
135
+ ...prevLatency,
136
+ ...lastJsonMessage.latency,
137
+ }))
138
+ }
130
139
  setSlots(formatSlots(lastJsonMessage.slots))
131
140
  setEvents(lastJsonMessage.events)
132
141
  const updatedStack = createHistoricalStack(
@@ -175,6 +184,21 @@ export function App() {
175
184
  : 'max-content minmax(10rem, 17.5rem) minmax(10rem, auto)',
176
185
  gridRowGap: rasaSpace[1],
177
186
  }
187
+ const rightColumnSx = {
188
+ height: '100%',
189
+ overflow: 'hidden',
190
+ gridTemplateColumns: '1fr',
191
+ gridTemplateRows: 'max-content 1fr',
192
+ gridRowGap: rasaSpace[1],
193
+ }
194
+
195
+ const chatContainerSx = {
196
+ ...borderRadiusSx,
197
+ padding: rasaSpace[1],
198
+ bg: useColorModeValue('neutral.50', 'neutral.50'),
199
+ overflow: 'auto', // Allow scrolling for chat
200
+ height: '100%',
201
+ }
178
202
 
179
203
  const onFrameSelected = (stack: Stack) => {
180
204
  setFrame({
@@ -188,8 +212,25 @@ export function App() {
188
212
  setShowRecruitmentPanel(false)
189
213
  }
190
214
 
215
+ const onLatencyUpdate = useCallback((newLatency: any) => {
216
+ setLatency((prevLatency: any) => ({
217
+ ...prevLatency,
218
+ ...newLatency,
219
+ }))
220
+ }, [])
221
+
222
+ // Make latency update function available globally for audio stream
223
+ useEffect(() => {
224
+ if (window.location.href.includes('browser_audio')) {
225
+ ;(window as any).updateLatency = onLatencyUpdate
226
+ }
227
+ return () => {
228
+ delete (window as any).updateLatency
229
+ }
230
+ }, [onLatencyUpdate])
231
+
191
232
  if (!rasaChatSessionId && !window.location.href.includes('socketio'))
192
- return <LoadingSpinner />
233
+ return <LoadingSpinner onLatencyUpdate={onLatencyUpdate} />
193
234
 
194
235
  return (
195
236
  <Grid sx={gridSx}>
@@ -222,11 +263,16 @@ export function App() {
222
263
  slots={slots}
223
264
  />
224
265
  </GridItem>
225
- {shouldShowTranscript && (
226
- <GridItem>
227
- <Chat events={events || []} />
228
- </GridItem>
229
- )}
266
+ <GridItem overflow="hidden">
267
+ <Grid sx={rightColumnSx}>
268
+ <LatencyDisplay latency={latency} sx={boxSx} />
269
+ {shouldShowTranscript && (
270
+ <GridItem sx={chatContainerSx}>
271
+ <Chat events={events || []} />
272
+ </GridItem>
273
+ )}
274
+ </Grid>
275
+ </GridItem>
230
276
  </Grid>
231
277
  )
232
278
  }
@@ -5,6 +5,7 @@ import { Command, Event } from '../types'
5
5
 
6
6
  interface Props extends FlexProps {
7
7
  events: Event[]
8
+ hasLatencyDisplay?: boolean
8
9
  }
9
10
 
10
11
  export const Chat = ({ sx, events, ...props }: Props) => {
@@ -12,9 +13,9 @@ export const Chat = ({ sx, events, ...props }: Props) => {
12
13
  ...sx,
13
14
  p: 0,
14
15
  flexDirection: 'column',
16
+ height: '100%',
15
17
  }
16
18
 
17
- const maxHeight = document.documentElement.scrollHeight - 64
18
19
  // 21 and 25 are the rem number we're using for the columns. We add 0.75rem for the padding
19
20
  // A potential improvement would be to add a onresize event for both width and height
20
21
  let remReference = 21.75
@@ -110,7 +111,7 @@ export const Chat = ({ sx, events, ...props }: Props) => {
110
111
  borderRadius: '10px',
111
112
  border: 'none',
112
113
  width: columnWidth,
113
- height: maxHeight,
114
+ height: '100%',
114
115
  }}
115
116
  history={messages}
116
117
  demo={true}
@@ -22,7 +22,7 @@ export const DiagramFlow = ({ stackFrame, stepTrail, flows, slots }: Props) => {
22
22
 
23
23
  const config = {
24
24
  startOnLoad: true,
25
- logLevel: 'info',
25
+ logLevel: 'warning',
26
26
  flowchart: {
27
27
  useMaxWidth: false,
28
28
  },
@@ -0,0 +1,268 @@
1
+ import {
2
+ Box,
3
+ Flex,
4
+ FlexProps,
5
+ Text,
6
+ useColorModeValue,
7
+ Tooltip,
8
+ Table,
9
+ Tbody,
10
+ Tr,
11
+ Td,
12
+ } from '@chakra-ui/react'
13
+ import { useOurTheme } from '../theme'
14
+ import { LatencyData } from '../types'
15
+
16
+ interface Props extends FlexProps {
17
+ latency: LatencyData
18
+ }
19
+
20
+ /**
21
+ * Simple latency display for text-only conversations.
22
+ * Shows a single response time value.
23
+ */
24
+ const MinimalDisplay = ({ latency, sx, ...props }: Props) => {
25
+ const containerSx = {
26
+ ...sx,
27
+ display: 'flex',
28
+ alignItems: 'center',
29
+ }
30
+
31
+ const getLatencyColor = (latency: number) => {
32
+ if (latency < 1500) return 'green.500'
33
+ if (latency < 2500) return 'orange.500'
34
+ return 'red.500'
35
+ }
36
+
37
+ const value = Math.round(latency.rasa_processing_latency_ms || 0);
38
+ const color = getLatencyColor(value);
39
+
40
+ return (
41
+ <Flex sx={containerSx} {...props}>
42
+ <Text fontSize="md" color={useColorModeValue('gray.700', 'gray.300')}>
43
+ Response latency:
44
+ <Text as="span" ml={2} fontWeight="bold" color={color}>
45
+ {value}ms
46
+ </Text>
47
+ </Text>
48
+ </Flex>
49
+ )
50
+ }
51
+
52
+ /**
53
+ * Detailed latency waterfall chart for voice conversations.
54
+ * Displays processing times for ASR, Rasa, and TTS components.
55
+ */
56
+ const WaterfallDisplay = ({ latency, sx, ...props }: Props) => {
57
+ const { rasaSpace } = useOurTheme()
58
+
59
+ const containerSx = {
60
+ ...sx,
61
+ flexDirection: 'column',
62
+ gap: rasaSpace[1],
63
+ }
64
+
65
+ const headerSx = {
66
+ fontSize: 'sm',
67
+ fontWeight: 'bold',
68
+ color: useColorModeValue('gray.700', 'gray.300'),
69
+ }
70
+
71
+ const waterfallBarSx = {
72
+ height: '24px',
73
+ borderRadius: '4px',
74
+ overflow: 'hidden',
75
+ border: '1px solid',
76
+ borderColor: useColorModeValue('gray.200', 'gray.600'),
77
+ }
78
+
79
+ const legendTableSx = {
80
+ size: 'sm',
81
+ mt: rasaSpace[0.5]
82
+ }
83
+
84
+ const getLatencyColor = (type: string) => {
85
+ const colors: { [key: string]: string } = {
86
+ asr: 'blue.500',
87
+ rasa: 'purple.500',
88
+ tts_first: 'orange.500',
89
+ tts_complete: 'green.500',
90
+ }
91
+ return colors[type] || 'gray.500'
92
+ }
93
+
94
+ const getLatencyDescription = (type: string) => {
95
+ const descriptions: { [key: string]: string } = {
96
+ 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
+ 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.'
100
+ }
101
+ return descriptions[type] || ''
102
+ }
103
+
104
+ // Metrics for proportional display (only actual processing latencies)
105
+ const chartMetrics = [
106
+ latency.asr_latency_ms && {
107
+ type: 'asr',
108
+ label: 'ASR',
109
+ value: latency.asr_latency_ms,
110
+ },
111
+ latency.rasa_processing_latency_ms && {
112
+ type: 'rasa',
113
+ label: 'Rasa',
114
+ value: latency.rasa_processing_latency_ms,
115
+ },
116
+ latency.tts_complete_latency_ms && {
117
+ type: 'tts_complete',
118
+ label: 'TTS',
119
+ value: latency.tts_complete_latency_ms,
120
+ },
121
+ ].filter(Boolean)
122
+
123
+ // All metrics for legend display
124
+ const allMetrics = [
125
+ latency.asr_latency_ms && {
126
+ type: 'asr',
127
+ label: 'ASR',
128
+ value: latency.asr_latency_ms,
129
+ },
130
+ latency.rasa_processing_latency_ms && {
131
+ type: 'rasa',
132
+ label: 'Rasa',
133
+ value: latency.rasa_processing_latency_ms,
134
+ },
135
+ latency.tts_first_byte_latency_ms && {
136
+ type: 'tts_first',
137
+ label: 'TTS First Byte',
138
+ value: latency.tts_first_byte_latency_ms,
139
+ },
140
+ latency.tts_complete_latency_ms && {
141
+ type: 'tts_complete',
142
+ label: 'TTS Complete',
143
+ value: latency.tts_complete_latency_ms,
144
+ },
145
+ ].filter(Boolean)
146
+
147
+ // Calculate total for proportional sizing (only processing latencies)
148
+ const totalLatency = chartMetrics.reduce(
149
+ (sum: number, metric: any) => sum + metric.value,
150
+ 0,
151
+ )
152
+
153
+ // 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
+
158
+ return (
159
+ <Flex sx={containerSx} {...props}>
160
+ <Text sx={headerSx}>
161
+ Response latency: ~{Math.round(totalDisplayLatency)}ms
162
+ </Text>
163
+
164
+ {/* Waterfall Bar */}
165
+ <Box>
166
+ <Flex sx={waterfallBarSx}>
167
+ {chartMetrics.map((metric: any) => {
168
+ const widthPercentage =
169
+ totalLatency > 0 ? (metric.value / totalLatency) * 100 : 0
170
+ return (
171
+ <Tooltip
172
+ key={metric.type}
173
+ label={
174
+ <Box>
175
+ <Text fontWeight="bold">
176
+ {metric.label}: {Math.round(metric.value)}ms
177
+ </Text>
178
+ <Text fontSize="xs" mt={1}>
179
+ {getLatencyDescription(metric.type)}
180
+ </Text>
181
+ </Box>
182
+ }
183
+ hasArrow
184
+ placement="top"
185
+ >
186
+ <Box
187
+ bg={getLatencyColor(metric.type)}
188
+ width={`${widthPercentage}%`}
189
+ height="100%"
190
+ minWidth="40px" // Increased minimum width for better visibility
191
+ cursor="pointer"
192
+ _hover={{ opacity: 0.8 }}
193
+ />
194
+ </Tooltip>
195
+ )
196
+ })}
197
+ </Flex>
198
+
199
+ {/* Legend */}
200
+ <Table sx={legendTableSx}>
201
+ <Tbody>
202
+ {/* Split metrics into pairs for 2x2 table */}
203
+ {Array.from(
204
+ { length: Math.ceil(allMetrics.length / 2) },
205
+ (_, rowIndex) => (
206
+ <Tr key={rowIndex}>
207
+ {allMetrics
208
+ .slice(rowIndex * 2, rowIndex * 2 + 2)
209
+ .map((metric: any) => (
210
+ <Td key={metric.type} p={rasaSpace[0.25]} border="none">
211
+ <Flex align="center" gap={rasaSpace[0.25]}>
212
+ <Box
213
+ width="8px"
214
+ height="8px"
215
+ bg={getLatencyColor(metric.type)}
216
+ borderRadius="2px"
217
+ flexShrink={0}
218
+ />
219
+ <Text
220
+ fontSize="xs"
221
+ color={useColorModeValue('gray.600', 'gray.400')}
222
+ noOfLines={1}
223
+ >
224
+ {metric.label}: {Math.round(metric.value)}ms
225
+ </Text>
226
+ </Flex>
227
+ </Td>
228
+ ))}
229
+ {/* Fill empty cell if odd number of metrics */}
230
+ {allMetrics.length % 2 !== 0 &&
231
+ rowIndex === Math.ceil(allMetrics.length / 2) - 1 && (
232
+ <Td p={rasaSpace[0.25]} border="none" />
233
+ )}
234
+ </Tr>
235
+ ),
236
+ )}
237
+ </Tbody>
238
+ </Table>
239
+ </Box>
240
+ </Flex>
241
+ )
242
+ }
243
+
244
+ /**
245
+ * Displays processing latency information for the conversation.
246
+ * Shows either a detailed waterfall chart for voice conversations or
247
+ * a simpler display for text-only conversations.
248
+ */
249
+ export const LatencyDisplay = ({
250
+ sx,
251
+ latency,
252
+ ...props
253
+ }: Props) => {
254
+ if (!latency) {
255
+ console.warn('Latency data is not available')
256
+ return null
257
+ }
258
+
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
262
+
263
+ if (isVoiceMetricsAvailable) {
264
+ return <WaterfallDisplay latency={latency} sx={sx} {...props} />
265
+ }
266
+
267
+ return <MinimalDisplay latency={latency} sx={sx} {...props} />
268
+ }
@@ -8,7 +8,11 @@ import {
8
8
  import { useOurTheme } from '../theme'
9
9
  import { createAudioConnection } from '../helpers/audio/audiostream.ts'
10
10
 
11
- export const LoadingSpinner = () => {
11
+ interface LoadingSpinnerProps {
12
+ onLatencyUpdate?: (latency: any) => void
13
+ }
14
+
15
+ export const LoadingSpinner = ({ onLatencyUpdate }: LoadingSpinnerProps) => {
12
16
  const { rasaSpace } = useOurTheme()
13
17
  const isVoice = window.location.href.includes('browser_audio')
14
18
  const text = isVoice
@@ -27,7 +31,7 @@ export const LoadingSpinner = () => {
27
31
  {isVoice ? (
28
32
  <Button
29
33
  onClick={async () =>
30
- await createAudioConnection(window.location.href)
34
+ await createAudioConnection(window.location.href, onLatencyUpdate)
31
35
  }
32
36
  >
33
37
  Go
@@ -187,7 +187,7 @@ const setupAudioPlayback = async (socket: WebSocket): Promise<AudioQueue> => {
187
187
  }
188
188
 
189
189
  const addDataToAudioQueue =
190
- (audioQueue: AudioQueue) => (message: MessageEvent<any>) => {
190
+ (audioQueue: AudioQueue, onLatencyUpdate?: (latency: any) => void) => (message: MessageEvent<any>) => {
191
191
  try {
192
192
  const data = JSON.parse(message.data.toString())
193
193
  if (data['error']) {
@@ -199,6 +199,10 @@ const addDataToAudioQueue =
199
199
  const audioData = intToFloatArray(int32Data)
200
200
  audioQueue.write(audioData)
201
201
  } else if (data['marker']) {
202
+ if (data['latency'] && onLatencyUpdate) {
203
+ onLatencyUpdate(data['latency'])
204
+ }
205
+ console.log('Voice Latency Metrics:', data['latency'])
202
206
  audioQueue.addMarker(data['marker'])
203
207
  }
204
208
  } catch (error) {
@@ -231,8 +235,9 @@ function getWebSocketUrl(baseUrl: string) {
231
235
  * Creates a WebSocket connection for browser audio and streams microphone input to the server
232
236
  *
233
237
  * @param baseUrl - The base URL (e.g., "https://example.com" or "http://localhost:5005")
238
+ * @param onLatencyUpdate - Optional callback function to receive latency updates
234
239
  */
235
- export async function createAudioConnection(baseUrl: string) {
240
+ export async function createAudioConnection(baseUrl: string, onLatencyUpdate?: (latency: any) => void) {
236
241
  const websocketURL = getWebSocketUrl(baseUrl)
237
242
  const socket = new WebSocket(websocketURL)
238
243
 
@@ -241,5 +246,5 @@ export async function createAudioConnection(baseUrl: string) {
241
246
  }
242
247
 
243
248
  const audioQueue = await setupAudioPlayback(socket)
244
- socket.onmessage = addDataToAudioQueue(audioQueue)
249
+ socket.onmessage = addDataToAudioQueue(audioQueue, onLatencyUpdate)
245
250
  }
@@ -42,11 +42,19 @@ export interface Stack {
42
42
  ended: boolean
43
43
  }
44
44
 
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
50
+ }
51
+
45
52
  export interface Tracker {
46
53
  sender_id: string
47
54
  slots: { [key: string]: unknown }
48
55
  events: Event[]
49
56
  stack: Stack[]
57
+ latency?: LatencyData
50
58
  }
51
59
 
52
60
  export interface Flow {