@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,72 @@
1
+ import "core-js/modules/es.array.flat.js";
2
+ import "core-js/modules/es.array.unscopables.flat.js";
3
+ import "core-js/modules/es.json.stringify.js";
4
+ import "core-js/modules/web.dom-collections.iterator.js";
5
+ import { createHooks, Emitter } from '@ray-js/t-agent';
6
+ const mock = {
7
+ loaded: false,
8
+ lastId: 1,
9
+ database: new Map(),
10
+ data: new Map(),
11
+ sleep: ms => new Promise(resolve => setTimeout(resolve, ms)),
12
+ generateInt: (min, max) => Math.floor(Math.random() * (max - min + 1)) + min,
13
+ emitter: new Emitter(),
14
+ hooks: createHooks(),
15
+ getRecords: () => {
16
+ var _rows;
17
+ if (mock.loaded) {
18
+ return Array.from(mock.database.values());
19
+ }
20
+ const ret = ty.getStorageSync({
21
+ key: 'AIStreamMockDatabase'
22
+ });
23
+ mock.loaded = true;
24
+ let rows = [];
25
+ if (typeof ret === 'string') {
26
+ rows = JSON.parse(ret);
27
+ } else if (typeof (ret === null || ret === void 0 ? void 0 : ret.data) === 'string') {
28
+ rows = JSON.parse(ret === null || ret === void 0 ? void 0 : ret.data);
29
+ }
30
+ if (Array.isArray((_rows = rows) === null || _rows === void 0 ? void 0 : _rows[0])) {
31
+ rows = rows.flat();
32
+ }
33
+ if (!rows) {
34
+ return [];
35
+ }
36
+ for (const row of rows) {
37
+ mock.database.set(row.id, row);
38
+ mock.lastId = Math.max(mock.lastId, row.id);
39
+ }
40
+ mock.lastId += 1;
41
+ return rows;
42
+ },
43
+ getRecord: id => {
44
+ mock.getRecords();
45
+ return mock.database.get(id);
46
+ },
47
+ setRecord: entry => {
48
+ mock.getRecords();
49
+ mock.database.set(entry.id, entry);
50
+ ty.setStorageSync({
51
+ key: 'AIStreamMockDatabase',
52
+ data: JSON.stringify(mock.getRecords())
53
+ });
54
+ },
55
+ deleteRecord: id => {
56
+ mock.getRecords();
57
+ mock.database.delete(id);
58
+ ty.setStorageSync({
59
+ key: 'AIStreamMockDatabase',
60
+ data: JSON.stringify(mock.getRecords())
61
+ });
62
+ },
63
+ clearRecords: () => {
64
+ mock.database.clear();
65
+ },
66
+ getId: () => {
67
+ const id = mock.lastId;
68
+ mock.lastId += 1;
69
+ return id;
70
+ }
71
+ };
72
+ export { mock };
@@ -0,0 +1,61 @@
1
+ import { AudioBody, ConnectStateBody, EventBody, FileBody, ImageBody, RecordAmplitudesBody, SessionStateBody, TextBody, VideoBody } from '../AIStreamTypes';
2
+ export type AIStreamDataEntry = {
3
+ type: 'text';
4
+ body: TextBody;
5
+ } | {
6
+ type: 'image';
7
+ body: ImageBody;
8
+ } | {
9
+ type: 'video';
10
+ body: VideoBody;
11
+ } | {
12
+ type: 'file';
13
+ body: FileBody;
14
+ } | {
15
+ type: 'audio';
16
+ body: AudioBody;
17
+ } | {
18
+ type: 'event';
19
+ body: EventBody;
20
+ } | {
21
+ type: 'connectionState';
22
+ body: ConnectStateBody;
23
+ } | {
24
+ type: 'sessionState';
25
+ body: SessionStateBody;
26
+ } | {
27
+ type: 'amplitudes';
28
+ body: RecordAmplitudesBody;
29
+ };
30
+ export interface AIStreamObserverOptions {
31
+ sessionState?: boolean;
32
+ connectionState?: boolean;
33
+ event?: boolean;
34
+ text?: boolean;
35
+ audio?: boolean;
36
+ video?: boolean;
37
+ file?: boolean;
38
+ image?: boolean;
39
+ dataChannels?: string[];
40
+ sessionId?: string;
41
+ amplitudes?: boolean;
42
+ }
43
+ export declare class AIStreamObserverPool {
44
+ isStarted: boolean;
45
+ private observerMap;
46
+ constructor();
47
+ private cancels;
48
+ start(): void;
49
+ dispose(): void;
50
+ connect(observer: AIStreamObserver): void;
51
+ disconnect(observer: AIStreamObserver): void;
52
+ }
53
+ export declare class AIStreamObserver {
54
+ readonly callback: (entry: AIStreamDataEntry, observer: AIStreamObserver) => void;
55
+ private readonly pool;
56
+ get options(): AIStreamObserverOptions;
57
+ private _options;
58
+ constructor(callback: (entry: AIStreamDataEntry, observer: AIStreamObserver) => void, pool: AIStreamObserverPool);
59
+ observe(options: AIStreamObserverOptions): void;
60
+ disconnect(): void;
61
+ }
@@ -0,0 +1,152 @@
1
+ import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
2
+ import "core-js/modules/esnext.iterator.constructor.js";
3
+ import "core-js/modules/esnext.iterator.for-each.js";
4
+ import "core-js/modules/web.dom-collections.iterator.js";
5
+ import { ConnectState, EventType, SessionState, StreamFlag } from '../AIStreamTypes';
6
+ import { listenAudioReceived, listenConnectStateChanged, listenEventReceived, listenImageReceived, listenRecordAmplitudes, listenSessionStateChanged, listenTextReceived } from './ttt';
7
+ import logger from './logger';
8
+ const types = ['connectionState', 'sessionState', 'event', 'text', 'audio', 'video', 'file', 'image', 'amplitudes'];
9
+ export class AIStreamObserverPool {
10
+ constructor() {
11
+ _defineProperty(this, "isStarted", false);
12
+ _defineProperty(this, "cancels", []);
13
+ this.observerMap = {
14
+ connectionState: new Set(),
15
+ sessionState: new Set(),
16
+ event: new Set(),
17
+ text: new Set(),
18
+ audio: new Set(),
19
+ video: new Set(),
20
+ file: new Set(),
21
+ image: new Set(),
22
+ amplitudes: new Set()
23
+ };
24
+ }
25
+ start() {
26
+ if (this.isStarted) {
27
+ return;
28
+ }
29
+ const handle = entry => {
30
+ const observers = this.observerMap[entry.type];
31
+ let state = '';
32
+ if (entry.type !== 'amplitudes') {
33
+ var _entry$body;
34
+ if (entry.type === 'connectionState') {
35
+ state = "".concat(ConnectState[entry.body.connectState], " ").concat(entry.body.code || '');
36
+ } else if (entry.type === 'sessionState') {
37
+ state = "".concat(SessionState[entry.body.sessionState], " ").concat(entry.body.code || '');
38
+ } else if (entry.type === 'event') {
39
+ state = "".concat(EventType[entry.body.eventType]);
40
+ } else if ((_entry$body = entry.body) !== null && _entry$body !== void 0 && _entry$body.streamFlag) {
41
+ state = "".concat(StreamFlag[entry.body.streamFlag]);
42
+ }
43
+ logger.debug("Pool received: %c".concat(observers.size ? '⊕' : '×').concat(entry.type, " ").concat(state), 'background: black; color: white', entry);
44
+ }
45
+ observers.forEach(observer => {
46
+ if (entry.type === 'text' || entry.type === 'audio' || entry.type === 'video' || entry.type === 'file' || entry.type === 'image') {
47
+ const {
48
+ dataChannels,
49
+ sessionId
50
+ } = observer.options;
51
+ let matched = true;
52
+ if (dataChannels !== null && dataChannels !== void 0 && dataChannels.length && !dataChannels.includes(entry.body.dataChannel)) {
53
+ matched = false;
54
+ }
55
+ if (sessionId && !entry.body.sessionIdList.includes(sessionId)) {
56
+ matched = false;
57
+ }
58
+ if (matched) {
59
+ observer.callback(entry, observer);
60
+ }
61
+ } else {
62
+ observer.callback(entry, observer);
63
+ }
64
+ });
65
+ };
66
+ this.cancels = [listenConnectStateChanged(body => handle({
67
+ type: 'connectionState',
68
+ body
69
+ })), listenSessionStateChanged(body => handle({
70
+ type: 'sessionState',
71
+ body
72
+ })), listenEventReceived(body => handle({
73
+ type: 'event',
74
+ body
75
+ })), listenTextReceived(body => handle({
76
+ type: 'text',
77
+ body
78
+ })), listenAudioReceived(body => handle({
79
+ type: 'audio',
80
+ body
81
+ })), listenImageReceived(body => handle({
82
+ type: 'image',
83
+ body
84
+ })), listenRecordAmplitudes(body => handle({
85
+ type: 'amplitudes',
86
+ body
87
+ }))
88
+ // listenVideoReceived(body => handle({ type: 'video', body })),
89
+ // listenFileReceived(body => handle({ type: 'file', body })),
90
+ ];
91
+ this.isStarted = true;
92
+ }
93
+ dispose() {
94
+ this.isStarted = false;
95
+ this.cancels.forEach(cancel => cancel());
96
+ this.cancels = [];
97
+ this.observerMap = {
98
+ connectionState: new Set(),
99
+ sessionState: new Set(),
100
+ event: new Set(),
101
+ text: new Set(),
102
+ audio: new Set(),
103
+ video: new Set(),
104
+ file: new Set(),
105
+ image: new Set(),
106
+ amplitudes: new Set()
107
+ };
108
+ }
109
+ connect(observer) {
110
+ if (!this.isStarted) {
111
+ this.start();
112
+ }
113
+ types.forEach(key => {
114
+ const type = key;
115
+ if (observer.options[type]) {
116
+ this.observerMap[type].add(observer);
117
+ }
118
+ });
119
+ }
120
+ disconnect(observer) {
121
+ types.forEach(key => {
122
+ this.observerMap[key].delete(observer);
123
+ });
124
+ let empty = true;
125
+ types.forEach(key => {
126
+ if (this.observerMap[key].size > 0) {
127
+ empty = false;
128
+ }
129
+ });
130
+ if (empty) {
131
+ this.dispose();
132
+ }
133
+ }
134
+ }
135
+ export class AIStreamObserver {
136
+ get options() {
137
+ return this._options;
138
+ }
139
+ constructor(callback, pool) {
140
+ _defineProperty(this, "_options", {});
141
+ this.callback = callback;
142
+ this.pool = pool;
143
+ }
144
+ observe(options) {
145
+ this.pool.disconnect(this);
146
+ this._options = options;
147
+ this.pool.connect(this);
148
+ }
149
+ disconnect() {
150
+ this.pool.disconnect(this);
151
+ }
152
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 数字转化为整数
3
+ * @param v 带转化的数字
4
+ * @param defaultValue 兜底值,默认为0
5
+ * @returns number 转化后的整数
6
+ * @example
7
+ * parseInt(1.1) // 1
8
+ * parseInt(1.9) // 1
9
+ */
10
+ export declare function safeParseInt(v: number | string, defaultValue?: number): number;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 数字转化为整数
3
+ * @param v 带转化的数字
4
+ * @param defaultValue 兜底值,默认为0
5
+ * @returns number 转化后的整数
6
+ * @example
7
+ * parseInt(1.1) // 1
8
+ * parseInt(1.9) // 1
9
+ */
10
+ export function safeParseInt(v) {
11
+ let defaultValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
12
+ return v ? Number.parseInt(String(v), 10) || defaultValue : defaultValue;
13
+ }
@@ -0,0 +1,18 @@
1
+ interface AsyncTTTFnParams<P> {
2
+ complete?: () => void;
3
+ success?: (params: P) => void;
4
+ fail?: (params: {
5
+ errorMsg: string;
6
+ errorCode: string | number;
7
+ innerError: {
8
+ errorCode: string | number;
9
+ errorMsg: string;
10
+ };
11
+ }) => void;
12
+ [key: string]: any;
13
+ }
14
+ export declare const getEnableMock: () => boolean;
15
+ export declare const setEnableMock: (enable: boolean) => boolean;
16
+ export declare function promisify<T extends AsyncTTTFnParams<any>>(fn: (options: any) => void, enableMock?: boolean): (options?: Omit<T, 'success' | 'fail'>) => Promise<Parameters<NonNullable<T["success"]>>[0]>;
17
+ export declare function listening<T>(on: (listener: (params: T) => void) => void, off: (listener: (params: T) => void) => void, enableMock?: boolean): (listener: (params: T) => void) => () => void;
18
+ export {};
@@ -0,0 +1,82 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import { isDevTools } from './ttt';
3
+ import logger from './logger';
4
+ import { mock } from './mock';
5
+ let callId = 100000;
6
+ export const getEnableMock = () => {
7
+ try {
8
+ const result = ty.getStorageSync({
9
+ key: 'AIAssistantEnableMock'
10
+ });
11
+ // @ts-ignore
12
+ if (result && (result === 'true' || result.data === 'true')) {
13
+ return true;
14
+ }
15
+ } catch (e) {
16
+ console.error('获取AIAssistantEnableMock配置失败', e);
17
+ }
18
+ return isDevTools();
19
+ };
20
+ export const setEnableMock = enable => {
21
+ try {
22
+ ty.setStorageSync({
23
+ key: 'AIAssistantEnableMock',
24
+ data: String(enable)
25
+ });
26
+ return true;
27
+ } catch (e) {
28
+ console.error('设置AIAssistantEnableMock配置失败', e);
29
+ return false;
30
+ }
31
+ };
32
+ export function promisify(fn) {
33
+ let enableMock = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
34
+ return options => {
35
+ return new Promise((_resolve, _reject) => {
36
+ if (!fn) {
37
+ _reject(new Error('fn is not a function'));
38
+ }
39
+ const id = callId++;
40
+ logger.debug("TTT call #".concat(id, " %c").concat(fn.name), 'background: blue; color: white', options);
41
+ const resolve = result => {
42
+ logger.debug("TTT resp #".concat(id, " %c").concat(fn.name), 'background: green; color: white', result);
43
+ _resolve(result);
44
+ };
45
+ const reject = error => {
46
+ logger.debug("TTT error #".concat(id, " %c").concat(fn.name), 'background: red; color: white', error);
47
+ _reject(error);
48
+ };
49
+ const context = {
50
+ options,
51
+ result: undefined
52
+ };
53
+ if (getEnableMock() && enableMock) {
54
+ mock.hooks.callHook(fn.name, context).then(() => resolve(context.result), reject);
55
+ } else {
56
+ fn.call(ty, _objectSpread(_objectSpread({}, options), {}, {
57
+ success: resolve,
58
+ fail: reject
59
+ }));
60
+ }
61
+ });
62
+ };
63
+ }
64
+ export function listening(on, off) {
65
+ let enableMock = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
66
+ return listener => {
67
+ if (getEnableMock() && enableMock) {
68
+ const cb = event => {
69
+ logger.debug('listening on event', on.name, event.detail);
70
+ listener(event.detail);
71
+ };
72
+ mock.emitter.addEventListener(on.name, cb);
73
+ return () => {
74
+ mock.emitter.removeEventListener(on.name, cb);
75
+ };
76
+ }
77
+ on(listener);
78
+ return () => {
79
+ off(listener);
80
+ };
81
+ };
82
+ }
@@ -0,0 +1,21 @@
1
+ import '../polyfill';
2
+ import { AIStreamSession } from './AIStream';
3
+ import { AIStreamChatAttribute } from '../AIStreamTypes';
4
+ import { InputBlock, StreamResponse } from '@ray-js/t-agent';
5
+ export interface SendBlocksToAIStreamParams {
6
+ blocks: InputBlock[];
7
+ session: AIStreamSession;
8
+ attribute?: AIStreamChatAttribute;
9
+ }
10
+ export declare class AIStreamSessionError extends Error {
11
+ readonly code: number;
12
+ constructor(message: string, code: number);
13
+ }
14
+ export declare class AIStreamConnectionError extends Error {
15
+ readonly code: number;
16
+ constructor(message: string, code: number);
17
+ }
18
+ export declare function sendBlocksToAIStream(params: SendBlocksToAIStreamParams): {
19
+ response: StreamResponse;
20
+ metaPromise: Promise<Record<string, any>>;
21
+ };
@@ -0,0 +1,241 @@
1
+ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
+ import "core-js/modules/es.json.stringify.js";
3
+ import "core-js/modules/web.dom-collections.iterator.js";
4
+ import '../polyfill';
5
+ import { ReadableStream } from 'web-streams-polyfill';
6
+ import { AIStreamAttributePayloadType, AIStreamAttributeType, AIStreamChatSysWorkflow, ConnectState, FileFormat, ReceivedTextPacketEof, ReceivedTextPacketType, SessionState, StreamFlag } from '../AIStreamTypes';
7
+ import { EmitterEvent, generateId, safeParseJSON, StreamResponse } from '@ray-js/t-agent';
8
+ const mimeTypeToFormatMap = {
9
+ 'video/mp4': FileFormat.MP4,
10
+ 'text/json': FileFormat.JSON,
11
+ 'application/json': FileFormat.JSON,
12
+ 'application/pdf': FileFormat.PDF
13
+ };
14
+ export class AIStreamSessionError extends Error {
15
+ constructor(message, code) {
16
+ super(message);
17
+ this.name = 'AIStreamSessionError';
18
+ this.code = code;
19
+ }
20
+ }
21
+ export class AIStreamConnectionError extends Error {
22
+ constructor(message, code) {
23
+ super(message);
24
+ this.name = 'AIStreamConnectionError';
25
+ this.code = code;
26
+ }
27
+ }
28
+ export function sendBlocksToAIStream(params) {
29
+ const {
30
+ session,
31
+ blocks
32
+ } = params;
33
+ let audioEmitter = null;
34
+ let amplitudeCount = 0;
35
+ for (const block of blocks) {
36
+ if (block.type === 'audio') {
37
+ if (audioEmitter) {
38
+ throw new Error('only one audio emitter is allowed');
39
+ }
40
+ audioEmitter = block.audio_emitter;
41
+ amplitudeCount = block.amplitude_count || 0;
42
+ }
43
+ }
44
+ const attribute = _objectSpread({
45
+ 'processing.interrupt': 'false',
46
+ 'asr.enableVad': 'false',
47
+ 'sys.workflow': audioEmitter ? AIStreamChatSysWorkflow.ASR_LLM : AIStreamChatSysWorkflow.LLM
48
+ }, params.attribute);
49
+ let canceled = false;
50
+ let event = null;
51
+ let metaResolve;
52
+ const metaPromise = new Promise(resolve => {
53
+ metaResolve = resolve;
54
+ });
55
+ const stream = new ReadableStream({
56
+ async start(controller) {
57
+ const enqueue = part => {
58
+ if (canceled) {
59
+ return;
60
+ }
61
+ controller.enqueue(part);
62
+ };
63
+ event = await session.startEvent({
64
+ userData: [{
65
+ type: AIStreamAttributeType.AI_CHAT,
66
+ payloadType: AIStreamAttributePayloadType.STRING,
67
+ value: JSON.stringify(attribute)
68
+ }]
69
+ });
70
+ const meta = {
71
+ sessionId: event.sessionId,
72
+ eventId: event.eventId
73
+ };
74
+ metaResolve(meta);
75
+ let prevText = '';
76
+ let imageId = null;
77
+ let audioId = null;
78
+ event.on('data', data => {
79
+ if (data.type === 'text') {
80
+ if (!data.body.text) {
81
+ return;
82
+ }
83
+ const packet = safeParseJSON(data.body.text);
84
+ if (packet.bizType === ReceivedTextPacketType.NLG) {
85
+ const text = prevText + packet.data.content;
86
+ enqueue({
87
+ type: 'text',
88
+ delta: packet.data.content,
89
+ text,
90
+ meta
91
+ });
92
+ prevText = text;
93
+ } else if (packet.bizType === ReceivedTextPacketType.SKILL) {
94
+ enqueue({
95
+ id: generateId(),
96
+ type: 'attachment',
97
+ attachmentType: 'skill',
98
+ attachment: packet.data,
99
+ meta
100
+ });
101
+ } else if (packet.bizType === ReceivedTextPacketType.ASR && audioEmitter) {
102
+ if (packet.eof === ReceivedTextPacketEof.END) {
103
+ audioEmitter.dispatchEvent(new EmitterEvent('finish', {
104
+ detail: {
105
+ text: packet.data.text
106
+ }
107
+ }));
108
+ } else {
109
+ audioEmitter.dispatchEvent(new EmitterEvent('update', {
110
+ detail: {
111
+ text: packet.data.text
112
+ }
113
+ }));
114
+ }
115
+ }
116
+ } else if (data.type === 'image') {
117
+ if (!imageId) {
118
+ imageId = generateId();
119
+ }
120
+ enqueue({
121
+ id: imageId,
122
+ type: 'attachment',
123
+ attachmentType: 'image',
124
+ attachment: {
125
+ path: data.body.path || null,
126
+ width: data.body.width || null,
127
+ height: data.body.height || null
128
+ },
129
+ meta
130
+ });
131
+ if (data.body.streamFlag === StreamFlag.END) {
132
+ imageId = null;
133
+ }
134
+ } else if (data.type === 'audio') {
135
+ if (!audioId) {
136
+ audioId = generateId();
137
+ }
138
+ enqueue({
139
+ id: audioId,
140
+ type: 'attachment',
141
+ attachmentType: 'audio',
142
+ attachment: {
143
+ path: data.body.path || null
144
+ },
145
+ meta
146
+ });
147
+ if (data.body.streamFlag === StreamFlag.END) {
148
+ audioId = null;
149
+ }
150
+ } else if (data.type === 'sessionState') {
151
+ if (data.body.sessionState === SessionState.CLOSED) {
152
+ enqueue({
153
+ type: 'error',
154
+ error: new AIStreamSessionError('Session closed', data.body.code),
155
+ level: 'error',
156
+ meta
157
+ });
158
+ }
159
+ } else if (data.type === 'connectionState') {
160
+ if (data.body.connectState === ConnectState.DISCONNECTED) {
161
+ enqueue({
162
+ type: 'error',
163
+ error: new AIStreamConnectionError('Connection disconnected', data.body.code),
164
+ level: 'error',
165
+ meta
166
+ });
167
+ }
168
+ }
169
+ });
170
+ event.on('finish', () => {
171
+ if (!canceled) {
172
+ // 当取消后,不需要关闭控制器
173
+ controller.close();
174
+ }
175
+ });
176
+ event.on('error', error => {
177
+ enqueue({
178
+ type: 'error',
179
+ level: 'error',
180
+ error: error instanceof Error ? error : new Error('Unknown error'),
181
+ meta: {}
182
+ });
183
+ });
184
+ for (const block of blocks) {
185
+ if (block.type === 'text') {
186
+ event.write({
187
+ type: 'text',
188
+ text: JSON.stringify({
189
+ type: 'text',
190
+ message: block.text
191
+ })
192
+ });
193
+ } else if (block.type === 'image_path') {
194
+ event.write({
195
+ type: 'image',
196
+ path: block.image_path.path
197
+ });
198
+ } else if (block.type === 'file_path') {
199
+ event.write({
200
+ type: 'file',
201
+ format: mimeTypeToFormatMap[block.file_url.mimeType] || 0,
202
+ path: block.file_path.path
203
+ });
204
+ } else if (block.type === 'video_path') {
205
+ event.write({
206
+ type: 'file',
207
+ format: FileFormat.MP4,
208
+ path: block.file_path.path
209
+ });
210
+ }
211
+ }
212
+ if (audioEmitter) {
213
+ await new Promise(resolve => {
214
+ const s = event.stream({
215
+ type: 'audio',
216
+ amplitudeCount
217
+ });
218
+ audioEmitter.addEventListener('confirm', () => {
219
+ s.stop().then(resolve);
220
+ });
221
+ audioEmitter.addEventListener('cancel', async () => {
222
+ await s.stop();
223
+ await event.abort();
224
+ resolve();
225
+ });
226
+ s.start();
227
+ });
228
+ }
229
+ await event.end();
230
+ },
231
+ async cancel() {
232
+ var _event;
233
+ canceled = true;
234
+ await ((_event = event) === null || _event === void 0 ? void 0 : _event.abort());
235
+ }
236
+ });
237
+ return {
238
+ response: new StreamResponse(stream),
239
+ metaPromise
240
+ };
241
+ }