agora-appbuilder-core 4.1.0-beta-2 → 4.1.0-beta-4
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.
- package/package.json +1 -1
- package/template/customization-api/atoms.ts +1 -0
- package/template/customization-api/customize.tsx +11 -0
- package/template/customization-api/sub-components.ts +2 -0
- package/template/src/ai-agent/assets/join-call.png +0 -0
- package/template/src/ai-agent/assets/leave-call.png +0 -0
- package/template/src/ai-agent/components/AgentControls/AgentConnectionWrapper.tsx +248 -0
- package/template/src/ai-agent/components/AgentControls/AgentContext.tsx +23 -1
- package/template/src/ai-agent/components/AgentControls/const.ts +38 -5
- package/template/src/ai-agent/components/AgentControls/index.tsx +60 -257
- package/template/src/ai-agent/components/AudioVisualizer.tsx +32 -20
- package/template/src/ai-agent/components/Bottombar.tsx +114 -51
- package/template/src/ai-agent/components/{CustomSidePanel.tsx → CustomChatPanel.tsx} +3 -3
- package/template/src/ai-agent/components/CustomCreate.tsx +7 -185
- package/template/src/ai-agent/components/CustomSettingsPanel.tsx +193 -0
- package/template/src/ai-agent/components/SelectAiAgent.tsx +74 -0
- package/template/src/ai-agent/components/SelectAiAgentVoice.tsx +68 -0
- package/template/src/ai-agent/components/UserPrompt.tsx +64 -0
- package/template/src/ai-agent/components/agent-chat-panel/agent-chat-ui.tsx +13 -11
- package/template/src/ai-agent/components/mobile/Bottombar.tsx +55 -39
- package/template/src/ai-agent/components/mobile/MobileLayoutComponent.tsx +1 -1
- package/template/src/ai-agent/components/mobile/Topbar.tsx +10 -27
- package/template/src/ai-agent/components/utils.ts +17 -0
- package/template/src/ai-agent/index.tsx +61 -206
- package/template/src/atoms/Dropdown.tsx +1 -1
- package/template/src/auth/AuthProvider.tsx +19 -0
- package/template/src/components/SettingsView.tsx +7 -3
- package/template/src/components/room-info/useRoomInfo.tsx +21 -0
- package/template/src/pages/video-call/VideoCallScreen.tsx +18 -3
- package/template/src/subComponents/FallbackLogo.tsx +1 -3
- package/template/src/subComponents/SelectDevice.tsx +7 -7
- package/template/src/subComponents/SidePanelHeader.tsx +2 -2
- package/template/src/utils/useJoinRoom.ts +40 -8
- package/template/src/ai-agent/components/AgentControls/LeaveCall.png +0 -0
- package/template/src/ai-agent/components/AgentControls/Vector.svg +0 -3
- package/template/src/ai-agent/components/CustomCreateNative.tsx +0 -265
- package/template/src/ai-agent/components/icons.tsx +0 -227
package/package.json
CHANGED
|
@@ -7,3 +7,4 @@ export {default as PrimaryButton} from '../src/atoms/PrimaryButton';
|
|
|
7
7
|
export {default as TertiaryButton} from '../src/atoms/TertiaryButton';
|
|
8
8
|
export {default as ActionMenu} from '../src/atoms/ActionMenu';
|
|
9
9
|
export {default as IconButton} from '../src/atoms/IconButton';
|
|
10
|
+
export {default as Dropdown} from '../src/atoms/Dropdown';
|
|
@@ -177,6 +177,17 @@ const mergeCustomization = (
|
|
|
177
177
|
);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
if (externalConfig?.components?.videoCall?.wrapper) {
|
|
181
|
+
const AiAgentVideoCallWrapper = aiAgentConfig.components.videoCall.wrapper;
|
|
182
|
+
const ExternalVideoCallWrapper =
|
|
183
|
+
externalConfig.components.videoCall.wrapper;
|
|
184
|
+
mergedData.components.videoCall.wrapper = props => (
|
|
185
|
+
<AiAgentVideoCallWrapper>
|
|
186
|
+
<ExternalVideoCallWrapper>{props.children}</ExternalVideoCallWrapper>
|
|
187
|
+
</AiAgentVideoCallWrapper>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
180
191
|
//override the i18n
|
|
181
192
|
if (externalConfig?.i18n && externalConfig?.i18n?.length) {
|
|
182
193
|
mergedData.i18n = externalConfig.i18n;
|
|
@@ -91,3 +91,5 @@ export {default as Loading} from '../src/subComponents/Loading';
|
|
|
91
91
|
export {default as UserAvatar} from '../src/atoms/UserAvatar';
|
|
92
92
|
export {default as Card} from '../src/atoms/Card';
|
|
93
93
|
export {default as ThemeConfig} from '../src/theme';
|
|
94
|
+
export {default as SelectDevice} from '../src/subComponents/SelectDevice';
|
|
95
|
+
export {EditName} from '../src/components/SettingsView';
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import {UidType, useContent, useRoomInfo, Toast} from 'customization-api';
|
|
2
|
+
import React, {createContext, useContext, useEffect} from 'react';
|
|
3
|
+
import {AgentContext} from './AgentContext';
|
|
4
|
+
import {AgentState} from './const';
|
|
5
|
+
import StorageContext from '../../../components/StorageContext';
|
|
6
|
+
|
|
7
|
+
export interface AgentContextInterface {
|
|
8
|
+
toggleAgentConnection: (forceStop?: boolean) => Promise<boolean>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const AgentConnectionContext = createContext<AgentContextInterface>({
|
|
12
|
+
toggleAgentConnection: () => {
|
|
13
|
+
return Promise.resolve(false);
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const AgentConnectionProvider: React.FC<{children: React.ReactNode}> = ({
|
|
18
|
+
children,
|
|
19
|
+
}) => {
|
|
20
|
+
const {activeUids: users} = useContent();
|
|
21
|
+
const {
|
|
22
|
+
agentUID,
|
|
23
|
+
agentConnectionState,
|
|
24
|
+
setAgentConnectionState,
|
|
25
|
+
agentId,
|
|
26
|
+
setAgentUID,
|
|
27
|
+
prompt,
|
|
28
|
+
} = useContext(AgentContext);
|
|
29
|
+
const {
|
|
30
|
+
data: {channel: channel_name, uid: localUid, agents},
|
|
31
|
+
} = useRoomInfo();
|
|
32
|
+
const {store} = useContext(StorageContext);
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
console.log('debugging users agent contrl', {users});
|
|
36
|
+
// welcome agent
|
|
37
|
+
const aiAgentUID = users.filter(item => item === agentUID);
|
|
38
|
+
|
|
39
|
+
if (
|
|
40
|
+
aiAgentUID.length &&
|
|
41
|
+
agentConnectionState === AgentState.AWAITING_JOIN
|
|
42
|
+
) {
|
|
43
|
+
setAgentConnectionState(AgentState.AGENT_CONNECTED);
|
|
44
|
+
|
|
45
|
+
Toast.show({
|
|
46
|
+
leadingIconName: 'tick-fill',
|
|
47
|
+
type: 'success',
|
|
48
|
+
text1: 'Say Hi!!',
|
|
49
|
+
text2: null,
|
|
50
|
+
visibilityTime: 3000,
|
|
51
|
+
primaryBtn: null,
|
|
52
|
+
secondaryBtn: null,
|
|
53
|
+
leadingIcon: null,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// when agent leaves, show left toast, and set agent to not connected state
|
|
57
|
+
if (
|
|
58
|
+
!aiAgentUID.length &&
|
|
59
|
+
agentConnectionState === AgentState.AWAITING_LEAVE
|
|
60
|
+
) {
|
|
61
|
+
setAgentConnectionState(AgentState.NOT_CONNECTED);
|
|
62
|
+
}
|
|
63
|
+
}, [users, agentUID]);
|
|
64
|
+
|
|
65
|
+
const handleConnectionToggle = async (forceStop: boolean = false) => {
|
|
66
|
+
try {
|
|
67
|
+
// connect to agent when agent is in not connected state or when earlier connect failed
|
|
68
|
+
if (
|
|
69
|
+
agentConnectionState === AgentState.NOT_CONNECTED ||
|
|
70
|
+
agentConnectionState === AgentState.AGENT_REQUEST_FAILED ||
|
|
71
|
+
agentConnectionState === AgentState.AWAITING_LEAVE
|
|
72
|
+
) {
|
|
73
|
+
try {
|
|
74
|
+
setAgentConnectionState(AgentState.REQUEST_SENT);
|
|
75
|
+
const data = await connectToAIAgent(
|
|
76
|
+
'start',
|
|
77
|
+
channel_name,
|
|
78
|
+
localUid,
|
|
79
|
+
store.token,
|
|
80
|
+
{
|
|
81
|
+
agent_id: agentId,
|
|
82
|
+
prompt: prompt,
|
|
83
|
+
voice: agents.find(a => a.id === agentId)?.config?.tts?.params
|
|
84
|
+
?.voice_name,
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
// console.log("response X-Client-ID", newClientId, typeof newClientId)
|
|
88
|
+
// @ts-ignore
|
|
89
|
+
const {agent_uid = null} = data;
|
|
90
|
+
|
|
91
|
+
//setClientId(agent_id);
|
|
92
|
+
setAgentUID(agent_uid);
|
|
93
|
+
|
|
94
|
+
setAgentConnectionState(AgentState.AWAITING_JOIN);
|
|
95
|
+
|
|
96
|
+
Toast.show({
|
|
97
|
+
leadingIconName: 'tick-fill',
|
|
98
|
+
type: 'success',
|
|
99
|
+
text1: 'Agent requested to join',
|
|
100
|
+
text2: null,
|
|
101
|
+
visibilityTime: 3000,
|
|
102
|
+
primaryBtn: null,
|
|
103
|
+
secondaryBtn: null,
|
|
104
|
+
leadingIcon: null,
|
|
105
|
+
});
|
|
106
|
+
return Promise.resolve(true);
|
|
107
|
+
} catch (agentConnectError) {
|
|
108
|
+
setAgentConnectionState(AgentState.AGENT_REQUEST_FAILED);
|
|
109
|
+
|
|
110
|
+
if (agentConnectError.toString().indexOf('401') !== -1) {
|
|
111
|
+
Toast.show({
|
|
112
|
+
leadingIconName: 'alert',
|
|
113
|
+
type: 'error',
|
|
114
|
+
text1: 'Your session is expired. Please sign in to join call.',
|
|
115
|
+
text2: null,
|
|
116
|
+
visibilityTime: 5000,
|
|
117
|
+
primaryBtn: null,
|
|
118
|
+
secondaryBtn: null,
|
|
119
|
+
leadingIcon: null,
|
|
120
|
+
});
|
|
121
|
+
} else {
|
|
122
|
+
Toast.show({
|
|
123
|
+
leadingIconName: 'alert',
|
|
124
|
+
type: 'error',
|
|
125
|
+
text1: 'Uh oh! Agent failed to connect',
|
|
126
|
+
text2: null,
|
|
127
|
+
visibilityTime: 5000,
|
|
128
|
+
primaryBtn: null,
|
|
129
|
+
secondaryBtn: null,
|
|
130
|
+
leadingIcon: null,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
throw agentConnectError;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// disconnect agent with agent is already connected or when earlier disconnect failed
|
|
138
|
+
if (
|
|
139
|
+
forceStop === true ||
|
|
140
|
+
agentConnectionState === AgentState.AGENT_CONNECTED ||
|
|
141
|
+
agentConnectionState === AgentState.AGENT_DISCONNECT_FAILED
|
|
142
|
+
) {
|
|
143
|
+
try {
|
|
144
|
+
setAgentConnectionState(AgentState.AGENT_DISCONNECT_REQUEST);
|
|
145
|
+
await connectToAIAgent('stop', channel_name, localUid, store.token, {
|
|
146
|
+
agent_id: agentId,
|
|
147
|
+
});
|
|
148
|
+
setAgentConnectionState(AgentState.AWAITING_LEAVE);
|
|
149
|
+
if (!forceStop) {
|
|
150
|
+
Toast.show({
|
|
151
|
+
leadingIconName: 'tick-fill',
|
|
152
|
+
type: 'success',
|
|
153
|
+
text1: 'Agent disconnected',
|
|
154
|
+
text2: null,
|
|
155
|
+
visibilityTime: 3000,
|
|
156
|
+
primaryBtn: null,
|
|
157
|
+
secondaryBtn: null,
|
|
158
|
+
leadingIcon: null,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return Promise.resolve(true);
|
|
162
|
+
} catch (agentDisconnectError) {
|
|
163
|
+
setAgentConnectionState(AgentState.AGENT_DISCONNECT_FAILED);
|
|
164
|
+
|
|
165
|
+
Toast.show({
|
|
166
|
+
leadingIconName: 'alert',
|
|
167
|
+
type: 'error',
|
|
168
|
+
text1: 'Uh oh! Agent failed to disconnect',
|
|
169
|
+
text2: null,
|
|
170
|
+
visibilityTime: 5000,
|
|
171
|
+
primaryBtn: null,
|
|
172
|
+
secondaryBtn: null,
|
|
173
|
+
leadingIcon: null,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
throw agentDisconnectError;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.log(`Agent failed to connect/disconnect - ${error}`);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const value = {
|
|
185
|
+
toggleAgentConnection: handleConnectionToggle,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<AgentConnectionContext.Provider value={value}>
|
|
190
|
+
{children}
|
|
191
|
+
</AgentConnectionContext.Provider>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const connectToAIAgent = async (
|
|
196
|
+
agentAction: 'start' | 'stop',
|
|
197
|
+
channel_name: string,
|
|
198
|
+
localUid: UidType,
|
|
199
|
+
agentAuthToken: string,
|
|
200
|
+
data?: {agent_id: string; prompt?: string; voice?: string},
|
|
201
|
+
): Promise<{}> => {
|
|
202
|
+
// const apiUrl = '/api/proxy';
|
|
203
|
+
const apiUrl = $config.BACKEND_ENDPOINT + '/v1/convoai';
|
|
204
|
+
const requestBody = {
|
|
205
|
+
channel_name: channel_name,
|
|
206
|
+
uid: localUid, // user uid // localUid or 0
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
if (data && data?.agent_id) {
|
|
210
|
+
requestBody['ai_agent_id'] = data.agent_id;
|
|
211
|
+
}
|
|
212
|
+
if (data && data?.voice) {
|
|
213
|
+
requestBody['voice'] = data.voice;
|
|
214
|
+
}
|
|
215
|
+
if (data && data?.prompt) {
|
|
216
|
+
requestBody['prompt'] = data.prompt;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const headers: HeadersInit = {
|
|
220
|
+
'Content-Type': 'application/json',
|
|
221
|
+
Authorization: `Bearer ${agentAuthToken}`,
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
const response = await fetch(`${apiUrl}/${agentAction}`, {
|
|
226
|
+
method: 'POST',
|
|
227
|
+
headers: headers,
|
|
228
|
+
body: JSON.stringify(requestBody),
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (!response.ok) {
|
|
232
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const data = await response.json();
|
|
236
|
+
|
|
237
|
+
console.log(
|
|
238
|
+
`AI agent ${agentAction === 'start' ? 'connected' : 'disconnected'}`,
|
|
239
|
+
data,
|
|
240
|
+
);
|
|
241
|
+
if (agentAction === 'start') {
|
|
242
|
+
return data;
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error(`Failed to ${agentAction} AI agent connection:`, error);
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, {createContext, useState} from 'react';
|
|
2
2
|
import {AIAgentState, AgentState} from './const';
|
|
3
3
|
import {UidType} from 'customization-api';
|
|
4
|
-
|
|
4
|
+
import {AI_AGENT_VOICE} from './const';
|
|
5
5
|
export interface ChatItem {
|
|
6
6
|
id: string;
|
|
7
7
|
uid: UidType;
|
|
@@ -22,6 +22,12 @@ export interface AgentContextInterface {
|
|
|
22
22
|
setAgentUID: (uid: UidType | null) => void;
|
|
23
23
|
chatItems: ChatItem[];
|
|
24
24
|
addChatItem: (newItem: ChatItem) => void;
|
|
25
|
+
agentId: string;
|
|
26
|
+
setAgentId: (id: string) => void;
|
|
27
|
+
agentVoice?: keyof typeof AI_AGENT_VOICE | '';
|
|
28
|
+
setAgentVoice: (voice: keyof typeof AI_AGENT_VOICE) => void;
|
|
29
|
+
prompt?: string;
|
|
30
|
+
setPrompt: (prompt: string) => void;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
export const AgentContext = createContext<AgentContextInterface>({
|
|
@@ -35,6 +41,12 @@ export const AgentContext = createContext<AgentContextInterface>({
|
|
|
35
41
|
setAgentUID: () => {},
|
|
36
42
|
chatItems: [],
|
|
37
43
|
addChatItem: () => {}, // Default no-op
|
|
44
|
+
agentVoice: '',
|
|
45
|
+
setAgentVoice: () => {},
|
|
46
|
+
agentId: '',
|
|
47
|
+
setAgentId: () => {},
|
|
48
|
+
prompt: '',
|
|
49
|
+
setPrompt: () => {},
|
|
38
50
|
});
|
|
39
51
|
|
|
40
52
|
/**
|
|
@@ -74,6 +86,10 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
74
86
|
const [agentUID, setAgentUID] = useState<UidType | null>(null);
|
|
75
87
|
const [isSubscribedForStreams, setIsSubscribedForStreams] = useState(false);
|
|
76
88
|
const [chatItems, setChatItems] = useState<ChatItem[]>([]);
|
|
89
|
+
const [agentId, setAgentId] = useState('');
|
|
90
|
+
const [agentVoice, setAgentVoice] =
|
|
91
|
+
useState<AgentContextInterface['agentVoice']>('');
|
|
92
|
+
const [prompt, setPrompt] = useState('');
|
|
77
93
|
|
|
78
94
|
/**
|
|
79
95
|
* Adds a new chat item to the chat state while ensuring:
|
|
@@ -155,6 +171,12 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
155
171
|
setAgentUID,
|
|
156
172
|
chatItems,
|
|
157
173
|
addChatItem, // Expose the function in the context
|
|
174
|
+
agentId,
|
|
175
|
+
setAgentId,
|
|
176
|
+
agentVoice,
|
|
177
|
+
setAgentVoice,
|
|
178
|
+
prompt,
|
|
179
|
+
setPrompt,
|
|
158
180
|
};
|
|
159
181
|
|
|
160
182
|
return (
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {isMobileUA} from '../../../utils/common';
|
|
2
2
|
|
|
3
3
|
export const AI_AGENT_STATE = {
|
|
4
|
-
NOT_CONNECTED: '
|
|
5
|
-
REQUEST_SENT: isMobileUA() ? '
|
|
6
|
-
AWAITING_JOIN: isMobileUA() ? '
|
|
4
|
+
NOT_CONNECTED: 'Join Call',
|
|
5
|
+
REQUEST_SENT: isMobileUA() ? 'Join Call' : 'Requesting agent join..', // loading - reg
|
|
6
|
+
AWAITING_JOIN: isMobileUA() ? 'Join Call' : 'Agent will join shortly..', // loading
|
|
7
7
|
AGENT_CONNECTED: 'End Call',
|
|
8
|
-
AGENT_REQUEST_FAILED: '
|
|
8
|
+
AGENT_REQUEST_FAILED: 'Join Call',
|
|
9
9
|
AGENT_DISCONNECT_REQUEST: isMobileUA()
|
|
10
10
|
? 'End Call'
|
|
11
11
|
: 'Disconnecting agent...', // loading - req
|
|
12
12
|
AGENT_DISCONNECT_FAILED: 'End Call',
|
|
13
|
-
AWAITING_LEAVE: '
|
|
13
|
+
AWAITING_LEAVE: 'Join Call', // loading
|
|
14
14
|
} as const;
|
|
15
15
|
|
|
16
16
|
export type AIAgentState = keyof typeof AI_AGENT_STATE;
|
|
@@ -56,3 +56,36 @@ export const AGORA_SSO_LOGIN_PATH = '/api/v0/oauth/authorize';
|
|
|
56
56
|
export const AGORA_SSO_LOGOUT_PATH = '/api/v0/logout';
|
|
57
57
|
|
|
58
58
|
export const AGORA_SSO_CLIENT_ID = 'openai_agora';
|
|
59
|
+
|
|
60
|
+
export const AI_AGENT_VOICE = {
|
|
61
|
+
'en-US-AvaMultilingualNeural': 'en-US-AvaMultilingualNeural',
|
|
62
|
+
'en-US-AndrewMultilingualNeural': 'en-US-AndrewMultilingualNeural',
|
|
63
|
+
'en-US-EmmaMultilingualNeural': 'en-US-EmmaMultilingualNeural',
|
|
64
|
+
'en-US-BrianMultilingualNeural': 'en-US-BrianMultilingualNeural',
|
|
65
|
+
'en-US-AvaNeural': 'en-US-AvaNeural',
|
|
66
|
+
'en-US-AndrewNeural': 'en-US-AndrewNeural',
|
|
67
|
+
'en-US-EmmaNeural': 'en-US-EmmaNeural',
|
|
68
|
+
'en-US-BrianNeural': 'en-US-BrianNeural',
|
|
69
|
+
'en-US-JennyNeural': 'en-US-JennyNeural',
|
|
70
|
+
'en-US-GuyNeural': 'en-US-GuyNeural',
|
|
71
|
+
'en-US-AriaNeural': 'en-US-AriaNeural',
|
|
72
|
+
'en-US-DavisNeural': 'en-US-DavisNeural',
|
|
73
|
+
'en-US-JaneNeural': 'en-US-JaneNeural',
|
|
74
|
+
'en-US-JasonNeural': 'en-US-JasonNeural',
|
|
75
|
+
'en-US-SaraNeural': 'en-US-SaraNeural',
|
|
76
|
+
'en-US-TonyNeural': 'en-US-TonyNeural',
|
|
77
|
+
'en-US-NancyNeural': 'en-US-NancyNeural',
|
|
78
|
+
'en-US-AmberNeural': 'en-US-AmberNeural',
|
|
79
|
+
'en-US-AnaNeural': 'en-US-AnaNeural',
|
|
80
|
+
'en-US-AshleyNeural': 'en-US-AshleyNeural',
|
|
81
|
+
'en-US-BrandonNeural': 'en-US-BrandonNeural',
|
|
82
|
+
'en-US-ChristopherNeural': 'en-US-ChristopherNeural',
|
|
83
|
+
'en-US-CoraNeural': 'en-US-CoraNeural',
|
|
84
|
+
'en-US-ElizabethNeural': 'en-US-ElizabethNeural',
|
|
85
|
+
'en-US-EricNeural': 'en-US-EricNeural',
|
|
86
|
+
'en-US-JacobNeural': 'en-US-JacobNeural',
|
|
87
|
+
'en-US-JennyMultilingualNeural4': 'en-US-JennyMultilingualNeural4',
|
|
88
|
+
'en-US-MichelleNeural': 'en-US-MichelleNeural',
|
|
89
|
+
'en-US-MonicaNeural': 'en-US-MonicaNeural',
|
|
90
|
+
'en-US-RogerNeural': 'en-US-RogerNeural',
|
|
91
|
+
};
|