@ray-js/t-agent-plugin-aistream 0.2.0-beta-16 → 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;
@@ -42,6 +43,7 @@ interface AIStreamSessionOptions {
42
43
  userData?: Attribute[];
43
44
  }
44
45
  interface AIStreamEventOptions {
46
+ signal?: AbortSignal;
45
47
  userData?: Attribute[];
46
48
  }
47
49
  export declare class AIStreamSession {
@@ -123,15 +125,15 @@ export declare class AIStreamEvent {
123
125
  }): void;
124
126
  on(name: 'close', callback: () => void): void;
125
127
  on(name: 'finish', callback: () => void): void;
126
- on(name: 'error', callback: (error: any) => void): void;
128
+ on(name: 'error', callback: (error: AIStreamError) => void): void;
127
129
  on(name: 'data', callback: (entry: AIStreamDataEntry) => void): void;
128
130
  off(name: 'finish', callback: () => void): void;
129
131
  off(name: 'close', callback: () => void): void;
130
- off(name: 'error', callback: (error: any) => void): void;
132
+ off(name: 'error', callback: (error: AIStreamError) => void): void;
131
133
  off(name: 'data', callback: (entry: AIStreamDataEntry) => void): void;
132
134
  emit(name: 'finish'): void;
133
135
  emit(name: 'close'): void;
134
- emit(name: 'error', data: Error): void;
136
+ emit(name: 'error', data: AIStreamError): void;
135
137
  emit(name: 'data', data: AIStreamDataEntry): void;
136
138
  private dispose;
137
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,37 +201,55 @@ 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
  })();
@@ -226,17 +258,30 @@ export class AIStreamSession {
226
258
  async startEvent() {
227
259
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
228
260
  if (this.disposed) {
229
- throw new AIStreamEventError('Event has been disposed', AIStreamErrorCode.AIStreamError);
261
+ throw new AIStreamError('Event has been disposed', AIStreamErrorCode.EVENT_DISPOSED);
230
262
  }
231
263
  if (this.activeEvent) {
232
- 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);
233
265
  }
266
+ const {
267
+ signal
268
+ } = options,
269
+ rest = _objectWithoutProperties(options, _excluded);
234
270
  await this.ensureSession();
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
+ }
235
282
  const {
236
283
  eventId
237
- } = await sendEventStart(_objectSpread({
238
- sessionId: this.sessionId
239
- }, options));
284
+ } = result;
240
285
  this.activeEvent = new AIStreamEvent({
241
286
  eventId,
242
287
  sessionId: this.sessionId,
@@ -316,13 +361,13 @@ export class AIStreamEvent {
316
361
  findFirstCode(type) {
317
362
  const code = this.sendDataChannels.find(code => code.startsWith(type));
318
363
  if (!code) {
319
- 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);
320
365
  }
321
366
  return code;
322
367
  }
323
368
  write(chunk) {
324
369
  if (this.closed) {
325
- throw new AIStreamEventError('Cannot write to a closed event', AIStreamErrorCode.AIStreamError);
370
+ throw new AIStreamError('Cannot write to a closed event', AIStreamErrorCode.EVENT_CLOSED);
326
371
  }
327
372
  const dataChannel = chunk.dataChannel || this.findFirstCode(chunk.type);
328
373
  let promise = this.chains[dataChannel];
@@ -334,13 +379,17 @@ export class AIStreamEvent {
334
379
  return;
335
380
  }
336
381
  if (chunk.type === 'text') {
337
- await sendTextData({
382
+ const [error] = await tryCatchTTT(() => sendTextData({
338
383
  sessionId: this.sessionId,
339
384
  text: chunk.text,
340
385
  dataChannel,
341
386
  userData: chunk.userData
342
- });
387
+ }));
388
+ if (error) {
389
+ throw error;
390
+ }
343
391
  } else if (chunk.type === 'file') {
392
+ logger.warn('File data sending is not implemented yet');
344
393
  // await sendFileData({
345
394
  // sessionId: this.sessionId,
346
395
  // path: chunk.path,
@@ -349,11 +398,12 @@ export class AIStreamEvent {
349
398
  // userData: chunk.userData,
350
399
  // });
351
400
  } else if (chunk.type === 'image') {
352
- await sendImageData({
401
+ const [error] = await tryCatchTTT(() => sendImageData({
353
402
  sessionId: this.sessionId,
354
403
  path: chunk.path,
355
404
  userData: chunk.userData
356
- });
405
+ }));
406
+ throw error;
357
407
  }
358
408
  sendEventPayloadEnd({
359
409
  eventId: this.eventId,
@@ -361,18 +411,19 @@ export class AIStreamEvent {
361
411
  dataChannel
362
412
  });
363
413
  }).catch(error => {
364
- this.emit('error', error);
365
- throw error;
414
+ const e = new AIStreamError(error.message, error.code || AIStreamErrorCode.EVENT_WRITE_FAILED);
415
+ this.emit('error', e);
416
+ throw e;
366
417
  });
367
418
  return this.chains[dataChannel];
368
419
  }
369
420
  stream(source) {
370
421
  if (this.closed) {
371
- throw new AIStreamEventError('Cannot stream to a closed event', AIStreamErrorCode.AIStreamError);
422
+ throw new AIStreamError('Cannot stream to a closed event', AIStreamErrorCode.EVENT_CLOSED);
372
423
  }
373
424
  const dataChannel = source.dataChannel || this.findFirstCode(source.type);
374
425
  if (this.streams[dataChannel]) {
375
- throw new AIStreamEventError("".concat(dataChannel, " stream already exists"), AIStreamErrorCode.AIStreamError);
426
+ throw new AIStreamError("".concat(dataChannel, " stream already exists"), AIStreamErrorCode.STREAM_EXISTS);
376
427
  }
377
428
  let startPromise = null;
378
429
  const stream = {
@@ -381,7 +432,7 @@ export class AIStreamEvent {
381
432
  started: false,
382
433
  start: async () => {
383
434
  if (this.closed) {
384
- throw new AIStreamEventError('Cannot stream to a closed event', AIStreamErrorCode.AIStreamError);
435
+ throw new AIStreamError('Cannot stream to a closed event', AIStreamErrorCode.EVENT_CLOSED);
385
436
  }
386
437
  if (stream.started) {
387
438
  return;
@@ -414,11 +465,7 @@ export class AIStreamEvent {
414
465
  }
415
466
  // 一定要等录音开始,才能结束
416
467
  if (startPromise) {
417
- try {
418
- await startPromise;
419
- } catch (e) {
420
- // pass
421
- }
468
+ await tryCatchTTT(() => startPromise);
422
469
  startPromise = null;
423
470
  }
424
471
  if (source.type === 'audio') {
@@ -433,6 +480,7 @@ export class AIStreamEvent {
433
480
  userData: source.userData
434
481
  });
435
482
  } else if (source.type === 'video') {
483
+ logger.warn('Video data sending is not implemented yet');
436
484
  // const cameraType = source.cameraType || VideoCameraType.BACK;
437
485
  // await stopRecordAndSendVideoData({
438
486
  // dataChannel,
@@ -475,7 +523,7 @@ export class AIStreamEvent {
475
523
  userData: options === null || options === void 0 ? void 0 : options.userData
476
524
  });
477
525
  }
478
- const error = new Error('This operation was aborted');
526
+ const error = new AIStreamError('This operation was aborted', AIStreamErrorCode.EVENT_ABORTED);
479
527
  error.name = 'AbortError';
480
528
  // 发送 break 后,stream 自动会关掉,在这里提前关闭
481
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', () => {
@@ -100,6 +107,7 @@ export function sendBlocksToAIStream(params) {
100
107
  }
101
108
  let error;
102
109
  [error, event] = await tryCatch(() => session.startEvent({
110
+ signal,
103
111
  userData: [{
104
112
  type: AIStreamAttributeType.AI_CHAT,
105
113
  payloadType: AIStreamAttributePayloadType.STRING,
@@ -107,16 +115,10 @@ export function sendBlocksToAIStream(params) {
107
115
  }]
108
116
  }));
109
117
  if (error) {
110
- const e = new AIStreamSessionError(error.message, error.code || 0);
111
- enqueue({
112
- type: 'error',
113
- error: e,
114
- level: 'error',
115
- meta: {}
116
- });
118
+ emitError(error);
117
119
  if (audioEmitter) {
118
120
  audioEmitter.dispatchEvent(new EmitterEvent('error', {
119
- detail: e
121
+ detail: error
120
122
  }));
121
123
  }
122
124
  close();
@@ -166,10 +168,9 @@ export function sendBlocksToAIStream(params) {
166
168
  if (packet.data.text === '') {
167
169
  logger.debug('sendBlocksToAIStream Receive ASR EOF EMPTY');
168
170
  // 没识别出任何文本
171
+ const e = new AIStreamError('ASR data is empty', AIStreamErrorCode.ASR_EMPTY);
169
172
  audioEmitter.dispatchEvent(new EmitterEvent('error', {
170
- detail: {
171
- name: 'AsrEmptyError'
172
- }
173
+ detail: e
173
174
  }));
174
175
  } else {
175
176
  logger.debug('sendBlocksToAIStream Receive ASR EOF', packet.data.text);
@@ -224,13 +225,9 @@ export function sendBlocksToAIStream(params) {
224
225
  }
225
226
  } else if (data.type === 'sessionState') {
226
227
  if (data.body.sessionState === SessionState.CLOSED || data.body.sessionState === SessionState.CREATE_FAILED) {
227
- const e = new AIStreamSessionError('Session closed', data.body.code);
228
- enqueue({
229
- type: 'error',
230
- error: e,
231
- level: 'error',
232
- meta
233
- });
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);
234
231
  if (audioEmitter) {
235
232
  audioEmitter.dispatchEvent(new EmitterEvent('error', {
236
233
  detail: e
@@ -240,13 +237,8 @@ export function sendBlocksToAIStream(params) {
240
237
  }
241
238
  } else if (data.type === 'connectionState') {
242
239
  if (data.body.connectState === ConnectState.DISCONNECTED || data.body.connectState === ConnectState.CLOSED) {
243
- const e = new AIStreamConnectionError('Connection disconnected', data.body.code);
244
- enqueue({
245
- type: 'error',
246
- error: e,
247
- level: 'error',
248
- meta
249
- });
240
+ const e = new AIStreamError("Session disconnected, (original_code: ".concat(data.body.code, ")"), transformErrorCode(data.body.code));
241
+ emitError(error);
250
242
  if (audioEmitter) {
251
243
  audioEmitter.dispatchEvent(new EmitterEvent('error', {
252
244
  detail: e
@@ -268,14 +260,7 @@ export function sendBlocksToAIStream(params) {
268
260
  close();
269
261
  }
270
262
  });
271
- event.on('error', error => {
272
- enqueue({
273
- type: 'error',
274
- level: 'error',
275
- error: error instanceof Error ? error : new Error('Unknown error'),
276
- meta: {}
277
- });
278
- });
263
+ event.on('error', emitError);
279
264
  for (const block of blocks) {
280
265
  if (block.type === 'text') {
281
266
  event.write({
@@ -284,24 +269,24 @@ export function sendBlocksToAIStream(params) {
284
269
  type: 'text',
285
270
  message: block.text
286
271
  })
287
- });
272
+ }).catch(emitError);
288
273
  } else if (block.type === 'image_path') {
289
274
  event.write({
290
275
  type: 'image',
291
276
  path: block.image_path.path
292
- });
277
+ }).catch(emitError);
293
278
  } else if (block.type === 'file_path') {
294
279
  event.write({
295
280
  type: 'file',
296
281
  format: mimeTypeToFormatMap[block.file_url.mimeType] || 0,
297
282
  path: block.file_path.path
298
- });
283
+ }).catch(emitError);
299
284
  } else if (block.type === 'video_path') {
300
285
  event.write({
301
286
  type: 'file',
302
287
  format: FileFormat.MP4,
303
288
  path: block.file_path.path
304
- });
289
+ }).catch(emitError);
305
290
  }
306
291
  }
307
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-16",
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": "d9abc8e193dc1db6a7f9fca9ebeba2ff6462b713"
38
+ "gitHead": "0b5d552289721444563828b03a51a6e8695779db"
39
39
  }