@trtc/calls-uikit-react 4.4.3 → 4.4.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 +2 -2
- package/src/Components/assets/aiAssistant/desktop/subtitleSettings.svg +4 -0
- package/src/Components/assets/aiAssistant/mobile/close-aiAssistant.svg +7 -0
- package/src/Components/assets/aiAssistant/mobile/open-aiAssistant.svg +7 -0
- package/src/Components/assets/aiAssistant/mobile/subtitleSettings.svg +3 -0
- package/src/Components/components/base/CustomSelect/CustomSelect.scss +116 -0
- package/src/Components/components/base/CustomSelect/CustomSelect.tsx +96 -0
- package/src/Components/components/common/AIAssistant/AISubtitle.scss +109 -0
- package/src/Components/components/common/AIAssistant/AISubtitle.tsx +52 -66
- package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchH5.tsx +39 -0
- package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.scss +46 -0
- package/src/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.tsx +42 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleContent.scss +67 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleContent.tsx +134 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleSettingsH5.scss +275 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleSettingsH5.tsx +265 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleSettingsPC.scss +98 -0
- package/src/Components/components/common/AIAssistant/components/SubtitleSettingsPC.tsx +198 -0
- package/src/Components/components/common/AIAssistant/components/index.ts +5 -0
- package/src/Components/components/common/AIAssistant/index.ts +3 -1
- package/src/Components/components/common/TopBar/h5/TopBarH5.tsx +11 -1
- package/src/Components/components/common/TopBar/pc/TopBarPC.module.scss +20 -0
- package/src/Components/components/common/TopBar/pc/TopBarPC.tsx +14 -9
- package/src/Components/hooks/index.ts +2 -0
- package/src/Components/hooks/useAIAssistant.ts +174 -0
- package/src/TUICallService/CallService/AIAssistant.ts +285 -39
- package/src/TUICallService/CallService/index.ts +19 -9
- package/src/TUICallService/TUIStore/callStore.ts +5 -0
- package/src/TUICallService/const/index.ts +4 -0
- package/src/TUICallService/interface/ICallStore.ts +5 -0
- package/src/TUICallService/locales/en.ts +15 -0
- package/src/TUICallService/locales/ja_JP.ts +15 -0
- package/src/TUICallService/locales/zh-cn.ts +15 -0
- package/src/index.ts +1 -1
- package/tuicall-uikit-react.es.js +4265 -3703
- package/tuicall-uikit-react.umd.js +8 -3
- package/types/Components/components/base/CustomSelect/CustomSelect.d.ts +15 -0
- package/types/Components/components/common/AIAssistant/AISubtitle.d.ts +1 -0
- package/types/Components/components/common/AIAssistant/components/AITranscriberSwitchH5.d.ts +3 -0
- package/types/Components/components/common/AIAssistant/components/AITranscriberSwitchPC.d.ts +4 -0
- package/types/Components/components/common/AIAssistant/components/SubtitleContent.d.ts +8 -0
- package/types/Components/components/common/AIAssistant/components/SubtitleSettingsH5.d.ts +13 -0
- package/types/Components/components/common/AIAssistant/components/SubtitleSettingsPC.d.ts +13 -0
- package/types/Components/components/common/AIAssistant/components/index.d.ts +5 -0
- package/types/Components/components/common/AIAssistant/index.d.ts +1 -0
- package/types/Components/hooks/index.d.ts +2 -1
- package/types/Components/hooks/useAIAssistant.d.ts +22 -0
- package/types/TUICallService/CallService/AIAssistant.d.ts +72 -15
- package/types/TUICallService/CallService/index.d.ts +2 -1
- package/types/TUICallService/interface/ICallStore.d.ts +4 -0
- package/types/TUICallService/locales/en.d.ts +14 -0
- package/types/TUICallService/locales/ja_JP.d.ts +14 -0
- package/types/TUICallService/locales/zh-cn.d.ts +14 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import useTranslate from "./useTranslate";
|
|
2
2
|
import useDeviceList from "./useDeviceList";
|
|
3
3
|
import useCallDuration from "./useCallDuration";
|
|
4
|
+
import { useAIAssistant } from "./useAIAssistant";
|
|
4
5
|
|
|
5
6
|
export {
|
|
6
7
|
useTranslate,
|
|
7
8
|
useDeviceList,
|
|
8
9
|
useCallDuration,
|
|
10
|
+
useAIAssistant,
|
|
9
11
|
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Assistant Hook
|
|
3
|
+
* @description React hook for AI Transcriber functionality
|
|
4
|
+
*/
|
|
5
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
6
|
+
import { TUIStore, StoreName, NAME } from '../../TUICallService/index';
|
|
7
|
+
|
|
8
|
+
// Translation content interface
|
|
9
|
+
interface ITranslationContent {
|
|
10
|
+
language: string;
|
|
11
|
+
text: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Transcriber message interface (simplified for React version)
|
|
15
|
+
interface TranscriberMessage {
|
|
16
|
+
segmentId: string;
|
|
17
|
+
speakerUserId: string;
|
|
18
|
+
sourceText: string;
|
|
19
|
+
isCompleted: boolean;
|
|
20
|
+
translationTexts: any;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Converted message interface
|
|
24
|
+
interface ITranslationInfo {
|
|
25
|
+
roundId: string;
|
|
26
|
+
sender: string;
|
|
27
|
+
nick?: string;
|
|
28
|
+
text: string;
|
|
29
|
+
end: boolean;
|
|
30
|
+
translation: ITranslationContent[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* AI Assistant hook
|
|
35
|
+
* Provides state management for AI transcription functionality
|
|
36
|
+
*/
|
|
37
|
+
export function useAIAssistant() {
|
|
38
|
+
// AI Transcriber Status State
|
|
39
|
+
const [isAITranscriberEnabled, setIsAITranscriberEnabled] = useState<boolean>(false);
|
|
40
|
+
const [isAITranscriberRunning, setIsAITranscriberRunning] = useState<boolean>(false);
|
|
41
|
+
|
|
42
|
+
// Converted message list
|
|
43
|
+
const [subtitleInfoList, setSubtitleInfoList] = useState<ITranslationInfo[]>([]);
|
|
44
|
+
|
|
45
|
+
// Helper functions for message conversion
|
|
46
|
+
const parseTranslationTexts = useCallback((translationTexts: any): ITranslationContent[] => {
|
|
47
|
+
if (!translationTexts) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Handle Map type
|
|
52
|
+
if (translationTexts instanceof Map) {
|
|
53
|
+
return Array.from(translationTexts.entries()).map(([language, content]) => ({
|
|
54
|
+
language: String(language),
|
|
55
|
+
text: ensureStringContent(content)
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Handle plain object type
|
|
60
|
+
if (typeof translationTexts === 'object') {
|
|
61
|
+
return Object.entries(translationTexts).map(([language, content]) => ({
|
|
62
|
+
language: String(language),
|
|
63
|
+
text: ensureStringContent(content)
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return [];
|
|
68
|
+
}, []);
|
|
69
|
+
|
|
70
|
+
const ensureStringContent = useCallback((content: any): string => {
|
|
71
|
+
if (content === null || content === undefined) {
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (typeof content === 'string') {
|
|
76
|
+
return content;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (typeof content === 'object') {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.stringify(content);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.warn('Failed to stringify translation content:', error);
|
|
84
|
+
return '[Invalid Object]';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return String(content);
|
|
89
|
+
}, []);
|
|
90
|
+
|
|
91
|
+
const convertToLegacyFormat = useCallback((messages: TranscriberMessage[]): ITranslationInfo[] => {
|
|
92
|
+
return messages.map(msg => ({
|
|
93
|
+
roundId: msg.segmentId,
|
|
94
|
+
sender: msg.speakerUserId,
|
|
95
|
+
nick: '',
|
|
96
|
+
text: msg.sourceText,
|
|
97
|
+
end: msg.isCompleted,
|
|
98
|
+
translation: parseTranslationTexts(msg.translationTexts)
|
|
99
|
+
}));
|
|
100
|
+
}, [parseTranslationTexts]);
|
|
101
|
+
|
|
102
|
+
// Store watcher callbacks
|
|
103
|
+
const handleAITranscriberEnabledChange = useCallback((value: boolean) => {
|
|
104
|
+
setIsAITranscriberEnabled(value || false);
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
107
|
+
const handleAITranscriberRunningChange = useCallback((value: boolean) => {
|
|
108
|
+
setIsAITranscriberRunning(value || false);
|
|
109
|
+
}, []);
|
|
110
|
+
|
|
111
|
+
const handleRealtimeMessageListChange = useCallback((messageList: TranscriberMessage[]) => {
|
|
112
|
+
if (messageList && messageList.length > 0) {
|
|
113
|
+
const convertedMessages = convertToLegacyFormat(messageList);
|
|
114
|
+
setSubtitleInfoList(convertedMessages);
|
|
115
|
+
} else {
|
|
116
|
+
setSubtitleInfoList([]);
|
|
117
|
+
}
|
|
118
|
+
}, [convertToLegacyFormat]);
|
|
119
|
+
|
|
120
|
+
// Effect for lifecycle management
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
// Try to get initial values from TUIStore if available
|
|
123
|
+
try {
|
|
124
|
+
const initialEnabled = TUIStore.getData(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_ENABLED);
|
|
125
|
+
const initialRunning = TUIStore.getData(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_RUNNING);
|
|
126
|
+
const initialMessages = TUIStore.getData(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST);
|
|
127
|
+
|
|
128
|
+
if (initialEnabled !== undefined) {
|
|
129
|
+
setIsAITranscriberEnabled(initialEnabled);
|
|
130
|
+
}
|
|
131
|
+
if (initialRunning !== undefined) {
|
|
132
|
+
setIsAITranscriberRunning(initialRunning);
|
|
133
|
+
}
|
|
134
|
+
if (initialMessages) {
|
|
135
|
+
handleRealtimeMessageListChange(initialMessages);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Subscribe to TUIStore changes if the store supports these fields
|
|
139
|
+
if (NAME.IS_AI_TRANSCRIBER_ENABLED && NAME.IS_AI_TRANSCRIBER_RUNNING && NAME.REALTIME_MESSAGE_LIST) {
|
|
140
|
+
TUIStore.watch(StoreName.CALL, {
|
|
141
|
+
[NAME.IS_AI_TRANSCRIBER_ENABLED]: handleAITranscriberEnabledChange,
|
|
142
|
+
[NAME.IS_AI_TRANSCRIBER_RUNNING]: handleAITranscriberRunningChange,
|
|
143
|
+
[NAME.REALTIME_MESSAGE_LIST]: handleRealtimeMessageListChange,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.warn('AI Assistant hook initialization failed:', error);
|
|
148
|
+
// Set default values for development/testing
|
|
149
|
+
setIsAITranscriberEnabled(true);
|
|
150
|
+
setIsAITranscriberRunning(false);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Cleanup function
|
|
154
|
+
return () => {
|
|
155
|
+
try {
|
|
156
|
+
if (NAME.IS_AI_TRANSCRIBER_ENABLED && NAME.IS_AI_TRANSCRIBER_RUNNING && NAME.REALTIME_MESSAGE_LIST) {
|
|
157
|
+
TUIStore.unwatch(StoreName.CALL, {
|
|
158
|
+
[NAME.IS_AI_TRANSCRIBER_ENABLED]: handleAITranscriberEnabledChange,
|
|
159
|
+
[NAME.IS_AI_TRANSCRIBER_RUNNING]: handleAITranscriberRunningChange,
|
|
160
|
+
[NAME.REALTIME_MESSAGE_LIST]: handleRealtimeMessageListChange,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.warn('AI Assistant hook cleanup failed:', error);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}, [handleAITranscriberEnabledChange, handleAITranscriberRunningChange, handleRealtimeMessageListChange]);
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
isAITranscriberEnabled,
|
|
171
|
+
isAITranscriberRunning,
|
|
172
|
+
subtitleInfoList,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
@@ -1,51 +1,297 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
/**
|
|
2
|
+
* AI Realtime Transcriber State Management
|
|
3
|
+
* @module AITranscriberState
|
|
4
|
+
* @description Manages realtime voice transcription state and operations, including message reception and history
|
|
5
|
+
*/
|
|
6
|
+
import { ITUIStore } from '../interface/ITUIStore';
|
|
7
|
+
import { StoreName, NAME } from '../const/index';
|
|
8
|
+
import TuiStore from '../TUIStore/tuiStore';
|
|
9
|
+
|
|
10
|
+
// AI Transcriber events
|
|
11
|
+
export enum RealtimeTranscriberEvent {
|
|
12
|
+
onReceiveTranscriberMessage = 'onReceiveTranscriberMessage',
|
|
13
|
+
onRealtimeTranscriberStarted = 'onRealtimeTranscriberStarted',
|
|
14
|
+
onRealtimeTranscriberStopped = 'onRealtimeTranscriberStopped',
|
|
15
|
+
onRealtimeTranscriberError = 'onRealtimeTranscriberError',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Transcriber language type
|
|
19
|
+
export type TranscriberLanguage = string;
|
|
20
|
+
|
|
21
|
+
// Transcriber message interface
|
|
22
|
+
export interface TranscriberMessage {
|
|
23
|
+
segmentId: string;
|
|
24
|
+
speakerUserId: string;
|
|
25
|
+
sourceText: string;
|
|
26
|
+
translationTexts?: Map<string, string>;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
isCompleted: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Event callback types
|
|
32
|
+
export interface RealtimeTranscriberEventInfoMap {
|
|
33
|
+
[RealtimeTranscriberEvent.onReceiveTranscriberMessage]: {
|
|
34
|
+
roomId: string | number;
|
|
35
|
+
message: TranscriberMessage;
|
|
36
|
+
};
|
|
37
|
+
[RealtimeTranscriberEvent.onRealtimeTranscriberStarted]: {
|
|
38
|
+
roomId: string | number;
|
|
39
|
+
transcriberRobotId: string;
|
|
40
|
+
sourceLanguage: string;
|
|
41
|
+
};
|
|
42
|
+
[RealtimeTranscriberEvent.onRealtimeTranscriberStopped]: {
|
|
43
|
+
roomId: string | number;
|
|
44
|
+
transcriberRobotId: string;
|
|
45
|
+
};
|
|
46
|
+
[RealtimeTranscriberEvent.onRealtimeTranscriberError]: {
|
|
47
|
+
roomId: string | number;
|
|
48
|
+
transcriberRobotId: string;
|
|
49
|
+
error: number;
|
|
50
|
+
errorMessage: string;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type RealtimeTranscriberEventCallback = <T extends RealtimeTranscriberEvent>(
|
|
55
|
+
eventInfo: RealtimeTranscriberEventInfoMap[T]
|
|
56
|
+
) => void;
|
|
57
|
+
|
|
58
|
+
const TUIStore: ITUIStore = TuiStore.getInstance();
|
|
59
|
+
|
|
60
|
+
export default class AIAssistant {
|
|
61
|
+
static instance: AIAssistant;
|
|
62
|
+
private _callService: any;
|
|
63
|
+
private eventListeners = new Map<RealtimeTranscriberEvent, RealtimeTranscriberEventCallback[]>();
|
|
64
|
+
|
|
65
|
+
constructor(options: { callService: any }) {
|
|
66
|
+
this._callService = options.callService;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static getInstance(options: { callService: any }) {
|
|
17
70
|
if (!AIAssistant.instance) {
|
|
18
|
-
AIAssistant.instance = new AIAssistant();
|
|
71
|
+
AIAssistant.instance = new AIAssistant(options);
|
|
19
72
|
}
|
|
20
73
|
return AIAssistant.instance;
|
|
21
74
|
}
|
|
22
75
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Get AI Transcriber Manager instance
|
|
78
|
+
* @returns AITranscriberManager instance or null if not available
|
|
79
|
+
*/
|
|
80
|
+
private getTranscriberManager = () => {
|
|
81
|
+
const callEngine = this._callService?.getTUICallEngineInstance();
|
|
82
|
+
if (!callEngine) {
|
|
83
|
+
console.warn(`${NAME.PREFIX}Failed to get call engine instance.`);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
28
86
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return
|
|
87
|
+
const trtcCloud = callEngine.getTRTCCloudInstance?.();
|
|
88
|
+
if (!trtcCloud || !trtcCloud.getAITranscriberManager) {
|
|
89
|
+
console.warn(`${NAME.PREFIX}AI Transcriber not supported.`);
|
|
90
|
+
return null;
|
|
33
91
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
92
|
+
|
|
93
|
+
return trtcCloud.getAITranscriberManager();
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
private triggerEvent = <T extends RealtimeTranscriberEvent>(
|
|
97
|
+
event: T,
|
|
98
|
+
eventInfo: RealtimeTranscriberEventInfoMap[T],
|
|
99
|
+
): void => {
|
|
100
|
+
const listeners = this.eventListeners.get(event);
|
|
101
|
+
if (listeners) {
|
|
102
|
+
listeners.forEach((callback) => {
|
|
103
|
+
try {
|
|
104
|
+
callback(eventInfo);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(`${NAME.PREFIX}Error in event callback for ${event}:`, error);
|
|
107
|
+
}
|
|
39
108
|
});
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
public startRealtimeTranscriber = async (config: {
|
|
113
|
+
sourceLanguage: TranscriberLanguage;
|
|
114
|
+
translationLanguages?: TranscriberLanguage[];
|
|
115
|
+
}) => {
|
|
116
|
+
const transcriberManager = this.getTranscriberManager();
|
|
117
|
+
if (!transcriberManager) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const transcriberRobotId = await transcriberManager.startRealtimeTranscriber(config);
|
|
123
|
+
|
|
124
|
+
TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, transcriberRobotId);
|
|
125
|
+
TUIStore.update(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_RUNNING, true);
|
|
126
|
+
return transcriberRobotId;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error(`${NAME.PREFIX}startRealtimeTranscriber error:`, error);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
public stopRealtimeTranscriber = async () => {
|
|
134
|
+
const transcriberManager = this.getTranscriberManager();
|
|
135
|
+
if (!transcriberManager) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const transcriberRobotId = TUIStore.getData(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID);
|
|
141
|
+
|
|
142
|
+
if (transcriberRobotId) {
|
|
143
|
+
await transcriberManager.stopRealtimeTranscriber(transcriberRobotId);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, null);
|
|
147
|
+
TUIStore.update(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_RUNNING, false);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error(`${NAME.PREFIX}stopRealtimeTranscriber error:`, error);
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
public updateRealTimeTranscriber = async (config: {
|
|
155
|
+
sourceLanguage: TranscriberLanguage;
|
|
156
|
+
translationLanguages?: TranscriberLanguage[];
|
|
157
|
+
}) => {
|
|
158
|
+
const transcriberManager = this.getTranscriberManager();
|
|
159
|
+
if (!transcriberManager) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const currentTranscriberRobotId = TUIStore.getData(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID);
|
|
165
|
+
|
|
166
|
+
if (currentTranscriberRobotId) {
|
|
167
|
+
await transcriberManager.stopRealtimeTranscriber(currentTranscriberRobotId);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const newTranscriberRobotId = await transcriberManager.startRealtimeTranscriber(config);
|
|
171
|
+
TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, newTranscriberRobotId);
|
|
172
|
+
|
|
173
|
+
return newTranscriberRobotId;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error(`${NAME.PREFIX}updateRealTimeTranscriber error:`, error);
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
public subscribeEvent = (event: RealtimeTranscriberEvent, callback: RealtimeTranscriberEventCallback) => {
|
|
181
|
+
if (!this.eventListeners.has(event)) {
|
|
182
|
+
this.eventListeners.set(event, []);
|
|
183
|
+
}
|
|
184
|
+
this.eventListeners.get(event)?.push(callback);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
public unsubscribeEvent = (event: RealtimeTranscriberEvent, callback: RealtimeTranscriberEventCallback) => {
|
|
188
|
+
const listeners = this.eventListeners.get(event);
|
|
189
|
+
if (listeners) {
|
|
190
|
+
const index = listeners.indexOf(callback);
|
|
191
|
+
if (index > -1) {
|
|
192
|
+
listeners.splice(index, 1);
|
|
193
|
+
}
|
|
194
|
+
if (listeners.length === 0) {
|
|
195
|
+
this.eventListeners.delete(event);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
private onReceiveTranscriberMessage = (
|
|
201
|
+
_roomId: string | number,
|
|
202
|
+
message: {
|
|
203
|
+
segmentId: string;
|
|
204
|
+
speakerUserId: string;
|
|
205
|
+
sourceText: string;
|
|
206
|
+
translationTexts?: Map<string, string>;
|
|
207
|
+
timestamp: number;
|
|
208
|
+
isCompleted: boolean;
|
|
209
|
+
},
|
|
210
|
+
) => {
|
|
211
|
+
const { segmentId } = message;
|
|
212
|
+
let realtimeMessageList: TranscriberMessage[] = TUIStore.getData(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST) || [];
|
|
213
|
+
|
|
214
|
+
const idx = realtimeMessageList.findIndex(msg => msg.segmentId === segmentId);
|
|
215
|
+
if (idx >= 0) {
|
|
216
|
+
realtimeMessageList[idx] = message;
|
|
40
217
|
} else {
|
|
41
|
-
|
|
218
|
+
realtimeMessageList.push(message);
|
|
42
219
|
}
|
|
43
|
-
|
|
220
|
+
|
|
221
|
+
TUIStore.update(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST, [...realtimeMessageList]);
|
|
222
|
+
|
|
223
|
+
this.triggerEvent(RealtimeTranscriberEvent.onReceiveTranscriberMessage, {
|
|
224
|
+
roomId: _roomId,
|
|
225
|
+
message,
|
|
226
|
+
});
|
|
227
|
+
};
|
|
44
228
|
|
|
45
|
-
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
229
|
+
private onRealtimeTranscriberStarted = (_roomId: string | number, _transcriberRobotId: string, _sourceLanguage: string) => {
|
|
230
|
+
this.triggerEvent(RealtimeTranscriberEvent.onRealtimeTranscriberStarted, {
|
|
231
|
+
roomId: _roomId,
|
|
232
|
+
transcriberRobotId: _transcriberRobotId,
|
|
233
|
+
sourceLanguage: _sourceLanguage,
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
private onRealtimeTranscriberStopped = (_roomId: string | number, _transcriberRobotId: string) => {
|
|
238
|
+
this.triggerEvent(RealtimeTranscriberEvent.onRealtimeTranscriberStopped, {
|
|
239
|
+
roomId: _roomId,
|
|
240
|
+
transcriberRobotId: _transcriberRobotId,
|
|
241
|
+
});
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
private onRealtimeTranscriberError = (_roomId: string | number, _transcriberRobotId: string, error: number, errorMessage: string) => {
|
|
245
|
+
this.triggerEvent(RealtimeTranscriberEvent.onRealtimeTranscriberError, {
|
|
246
|
+
roomId: _roomId,
|
|
247
|
+
transcriberRobotId: _transcriberRobotId,
|
|
248
|
+
error,
|
|
249
|
+
errorMessage,
|
|
250
|
+
});
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
public bindEvent = () => {
|
|
254
|
+
const transcriberManager = this.getTranscriberManager();
|
|
255
|
+
if (!transcriberManager) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
transcriberManager.addListener({
|
|
261
|
+
onReceiveTranscriberMessage: this.onReceiveTranscriberMessage,
|
|
262
|
+
onRealtimeTranscriberStarted: this.onRealtimeTranscriberStarted,
|
|
263
|
+
onRealtimeTranscriberStopped: this.onRealtimeTranscriberStopped,
|
|
264
|
+
onRealtimeTranscriberError: this.onRealtimeTranscriberError,
|
|
265
|
+
});
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error(`${NAME.PREFIX}bindEvent error:`, error);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
public unbindEvent = () => {
|
|
272
|
+
const transcriberManager = this.getTranscriberManager();
|
|
273
|
+
if (!transcriberManager) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
transcriberManager.removeListener({
|
|
279
|
+
onReceiveTranscriberMessage: this.onReceiveTranscriberMessage,
|
|
280
|
+
onRealtimeTranscriberStarted: this.onRealtimeTranscriberStarted,
|
|
281
|
+
onRealtimeTranscriberStopped: this.onRealtimeTranscriberStopped,
|
|
282
|
+
onRealtimeTranscriberError: this.onRealtimeTranscriberError,
|
|
283
|
+
});
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error(`${NAME.PREFIX}unbindEvent error:`, error);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
public reset = () => {
|
|
290
|
+
TUIStore.update(StoreName.CALL, NAME.TRANSCRIBER_ROBOT_ID, null);
|
|
291
|
+
TUIStore.update(StoreName.CALL, NAME.REALTIME_MESSAGE_LIST, []);
|
|
292
|
+
TUIStore.update(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_ENABLED, false);
|
|
293
|
+
this.eventListeners.clear();
|
|
294
|
+
};
|
|
51
295
|
}
|
|
296
|
+
|
|
297
|
+
|
|
@@ -27,15 +27,15 @@ const TUIGlobal: ITUIGlobal = TuiGlobal.getInstance();
|
|
|
27
27
|
const TUIStore: ITUIStore = TuiStore.getInstance();
|
|
28
28
|
const uiDesign = UIDesign.getInstance();
|
|
29
29
|
uiDesign.setTUIStore(TUIStore);
|
|
30
|
-
const version = '4.4.
|
|
31
|
-
import
|
|
32
|
-
const aiAssistant = AIAssistant.getInstance();
|
|
30
|
+
const version = '4.4.4';
|
|
31
|
+
import AIAssistant from './AIAssistant'; // 仅 web 支持 AI 实时字幕
|
|
33
32
|
const frameWork = 'react';
|
|
34
33
|
export { TUIGlobal, TUIStore, uiDesign };
|
|
35
34
|
|
|
36
35
|
export default class TUICallService {
|
|
37
36
|
static instance: TUICallService;
|
|
38
37
|
public _tuiCallEngine: any;
|
|
38
|
+
public _aiAssistant: any = null;
|
|
39
39
|
private _tim: any = null;
|
|
40
40
|
private _TUICore: any = null;
|
|
41
41
|
private _timerId: number = -1;
|
|
@@ -62,6 +62,7 @@ export default class TUICallService {
|
|
|
62
62
|
this._engineEventHandler = EngineEventHandler.getInstance({ callService: this });
|
|
63
63
|
|
|
64
64
|
this._chatCombine = ChatCombine.getInstance({ callService: this });
|
|
65
|
+
this._aiAssistant = AIAssistant.getInstance({ callService: this });
|
|
65
66
|
initBrowserCloseDetection(this.handleExceptionExit.bind(this));
|
|
66
67
|
}
|
|
67
68
|
static getInstance() {
|
|
@@ -122,9 +123,7 @@ export default class TUICallService {
|
|
|
122
123
|
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, { userId: userID });
|
|
123
124
|
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, { userId: userID });
|
|
124
125
|
uiDesign.updateViewBackgroundUserId('local');
|
|
125
|
-
|
|
126
|
-
aiAssistant.setImInstance(this.getTim());
|
|
127
|
-
this.enableAISubtitle(true);
|
|
126
|
+
this._aiAssistant?.bindEvent();
|
|
128
127
|
|
|
129
128
|
await this._tuiCallEngine.login({ userID, userSig, assetsPath: '' }); // web && mini
|
|
130
129
|
const uiConfig = TUIStore.getData(StoreName.CALL, NAME.CUSTOM_UI_CONFIG);
|
|
@@ -157,6 +156,8 @@ export default class TUICallService {
|
|
|
157
156
|
await this._tuiCallEngine.destroyInstance();
|
|
158
157
|
this._tuiCallEngine = null;
|
|
159
158
|
}
|
|
159
|
+
this._aiAssistant?.unbindEvent();
|
|
160
|
+
this._aiAssistant?.reset();
|
|
160
161
|
this._bellContext?.destroy();
|
|
161
162
|
this._bellContext = null;
|
|
162
163
|
} catch (error) {
|
|
@@ -275,6 +276,16 @@ export default class TUICallService {
|
|
|
275
276
|
throw error;
|
|
276
277
|
}
|
|
277
278
|
}
|
|
279
|
+
public async enableAITranscriber(enable: boolean) {
|
|
280
|
+
try {
|
|
281
|
+
// Update store to control UI translation switch and translation area visibility
|
|
282
|
+
TUIStore.update(StoreName.CALL, NAME.IS_AI_TRANSCRIBER_ENABLED, enable);
|
|
283
|
+
console.log(`${NAME.PREFIX}enableAITranscriber: ${enable}.`);
|
|
284
|
+
} catch (error: any) {
|
|
285
|
+
console.error(`${NAME.PREFIX}enableAITranscriber failed, error: ${error}.`);
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
278
289
|
// 修改默认铃声:只支持本地铃声文件,不支持在线铃声文件;修改铃声修改的是被叫的铃声
|
|
279
290
|
public async setCallingBell(filePath?: string) {
|
|
280
291
|
let isCheckFileExist: boolean = true;
|
|
@@ -310,9 +321,7 @@ export default class TUICallService {
|
|
|
310
321
|
public setCameraDefaultState(isOpen: boolean) {
|
|
311
322
|
uiDesign.setCameraDefaultState(isOpen);
|
|
312
323
|
}
|
|
313
|
-
|
|
314
|
-
aiAssistant.enableAISubtitle(enable);
|
|
315
|
-
}
|
|
324
|
+
// AI Subtitle functionality removed, focusing on real-time transcriber
|
|
316
325
|
// =============================【实验性接口】=============================
|
|
317
326
|
public callExperimentalAPI(jsonStr: string) {
|
|
318
327
|
const jsonObj = JSON.parse(jsonStr);
|
|
@@ -744,6 +753,7 @@ export default class TUICallService {
|
|
|
744
753
|
case NAME.ENABLE_FLOAT_WINDOW:
|
|
745
754
|
case NAME.LOCAL_USER_INFO:
|
|
746
755
|
case NAME.IS_SHOW_ENABLE_VIRTUAL_BACKGROUND:
|
|
756
|
+
case NAME.IS_AI_TRANSCRIBER_ENABLED:
|
|
747
757
|
case NAME.IS_FORCE_USE_V2_API:
|
|
748
758
|
case NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN: {
|
|
749
759
|
return false;
|
|
@@ -54,6 +54,11 @@ export default class CallStore {
|
|
|
54
54
|
// translate function
|
|
55
55
|
translate: t,
|
|
56
56
|
isForceUseV2API: false,
|
|
57
|
+
// AI Transcriber related fields
|
|
58
|
+
transcriberRobotId: null,
|
|
59
|
+
realtimeMessageList: [],
|
|
60
|
+
isAITranscriberEnabled: false,
|
|
61
|
+
isAITranscriberRunning: false,
|
|
57
62
|
};
|
|
58
63
|
public store: ICallStore = deepClone(this.defaultStore);
|
|
59
64
|
public prevStore: ICallStore = deepClone(this.defaultStore);
|
|
@@ -39,6 +39,10 @@ export const CALL_DATA_KEY: any = {
|
|
|
39
39
|
PUSHER_ID: 'pusherId',
|
|
40
40
|
IS_FORCE_USE_V2_API: 'isForceUseV2API',
|
|
41
41
|
MODAL_ERROR: 'modalError',
|
|
42
|
+
TRANSCRIBER_ROBOT_ID: 'transcriberRobotId',
|
|
43
|
+
REALTIME_MESSAGE_LIST: 'realtimeMessageList',
|
|
44
|
+
IS_AI_TRANSCRIBER_ENABLED: 'isAITranscriberEnabled', // Controls UI visibility
|
|
45
|
+
IS_AI_TRANSCRIBER_RUNNING: 'isAITranscriberRunning', // Controls AI transcriber running state
|
|
42
46
|
};
|
|
43
47
|
|
|
44
48
|
export const CHAT_DATA_KEY: any = {
|
|
@@ -52,4 +52,9 @@ export interface ICallStore {
|
|
|
52
52
|
// translate function
|
|
53
53
|
translate: Function,
|
|
54
54
|
isForceUseV2API: boolean, // 是否使用 call/groupCall 接口, 默认: false
|
|
55
|
+
// AI Transcriber related fields
|
|
56
|
+
transcriberRobotId: string | null, // AI 转写机器人 ID
|
|
57
|
+
realtimeMessageList: [], // 实时转写消息列表
|
|
58
|
+
isAITranscriberEnabled: boolean, // AI 转写UI是否显示
|
|
59
|
+
isAITranscriberRunning: boolean, // AI transcriber running state
|
|
55
60
|
}
|
|
@@ -146,4 +146,19 @@ export const en = {
|
|
|
146
146
|
'error.10013': "Call failed: You have been blocked by the other party or you have blocked the other party",
|
|
147
147
|
'error.10014': "Call initiation failed. User ID is invalid. Please confirm that the user is registered.",
|
|
148
148
|
'error': 'Error',
|
|
149
|
+
// AI Subtitle Settings
|
|
150
|
+
'ai-subtitle': 'AI Subtitle',
|
|
151
|
+
'ai-subtitle-settings': 'AI Subtitle Settings',
|
|
152
|
+
'recognition-language': 'Recognition Language',
|
|
153
|
+
'translation-language': 'Translation Language',
|
|
154
|
+
'subtitle-display-settings': 'Subtitle Display Settings',
|
|
155
|
+
'subtitle-display-bilingual': 'Show Bilingual Subtitles',
|
|
156
|
+
'no-translation': 'No Translation',
|
|
157
|
+
'show-bilingual': 'Show Bilingual',
|
|
158
|
+
'show-original-only': 'Show Original Only',
|
|
159
|
+
'save': 'Save',
|
|
160
|
+
'select-recognition-language': 'Please select recognition language',
|
|
161
|
+
'select-translation-language': 'Please select translation language',
|
|
162
|
+
'confirm': 'Confirm',
|
|
163
|
+
'recognition-and-translation-settings': 'Recognition & Translation Settings',
|
|
149
164
|
};
|
|
@@ -145,4 +145,19 @@ export const ja_JP = {
|
|
|
145
145
|
'error.10013': "通話失敗:相手にブロックされているか、または相手をブロックしています",
|
|
146
146
|
'error.10014': "通話の開始に失敗しました。ユーザーIDが無効です。ユーザーが登録されていることを確認してください。",
|
|
147
147
|
'error': 'エラー',
|
|
148
|
+
// AI Subtitle Settings
|
|
149
|
+
'ai-subtitle': 'AI字幕',
|
|
150
|
+
'ai-subtitle-settings': 'AI字幕設定',
|
|
151
|
+
'recognition-language': '認識言語',
|
|
152
|
+
'translation-language': '翻訳言語',
|
|
153
|
+
'subtitle-display-settings': '字幕表示設定',
|
|
154
|
+
'subtitle-display-bilingual': 'バイリンガル字幕を表示',
|
|
155
|
+
'no-translation': '翻訳しない',
|
|
156
|
+
'show-bilingual': 'バイリンガル表示',
|
|
157
|
+
'show-original-only': '原文のみ表示',
|
|
158
|
+
'save': '保存',
|
|
159
|
+
'select-recognition-language': '認識言語を選択してください',
|
|
160
|
+
'select-translation-language': '翻訳言語を選択してください',
|
|
161
|
+
'confirm': '確認',
|
|
162
|
+
'recognition-and-translation-settings': '認識と翻訳の設定',
|
|
148
163
|
};
|