agora-appbuilder-core 4.1.0-beta-9 → 4.1.0-beta-11
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/android/build.gradle +2 -0
- package/template/bridge/rtc/webNg/RtcEngine.ts +7 -7
- package/template/defaultConfig.js +2 -2
- package/template/package.json +1 -0
- package/template/src/ai-agent/components/AgentControls/AgentContext.tsx +55 -188
- package/template/src/ai-agent/components/AgentControls/index.tsx +0 -6
- package/template/src/ai-agent/components/AgentControls/message.ts +1003 -0
- package/template/src/ai-agent/components/AudioVisualizer.tsx +18 -14
- package/template/src/ai-agent/components/CustomSettingsPanel.tsx +5 -3
- package/template/src/ai-agent/components/SelectAiAgent.tsx +1 -1
- package/template/src/ai-agent/components/SelectAiAgentVoice.tsx +3 -3
- package/template/src/ai-agent/components/SelectUserLanguage.tsx +3 -2
- package/template/src/ai-agent/components/UserPrompt.tsx +1 -1
- package/template/src/ai-agent/components/agent-chat-panel/agent-chat-ui.tsx +94 -13
- package/template/src/ai-agent/components/mobile/MobileLayoutComponent.tsx +0 -1
- package/template/src/ai-agent/index.tsx +0 -7
- package/template/src/ai-agent/layout/ConversationalAI.tsx +0 -2
- package/template/src/ai-agent/layout/DefaultAIOnly.tsx +1 -7
- package/template/src/ai-agent/utils.ts +56 -1
- package/template/src/components/ChatContext.ts +5 -0
- package/template/src/components/StorageContext.tsx +16 -0
- package/template/src/logger/AppBuilderLogger.tsx +1 -1
- package/template/src/pages/video-call/VideoCallMobileView.tsx +5 -5
- package/template/src/rtm-events-api/LocalEvents.ts +1 -0
- package/template/src/subComponents/ChatBubble.tsx +16 -1
package/package.json
CHANGED
|
@@ -10,6 +10,8 @@ buildscript {
|
|
|
10
10
|
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
|
|
11
11
|
ndkVersion = "23.1.7779620"
|
|
12
12
|
kotlinVersion = "1.8.21" // added this for datadog logs - https://docs.datadoghq.com/real_user_monitoring/mobile_and_tv_monitoring/setup/reactnative/#setup
|
|
13
|
+
androidXAnnotation = "1.2.0"
|
|
14
|
+
androidXBrowser = "1.3.0"
|
|
13
15
|
}
|
|
14
16
|
subprojects { subproject ->
|
|
15
17
|
afterEvaluate{
|
|
@@ -894,13 +894,13 @@ export default class RtcEngine {
|
|
|
894
894
|
|
|
895
895
|
/* Recieve Captions */
|
|
896
896
|
this.client.on('stream-message', (uid: UID, payload: UInt8Array) => {
|
|
897
|
-
logger.debug(
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
);
|
|
897
|
+
// logger.debug(
|
|
898
|
+
// LogSource.AgoraSDK,
|
|
899
|
+
// 'Event',
|
|
900
|
+
// 'RTC [stream-message](stt-web: onStreamMessageCallback)',
|
|
901
|
+
// uid,
|
|
902
|
+
// payload,
|
|
903
|
+
// );
|
|
904
904
|
(this.eventsMap.get('onStreamMessage') as callbackType)(uid, payload);
|
|
905
905
|
});
|
|
906
906
|
|
|
@@ -76,8 +76,8 @@ const DefaultConfig = {
|
|
|
76
76
|
CHAT_ORG_NAME: '',
|
|
77
77
|
CHAT_APP_NAME: '',
|
|
78
78
|
CHAT_URL: '',
|
|
79
|
-
CLI_VERSION: '3.1.0-beta-
|
|
80
|
-
CORE_VERSION: '4.1.0-beta-
|
|
79
|
+
CLI_VERSION: '3.1.0-beta-11',
|
|
80
|
+
CORE_VERSION: '4.1.0-beta-11',
|
|
81
81
|
DISABLE_LANDSCAPE_MODE: false,
|
|
82
82
|
STT_AUTO_START: false,
|
|
83
83
|
CLOUD_RECORDING_AUTO_START: false,
|
package/template/package.json
CHANGED
|
@@ -10,6 +10,15 @@ import {
|
|
|
10
10
|
Toast,
|
|
11
11
|
useRtc,
|
|
12
12
|
} from 'customization-api';
|
|
13
|
+
import LocalEventEmitter, {
|
|
14
|
+
LocalEventsEnum,
|
|
15
|
+
} from '../../../../src/rtm-events-api/LocalEvents';
|
|
16
|
+
import {
|
|
17
|
+
messageService,
|
|
18
|
+
initializeMessageEngine,
|
|
19
|
+
closeMessageEngine,
|
|
20
|
+
IMessageListItem,
|
|
21
|
+
} from './message';
|
|
13
22
|
|
|
14
23
|
export interface ChatItem {
|
|
15
24
|
id: string;
|
|
@@ -30,8 +39,6 @@ export interface AgentContextInterface {
|
|
|
30
39
|
setIsSubscribedForStreams: (state: boolean) => void;
|
|
31
40
|
agentUID: UidType | null;
|
|
32
41
|
setAgentUID: (uid: UidType | null) => void;
|
|
33
|
-
chatItems: ChatItem[];
|
|
34
|
-
addChatItem: (newItem: ChatItem) => void;
|
|
35
42
|
agentId: string;
|
|
36
43
|
setAgentId: (id: string) => void;
|
|
37
44
|
agentVoice?: keyof typeof AI_AGENT_VOICE | '';
|
|
@@ -42,6 +49,9 @@ export interface AgentContextInterface {
|
|
|
42
49
|
setPrompt: (prompt: string) => void;
|
|
43
50
|
isInterruptionHandlingEnabled: boolean;
|
|
44
51
|
setIsInterruptionHandlingEnabled: (value: boolean) => void;
|
|
52
|
+
chatHistory: IMessageListItem[];
|
|
53
|
+
setChatHistory: (history: IMessageListItem[]) => void;
|
|
54
|
+
clearChatHistory: () => void;
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
export const AgentContext = createContext<AgentContextInterface>({
|
|
@@ -56,8 +66,6 @@ export const AgentContext = createContext<AgentContextInterface>({
|
|
|
56
66
|
setIsSubscribedForStreams: () => {},
|
|
57
67
|
agentUID: null,
|
|
58
68
|
setAgentUID: () => {},
|
|
59
|
-
chatItems: [],
|
|
60
|
-
addChatItem: () => {}, // Default no-op
|
|
61
69
|
agentVoice: '',
|
|
62
70
|
setAgentVoice: () => {},
|
|
63
71
|
agentId: '',
|
|
@@ -68,6 +76,9 @@ export const AgentContext = createContext<AgentContextInterface>({
|
|
|
68
76
|
setIsInterruptionHandlingEnabled: () => {},
|
|
69
77
|
language: '',
|
|
70
78
|
setLanguage: () => {},
|
|
79
|
+
chatHistory: [],
|
|
80
|
+
setChatHistory: () => {},
|
|
81
|
+
clearChatHistory: () => {},
|
|
71
82
|
});
|
|
72
83
|
|
|
73
84
|
/**
|
|
@@ -106,7 +117,7 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
106
117
|
const [agentAuthToken, setAgentAuthToken] = useState<string | null>(null);
|
|
107
118
|
const [agentUID, setAgentUID] = useState<UidType | null>(null);
|
|
108
119
|
const [isSubscribedForStreams, setIsSubscribedForStreams] = useState(false);
|
|
109
|
-
const [
|
|
120
|
+
const [chatHistory, setChatHistory] = useState<IMessageListItem[]>([]);
|
|
110
121
|
const [agentId, setAgentId] = useState('');
|
|
111
122
|
const [agentVoice, setAgentVoice] =
|
|
112
123
|
useState<AgentContextInterface['agentVoice']>('');
|
|
@@ -140,117 +151,34 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
140
151
|
|
|
141
152
|
React.useEffect(() => {
|
|
142
153
|
if (!isSubscribedForStreams) {
|
|
143
|
-
RtcEngineUnsafe.addListener(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
);
|
|
154
|
+
RtcEngineUnsafe.addListener('onStreamMessage', (...args: any[]) => {
|
|
155
|
+
messageService?.handleStreamMessage(args[1]);
|
|
156
|
+
});
|
|
147
157
|
setIsSubscribedForStreams(true);
|
|
148
158
|
}
|
|
149
159
|
}, []);
|
|
150
160
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
let decoder = new TextDecoder('utf-8');
|
|
158
|
-
let decodedMessage = decoder.decode(data);
|
|
159
|
-
console.log('[test] textstream raw data', decodedMessage);
|
|
160
|
-
handleChunk(decodedMessage);
|
|
161
|
-
};
|
|
162
|
-
// Function to process received chunk via event emitter
|
|
163
|
-
const handleChunk = (formattedChunk: string) => {
|
|
164
|
-
try {
|
|
165
|
-
// Split the chunk by the delimiter "|"
|
|
166
|
-
const [message_id, partIndexStr, totalPartsStr, content] =
|
|
167
|
-
formattedChunk.split('|');
|
|
168
|
-
|
|
169
|
-
const part_index = parseInt(partIndexStr, 10);
|
|
170
|
-
const total_parts =
|
|
171
|
-
totalPartsStr === '???' ? -1 : parseInt(totalPartsStr, 10); // -1 means total parts unknown
|
|
172
|
-
|
|
173
|
-
// Ensure total_parts is known before processing further
|
|
174
|
-
if (total_parts === -1) {
|
|
175
|
-
console.warn(
|
|
176
|
-
`Total parts for message ${message_id} unknown, waiting for further parts.`,
|
|
177
|
-
);
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const chunkData = {
|
|
182
|
-
message_id,
|
|
183
|
-
part_index,
|
|
184
|
-
total_parts,
|
|
185
|
-
content,
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
// Check if we already have an entry for this message
|
|
189
|
-
if (!messageCache[message_id]) {
|
|
190
|
-
messageCache[message_id] = [];
|
|
191
|
-
// Set a timeout to discard incomplete messages
|
|
192
|
-
setTimeout(() => {
|
|
193
|
-
if (messageCache[message_id]?.length !== total_parts) {
|
|
194
|
-
console.warn(`Incomplete message with ID ${message_id} discarded`);
|
|
195
|
-
delete messageCache[message_id]; // Discard incomplete message
|
|
196
|
-
}
|
|
197
|
-
}, TIMEOUT_MS);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Cache this chunk by message_id
|
|
201
|
-
messageCache[message_id].push(chunkData);
|
|
202
|
-
|
|
203
|
-
// If all parts are received, reconstruct the message
|
|
204
|
-
if (messageCache[message_id].length === total_parts) {
|
|
205
|
-
const completeMessage = reconstructMessage(messageCache[message_id]);
|
|
206
|
-
const data = atob(completeMessage);
|
|
207
|
-
const {stream_id, is_final, text, text_ts} = JSON.parse(data);
|
|
208
|
-
/** Data type of above object
|
|
209
|
-
* stream_id: number
|
|
210
|
-
* is_final: boolean
|
|
211
|
-
* text: string
|
|
212
|
-
* text_ts: number
|
|
213
|
-
*/
|
|
214
|
-
const textItem = {
|
|
215
|
-
id: message_id,
|
|
216
|
-
uid: stream_id,
|
|
217
|
-
time: text_ts,
|
|
218
|
-
dataType: 'transcribe',
|
|
219
|
-
text: text,
|
|
220
|
-
isFinal: is_final,
|
|
221
|
-
isSelf: stream_id === 0 ? false : true,
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
if (text.trim().length > 0) {
|
|
225
|
-
//this.emit("textChanged", textItem);
|
|
226
|
-
console.warn('emit textChanged: ', textItem);
|
|
227
|
-
addChatItem(textItem);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Clean up the cache
|
|
231
|
-
delete messageCache[message_id];
|
|
161
|
+
React.useEffect(() => {
|
|
162
|
+
const getChatHistoryFromEvent = (event: MessageEvent) => {
|
|
163
|
+
const {data} = event;
|
|
164
|
+
// console.log('get chat history from event', data);
|
|
165
|
+
if (data.type === 'message') {
|
|
166
|
+
setChatHistory(prevChatHistory => [...(data?.chatHistory || [])]);
|
|
232
167
|
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const reconstructMessage = chunks => {
|
|
239
|
-
// Sort chunks by their part index
|
|
240
|
-
chunks.sort((a, b) => a.part_index - b.part_index);
|
|
241
|
-
|
|
242
|
-
// Concatenate all chunks to form the full message
|
|
243
|
-
return chunks.map(chunk => chunk.content).join('');
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
useEffect(() => {
|
|
247
|
-
console.log(
|
|
248
|
-
'debugging users agent contrl',
|
|
249
|
-
agentConnectionState,
|
|
250
|
-
{users},
|
|
251
|
-
agentUID,
|
|
168
|
+
};
|
|
169
|
+
LocalEventEmitter.on(
|
|
170
|
+
LocalEventsEnum.AGENT_TRANSCRIPT_CHANGE,
|
|
171
|
+
getChatHistoryFromEvent,
|
|
252
172
|
);
|
|
173
|
+
return () => {
|
|
174
|
+
LocalEventEmitter.off(
|
|
175
|
+
LocalEventsEnum.AGENT_TRANSCRIPT_CHANGE,
|
|
176
|
+
getChatHistoryFromEvent,
|
|
177
|
+
);
|
|
178
|
+
};
|
|
179
|
+
}, []);
|
|
253
180
|
|
|
181
|
+
useEffect(() => {
|
|
254
182
|
// welcome agent
|
|
255
183
|
const aiAgentUID = users.filter(item => item === agentUID);
|
|
256
184
|
|
|
@@ -266,6 +194,7 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
266
194
|
agentConnectionState !== AgentState.AGENT_CONNECTED)
|
|
267
195
|
) {
|
|
268
196
|
setAgentConnectionState(AgentState.AGENT_CONNECTED);
|
|
197
|
+
initializeMessageEngine();
|
|
269
198
|
if (isStartAPICalled) {
|
|
270
199
|
setStartAPICalled(false);
|
|
271
200
|
}
|
|
@@ -294,6 +223,7 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
294
223
|
!aiAgentUID.length &&
|
|
295
224
|
agentConnectionState !== AgentState.NOT_CONNECTED)
|
|
296
225
|
) {
|
|
226
|
+
closeMessageEngine(); // release message engine
|
|
297
227
|
setAgentConnectionState(AgentState.NOT_CONNECTED);
|
|
298
228
|
if (isStopAPICalled) {
|
|
299
229
|
setStartAPICalled(true);
|
|
@@ -307,6 +237,10 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
307
237
|
isStopAPICalled,
|
|
308
238
|
]);
|
|
309
239
|
|
|
240
|
+
const clearChatHistory = () => {
|
|
241
|
+
setChatHistory([]);
|
|
242
|
+
};
|
|
243
|
+
|
|
310
244
|
const handleConnectionToggle = async (forceStop: boolean = false) => {
|
|
311
245
|
try {
|
|
312
246
|
// connect to agent when agent is in not connected state or when earlier connect failed
|
|
@@ -318,19 +252,20 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
318
252
|
try {
|
|
319
253
|
setAgentConnectionState(AgentState.REQUEST_SENT);
|
|
320
254
|
setStartAPICalled(true);
|
|
255
|
+
const params = {
|
|
256
|
+
agent_id: agentId,
|
|
257
|
+
prompt: prompt,
|
|
258
|
+
voice: agents?.find(a => a.id === agentId)?.config?.tts?.params
|
|
259
|
+
?.voice_name,
|
|
260
|
+
enable_interruption_handling: isInterruptionHandlingEnabled,
|
|
261
|
+
language: language,
|
|
262
|
+
};
|
|
321
263
|
const data = await connectToAIAgent(
|
|
322
264
|
'start',
|
|
323
265
|
channel_name,
|
|
324
266
|
localUid,
|
|
325
267
|
store.token,
|
|
326
|
-
|
|
327
|
-
agent_id: agentId || agents?.length ? agents[0].id : null,
|
|
328
|
-
prompt: prompt,
|
|
329
|
-
voice: agents.find(a => a.id === agentId)?.config?.tts?.params
|
|
330
|
-
?.voice_name,
|
|
331
|
-
enable_interruption_handling: isInterruptionHandlingEnabled,
|
|
332
|
-
language: language,
|
|
333
|
-
},
|
|
268
|
+
params,
|
|
334
269
|
);
|
|
335
270
|
// console.log("response X-Client-ID", newClientId, typeof newClientId)
|
|
336
271
|
// @ts-ignore
|
|
@@ -444,75 +379,6 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
444
379
|
}
|
|
445
380
|
};
|
|
446
381
|
|
|
447
|
-
/**
|
|
448
|
-
* Adds a new chat item to the chat state while ensuring:
|
|
449
|
-
* - Outdated messages are discarded.
|
|
450
|
-
* - Non-finalized messages are updated if a newer message is received.
|
|
451
|
-
* - Finalized messages are added without duplication.
|
|
452
|
-
* - Chat items remain sorted by their `time` property.
|
|
453
|
-
*
|
|
454
|
-
* @param newItem The new chat item to add.
|
|
455
|
-
*/
|
|
456
|
-
const addChatItem = (newItem: ChatItem) => {
|
|
457
|
-
setChatItems(prevItems => {
|
|
458
|
-
// Find the index of the last finalized chat item for the same user
|
|
459
|
-
// Finalized messages are typically considered "complete" and should not be updated by non-final messages
|
|
460
|
-
const LastFinalIndex = prevItems.findLastIndex(
|
|
461
|
-
el => el.uid === newItem.uid && el.isFinal,
|
|
462
|
-
);
|
|
463
|
-
|
|
464
|
-
// Find the index of the last non-finalized chat item for the same user
|
|
465
|
-
// Non-finalized messages represent "in-progress" messages that can be updated or replaced
|
|
466
|
-
const LastNonFinalIndex = prevItems.findLastIndex(
|
|
467
|
-
el => el.uid === newItem.uid && !el.isFinal,
|
|
468
|
-
);
|
|
469
|
-
|
|
470
|
-
// Retrieve the actual items for the indices found above
|
|
471
|
-
const LastFinalItem =
|
|
472
|
-
LastFinalIndex !== -1 ? prevItems[LastFinalIndex] : null;
|
|
473
|
-
const LastNonFinalItem =
|
|
474
|
-
LastNonFinalIndex !== -1 ? prevItems[LastNonFinalIndex] : null;
|
|
475
|
-
|
|
476
|
-
// If the new message's timestamp is older than or equal to the last finalized message,
|
|
477
|
-
// it is considered outdated and discarded to prevent unnecessary overwrites.
|
|
478
|
-
if (LastFinalItem && newItem.time <= LastFinalItem.time) {
|
|
479
|
-
console.log(
|
|
480
|
-
'[AgentProvider] addChatItem - Discarded outdated message:',
|
|
481
|
-
newItem,
|
|
482
|
-
);
|
|
483
|
-
return prevItems; // Return the previous state without changes
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Create a new copy of the current chat items to maintain immutability
|
|
487
|
-
let updatedItems = [...prevItems];
|
|
488
|
-
|
|
489
|
-
// If there is a non-finalized message for the same user, replace it with the new message
|
|
490
|
-
if (LastNonFinalItem) {
|
|
491
|
-
console.log(
|
|
492
|
-
'[AgentProvider] addChatItem - Updating non-finalized message:',
|
|
493
|
-
newItem,
|
|
494
|
-
);
|
|
495
|
-
updatedItems[LastNonFinalIndex] = newItem; // Replace the non-finalized message
|
|
496
|
-
} else {
|
|
497
|
-
// If no non-finalized message exists, the new message is added to the array
|
|
498
|
-
console.log(
|
|
499
|
-
'[AgentProvider] addChatItem - Adding new message:',
|
|
500
|
-
newItem,
|
|
501
|
-
);
|
|
502
|
-
|
|
503
|
-
// Use binary search to find the correct insertion index for the new message
|
|
504
|
-
// This ensures the array remains sorted by the `time` property
|
|
505
|
-
const insertIndex = findInsertionIndex(updatedItems, newItem.time);
|
|
506
|
-
|
|
507
|
-
// Insert the new message at the correct position to maintain chronological order
|
|
508
|
-
updatedItems.splice(insertIndex, 0, newItem);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
// Return the updated array, which will replace the previous state
|
|
512
|
-
return updatedItems;
|
|
513
|
-
});
|
|
514
|
-
};
|
|
515
|
-
|
|
516
382
|
const value = {
|
|
517
383
|
toggleAgentConnection: handleConnectionToggle,
|
|
518
384
|
agentConnectionState,
|
|
@@ -523,8 +389,6 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
523
389
|
setIsSubscribedForStreams,
|
|
524
390
|
agentUID,
|
|
525
391
|
setAgentUID,
|
|
526
|
-
chatItems,
|
|
527
|
-
addChatItem, // Expose the function in the context
|
|
528
392
|
agentId,
|
|
529
393
|
setAgentId,
|
|
530
394
|
agentVoice,
|
|
@@ -535,6 +399,9 @@ export const AgentProvider: React.FC<{children: React.ReactNode}> = ({
|
|
|
535
399
|
setIsInterruptionHandlingEnabled,
|
|
536
400
|
language,
|
|
537
401
|
setLanguage,
|
|
402
|
+
setChatHistory,
|
|
403
|
+
chatHistory,
|
|
404
|
+
clearChatHistory,
|
|
538
405
|
};
|
|
539
406
|
|
|
540
407
|
return (
|
|
@@ -18,12 +18,6 @@ export const AgentControl: React.FC = () => {
|
|
|
18
18
|
const isAwaitingLeave = agentConnectionState === AgentState.AWAITING_LEAVE;
|
|
19
19
|
const isAgentAvailable = useIsAgentAvailable();
|
|
20
20
|
|
|
21
|
-
console.log(
|
|
22
|
-
'Agent Control--',
|
|
23
|
-
{agentConnectionState},
|
|
24
|
-
{bth: AI_AGENT_STATE[agentConnectionState]},
|
|
25
|
-
);
|
|
26
|
-
|
|
27
21
|
const isLoading =
|
|
28
22
|
agentConnectionState === AgentState.REQUEST_SENT ||
|
|
29
23
|
agentConnectionState === AgentState.AGENT_DISCONNECT_REQUEST ||
|