@decartai/sdk 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +423 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +36 -0
- package/dist/process/client.d.ts +7 -0
- package/dist/process/client.js +26 -0
- package/dist/process/request.js +35 -0
- package/dist/process/types.d.ts +12 -0
- package/dist/realtime/client.d.ts +55 -0
- package/dist/realtime/client.js +62 -0
- package/dist/realtime/methods.js +38 -0
- package/dist/realtime/webrtc-connection.js +144 -0
- package/dist/realtime/webrtc-manager.js +56 -0
- package/dist/shared/model.d.ts +82 -0
- package/dist/shared/model.js +197 -0
- package/dist/shared/types.d.ts +13 -0
- package/dist/shared/types.js +13 -0
- package/dist/utils/errors.d.ts +18 -0
- package/dist/utils/errors.js +36 -0
- package/package.json +59 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createWebrtcError } from "../utils/errors.js";
|
|
2
|
+
import { modelDefinitionSchema } from "../shared/model.js";
|
|
3
|
+
import { modelStateSchema } from "../shared/types.js";
|
|
4
|
+
import { realtimeMethods } from "./methods.js";
|
|
5
|
+
import { WebRTCManager } from "./webrtc-manager.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import mitt from "mitt";
|
|
8
|
+
import { v4 } from "uuid";
|
|
9
|
+
|
|
10
|
+
//#region src/realtime/client.ts
|
|
11
|
+
const realTimeClientInitialStateSchema = modelStateSchema;
|
|
12
|
+
const createAsyncFunctionSchema = (schema) => z.custom((fn) => schema.implementAsync(fn));
|
|
13
|
+
const realTimeClientConnectOptionsSchema = z.object({
|
|
14
|
+
model: modelDefinitionSchema,
|
|
15
|
+
onRemoteStream: z.custom((val) => typeof val === "function", { message: "onRemoteStream must be a function" }),
|
|
16
|
+
initialState: realTimeClientInitialStateSchema.optional(),
|
|
17
|
+
customizeOffer: createAsyncFunctionSchema(z.function()).optional()
|
|
18
|
+
});
|
|
19
|
+
const createRealTimeClient = (opts) => {
|
|
20
|
+
const { baseUrl, apiKey } = opts;
|
|
21
|
+
const connect = async (stream, options) => {
|
|
22
|
+
const eventEmitter = mitt();
|
|
23
|
+
const parsedOptions = realTimeClientConnectOptionsSchema.safeParse(options);
|
|
24
|
+
if (!parsedOptions.success) throw parsedOptions.error;
|
|
25
|
+
const sessionId = v4();
|
|
26
|
+
const { onRemoteStream, initialState } = parsedOptions.data;
|
|
27
|
+
const url = `${baseUrl}${options.model.urlPath}`;
|
|
28
|
+
const webrtcManager = new WebRTCManager({
|
|
29
|
+
webrtcUrl: `${url}?api_key=${apiKey}&model=${options.model.name}`,
|
|
30
|
+
apiKey,
|
|
31
|
+
sessionId,
|
|
32
|
+
fps: options.model.fps,
|
|
33
|
+
initialState,
|
|
34
|
+
onRemoteStream,
|
|
35
|
+
onConnectionStateChange: (state) => {
|
|
36
|
+
eventEmitter.emit("connectionChange", state);
|
|
37
|
+
},
|
|
38
|
+
onError: (error) => {
|
|
39
|
+
console.error("WebRTC error:", error);
|
|
40
|
+
eventEmitter.emit("error", createWebrtcError(error));
|
|
41
|
+
},
|
|
42
|
+
customizeOffer: options.customizeOffer
|
|
43
|
+
});
|
|
44
|
+
await webrtcManager.connect(stream);
|
|
45
|
+
const methods = realtimeMethods(webrtcManager);
|
|
46
|
+
return {
|
|
47
|
+
enrichPrompt: methods.enrichPrompt,
|
|
48
|
+
setPrompt: methods.setPrompt,
|
|
49
|
+
setMirror: methods.setMirror,
|
|
50
|
+
isConnected: () => webrtcManager.isConnected(),
|
|
51
|
+
getConnectionState: () => webrtcManager.getConnectionState(),
|
|
52
|
+
disconnect: () => webrtcManager.cleanup(),
|
|
53
|
+
on: eventEmitter.on,
|
|
54
|
+
off: eventEmitter.off,
|
|
55
|
+
sessionId
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
return { connect };
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { createRealTimeClient };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/realtime/methods.ts
|
|
4
|
+
const realtimeMethods = (webrtcManager) => {
|
|
5
|
+
const enrichPrompt = (_prompt) => {
|
|
6
|
+
throw new Error("Not implemented");
|
|
7
|
+
};
|
|
8
|
+
const setPrompt = (prompt, { enrich } = {}) => {
|
|
9
|
+
const parsedInput = z.object({
|
|
10
|
+
prompt: z.string().min(1),
|
|
11
|
+
enrich: z.boolean().optional().default(true)
|
|
12
|
+
}).safeParse({
|
|
13
|
+
prompt,
|
|
14
|
+
enrich
|
|
15
|
+
});
|
|
16
|
+
if (!parsedInput.success) throw parsedInput.error;
|
|
17
|
+
webrtcManager.sendMessage({
|
|
18
|
+
type: "prompt",
|
|
19
|
+
prompt: parsedInput.data.prompt
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
const setMirror = (enabled) => {
|
|
23
|
+
const parsedInput = z.object({ enabled: z.boolean() }).safeParse({ enabled });
|
|
24
|
+
if (!parsedInput.success) throw parsedInput.error;
|
|
25
|
+
webrtcManager.sendMessage({
|
|
26
|
+
type: "switch_camera",
|
|
27
|
+
rotateY: parsedInput.data.enabled ? 2 : 0
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
enrichPrompt,
|
|
32
|
+
setPrompt,
|
|
33
|
+
setMirror
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { realtimeMethods };
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
//#region src/realtime/webrtc-connection.ts
|
|
2
|
+
const ICE_SERVERS = [{ urls: "stun:stun.l.google.com:19302" }];
|
|
3
|
+
var WebRTCConnection = class {
|
|
4
|
+
pc = null;
|
|
5
|
+
ws = null;
|
|
6
|
+
state = "disconnected";
|
|
7
|
+
constructor(callbacks = {}) {
|
|
8
|
+
this.callbacks = callbacks;
|
|
9
|
+
}
|
|
10
|
+
async connect(url, localStream, timeout = 15e3) {
|
|
11
|
+
const deadline = Date.now() + timeout;
|
|
12
|
+
await new Promise((resolve, reject) => {
|
|
13
|
+
const timer = setTimeout(() => reject(/* @__PURE__ */ new Error("WebSocket timeout")), timeout);
|
|
14
|
+
this.ws = new WebSocket(url);
|
|
15
|
+
this.ws.onopen = () => {
|
|
16
|
+
clearTimeout(timer);
|
|
17
|
+
resolve();
|
|
18
|
+
};
|
|
19
|
+
this.ws.onmessage = (e) => {
|
|
20
|
+
try {
|
|
21
|
+
this.handleSignalingMessage(JSON.parse(e.data));
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.error("[WebRTC] Parse error:", err);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
this.ws.onerror = () => {
|
|
27
|
+
clearTimeout(timer);
|
|
28
|
+
};
|
|
29
|
+
this.ws.onclose = () => this.setState("disconnected");
|
|
30
|
+
});
|
|
31
|
+
this.pc?.close();
|
|
32
|
+
this.pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });
|
|
33
|
+
localStream.getTracks().forEach((track) => this.pc.addTrack(track, localStream));
|
|
34
|
+
this.pc.ontrack = (e) => {
|
|
35
|
+
if (e.streams?.[0]) this.callbacks.onRemoteStream?.(e.streams[0]);
|
|
36
|
+
};
|
|
37
|
+
this.pc.onicecandidate = (e) => {
|
|
38
|
+
if (e.candidate) this.send({
|
|
39
|
+
type: "ice-candidate",
|
|
40
|
+
candidate: e.candidate
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
this.pc.onconnectionstatechange = () => {
|
|
44
|
+
if (!this.pc) return;
|
|
45
|
+
const s = this.pc.connectionState;
|
|
46
|
+
this.setState(s === "connected" ? "connected" : ["connecting", "new"].includes(s) ? "connecting" : "disconnected");
|
|
47
|
+
};
|
|
48
|
+
this.handleSignalingMessage({ type: "ready" });
|
|
49
|
+
while (Date.now() < deadline) {
|
|
50
|
+
if (this.state === "connected") return;
|
|
51
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
52
|
+
}
|
|
53
|
+
throw new Error("Connection timeout");
|
|
54
|
+
}
|
|
55
|
+
async handleSignalingMessage(msg) {
|
|
56
|
+
if (!this.pc) return;
|
|
57
|
+
try {
|
|
58
|
+
switch (msg.type) {
|
|
59
|
+
case "ready": {
|
|
60
|
+
await this.applyCodecPreference("video/VP8");
|
|
61
|
+
const offer = await this.pc.createOffer();
|
|
62
|
+
await this.callbacks.customizeOffer?.(offer);
|
|
63
|
+
await this.pc.setLocalDescription(offer);
|
|
64
|
+
this.send({
|
|
65
|
+
type: "offer",
|
|
66
|
+
sdp: offer.sdp || ""
|
|
67
|
+
});
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case "offer": {
|
|
71
|
+
await this.pc.setRemoteDescription({
|
|
72
|
+
type: "offer",
|
|
73
|
+
sdp: msg.sdp
|
|
74
|
+
});
|
|
75
|
+
const answer = await this.pc.createAnswer();
|
|
76
|
+
await this.pc.setLocalDescription(answer);
|
|
77
|
+
this.send({
|
|
78
|
+
type: "answer",
|
|
79
|
+
sdp: answer.sdp || ""
|
|
80
|
+
});
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case "answer":
|
|
84
|
+
await this.pc.setRemoteDescription({
|
|
85
|
+
type: "answer",
|
|
86
|
+
sdp: msg.sdp
|
|
87
|
+
});
|
|
88
|
+
break;
|
|
89
|
+
case "ice-candidate":
|
|
90
|
+
if (msg.candidate) await this.pc.addIceCandidate(msg.candidate);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error("[WebRTC] Error:", error);
|
|
95
|
+
this.callbacks.onError?.(error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
send(message) {
|
|
99
|
+
if (this.ws?.readyState === WebSocket.OPEN) this.ws.send(JSON.stringify(message));
|
|
100
|
+
}
|
|
101
|
+
setState(state) {
|
|
102
|
+
if (this.state !== state) {
|
|
103
|
+
this.state = state;
|
|
104
|
+
console.log(`[WebRTC] State: ${state}`);
|
|
105
|
+
this.callbacks.onStateChange?.(state);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
cleanup() {
|
|
109
|
+
this.pc?.getSenders().forEach((s) => s.track?.stop());
|
|
110
|
+
this.pc?.close();
|
|
111
|
+
this.pc = null;
|
|
112
|
+
this.ws?.close();
|
|
113
|
+
this.ws = null;
|
|
114
|
+
this.setState("disconnected");
|
|
115
|
+
}
|
|
116
|
+
async applyCodecPreference(preferredCodecName) {
|
|
117
|
+
if (!this.pc) return;
|
|
118
|
+
const videoTransceiver = this.pc.getTransceivers().find((r) => r.sender.track?.kind === "video");
|
|
119
|
+
if (!videoTransceiver) {
|
|
120
|
+
console.error("Could not find video transceiver. Ensure track is added to peer connection.");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const capabilities = RTCRtpSender.getCapabilities("video");
|
|
124
|
+
if (!capabilities) {
|
|
125
|
+
console.error("Could not get video sender capabilities.");
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const preferredCodecs = [];
|
|
129
|
+
const otherCodecs = [];
|
|
130
|
+
capabilities.codecs.forEach((codec) => {
|
|
131
|
+
if (codec.mimeType.toLowerCase() === preferredCodecName.toLowerCase()) preferredCodecs.push(codec);
|
|
132
|
+
else otherCodecs.push(codec);
|
|
133
|
+
});
|
|
134
|
+
const orderedCodecs = [...preferredCodecs, ...otherCodecs];
|
|
135
|
+
if (orderedCodecs.length === 0) {
|
|
136
|
+
console.warn("No video codecs found to set preferences for.");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
await videoTransceiver.setCodecPreferences(orderedCodecs);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
//#endregion
|
|
144
|
+
export { WebRTCConnection };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { WebRTCConnection } from "./webrtc-connection.js";
|
|
2
|
+
import pRetry from "p-retry";
|
|
3
|
+
|
|
4
|
+
//#region src/realtime/webrtc-manager.ts
|
|
5
|
+
const PERMANENT_ERRORS = [
|
|
6
|
+
"permission denied",
|
|
7
|
+
"not allowed",
|
|
8
|
+
"invalid session"
|
|
9
|
+
];
|
|
10
|
+
var WebRTCManager = class {
|
|
11
|
+
connection;
|
|
12
|
+
config;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
this.connection = new WebRTCConnection({
|
|
16
|
+
onRemoteStream: config.onRemoteStream,
|
|
17
|
+
onStateChange: config.onConnectionStateChange,
|
|
18
|
+
onError: config.onError,
|
|
19
|
+
customizeOffer: config.customizeOffer
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async connect(localStream) {
|
|
23
|
+
return pRetry(async () => {
|
|
24
|
+
await this.connection.connect(this.config.webrtcUrl, localStream);
|
|
25
|
+
return true;
|
|
26
|
+
}, {
|
|
27
|
+
retries: 5,
|
|
28
|
+
factor: 2,
|
|
29
|
+
minTimeout: 1e3,
|
|
30
|
+
maxTimeout: 1e4,
|
|
31
|
+
onFailedAttempt: (error) => {
|
|
32
|
+
console.log(`[WebRTC] Retry ${error.attemptNumber} failed: ${error.message}`);
|
|
33
|
+
this.connection.cleanup();
|
|
34
|
+
},
|
|
35
|
+
shouldRetry: (error) => {
|
|
36
|
+
const msg = error.message.toLowerCase();
|
|
37
|
+
return !PERMANENT_ERRORS.some((err) => msg.includes(err));
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
sendMessage(message) {
|
|
42
|
+
this.connection.send(message);
|
|
43
|
+
}
|
|
44
|
+
cleanup() {
|
|
45
|
+
this.connection.cleanup();
|
|
46
|
+
}
|
|
47
|
+
isConnected() {
|
|
48
|
+
return this.connection.state === "connected";
|
|
49
|
+
}
|
|
50
|
+
getConnectionState() {
|
|
51
|
+
return this.connection.state;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
export { WebRTCManager };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/shared/model.d.ts
|
|
4
|
+
declare const realtimeModels: z.ZodUnion<readonly [z.ZodLiteral<"mirage">, z.ZodLiteral<"lucy_v2v_720p_rt">]>;
|
|
5
|
+
declare const videoModels: z.ZodUnion<readonly [z.ZodLiteral<"lucy-dev-i2v">, z.ZodLiteral<"lucy-dev-v2v">, z.ZodLiteral<"lucy-pro-t2v">, z.ZodLiteral<"lucy-pro-i2v">, z.ZodLiteral<"lucy-pro-v2v">, z.ZodLiteral<"lucy-pro-flf2v">]>;
|
|
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<"lucy_v2v_720p_rt">]>, z.ZodUnion<readonly [z.ZodLiteral<"lucy-dev-i2v">, z.ZodLiteral<"lucy-dev-v2v">, z.ZodLiteral<"lucy-pro-t2v">, z.ZodLiteral<"lucy-pro-i2v">, z.ZodLiteral<"lucy-pro-v2v">, z.ZodLiteral<"lucy-pro-flf2v">]>, z.ZodUnion<readonly [z.ZodLiteral<"lucy-pro-t2i">, z.ZodLiteral<"lucy-pro-i2i">]>]>;
|
|
8
|
+
type Model = z.infer<typeof modelSchema>;
|
|
9
|
+
type RealTimeModels = z.infer<typeof realtimeModels>;
|
|
10
|
+
type VideoModels = z.infer<typeof videoModels>;
|
|
11
|
+
type ImageModels = z.infer<typeof imageModels>;
|
|
12
|
+
declare const modelInputSchemas: {
|
|
13
|
+
readonly "lucy-pro-t2v": z.ZodObject<{
|
|
14
|
+
prompt: z.ZodString;
|
|
15
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
16
|
+
resolution: z.ZodOptional<z.ZodString>;
|
|
17
|
+
orientation: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
readonly "lucy-pro-t2i": z.ZodObject<{
|
|
20
|
+
prompt: z.ZodString;
|
|
21
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
22
|
+
resolution: z.ZodOptional<z.ZodString>;
|
|
23
|
+
orientation: z.ZodOptional<z.ZodString>;
|
|
24
|
+
}, z.core.$strip>;
|
|
25
|
+
readonly "lucy-pro-i2v": z.ZodObject<{
|
|
26
|
+
prompt: z.ZodString;
|
|
27
|
+
data: z.ZodUnion<readonly [z.ZodCustom<File, File>, z.ZodCustom<Blob, Blob>, z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>, z.ZodCustom<URL, URL>, z.ZodURL]>;
|
|
28
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
29
|
+
resolution: z.ZodOptional<z.ZodString>;
|
|
30
|
+
}, z.core.$strip>;
|
|
31
|
+
readonly "lucy-dev-i2v": z.ZodObject<{
|
|
32
|
+
prompt: z.ZodString;
|
|
33
|
+
data: z.ZodUnion<readonly [z.ZodCustom<File, File>, z.ZodCustom<Blob, Blob>, z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>, z.ZodCustom<URL, URL>, z.ZodURL]>;
|
|
34
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
35
|
+
resolution: z.ZodOptional<z.ZodString>;
|
|
36
|
+
}, z.core.$strip>;
|
|
37
|
+
readonly "lucy-pro-v2v": z.ZodObject<{
|
|
38
|
+
prompt: z.ZodString;
|
|
39
|
+
data: z.ZodUnion<readonly [z.ZodCustom<File, File>, z.ZodCustom<Blob, Blob>, z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>, z.ZodCustom<URL, URL>, z.ZodURL]>;
|
|
40
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
resolution: z.ZodOptional<z.ZodString>;
|
|
42
|
+
enhance_prompt: z.ZodOptional<z.ZodBoolean>;
|
|
43
|
+
num_inference_steps: z.ZodOptional<z.ZodNumber>;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
readonly "lucy-dev-v2v": z.ZodObject<{
|
|
46
|
+
prompt: z.ZodString;
|
|
47
|
+
data: z.ZodUnion<readonly [z.ZodCustom<File, File>, z.ZodCustom<Blob, Blob>, z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>, z.ZodCustom<URL, URL>, z.ZodURL]>;
|
|
48
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
49
|
+
resolution: z.ZodOptional<z.ZodString>;
|
|
50
|
+
enhance_prompt: z.ZodOptional<z.ZodBoolean>;
|
|
51
|
+
}, z.core.$strip>;
|
|
52
|
+
readonly "lucy-pro-flf2v": z.ZodObject<{
|
|
53
|
+
prompt: z.ZodString;
|
|
54
|
+
start: z.ZodUnion<readonly [z.ZodCustom<File, File>, z.ZodCustom<Blob, Blob>, z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>, z.ZodCustom<URL, URL>, z.ZodURL]>;
|
|
55
|
+
end: z.ZodUnion<readonly [z.ZodCustom<File, File>, z.ZodCustom<Blob, Blob>, z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>, z.ZodCustom<URL, URL>, z.ZodURL]>;
|
|
56
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
57
|
+
resolution: z.ZodOptional<z.ZodString>;
|
|
58
|
+
}, z.core.$strip>;
|
|
59
|
+
readonly "lucy-pro-i2i": z.ZodObject<{
|
|
60
|
+
prompt: z.ZodString;
|
|
61
|
+
data: z.ZodUnion<readonly [z.ZodCustom<File, File>, z.ZodCustom<Blob, Blob>, z.ZodCustom<ReadableStream<unknown>, ReadableStream<unknown>>, z.ZodCustom<URL, URL>, z.ZodURL]>;
|
|
62
|
+
seed: z.ZodOptional<z.ZodNumber>;
|
|
63
|
+
resolution: z.ZodOptional<z.ZodString>;
|
|
64
|
+
enhance_prompt: z.ZodOptional<z.ZodBoolean>;
|
|
65
|
+
}, z.core.$strip>;
|
|
66
|
+
};
|
|
67
|
+
type ModelInputSchemas = typeof modelInputSchemas;
|
|
68
|
+
type ModelDefinition<T extends Model = Model> = {
|
|
69
|
+
name: T;
|
|
70
|
+
urlPath: string;
|
|
71
|
+
fps: number;
|
|
72
|
+
width: number;
|
|
73
|
+
height: number;
|
|
74
|
+
inputSchema: T extends keyof ModelInputSchemas ? ModelInputSchemas[T] : z.ZodObject<any>;
|
|
75
|
+
};
|
|
76
|
+
declare const models: {
|
|
77
|
+
realtime: <T extends RealTimeModels>(model: T) => ModelDefinition<T>;
|
|
78
|
+
video: <T extends VideoModels>(model: T) => ModelDefinition<T>;
|
|
79
|
+
image: <T extends ImageModels>(model: T) => ModelDefinition<T>;
|
|
80
|
+
};
|
|
81
|
+
//#endregion
|
|
82
|
+
export { Model, ModelDefinition, ModelInputSchemas, models };
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { createModelNotFoundError } from "../utils/errors.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
//#region src/shared/model.ts
|
|
5
|
+
const realtimeModels = z.union([z.literal("mirage"), z.literal("lucy_v2v_720p_rt")]);
|
|
6
|
+
const videoModels = z.union([
|
|
7
|
+
z.literal("lucy-dev-i2v"),
|
|
8
|
+
z.literal("lucy-dev-v2v"),
|
|
9
|
+
z.literal("lucy-pro-t2v"),
|
|
10
|
+
z.literal("lucy-pro-i2v"),
|
|
11
|
+
z.literal("lucy-pro-v2v"),
|
|
12
|
+
z.literal("lucy-pro-flf2v")
|
|
13
|
+
]);
|
|
14
|
+
const imageModels = z.union([z.literal("lucy-pro-t2i"), z.literal("lucy-pro-i2i")]);
|
|
15
|
+
const modelSchema = z.union([
|
|
16
|
+
realtimeModels,
|
|
17
|
+
videoModels,
|
|
18
|
+
imageModels
|
|
19
|
+
]);
|
|
20
|
+
const fileInputSchema = z.union([
|
|
21
|
+
z.instanceof(File),
|
|
22
|
+
z.instanceof(Blob),
|
|
23
|
+
z.instanceof(ReadableStream),
|
|
24
|
+
z.instanceof(URL),
|
|
25
|
+
z.url()
|
|
26
|
+
]);
|
|
27
|
+
const modelInputSchemas = {
|
|
28
|
+
"lucy-pro-t2v": z.object({
|
|
29
|
+
prompt: z.string(),
|
|
30
|
+
seed: z.number().optional(),
|
|
31
|
+
resolution: z.string().optional(),
|
|
32
|
+
orientation: z.string().optional()
|
|
33
|
+
}),
|
|
34
|
+
"lucy-pro-t2i": z.object({
|
|
35
|
+
prompt: z.string(),
|
|
36
|
+
seed: z.number().optional(),
|
|
37
|
+
resolution: z.string().optional(),
|
|
38
|
+
orientation: z.string().optional()
|
|
39
|
+
}),
|
|
40
|
+
"lucy-pro-i2v": z.object({
|
|
41
|
+
prompt: z.string(),
|
|
42
|
+
data: fileInputSchema,
|
|
43
|
+
seed: z.number().optional(),
|
|
44
|
+
resolution: z.string().optional()
|
|
45
|
+
}),
|
|
46
|
+
"lucy-dev-i2v": z.object({
|
|
47
|
+
prompt: z.string(),
|
|
48
|
+
data: fileInputSchema,
|
|
49
|
+
seed: z.number().optional(),
|
|
50
|
+
resolution: z.string().optional()
|
|
51
|
+
}),
|
|
52
|
+
"lucy-pro-v2v": z.object({
|
|
53
|
+
prompt: z.string(),
|
|
54
|
+
data: fileInputSchema,
|
|
55
|
+
seed: z.number().optional(),
|
|
56
|
+
resolution: z.string().optional(),
|
|
57
|
+
enhance_prompt: z.boolean().optional(),
|
|
58
|
+
num_inference_steps: z.number().optional()
|
|
59
|
+
}),
|
|
60
|
+
"lucy-dev-v2v": z.object({
|
|
61
|
+
prompt: z.string(),
|
|
62
|
+
data: fileInputSchema,
|
|
63
|
+
seed: z.number().optional(),
|
|
64
|
+
resolution: z.string().optional(),
|
|
65
|
+
enhance_prompt: z.boolean().optional()
|
|
66
|
+
}),
|
|
67
|
+
"lucy-pro-flf2v": z.object({
|
|
68
|
+
prompt: z.string(),
|
|
69
|
+
start: fileInputSchema,
|
|
70
|
+
end: fileInputSchema,
|
|
71
|
+
seed: z.number().optional(),
|
|
72
|
+
resolution: z.string().optional()
|
|
73
|
+
}),
|
|
74
|
+
"lucy-pro-i2i": z.object({
|
|
75
|
+
prompt: z.string(),
|
|
76
|
+
data: fileInputSchema,
|
|
77
|
+
seed: z.number().optional(),
|
|
78
|
+
resolution: z.string().optional(),
|
|
79
|
+
enhance_prompt: z.boolean().optional()
|
|
80
|
+
})
|
|
81
|
+
};
|
|
82
|
+
const modelDefinitionSchema = z.object({
|
|
83
|
+
name: modelSchema,
|
|
84
|
+
urlPath: z.string(),
|
|
85
|
+
fps: z.number().min(1),
|
|
86
|
+
width: z.number().min(1),
|
|
87
|
+
height: z.number().min(1),
|
|
88
|
+
inputSchema: z.any()
|
|
89
|
+
});
|
|
90
|
+
const _models = {
|
|
91
|
+
realtime: {
|
|
92
|
+
mirage: {
|
|
93
|
+
urlPath: "/v1/stream",
|
|
94
|
+
name: "mirage",
|
|
95
|
+
fps: 25,
|
|
96
|
+
width: 1280,
|
|
97
|
+
height: 704,
|
|
98
|
+
inputSchema: z.object({})
|
|
99
|
+
},
|
|
100
|
+
lucy_v2v_720p_rt: {
|
|
101
|
+
urlPath: "/v1/stream",
|
|
102
|
+
name: "lucy_v2v_720p_rt",
|
|
103
|
+
fps: 25,
|
|
104
|
+
width: 1280,
|
|
105
|
+
height: 704,
|
|
106
|
+
inputSchema: z.object({})
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
image: {
|
|
110
|
+
"lucy-pro-t2i": {
|
|
111
|
+
urlPath: "/v1/generate/lucy-pro-t2i",
|
|
112
|
+
name: "lucy-pro-t2i",
|
|
113
|
+
fps: 25,
|
|
114
|
+
width: 1280,
|
|
115
|
+
height: 704,
|
|
116
|
+
inputSchema: modelInputSchemas["lucy-pro-t2i"]
|
|
117
|
+
},
|
|
118
|
+
"lucy-pro-i2i": {
|
|
119
|
+
urlPath: "/v1/generate/lucy-pro-i2i",
|
|
120
|
+
name: "lucy-pro-i2i",
|
|
121
|
+
fps: 25,
|
|
122
|
+
width: 1280,
|
|
123
|
+
height: 704,
|
|
124
|
+
inputSchema: modelInputSchemas["lucy-pro-i2i"]
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
video: {
|
|
128
|
+
"lucy-dev-i2v": {
|
|
129
|
+
urlPath: "/v1/generate/lucy-dev-i2v",
|
|
130
|
+
name: "lucy-dev-i2v",
|
|
131
|
+
fps: 25,
|
|
132
|
+
width: 1280,
|
|
133
|
+
height: 704,
|
|
134
|
+
inputSchema: modelInputSchemas["lucy-dev-i2v"]
|
|
135
|
+
},
|
|
136
|
+
"lucy-dev-v2v": {
|
|
137
|
+
urlPath: "/v1/generate/lucy-dev-v2v",
|
|
138
|
+
name: "lucy-dev-v2v",
|
|
139
|
+
fps: 25,
|
|
140
|
+
width: 1280,
|
|
141
|
+
height: 704,
|
|
142
|
+
inputSchema: modelInputSchemas["lucy-dev-v2v"]
|
|
143
|
+
},
|
|
144
|
+
"lucy-pro-t2v": {
|
|
145
|
+
urlPath: "/v1/generate/lucy-pro-t2v",
|
|
146
|
+
name: "lucy-pro-t2v",
|
|
147
|
+
fps: 25,
|
|
148
|
+
width: 1280,
|
|
149
|
+
height: 704,
|
|
150
|
+
inputSchema: modelInputSchemas["lucy-pro-t2v"]
|
|
151
|
+
},
|
|
152
|
+
"lucy-pro-i2v": {
|
|
153
|
+
urlPath: "/v1/generate/lucy-pro-i2v",
|
|
154
|
+
name: "lucy-pro-i2v",
|
|
155
|
+
fps: 25,
|
|
156
|
+
width: 1280,
|
|
157
|
+
height: 704,
|
|
158
|
+
inputSchema: modelInputSchemas["lucy-pro-i2v"]
|
|
159
|
+
},
|
|
160
|
+
"lucy-pro-v2v": {
|
|
161
|
+
urlPath: "/v1/generate/lucy-pro-v2v",
|
|
162
|
+
name: "lucy-pro-v2v",
|
|
163
|
+
fps: 25,
|
|
164
|
+
width: 1280,
|
|
165
|
+
height: 704,
|
|
166
|
+
inputSchema: modelInputSchemas["lucy-pro-v2v"]
|
|
167
|
+
},
|
|
168
|
+
"lucy-pro-flf2v": {
|
|
169
|
+
urlPath: "/v1/generate/lucy-pro-flf2v",
|
|
170
|
+
name: "lucy-pro-flf2v",
|
|
171
|
+
fps: 25,
|
|
172
|
+
width: 1280,
|
|
173
|
+
height: 704,
|
|
174
|
+
inputSchema: modelInputSchemas["lucy-pro-flf2v"]
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const models = {
|
|
179
|
+
realtime: (model) => {
|
|
180
|
+
const modelDefinition = _models.realtime[model];
|
|
181
|
+
if (!modelDefinition) throw createModelNotFoundError(model);
|
|
182
|
+
return modelDefinition;
|
|
183
|
+
},
|
|
184
|
+
video: (model) => {
|
|
185
|
+
const modelDefinition = _models.video[model];
|
|
186
|
+
if (!modelDefinition) throw createModelNotFoundError(model);
|
|
187
|
+
return modelDefinition;
|
|
188
|
+
},
|
|
189
|
+
image: (model) => {
|
|
190
|
+
const modelDefinition = _models.image[model];
|
|
191
|
+
if (!modelDefinition) throw createModelNotFoundError(model);
|
|
192
|
+
return modelDefinition;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
//#endregion
|
|
197
|
+
export { modelDefinitionSchema, models };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/shared/types.d.ts
|
|
4
|
+
declare const modelStateSchema: z.ZodObject<{
|
|
5
|
+
prompt: z.ZodOptional<z.ZodObject<{
|
|
6
|
+
text: z.ZodString;
|
|
7
|
+
enrich: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
8
|
+
}, z.core.$strip>>;
|
|
9
|
+
mirror: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
type ModelState = z.infer<typeof modelStateSchema>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ModelState };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/shared/types.ts
|
|
4
|
+
const modelStateSchema = z.object({
|
|
5
|
+
prompt: z.object({
|
|
6
|
+
text: z.string().min(1),
|
|
7
|
+
enrich: z.boolean().optional().default(true)
|
|
8
|
+
}).optional(),
|
|
9
|
+
mirror: z.boolean().optional().default(false)
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
export { modelStateSchema };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/utils/errors.d.ts
|
|
2
|
+
type DecartSDKError = {
|
|
3
|
+
code: string;
|
|
4
|
+
message: string;
|
|
5
|
+
data?: Record<string, unknown>;
|
|
6
|
+
cause?: Error;
|
|
7
|
+
};
|
|
8
|
+
declare const ERROR_CODES: {
|
|
9
|
+
readonly INVALID_API_KEY: "INVALID_API_KEY";
|
|
10
|
+
readonly INVALID_BASE_URL: "INVALID_BASE_URL";
|
|
11
|
+
readonly WEB_RTC_ERROR: "WEB_RTC_ERROR";
|
|
12
|
+
readonly PROCESSING_ERROR: "PROCESSING_ERROR";
|
|
13
|
+
readonly INVALID_INPUT: "INVALID_INPUT";
|
|
14
|
+
readonly INVALID_OPTIONS: "INVALID_OPTIONS";
|
|
15
|
+
readonly MODEL_NOT_FOUND: "MODEL_NOT_FOUND";
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { DecartSDKError, ERROR_CODES };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/utils/errors.ts
|
|
2
|
+
const ERROR_CODES = {
|
|
3
|
+
INVALID_API_KEY: "INVALID_API_KEY",
|
|
4
|
+
INVALID_BASE_URL: "INVALID_BASE_URL",
|
|
5
|
+
WEB_RTC_ERROR: "WEB_RTC_ERROR",
|
|
6
|
+
PROCESSING_ERROR: "PROCESSING_ERROR",
|
|
7
|
+
INVALID_INPUT: "INVALID_INPUT",
|
|
8
|
+
INVALID_OPTIONS: "INVALID_OPTIONS",
|
|
9
|
+
MODEL_NOT_FOUND: "MODEL_NOT_FOUND"
|
|
10
|
+
};
|
|
11
|
+
function createSDKError(code, message, data, cause) {
|
|
12
|
+
return {
|
|
13
|
+
code,
|
|
14
|
+
message,
|
|
15
|
+
data,
|
|
16
|
+
cause
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function createInvalidApiKeyError() {
|
|
20
|
+
return createSDKError(ERROR_CODES.INVALID_API_KEY, "API key is required and must be a non-empty string");
|
|
21
|
+
}
|
|
22
|
+
function createInvalidBaseUrlError(url) {
|
|
23
|
+
return createSDKError(ERROR_CODES.INVALID_BASE_URL, `Invalid base URL${url ? `: ${url}` : ""}`);
|
|
24
|
+
}
|
|
25
|
+
function createWebrtcError(error) {
|
|
26
|
+
return createSDKError(ERROR_CODES.WEB_RTC_ERROR, "WebRTC error", { cause: error });
|
|
27
|
+
}
|
|
28
|
+
function createInvalidInputError(message) {
|
|
29
|
+
return createSDKError(ERROR_CODES.INVALID_INPUT, message);
|
|
30
|
+
}
|
|
31
|
+
function createModelNotFoundError(model) {
|
|
32
|
+
return createSDKError(ERROR_CODES.MODEL_NOT_FOUND, `Model ${model} not found`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { ERROR_CODES, createInvalidApiKeyError, createInvalidBaseUrlError, createInvalidInputError, createModelNotFoundError, createSDKError, createWebrtcError };
|