@tencent-rtc/mcp 1.7.3 → 1.7.5
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/CHANGELOG.md +23 -0
- package/README.md +22 -23
- package/knowledge/path-meta.js +23 -1
- package/package.json +1 -1
- package/resource/index/manifest.json +1 -1
- package/resource/knowledge/callkit/best-practice/android/video-chat-dating.md +497 -0
- package/resource/knowledge/callkit/best-practice/flutter/android-keep-call-alive-background.md +79 -0
- package/resource/knowledge/callkit/best-practice/flutter/background-incoming-call-no-notification.md +99 -0
- package/resource/knowledge/callkit/best-practice/flutter/ios-keep-call-alive-background.md +93 -0
- package/resource/knowledge/callkit/best-practice/flutter/ios-startup-crash-version-voip.md +68 -0
- package/resource/knowledge/callkit/core-sdk/miniprogram/call-engine.md +386 -0
- package/resource/knowledge/callkit/core-sdk/miniprogram/call-event.md +315 -0
- package/resource/knowledge/callkit/faq/uniapp-miniprogram.md +204 -0
- package/resource/knowledge/callkit/preset/harmonyos/api.md +362 -0
- package/resource/knowledge/callkit/preset/harmonyos/integration.md +318 -0
- package/resource/knowledge/callkit/preset/miniprogram/api-uniapp.md +430 -0
- package/resource/knowledge/callkit/preset/miniprogram/api.md +293 -0
- package/resource/knowledge/callkit/preset/miniprogram/integration.md +294 -0
- package/resource/knowledge/callkit/preset/miniprogram/uniapp-integration.md +224 -0
- package/resource/knowledge/callkit/preset/uni-app/api.md +339 -0
- package/resource/knowledge/callkit/preset/uni-app/integration.md +243 -0
- package/resource/knowledge/chat/preset/react/h5/integration.md +964 -0
- package/resource/knowledge/chat/preset/vue/h5/integration.md +717 -0
- package/resource/knowledge/rtcengine/web/plugin/23-advanced-support-detection.md +6 -6
- package/resource/knowledge/rtcengine/web/plugin/31-advanced-plugins-overview.md +8 -6
- package/resource/knowledge/rtcengine/web/plugin/42-advanced-realtime-transcriber.md +30 -10
- package/server.js +1 -1
- package/utils/preflight-route.js +203 -185
- package/utils/report-cls-client.js +1 -1
- package/utils/report-es-client.js +1 -1
|
@@ -23,7 +23,7 @@ TRTC.isSupported().then(checkResult => {
|
|
|
23
23
|
|
|
24
24
|
### Option 1: Use Device Detector Plugin (including default UI)
|
|
25
25
|
|
|
26
|
-
The device detector plugin is used to test the user's media device(camera, microphone, speaker) and network which is recommended to be used before the user enters the room.
|
|
26
|
+
The device detector plugin is used to test the user's media device(camera, microphone, speaker) and network which is recommended to be used before the user enters the room.
|
|
27
27
|
|
|
28
28
|
> - Support TRTC Web SDK version >= v5.8.0
|
|
29
29
|
> - The plugin includes a default UI. If the UI does not meet your product design requirements, you can refer to Option 2 for a custom UI implementation.
|
|
@@ -38,7 +38,7 @@ const trtc = TRTC.create({ plugins: [DeviceDetector] });
|
|
|
38
38
|
const result = await trtc.startPlugin('DeviceDetector');
|
|
39
39
|
|
|
40
40
|
// 2. Test Media Device & Network Quality
|
|
41
|
-
const options = {
|
|
41
|
+
const options = {
|
|
42
42
|
networkDetect: { sdkAppId, userId, userSig }
|
|
43
43
|
}
|
|
44
44
|
const resultWithNetwork = await trtc.startPlugin('DeviceDetector', options);
|
|
@@ -70,11 +70,11 @@ Enable the mirror checkbox during camera detection
|
|
|
70
70
|
|
|
71
71
|
| Name | Type | Attributes | Description |
|
|
72
72
|
| --------------- | ------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
73
|
-
| sdkAppId |
|
|
73
|
+
| sdkAppId | number | required | Your sdkAppId |
|
|
74
74
|
| userId | string | required | The userId for testing uplink network quality. It should be different from downlinkUserId. |
|
|
75
75
|
| userSig | string | required | The [UserSig](https://trtc.io/document/35166) of the userId. |
|
|
76
|
-
| downlinkUserId | string |
|
|
77
|
-
| downlinkUserSig | string |
|
|
76
|
+
| downlinkUserId | string | optional | The userId for testing downlink network quality. It should be different from userId. Fill in downlinkUserId and downlinkUserSig will perform a downlink network test. |
|
|
77
|
+
| downlinkUserSig | string | optional | The [UserSig](https://trtc.io/document/35166) of the downlinkUserId. |
|
|
78
78
|
| roomId | number | optional | Optional. The default value is 8080. The value must be an integer of [1, 4294967294] |
|
|
79
79
|
|
|
80
80
|
##### trtc.stopPlugin('DeviceDetector')
|
|
@@ -125,7 +125,7 @@ The device connection checks if the user's device has a camera, microphone, and
|
|
|
125
125
|
|
|
126
126
|
```javascript
|
|
127
127
|
import TRTC from 'trtc-sdk-v5';
|
|
128
|
-
|
|
128
|
+
|
|
129
129
|
const cameraList = await TRTC.getCameraList();
|
|
130
130
|
const micList = await TRTC.getMicrophoneList();
|
|
131
131
|
const hasCameraDevice = cameraList.some(camera => camera.deviceId !== '');
|
|
@@ -28,6 +28,7 @@ TRTC Web SDK v5 provides a plugin system that supports on-demand loading of vari
|
|
|
28
28
|
| **AIDenoiser** | AI noise reduction with support for far-field cancellation mode | - | Exclusive Edition | Required | {@tutorial 35-advanced-ai-denoiser} |
|
|
29
29
|
| **VoiceChanger** | Voice effects with 11 voice types (child, loli, uncle, etc.) | ≥ 5.10.0 | Premium Edition | - | {@tutorial 37-advanced-voice-changer} |
|
|
30
30
|
| **RealtimeTranscriber** | Real-time speech transcription and translation, converting audio to text with multi-language translation | ≥ 5.15.0 | None | - | {@tutorial 42-advanced-realtime-transcriber} |
|
|
31
|
+
| **Chorus** | KTV chorus with lead/backup singer roles, lyrics sync
|
|
31
32
|
|
|
32
33
|
### Streaming & Mixing
|
|
33
34
|
|
|
@@ -42,6 +43,7 @@ TRTC Web SDK v5 provides a plugin system that supports on-demand loading of vari
|
|
|
42
43
|
|---------|---------|---------|---------|---------|---------|
|
|
43
44
|
| **DeviceDetector** | Device detection for camera, microphone, speaker, and network with default UI | ≥ 5.8.0 | None | - | {@tutorial 23-advanced-support-detection} |
|
|
44
45
|
| **Debug** | Debug mode with full log upload/export and audio/video dump | ≥ 5.8.0 | None | - | {@tutorial 18-basic-debug} |
|
|
46
|
+
| **TRTCVideoDecoder** | Video decoder fallback to software decoding on failure | ≥ 5.9
|
|
45
47
|
| **SmallStreamAutoSwitcher** | Automatic dual-stream switching for smooth streaming in poor network | ≥ 5.11.0 | None | - | {@tutorial 41-advanced-small-stream-auto-switcher} |
|
|
46
48
|
<!-- | **CustomEncryption** | Custom encryption for end-to-end audio/video encryption with built-in or custom algorithms | - | Contact us | - | - | -->
|
|
47
49
|
|
|
@@ -61,7 +63,7 @@ import TRTC from 'trtc-sdk-v5';
|
|
|
61
63
|
import { VirtualBackground } from 'trtc-sdk-v5/plugins/video-effect/virtual-background';
|
|
62
64
|
import { AIDenoiser } from 'trtc-sdk-v5/plugins/ai-denoiser';
|
|
63
65
|
|
|
64
|
-
const trtc = TRTC.create({
|
|
66
|
+
const trtc = TRTC.create({
|
|
65
67
|
plugins: [VirtualBackground, AIDenoiser],
|
|
66
68
|
assetsPath: 'https://your-cdn/assets' // If plugins require static assets
|
|
67
69
|
});
|
|
@@ -89,7 +91,7 @@ Use `trtc.updatePlugin(pluginName, options)` to dynamically update plugin parame
|
|
|
89
91
|
```javascript
|
|
90
92
|
await trtc.updatePlugin('VirtualBackground', {
|
|
91
93
|
type: 'image',
|
|
92
|
-
|
|
94
|
+
src: 'https://example.com/background.jpg'
|
|
93
95
|
});
|
|
94
96
|
```
|
|
95
97
|
|
|
@@ -164,8 +166,8 @@ await trtc.updatePlugin('VideoMixer', {
|
|
|
164
166
|
});
|
|
165
167
|
|
|
166
168
|
// Publish mixed track
|
|
167
|
-
await trtc.startLocalVideo({
|
|
168
|
-
option: {
|
|
169
|
+
await trtc.startLocalVideo({
|
|
170
|
+
option: {
|
|
169
171
|
videoTrack: track,
|
|
170
172
|
profile: { width: 1920, height: 1080, bitrate: 2000 }
|
|
171
173
|
}
|
|
@@ -185,7 +187,7 @@ TRTCVideoDecoder automatically starts fallback logic on decode failure after reg
|
|
|
185
187
|
```javascript
|
|
186
188
|
import TRTCVideoDecoder from 'trtc-sdk-v5/plugins/video-decoder';
|
|
187
189
|
|
|
188
|
-
const trtc = TRTC.create({
|
|
190
|
+
const trtc = TRTC.create({
|
|
189
191
|
plugins: [TRTCVideoDecoder],
|
|
190
192
|
assetsPath: 'https://your-cdn/assets'
|
|
191
193
|
});
|
|
@@ -309,7 +311,7 @@ cp -r node_modules/trtc-sdk-v5/assets ./public/assets
|
|
|
309
311
|
Pass the asset path when creating TRTC instance:
|
|
310
312
|
|
|
311
313
|
```javascript
|
|
312
|
-
const trtc = TRTC.create({
|
|
314
|
+
const trtc = TRTC.create({
|
|
313
315
|
plugins: [VirtualBackground, AIDenoiser],
|
|
314
316
|
assetsPath: 'https://your-cdn.com/assets'
|
|
315
317
|
});
|
|
@@ -90,7 +90,7 @@ const robotId = await trtc.startPlugin('RealtimeTranscriber', {
|
|
|
90
90
|
trtc.on(TRTC.EVENT.REALTIME_TRANSCRIBER_MESSAGE, (data) => {
|
|
91
91
|
console.log(`👤 ${data.speakerUserId}:`);
|
|
92
92
|
console.log(` Original: ${data.sourceText}`);
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
if (data.translationTexts) {
|
|
95
95
|
Object.entries(data.translationTexts).forEach(([lang, text]) => {
|
|
96
96
|
console.log(` ${lang.toUpperCase()}: ${text}`);
|
|
@@ -119,7 +119,7 @@ Starts a real-time transcription task.
|
|
|
119
119
|
|
|
120
120
|
| Name | Type | Required | Description |
|
|
121
121
|
|------|------|----------|-------------|
|
|
122
|
-
| sourceLanguage | `string` | Required | Source language code
|
|
122
|
+
| sourceLanguage | `string` | Required | Source language code. Specifies the language of the source audio. Use a standard language code (for example, `'zh'`). For the list of supported languages, see [Supported Languages](#supported-languages) |
|
|
123
123
|
| translationLanguages | `string` \| `string[]` | Optional | Target translation language(s), supports single language or array, e.g., `'en'` or `['en', 'ja']` |
|
|
124
124
|
| userIdsToTranscribe | `string` \| `string[]` | Optional | User ID(s) to transcribe, defaults to `'all'` for all users, can also specify single or multiple user IDs |
|
|
125
125
|
| transcriberRobotId | `string` | Optional | Custom transcriber robot ID, auto-generated if not provided |
|
|
@@ -183,6 +183,30 @@ trtc.on(TRTC.EVENT.REALTIME_TRANSCRIBER_STATE_CHANGED, (event) => {
|
|
|
183
183
|
});
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
+
#### Backend Resource Limitation Notification
|
|
187
|
+
|
|
188
|
+
When the backend AI service (ASR/translation) resources are limited, the SDK proactively notifies through the `TRTC.EVENT.ERROR` event (the backend sends this notification at most once per minute, and stops sending it once resources recover). In this case:
|
|
189
|
+
|
|
190
|
+
- `error.code` is always `5400` (SERVER_ERROR, indicating the server failed to process the request)
|
|
191
|
+
- `error.extraCode` is the downgrade event code, see the table below
|
|
192
|
+
- `error.data` carries `{ roomId, transcriberRobotId, taskId, resourceType }` to identify the affected transcription task
|
|
193
|
+
|
|
194
|
+
You should degrade or disable the relevant AI capabilities in time to avoid affecting your online business.
|
|
195
|
+
|
|
196
|
+
| Event Code (extraCode) | Description | Suggested Action |
|
|
197
|
+
|------------|-------------|------------------|
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
trtc.on(TRTC.EVENT.ERROR, (error) => {
|
|
201
|
+
if (error.code === 5400 && error.extraCode === 5001) {
|
|
202
|
+
// e.g. error.message: 'The ASR/Translate concurrent usage has exceeded the limit. ...'
|
|
203
|
+
// error.data: { roomId, transcriberRobotId, taskId, resourceType }
|
|
204
|
+
console.warn(`Transcription service resources are limited, robotId: ${error.data.transcriberRobotId}, ${error.message}`);
|
|
205
|
+
// Degrade or disable the relevant AI capabilities as needed
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
186
210
|
### TRTC.EVENT.REALTIME_TRANSCRIBER_MESSAGE
|
|
187
211
|
|
|
188
212
|
Transcription message event, triggered when new transcription results are available.
|
|
@@ -204,10 +228,10 @@ Transcription message event, triggered when new transcription results are availa
|
|
|
204
228
|
```javascript
|
|
205
229
|
trtc.on(TRTC.EVENT.REALTIME_TRANSCRIBER_MESSAGE, (event) => {
|
|
206
230
|
const { speakerUserId, sourceText, translationTexts, isCompleted } = event;
|
|
207
|
-
|
|
231
|
+
|
|
208
232
|
// Display real-time transcription result
|
|
209
233
|
console.log(`[${isCompleted ? 'Completed' : 'In Progress'}] ${speakerUserId}: ${sourceText}`);
|
|
210
|
-
|
|
234
|
+
|
|
211
235
|
// Display translation results
|
|
212
236
|
if (translationTexts) {
|
|
213
237
|
translationTexts.forEach(({ language, text }) => {
|
|
@@ -228,10 +252,6 @@ trtc.off(TRTC.EVENT.REALTIME_TRANSCRIBER_MESSAGE, handler);
|
|
|
228
252
|
|----------|------|
|
|
229
253
|
| Chinese | `zh` |
|
|
230
254
|
| English | `en` |
|
|
231
|
-
|
|
232
|
-
<!--
|
|
233
|
-
| Traditional Chinese | `zh-tw` |
|
|
234
|
-
| Chinese Dialects | `zh-dialect` |
|
|
235
255
|
| Cantonese | `zh-yue` |
|
|
236
256
|
| Vietnamese | `vi` |
|
|
237
257
|
| Japanese | `ja` |
|
|
@@ -252,7 +272,7 @@ trtc.off(TRTC.EVENT.REALTIME_TRANSCRIBER_MESSAGE, handler);
|
|
|
252
272
|
| Swedish | `sv` |
|
|
253
273
|
| Danish | `da` |
|
|
254
274
|
| Norwegian | `no` |
|
|
255
|
-
|
|
275
|
+
| For more languages
|
|
256
276
|
|
|
257
277
|
### Translation Target Languages
|
|
258
278
|
|
|
@@ -301,7 +321,7 @@ const subtitleMap = new Map();
|
|
|
301
321
|
|
|
302
322
|
trtc.on(TRTC.EVENT.REALTIME_TRANSCRIBER_MESSAGE, (event) => {
|
|
303
323
|
subtitleMap.set(event.segmentId, event);
|
|
304
|
-
|
|
324
|
+
|
|
305
325
|
if (event.isCompleted) {
|
|
306
326
|
// Speech segment completed, can be archived
|
|
307
327
|
archiveSubtitle(subtitleMap.get(event.segmentId));
|
package/server.js
CHANGED
package/utils/preflight-route.js
CHANGED
|
@@ -1,209 +1,227 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
live: /LiveKit|TUILiveKit|直播|语聊房|连麦|开播|观看直播|\blive\s+stream(?:ing)?\b|\bvoice\s+room\b|co-?host(?:ing)?|go\s+live|start\s+stream(?:ing)?|watch(?:ing)?\s+live|\bbroadcast(?:ing)?\b/i,
|
|
6
|
-
room: /RoomKit|会议|会中|会议室|屏幕分享|白板|\bconference\b|\bmeetings?\b|in-?meeting|meeting\s+room|screen\s+shar(?:e|ing)|white\s*board/i,
|
|
7
|
-
chat: /\bchat\b|\bim\b|会话|消息|TUIKit|ChatKit|\bconversation\b|\bmessaging\b|\binstant\s+messaging\b/i,
|
|
8
|
-
push: /TIMPush|推送|离线推送|FCM|APNs|华为|小米|OPPO|vivo|\bpush(?:\s+notification)?s?\b|offline\s+push|vendor\s+channel|\bHuawei\b|\bXiaomi\b|\bHonor\b/i,
|
|
9
|
-
};
|
|
10
|
-
// 平台词(纯运行形态,不含任何产品词)
|
|
11
|
-
const PLATFORM_NATIVE_SIGNAL = /\b(android|ios|flutter|c\+\+|native)\b|安卓\s*(sdk|端|原生)|iOS\s*(SDK|端|原生)|原生\s*(sdk|端)?|native\s+(?:sdk|client|app)|mobile\s+(?:sdk|native)/i;
|
|
12
|
-
const PLATFORM_WEB_SIGNAL = /\b(web|h5|html5|browser|webview)\b|浏览器|网页|Web端|H5端|网页端|web\s+(?:app|client|page)|in-?browser|mobile\s+browser|安卓\s*(浏览器|网页|H5|Web|webview)|iOS\s*(浏览器|网页|H5|Web|webview)|Android\s*(?:浏览器|网页|H5|Web|webview|browser|web)|iPad\s*(?:浏览器|网页|H5|Web|browser|web)|\b(Safari|Chrome|Edge|UC|QQ浏览器|QQ\s+Browser)\b/i;
|
|
13
|
-
// 小程序信号:小程序 / 微信小程序 / miniprogram / mini program / AppService(小程序逻辑层专有名词)。
|
|
14
|
-
// 命中即锁定 miniprogram,并像 web 一样压制 native 单字与 native 兜底,
|
|
15
|
-
// 避免「PC 微信小程序…没有原生 WebSocket」这类描述里的「原生」被 PLATFORM_NATIVE_SIGNAL 误命中后兜底成 android。
|
|
1
|
+
const PLATFORM_WEB_SIGNAL = /\b(web|h5|html5|browser|webview)\b|\bweb[_-]?rtc\b|浏览器|网页|Web端|H5端|网页端|web\s+(?:app|client|page)|in-?browser|mobile\s+browser|安卓\s*(浏览器|网页|H5|Web|webview)|iOS\s*(浏览器|网页|H5|Web|webview)|Android\s*(?:浏览器|网页|H5|Web|webview|browser|web)|iPad\s*(?:浏览器|网页|H5|Web|browser|web)|\b(Safari|Chrome|Edge|UC|QQ浏览器|QQ\s+Browser)\b/i;
|
|
2
|
+
const FRAMEWORK_UNIAPP_SIGNAL = /\buni[-\s]?app\b|\buniapp\b|uni-app|HBuilderX/i;
|
|
3
|
+
const FRAMEWORK_FLUTTER_SIGNAL = /\bflutter\b/i;
|
|
4
|
+
const FRAMEWORK_REACT_NATIVE_SIGNAL = /\breact-native\b/i;
|
|
16
5
|
const PLATFORM_MINIPROGRAM_SIGNAL = /小程序|微信小程序|\bmini[-\s]?program\b|\bminiprogram\b|\bAppService\b/i;
|
|
17
|
-
|
|
18
|
-
const WEB_PRODUCT_PAIR_SIGNAL = /\bweb\s+(trtc|sdk|im|chat|roomkit)\b|\b(trtc|sdk|im|chat|roomkit)\s+web\b|trtc[-\s]?sdk|trtc\.js/i;
|
|
19
|
-
// API 词(客户端调用形态)
|
|
6
|
+
const WEB_PRODUCT_PAIR_SIGNAL = /\bweb\s+(trtc|sdk|im|chat|roomkit)\b|\b(trtc|sdk|im|chat|roomkit)\s+web\b|trtc[-\s]sdk[-\s]?web|trtc\.js/i;
|
|
20
7
|
const NATIVE_TRTC_CLASS_SIGNAL = /\b(TRTCCloud|TRTCCloudListener|TRTCCloudDelegate|ITRTCCloud|TRTCTypeDef|TRTCCloudCallback|TXAudioEffectManager|TXBeautyManager|TXDeviceManager|TXLiteAVCode|TXLiveSDKTypeDef)\b/i;
|
|
21
8
|
const NATIVE_TRTC_METHOD_SIGNAL = /\b(?:TRTCCloud|ITRTCCloud|TX[A-Za-z0-9_]+)\s*(?:\.|::|->)\s*(?:set|get|start|stop|enable|disable)[A-Z][A-Za-z0-9_]+\b/i;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
//
|
|
9
|
+
// Web-only TRTC API 特征识别(合并为一条正则,用 | 分支组织三类判定条件):
|
|
10
|
+
// 1. setCurrentSpeaker/getMicrophoneList/getSpeakerList/getCameraList
|
|
11
|
+
// —— Web SDK 专属命名,native SDK 无同名方法,允许裸词命中(无需 "TRTC."/"trtc." 前缀)
|
|
12
|
+
// 2. TRTC.isSupported / TRTC.create —— 语义过于通用,仍要求带点号前缀限定
|
|
13
|
+
// 3. trtc.startLocalAudio/startLocalVideo/updateLocalAudio/updateLocalVideo
|
|
14
|
+
// —— native(TRTCCloud.xxx)也有同名方法,不是 Web 专属,仍要求带 "trtc."(小写)前缀区分
|
|
15
|
+
const WEB_TRTC_API_RE = /\b(setCurrentSpeaker|getMicrophoneList|getSpeakerList|getCameraList)\b|TRTC\.(isSupported|create)|trtc\.(startLocalAudio|startLocalVideo|updateLocalAudio|updateLocalVideo)/i;
|
|
16
|
+
const TRTC_FAMILY = new Set(['rtcengine', 'native_trtc_sdk']);
|
|
27
17
|
function uniq(list) {
|
|
28
18
|
return [...new Set(list)];
|
|
29
19
|
}
|
|
30
|
-
function
|
|
31
|
-
const matches = prompt.match(/\b(?:TRTC|trtc)\.[A-Za-z_][A-Za-z0-9_]*\b/g) ?? [];
|
|
32
|
-
return uniq(matches.map((m) => m.replace(/^trtc\./i, 'TRTC.')));
|
|
33
|
-
}
|
|
34
|
-
function collectPromptSignals(prompt) {
|
|
35
|
-
const products = new Set();
|
|
36
|
-
if (PRODUCT_SIGNALS.call.test(prompt))
|
|
37
|
-
products.add('call');
|
|
38
|
-
if (PRODUCT_SIGNALS.live.test(prompt))
|
|
39
|
-
products.add('live');
|
|
40
|
-
if (PRODUCT_SIGNALS.room.test(prompt))
|
|
41
|
-
products.add('room');
|
|
42
|
-
if (PRODUCT_SIGNALS.chat.test(prompt))
|
|
43
|
-
products.add('chat');
|
|
44
|
-
if (PRODUCT_SIGNALS.push.test(prompt))
|
|
45
|
-
products.add('push');
|
|
46
|
-
if (TRTC_SIGNAL.test(prompt))
|
|
47
|
-
products.add('trtc');
|
|
48
|
-
// 平台 tokens:web signal 锁定 → 压制 native 单字(安卓/iOS 仅归 hostEnv);
|
|
49
|
-
// miniprogram signal 锁定 → 同样压制 native 单字与 native 兜底(小程序逻辑层"没有原生 X"不应被判成 native)。
|
|
50
|
-
// 真复合词(unreal/unity/harmonyos/react-native/flutter/c++)不受影响。
|
|
51
|
-
const platforms = [];
|
|
52
|
-
const webLocked = PLATFORM_WEB_SIGNAL.test(prompt) || WEB_PRODUCT_PAIR_SIGNAL.test(prompt);
|
|
53
|
-
if (webLocked) {
|
|
54
|
-
platforms.push('web');
|
|
55
|
-
}
|
|
56
|
-
// 小程序信号优先级高于 native 单字/兜底:命中即锁定 miniprogram。
|
|
57
|
-
// web + 小程序可同时成立(如"小程序 web-view"),各自入 platforms 由 search core 做多平台归并。
|
|
58
|
-
const mpLocked = PLATFORM_MINIPROGRAM_SIGNAL.test(prompt);
|
|
59
|
-
if (mpLocked) {
|
|
60
|
-
platforms.push('miniprogram');
|
|
61
|
-
}
|
|
62
|
-
const nativeSpecificHits = [];
|
|
63
|
-
if (/\b(?:unreal|UE[45]?|unreal-engine)\b/i.test(prompt))
|
|
64
|
-
nativeSpecificHits.push('unreal-engine');
|
|
65
|
-
if (/\b(?:unity)\b/i.test(prompt))
|
|
66
|
-
nativeSpecificHits.push('unity');
|
|
67
|
-
if (/\b(?:harmonyos|鸿蒙|HarmonyOS)\b/i.test(prompt))
|
|
68
|
-
nativeSpecificHits.push('harmonyos');
|
|
69
|
-
if (/\b(?:react-native)\b/i.test(prompt))
|
|
70
|
-
nativeSpecificHits.push('react-native');
|
|
71
|
-
if (/\b(?:flutter)\b/i.test(prompt))
|
|
72
|
-
nativeSpecificHits.push('flutter');
|
|
73
|
-
if (/\b(?:c\+\+)\b/i.test(prompt))
|
|
74
|
-
nativeSpecificHits.push('c++');
|
|
75
|
-
// 安卓/iOS 单字只在非 web-locked 且非 miniprogram-locked 时进 platforms(否则仅归 hostEnv)
|
|
76
|
-
if (!webLocked && !mpLocked && /\b(?:ios|iphone|ipad|苹果)\b/i.test(prompt))
|
|
77
|
-
nativeSpecificHits.push('ios');
|
|
78
|
-
if (!webLocked && !mpLocked && /\b(?:android|安卓)\b/i.test(prompt))
|
|
79
|
-
nativeSpecificHits.push('android');
|
|
80
|
-
// 真复合词(如 unity/unreal/flutter/harmonyos/react-native/c++)无条件 push;单字在 web/miniprogram 锁定时被压制
|
|
81
|
-
if (nativeSpecificHits.length > 0) {
|
|
82
|
-
platforms.push(...nativeSpecificHits);
|
|
83
|
-
}
|
|
84
|
-
else if (!webLocked && !mpLocked && PLATFORM_NATIVE_SIGNAL.test(prompt)) {
|
|
85
|
-
// 仅 "native" 词,未指定细分 → 默认 android(web/miniprogram 锁定时不触发 fallback,
|
|
86
|
-
// 避免被 \b(android|ios|...) 或"原生"反向命中把 web/小程序 路径覆盖成 native)
|
|
87
|
-
platforms.push('android');
|
|
88
|
-
}
|
|
89
|
-
// Host env:仅用于标记,不进 platforms 归一化
|
|
90
|
-
let hostEnv = null;
|
|
20
|
+
function detectHostEnv(prompt) {
|
|
91
21
|
if (/\bios\b|iphone|ipad|苹果/i.test(prompt))
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const hasWebTrtcApi = PLATFORM_WEB_SIGNAL.test(prompt)
|
|
97
|
-
|| WEB_TRTC_API_RE.test(prompt)
|
|
98
|
-
|| queryApiTokens.some((t) => WEB_TRTC_TOKEN_RE.test(t));
|
|
99
|
-
const hasNativeTrtcApi = NATIVE_TRTC_CLASS_SIGNAL.test(prompt) || NATIVE_TRTC_METHOD_SIGNAL.test(prompt);
|
|
100
|
-
return { products, platforms, hostEnv, hasWebTrtcApi, hasNativeTrtcApi, queryApiTokens };
|
|
22
|
+
return 'ios';
|
|
23
|
+
if (/\bandroid\b|安卓/i.test(prompt))
|
|
24
|
+
return 'android';
|
|
25
|
+
return null;
|
|
101
26
|
}
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
&& explicit.length === promptValues.length
|
|
114
|
-
&& explicit.every((v) => promptSet.has(v));
|
|
115
|
-
return { values: uniq(promptValues), corrected: !sameAsExplicit };
|
|
116
|
-
}
|
|
117
|
-
// 情形 2:prompt 真值无平台信息 → 信任上游 agent 基于上下文(IDE/文件/历史)的推理
|
|
118
|
-
if (!promptValues.length) {
|
|
119
|
-
return { values: uniq(explicit ?? []), corrected: false };
|
|
120
|
-
}
|
|
121
|
-
return { values: [], corrected: false };
|
|
27
|
+
function applyFrameworkLock(frameworks) {
|
|
28
|
+
const unique = uniq(frameworks);
|
|
29
|
+
if (unique.includes('uni-app'))
|
|
30
|
+
return ['uni-app'];
|
|
31
|
+
if (unique.includes('flutter'))
|
|
32
|
+
return ['flutter'];
|
|
33
|
+
if (unique.includes('react-native'))
|
|
34
|
+
return ['react-native'];
|
|
35
|
+
if (unique.includes('miniprogram'))
|
|
36
|
+
return ['miniprogram'];
|
|
37
|
+
return unique;
|
|
122
38
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
39
|
+
function collectPromptFrameworks(prompt) {
|
|
40
|
+
const values = [];
|
|
41
|
+
const uniapp = FRAMEWORK_UNIAPP_SIGNAL.test(prompt);
|
|
42
|
+
const flutter = FRAMEWORK_FLUTTER_SIGNAL.test(prompt);
|
|
43
|
+
const reactNative = FRAMEWORK_REACT_NATIVE_SIGNAL.test(prompt);
|
|
44
|
+
if (uniapp)
|
|
45
|
+
values.push('uni-app');
|
|
46
|
+
if (flutter)
|
|
47
|
+
values.push('flutter');
|
|
48
|
+
if (reactNative)
|
|
49
|
+
values.push('react-native');
|
|
50
|
+
const web = !uniapp && !flutter && !reactNative && (PLATFORM_WEB_SIGNAL.test(prompt) || WEB_PRODUCT_PAIR_SIGNAL.test(prompt));
|
|
51
|
+
if (web)
|
|
52
|
+
values.push('web');
|
|
53
|
+
const miniprogram = !uniapp && !flutter && !reactNative && PLATFORM_MINIPROGRAM_SIGNAL.test(prompt);
|
|
54
|
+
if (miniprogram)
|
|
55
|
+
values.push('miniprogram');
|
|
56
|
+
if (!uniapp && !flutter && !reactNative && /\b(?:unreal|UE[45]?|unreal-engine)\b/i.test(prompt))
|
|
57
|
+
values.push('unreal-engine');
|
|
58
|
+
if (!uniapp && !flutter && !reactNative && /\b(?:unity)\b/i.test(prompt))
|
|
59
|
+
values.push('unity');
|
|
60
|
+
if (!uniapp && !flutter && !reactNative && /\b(?:harmonyos|鸿蒙|HarmonyOS)\b/i.test(prompt))
|
|
61
|
+
values.push('harmonyos');
|
|
62
|
+
if (!uniapp && !flutter && !reactNative && /\b(?:c\+\+)\b/i.test(prompt))
|
|
63
|
+
values.push('c++');
|
|
64
|
+
if (!uniapp && !flutter && !reactNative && !web && !miniprogram && /\b(?:ios|iphone|ipad)\b|苹果/i.test(prompt))
|
|
65
|
+
values.push('ios');
|
|
66
|
+
if (!uniapp && !flutter && !reactNative && !web && !miniprogram && /\b(?:apns|voip)\b/i.test(prompt))
|
|
67
|
+
values.push('ios');
|
|
68
|
+
if (!uniapp && !flutter && !reactNative && !web && !miniprogram && /\bandroid\b|安卓/i.test(prompt))
|
|
69
|
+
values.push('android');
|
|
70
|
+
return uniq(values);
|
|
130
71
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const envDiff = hostEnv != null && !frameworks.includes(hostEnv);
|
|
72
|
+
function collectHardSignals(prompt) {
|
|
73
|
+
const hasWebTrtcApi = WEB_TRTC_API_RE.test(prompt);
|
|
134
74
|
return {
|
|
135
|
-
|
|
136
|
-
|
|
75
|
+
hostEnv: detectHostEnv(prompt),
|
|
76
|
+
promptFrameworks: collectPromptFrameworks(prompt),
|
|
77
|
+
hasStrongFrameworkLock: FRAMEWORK_UNIAPP_SIGNAL.test(prompt)
|
|
78
|
+
|| FRAMEWORK_FLUTTER_SIGNAL.test(prompt)
|
|
79
|
+
|| FRAMEWORK_REACT_NATIVE_SIGNAL.test(prompt)
|
|
80
|
+
|| PLATFORM_MINIPROGRAM_SIGNAL.test(prompt),
|
|
81
|
+
hasWebRtcHardSignal: /\bweb[_-]?rtc\b|webrtc/i.test(prompt)
|
|
82
|
+
|| hasWebTrtcApi
|
|
83
|
+
|| /trtc\.js/i.test(prompt),
|
|
84
|
+
hasWebTrtcApiSignal: hasWebTrtcApi,
|
|
85
|
+
hasNativeTrtcHardSignal: NATIVE_TRTC_CLASS_SIGNAL.test(prompt) || NATIVE_TRTC_METHOD_SIGNAL.test(prompt),
|
|
137
86
|
};
|
|
138
87
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return
|
|
142
|
-
primary: { ...params, product: products, frameworks },
|
|
143
|
-
meta: { route_reason: reason, environment_downgraded: env },
|
|
144
|
-
};
|
|
88
|
+
function intersect(base, target) {
|
|
89
|
+
const set = new Set(target);
|
|
90
|
+
return base.filter((item) => set.has(item));
|
|
145
91
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
92
|
+
function sameStringArray(a, b) {
|
|
93
|
+
const left = uniq(a ?? []);
|
|
94
|
+
const right = uniq(b);
|
|
95
|
+
if (left.length !== right.length)
|
|
96
|
+
return false;
|
|
97
|
+
const set = new Set(left);
|
|
98
|
+
return right.every((item) => set.has(item));
|
|
99
|
+
}
|
|
100
|
+
function reconcileFrameworkAgentFirst(agentFrameworks, signals) {
|
|
101
|
+
// agent 保留原始多框架,不执行 applyFrameworkLock(避免丢失 miniprogram/android 等)
|
|
102
|
+
const agent = uniq(agentFrameworks ?? []);
|
|
103
|
+
// prompt 保留完整采集信号,不提前强锁(强锁只在最终输出时按需执行)
|
|
104
|
+
const prompt = uniq(signals.promptFrameworks);
|
|
105
|
+
// WebRTC 硬信号:强制剔除 host-env(android/ios),保留真实 framework 或补 web。
|
|
106
|
+
if (signals.hasWebRtcHardSignal) {
|
|
107
|
+
const strongFw = agent.filter((f) => !['android', 'ios'].includes(f));
|
|
108
|
+
if (strongFw.length > 0) {
|
|
109
|
+
const same = sameStringArray(agentFrameworks, strongFw);
|
|
110
|
+
return { values: strongFw, changed: !same, reason: same ? 'framework_keep_agent' : 'framework_narrowed_by_prompt' };
|
|
111
|
+
}
|
|
112
|
+
const already = agent.includes('web');
|
|
113
|
+
return { values: ['web'], changed: !already, reason: already ? 'framework_keep_agent' : 'framework_corrected_by_hard_signal' };
|
|
157
114
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const hasWeb = frameworks.includes('web');
|
|
169
|
-
const natives = frameworks.filter((f) => f !== 'web');
|
|
170
|
-
// TRTC + 多平台(web + native)→ product 数组,search core filterPool 原生接受多 product 交集
|
|
171
|
-
if (hasWeb && natives.length > 0) {
|
|
172
|
-
const reason = fwRecon.corrected ? 'trtc_multi_platform_prompt_override' : 'trtc_multi_platform';
|
|
173
|
-
return trcMultiProductPlan(params, ['rtcengine', 'native_trtc_sdk'], frameworks, reason, envDowngraded);
|
|
115
|
+
if (signals.hasStrongFrameworkLock && prompt.length > 0) {
|
|
116
|
+
const promptSet = new Set(prompt);
|
|
117
|
+
// 强框架命中时,先剔除 agent 里的纯 host-env(ios/android 不应作为独立 framework 保留)
|
|
118
|
+
const HOST_ENV = new Set(['android', 'ios']);
|
|
119
|
+
const agentNonHost = agent.filter((f) => !HOST_ENV.has(f));
|
|
120
|
+
const base = agentNonHost.length > 0 ? agentNonHost : agent;
|
|
121
|
+
// prompt 是 base 子集 → base 更丰富,保留 base,不收窄
|
|
122
|
+
if (prompt.every((f) => new Set(base).has(f))) {
|
|
123
|
+
const same = sameStringArray(agentFrameworks, base);
|
|
124
|
+
return { values: base, changed: !same, reason: same ? 'framework_keep_agent' : 'framework_narrowed_by_prompt' };
|
|
174
125
|
}
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
126
|
+
// prompt 与 base 有交集 → 取交集,有限收窄
|
|
127
|
+
const intersection = base.filter((f) => promptSet.has(f));
|
|
128
|
+
if (intersection.length > 0) {
|
|
129
|
+
const same = sameStringArray(agentFrameworks, intersection);
|
|
130
|
+
return { values: intersection, changed: !same, reason: same ? 'framework_keep_agent' : 'framework_narrowed_by_prompt' };
|
|
179
131
|
}
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
132
|
+
// 无交集(真冲突):
|
|
133
|
+
// - agentNonHost 含真实框架(非 host-env、非泛平台词 web)→ agent 推断可信,透传
|
|
134
|
+
// - agentNonHost 为空或只含泛平台词(web/android/ios)→ prompt 强框架更具体,执行纠偏
|
|
135
|
+
const GENERIC_PLATFORM = new Set(['android', 'ios', 'web']);
|
|
136
|
+
const agentSpecific = agentNonHost.filter((f) => !GENERIC_PLATFORM.has(f));
|
|
137
|
+
if (agentSpecific.length > 0) {
|
|
138
|
+
return { values: base, changed: false, reason: 'framework_keep_agent' };
|
|
139
|
+
}
|
|
140
|
+
return { values: applyFrameworkLock(prompt), changed: true, reason: 'framework_corrected_by_hard_signal' };
|
|
141
|
+
}
|
|
142
|
+
if (agent.length > 0 && prompt.length > 0) {
|
|
143
|
+
const narrowed = intersect(agent, prompt);
|
|
144
|
+
if (narrowed.length > 0 && narrowed.length < agent.length) {
|
|
145
|
+
return { values: narrowed, changed: true, reason: 'framework_narrowed_by_prompt' };
|
|
184
146
|
}
|
|
185
|
-
|
|
186
|
-
return passthroughPlan(params, 'trtc_no_platform_resolved', false);
|
|
147
|
+
return { values: agent, changed: false, reason: 'framework_keep_agent' };
|
|
187
148
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
149
|
+
if (agent.length > 0) {
|
|
150
|
+
return { values: agent, changed: false, reason: 'framework_keep_agent' };
|
|
151
|
+
}
|
|
152
|
+
if (prompt.length > 0) {
|
|
153
|
+
return { values: applyFrameworkLock(prompt), changed: true, reason: 'framework_adopt_prompt' };
|
|
154
|
+
}
|
|
155
|
+
return { values: [], changed: false, reason: 'framework_no_signal' };
|
|
156
|
+
}
|
|
157
|
+
function reconcileProductAgentFirst(agentProducts, signals) {
|
|
158
|
+
const agent = uniq(agentProducts ?? []);
|
|
159
|
+
// native TRTC 纠偏:agent 传 rtcengine 但 prompt 明确出现 native 类/方法时,收窄为 native_trtc_sdk。
|
|
160
|
+
// 前提:无 WebRTC 硬信号(避免 web+native 混场景误纠)。
|
|
161
|
+
if (signals.hasNativeTrtcHardSignal && !signals.hasWebRtcHardSignal) {
|
|
162
|
+
if (agent.includes('rtcengine') && !agent.includes('native_trtc_sdk')) {
|
|
163
|
+
return { values: ['native_trtc_sdk'], changed: true, reason: 'product_corrected_by_native_trtc_hard_signal' };
|
|
195
164
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
165
|
+
return { values: agent, changed: false, reason: agent.length > 0 ? 'product_keep_agent' : 'product_no_signal' };
|
|
166
|
+
}
|
|
167
|
+
if (!signals.hasWebRtcHardSignal) {
|
|
168
|
+
return { values: agent, changed: false, reason: agent.length > 0 ? 'product_keep_agent' : 'product_no_signal' };
|
|
169
|
+
}
|
|
170
|
+
const targetProduct = 'rtcengine';
|
|
171
|
+
// agent 未提供产品时:若命中 Web-only TRTC API 级强信号(如 setCurrentSpeaker),
|
|
172
|
+
// 该信号本身足以确定产品为 rtcengine;否则(仅泛协议词如 "webrtc")不替 agent 猜测,透传空。
|
|
173
|
+
if (agent.length === 0) {
|
|
174
|
+
if (signals.hasWebTrtcApiSignal) {
|
|
175
|
+
return { values: [targetProduct], changed: true, reason: 'product_corrected_by_webrtc_hard_signal' };
|
|
200
176
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
177
|
+
return { values: [], changed: false, reason: 'product_no_signal' };
|
|
178
|
+
}
|
|
179
|
+
// agent 传的不是 TRTC 家族产品时,WebRTC 信号不足以覆盖,保持 agent 结果。
|
|
180
|
+
if (!agent.some((p) => TRTC_FAMILY.has(p))) {
|
|
181
|
+
return { values: agent, changed: false, reason: 'product_keep_agent' };
|
|
182
|
+
}
|
|
183
|
+
if (agent.includes(targetProduct)) {
|
|
184
|
+
if (agent.length === 1) {
|
|
185
|
+
return { values: agent, changed: false, reason: 'product_keep_agent' };
|
|
204
186
|
}
|
|
205
|
-
return
|
|
187
|
+
return { values: [targetProduct], changed: true, reason: 'product_narrowed_by_prompt' };
|
|
206
188
|
}
|
|
207
|
-
|
|
208
|
-
|
|
189
|
+
return { values: [targetProduct], changed: true, reason: 'product_corrected_by_webrtc_hard_signal' };
|
|
190
|
+
}
|
|
191
|
+
function buildRouteReason(frameworkDecision, productDecision) {
|
|
192
|
+
const reasons = [frameworkDecision.reason, productDecision.reason]
|
|
193
|
+
.filter((r) => r !== 'framework_no_signal' && r !== 'product_no_signal' && r !== 'framework_keep_agent' && r !== 'product_keep_agent');
|
|
194
|
+
if (reasons.length === 0)
|
|
195
|
+
return 'agent_passthrough';
|
|
196
|
+
return reasons.join('+');
|
|
197
|
+
}
|
|
198
|
+
function passthroughPlan(params, reason, env) {
|
|
199
|
+
return { primary: params, meta: { route_reason: reason, environment_downgraded: env } };
|
|
200
|
+
}
|
|
201
|
+
export function preflightRoute(params) {
|
|
202
|
+
const prompt = params.prompt ?? '';
|
|
203
|
+
const signals = collectHardSignals(prompt);
|
|
204
|
+
const frameworkDecision = reconcileFrameworkAgentFirst(params.frameworks, signals);
|
|
205
|
+
const finalFrameworks = frameworkDecision.values;
|
|
206
|
+
const productDecision = reconcileProductAgentFirst(params.product, signals);
|
|
207
|
+
const finalProducts = productDecision.values;
|
|
208
|
+
const reason = buildRouteReason(frameworkDecision, productDecision);
|
|
209
|
+
const envDowngraded = signals.hostEnv != null && finalFrameworks.length > 0 && !finalFrameworks.includes(signals.hostEnv);
|
|
210
|
+
if (!frameworkDecision.changed
|
|
211
|
+
&& !productDecision.changed
|
|
212
|
+
&& sameStringArray(params.product, finalProducts)
|
|
213
|
+
&& sameStringArray(params.frameworks, finalFrameworks)) {
|
|
214
|
+
return passthroughPlan(params, reason, envDowngraded);
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
primary: {
|
|
218
|
+
...params,
|
|
219
|
+
product: finalProducts,
|
|
220
|
+
frameworks: finalFrameworks,
|
|
221
|
+
},
|
|
222
|
+
meta: {
|
|
223
|
+
route_reason: reason,
|
|
224
|
+
environment_downgraded: envDowngraded,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
209
227
|
}
|