@decartai/sdk 0.0.67 → 0.1.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.
Files changed (45) hide show
  1. package/README.md +55 -5
  2. package/dist/index.d.ts +7 -5
  3. package/dist/index.js +43 -28
  4. package/dist/process/client.js +1 -3
  5. package/dist/process/request.js +1 -3
  6. package/dist/queue/client.js +1 -3
  7. package/dist/queue/polling.js +1 -2
  8. package/dist/queue/request.js +1 -3
  9. package/dist/realtime/client.d.ts +23 -13
  10. package/dist/realtime/client.js +74 -244
  11. package/dist/realtime/config-realtime.js +49 -0
  12. package/dist/realtime/event-buffer.js +1 -3
  13. package/dist/realtime/initial-state-gate.js +21 -0
  14. package/dist/realtime/media-channel.js +82 -0
  15. package/dist/realtime/methods.js +12 -42
  16. package/dist/realtime/mirror-stream.js +1 -2
  17. package/dist/realtime/observability/diagnostics.d.ts +39 -0
  18. package/dist/realtime/observability/livekit-stats-provider.js +25 -0
  19. package/dist/realtime/observability/realtime-observability.js +173 -0
  20. package/dist/realtime/{telemetry-reporter.js → observability/telemetry-reporter.js} +12 -31
  21. package/dist/realtime/observability/webrtc-stats.d.ts +148 -0
  22. package/dist/realtime/observability/webrtc-stats.js +276 -0
  23. package/dist/realtime/signaling-channel.js +286 -0
  24. package/dist/realtime/stream-session.js +252 -0
  25. package/dist/realtime/subscribe-client.d.ts +2 -1
  26. package/dist/realtime/subscribe-client.js +115 -11
  27. package/dist/realtime/types.d.ts +25 -1
  28. package/dist/shared/model.d.ts +11 -1
  29. package/dist/shared/model.js +51 -14
  30. package/dist/shared/request.js +1 -3
  31. package/dist/shared/types.js +1 -3
  32. package/dist/tokens/client.js +1 -3
  33. package/dist/utils/env.js +1 -2
  34. package/dist/utils/errors.js +1 -2
  35. package/dist/utils/logger.js +1 -2
  36. package/dist/utils/media.js +43 -0
  37. package/dist/utils/platform.js +13 -0
  38. package/dist/utils/user-agent.js +1 -3
  39. package/dist/version.js +1 -2
  40. package/package.json +2 -1
  41. package/dist/realtime/diagnostics.d.ts +0 -78
  42. package/dist/realtime/webrtc-connection.js +0 -501
  43. package/dist/realtime/webrtc-manager.js +0 -189
  44. package/dist/realtime/webrtc-stats.d.ts +0 -59
  45. package/dist/realtime/webrtc-stats.js +0 -154
@@ -0,0 +1,252 @@
1
+ import { createConsoleLogger } from "../utils/logger.js";
2
+ import { REALTIME_CONFIG } from "./config-realtime.js";
3
+ import { InitialStateGate } from "./initial-state-gate.js";
4
+ import { MediaChannel } from "./media-channel.js";
5
+ import { SignalingChannel } from "./signaling-channel.js";
6
+ import mitt from "mitt";
7
+ import pRetry, { AbortError } from "p-retry";
8
+ //#region src/realtime/stream-session.ts
9
+ function encodeSubscribeToken(roomName) {
10
+ return btoa(JSON.stringify({ room_name: roomName }));
11
+ }
12
+ function getInitialImageSizeKb(image) {
13
+ if (!image) return null;
14
+ const commaIdx = image.indexOf(",");
15
+ const base64 = commaIdx >= 0 && image.startsWith("data:") ? image.slice(commaIdx + 1) : image;
16
+ const padding = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
17
+ const bytes = Math.floor(base64.length * 3 / 4) - padding;
18
+ return Math.max(0, Math.round(bytes / 1024));
19
+ }
20
+ var StreamSession = class {
21
+ signaling;
22
+ media;
23
+ events = mitt();
24
+ state = "disconnected";
25
+ queue = null;
26
+ disposed = false;
27
+ currentAttempt = 0;
28
+ initialStateGate = new InitialStateGate();
29
+ logger;
30
+ constructor(config) {
31
+ this.config = config;
32
+ this.logger = config.logger ?? createConsoleLogger("warn");
33
+ this.createTransport();
34
+ }
35
+ on(event, handler) {
36
+ this.events.on(event, handler);
37
+ }
38
+ off(event, handler) {
39
+ this.events.off(event, handler);
40
+ }
41
+ getStatus() {
42
+ return {
43
+ connection: this.state,
44
+ queue: this.queue
45
+ };
46
+ }
47
+ getConnectionState() {
48
+ return this.state;
49
+ }
50
+ isConnected() {
51
+ return this.state === "connected" || this.state === "generating";
52
+ }
53
+ async connect() {
54
+ this.disposed = false;
55
+ const attempt = ++this.currentAttempt;
56
+ this.setState("connecting");
57
+ this.logger.info("realtime connect: starting", { attemptCycle: attempt });
58
+ try {
59
+ await pRetry(() => this.runOneConnect(attempt), this.retryOptionsFor(attempt));
60
+ } catch (error) {
61
+ const message = error instanceof Error ? error.message : String(error);
62
+ this.logger.error("realtime connect: exhausted all retries", { error: message });
63
+ if (this.currentAttempt === attempt && !this.disposed) this.setState("disconnected");
64
+ throw error;
65
+ }
66
+ }
67
+ async sendPrompt(text, opts) {
68
+ this.assertConnected();
69
+ return this.signaling.sendPrompt(text, opts);
70
+ }
71
+ async setImage(image, opts) {
72
+ this.assertConnected();
73
+ return this.signaling.setImage(image, opts);
74
+ }
75
+ disconnect() {
76
+ this.disposed = true;
77
+ this.tearDown();
78
+ this.setState("disconnected");
79
+ }
80
+ assertConnected() {
81
+ if (!this.isConnected()) throw new Error(`Cannot send message: connection is ${this.state}`);
82
+ }
83
+ retryOptionsFor(attempt) {
84
+ return {
85
+ ...REALTIME_CONFIG.session.retry,
86
+ onFailedAttempt: (_error) => {
87
+ this.tearDown();
88
+ },
89
+ shouldRetry: (error) => {
90
+ if (this.disposed || this.currentAttempt !== attempt) return false;
91
+ const msg = error.message.toLowerCase();
92
+ const permanent = REALTIME_CONFIG.session.permanentErrorSubstrings.some((err) => msg.includes(err));
93
+ if (permanent) this.logger.error("realtime connect: permanent error, not retrying", { error: error.message });
94
+ return !permanent;
95
+ }
96
+ };
97
+ }
98
+ async runOneConnect(attempt) {
99
+ if (this.disposed || this.currentAttempt !== attempt) throw new AbortError("Stale connect attempt");
100
+ try {
101
+ this.resetHandshakeState();
102
+ const initialState = this.getInitialState();
103
+ this.config.observability?.beginConnectionBreakdown(attempt, getInitialImageSizeKb(initialState?.image));
104
+ const gateAttempt = this.initialStateGate.startAttempt(initialState);
105
+ const { roomInfo, initialStateAck } = await this.signaling.openAndJoin({
106
+ connectTimeout: REALTIME_CONFIG.session.connectionTimeoutMs,
107
+ initialState
108
+ });
109
+ if (this.disposed || this.currentAttempt !== attempt) {
110
+ this.tearDown();
111
+ throw new AbortError("Stale connect attempt");
112
+ }
113
+ this.queue = null;
114
+ try {
115
+ await this.media.connect({
116
+ url: roomInfo.livekitUrl,
117
+ token: roomInfo.token
118
+ });
119
+ if (!await gateAttempt.waitForReadiness(initialStateAck)) throw new AbortError("Stale connect attempt");
120
+ await this.media.publishLocalTracks();
121
+ } catch (error) {
122
+ this.tearDown();
123
+ throw error;
124
+ }
125
+ if (this.disposed || this.currentAttempt !== attempt) {
126
+ this.tearDown();
127
+ throw new AbortError("Stale connect attempt");
128
+ }
129
+ this.config.observability?.finishConnectionBreakdown({ success: true });
130
+ this.setState("connected");
131
+ this.events.emit("sessionStarted", {
132
+ sessionId: roomInfo.sessionId,
133
+ subscribeToken: encodeSubscribeToken(roomInfo.roomName)
134
+ });
135
+ } catch (error) {
136
+ this.config.observability?.finishConnectionBreakdown({
137
+ success: false,
138
+ error: error instanceof Error ? error.message : String(error)
139
+ });
140
+ throw error;
141
+ }
142
+ }
143
+ getInitialState() {
144
+ if (this.config.initialImage !== void 0) return {
145
+ image: this.config.initialImage,
146
+ prompt: this.config.initialPrompt?.text,
147
+ enhance: this.config.initialPrompt?.enhance
148
+ };
149
+ if (this.config.initialPrompt) return {
150
+ prompt: this.config.initialPrompt.text,
151
+ enhance: this.config.initialPrompt.enhance
152
+ };
153
+ if (this.config.localStream) return {
154
+ image: null,
155
+ prompt: null
156
+ };
157
+ }
158
+ wireSignalingEvents() {
159
+ this.signaling.on("queuePosition", (qp) => {
160
+ this.queue = qp;
161
+ this.events.emit("queuePosition", qp);
162
+ });
163
+ this.signaling.on("generationTick", (e) => this.events.emit("generationTick", e));
164
+ this.signaling.on("generationEnded", (e) => this.events.emit("generationEnded", e));
165
+ this.signaling.on("serverError", (err) => this.events.emit("error", err));
166
+ this.signaling.on("closed", (info) => this.handleConnectionLoss({
167
+ source: "signaling",
168
+ ...info
169
+ }));
170
+ }
171
+ wireMediaEvents() {
172
+ this.media.on("remoteStream", (stream) => this.events.emit("remoteStream", stream));
173
+ this.media.on("firstFrame", () => {
174
+ if (this.state === "connected") this.setState("generating");
175
+ });
176
+ this.media.on("disconnected", (info) => this.handleConnectionLoss({
177
+ source: "media",
178
+ reason: info.reason
179
+ }));
180
+ }
181
+ handleConnectionLoss(cause) {
182
+ if (this.disposed) return;
183
+ if (this.state !== "connected" && this.state !== "generating") {
184
+ this.logger.debug("connection loss ignored (not connected)", {
185
+ state: this.state,
186
+ ...cause
187
+ });
188
+ return;
189
+ }
190
+ this.logger.warn("realtime connection lost; scheduling reconnect", {
191
+ state: this.state,
192
+ ...cause
193
+ });
194
+ this.scheduleReconnect();
195
+ }
196
+ scheduleReconnect() {
197
+ const attempt = ++this.currentAttempt;
198
+ this.setState("reconnecting");
199
+ pRetry(async () => {
200
+ if (this.disposed || this.currentAttempt !== attempt) throw new AbortError("Reconnect cancelled");
201
+ this.tearDown();
202
+ this.createTransport();
203
+ await this.runOneConnect(attempt);
204
+ }, this.retryOptionsFor(attempt)).then(() => {
205
+ if (this.disposed || this.currentAttempt !== attempt) return;
206
+ this.logger.info("realtime reconnect: succeeded");
207
+ }).catch((error) => {
208
+ if (this.disposed || this.currentAttempt !== attempt) return;
209
+ const message = error instanceof Error ? error.message : String(error);
210
+ this.logger.error("realtime reconnect: failed permanently", { error: message });
211
+ this.tearDown();
212
+ this.setState("disconnected");
213
+ this.events.emit("error", error instanceof Error ? error : new Error(String(error)));
214
+ });
215
+ }
216
+ createTransport() {
217
+ this.signaling = new SignalingChannel({
218
+ url: this.config.url,
219
+ integration: this.config.integration,
220
+ logger: this.logger,
221
+ observability: this.config.observability
222
+ });
223
+ this.media = new MediaChannel({
224
+ observability: this.config.observability,
225
+ localStream: this.config.localStream,
226
+ logger: this.logger,
227
+ videoCodec: this.config.videoCodec
228
+ });
229
+ this.wireSignalingEvents();
230
+ this.wireMediaEvents();
231
+ }
232
+ tearDown() {
233
+ this.signaling.close();
234
+ this.media.disconnect();
235
+ this.initialStateGate.reset();
236
+ this.resetHandshakeState();
237
+ }
238
+ resetHandshakeState() {
239
+ this.queue = null;
240
+ }
241
+ setState(state) {
242
+ if (this.state === state) return;
243
+ this.logger.debug("realtime state change", {
244
+ from: this.state,
245
+ to: state
246
+ });
247
+ this.state = state;
248
+ this.events.emit("connectionChange", state);
249
+ }
250
+ };
251
+ //#endregion
252
+ export { StreamSession };
@@ -1,5 +1,5 @@
1
1
  import { DecartSDKError } from "../utils/errors.js";
2
- import { DiagnosticEvent } from "./diagnostics.js";
2
+ import { DiagnosticEvent } from "./observability/diagnostics.js";
3
3
  import { ConnectionState } from "./types.js";
4
4
 
5
5
  //#region src/realtime/subscribe-client.d.ts
@@ -19,6 +19,7 @@ type RealTimeSubscribeClient = {
19
19
  type SubscribeOptions = {
20
20
  token: string;
21
21
  onRemoteStream: (stream: MediaStream) => void;
22
+ onConnectionChange?: (state: ConnectionState) => void;
22
23
  };
23
24
  //#endregion
24
25
  export { RealTimeSubscribeClient, SubscribeEvents, SubscribeOptions };
@@ -1,20 +1,124 @@
1
+ import { classifyWebrtcError } from "../utils/errors.js";
2
+ import { createConsoleLogger } from "../utils/logger.js";
3
+ import { createEventBuffer } from "./event-buffer.js";
4
+ import { REALTIME_CONFIG } from "./config-realtime.js";
5
+ import { RealtimeObservability } from "./observability/realtime-observability.js";
6
+ import { ConnectionState, Room, RoomEvent, Track } from "livekit-client";
1
7
  //#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
8
  function decodeSubscribeToken(token) {
10
9
  try {
11
10
  const payload = JSON.parse(atob(token));
12
- if (!payload.sid || !payload.ip || !payload.port) throw new Error("Invalid subscribe token format");
13
- return payload;
11
+ if (!payload.room_name || typeof payload.room_name !== "string") throw new Error("Invalid subscribe token format");
12
+ return { room_name: payload.room_name };
14
13
  } catch {
15
14
  throw new Error("Invalid subscribe token");
16
15
  }
17
16
  }
18
-
17
+ function mapLiveKitState(state) {
18
+ switch (state) {
19
+ case ConnectionState.Connecting: return "connecting";
20
+ case ConnectionState.Connected: return "connected";
21
+ case ConnectionState.Reconnecting:
22
+ case ConnectionState.SignalReconnecting: return "reconnecting";
23
+ case ConnectionState.Disconnected: return "disconnected";
24
+ default: return "disconnected";
25
+ }
26
+ }
27
+ async function fetchWatchStreamCredentials(opts) {
28
+ if (!/^https?:\/\//i.test(opts.baseUrl)) throw new Error(`watch-stream baseUrl must use http(s); got ${opts.baseUrl}`);
29
+ const url = `${opts.baseUrl}/watch-stream/${encodeURIComponent(opts.roomName)}`;
30
+ const res = await fetch(url, {
31
+ method: "POST",
32
+ headers: {
33
+ "x-api-key": opts.apiKey,
34
+ "content-type": "application/json"
35
+ }
36
+ });
37
+ if (!res.ok) {
38
+ const body = await res.text().catch(() => "");
39
+ throw new Error(`watch-stream request failed (${res.status}): ${body || res.statusText}`);
40
+ }
41
+ const json = await res.json();
42
+ if (!json.livekit_url || !json.token || !json.room_name) throw new Error("watch-stream response missing required fields");
43
+ return {
44
+ livekit_url: json.livekit_url,
45
+ token: json.token,
46
+ room_name: json.room_name
47
+ };
48
+ }
49
+ const createRealTimeSubscribeClient = (opts) => {
50
+ const { baseUrl, apiKey, integration } = opts;
51
+ const logger = opts.logger ?? createConsoleLogger("info");
52
+ const subscribe = async (options) => {
53
+ const { room_name: roomName } = decodeSubscribeToken(options.token);
54
+ const { emitter, emitOrBuffer, flush, stop } = createEventBuffer();
55
+ let observability;
56
+ let room;
57
+ let currentState = "connecting";
58
+ let remoteStream = null;
59
+ const setState = (state) => {
60
+ if (currentState === state) return;
61
+ currentState = state;
62
+ options.onConnectionChange?.(state);
63
+ emitOrBuffer("connectionChange", state);
64
+ };
65
+ try {
66
+ observability = new RealtimeObservability({
67
+ telemetryEnabled: false,
68
+ apiKey,
69
+ integration,
70
+ logger,
71
+ onDiagnostic: (event) => emitOrBuffer("diagnostic", event)
72
+ });
73
+ setState("connecting");
74
+ const creds = await fetchWatchStreamCredentials({
75
+ baseUrl,
76
+ apiKey,
77
+ roomName
78
+ });
79
+ room = new Room(REALTIME_CONFIG.livekit.roomOptions);
80
+ const activeRoom = room;
81
+ activeRoom.on(RoomEvent.TrackSubscribed, (track, _pub, participant) => {
82
+ if (!participant.identity.startsWith(REALTIME_CONFIG.livekit.inferenceServerIdentityPrefix)) return;
83
+ if (track.kind !== Track.Kind.Video && track.kind !== Track.Kind.Audio) return;
84
+ track.attach();
85
+ const mediaStreamTrack = track.mediaStreamTrack;
86
+ if (!mediaStreamTrack) return;
87
+ remoteStream ??= new MediaStream();
88
+ if (!remoteStream.getTracks().includes(mediaStreamTrack)) remoteStream.addTrack(mediaStreamTrack);
89
+ options.onRemoteStream(remoteStream);
90
+ });
91
+ activeRoom.on(RoomEvent.ConnectionStateChanged, (state) => {
92
+ setState(mapLiveKitState(state));
93
+ });
94
+ activeRoom.on(RoomEvent.Disconnected, () => {
95
+ setState("disconnected");
96
+ });
97
+ await activeRoom.connect(creds.livekit_url, creds.token);
98
+ observability.setLiveKitRoom(activeRoom);
99
+ setState("connected");
100
+ const client = {
101
+ isConnected: () => activeRoom.state === ConnectionState.Connected,
102
+ getConnectionState: () => mapLiveKitState(activeRoom.state),
103
+ disconnect: () => {
104
+ observability?.stop();
105
+ stop();
106
+ activeRoom.disconnect().catch(() => {});
107
+ },
108
+ on: emitter.on,
109
+ off: emitter.off
110
+ };
111
+ flush();
112
+ return client;
113
+ } catch (error) {
114
+ observability?.stop();
115
+ if (room) room.disconnect().catch(() => {});
116
+ const err = error instanceof Error ? error : new Error(String(error));
117
+ logger.error("Realtime subscribe error", { error: err.message });
118
+ throw classifyWebrtcError(err);
119
+ }
120
+ };
121
+ return { subscribe };
122
+ };
19
123
  //#endregion
20
- export { decodeSubscribeToken, encodeSubscribeToken };
124
+ export { createRealTimeSubscribeClient };
@@ -1,5 +1,29 @@
1
1
  //#region src/realtime/types.d.ts
2
2
 
3
+ type GenerationEndedMessage = GenerationEnded & {
4
+ type: "generation_ended";
5
+ };
6
+ type QueuePositionMessage = {
7
+ type: "queue_position";
8
+ position: number;
9
+ queue_size: number;
10
+ };
11
+ type QueuePosition = {
12
+ position: number;
13
+ queueSize: number;
14
+ };
3
15
  type ConnectionState = "connecting" | "connected" | "generating" | "disconnected" | "reconnecting";
16
+ type GenerationTick = {
17
+ seconds: number;
18
+ };
19
+ type GenerationEnded = {
20
+ seconds: number;
21
+ reason: string;
22
+ };
23
+ type ImageSetOptions = {
24
+ prompt?: string | null;
25
+ enhance?: boolean;
26
+ timeout?: number;
27
+ };
4
28
  //#endregion
5
- export { ConnectionState };
29
+ export { ConnectionState, GenerationEnded, GenerationEndedMessage, GenerationTick, ImageSetOptions, QueuePosition, QueuePositionMessage };
@@ -318,11 +318,17 @@ declare const modelInputSchemas: {
318
318
  }, z.core.$strip>;
319
319
  };
320
320
  type ModelInputSchemas = typeof modelInputSchemas;
321
+ type ModelFps = number | {
322
+ max?: number;
323
+ min?: number;
324
+ ideal?: number;
325
+ exact?: number;
326
+ };
321
327
  type ModelDefinition<T extends Model = Model> = {
322
328
  name: T;
323
329
  urlPath: string;
324
330
  queueUrlPath?: string;
325
- fps: number;
331
+ fps: ModelFps;
326
332
  width: number;
327
333
  height: number;
328
334
  inputSchema: T extends keyof ModelInputSchemas ? ModelInputSchemas[T] : z.ZodTypeAny;
@@ -341,6 +347,7 @@ type CustomModelDefinition = Omit<ModelDefinition, "name" | "inputSchema"> & {
341
347
  * Requires `queueUrlPath` to distinguish from realtime definitions of the same model name.
342
348
  */
343
349
  type ImageModelDefinition = ModelDefinition<ImageModels> & {
350
+ fps: number;
344
351
  queueUrlPath: string;
345
352
  };
346
353
  /**
@@ -349,6 +356,7 @@ type ImageModelDefinition = ModelDefinition<ImageModels> & {
349
356
  * Requires `queueUrlPath` to distinguish from realtime definitions of the same model name.
350
357
  */
351
358
  type VideoModelDefinition = ModelDefinition<VideoModels> & {
359
+ fps: number;
352
360
  queueUrlPath: string;
353
361
  };
354
362
  type ModelKind = "realtime" | "video" | "image";
@@ -386,6 +394,7 @@ declare const models: {
386
394
  * - `"lucy-restyle-2"` - Video restyling
387
395
  */
388
396
  video: <T extends VideoModels>(model: T) => ModelDefinition<T> & {
397
+ fps: number;
389
398
  queueUrlPath: string;
390
399
  };
391
400
  /**
@@ -395,6 +404,7 @@ declare const models: {
395
404
  * - `"lucy-image-2"` - Image-to-image editing
396
405
  */
397
406
  image: <T extends ImageModels>(model: T) => ModelDefinition<T> & {
407
+ fps: number;
398
408
  queueUrlPath: string;
399
409
  };
400
410
  };
@@ -1,6 +1,5 @@
1
1
  import { createModelNotFoundError } from "../utils/errors.js";
2
2
  import { z } from "zod";
3
-
4
3
  //#region src/shared/model.ts
5
4
  const CANONICAL_MODEL_NAMES = [
6
5
  "lucy-2.1",
@@ -195,11 +194,20 @@ const modelInputSchemas = {
195
194
  "lucy-pro-i2i": imageEditSchema,
196
195
  "lucy-restyle-v2v": restyleSchema
197
196
  };
197
+ function resolveFpsNumber(fps, fallback = 30) {
198
+ if (typeof fps === "number") return fps;
199
+ return fps.ideal ?? fps.max ?? fps.exact ?? fps.min ?? fallback;
200
+ }
198
201
  const modelDefinitionSchema = z.object({
199
202
  name: z.string(),
200
203
  urlPath: z.string(),
201
204
  queueUrlPath: z.string().optional(),
202
- fps: z.number().min(1),
205
+ fps: z.union([z.number().min(1), z.object({
206
+ max: z.number().min(1).optional(),
207
+ min: z.number().min(1).optional(),
208
+ ideal: z.number().min(1).optional(),
209
+ exact: z.number().min(1).optional()
210
+ })]),
203
211
  width: z.number().min(1),
204
212
  height: z.number().min(1),
205
213
  inputSchema: z.any().optional()
@@ -209,7 +217,10 @@ const _models = {
209
217
  "lucy-2.1": {
210
218
  urlPath: "/v1/stream",
211
219
  name: "lucy-2.1",
212
- fps: 20,
220
+ fps: {
221
+ ideal: 30,
222
+ max: 30
223
+ },
213
224
  width: 1088,
214
225
  height: 624,
215
226
  inputSchema: z.object({})
@@ -217,7 +228,10 @@ const _models = {
217
228
  "lucy-2.1-vton": {
218
229
  urlPath: "/v1/stream",
219
230
  name: "lucy-2.1-vton",
220
- fps: 20,
231
+ fps: {
232
+ ideal: 30,
233
+ max: 30
234
+ },
221
235
  width: 1088,
222
236
  height: 624,
223
237
  inputSchema: z.object({})
@@ -225,7 +239,10 @@ const _models = {
225
239
  "lucy-vton-2": {
226
240
  urlPath: "/v1/stream",
227
241
  name: "lucy-vton-2",
228
- fps: 20,
242
+ fps: {
243
+ ideal: 30,
244
+ max: 30
245
+ },
229
246
  width: 1088,
230
247
  height: 624,
231
248
  inputSchema: z.object({})
@@ -233,7 +250,10 @@ const _models = {
233
250
  "lucy-restyle-2": {
234
251
  urlPath: "/v1/stream",
235
252
  name: "lucy-restyle-2",
236
- fps: 22,
253
+ fps: {
254
+ ideal: 30,
255
+ max: 30
256
+ },
237
257
  width: 1280,
238
258
  height: 704,
239
259
  inputSchema: z.object({})
@@ -241,7 +261,10 @@ const _models = {
241
261
  "lucy-latest": {
242
262
  urlPath: "/v1/stream",
243
263
  name: "lucy-latest",
244
- fps: 20,
264
+ fps: {
265
+ ideal: 30,
266
+ max: 30
267
+ },
245
268
  width: 1088,
246
269
  height: 624,
247
270
  inputSchema: z.object({})
@@ -249,7 +272,10 @@ const _models = {
249
272
  "lucy-vton-latest": {
250
273
  urlPath: "/v1/stream",
251
274
  name: "lucy-vton-latest",
252
- fps: 20,
275
+ fps: {
276
+ ideal: 30,
277
+ max: 30
278
+ },
253
279
  width: 1088,
254
280
  height: 624,
255
281
  inputSchema: z.object({})
@@ -257,7 +283,10 @@ const _models = {
257
283
  "lucy-restyle-latest": {
258
284
  urlPath: "/v1/stream",
259
285
  name: "lucy-restyle-latest",
260
- fps: 22,
286
+ fps: {
287
+ ideal: 30,
288
+ max: 30
289
+ },
261
290
  width: 1280,
262
291
  height: 704,
263
292
  inputSchema: z.object({})
@@ -265,7 +294,10 @@ const _models = {
265
294
  mirage_v2: {
266
295
  urlPath: "/v1/stream",
267
296
  name: "mirage_v2",
268
- fps: 22,
297
+ fps: {
298
+ ideal: 30,
299
+ max: 30
300
+ },
269
301
  width: 1280,
270
302
  height: 704,
271
303
  inputSchema: z.object({})
@@ -273,7 +305,10 @@ const _models = {
273
305
  "lucy-vton": {
274
306
  urlPath: "/v1/stream",
275
307
  name: "lucy-vton",
276
- fps: 20,
308
+ fps: {
309
+ ideal: 30,
310
+ max: 30
311
+ },
277
312
  width: 1088,
278
313
  height: 624,
279
314
  inputSchema: z.object({})
@@ -281,7 +316,10 @@ const _models = {
281
316
  "lucy-2.1-vton-2": {
282
317
  urlPath: "/v1/stream",
283
318
  name: "lucy-2.1-vton-2",
284
- fps: 20,
319
+ fps: {
320
+ ideal: 30,
321
+ max: 30
322
+ },
285
323
  width: 1088,
286
324
  height: 624,
287
325
  inputSchema: z.object({})
@@ -479,6 +517,5 @@ const models = {
479
517
  return modelDefinition;
480
518
  }
481
519
  };
482
-
483
520
  //#endregion
484
- export { isCanonicalModel, isImageModel, isModel, isRealtimeModel, isVideoModel, listModels, modelAliases, modelDefinitionSchema, models, resolveCanonicalModelAlias, resolveModelAlias };
521
+ export { isCanonicalModel, isImageModel, isModel, isRealtimeModel, isVideoModel, listModels, modelAliases, modelDefinitionSchema, models, resolveCanonicalModelAlias, resolveFpsNumber, resolveModelAlias };
@@ -1,6 +1,5 @@
1
1
  import { createInvalidInputError } from "../utils/errors.js";
2
2
  import { buildUserAgent } from "../utils/user-agent.js";
3
-
4
3
  //#region src/shared/request.ts
5
4
  /**
6
5
  * Type guard to check if a value is a React Native file object.
@@ -46,6 +45,5 @@ function buildFormData(inputs) {
46
45
  else formData.append(key, String(value));
47
46
  return formData;
48
47
  }
49
-
50
48
  //#endregion
51
- export { buildAuthHeaders, buildFormData, fileInputToBlob };
49
+ export { buildAuthHeaders, buildFormData, fileInputToBlob };
@@ -1,5 +1,4 @@
1
1
  import { z } from "zod";
2
-
3
2
  //#region src/shared/types.ts
4
3
  const modelStateSchema = z.object({
5
4
  prompt: z.object({
@@ -12,6 +11,5 @@ const modelStateSchema = z.object({
12
11
  z.string()
13
12
  ]).optional()
14
13
  });
15
-
16
14
  //#endregion
17
- export { modelStateSchema };
15
+ export { modelStateSchema };
@@ -1,6 +1,5 @@
1
1
  import { createSDKError } from "../utils/errors.js";
2
2
  import { buildAuthHeaders } from "../shared/request.js";
3
-
4
3
  //#region src/tokens/client.ts
5
4
  const createTokensClient = (opts) => {
6
5
  const { baseUrl, apiKey, integration } = opts;
@@ -25,6 +24,5 @@ const createTokensClient = (opts) => {
25
24
  };
26
25
  return { create };
27
26
  };
28
-
29
27
  //#endregion
30
- export { createTokensClient };
28
+ export { createTokensClient };
package/dist/utils/env.js CHANGED
@@ -4,6 +4,5 @@ const readEnv = (env) => {
4
4
  if (typeof globalThisAny.process !== "undefined") return globalThisAny.process.env?.[env]?.trim();
5
5
  if (typeof globalThisAny.Deno !== "undefined") return globalThisAny.Deno.env?.get?.(env)?.trim();
6
6
  };
7
-
8
7
  //#endregion
9
- export { readEnv };
8
+ export { readEnv };
@@ -79,6 +79,5 @@ function createQueueStatusError(message, status) {
79
79
  function createQueueResultError(message, status) {
80
80
  return createSDKError(ERROR_CODES.QUEUE_RESULT_ERROR, message, { status });
81
81
  }
82
-
83
82
  //#endregion
84
- export { ERROR_CODES, classifyWebrtcError, createInvalidApiKeyError, createInvalidBaseUrlError, createInvalidInputError, createModelNotFoundError, createQueueResultError, createQueueStatusError, createQueueSubmitError, createSDKError };
83
+ export { ERROR_CODES, classifyWebrtcError, createInvalidApiKeyError, createInvalidBaseUrlError, createInvalidInputError, createModelNotFoundError, createQueueResultError, createQueueStatusError, createQueueSubmitError, createSDKError };