@decartai/sdk 0.0.45 → 0.0.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -1
- package/dist/realtime/client.d.ts +5 -1
- package/dist/realtime/client.js +68 -30
- package/dist/realtime/event-buffer.js +35 -0
- package/dist/realtime/subscribe-client.d.ts +22 -0
- package/dist/realtime/subscribe-client.js +20 -0
- package/dist/realtime/webrtc-connection.js +24 -5
- package/dist/realtime/webrtc-manager.js +6 -4
- package/package.json +1 -2
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { QueueClient } from "./queue/client.js";
|
|
|
6
6
|
import { DecartSDKError, ERROR_CODES } from "./utils/errors.js";
|
|
7
7
|
import { ConnectionState } from "./realtime/types.js";
|
|
8
8
|
import { SetInput } from "./realtime/methods.js";
|
|
9
|
+
import { RealTimeSubscribeClient, SubscribeEvents, SubscribeOptions } from "./realtime/subscribe-client.js";
|
|
9
10
|
import { AvatarOptions, Events, RealTimeClient, RealTimeClientConnectOptions, RealTimeClientInitialState } from "./realtime/client.js";
|
|
10
11
|
import { ModelState } from "./shared/types.js";
|
|
11
12
|
import { CreateTokenResponse, TokensClient } from "./tokens/client.js";
|
|
@@ -46,6 +47,7 @@ type DecartClientOptions = {
|
|
|
46
47
|
declare const createDecartClient: (options?: DecartClientOptions) => {
|
|
47
48
|
realtime: {
|
|
48
49
|
connect: (stream: MediaStream | null, options: RealTimeClientConnectOptions) => Promise<RealTimeClient>;
|
|
50
|
+
subscribe: (options: SubscribeOptions) => Promise<RealTimeSubscribeClient>;
|
|
49
51
|
};
|
|
50
52
|
/**
|
|
51
53
|
* Client for synchronous image generation.
|
|
@@ -119,4 +121,4 @@ declare const createDecartClient: (options?: DecartClientOptions) => {
|
|
|
119
121
|
tokens: TokensClient;
|
|
120
122
|
};
|
|
121
123
|
//#endregion
|
|
122
|
-
export { type AvatarOptions, type ConnectionState, type CreateTokenResponse, DecartClientOptions, type DecartSDKError, ERROR_CODES, type FileInput, type ImageModelDefinition, type ImageModels, type JobStatus, type JobStatusResponse, type JobSubmitResponse, type Model, type ModelDefinition, type ModelState, type ProcessClient, type ProcessOptions, type QueueClient, type QueueJobResult, type QueueSubmitAndPollOptions, type QueueSubmitOptions, type ReactNativeFile, type RealTimeClient, type RealTimeClientConnectOptions, type RealTimeClientInitialState, type Events as RealTimeEvents, type RealTimeModels, type SetInput, type TokensClient, type VideoModelDefinition, type VideoModels, createDecartClient, isImageModel, isRealtimeModel, isVideoModel, models };
|
|
124
|
+
export { type AvatarOptions, type ConnectionState, type CreateTokenResponse, DecartClientOptions, type DecartSDKError, ERROR_CODES, type FileInput, type ImageModelDefinition, type ImageModels, type JobStatus, type JobStatusResponse, type JobSubmitResponse, type Model, type ModelDefinition, type ModelState, type ProcessClient, type ProcessOptions, type QueueClient, type QueueJobResult, type QueueSubmitAndPollOptions, type QueueSubmitOptions, type ReactNativeFile, type RealTimeClient, type RealTimeClientConnectOptions, type RealTimeClientInitialState, type Events as RealTimeEvents, type RealTimeModels, type RealTimeSubscribeClient, type SetInput, type SubscribeEvents, type SubscribeOptions, type TokensClient, type VideoModelDefinition, type VideoModels, createDecartClient, isImageModel, isRealtimeModel, isVideoModel, models };
|
|
@@ -43,6 +43,9 @@ type RealTimeClientConnectOptions = z.infer<typeof realTimeClientConnectOptionsS
|
|
|
43
43
|
type Events = {
|
|
44
44
|
connectionChange: ConnectionState;
|
|
45
45
|
error: DecartSDKError;
|
|
46
|
+
generationTick: {
|
|
47
|
+
seconds: number;
|
|
48
|
+
};
|
|
46
49
|
};
|
|
47
50
|
type RealTimeClient = {
|
|
48
51
|
set: (input: SetInput) => Promise<void>;
|
|
@@ -56,7 +59,8 @@ type RealTimeClient = {
|
|
|
56
59
|
disconnect: () => void;
|
|
57
60
|
on: <K extends keyof Events>(event: K, listener: (data: Events[K]) => void) => void;
|
|
58
61
|
off: <K extends keyof Events>(event: K, listener: (data: Events[K]) => void) => void;
|
|
59
|
-
sessionId: string;
|
|
62
|
+
sessionId: string | null;
|
|
63
|
+
subscribeToken: string | null;
|
|
60
64
|
setImage: (image: Blob | File | string | null, options?: {
|
|
61
65
|
prompt?: string;
|
|
62
66
|
enhance?: boolean;
|
package/dist/realtime/client.js
CHANGED
|
@@ -2,11 +2,11 @@ import { createWebrtcError } from "../utils/errors.js";
|
|
|
2
2
|
import { modelDefinitionSchema } from "../shared/model.js";
|
|
3
3
|
import { modelStateSchema } from "../shared/types.js";
|
|
4
4
|
import { AudioStreamManager } from "./audio-stream-manager.js";
|
|
5
|
+
import { createEventBuffer } from "./event-buffer.js";
|
|
5
6
|
import { realtimeMethods } from "./methods.js";
|
|
7
|
+
import { decodeSubscribeToken, encodeSubscribeToken } from "./subscribe-client.js";
|
|
6
8
|
import { WebRTCManager } from "./webrtc-manager.js";
|
|
7
9
|
import { z } from "zod";
|
|
8
|
-
import mitt from "mitt";
|
|
9
|
-
import { v4 } from "uuid";
|
|
10
10
|
|
|
11
11
|
//#region src/realtime/client.ts
|
|
12
12
|
async function blobToBase64(blob) {
|
|
@@ -66,10 +66,8 @@ const realTimeClientConnectOptionsSchema = z.object({
|
|
|
66
66
|
const createRealTimeClient = (opts) => {
|
|
67
67
|
const { baseUrl, apiKey, integration } = opts;
|
|
68
68
|
const connect = async (stream, options) => {
|
|
69
|
-
const eventEmitter = mitt();
|
|
70
69
|
const parsedOptions = realTimeClientConnectOptionsSchema.safeParse(options);
|
|
71
70
|
if (!parsedOptions.success) throw parsedOptions.error;
|
|
72
|
-
const sessionId = v4();
|
|
73
71
|
const isAvatarLive = options.model.name === "live_avatar";
|
|
74
72
|
const { onRemoteStream, initialState, avatar } = parsedOptions.data;
|
|
75
73
|
let audioStreamManager;
|
|
@@ -91,22 +89,7 @@ const createRealTimeClient = (opts) => {
|
|
|
91
89
|
enhance: initialState.prompt.enhance
|
|
92
90
|
} : void 0;
|
|
93
91
|
const url = `${baseUrl}${options.model.urlPath}`;
|
|
94
|
-
const
|
|
95
|
-
let buffering = true;
|
|
96
|
-
const emitOrBuffer = (event, data) => {
|
|
97
|
-
if (buffering) eventBuffer.push({
|
|
98
|
-
event,
|
|
99
|
-
data
|
|
100
|
-
});
|
|
101
|
-
else eventEmitter.emit(event, data);
|
|
102
|
-
};
|
|
103
|
-
const flushBufferedEvents = () => {
|
|
104
|
-
setTimeout(() => {
|
|
105
|
-
buffering = false;
|
|
106
|
-
for (const { event, data } of eventBuffer) eventEmitter.emit(event, data);
|
|
107
|
-
eventBuffer.length = 0;
|
|
108
|
-
}, 0);
|
|
109
|
-
};
|
|
92
|
+
const { emitter: eventEmitter, emitOrBuffer, flush, stop } = createEventBuffer();
|
|
110
93
|
webrtcManager = new WebRTCManager({
|
|
111
94
|
webrtcUrl: `${url}?api_key=${encodeURIComponent(apiKey)}&model=${encodeURIComponent(options.model.name)}`,
|
|
112
95
|
integration,
|
|
@@ -126,6 +109,17 @@ const createRealTimeClient = (opts) => {
|
|
|
126
109
|
initialPrompt
|
|
127
110
|
});
|
|
128
111
|
const manager = webrtcManager;
|
|
112
|
+
let sessionId = null;
|
|
113
|
+
let subscribeToken = null;
|
|
114
|
+
const sessionIdListener = (msg) => {
|
|
115
|
+
subscribeToken = encodeSubscribeToken(msg.session_id, msg.server_ip, msg.server_port);
|
|
116
|
+
sessionId = msg.session_id;
|
|
117
|
+
};
|
|
118
|
+
manager.getWebsocketMessageEmitter().on("sessionId", sessionIdListener);
|
|
119
|
+
const tickListener = (msg) => {
|
|
120
|
+
emitOrBuffer("generationTick", { seconds: msg.seconds });
|
|
121
|
+
};
|
|
122
|
+
manager.getWebsocketMessageEmitter().on("generationTick", tickListener);
|
|
129
123
|
await manager.connect(inputStream);
|
|
130
124
|
const methods = realtimeMethods(manager, imageToBase64);
|
|
131
125
|
if (!isAvatarLive && initialState?.prompt) {
|
|
@@ -138,25 +132,29 @@ const createRealTimeClient = (opts) => {
|
|
|
138
132
|
isConnected: () => manager.isConnected(),
|
|
139
133
|
getConnectionState: () => manager.getConnectionState(),
|
|
140
134
|
disconnect: () => {
|
|
141
|
-
|
|
142
|
-
eventBuffer.length = 0;
|
|
135
|
+
stop();
|
|
143
136
|
manager.cleanup();
|
|
144
137
|
audioStreamManager?.cleanup();
|
|
145
138
|
},
|
|
146
139
|
on: eventEmitter.on,
|
|
147
140
|
off: eventEmitter.off,
|
|
148
|
-
sessionId
|
|
149
|
-
|
|
150
|
-
|
|
141
|
+
get sessionId() {
|
|
142
|
+
return sessionId;
|
|
143
|
+
},
|
|
144
|
+
get subscribeToken() {
|
|
145
|
+
return subscribeToken;
|
|
146
|
+
},
|
|
147
|
+
setImage: async (image, options) => {
|
|
148
|
+
if (image === null) return manager.setImage(null, options);
|
|
151
149
|
const base64 = await imageToBase64(image);
|
|
152
|
-
return manager.setImage(base64, options
|
|
150
|
+
return manager.setImage(base64, options);
|
|
153
151
|
}
|
|
154
152
|
};
|
|
155
153
|
if (isAvatarLive && audioStreamManager) {
|
|
156
|
-
const manager
|
|
157
|
-
client.playAudio = (audio) => manager
|
|
154
|
+
const manager = audioStreamManager;
|
|
155
|
+
client.playAudio = (audio) => manager.playAudio(audio);
|
|
158
156
|
}
|
|
159
|
-
|
|
157
|
+
flush();
|
|
160
158
|
return client;
|
|
161
159
|
} catch (error) {
|
|
162
160
|
webrtcManager?.cleanup();
|
|
@@ -164,7 +162,47 @@ const createRealTimeClient = (opts) => {
|
|
|
164
162
|
throw error;
|
|
165
163
|
}
|
|
166
164
|
};
|
|
167
|
-
|
|
165
|
+
const subscribe = async (options) => {
|
|
166
|
+
const { sid, ip, port } = decodeSubscribeToken(options.token);
|
|
167
|
+
const subscribeUrl = `${baseUrl}/subscribe/${encodeURIComponent(sid)}?IP=${encodeURIComponent(ip)}&port=${encodeURIComponent(port)}&api_key=${encodeURIComponent(apiKey)}`;
|
|
168
|
+
const { emitter: eventEmitter, emitOrBuffer, flush, stop } = createEventBuffer();
|
|
169
|
+
let webrtcManager;
|
|
170
|
+
try {
|
|
171
|
+
webrtcManager = new WebRTCManager({
|
|
172
|
+
webrtcUrl: subscribeUrl,
|
|
173
|
+
integration,
|
|
174
|
+
onRemoteStream: options.onRemoteStream,
|
|
175
|
+
onConnectionStateChange: (state) => {
|
|
176
|
+
emitOrBuffer("connectionChange", state);
|
|
177
|
+
},
|
|
178
|
+
onError: (error) => {
|
|
179
|
+
console.error("WebRTC subscribe error:", error);
|
|
180
|
+
emitOrBuffer("error", createWebrtcError(error));
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
const manager = webrtcManager;
|
|
184
|
+
await manager.connect(null);
|
|
185
|
+
const client = {
|
|
186
|
+
isConnected: () => manager.isConnected(),
|
|
187
|
+
getConnectionState: () => manager.getConnectionState(),
|
|
188
|
+
disconnect: () => {
|
|
189
|
+
stop();
|
|
190
|
+
manager.cleanup();
|
|
191
|
+
},
|
|
192
|
+
on: eventEmitter.on,
|
|
193
|
+
off: eventEmitter.off
|
|
194
|
+
};
|
|
195
|
+
flush();
|
|
196
|
+
return client;
|
|
197
|
+
} catch (error) {
|
|
198
|
+
webrtcManager?.cleanup();
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
return {
|
|
203
|
+
connect,
|
|
204
|
+
subscribe
|
|
205
|
+
};
|
|
168
206
|
};
|
|
169
207
|
|
|
170
208
|
//#endregion
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import mitt from "mitt";
|
|
2
|
+
|
|
3
|
+
//#region src/realtime/event-buffer.ts
|
|
4
|
+
function createEventBuffer() {
|
|
5
|
+
const emitter = mitt();
|
|
6
|
+
const buffer = [];
|
|
7
|
+
let buffering = true;
|
|
8
|
+
const emitOrBuffer = (event, data) => {
|
|
9
|
+
if (buffering) buffer.push({
|
|
10
|
+
event,
|
|
11
|
+
data
|
|
12
|
+
});
|
|
13
|
+
else emitter.emit(event, data);
|
|
14
|
+
};
|
|
15
|
+
const flush = () => {
|
|
16
|
+
setTimeout(() => {
|
|
17
|
+
buffering = false;
|
|
18
|
+
for (const { event, data } of buffer) emitter.emit(event, data);
|
|
19
|
+
buffer.length = 0;
|
|
20
|
+
}, 0);
|
|
21
|
+
};
|
|
22
|
+
const stop = () => {
|
|
23
|
+
buffering = false;
|
|
24
|
+
buffer.length = 0;
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
emitter,
|
|
28
|
+
emitOrBuffer,
|
|
29
|
+
flush,
|
|
30
|
+
stop
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { createEventBuffer };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DecartSDKError } from "../utils/errors.js";
|
|
2
|
+
import { ConnectionState } from "./types.js";
|
|
3
|
+
|
|
4
|
+
//#region src/realtime/subscribe-client.d.ts
|
|
5
|
+
|
|
6
|
+
type SubscribeEvents = {
|
|
7
|
+
connectionChange: ConnectionState;
|
|
8
|
+
error: DecartSDKError;
|
|
9
|
+
};
|
|
10
|
+
type RealTimeSubscribeClient = {
|
|
11
|
+
isConnected: () => boolean;
|
|
12
|
+
getConnectionState: () => ConnectionState;
|
|
13
|
+
disconnect: () => void;
|
|
14
|
+
on: <K extends keyof SubscribeEvents>(event: K, listener: (data: SubscribeEvents[K]) => void) => void;
|
|
15
|
+
off: <K extends keyof SubscribeEvents>(event: K, listener: (data: SubscribeEvents[K]) => void) => void;
|
|
16
|
+
};
|
|
17
|
+
type SubscribeOptions = {
|
|
18
|
+
token: string;
|
|
19
|
+
onRemoteStream: (stream: MediaStream) => void;
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { RealTimeSubscribeClient, SubscribeEvents, SubscribeOptions };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/realtime/subscribe-client.ts
|
|
2
|
+
function encodeSubscribeToken(sessionId, serverIp, serverPort) {
|
|
3
|
+
return btoa(JSON.stringify({
|
|
4
|
+
sid: sessionId,
|
|
5
|
+
ip: serverIp,
|
|
6
|
+
port: serverPort
|
|
7
|
+
}));
|
|
8
|
+
}
|
|
9
|
+
function decodeSubscribeToken(token) {
|
|
10
|
+
try {
|
|
11
|
+
const payload = JSON.parse(atob(token));
|
|
12
|
+
if (!payload.sid || !payload.ip || !payload.port) throw new Error("Invalid subscribe token format");
|
|
13
|
+
return payload;
|
|
14
|
+
} catch {
|
|
15
|
+
throw new Error("Invalid subscribe token");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
export { decodeSubscribeToken, encodeSubscribeToken };
|
|
@@ -98,6 +98,15 @@ var WebRTCConnection = class {
|
|
|
98
98
|
this.setState("generating");
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
101
|
+
if (msg.type === "generation_tick") {
|
|
102
|
+
this.websocketMessagesEmitter.emit("generationTick", msg);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (msg.type === "generation_ended") return;
|
|
106
|
+
if (msg.type === "session_id") {
|
|
107
|
+
this.websocketMessagesEmitter.emit("sessionId", msg);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
101
110
|
if (!this.pc) return;
|
|
102
111
|
switch (msg.type) {
|
|
103
112
|
case "ready": {
|
|
@@ -225,7 +234,6 @@ var WebRTCConnection = class {
|
|
|
225
234
|
}
|
|
226
235
|
}
|
|
227
236
|
async setupNewPeerConnection(turnConfig) {
|
|
228
|
-
if (!this.localStream) throw new Error("No local stream found");
|
|
229
237
|
if (this.pc) {
|
|
230
238
|
this.pc.getSenders().forEach((sender) => {
|
|
231
239
|
if (sender.track && this.pc) this.pc.removeTrack(sender);
|
|
@@ -240,12 +248,23 @@ var WebRTCConnection = class {
|
|
|
240
248
|
});
|
|
241
249
|
this.pc = new RTCPeerConnection({ iceServers });
|
|
242
250
|
this.setState("connecting");
|
|
243
|
-
if (this.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
251
|
+
if (this.localStream) {
|
|
252
|
+
if (this.callbacks.isAvatarLive) this.pc.addTransceiver("video", { direction: "recvonly" });
|
|
253
|
+
this.localStream.getTracks().forEach((track) => {
|
|
254
|
+
if (this.pc && this.localStream) this.pc.addTrack(track, this.localStream);
|
|
255
|
+
});
|
|
256
|
+
} else {
|
|
257
|
+
this.pc.addTransceiver("video", { direction: "recvonly" });
|
|
258
|
+
this.pc.addTransceiver("audio", { direction: "recvonly" });
|
|
259
|
+
}
|
|
260
|
+
let fallbackStream = null;
|
|
247
261
|
this.pc.ontrack = (e) => {
|
|
248
262
|
if (e.streams?.[0]) this.callbacks.onRemoteStream?.(e.streams[0]);
|
|
263
|
+
else {
|
|
264
|
+
if (!fallbackStream) fallbackStream = new MediaStream();
|
|
265
|
+
fallbackStream.addTrack(e.track);
|
|
266
|
+
this.callbacks.onRemoteStream?.(fallbackStream);
|
|
267
|
+
}
|
|
249
268
|
};
|
|
250
269
|
this.pc.onicecandidate = (e) => {
|
|
251
270
|
this.send({
|
|
@@ -21,6 +21,7 @@ var WebRTCManager = class {
|
|
|
21
21
|
connection;
|
|
22
22
|
config;
|
|
23
23
|
localStream = null;
|
|
24
|
+
subscribeMode = false;
|
|
24
25
|
managerState = "disconnected";
|
|
25
26
|
hasConnected = false;
|
|
26
27
|
isReconnecting = false;
|
|
@@ -66,17 +67,17 @@ var WebRTCManager = class {
|
|
|
66
67
|
this.emitState(state);
|
|
67
68
|
}
|
|
68
69
|
async reconnect() {
|
|
69
|
-
if (this.isReconnecting || this.intentionalDisconnect
|
|
70
|
+
if (this.isReconnecting || this.intentionalDisconnect) return;
|
|
71
|
+
if (!this.subscribeMode && !this.localStream) return;
|
|
70
72
|
const reconnectGeneration = ++this.reconnectGeneration;
|
|
71
73
|
this.isReconnecting = true;
|
|
72
74
|
this.emitState("reconnecting");
|
|
73
75
|
try {
|
|
74
76
|
await pRetry(async () => {
|
|
75
77
|
if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) throw new AbortError("Reconnect cancelled");
|
|
76
|
-
|
|
77
|
-
if (!stream) throw new AbortError("Reconnect cancelled: no local stream");
|
|
78
|
+
if (!this.subscribeMode && !this.localStream) throw new AbortError("Reconnect cancelled: no local stream");
|
|
78
79
|
this.connection.cleanup();
|
|
79
|
-
await this.connection.connect(this.config.webrtcUrl,
|
|
80
|
+
await this.connection.connect(this.config.webrtcUrl, this.localStream, CONNECTION_TIMEOUT, this.config.integration);
|
|
80
81
|
if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) {
|
|
81
82
|
this.connection.cleanup();
|
|
82
83
|
throw new AbortError("Reconnect cancelled");
|
|
@@ -103,6 +104,7 @@ var WebRTCManager = class {
|
|
|
103
104
|
}
|
|
104
105
|
async connect(localStream) {
|
|
105
106
|
this.localStream = localStream;
|
|
107
|
+
this.subscribeMode = localStream === null;
|
|
106
108
|
this.intentionalDisconnect = false;
|
|
107
109
|
this.hasConnected = false;
|
|
108
110
|
this.isReconnecting = false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decartai/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.47",
|
|
4
4
|
"description": "Decart's JavaScript SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"mitt": "^3.0.1",
|
|
45
45
|
"p-retry": "^6.2.1",
|
|
46
|
-
"uuid": "^13.0.0",
|
|
47
46
|
"zod": "^4.0.17"
|
|
48
47
|
},
|
|
49
48
|
"scripts": {
|