@ray-js/t-agent-plugin-aistream 0.2.0-beta-17 → 0.2.0-beta.0

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.
@@ -655,19 +655,47 @@ export declare enum FileFormat {
655
655
  SWEEPER_MAP = 6
656
656
  }
657
657
  export declare enum AIStreamErrorCode {
658
- AIStreamError = -1,
658
+ UNKNOWN_ERROR = "unknown-error",
659
+ NETWORK_OFFLINE = "network-offline",
660
+ INVALID_PARAMS = "invalid-params",
661
+ SESSION_CREATE_FAILED = "session-create-failed",
662
+ CONNECTION_CLOSED = "connection-closed",
663
+ EVENT_EXISTS = "event-exists",
664
+ EVENT_DISPOSED = "event-disposed",
665
+ EVENT_CLOSED = "event-closed",
666
+ EVENT_ABORTED = "event-aborted",
667
+ EVENT_WRITE_FAILED = "event-write-failed",
668
+ EVENT_NO_DATA_CODE = "event-no-data-code",
669
+ STREAM_EXISTS = "stream-exists",
670
+ TIMEOUT = "timeout",
671
+ ASR_EMPTY = "asr-empty"
672
+ }
673
+ export declare enum AIStreamAppErrorCode {
674
+ GENERIC_ERROR = 39001,
675
+ INVALID_PARAMS = 39002,
676
+ HTTP_REQUEST_FAILED = 39003,
677
+ CONNECTION_INVALID = 39004,
678
+ SESSION_ID_INVALID = 39005,
679
+ EVENT_ID_INVALID = 39006,
680
+ DATA_CHANNEL_NOT_FOUND = 39007,
681
+ INVALID_DATA_PACKET = 39008,
682
+ FILE_DATA_READ_ERROR = 39009,
683
+ DATA_SEND_FAILED = 39010,
684
+ CONNECTION_CLOSED_BY_REMOTE = 39012
685
+ }
686
+ export declare enum AIStreamServerErrorCode {
659
687
  OK = 200,
660
- BadRequest = 400,
661
- Unauthenticated = 401,
662
- NotFound = 404,
663
- RequestTimeout = 408,
664
- InternalServerError = 500,
665
- GatewayTimeout = 504,
666
- CloseByClient = 601,
667
- CloseByReuse = 602,
668
- CloseByIO = 603,
669
- CloseByKeepalive = 604,
670
- CloseByExpire = 605
688
+ BAD_REQUEST = 400,
689
+ UNAUTHENTICATED = 401,
690
+ NOT_FOUND = 404,
691
+ REQUEST_TIMEOUT = 408,
692
+ INTERNAL_SERVER_ERROR = 500,
693
+ GATEWAY_TIMEOUT = 504,
694
+ CLOSE_BY_CLIENT = 601,
695
+ CLOSE_BY_REUSE = 602,
696
+ CLOSE_BY_IO = 603,
697
+ CLOSE_BY_KEEPALIVE = 604,
698
+ CLOSE_BY_EXPIRE = 605
671
699
  }
672
700
  export declare enum BizCode {
673
701
  CHAT = 65537
@@ -713,6 +741,60 @@ export interface AIStreamChatAttribute {
713
741
  'sys.workflow'?: AIStreamChatSysWorkflow;
714
742
  [key: string]: string;
715
743
  }
744
+ export type StartPlayAudioParams = {
745
+ /** 音频缓存路径 */
746
+ path: string;
747
+ /**
748
+ * 音频编码类型,表示音频数据的编码格式(目前仅支持 PCM 播放)
749
+ * - 100: ADPCM
750
+ * - 101: PCM
751
+ * - 102: AACRaw
752
+ * - 103: AACADTS
753
+ * - 104: AACLATM
754
+ * - 105: G711U
755
+ * - 106: G711A
756
+ * - 107: G726
757
+ * - 108: SPEEX
758
+ * - 109: MP3
759
+ * - 110: G722
760
+ * - 111: Opus
761
+ */
762
+ codecType: number;
763
+ /**
764
+ * 音频采样率,单位Hz
765
+ * 表示每秒采样次数,常见值:8000, 16000, 44100等
766
+ * 数据类型:uint32,最大值:2^32-1
767
+ */
768
+ sampleRate: number;
769
+ /**
770
+ * 音频通道数
771
+ * - 0: 单声道
772
+ * - 1: 立体声
773
+ * 数据类型:uint16
774
+ */
775
+ channels: number;
776
+ /**
777
+ * 音频位深,表示每个采样点的位数
778
+ * 常见值:8, 16, 24, 32等
779
+ * 数据类型:uint16,最大值:2^16-1
780
+ */
781
+ bitDepth: number;
782
+ /**
783
+ * 微秒级时间戳
784
+ * 用于音频回放时的同步
785
+ */
786
+ pts?: number;
787
+ success?: (params: null) => void;
788
+ fail?: (params: {
789
+ errorMsg: string;
790
+ errorCode: string | number;
791
+ innerError: {
792
+ errorCode: string | number;
793
+ errorMsg: string;
794
+ };
795
+ }) => void;
796
+ complete?: () => void;
797
+ };
716
798
  export type Attribute = {
717
799
  /** Attribute 类型 */
718
800
  type: AIStreamAttributeType;
@@ -732,16 +814,57 @@ export type EventBody = {
732
814
  userData?: Attribute[];
733
815
  };
734
816
  export type AudioBody = {
735
- /** 接收数据通道 */
817
+ /** 接收数据通道 Code */
736
818
  dataChannel: string;
737
819
  /** 数据流类型: 0-仅一包, 1-传输开始, 2-传输中, 3-传输结束 */
738
- streamFlag: StreamFlag;
739
- /** 音频缓存路径,传输结束后可访问(streamFlag == 1 || StreamFlag == 3) */
820
+ streamFlag: number;
821
+ /** 音频缓存路径 */
740
822
  path: string;
741
823
  /** 扩展属性 */
742
824
  userData?: Attribute[];
743
825
  /** SessionId 列表, 云端返回,可能为空 */
744
826
  sessionIdList?: string[];
827
+ /**
828
+ * 微秒级时间戳
829
+ * 用于音频回放时的同步
830
+ */
831
+ pts?: number;
832
+ /**
833
+ * 音频编码类型,表示音频数据的编码格式
834
+ * - 100: ADPCM
835
+ * - 101: PCM
836
+ * - 102: AACRaw
837
+ * - 103: AACADTS
838
+ * - 104: AACLATM
839
+ * - 105: G711U
840
+ * - 106: G711A
841
+ * - 107: G726
842
+ * - 108: SPEEX
843
+ * - 109: MP3
844
+ * - 110: G722
845
+ * - 111: Opus
846
+ * 仅 streamFlag == 0 || == 1 时有值
847
+ */
848
+ codecType?: number;
849
+ /**
850
+ * 音频采样率,单位Hz
851
+ * 表示每秒采样次数,常见值:8000, 16000, 44100等
852
+ * 仅 streamFlag == 0 || == 1 时有值
853
+ */
854
+ sampleRate?: number;
855
+ /**
856
+ * 音频通道数
857
+ * - 0: 单声道
858
+ * - 1: 立体声
859
+ * 仅 streamFlag == 0 || == 1 时有值
860
+ */
861
+ channels?: number;
862
+ /**
863
+ * 音频位深,表示每个采样点的位数
864
+ * 常见值:8, 16, 24, 32等
865
+ * 仅 streamFlag == 0 || == 1 时有值
866
+ */
867
+ bitDepth?: number;
745
868
  };
746
869
  export type VideoBody = {
747
870
  /** 接收数据通道 */
@@ -803,7 +926,7 @@ export type ConnectStateBody = {
803
926
  /** 连接状态: 0-初始化,1-连接中,2-鉴权中,3-已连接,4-被云端断开,5-主动关闭 */
804
927
  connectState: ConnectState;
805
928
  /** 被云端断开错误码 */
806
- code?: AIStreamErrorCode;
929
+ code?: AIStreamServerErrorCode;
807
930
  };
808
931
  export type SessionStateBody = {
809
932
  /** 会话 id */
@@ -811,12 +934,37 @@ export type SessionStateBody = {
811
934
  /** 会话状态: 0-会话创建成功, 1-会话创建失败, 2-会话被云端关闭 */
812
935
  sessionState: SessionState;
813
936
  /** 会话被关闭原因 */
814
- code?: AIStreamErrorCode;
937
+ code?: AIStreamServerErrorCode;
815
938
  };
816
939
  export type RecordAmplitudesBody = {
817
940
  /** 振幅数据 */
818
941
  amplitudes: number[];
819
942
  };
943
+ export declare enum NetworkType {
944
+ NONE = "none",
945
+ CELL_2G = "2g",
946
+ CELL_3G = "3g",
947
+ CELL_4G = "4g",
948
+ CELL_5G = "5g",
949
+ WIFI = "wifi"
950
+ }
951
+ export type GetNetworkTypeParams = {
952
+ complete?: () => void;
953
+ success?: (params: {
954
+ /** 网络类型 */
955
+ networkType: NetworkType;
956
+ /** 信号强弱,单位 dbm */
957
+ signalStrength: number;
958
+ }) => void;
959
+ fail?: (params: {
960
+ errorMsg: string;
961
+ errorCode: string | number;
962
+ innerError: {
963
+ errorCode: string | number;
964
+ errorMsg: string;
965
+ };
966
+ }) => void;
967
+ };
820
968
  export type CheckConnectParams = {
821
969
  /** client 类型: 1-作为设备代理, 2-作为 App */
822
970
  clientType: ConnectClientType;
@@ -956,7 +1104,7 @@ export type CloseSessionParams = {
956
1104
  /** 会话 id */
957
1105
  sessionId: string;
958
1106
  /** 关闭原因, 非异常则使用 200 来关闭 */
959
- code: AIStreamErrorCode;
1107
+ code: AIStreamServerErrorCode;
960
1108
  success?: (params: null) => void;
961
1109
  fail?: (params: {
962
1110
  errorMsg: string;
@@ -1445,14 +1593,16 @@ export type ReceivedTextSkillPacketBody<Code = BuildInSkillCode | string, G = an
1445
1593
  export type ReceivedTextSkillPacket<Code = BuildInSkillCode | string, G = any, C = any> = ReceivedTextPacketBase<ReceivedTextPacketType.SKILL, ReceivedTextSkillPacketBody<Code, G, C>>;
1446
1594
  export type ReceivedTextPacket = ReceivedTextAsrPacket | ReceivedTextNlgPacket | ReceivedTextSkillPacket;
1447
1595
  export type ReceivedSearchKnowledgeSkill = ReceivedTextSkillPacketBody<BuildInSkillCode.SEARCH_KNOWLEDGE, any, {
1448
- documents: Array<{
1449
- libCode: string;
1450
- itemId: string;
1451
- itemType: 'KNOWLEDGE';
1452
- title: string;
1453
- url: string;
1454
- contentBody: string;
1455
- }>;
1596
+ data: {
1597
+ documents: Array<{
1598
+ libCode: string;
1599
+ itemId: string;
1600
+ itemType: 'KNOWLEDGE';
1601
+ title: string;
1602
+ url: string;
1603
+ contentBody: string;
1604
+ }>;
1605
+ };
1456
1606
  }>;
1457
1607
  export interface ReceivedSmartHomeSkillDevice {
1458
1608
  devId: string;
@@ -118,21 +118,51 @@ export let FileFormat = /*#__PURE__*/function (FileFormat) {
118
118
  return FileFormat;
119
119
  }({});
120
120
  export let AIStreamErrorCode = /*#__PURE__*/function (AIStreamErrorCode) {
121
- AIStreamErrorCode[AIStreamErrorCode["AIStreamError"] = -1] = "AIStreamError";
122
- AIStreamErrorCode[AIStreamErrorCode["OK"] = 200] = "OK";
123
- AIStreamErrorCode[AIStreamErrorCode["BadRequest"] = 400] = "BadRequest";
124
- AIStreamErrorCode[AIStreamErrorCode["Unauthenticated"] = 401] = "Unauthenticated";
125
- AIStreamErrorCode[AIStreamErrorCode["NotFound"] = 404] = "NotFound";
126
- AIStreamErrorCode[AIStreamErrorCode["RequestTimeout"] = 408] = "RequestTimeout";
127
- AIStreamErrorCode[AIStreamErrorCode["InternalServerError"] = 500] = "InternalServerError";
128
- AIStreamErrorCode[AIStreamErrorCode["GatewayTimeout"] = 504] = "GatewayTimeout";
129
- AIStreamErrorCode[AIStreamErrorCode["CloseByClient"] = 601] = "CloseByClient";
130
- AIStreamErrorCode[AIStreamErrorCode["CloseByReuse"] = 602] = "CloseByReuse";
131
- AIStreamErrorCode[AIStreamErrorCode["CloseByIO"] = 603] = "CloseByIO";
132
- AIStreamErrorCode[AIStreamErrorCode["CloseByKeepalive"] = 604] = "CloseByKeepalive";
133
- AIStreamErrorCode[AIStreamErrorCode["CloseByExpire"] = 605] = "CloseByExpire";
121
+ AIStreamErrorCode["UNKNOWN_ERROR"] = "unknown-error";
122
+ AIStreamErrorCode["NETWORK_OFFLINE"] = "network-offline";
123
+ AIStreamErrorCode["INVALID_PARAMS"] = "invalid-params";
124
+ AIStreamErrorCode["SESSION_CREATE_FAILED"] = "session-create-failed";
125
+ AIStreamErrorCode["CONNECTION_CLOSED"] = "connection-closed";
126
+ AIStreamErrorCode["EVENT_EXISTS"] = "event-exists";
127
+ AIStreamErrorCode["EVENT_DISPOSED"] = "event-disposed";
128
+ AIStreamErrorCode["EVENT_CLOSED"] = "event-closed";
129
+ AIStreamErrorCode["EVENT_ABORTED"] = "event-aborted";
130
+ AIStreamErrorCode["EVENT_WRITE_FAILED"] = "event-write-failed";
131
+ AIStreamErrorCode["EVENT_NO_DATA_CODE"] = "event-no-data-code";
132
+ AIStreamErrorCode["STREAM_EXISTS"] = "stream-exists";
133
+ AIStreamErrorCode["TIMEOUT"] = "timeout";
134
+ AIStreamErrorCode["ASR_EMPTY"] = "asr-empty";
134
135
  return AIStreamErrorCode;
135
136
  }({});
137
+ export let AIStreamAppErrorCode = /*#__PURE__*/function (AIStreamAppErrorCode) {
138
+ AIStreamAppErrorCode[AIStreamAppErrorCode["GENERIC_ERROR"] = 39001] = "GENERIC_ERROR";
139
+ AIStreamAppErrorCode[AIStreamAppErrorCode["INVALID_PARAMS"] = 39002] = "INVALID_PARAMS";
140
+ AIStreamAppErrorCode[AIStreamAppErrorCode["HTTP_REQUEST_FAILED"] = 39003] = "HTTP_REQUEST_FAILED";
141
+ AIStreamAppErrorCode[AIStreamAppErrorCode["CONNECTION_INVALID"] = 39004] = "CONNECTION_INVALID";
142
+ AIStreamAppErrorCode[AIStreamAppErrorCode["SESSION_ID_INVALID"] = 39005] = "SESSION_ID_INVALID";
143
+ AIStreamAppErrorCode[AIStreamAppErrorCode["EVENT_ID_INVALID"] = 39006] = "EVENT_ID_INVALID";
144
+ AIStreamAppErrorCode[AIStreamAppErrorCode["DATA_CHANNEL_NOT_FOUND"] = 39007] = "DATA_CHANNEL_NOT_FOUND";
145
+ AIStreamAppErrorCode[AIStreamAppErrorCode["INVALID_DATA_PACKET"] = 39008] = "INVALID_DATA_PACKET";
146
+ AIStreamAppErrorCode[AIStreamAppErrorCode["FILE_DATA_READ_ERROR"] = 39009] = "FILE_DATA_READ_ERROR";
147
+ AIStreamAppErrorCode[AIStreamAppErrorCode["DATA_SEND_FAILED"] = 39010] = "DATA_SEND_FAILED";
148
+ AIStreamAppErrorCode[AIStreamAppErrorCode["CONNECTION_CLOSED_BY_REMOTE"] = 39012] = "CONNECTION_CLOSED_BY_REMOTE";
149
+ return AIStreamAppErrorCode;
150
+ }({});
151
+ export let AIStreamServerErrorCode = /*#__PURE__*/function (AIStreamServerErrorCode) {
152
+ AIStreamServerErrorCode[AIStreamServerErrorCode["OK"] = 200] = "OK";
153
+ AIStreamServerErrorCode[AIStreamServerErrorCode["BAD_REQUEST"] = 400] = "BAD_REQUEST";
154
+ AIStreamServerErrorCode[AIStreamServerErrorCode["UNAUTHENTICATED"] = 401] = "UNAUTHENTICATED";
155
+ AIStreamServerErrorCode[AIStreamServerErrorCode["NOT_FOUND"] = 404] = "NOT_FOUND";
156
+ AIStreamServerErrorCode[AIStreamServerErrorCode["REQUEST_TIMEOUT"] = 408] = "REQUEST_TIMEOUT";
157
+ AIStreamServerErrorCode[AIStreamServerErrorCode["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
158
+ AIStreamServerErrorCode[AIStreamServerErrorCode["GATEWAY_TIMEOUT"] = 504] = "GATEWAY_TIMEOUT";
159
+ AIStreamServerErrorCode[AIStreamServerErrorCode["CLOSE_BY_CLIENT"] = 601] = "CLOSE_BY_CLIENT";
160
+ AIStreamServerErrorCode[AIStreamServerErrorCode["CLOSE_BY_REUSE"] = 602] = "CLOSE_BY_REUSE";
161
+ AIStreamServerErrorCode[AIStreamServerErrorCode["CLOSE_BY_IO"] = 603] = "CLOSE_BY_IO";
162
+ AIStreamServerErrorCode[AIStreamServerErrorCode["CLOSE_BY_KEEPALIVE"] = 604] = "CLOSE_BY_KEEPALIVE";
163
+ AIStreamServerErrorCode[AIStreamServerErrorCode["CLOSE_BY_EXPIRE"] = 605] = "CLOSE_BY_EXPIRE";
164
+ return AIStreamServerErrorCode;
165
+ }({});
136
166
  export let BizCode = /*#__PURE__*/function (BizCode) {
137
167
  BizCode[BizCode["CHAT"] = 65537] = "CHAT";
138
168
  return BizCode;
@@ -162,6 +192,15 @@ export let AIStreamChatSysWorkflow = /*#__PURE__*/function (AIStreamChatSysWorkf
162
192
  AIStreamChatSysWorkflow["LLM_TTS"] = "llm-tts";
163
193
  return AIStreamChatSysWorkflow;
164
194
  }({});
195
+ export let NetworkType = /*#__PURE__*/function (NetworkType) {
196
+ NetworkType["NONE"] = "none";
197
+ NetworkType["CELL_2G"] = "2g";
198
+ NetworkType["CELL_3G"] = "3g";
199
+ NetworkType["CELL_4G"] = "4g";
200
+ NetworkType["CELL_5G"] = "5g";
201
+ NetworkType["WIFI"] = "wifi";
202
+ return NetworkType;
203
+ }({});
165
204
 
166
205
  /**
167
206
  *@description 发起通道连接,若此前已连接会直接回调成功
@@ -55,10 +55,10 @@ export function withBuildIn() {
55
55
  continue;
56
56
  }
57
57
  const content = skill;
58
- if (!((_content$custom = content.custom) !== null && _content$custom !== void 0 && _content$custom.documents)) {
58
+ if (!((_content$custom = content.custom) !== null && _content$custom !== void 0 && (_content$custom = _content$custom.data) !== null && _content$custom !== void 0 && _content$custom.documents)) {
59
59
  continue;
60
60
  }
61
- for (const doc of content.custom.documents) {
61
+ for (const doc of content.custom.data.documents) {
62
62
  data.documents.push({
63
63
  title: doc.title,
64
64
  url: doc.url
@@ -1,5 +1,6 @@
1
1
  import { Attribute, BizTag, ConnectClientType, ConnectState, FileFormat, VideoCameraType } from '../AIStreamTypes';
2
2
  import { AIStreamDataEntry, AIStreamObserverPool } from './observer';
3
+ import { AIStreamError } from './errors';
3
4
  interface AIStreamConnectionOptions {
4
5
  /** client 类型: 1-作为设备代理, 2-作为 App */
5
6
  clientType: ConnectClientType;
@@ -124,15 +125,15 @@ export declare class AIStreamEvent {
124
125
  }): void;
125
126
  on(name: 'close', callback: () => void): void;
126
127
  on(name: 'finish', callback: () => void): void;
127
- on(name: 'error', callback: (error: any) => void): void;
128
+ on(name: 'error', callback: (error: AIStreamError) => void): void;
128
129
  on(name: 'data', callback: (entry: AIStreamDataEntry) => void): void;
129
130
  off(name: 'finish', callback: () => void): void;
130
131
  off(name: 'close', callback: () => void): void;
131
- off(name: 'error', callback: (error: any) => void): void;
132
+ off(name: 'error', callback: (error: AIStreamError) => void): void;
132
133
  off(name: 'data', callback: (entry: AIStreamDataEntry) => void): void;
133
134
  emit(name: 'finish'): void;
134
135
  emit(name: 'close'): void;
135
- emit(name: 'error', data: Error): void;
136
+ emit(name: 'error', data: AIStreamError): void;
136
137
  emit(name: 'data', data: AIStreamDataEntry): void;
137
138
  private dispose;
138
139
  }
@@ -1,17 +1,18 @@
1
+ import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
1
2
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
2
3
  import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
4
+ const _excluded = ["signal"];
3
5
  import "core-js/modules/esnext.iterator.constructor.js";
4
6
  import "core-js/modules/esnext.iterator.find.js";
5
7
  import "core-js/modules/esnext.iterator.for-each.js";
6
8
  import "core-js/modules/esnext.iterator.map.js";
7
9
  import "core-js/modules/web.dom-collections.iterator.js";
8
- import { AIStreamErrorCode, BizTag, ConnectClientType, ConnectState, EventType, SessionState } from '../AIStreamTypes';
9
- import { closeSession, connect, createSession, disconnect, getCurrentHomeInfo, isConnected, queryAgentToken, registerRecordAmplitudes, sendEventChatBreak, sendEventEnd, sendEventPayloadEnd, sendEventStart, sendImageData, sendTextData, startRecordAndSendAudioData, stopRecordAndSendAudioData, unregisterVoiceAmplitudes } from './ttt';
10
+ import { AIStreamErrorCode, AIStreamServerErrorCode, BizTag, ConnectClientType, ConnectState, EventType, NetworkType, SessionState } from '../AIStreamTypes';
11
+ import { closeSession, connect, createSession, disconnect, getCurrentHomeInfo, getNetworkType, isConnected, queryAgentToken, registerRecordAmplitudes, sendEventChatBreak, sendEventEnd, sendEventPayloadEnd, sendEventStart, sendImageData, sendTextData, startRecordAndSendAudioData, stopRecordAndSendAudioData, unregisterVoiceAmplitudes } from './ttt';
10
12
  import { AIStreamObserver, AIStreamObserverPool } from './observer';
11
13
  import { isAbortError } from '@ray-js/t-agent';
12
14
  import logger from './logger';
13
- import { AIStreamConnectionError, AIStreamEventError } from './errors';
14
- import { tryCatch } from './misc';
15
+ import { AIStreamError, tryCatchTTT } from './errors';
15
16
  export class AIStreamClient {
16
17
  constructor() {
17
18
  _defineProperty(this, "pool", new AIStreamObserverPool());
@@ -72,12 +73,24 @@ export class AIStreamConnection {
72
73
  }
73
74
  };
74
75
  this.promise = (async () => {
76
+ // 检查网络状态
77
+ {
78
+ const [error, result] = await tryCatchTTT(() => getNetworkType());
79
+ if (error || (result === null || result === void 0 ? void 0 : result.networkType) === NetworkType.NONE) {
80
+ this.cleanup();
81
+ this.state = ConnectState.DISCONNECTED;
82
+ // 未连接网络
83
+ throw new AIStreamError('Network is offline', AIStreamErrorCode.NETWORK_OFFLINE);
84
+ }
85
+ }
86
+
75
87
  // 检查连接状态
76
88
  {
77
- const [error, result] = await tryCatch(() => isConnected(this.options));
89
+ const [error, result] = await tryCatchTTT(() => isConnected(this.options));
78
90
  if (error) {
79
- this.promise = null;
80
- throw new AIStreamConnectionError(error.message, error.code || AIStreamErrorCode.AIStreamError);
91
+ this.cleanup();
92
+ this.state = ConnectState.DISCONNECTED;
93
+ throw error;
81
94
  }
82
95
  if (result.connected) {
83
96
  this.state = result.state;
@@ -89,10 +102,11 @@ export class AIStreamConnection {
89
102
 
90
103
  // 未连接的情况,调用 connect
91
104
  {
92
- const [error, result] = await tryCatch(() => connect(this.options));
105
+ const [error, result] = await tryCatchTTT(() => connect(this.options));
93
106
  if (error) {
94
- this.promise = null;
95
- throw new AIStreamConnectionError(error.message, error.code || AIStreamErrorCode.AIStreamError);
107
+ this.cleanup();
108
+ this.state = ConnectState.DISCONNECTED;
109
+ throw error;
96
110
  }
97
111
  this.connectionId = result.connectionId;
98
112
  this.state = ConnectState.CONNECTED;
@@ -122,7 +136,7 @@ export class AIStreamConnection {
122
136
  if (session.sessionId && this.connectionId) {
123
137
  await closeSession({
124
138
  sessionId: session.sessionId,
125
- code: AIStreamErrorCode.OK
139
+ code: AIStreamServerErrorCode.OK
126
140
  });
127
141
  }
128
142
  this.activeSessions.delete(session);
@@ -187,58 +201,87 @@ export class AIStreamSession {
187
201
  this.promise = (async () => {
188
202
  try {
189
203
  await this.connection._ensureConnected();
190
- let ownerId = this.options.ownerId;
191
- if (!ownerId) {
192
- if (this.connection.options.clientType === ConnectClientType.APP) {
204
+ } catch (e) {
205
+ this.promise = null;
206
+ throw e;
207
+ }
208
+ let ownerId = this.options.ownerId;
209
+ if (!ownerId) {
210
+ if (this.connection.options.clientType === ConnectClientType.APP) {
211
+ try {
193
212
  const {
194
213
  homeId
195
214
  } = await getCurrentHomeInfo();
196
215
  ownerId = homeId;
197
- } else {
198
- ownerId = this.connection.options.deviceId;
216
+ } catch (e) {
217
+ this.promise = null;
218
+ throw new AIStreamError('Cannot get current home info by getCurrentHomeInfo', AIStreamErrorCode.INVALID_PARAMS);
199
219
  }
200
- this.options.ownerId = ownerId;
220
+ } else {
221
+ ownerId = this.connection.options.deviceId;
201
222
  }
202
- const options = _objectSpread({
203
- bizTag: BizTag.DEFAULT,
204
- ownerId,
205
- extParams: {}
206
- }, this.options);
207
- const {
208
- agentToken,
209
- bizConfig
210
- } = await queryAgentToken(options);
211
- const res = await createSession({
223
+ this.options.ownerId = ownerId;
224
+ }
225
+ const options = _objectSpread({
226
+ bizTag: BizTag.DEFAULT,
227
+ ownerId,
228
+ extParams: {}
229
+ }, this.options);
230
+ const [error, result] = await tryCatchTTT(() => queryAgentToken(options));
231
+ if (error) {
232
+ this.promise = null;
233
+ throw error;
234
+ }
235
+ const {
236
+ agentToken,
237
+ bizConfig
238
+ } = result;
239
+ {
240
+ const [err, res] = await tryCatchTTT(() => createSession({
212
241
  bizTag: options.bizTag,
213
242
  agentToken,
214
243
  bizConfig,
215
244
  userData: options.userData
216
- });
245
+ }));
246
+ if (err) {
247
+ this.promise = null;
248
+ throw err;
249
+ }
217
250
  this.sessionId = res.sessionId;
218
251
  this.sendDataChannels = res.sendDataChannels;
219
252
  this.revDataChannels = res.revDataChannels;
220
- } finally {
221
253
  this.promise = null;
222
254
  }
223
255
  })();
224
256
  return this.promise;
225
257
  }
226
258
  async startEvent() {
227
- var _options$signal;
228
259
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
229
260
  if (this.disposed) {
230
- throw new AIStreamEventError('Event has been disposed', AIStreamErrorCode.AIStreamError);
261
+ throw new AIStreamError('Event has been disposed', AIStreamErrorCode.EVENT_DISPOSED);
231
262
  }
232
263
  if (this.activeEvent) {
233
- throw new AIStreamEventError('Cannot start a new event while another is active', AIStreamErrorCode.AIStreamError);
264
+ throw new AIStreamError('Cannot start a new event while another is active', AIStreamErrorCode.EVENT_EXISTS);
234
265
  }
266
+ const {
267
+ signal
268
+ } = options,
269
+ rest = _objectWithoutProperties(options, _excluded);
235
270
  await this.ensureSession();
236
- (_options$signal = options.signal) === null || _options$signal === void 0 || _options$signal.throwIfAborted();
271
+ if (signal !== null && signal !== void 0 && signal.aborted) {
272
+ const error = new AIStreamError('start event was aborted', AIStreamErrorCode.EVENT_ABORTED);
273
+ error.name = 'AbortError';
274
+ throw error;
275
+ }
276
+ const [error, result] = await tryCatchTTT(() => sendEventStart(_objectSpread({
277
+ sessionId: this.sessionId
278
+ }, rest)));
279
+ if (error) {
280
+ throw error;
281
+ }
237
282
  const {
238
283
  eventId
239
- } = await sendEventStart(_objectSpread({
240
- sessionId: this.sessionId
241
- }, options));
284
+ } = result;
242
285
  this.activeEvent = new AIStreamEvent({
243
286
  eventId,
244
287
  sessionId: this.sessionId,
@@ -318,13 +361,13 @@ export class AIStreamEvent {
318
361
  findFirstCode(type) {
319
362
  const code = this.sendDataChannels.find(code => code.startsWith(type));
320
363
  if (!code) {
321
- throw new AIStreamEventError("No available data code for type: ".concat(type), AIStreamErrorCode.AIStreamError);
364
+ throw new AIStreamError("No available data code for type: ".concat(type), AIStreamErrorCode.EVENT_NO_DATA_CODE);
322
365
  }
323
366
  return code;
324
367
  }
325
368
  write(chunk) {
326
369
  if (this.closed) {
327
- throw new AIStreamEventError('Cannot write to a closed event', AIStreamErrorCode.AIStreamError);
370
+ throw new AIStreamError('Cannot write to a closed event', AIStreamErrorCode.EVENT_CLOSED);
328
371
  }
329
372
  const dataChannel = chunk.dataChannel || this.findFirstCode(chunk.type);
330
373
  let promise = this.chains[dataChannel];
@@ -336,13 +379,17 @@ export class AIStreamEvent {
336
379
  return;
337
380
  }
338
381
  if (chunk.type === 'text') {
339
- await sendTextData({
382
+ const [error] = await tryCatchTTT(() => sendTextData({
340
383
  sessionId: this.sessionId,
341
384
  text: chunk.text,
342
385
  dataChannel,
343
386
  userData: chunk.userData
344
- });
387
+ }));
388
+ if (error) {
389
+ throw error;
390
+ }
345
391
  } else if (chunk.type === 'file') {
392
+ logger.warn('File data sending is not implemented yet');
346
393
  // await sendFileData({
347
394
  // sessionId: this.sessionId,
348
395
  // path: chunk.path,
@@ -351,11 +398,12 @@ export class AIStreamEvent {
351
398
  // userData: chunk.userData,
352
399
  // });
353
400
  } else if (chunk.type === 'image') {
354
- await sendImageData({
401
+ const [error] = await tryCatchTTT(() => sendImageData({
355
402
  sessionId: this.sessionId,
356
403
  path: chunk.path,
357
404
  userData: chunk.userData
358
- });
405
+ }));
406
+ throw error;
359
407
  }
360
408
  sendEventPayloadEnd({
361
409
  eventId: this.eventId,
@@ -363,18 +411,19 @@ export class AIStreamEvent {
363
411
  dataChannel
364
412
  });
365
413
  }).catch(error => {
366
- this.emit('error', error);
367
- throw error;
414
+ const e = new AIStreamError(error.message, error.code || AIStreamErrorCode.EVENT_WRITE_FAILED);
415
+ this.emit('error', e);
416
+ throw e;
368
417
  });
369
418
  return this.chains[dataChannel];
370
419
  }
371
420
  stream(source) {
372
421
  if (this.closed) {
373
- throw new AIStreamEventError('Cannot stream to a closed event', AIStreamErrorCode.AIStreamError);
422
+ throw new AIStreamError('Cannot stream to a closed event', AIStreamErrorCode.EVENT_CLOSED);
374
423
  }
375
424
  const dataChannel = source.dataChannel || this.findFirstCode(source.type);
376
425
  if (this.streams[dataChannel]) {
377
- throw new AIStreamEventError("".concat(dataChannel, " stream already exists"), AIStreamErrorCode.AIStreamError);
426
+ throw new AIStreamError("".concat(dataChannel, " stream already exists"), AIStreamErrorCode.STREAM_EXISTS);
378
427
  }
379
428
  let startPromise = null;
380
429
  const stream = {
@@ -383,7 +432,7 @@ export class AIStreamEvent {
383
432
  started: false,
384
433
  start: async () => {
385
434
  if (this.closed) {
386
- throw new AIStreamEventError('Cannot stream to a closed event', AIStreamErrorCode.AIStreamError);
435
+ throw new AIStreamError('Cannot stream to a closed event', AIStreamErrorCode.EVENT_CLOSED);
387
436
  }
388
437
  if (stream.started) {
389
438
  return;
@@ -416,11 +465,7 @@ export class AIStreamEvent {
416
465
  }
417
466
  // 一定要等录音开始,才能结束
418
467
  if (startPromise) {
419
- try {
420
- await startPromise;
421
- } catch (e) {
422
- // pass
423
- }
468
+ await tryCatchTTT(() => startPromise);
424
469
  startPromise = null;
425
470
  }
426
471
  if (source.type === 'audio') {
@@ -435,6 +480,7 @@ export class AIStreamEvent {
435
480
  userData: source.userData
436
481
  });
437
482
  } else if (source.type === 'video') {
483
+ logger.warn('Video data sending is not implemented yet');
438
484
  // const cameraType = source.cameraType || VideoCameraType.BACK;
439
485
  // await stopRecordAndSendVideoData({
440
486
  // dataChannel,
@@ -477,7 +523,7 @@ export class AIStreamEvent {
477
523
  userData: options === null || options === void 0 ? void 0 : options.userData
478
524
  });
479
525
  }
480
- const error = new Error('This operation was aborted');
526
+ const error = new AIStreamError('This operation was aborted', AIStreamErrorCode.EVENT_ABORTED);
481
527
  error.name = 'AbortError';
482
528
  // 发送 break 后,stream 自动会关掉,在这里提前关闭
483
529
  Object.values(this.streams).forEach(s => {
@@ -8,9 +8,10 @@ import "core-js/modules/esnext.iterator.map.js";
8
8
  import "core-js/modules/web.dom-collections.iterator.js";
9
9
  import { mock } from './mock';
10
10
  import { EmitterEvent, generateId } from '@ray-js/t-agent';
11
- import { AIStreamErrorCode, BizCode, ConnectState, EventType, ReceivedTextPacketEof, ReceivedTextPacketType, SessionState, StreamFlag } from '../AIStreamTypes';
11
+ import { AIStreamAppErrorCode, AIStreamServerErrorCode, BizCode, ConnectState, EventType, NetworkType, ReceivedTextPacketEof, ReceivedTextPacketType, SessionState, StreamFlag } from '../AIStreamTypes';
12
12
  import AbortController from './abort';
13
13
  import { tryCatch } from './misc';
14
+ import { TTTError } from './errors';
14
15
  function splitString(input) {
15
16
  return input.match(/[a-zA-Z0-9]+\s*|[\u4e00-\u9fff]+\s*|[^\w\s\u4e00-\u9fff]+\s*|[\s]+/g) || [];
16
17
  }
@@ -18,31 +19,37 @@ mock.data.set('sessionMap', new Map());
18
19
  const getSession = (sessionId, eventId) => {
19
20
  const connection = getCurrentConnection();
20
21
  if (!connection) {
21
- throw new Error('not connected');
22
+ throw new TTTError('not connect', AIStreamAppErrorCode.CONNECTION_INVALID);
22
23
  }
23
24
  const map = mock.data.get('sessionMap');
24
25
  const session = map.get(sessionId);
25
26
  if (!session) {
26
- throw new Error('session not exists');
27
+ throw new TTTError('session is invalid', AIStreamAppErrorCode.SESSION_ID_INVALID);
27
28
  }
28
29
  if (eventId) {
29
30
  if (!session.currentEvent) {
30
- throw new Error('event not exists');
31
+ throw new TTTError('eventId is invalid', AIStreamAppErrorCode.EVENT_ID_INVALID);
31
32
  }
32
33
  if (session.currentEvent.eventId !== eventId) {
33
- throw new Error('eventId mismatch');
34
+ throw new TTTError('eventId is invalid', AIStreamAppErrorCode.EVENT_ID_INVALID);
34
35
  }
35
36
  }
36
37
  return session;
37
38
  };
39
+ const getCurrentConnection = () => {
40
+ return mock.data.get('currentConnection');
41
+ };
42
+ mock.hooks.hook('getNetworkType', context => {
43
+ context.result = {
44
+ networkType: NetworkType.WIFI,
45
+ signalStrength: 85
46
+ };
47
+ });
38
48
  mock.hooks.hook('getMiniAppConfig', context => {
39
49
  context.result = {
40
50
  config: {}
41
51
  };
42
52
  });
43
- const getCurrentConnection = () => {
44
- return mock.data.get('currentConnection');
45
- };
46
53
  mock.hooks.hook('isConnected', context => {
47
54
  const connection = getCurrentConnection();
48
55
  if (connection) {
@@ -61,13 +68,13 @@ mock.hooks.hook('isConnected', context => {
61
68
  mock.hooks.hook('connect', context => {
62
69
  let connection = getCurrentConnection();
63
70
  if (connection) {
64
- throw new Error('already connected');
65
- } else {
66
- connection = {
67
- connectionId: generateId()
68
- };
69
- mock.data.set('currentConnection', connection);
71
+ context.result = connection;
72
+ return;
70
73
  }
74
+ connection = {
75
+ connectionId: generateId()
76
+ };
77
+ mock.data.set('currentConnection', connection);
71
78
  context.result = connection;
72
79
  });
73
80
  mock.hooks.hook('disconnect', context => {
@@ -78,10 +85,10 @@ mock.hooks.hook('disconnect', context => {
78
85
  } = context;
79
86
  const connection = getCurrentConnection();
80
87
  if (!connection) {
81
- throw new Error('not connected');
88
+ return;
82
89
  }
83
90
  if (connection.connectionId !== connectionId) {
84
- throw new Error('connectionId mismatch');
91
+ throw new TTTError('not connect', AIStreamAppErrorCode.CONNECTION_INVALID);
85
92
  }
86
93
  mock.data.set('currentConnection', null);
87
94
  mock.data.set('sessionMap', new Map());
@@ -104,7 +111,7 @@ const dispatch = (type, detail) => {
104
111
  mock.hooks.hook('createSession', context => {
105
112
  const connection = getCurrentConnection();
106
113
  if (!connection) {
107
- throw new Error('not connected');
114
+ throw new TTTError('not connect', AIStreamAppErrorCode.CONNECTION_INVALID);
108
115
  }
109
116
  const map = mock.data.get('sessionMap');
110
117
  const session = {
@@ -161,12 +168,12 @@ mock.hooks.hook('sendEventStart', async context => {
161
168
  sessionId
162
169
  } = options;
163
170
 
164
- // throw new Error('sendEventStart is deprecated, use sendEventStartV2 instead');
171
+ // throw new TTTError('sendEventStart is deprecated, use sendEventStartV2 instead');
165
172
 
166
173
  const session = getSession(sessionId);
167
174
  await mock.sleep(200);
168
175
  if (session.currentEvent) {
169
- throw new Error('sendEventStart already in event');
176
+ throw new TTTError('sendEventStart already in event', AIStreamAppErrorCode.EVENT_ID_INVALID);
170
177
  }
171
178
  const eventId = generateId();
172
179
  const event = {
@@ -176,7 +183,7 @@ mock.hooks.hook('sendEventStart', async context => {
176
183
  return;
177
184
  }
178
185
  if (!session.currentEvent || session.currentEvent.eventId !== eventId) {
179
- throw new Error('replyEvent event not exists or not in current event');
186
+ throw new TTTError('replyEvent event not exists or not in current event', AIStreamAppErrorCode.DATA_SEND_FAILED);
180
187
  }
181
188
  dispatch('onTextReceived', {
182
189
  dataChannel: 'text',
@@ -190,7 +197,7 @@ mock.hooks.hook('sendEventStart', async context => {
190
197
  return;
191
198
  }
192
199
  if (!session.currentEvent || session.currentEvent.eventId !== eventId) {
193
- throw new Error('replyEvent event not exists or not in current event');
200
+ throw new TTTError('replyEvent event not exists or not in current event', AIStreamAppErrorCode.DATA_SEND_FAILED);
194
201
  }
195
202
  dispatch('onEventReceived', {
196
203
  eventId,
@@ -225,7 +232,7 @@ mock.hooks.hook('sendEventEnd', async context => {
225
232
  context.result = true;
226
233
  const event = session.currentEvent;
227
234
  if (event.eventId !== context.options.eventId) {
228
- throw new Error('sendEventEnd eventId mismatch');
235
+ throw new TTTError('sendEventEnd eventId mismatch', AIStreamAppErrorCode.EVENT_ID_INVALID);
229
236
  }
230
237
  await mock.sleep(100);
231
238
  (async () => {
@@ -240,7 +247,7 @@ mock.hooks.hook('sendEventEnd', async context => {
240
247
  dispatch('onSessionStateChanged', {
241
248
  sessionId: session.sessionId,
242
249
  sessionState: SessionState.CLOSED,
243
- code: AIStreamErrorCode.InternalServerError
250
+ code: AIStreamServerErrorCode.INTERNAL_SERVER_ERROR
244
251
  });
245
252
  session.closed = true;
246
253
  const map = mock.data.get('sessionMap');
@@ -340,7 +347,7 @@ mock.hooks.hook('sendEventChatBreak', async context => {
340
347
  mock.hooks.hook('startRecordAndSendAudioData', async context => {
341
348
  const session = getSession(context.options.sessionId);
342
349
  if (!session.currentEvent) {
343
- throw new Error('startRecordAndSendAudioData event not exists');
350
+ throw new TTTError('startRecordAndSendAudioData event not exists', AIStreamAppErrorCode.EVENT_ID_INVALID);
344
351
  }
345
352
  const event = session.currentEvent;
346
353
  context.result = true;
@@ -444,7 +451,7 @@ mock.hooks.hook('startRecordAndSendAudioData', async context => {
444
451
  mock.hooks.hook('stopRecordAndSendAudioData', async context => {
445
452
  const session = getSession(context.options.sessionId);
446
453
  if (!session.currentEvent) {
447
- throw new Error('stopRecordAndSendAudioData event not exists');
454
+ throw new TTTError('stopRecordAndSendAudioData event not exists', AIStreamAppErrorCode.SESSION_ID_INVALID);
448
455
  }
449
456
  session.currentEvent.asr.finishController.abort(new Error('stopRecordAndSendAudioData'));
450
457
  context.result = true;
@@ -459,7 +466,7 @@ mock.hooks.hook('stopRecordAndSendVideoData', context => {
459
466
  mock.hooks.hook('sendImageData', context => {
460
467
  const session = getSession(context.options.sessionId);
461
468
  if (!session.currentEvent) {
462
- throw new Error('sendImageData event not exists');
469
+ throw new TTTError('sendImageData event not exists', AIStreamAppErrorCode.SESSION_ID_INVALID);
463
470
  }
464
471
  session.currentEvent.data.push({
465
472
  type: 'image',
@@ -472,7 +479,7 @@ mock.hooks.hook('sendTextData', context => {
472
479
  context.result = true;
473
480
  const session = getSession(context.options.sessionId);
474
481
  if (!session.currentEvent) {
475
- throw new Error('sendTextData event not exists');
482
+ throw new TTTError('sendTextData event not exists', AIStreamAppErrorCode.SESSION_ID_INVALID);
476
483
  }
477
484
  session.currentEvent.data.push({
478
485
  type: 'text',
@@ -595,7 +602,7 @@ mock.hooks.hook('updateRecord', context => {
595
602
  id: [id]
596
603
  });
597
604
  if (!records.length) {
598
- throw new Error('record not exists');
605
+ throw new TTTError('record not exists', AIStreamAppErrorCode.INVALID_PARAMS);
599
606
  }
600
607
  const newRecord = _objectSpread(_objectSpread({}, records[0]), options);
601
608
  mock.setRecord({
@@ -1,3 +1,4 @@
1
+ import { AIStreamErrorCode } from '../AIStreamTypes';
1
2
  export declare class BaseError extends Error {
2
3
  message: string;
3
4
  code: string | number;
@@ -9,18 +10,10 @@ export declare class TTTError extends BaseError {
9
10
  code: string | number;
10
11
  constructor(message: string, code: string | number);
11
12
  }
12
- export declare class AIStreamConnectionError extends BaseError {
13
- message: string;
14
- code: string | number;
15
- constructor(message: string, code: string | number);
16
- }
17
- export declare class AIStreamSessionError extends BaseError {
18
- message: string;
19
- code: string | number;
20
- constructor(message: string, code: string | number);
21
- }
22
- export declare class AIStreamEventError extends BaseError {
13
+ export declare class AIStreamError extends BaseError {
23
14
  message: string;
24
15
  code: string | number;
25
16
  constructor(message: string, code: string | number);
26
17
  }
18
+ export declare const transformErrorCode: (code: string | number) => AIStreamErrorCode;
19
+ export declare function tryCatchTTT<T>(fn: () => Promise<T>): Promise<[AIStreamError, null] | [null, T]>;
@@ -1,3 +1,6 @@
1
+ import "core-js/modules/web.dom-collections.iterator.js";
2
+ import { AIStreamAppErrorCode, AIStreamErrorCode, AIStreamServerErrorCode } from '../AIStreamTypes';
3
+ import { tryCatch } from './misc';
1
4
  export class BaseError extends Error {
2
5
  constructor(message, code) {
3
6
  super(message);
@@ -16,27 +19,87 @@ export class TTTError extends BaseError {
16
19
  this.name = 'TTTError';
17
20
  }
18
21
  }
19
- export class AIStreamConnectionError extends BaseError {
22
+ export class AIStreamError extends BaseError {
20
23
  constructor(message, code) {
21
24
  super(message, code);
22
25
  this.message = message;
23
26
  this.code = code;
24
- this.name = 'AIStreamConnectionError';
27
+ this.name = 'AIStreamError';
25
28
  }
26
29
  }
27
- export class AIStreamSessionError extends BaseError {
28
- constructor(message, code) {
29
- super(message, code);
30
- this.message = message;
31
- this.code = code;
32
- this.name = 'AIStreamSessionError';
30
+
31
+ /**
32
+ * 移除错误对象的顶部栈帧
33
+ *
34
+ * @param error 需要处理的错误对象
35
+ * @returns 移除顶部栈帧后的错误对象
36
+ */
37
+ function removeTopStackFrame(error) {
38
+ if (!error.stack) {
39
+ return error;
40
+ }
41
+
42
+ // 按行分割错误栈信息
43
+ const stackLines = error.stack.split('\n');
44
+
45
+ // 检查是否有足够的行来移除
46
+ if (stackLines.length <= 1) {
47
+ return error;
33
48
  }
49
+
50
+ // 保留第一行错误信息,移除第二行(顶部栈帧)
51
+ const newStack = [stackLines[0]].concat(stackLines.slice(2)).join('\n');
52
+ error.stack = newStack;
53
+ return error;
34
54
  }
35
- export class AIStreamEventError extends BaseError {
36
- constructor(message, code) {
37
- super(message, code);
38
- this.message = message;
39
- this.code = code;
40
- this.name = 'AIStreamEventError';
55
+ export const transformErrorCode = code => {
56
+ switch (code) {
57
+ case AIStreamAppErrorCode.GENERIC_ERROR:
58
+ return AIStreamErrorCode.UNKNOWN_ERROR;
59
+ case AIStreamAppErrorCode.INVALID_PARAMS:
60
+ case AIStreamAppErrorCode.CONNECTION_INVALID:
61
+ case AIStreamAppErrorCode.SESSION_ID_INVALID:
62
+ case AIStreamAppErrorCode.EVENT_ID_INVALID:
63
+ case AIStreamAppErrorCode.INVALID_DATA_PACKET:
64
+ case AIStreamAppErrorCode.FILE_DATA_READ_ERROR:
65
+ case AIStreamAppErrorCode.DATA_CHANNEL_NOT_FOUND:
66
+ return AIStreamErrorCode.INVALID_PARAMS;
67
+ case AIStreamAppErrorCode.HTTP_REQUEST_FAILED:
68
+ return AIStreamErrorCode.SESSION_CREATE_FAILED;
69
+ case AIStreamAppErrorCode.CONNECTION_CLOSED_BY_REMOTE:
70
+ return AIStreamErrorCode.CONNECTION_CLOSED;
71
+ case AIStreamServerErrorCode.OK:
72
+ case AIStreamServerErrorCode.CLOSE_BY_CLIENT:
73
+ case AIStreamServerErrorCode.CLOSE_BY_EXPIRE:
74
+ case AIStreamServerErrorCode.CLOSE_BY_IO:
75
+ case AIStreamServerErrorCode.CLOSE_BY_KEEPALIVE:
76
+ case AIStreamServerErrorCode.CLOSE_BY_REUSE:
77
+ return AIStreamErrorCode.CONNECTION_CLOSED;
78
+ case AIStreamServerErrorCode.BAD_REQUEST:
79
+ case AIStreamServerErrorCode.NOT_FOUND:
80
+ case AIStreamServerErrorCode.UNAUTHENTICATED:
81
+ return AIStreamErrorCode.INVALID_PARAMS;
82
+ case AIStreamServerErrorCode.INTERNAL_SERVER_ERROR:
83
+ return AIStreamErrorCode.UNKNOWN_ERROR;
84
+ case AIStreamServerErrorCode.REQUEST_TIMEOUT:
85
+ case AIStreamServerErrorCode.GATEWAY_TIMEOUT:
86
+ return AIStreamErrorCode.TIMEOUT;
87
+ }
88
+ return AIStreamErrorCode.UNKNOWN_ERROR;
89
+ };
90
+
91
+ // 函数实现
92
+ export async function tryCatchTTT(fn) {
93
+ const [error, result] = await tryCatch(fn);
94
+ if (error) {
95
+ let {
96
+ code,
97
+ message
98
+ } = error;
99
+ message = "".concat(message, " (original_code: ").concat(code || '-', ")");
100
+ code = transformErrorCode(code);
101
+ const e = removeTopStackFrame(new AIStreamError(message, code));
102
+ return [e, null];
41
103
  }
104
+ return [null, result];
42
105
  }
@@ -8,10 +8,6 @@ export interface SendBlocksToAIStreamParams {
8
8
  attribute?: AIStreamChatAttribute;
9
9
  signal?: AbortSignal;
10
10
  }
11
- export declare class AIStreamSessionError extends Error {
12
- readonly code: number;
13
- constructor(message: string, code: number);
14
- }
15
11
  export declare function sendBlocksToAIStream(params: SendBlocksToAIStreamParams): {
16
12
  response: StreamResponse;
17
13
  metaPromise: Promise<Record<string, any>>;
@@ -3,10 +3,10 @@ import "core-js/modules/es.json.stringify.js";
3
3
  import "core-js/modules/web.dom-collections.iterator.js";
4
4
  import '../polyfill';
5
5
  import { ReadableStream } from 'web-streams-polyfill';
6
- import { AIStreamAttributePayloadType, AIStreamAttributeType, AIStreamChatSysWorkflow, ConnectState, FileFormat, ReceivedTextPacketEof, ReceivedTextPacketType, SessionState, StreamFlag } from '../AIStreamTypes';
6
+ import { AIStreamAttributePayloadType, AIStreamAttributeType, AIStreamChatSysWorkflow, AIStreamErrorCode, ConnectState, FileFormat, ReceivedTextPacketEof, ReceivedTextPacketType, SessionState, StreamFlag } from '../AIStreamTypes';
7
7
  import { EmitterEvent, generateId, safeParseJSON, StreamResponse } from '@ray-js/t-agent';
8
8
  import { tryCatch } from './misc';
9
- import { AIStreamConnectionError } from './errors';
9
+ import { AIStreamError, transformErrorCode } from './errors';
10
10
  import logger from './logger';
11
11
  const mimeTypeToFormatMap = {
12
12
  'video/mp4': FileFormat.MP4,
@@ -14,13 +14,6 @@ const mimeTypeToFormatMap = {
14
14
  'application/json': FileFormat.JSON,
15
15
  'application/pdf': FileFormat.PDF
16
16
  };
17
- export class AIStreamSessionError extends Error {
18
- constructor(message, code) {
19
- super(message);
20
- this.name = 'AIStreamSessionError';
21
- this.code = code;
22
- }
23
- }
24
17
  export function sendBlocksToAIStream(params) {
25
18
  logger.debug('sendBlocksToAIStream start');
26
19
  const {
@@ -80,6 +73,20 @@ export function sendBlocksToAIStream(params) {
80
73
  }
81
74
  }
82
75
  };
76
+ const emitError = error => {
77
+ if (!error) {
78
+ error = new AIStreamError('Unknown error', AIStreamErrorCode.UNKNOWN_ERROR);
79
+ }
80
+ if (!(error instanceof AIStreamError)) {
81
+ error = new AIStreamError("AIStream Error: ".concat(error.message || 'Unknown error'), transformErrorCode(error.code));
82
+ }
83
+ enqueue({
84
+ type: 'error',
85
+ level: 'error',
86
+ error,
87
+ meta: {}
88
+ });
89
+ };
83
90
  let pendingCancel = false;
84
91
  if (audioEmitter) {
85
92
  audioEmitter.addEventListener('confirm', () => {
@@ -108,16 +115,10 @@ export function sendBlocksToAIStream(params) {
108
115
  }]
109
116
  }));
110
117
  if (error) {
111
- const e = new AIStreamSessionError(error.message, error.code || 0);
112
- enqueue({
113
- type: 'error',
114
- error: e,
115
- level: 'error',
116
- meta: {}
117
- });
118
+ emitError(error);
118
119
  if (audioEmitter) {
119
120
  audioEmitter.dispatchEvent(new EmitterEvent('error', {
120
- detail: e
121
+ detail: error
121
122
  }));
122
123
  }
123
124
  close();
@@ -167,10 +168,9 @@ export function sendBlocksToAIStream(params) {
167
168
  if (packet.data.text === '') {
168
169
  logger.debug('sendBlocksToAIStream Receive ASR EOF EMPTY');
169
170
  // 没识别出任何文本
171
+ const e = new AIStreamError('ASR data is empty', AIStreamErrorCode.ASR_EMPTY);
170
172
  audioEmitter.dispatchEvent(new EmitterEvent('error', {
171
- detail: {
172
- name: 'AsrEmptyError'
173
- }
173
+ detail: e
174
174
  }));
175
175
  } else {
176
176
  logger.debug('sendBlocksToAIStream Receive ASR EOF', packet.data.text);
@@ -225,13 +225,9 @@ export function sendBlocksToAIStream(params) {
225
225
  }
226
226
  } else if (data.type === 'sessionState') {
227
227
  if (data.body.sessionState === SessionState.CLOSED || data.body.sessionState === SessionState.CREATE_FAILED) {
228
- const e = new AIStreamSessionError('Session closed', data.body.code);
229
- enqueue({
230
- type: 'error',
231
- error: e,
232
- level: 'error',
233
- meta
234
- });
228
+ const msg = SessionState[data.body.sessionState];
229
+ const e = new AIStreamError("Session Error: ".concat(msg, ", (original_code: ").concat(data.body.code, ")"), transformErrorCode(data.body.code));
230
+ emitError(error);
235
231
  if (audioEmitter) {
236
232
  audioEmitter.dispatchEvent(new EmitterEvent('error', {
237
233
  detail: e
@@ -241,13 +237,8 @@ export function sendBlocksToAIStream(params) {
241
237
  }
242
238
  } else if (data.type === 'connectionState') {
243
239
  if (data.body.connectState === ConnectState.DISCONNECTED || data.body.connectState === ConnectState.CLOSED) {
244
- const e = new AIStreamConnectionError('Connection disconnected', data.body.code);
245
- enqueue({
246
- type: 'error',
247
- error: e,
248
- level: 'error',
249
- meta
250
- });
240
+ const e = new AIStreamError("Session disconnected, (original_code: ".concat(data.body.code, ")"), transformErrorCode(data.body.code));
241
+ emitError(error);
251
242
  if (audioEmitter) {
252
243
  audioEmitter.dispatchEvent(new EmitterEvent('error', {
253
244
  detail: e
@@ -269,14 +260,7 @@ export function sendBlocksToAIStream(params) {
269
260
  close();
270
261
  }
271
262
  });
272
- event.on('error', error => {
273
- enqueue({
274
- type: 'error',
275
- level: 'error',
276
- error: error instanceof Error ? error : new Error('Unknown error'),
277
- meta: {}
278
- });
279
- });
263
+ event.on('error', emitError);
280
264
  for (const block of blocks) {
281
265
  if (block.type === 'text') {
282
266
  event.write({
@@ -285,24 +269,24 @@ export function sendBlocksToAIStream(params) {
285
269
  type: 'text',
286
270
  message: block.text
287
271
  })
288
- });
272
+ }).catch(emitError);
289
273
  } else if (block.type === 'image_path') {
290
274
  event.write({
291
275
  type: 'image',
292
276
  path: block.image_path.path
293
- });
277
+ }).catch(emitError);
294
278
  } else if (block.type === 'file_path') {
295
279
  event.write({
296
280
  type: 'file',
297
281
  format: mimeTypeToFormatMap[block.file_url.mimeType] || 0,
298
282
  path: block.file_path.path
299
- });
283
+ }).catch(emitError);
300
284
  } else if (block.type === 'video_path') {
301
285
  event.write({
302
286
  type: 'file',
303
287
  format: FileFormat.MP4,
304
288
  path: block.file_path.path
305
- });
289
+ }).catch(emitError);
306
290
  }
307
291
  }
308
292
  if (audioEmitter) {
@@ -1,10 +1,11 @@
1
- import { ApiRequestByAtopParams, ApiRequestByHighwayParams, AudioBody, AuthorizeParams, AuthorizePolicyStatusParams, CanIUseRouterParams, CloseSessionParams, ConnectParams, ConnectStateBody, CreateSessionParams, DeleteRecordListParams, DisconnectParams, EventBody, EventChannelMessageParams, GetAppInfoParams, GetCurrentHomeInfoParams, GetMiniAppConfigParams, GetAccountInfoParams, ImageBody, InsertRecordParams, NavigateToMiniProgramParams, OpenInnerH5Params, OpenMiniWidgetParams, QueryAgentTokenParams, QueryRecordListParams, RecordAmplitudesBody, RegisterChannelParams, RouterParams, SendEventChatBreakParams, SendEventEndParams, SendEventPayloadEndParams, SendEventStartParams, SendImageDataParams, SendTextDataParams, SessionStateBody, StartRecordAndSendAudioDataParams, StopRecordAndSendAudioDataParams, TextBody, UpdateRecordParams, IsConnectedParams } from '../AIStreamTypes';
1
+ import { ApiRequestByAtopParams, ApiRequestByHighwayParams, AudioBody, AuthorizeParams, AuthorizePolicyStatusParams, CanIUseRouterParams, CloseSessionParams, ConnectParams, ConnectStateBody, CreateSessionParams, DeleteRecordListParams, DisconnectParams, EventBody, EventChannelMessageParams, GetAppInfoParams, GetCurrentHomeInfoParams, GetMiniAppConfigParams, GetAccountInfoParams, ImageBody, InsertRecordParams, NavigateToMiniProgramParams, OpenInnerH5Params, OpenMiniWidgetParams, QueryAgentTokenParams, QueryRecordListParams, RecordAmplitudesBody, RegisterChannelParams, RouterParams, SendEventChatBreakParams, SendEventEndParams, SendEventPayloadEndParams, SendEventStartParams, SendImageDataParams, SendTextDataParams, SessionStateBody, StartRecordAndSendAudioDataParams, StopRecordAndSendAudioDataParams, TextBody, UpdateRecordParams, IsConnectedParams, GetNetworkTypeParams, StartPlayAudioParams } from '../AIStreamTypes';
2
2
  export declare const getMiniAppConfig: (options?: Omit<GetMiniAppConfigParams, "success" | "fail"> | undefined) => Promise<{
3
3
  config: any;
4
4
  }>;
5
5
  export declare const getAccountInfo: (options?: Omit<GetAccountInfoParams, "success" | "fail"> | undefined) => Promise<{
6
6
  miniProgram: import("../AIStreamTypes").MiniProgramAccountInfo;
7
7
  }>;
8
+ export declare const systemInfo: any;
8
9
  export declare const isDevTools: () => boolean;
9
10
  export declare const apiRequestByAtop: (options?: Omit<ApiRequestByAtopParams, "success" | "fail"> | undefined) => Promise<{
10
11
  thing_json_?: any;
@@ -85,7 +86,11 @@ export declare const registerRecordAmplitudes: (options?: Omit<{
85
86
  count: number;
86
87
  }, "success" | "fail"> | undefined) => Promise<never>;
87
88
  export declare const unregisterVoiceAmplitudes: (options?: Omit<any, "success" | "fail"> | undefined) => Promise<unknown>;
89
+ export declare const startPlayAudio: (options?: Omit<StartPlayAudioParams, "success" | "fail"> | undefined) => Promise<null>;
88
90
  export declare const listenEventReceived: (listener: (params: EventBody) => void) => () => void;
91
+ export declare const listenAudioPlayEnd: (listener: (params: {
92
+ path: string;
93
+ }) => void) => () => void;
89
94
  export declare const listenAudioReceived: (listener: (params: AudioBody) => void) => () => void;
90
95
  export declare const listenTextReceived: (listener: (params: TextBody) => void) => () => void;
91
96
  export declare const listenImageReceived: (listener: (params: ImageBody) => void) => () => void;
@@ -101,3 +106,7 @@ export declare const updateRecord: (options?: Omit<UpdateRecordParams, "success"
101
106
  export declare const insertRecord: (options?: Omit<InsertRecordParams, "success" | "fail"> | undefined) => Promise<{
102
107
  id: number;
103
108
  }>;
109
+ export declare const getNetworkType: (options?: Omit<GetNetworkTypeParams, "success" | "fail"> | undefined) => Promise<{
110
+ networkType: import("../AIStreamTypes").NetworkType;
111
+ signalStrength: number;
112
+ }>;
package/dist/utils/ttt.js CHANGED
@@ -1,13 +1,14 @@
1
1
  import { listening, promisify } from './promisify';
2
2
  export const getMiniAppConfig = promisify(ty.getMiniAppConfig, true);
3
3
  export const getAccountInfo = promisify(ty.getAccountInfo);
4
+ export const systemInfo = ty.getSystemInfoSync();
4
5
  export const isDevTools = () => {
5
- return ty.getSystemInfoSync().brand === 'devtools';
6
+ return systemInfo.brand === 'devtools';
6
7
  };
7
8
  export const apiRequestByAtop = promisify(ty.apiRequestByAtop);
8
9
  export const apiRequestByHighway = promisify(ty.apiRequestByHighway);
9
10
  export const createNativeEventManager = options => {
10
- if (isDevTools) {
11
+ if (isDevTools()) {
11
12
  return {
12
13
  onListener: () => null,
13
14
  offerListener: () => null
@@ -55,10 +56,12 @@ export const sendImageData = promisify(ty.aistream.sendImageData, true);
55
56
  export const sendTextData = promisify(ty.aistream.sendTextData, true);
56
57
  export const registerRecordAmplitudes = promisify(ty.aistream.registerRecordAmplitudes, true);
57
58
  export const unregisterVoiceAmplitudes = promisify(ty.aistream.unregisterVoiceAmplitudes, true);
59
+ export const startPlayAudio = promisify(ty.aistream.startPlayAudio, true);
58
60
 
59
61
  // export const sendFileData = promisify<SendFileDataParams>(ty.aistream.sendFileData);
60
62
 
61
63
  export const listenEventReceived = listening(ty.aistream.onEventReceived, ty.aistream.offEventReceived, true);
64
+ export const listenAudioPlayEnd = listening(ty.aistream.onAudioPlayEnd, ty.aistream.onAudioPlayEnd, true);
62
65
  export const listenAudioReceived = listening(ty.aistream.onAudioReceived, ty.aistream.offAudioReceived, true);
63
66
 
64
67
  // export const listenVideoReceived = listening<VideoBody>(
@@ -82,4 +85,5 @@ export const listenRecordAmplitudes = listening(ty.aistream.onRecordAmplitudes,
82
85
  export const queryRecordList = promisify(ty.aistream.queryRecordList, true);
83
86
  export const deleteRecordList = promisify(ty.aistream.deleteRecordList, true);
84
87
  export const updateRecord = promisify(ty.aistream.updateRecord, true);
85
- export const insertRecord = promisify(ty.aistream.insertRecord, true);
88
+ export const insertRecord = promisify(ty.aistream.insertRecord, true);
89
+ export const getNetworkType = promisify(ty.getNetworkType, true);
@@ -43,7 +43,7 @@ export declare function withAIStream(options?: AIStreamOptions): (agent: ChatAge
43
43
  hooks: import("hookable").Hookable<any, string>;
44
44
  aiStream: {
45
45
  send: (blocks: InputBlock[], signal?: AbortSignal, extraOptions?: Record<string, any>) => {
46
- response: import("@ray-js/t-agent").StreamResponse;
46
+ response: import("@ray-js/t-agent").StreamResponse; /** 是否将输入块传递给智能体,默认为 true,设置为 false 时,需要你自己编写 onInputBlocksPush Hook 来处理输入块 */
47
47
  metaPromise: Promise<Record<string, any>>;
48
48
  };
49
49
  chat: (blocks: InputBlock[], signal?: AbortSignal, options?: {
@@ -344,8 +344,10 @@ export function withAIStream() {
344
344
  }
345
345
  if (message.bubble.text) {
346
346
  await message.persist();
347
- } else {
347
+ } else if (message.bubble.status === BubbleTileStatus.NORMAL) {
348
348
  await message.remove();
349
+ } else {
350
+ await message.persist();
349
351
  }
350
352
  return [userMsg, ...result.messages];
351
353
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/t-agent-plugin-aistream",
3
- "version": "0.2.0-beta-17",
3
+ "version": "0.2.0-beta.0",
4
4
  "author": "Tuya.inc",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -35,5 +35,5 @@
35
35
  "devDependencies": {
36
36
  "@types/url-parse": "^1.4.11"
37
37
  },
38
- "gitHead": "32e3d3ae017d11825e0bfd4dc4441abe67bd8ef0"
38
+ "gitHead": "0b5d552289721444563828b03a51a6e8695779db"
39
39
  }