@ray-js/t-agent-plugin-aistream 0.2.0-beta-1

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.
Files changed (46) hide show
  1. package/LICENSE.md +21 -0
  2. package/README-zh_CN.md +12 -0
  3. package/README.md +12 -0
  4. package/dist/AIStreamTypes.d.ts +1413 -0
  5. package/dist/AIStreamTypes.js +216 -0
  6. package/dist/ChatHistoryStore.d.ts +66 -0
  7. package/dist/ChatHistoryStore.js +160 -0
  8. package/dist/global.d.ts +4 -0
  9. package/dist/global.js +4 -0
  10. package/dist/index.d.ts +4 -0
  11. package/dist/index.js +4 -0
  12. package/dist/polyfill.d.ts +1 -0
  13. package/dist/polyfill.js +8 -0
  14. package/dist/utils/AIStream.d.ts +137 -0
  15. package/dist/utils/AIStream.js +486 -0
  16. package/dist/utils/abort.d.ts +38 -0
  17. package/dist/utils/abort.js +177 -0
  18. package/dist/utils/actions.d.ts +48 -0
  19. package/dist/utils/actions.js +76 -0
  20. package/dist/utils/apis.d.ts +15 -0
  21. package/dist/utils/apis.js +10 -0
  22. package/dist/utils/defaultMock.d.ts +1 -0
  23. package/dist/utils/defaultMock.js +429 -0
  24. package/dist/utils/index.d.ts +11 -0
  25. package/dist/utils/index.js +11 -0
  26. package/dist/utils/logger.d.ts +2 -0
  27. package/dist/utils/logger.js +3 -0
  28. package/dist/utils/mock.d.ts +48 -0
  29. package/dist/utils/mock.js +72 -0
  30. package/dist/utils/observer.d.ts +61 -0
  31. package/dist/utils/observer.js +152 -0
  32. package/dist/utils/parsers.d.ts +10 -0
  33. package/dist/utils/parsers.js +13 -0
  34. package/dist/utils/promisify.d.ts +18 -0
  35. package/dist/utils/promisify.js +82 -0
  36. package/dist/utils/sendMessage.d.ts +21 -0
  37. package/dist/utils/sendMessage.js +241 -0
  38. package/dist/utils/ttt.d.ts +99 -0
  39. package/dist/utils/ttt.js +97 -0
  40. package/dist/utils/url.d.ts +11 -0
  41. package/dist/utils/url.js +31 -0
  42. package/dist/utils/version.d.ts +1 -0
  43. package/dist/utils/version.js +63 -0
  44. package/dist/withAIStream.d.ts +64 -0
  45. package/dist/withAIStream.js +420 -0
  46. package/package.json +39 -0
@@ -0,0 +1,420 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
3
+ const _excluded = ["message"];
4
+ import "core-js/modules/es.array.flat.js";
5
+ import "core-js/modules/es.array.unscopables.flat.js";
6
+ import "core-js/modules/esnext.iterator.constructor.js";
7
+ import "core-js/modules/esnext.iterator.for-each.js";
8
+ import "core-js/modules/esnext.iterator.map.js";
9
+ import "core-js/modules/web.dom-collections.iterator.js";
10
+ import { BubbleTileStatus, ChatMessageStatus, createHooks } from '@ray-js/t-agent';
11
+ import { getAccountInfo } from './utils/ttt';
12
+ import { tAgentMessageAppraise } from './utils/apis';
13
+ import { AIStreamObserver, getCurrentHomeInfo, sendBlocksToAIStream } from './utils';
14
+ import { ConnectClientType, ConnectState } from './AIStreamTypes';
15
+ import { ChatHistoryLocalStore, ChatHistoryStore } from './ChatHistoryStore';
16
+ import { DEFAULT_TOKEN_API, DEFAULT_TOKEN_API_VERSION, globalAIStreamClient } from './global';
17
+ import { runTTTAction } from '@ray-js/t-agent-plugin-assistant';
18
+ import logger from './utils/logger';
19
+ export function withAIStream() {
20
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
21
+ const hooks = createHooks();
22
+ return agent => {
23
+ const {
24
+ onAgentStart,
25
+ onChatStart,
26
+ onChatResume,
27
+ onInputBlocksPush,
28
+ onMessagePersist,
29
+ onMessageChange,
30
+ session,
31
+ createMessage,
32
+ onAgentDispose,
33
+ onTileEvent
34
+ } = agent;
35
+ const ui = agent.plugins.ui;
36
+ onAgentStart(async () => {
37
+ /**
38
+ * 1. 管理配置数据
39
+ */
40
+ const {
41
+ deviceId,
42
+ agentId,
43
+ tokenApi,
44
+ tokenApiVersion
45
+ } = options;
46
+ let homeId = options.homeId;
47
+ if (!homeId) {
48
+ const info = await getCurrentHomeInfo();
49
+ homeId = +info.homeId;
50
+ }
51
+ const indexId = options.indexId || 'default';
52
+ await agent.session.set('AIStream.indexId', indexId);
53
+ await agent.session.set('AIStream.agentId', agentId);
54
+ await agent.session.set('AIStream.deviceId', deviceId);
55
+
56
+ /*
57
+ * 2. 获取历史消息
58
+ */
59
+ let historyStore = null;
60
+ if (typeof options.createChatHistoryStore === 'function') {
61
+ historyStore = options.createChatHistoryStore(agent);
62
+ if (!(historyStore instanceof ChatHistoryStore)) {
63
+ throw new Error('createChatHistoryStore must return an instance of ChatHistoryStore');
64
+ }
65
+ } else {
66
+ historyStore = new ChatHistoryLocalStore({
67
+ agentId,
68
+ deviceId,
69
+ homeId,
70
+ indexId
71
+ });
72
+ }
73
+ await agent.session.set('AIStream.historyStore', historyStore);
74
+ const {
75
+ records: historyMsgList
76
+ } = await historyStore.queryBy({
77
+ solutionCode: options.agentId ? [options.agentId] : null,
78
+ devId: options.deviceId ? [options.deviceId] : null,
79
+ homeId: [homeId],
80
+ index: [indexId]
81
+ }, {
82
+ sort: 'asc',
83
+ pageSize: options.historySize || 1000,
84
+ pageNo: 0
85
+ });
86
+ await session.set('AIStream.rawMessages', historyMsgList);
87
+ session.isNewChat = historyMsgList.length === 0;
88
+ session.sessionId = indexId;
89
+
90
+ // 创建 streamSession
91
+ const connection = globalAIStreamClient.getConnection({
92
+ clientType: options.clientType || ConnectClientType.APP,
93
+ deviceId: options.deviceId
94
+ });
95
+ const streamSession = connection.createSession({
96
+ api: tokenApi || DEFAULT_TOKEN_API,
97
+ apiVersion: tokenApiVersion || DEFAULT_TOKEN_API_VERSION,
98
+ solutionCode: agentId
99
+ });
100
+ await session.set('AIStream.streamSession', streamSession);
101
+ const observer = new AIStreamObserver(data => {
102
+ if (data.type === 'amplitudes') {
103
+ ui.emitEvent('amplitudes', data);
104
+ } else if (data.type === 'connectionState') {
105
+ ui.emitEvent('networkChange', {
106
+ online: data.body.connectState === ConnectState.CONNECTED
107
+ });
108
+ }
109
+ }, globalAIStreamClient.pool);
110
+ observer.observe({
111
+ connectionState: true,
112
+ amplitudes: true
113
+ });
114
+ await session.set('AIStream.observer', observer);
115
+ });
116
+ onAgentDispose(async () => {
117
+ const observer = session.get('AIStream.observer');
118
+ if (observer) {
119
+ observer.disconnect();
120
+ }
121
+ const streamSession = session.get('AIStream.streamSession');
122
+
123
+ /**
124
+ * 关闭session
125
+ */
126
+ await streamSession.close();
127
+ });
128
+ const persist = async message => {
129
+ console.log('persist', message);
130
+ // 对消息进行持久化,已存在的数据进行更新
131
+ const historyStore = await session.get('AIStream.historyStore');
132
+ const storeId = message.meta.id;
133
+ const messageObj = message.toObject();
134
+ if (storeId) {
135
+ await historyStore.update(storeId, messageObj);
136
+ if (message.isShow) {
137
+ await message.update();
138
+ }
139
+ } else {
140
+ delete messageObj.id;
141
+ const insertRes = await historyStore.insert(messageObj);
142
+ if (insertRes && insertRes.id) {
143
+ // 把 id 设置到 meta上,标识已经持久化
144
+ message.setMeta({
145
+ id: insertRes.id
146
+ });
147
+ }
148
+ }
149
+ };
150
+ onMessagePersist((payload, message) => persist(message));
151
+ onChatResume(async result => {
152
+ /** 格式化历史消息 */
153
+ const list = await session.get('AIStream.rawMessages');
154
+ if (list && list.length > 0) {
155
+ const messages = await Promise.all(list.map(async item => {
156
+ const {
157
+ message
158
+ } = item,
159
+ messageMeta = _objectWithoutProperties(item, _excluded);
160
+ const messageObj = createMessage(message);
161
+ messageObj.setMeta(messageMeta);
162
+ const messages = [];
163
+ if (messageObj.tiles.length) {
164
+ messages.push(messageObj);
165
+ }
166
+ const result = {
167
+ messages
168
+ };
169
+ await hooks.callHook('onMessageParse', item, result);
170
+ return result.messages;
171
+ }));
172
+ result.messages = messages.flat();
173
+ }
174
+ }, 'before');
175
+ const removeMessage = async message => {
176
+ const storeId = message.meta.id;
177
+ if (!storeId) {
178
+ return;
179
+ }
180
+ const historyStore = await session.get('AIStream.historyStore');
181
+ await historyStore.remove(storeId);
182
+ };
183
+ onMessageChange(async (type, message) => {
184
+ if (type === 'remove') {
185
+ await removeMessage(message);
186
+ }
187
+ });
188
+ const composeHandler = {
189
+ attachmentCompose: async () => {
190
+ // if (part.attachmentType === 'extensions') {
191
+ // await hooks.callHook('onExtensionCompose', part.attachment, respMsg, result, 'stream');
192
+ // }
193
+ return {
194
+ messages: []
195
+ }.messages;
196
+ }
197
+ };
198
+ const send = (blocks, signal, extraOptions) => {
199
+ const streamSession = session.get('AIStream.streamSession');
200
+ const result = sendBlocksToAIStream({
201
+ blocks,
202
+ session: streamSession,
203
+ attribute: _objectSpread({}, extraOptions)
204
+ });
205
+ signal === null || signal === void 0 || signal.addEventListener('abort', event => {
206
+ result === null || result === void 0 || result.response.cancel(event.reason);
207
+ agent.hooks.callHook('onUserAbort', event.reason);
208
+ });
209
+ signal === null || signal === void 0 || signal.throwIfAborted();
210
+ return result;
211
+ };
212
+ const chat = async (blocks, signal, options) => {
213
+ const {
214
+ sendBy = 'user',
215
+ responseBy = 'assistant',
216
+ extraOptions
217
+ } = options || {};
218
+ let audioEmitter = null;
219
+ for (const block of blocks) {
220
+ if (block.type === 'audio') {
221
+ if (audioEmitter) {
222
+ throw new Error('only one audio emitter is allowed');
223
+ }
224
+ audioEmitter = block.audio_emitter;
225
+ }
226
+ }
227
+
228
+ // 创建用户消息
229
+ const userMsg = createMessage({
230
+ role: sendBy,
231
+ status: ChatMessageStatus.START
232
+ });
233
+ let audioPromise = null;
234
+ if (audioEmitter) {
235
+ let end = false;
236
+ audioPromise = new Promise((resolve, reject) => {
237
+ // 当确认发送时,展示 loading
238
+ const onConfirm = async () => {
239
+ if (end) {
240
+ return;
241
+ }
242
+ userMsg.set({
243
+ status: ChatMessageStatus.START
244
+ });
245
+ userMsg.bubble.setStatus(BubbleTileStatus.NORMAL);
246
+ await userMsg.show();
247
+ audioEmitter.removeEventListener('confirm', onConfirm);
248
+ };
249
+ audioEmitter.addEventListener('confirm', onConfirm);
250
+
251
+ // 当音频识别完毕时,展示文本
252
+ const onFinished = async event => {
253
+ if (end) {
254
+ return;
255
+ }
256
+ if (!event.detail.text) {
257
+ await userMsg.remove();
258
+ reject(new Error('No text found in audio event'));
259
+ return;
260
+ }
261
+ userMsg.bubble.initWithInputBlocks(blocks);
262
+ userMsg.bubble.setText(event.detail.text);
263
+ userMsg.set({
264
+ status: ChatMessageStatus.FINISH
265
+ });
266
+ await userMsg.update();
267
+ await userMsg.persist();
268
+ end = true;
269
+ audioEmitter.removeEventListener('finish', onFinished);
270
+ resolve();
271
+ };
272
+ audioEmitter.addEventListener('finish', onFinished);
273
+ const onCancel = () => {
274
+ end = true;
275
+ audioEmitter.removeEventListener('cancel', onCancel);
276
+ reject(new Error('User cancel'));
277
+ };
278
+ audioEmitter.addEventListener('cancel', onCancel);
279
+ });
280
+ } else {
281
+ userMsg.set({
282
+ status: ChatMessageStatus.FINISH
283
+ });
284
+ userMsg.bubble.initWithInputBlocks(blocks);
285
+ await userMsg.show();
286
+ await userMsg.persist();
287
+ }
288
+ const {
289
+ response,
290
+ metaPromise
291
+ } = send(blocks, signal, extraOptions);
292
+ if (audioPromise) {
293
+ try {
294
+ await audioPromise;
295
+ } catch (e) {
296
+ // 没有识别到文字
297
+ return [];
298
+ }
299
+ }
300
+ metaPromise.then(meta => userMsg.setMeta(meta).update());
301
+ const message = createMessage({
302
+ role: responseBy
303
+ });
304
+ const messages = await agent.flushStreamToShow(message, response, composeHandler);
305
+ if (message.bubble.status === BubbleTileStatus.ABORTED) {
306
+ if (!message.bubble.text) {
307
+ await message.remove();
308
+ }
309
+ } else {
310
+ await message.persist();
311
+ }
312
+ return [userMsg, ...messages];
313
+ };
314
+ onInputBlocksPush(chat);
315
+ const handleTTTAction = async (tile, tttAction) => {
316
+ const result = {
317
+ action: tttAction
318
+ };
319
+ await hooks.callHook('onTTTAction', tile, result);
320
+ if (result.action) {
321
+ await runTTTAction(result.action, {
322
+ onSendSkills: async () => {
323
+ throw new Error('send skill not support');
324
+ },
325
+ onSendMessages: async (blocks, sendImmediately) => {
326
+ if (sendImmediately) {
327
+ ui.emitEvent('sendMessage', {
328
+ blocks
329
+ });
330
+ } else {
331
+ ui.emitEvent('setInputBlocks', {
332
+ blocks
333
+ });
334
+ }
335
+ },
336
+ onBuildIn(name, data) {
337
+ if (name.startsWith('ty.')) {
338
+ const fn = name.slice(3);
339
+ if (ty[fn]) {
340
+ ty[fn](data);
341
+ } else {
342
+ logger.error('handleTTTAction onBuildIn can not find TTT method:', name);
343
+ }
344
+ } else {
345
+ logger.error('handleTTTAction onBuildIn dont support:', name);
346
+ }
347
+ }
348
+ });
349
+ }
350
+ };
351
+ onTileEvent(async (tile, payload) => {
352
+ if (payload !== null && payload !== void 0 && payload.tttAction) {
353
+ await handleTTTAction(tile, payload.tttAction);
354
+ }
355
+ });
356
+ onTileEvent(async (tile, payload) => {
357
+ if (tile.type === 'card' && (payload === null || payload === void 0 ? void 0 : payload.type) === '@buildIn/setCardState') {
358
+ const {
359
+ state,
360
+ options
361
+ } = payload;
362
+ tile.data.card.cardState = state;
363
+ await tile.update();
364
+ if (options.persist) {
365
+ await tile.message.persist();
366
+ }
367
+ }
368
+ });
369
+ return {
370
+ hooks,
371
+ assistant: {
372
+ send,
373
+ chat,
374
+ composeHandler,
375
+ options,
376
+ removeMessage,
377
+ clearAllMessages: async () => {
378
+ // 删除内存中的消息
379
+ const messages = agent.session.messages.values();
380
+ Array.from(messages).forEach(message => {
381
+ message.remove();
382
+ });
383
+ if (options !== null && options !== void 0 && options.indexId) {
384
+ return;
385
+ }
386
+ // 清空缓存的数据
387
+ const historyStore = await session.get('AIStream.historyStore');
388
+ historyStore.removeBy({
389
+ index: [options.indexId]
390
+ });
391
+ },
392
+ feedback: async _ref => {
393
+ var _accountInfo$miniProg;
394
+ let {
395
+ requestId,
396
+ type
397
+ } = _ref;
398
+ const aiPtChannel = session.get('AIStream.aiPtChannel');
399
+ if (!aiPtChannel) {
400
+ logger.error('aiPtChannel is not found');
401
+ return null;
402
+ }
403
+ const accountInfo = await getAccountInfo();
404
+ const appId = accountInfo === null || accountInfo === void 0 || (_accountInfo$miniProg = accountInfo.miniProgram) === null || _accountInfo$miniProg === void 0 ? void 0 : _accountInfo$miniProg.appId;
405
+ if (!appId) {
406
+ logger.error('appId is not found');
407
+ return null;
408
+ }
409
+ return tAgentMessageAppraise({
410
+ aiPtChannel,
411
+ miniProgramId: appId,
412
+ requestId,
413
+ /** 评价(1-有帮助,2-无帮助) */
414
+ approveStatus: type === 'like' ? 1 : 2
415
+ });
416
+ }
417
+ }
418
+ };
419
+ };
420
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@ray-js/t-agent-plugin-aistream",
3
+ "version": "0.2.0-beta-1",
4
+ "author": "Tuya.inc",
5
+ "license": "MIT",
6
+ "private": false,
7
+ "main": "dist/index.js",
8
+ "typings": "dist/index.d.ts",
9
+ "maintainers": [
10
+ "tuya_npm",
11
+ "tuyafe"
12
+ ],
13
+ "files": [
14
+ "dist",
15
+ "README.md",
16
+ "README-zh_CN.md"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "scripts": {
22
+ "dev": "ray start --type=component --output dist --emit-declaration-dev",
23
+ "build": "ray build --type=component --output dist",
24
+ "clean": "rimraf ./dist"
25
+ },
26
+ "dependencies": {
27
+ "dayjs": "^1.10.4",
28
+ "url-parse": "^1.5.10",
29
+ "web-streams-polyfill": "^4.0.0"
30
+ },
31
+ "peerDependencies": {
32
+ "@ray-js/ray": ">=1.6.8",
33
+ "@ray-js/t-agent": "*"
34
+ },
35
+ "devDependencies": {
36
+ "@types/url-parse": "^1.4.11"
37
+ },
38
+ "gitHead": "98229f8a9cb090a6fa0d9b47fa70b1f44ec2b34a"
39
+ }