@decartai/sdk 0.0.26 → 0.0.28
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 +4 -4
- package/dist/index.js +2 -2
- package/dist/realtime/audio-stream-manager.js +90 -0
- package/dist/realtime/client.d.ts +11 -2
- package/dist/realtime/client.js +62 -7
- package/dist/realtime/webrtc-connection.js +44 -12
- package/dist/realtime/webrtc-manager.js +6 -1
- package/dist/shared/model.d.ts +6 -3
- package/dist/shared/model.js +20 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { ImageModelDefinition, ImageModels, Model, ModelDefinition, RealTimeModels, VideoModelDefinition, VideoModels, models } from "./shared/model.js";
|
|
1
|
+
import { ImageModelDefinition, ImageModels, Model, ModelDefinition, RealTimeModels, VideoModelDefinition, VideoModels, isImageModel, isRealtimeModel, isVideoModel, models } from "./shared/model.js";
|
|
2
2
|
import { FileInput, ProcessOptions } from "./process/types.js";
|
|
3
3
|
import { ProcessClient } from "./process/client.js";
|
|
4
4
|
import { JobStatus, JobStatusResponse, JobSubmitResponse, QueueJobResult, QueueSubmitAndPollOptions, QueueSubmitOptions } from "./queue/types.js";
|
|
5
5
|
import { QueueClient } from "./queue/client.js";
|
|
6
6
|
import { DecartSDKError, ERROR_CODES } from "./utils/errors.js";
|
|
7
|
-
import { RealTimeClient, RealTimeClientConnectOptions, RealTimeClientInitialState } from "./realtime/client.js";
|
|
7
|
+
import { AvatarOptions, RealTimeClient, RealTimeClientConnectOptions, RealTimeClientInitialState } from "./realtime/client.js";
|
|
8
8
|
import { ModelState } from "./shared/types.js";
|
|
9
9
|
import { CreateTokenResponse, TokensClient } from "./tokens/client.js";
|
|
10
10
|
import { z } from "zod";
|
|
@@ -35,7 +35,7 @@ type DecartClientOptions = z.infer<typeof decartClientOptionsSchema>;
|
|
|
35
35
|
*/
|
|
36
36
|
declare const createDecartClient: (options?: DecartClientOptions) => {
|
|
37
37
|
realtime: {
|
|
38
|
-
connect: (stream: MediaStream, options: RealTimeClientConnectOptions) => Promise<RealTimeClient>;
|
|
38
|
+
connect: (stream: MediaStream | null, options: RealTimeClientConnectOptions) => Promise<RealTimeClient>;
|
|
39
39
|
};
|
|
40
40
|
/**
|
|
41
41
|
* Client for synchronous image generation.
|
|
@@ -109,4 +109,4 @@ declare const createDecartClient: (options?: DecartClientOptions) => {
|
|
|
109
109
|
tokens: TokensClient;
|
|
110
110
|
};
|
|
111
111
|
//#endregion
|
|
112
|
-
export { 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 RealTimeClient, type RealTimeClientConnectOptions, type RealTimeClientInitialState, type RealTimeModels, type TokensClient, type VideoModelDefinition, type VideoModels, createDecartClient, models };
|
|
112
|
+
export { type AvatarOptions, 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 RealTimeClient, type RealTimeClientConnectOptions, type RealTimeClientInitialState, type RealTimeModels, type TokensClient, type VideoModelDefinition, type VideoModels, createDecartClient, isImageModel, isRealtimeModel, isVideoModel, models };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ERROR_CODES, createInvalidApiKeyError, createInvalidBaseUrlError } from "./utils/errors.js";
|
|
2
2
|
import { createProcessClient } from "./process/client.js";
|
|
3
3
|
import { createQueueClient } from "./queue/client.js";
|
|
4
|
-
import { models } from "./shared/model.js";
|
|
4
|
+
import { isImageModel, isRealtimeModel, isVideoModel, models } from "./shared/model.js";
|
|
5
5
|
import { createRealTimeClient } from "./realtime/client.js";
|
|
6
6
|
import { createTokensClient } from "./tokens/client.js";
|
|
7
7
|
import { readEnv } from "./utils/env.js";
|
|
@@ -67,4 +67,4 @@ const createDecartClient = (options = {}) => {
|
|
|
67
67
|
};
|
|
68
68
|
|
|
69
69
|
//#endregion
|
|
70
|
-
export { ERROR_CODES, createDecartClient, models };
|
|
70
|
+
export { ERROR_CODES, createDecartClient, isImageModel, isRealtimeModel, isVideoModel, models };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
//#region src/realtime/audio-stream-manager.ts
|
|
2
|
+
/**
|
|
3
|
+
* Manages an audio stream for avatar-live mode.
|
|
4
|
+
* Creates a continuous audio stream that outputs silence by default,
|
|
5
|
+
* and allows playing audio files through the stream.
|
|
6
|
+
*/
|
|
7
|
+
var AudioStreamManager = class {
|
|
8
|
+
audioContext;
|
|
9
|
+
destination;
|
|
10
|
+
silenceOscillator;
|
|
11
|
+
silenceGain;
|
|
12
|
+
currentSource = null;
|
|
13
|
+
_isPlaying = false;
|
|
14
|
+
constructor() {
|
|
15
|
+
this.audioContext = new AudioContext();
|
|
16
|
+
this.destination = this.audioContext.createMediaStreamDestination();
|
|
17
|
+
this.silenceOscillator = this.audioContext.createOscillator();
|
|
18
|
+
this.silenceGain = this.audioContext.createGain();
|
|
19
|
+
this.silenceGain.gain.value = 0;
|
|
20
|
+
this.silenceOscillator.connect(this.silenceGain);
|
|
21
|
+
this.silenceGain.connect(this.destination);
|
|
22
|
+
this.silenceOscillator.start();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get the MediaStream to pass to WebRTC.
|
|
26
|
+
* This stream outputs silence when no audio is playing.
|
|
27
|
+
*/
|
|
28
|
+
getStream() {
|
|
29
|
+
return this.destination.stream;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Check if audio is currently playing.
|
|
33
|
+
*/
|
|
34
|
+
isPlaying() {
|
|
35
|
+
return this._isPlaying;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Play audio through the stream.
|
|
39
|
+
* When the audio ends, the stream automatically reverts to silence.
|
|
40
|
+
* @param audio - Audio data as Blob, File, or ArrayBuffer
|
|
41
|
+
* @returns Promise that resolves when audio finishes playing
|
|
42
|
+
*/
|
|
43
|
+
async playAudio(audio) {
|
|
44
|
+
if (this.audioContext.state === "suspended") await this.audioContext.resume();
|
|
45
|
+
if (this._isPlaying) this.stopAudio();
|
|
46
|
+
let arrayBuffer;
|
|
47
|
+
if (audio instanceof ArrayBuffer) arrayBuffer = audio;
|
|
48
|
+
else arrayBuffer = await audio.arrayBuffer();
|
|
49
|
+
const audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer);
|
|
50
|
+
const source = this.audioContext.createBufferSource();
|
|
51
|
+
source.buffer = audioBuffer;
|
|
52
|
+
source.connect(this.destination);
|
|
53
|
+
this.currentSource = source;
|
|
54
|
+
this._isPlaying = true;
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
source.onended = () => {
|
|
57
|
+
this._isPlaying = false;
|
|
58
|
+
this.currentSource = null;
|
|
59
|
+
resolve();
|
|
60
|
+
};
|
|
61
|
+
source.start();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Stop currently playing audio immediately.
|
|
66
|
+
* The stream will revert to silence.
|
|
67
|
+
*/
|
|
68
|
+
stopAudio() {
|
|
69
|
+
if (this.currentSource) {
|
|
70
|
+
try {
|
|
71
|
+
this.currentSource.stop();
|
|
72
|
+
} catch {}
|
|
73
|
+
this.currentSource = null;
|
|
74
|
+
}
|
|
75
|
+
this._isPlaying = false;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Clean up resources.
|
|
79
|
+
*/
|
|
80
|
+
cleanup() {
|
|
81
|
+
this.stopAudio();
|
|
82
|
+
try {
|
|
83
|
+
this.silenceOscillator.stop();
|
|
84
|
+
} catch {}
|
|
85
|
+
this.audioContext.close();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
//#endregion
|
|
90
|
+
export { AudioStreamManager };
|
|
@@ -11,9 +11,13 @@ declare const realTimeClientInitialStateSchema: z.ZodObject<{
|
|
|
11
11
|
}, z.core.$strip>;
|
|
12
12
|
type OnRemoteStreamFn = (stream: MediaStream) => void;
|
|
13
13
|
type RealTimeClientInitialState = z.infer<typeof realTimeClientInitialStateSchema>;
|
|
14
|
+
declare const avatarOptionsSchema: z.ZodObject<{
|
|
15
|
+
avatarImage: z.ZodUnion<readonly [z.ZodCustom<Blob, Blob>, z.ZodCustom<File, File>, z.ZodString]>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
type AvatarOptions = z.infer<typeof avatarOptionsSchema>;
|
|
14
18
|
declare const realTimeClientConnectOptionsSchema: z.ZodObject<{
|
|
15
19
|
model: z.ZodObject<{
|
|
16
|
-
name: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodLiteral<"mirage">, z.ZodLiteral<"mirage_v2">, z.ZodLiteral<"lucy_v2v_720p_rt">]>, z.ZodUnion<readonly [z.ZodLiteral<"lucy-dev-i2v">, z.ZodLiteral<"lucy-fast-v2v">, z.ZodLiteral<"lucy-pro-t2v">, z.ZodLiteral<"lucy-pro-i2v">, z.ZodLiteral<"lucy-pro-v2v">, z.ZodLiteral<"lucy-pro-flf2v">, z.ZodLiteral<"lucy-motion">, z.ZodLiteral<"lucy-restyle-v2v">]>, z.ZodUnion<readonly [z.ZodLiteral<"lucy-pro-t2i">, z.ZodLiteral<"lucy-pro-i2i">]>]>;
|
|
20
|
+
name: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodLiteral<"mirage">, z.ZodLiteral<"mirage_v2">, z.ZodLiteral<"lucy_v2v_720p_rt">, z.ZodLiteral<"avatar-live">]>, z.ZodUnion<readonly [z.ZodLiteral<"lucy-dev-i2v">, z.ZodLiteral<"lucy-fast-v2v">, z.ZodLiteral<"lucy-pro-t2v">, z.ZodLiteral<"lucy-pro-i2v">, z.ZodLiteral<"lucy-pro-v2v">, z.ZodLiteral<"lucy-pro-flf2v">, z.ZodLiteral<"lucy-motion">, z.ZodLiteral<"lucy-restyle-v2v">]>, z.ZodUnion<readonly [z.ZodLiteral<"lucy-pro-t2i">, z.ZodLiteral<"lucy-pro-i2i">]>]>;
|
|
17
21
|
urlPath: z.ZodString;
|
|
18
22
|
queueUrlPath: z.ZodOptional<z.ZodString>;
|
|
19
23
|
fps: z.ZodNumber;
|
|
@@ -29,6 +33,9 @@ declare const realTimeClientConnectOptionsSchema: z.ZodObject<{
|
|
|
29
33
|
}, z.core.$strip>>;
|
|
30
34
|
}, z.core.$strip>>;
|
|
31
35
|
customizeOffer: z.ZodOptional<z.ZodCustom<z.core.$InferInnerFunctionTypeAsync<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>, z.core.$InferInnerFunctionTypeAsync<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>>;
|
|
36
|
+
avatar: z.ZodOptional<z.ZodObject<{
|
|
37
|
+
avatarImage: z.ZodUnion<readonly [z.ZodCustom<Blob, Blob>, z.ZodCustom<File, File>, z.ZodString]>;
|
|
38
|
+
}, z.core.$strip>>;
|
|
32
39
|
}, z.core.$strip>;
|
|
33
40
|
type RealTimeClientConnectOptions = z.infer<typeof realTimeClientConnectOptionsSchema>;
|
|
34
41
|
type Events = {
|
|
@@ -47,6 +54,8 @@ type RealTimeClient = {
|
|
|
47
54
|
on: <K extends keyof Events>(event: K, listener: (data: Events[K]) => void) => void;
|
|
48
55
|
off: <K extends keyof Events>(event: K, listener: (data: Events[K]) => void) => void;
|
|
49
56
|
sessionId: string;
|
|
57
|
+
setImage: (image: Blob | File | string) => Promise<void>;
|
|
58
|
+
playAudio?: (audio: Blob | File | ArrayBuffer) => Promise<void>;
|
|
50
59
|
};
|
|
51
60
|
//#endregion
|
|
52
|
-
export { RealTimeClient, RealTimeClientConnectOptions, RealTimeClientInitialState };
|
|
61
|
+
export { AvatarOptions, RealTimeClient, RealTimeClientConnectOptions, RealTimeClientInitialState };
|
package/dist/realtime/client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createWebrtcError } from "../utils/errors.js";
|
|
2
2
|
import { modelDefinitionSchema } from "../shared/model.js";
|
|
3
3
|
import { modelStateSchema } from "../shared/types.js";
|
|
4
|
+
import { AudioStreamManager } from "./audio-stream-manager.js";
|
|
4
5
|
import { realtimeMethods } from "./methods.js";
|
|
5
6
|
import { WebRTCManager } from "./webrtc-manager.js";
|
|
6
7
|
import { z } from "zod";
|
|
@@ -8,13 +9,30 @@ import mitt from "mitt";
|
|
|
8
9
|
import { v4 } from "uuid";
|
|
9
10
|
|
|
10
11
|
//#region src/realtime/client.ts
|
|
12
|
+
async function blobToBase64(blob) {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const reader = new FileReader();
|
|
15
|
+
reader.onloadend = () => {
|
|
16
|
+
const base64 = reader.result.split(",")[1];
|
|
17
|
+
resolve(base64);
|
|
18
|
+
};
|
|
19
|
+
reader.onerror = reject;
|
|
20
|
+
reader.readAsDataURL(blob);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
11
23
|
const realTimeClientInitialStateSchema = modelStateSchema;
|
|
12
24
|
const createAsyncFunctionSchema = (schema) => z.custom((fn) => schema.implementAsync(fn));
|
|
25
|
+
const avatarOptionsSchema = z.object({ avatarImage: z.union([
|
|
26
|
+
z.instanceof(Blob),
|
|
27
|
+
z.instanceof(File),
|
|
28
|
+
z.string()
|
|
29
|
+
]) });
|
|
13
30
|
const realTimeClientConnectOptionsSchema = z.object({
|
|
14
31
|
model: modelDefinitionSchema,
|
|
15
32
|
onRemoteStream: z.custom((val) => typeof val === "function", { message: "onRemoteStream must be a function" }),
|
|
16
33
|
initialState: realTimeClientInitialStateSchema.optional(),
|
|
17
|
-
customizeOffer: createAsyncFunctionSchema(z.function()).optional()
|
|
34
|
+
customizeOffer: createAsyncFunctionSchema(z.function()).optional(),
|
|
35
|
+
avatar: avatarOptionsSchema.optional()
|
|
18
36
|
});
|
|
19
37
|
const createRealTimeClient = (opts) => {
|
|
20
38
|
const { baseUrl, apiKey, integration } = opts;
|
|
@@ -23,7 +41,21 @@ const createRealTimeClient = (opts) => {
|
|
|
23
41
|
const parsedOptions = realTimeClientConnectOptionsSchema.safeParse(options);
|
|
24
42
|
if (!parsedOptions.success) throw parsedOptions.error;
|
|
25
43
|
const sessionId = v4();
|
|
26
|
-
const
|
|
44
|
+
const isAvatarLive = options.model.name === "avatar-live";
|
|
45
|
+
const { onRemoteStream, initialState, avatar } = parsedOptions.data;
|
|
46
|
+
let audioStreamManager;
|
|
47
|
+
let inputStream;
|
|
48
|
+
if (isAvatarLive && !stream) {
|
|
49
|
+
audioStreamManager = new AudioStreamManager();
|
|
50
|
+
inputStream = audioStreamManager.getStream();
|
|
51
|
+
} else inputStream = stream ?? new MediaStream();
|
|
52
|
+
let avatarImageBase64;
|
|
53
|
+
if (isAvatarLive && avatar?.avatarImage) {
|
|
54
|
+
let imageBlob;
|
|
55
|
+
if (typeof avatar.avatarImage === "string") imageBlob = await (await fetch(avatar.avatarImage)).blob();
|
|
56
|
+
else imageBlob = avatar.avatarImage;
|
|
57
|
+
avatarImageBase64 = await blobToBase64(imageBlob);
|
|
58
|
+
}
|
|
27
59
|
const webrtcManager = new WebRTCManager({
|
|
28
60
|
webrtcUrl: `${`${baseUrl}${options.model.urlPath}`}?api_key=${apiKey}&model=${options.model.name}`,
|
|
29
61
|
apiKey,
|
|
@@ -41,9 +73,11 @@ const createRealTimeClient = (opts) => {
|
|
|
41
73
|
},
|
|
42
74
|
customizeOffer: options.customizeOffer,
|
|
43
75
|
vp8MinBitrate: 300,
|
|
44
|
-
vp8StartBitrate: 600
|
|
76
|
+
vp8StartBitrate: 600,
|
|
77
|
+
isAvatarLive,
|
|
78
|
+
avatarImageBase64
|
|
45
79
|
});
|
|
46
|
-
await webrtcManager.connect(
|
|
80
|
+
await webrtcManager.connect(inputStream);
|
|
47
81
|
const methods = realtimeMethods(webrtcManager);
|
|
48
82
|
if (options.initialState) {
|
|
49
83
|
if (options.initialState.prompt) {
|
|
@@ -51,15 +85,36 @@ const createRealTimeClient = (opts) => {
|
|
|
51
85
|
methods.setPrompt(text, { enhance });
|
|
52
86
|
}
|
|
53
87
|
}
|
|
54
|
-
|
|
88
|
+
const client = {
|
|
55
89
|
setPrompt: methods.setPrompt,
|
|
56
90
|
isConnected: () => webrtcManager.isConnected(),
|
|
57
91
|
getConnectionState: () => webrtcManager.getConnectionState(),
|
|
58
|
-
disconnect: () =>
|
|
92
|
+
disconnect: () => {
|
|
93
|
+
webrtcManager.cleanup();
|
|
94
|
+
audioStreamManager?.cleanup();
|
|
95
|
+
},
|
|
59
96
|
on: eventEmitter.on,
|
|
60
97
|
off: eventEmitter.off,
|
|
61
|
-
sessionId
|
|
98
|
+
sessionId,
|
|
99
|
+
setImage: async (image) => {
|
|
100
|
+
let imageBase64;
|
|
101
|
+
if (typeof image === "string") {
|
|
102
|
+
let url = null;
|
|
103
|
+
try {
|
|
104
|
+
url = new URL(image);
|
|
105
|
+
} catch {}
|
|
106
|
+
if (url?.protocol === "data:") imageBase64 = image.split(",")[1];
|
|
107
|
+
else if (url?.protocol === "http:" || url?.protocol === "https:") imageBase64 = await blobToBase64(await (await fetch(image)).blob());
|
|
108
|
+
else imageBase64 = image;
|
|
109
|
+
} else imageBase64 = await blobToBase64(image);
|
|
110
|
+
return webrtcManager.setImage(imageBase64);
|
|
111
|
+
}
|
|
62
112
|
};
|
|
113
|
+
if (isAvatarLive && audioStreamManager) {
|
|
114
|
+
const manager = audioStreamManager;
|
|
115
|
+
client.playAudio = (audio) => manager.playAudio(audio);
|
|
116
|
+
}
|
|
117
|
+
return client;
|
|
63
118
|
};
|
|
64
119
|
return { connect };
|
|
65
120
|
};
|
|
@@ -3,6 +3,7 @@ import mitt from "mitt";
|
|
|
3
3
|
|
|
4
4
|
//#region src/realtime/webrtc-connection.ts
|
|
5
5
|
const ICE_SERVERS = [{ urls: "stun:stun.l.google.com:19302" }];
|
|
6
|
+
const AVATAR_SETUP_TIMEOUT_MS = 15e3;
|
|
6
7
|
var WebRTCConnection = class {
|
|
7
8
|
pc = null;
|
|
8
9
|
ws = null;
|
|
@@ -38,6 +39,7 @@ var WebRTCConnection = class {
|
|
|
38
39
|
};
|
|
39
40
|
this.ws.onclose = () => this.setState("disconnected");
|
|
40
41
|
});
|
|
42
|
+
if (this.callbacks.avatarImageBase64) await this.sendAvatarImage(this.callbacks.avatarImageBase64);
|
|
41
43
|
await this.setupNewPeerConnection();
|
|
42
44
|
return new Promise((resolve, reject) => {
|
|
43
45
|
this.connectionReject = reject;
|
|
@@ -55,18 +57,22 @@ var WebRTCConnection = class {
|
|
|
55
57
|
});
|
|
56
58
|
}
|
|
57
59
|
async handleSignalingMessage(msg) {
|
|
58
|
-
if (!this.pc) return;
|
|
59
60
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
this.connectionReject = null;
|
|
67
|
-
}
|
|
68
|
-
break;
|
|
61
|
+
if (msg.type === "error") {
|
|
62
|
+
const error = new Error(msg.error);
|
|
63
|
+
this.callbacks.onError?.(error);
|
|
64
|
+
if (this.connectionReject) {
|
|
65
|
+
this.connectionReject(error);
|
|
66
|
+
this.connectionReject = null;
|
|
69
67
|
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (msg.type === "image_set") {
|
|
71
|
+
this.websocketMessagesEmitter.emit("imageSet", msg);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (!this.pc) return;
|
|
75
|
+
switch (msg.type) {
|
|
70
76
|
case "ready": {
|
|
71
77
|
await this.applyCodecPreference("video/VP8");
|
|
72
78
|
const offer = await this.pc.createOffer();
|
|
@@ -118,10 +124,35 @@ var WebRTCConnection = class {
|
|
|
118
124
|
send(message) {
|
|
119
125
|
if (this.ws?.readyState === WebSocket.OPEN) this.ws.send(JSON.stringify(message));
|
|
120
126
|
}
|
|
127
|
+
async sendAvatarImage(imageBase64) {
|
|
128
|
+
return this.setImageBase64(imageBase64);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Send an image to the server (e.g., as a reference for inference).
|
|
132
|
+
* Can be called after connection is established.
|
|
133
|
+
*/
|
|
134
|
+
async setImageBase64(imageBase64) {
|
|
135
|
+
return new Promise((resolve, reject) => {
|
|
136
|
+
const timeoutId = setTimeout(() => {
|
|
137
|
+
this.websocketMessagesEmitter.off("imageSet", listener);
|
|
138
|
+
reject(/* @__PURE__ */ new Error("Image send timed out"));
|
|
139
|
+
}, AVATAR_SETUP_TIMEOUT_MS);
|
|
140
|
+
const listener = (msg) => {
|
|
141
|
+
clearTimeout(timeoutId);
|
|
142
|
+
this.websocketMessagesEmitter.off("imageSet", listener);
|
|
143
|
+
if (msg.status === "success") resolve();
|
|
144
|
+
else reject(/* @__PURE__ */ new Error(`Failed to send image: ${msg.status}`));
|
|
145
|
+
};
|
|
146
|
+
this.websocketMessagesEmitter.on("imageSet", listener);
|
|
147
|
+
this.send({
|
|
148
|
+
type: "set_image",
|
|
149
|
+
image_data: imageBase64
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
}
|
|
121
153
|
setState(state) {
|
|
122
154
|
if (this.state !== state) {
|
|
123
155
|
this.state = state;
|
|
124
|
-
console.log(`[WebRTC] State: ${state}`);
|
|
125
156
|
this.callbacks.onStateChange?.(state);
|
|
126
157
|
}
|
|
127
158
|
}
|
|
@@ -140,6 +171,7 @@ var WebRTCConnection = class {
|
|
|
140
171
|
username: turnConfig.username
|
|
141
172
|
});
|
|
142
173
|
this.pc = new RTCPeerConnection({ iceServers });
|
|
174
|
+
if (this.callbacks.isAvatarLive) this.pc.addTransceiver("video", { direction: "recvonly" });
|
|
143
175
|
this.localStream.getTracks().forEach((track) => {
|
|
144
176
|
if (this.pc && this.localStream) this.pc.addTrack(track, this.localStream);
|
|
145
177
|
});
|
|
@@ -173,7 +205,7 @@ var WebRTCConnection = class {
|
|
|
173
205
|
}
|
|
174
206
|
async applyCodecPreference(preferredCodecName) {
|
|
175
207
|
if (!this.pc) return;
|
|
176
|
-
const videoTransceiver = this.pc.getTransceivers().find((r) => r.sender.track?.kind === "video");
|
|
208
|
+
const videoTransceiver = this.pc.getTransceivers().find((r) => r.sender.track?.kind === "video" || r.receiver.track?.kind === "video");
|
|
177
209
|
if (!videoTransceiver) {
|
|
178
210
|
console.error("Could not find video transceiver. Ensure track is added to peer connection.");
|
|
179
211
|
return;
|
|
@@ -21,7 +21,9 @@ var WebRTCManager = class {
|
|
|
21
21
|
onError: config.onError,
|
|
22
22
|
customizeOffer: config.customizeOffer,
|
|
23
23
|
vp8MinBitrate: config.vp8MinBitrate,
|
|
24
|
-
vp8StartBitrate: config.vp8StartBitrate
|
|
24
|
+
vp8StartBitrate: config.vp8StartBitrate,
|
|
25
|
+
isAvatarLive: config.isAvatarLive,
|
|
26
|
+
avatarImageBase64: config.avatarImageBase64
|
|
25
27
|
});
|
|
26
28
|
}
|
|
27
29
|
async connect(localStream) {
|
|
@@ -58,6 +60,9 @@ var WebRTCManager = class {
|
|
|
58
60
|
getWebsocketMessageEmitter() {
|
|
59
61
|
return this.connection.websocketMessagesEmitter;
|
|
60
62
|
}
|
|
63
|
+
setImage(imageBase64) {
|
|
64
|
+
return this.connection.setImageBase64(imageBase64);
|
|
65
|
+
}
|
|
61
66
|
};
|
|
62
67
|
|
|
63
68
|
//#endregion
|
package/dist/shared/model.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
//#region src/shared/model.d.ts
|
|
4
|
-
declare const realtimeModels: z.ZodUnion<readonly [z.ZodLiteral<"mirage">, z.ZodLiteral<"mirage_v2">, z.ZodLiteral<"lucy_v2v_720p_rt">]>;
|
|
4
|
+
declare const realtimeModels: z.ZodUnion<readonly [z.ZodLiteral<"mirage">, z.ZodLiteral<"mirage_v2">, z.ZodLiteral<"lucy_v2v_720p_rt">, z.ZodLiteral<"avatar-live">]>;
|
|
5
5
|
declare const videoModels: z.ZodUnion<readonly [z.ZodLiteral<"lucy-dev-i2v">, z.ZodLiteral<"lucy-fast-v2v">, z.ZodLiteral<"lucy-pro-t2v">, z.ZodLiteral<"lucy-pro-i2v">, z.ZodLiteral<"lucy-pro-v2v">, z.ZodLiteral<"lucy-pro-flf2v">, z.ZodLiteral<"lucy-motion">, z.ZodLiteral<"lucy-restyle-v2v">]>;
|
|
6
6
|
declare const imageModels: z.ZodUnion<readonly [z.ZodLiteral<"lucy-pro-t2i">, z.ZodLiteral<"lucy-pro-i2i">]>;
|
|
7
|
-
declare const modelSchema: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodLiteral<"mirage">, z.ZodLiteral<"mirage_v2">, z.ZodLiteral<"lucy_v2v_720p_rt">]>, z.ZodUnion<readonly [z.ZodLiteral<"lucy-dev-i2v">, z.ZodLiteral<"lucy-fast-v2v">, z.ZodLiteral<"lucy-pro-t2v">, z.ZodLiteral<"lucy-pro-i2v">, z.ZodLiteral<"lucy-pro-v2v">, z.ZodLiteral<"lucy-pro-flf2v">, z.ZodLiteral<"lucy-motion">, z.ZodLiteral<"lucy-restyle-v2v">]>, z.ZodUnion<readonly [z.ZodLiteral<"lucy-pro-t2i">, z.ZodLiteral<"lucy-pro-i2i">]>]>;
|
|
7
|
+
declare const modelSchema: z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodLiteral<"mirage">, z.ZodLiteral<"mirage_v2">, z.ZodLiteral<"lucy_v2v_720p_rt">, z.ZodLiteral<"avatar-live">]>, z.ZodUnion<readonly [z.ZodLiteral<"lucy-dev-i2v">, z.ZodLiteral<"lucy-fast-v2v">, z.ZodLiteral<"lucy-pro-t2v">, z.ZodLiteral<"lucy-pro-i2v">, z.ZodLiteral<"lucy-pro-v2v">, z.ZodLiteral<"lucy-pro-flf2v">, z.ZodLiteral<"lucy-motion">, z.ZodLiteral<"lucy-restyle-v2v">]>, z.ZodUnion<readonly [z.ZodLiteral<"lucy-pro-t2i">, z.ZodLiteral<"lucy-pro-i2i">]>]>;
|
|
8
8
|
type Model = z.infer<typeof modelSchema>;
|
|
9
9
|
type RealTimeModels = z.infer<typeof realtimeModels>;
|
|
10
10
|
type VideoModels = z.infer<typeof videoModels>;
|
|
11
11
|
type ImageModels = z.infer<typeof imageModels>;
|
|
12
|
+
declare function isRealtimeModel(model: string): model is RealTimeModels;
|
|
13
|
+
declare function isVideoModel(model: string): model is VideoModels;
|
|
14
|
+
declare function isImageModel(model: string): model is ImageModels;
|
|
12
15
|
declare const modelInputSchemas: {
|
|
13
16
|
readonly "lucy-pro-t2v": z.ZodObject<{
|
|
14
17
|
prompt: z.ZodString;
|
|
@@ -141,4 +144,4 @@ declare const models: {
|
|
|
141
144
|
image: <T extends ImageModels>(model: T) => ModelDefinition<T>;
|
|
142
145
|
};
|
|
143
146
|
//#endregion
|
|
144
|
-
export { ImageModelDefinition, ImageModels, Model, ModelDefinition, ModelInputSchemas, RealTimeModels, VideoModelDefinition, VideoModels, models };
|
|
147
|
+
export { ImageModelDefinition, ImageModels, Model, ModelDefinition, ModelInputSchemas, RealTimeModels, VideoModelDefinition, VideoModels, isImageModel, isRealtimeModel, isVideoModel, models };
|
package/dist/shared/model.js
CHANGED
|
@@ -5,7 +5,8 @@ import { z } from "zod";
|
|
|
5
5
|
const realtimeModels = z.union([
|
|
6
6
|
z.literal("mirage"),
|
|
7
7
|
z.literal("mirage_v2"),
|
|
8
|
-
z.literal("lucy_v2v_720p_rt")
|
|
8
|
+
z.literal("lucy_v2v_720p_rt"),
|
|
9
|
+
z.literal("avatar-live")
|
|
9
10
|
]);
|
|
10
11
|
const videoModels = z.union([
|
|
11
12
|
z.literal("lucy-dev-i2v"),
|
|
@@ -23,6 +24,15 @@ const modelSchema = z.union([
|
|
|
23
24
|
videoModels,
|
|
24
25
|
imageModels
|
|
25
26
|
]);
|
|
27
|
+
function isRealtimeModel(model) {
|
|
28
|
+
return realtimeModels.safeParse(model).success;
|
|
29
|
+
}
|
|
30
|
+
function isVideoModel(model) {
|
|
31
|
+
return videoModels.safeParse(model).success;
|
|
32
|
+
}
|
|
33
|
+
function isImageModel(model) {
|
|
34
|
+
return imageModels.safeParse(model).success;
|
|
35
|
+
}
|
|
26
36
|
const fileInputSchema = z.union([
|
|
27
37
|
z.instanceof(File),
|
|
28
38
|
z.instanceof(Blob),
|
|
@@ -155,6 +165,14 @@ const _models = {
|
|
|
155
165
|
width: 1280,
|
|
156
166
|
height: 704,
|
|
157
167
|
inputSchema: z.object({})
|
|
168
|
+
},
|
|
169
|
+
"avatar-live": {
|
|
170
|
+
urlPath: "/v1/avatar-live/stream",
|
|
171
|
+
name: "avatar-live",
|
|
172
|
+
fps: 25,
|
|
173
|
+
width: 1280,
|
|
174
|
+
height: 720,
|
|
175
|
+
inputSchema: z.object({})
|
|
158
176
|
}
|
|
159
177
|
},
|
|
160
178
|
image: {
|
|
@@ -271,4 +289,4 @@ const models = {
|
|
|
271
289
|
};
|
|
272
290
|
|
|
273
291
|
//#endregion
|
|
274
|
-
export { modelDefinitionSchema, models };
|
|
292
|
+
export { isImageModel, isRealtimeModel, isVideoModel, modelDefinitionSchema, models };
|