@decartai/sdk 0.0.18 → 0.0.20

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 CHANGED
@@ -1,6 +1,8 @@
1
1
  import { ImageModels, Model, ModelDefinition, RealTimeModels, VideoModels, models } from "./shared/model.js";
2
2
  import { FileInput, ProcessOptions } from "./process/types.js";
3
3
  import { ProcessClient } from "./process/client.js";
4
+ import { JobStatus, JobStatusResponse, JobSubmitResponse, QueueJobResult, QueueSubmitAndPollOptions, QueueSubmitOptions } from "./queue/types.js";
5
+ import { QueueClient } from "./queue/client.js";
4
6
  import { DecartSDKError, ERROR_CODES } from "./utils/errors.js";
5
7
  import { RealTimeClient, RealTimeClientConnectOptions, RealTimeClientInitialState } from "./realtime/client.js";
6
8
  import { ModelState } from "./shared/types.js";
@@ -30,6 +32,44 @@ declare const createDecartClient: (options: DecartClientOptions) => {
30
32
  * ```
31
33
  */
32
34
  process: ProcessClient;
35
+ /**
36
+ * Client for queue-based async video and image generation.
37
+ * Jobs are submitted and processed asynchronously.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const client = createDecartClient({ apiKey: "your-api-key" });
42
+ *
43
+ * // Option 1: Submit and poll automatically
44
+ * const result = await client.queue.submitAndPoll({
45
+ * model: models.video("lucy-pro-t2v"),
46
+ * prompt: "A beautiful sunset over the ocean",
47
+ * onStatusChange: (job) => console.log(`Job ${job.job_id}: ${job.status}`)
48
+ * });
49
+ *
50
+ * // Option 2: Submit and poll manually
51
+ * const job = await client.queue.submit({
52
+ * model: models.video("lucy-pro-t2v"),
53
+ * prompt: "A beautiful sunset over the ocean"
54
+ * });
55
+ *
56
+ * // Poll until completion
57
+ * while (true) {
58
+ * const status = await client.queue.status(job.job_id);
59
+ * console.log(`Job ${status.job_id}: ${status.status}`);
60
+ *
61
+ * if (status.status === "completed") {
62
+ * const blob = await client.queue.result(job.job_id);
63
+ * break;
64
+ * }
65
+ * if (status.status === "failed") {
66
+ * throw new Error("Job failed");
67
+ * }
68
+ * await new Promise(resolve => setTimeout(resolve, 1500));
69
+ * }
70
+ * ```
71
+ */
72
+ queue: QueueClient;
33
73
  };
34
74
  //#endregion
35
- export { DecartClientOptions, type DecartSDKError, ERROR_CODES, type FileInput, type ImageModels, type Model, type ModelDefinition, type ModelState, type ProcessClient, type ProcessOptions, type RealTimeClient, type RealTimeClientConnectOptions, type RealTimeClientInitialState, type RealTimeModels, type VideoModels, createDecartClient, models };
75
+ export { DecartClientOptions, type DecartSDKError, ERROR_CODES, type FileInput, 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 VideoModels, createDecartClient, models };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ERROR_CODES, createInvalidApiKeyError, createInvalidBaseUrlError } from "./utils/errors.js";
2
2
  import { createProcessClient } from "./process/client.js";
3
+ import { createQueueClient } from "./queue/client.js";
3
4
  import { models } from "./shared/model.js";
4
5
  import { createRealTimeClient } from "./realtime/client.js";
5
6
  import { z } from "zod";
@@ -29,9 +30,15 @@ const createDecartClient = (options) => {
29
30
  apiKey,
30
31
  integration
31
32
  });
33
+ const queue = createQueueClient({
34
+ baseUrl,
35
+ apiKey,
36
+ integration
37
+ });
32
38
  return {
33
39
  realtime,
34
- process
40
+ process,
41
+ queue
35
42
  };
36
43
  };
37
44
 
@@ -1,5 +1,6 @@
1
1
  import { createInvalidInputError } from "../utils/errors.js";
2
- import { fileInputToBlob, sendRequest } from "./request.js";
2
+ import { fileInputToBlob } from "../shared/request.js";
3
+ import { sendRequest } from "./request.js";
3
4
 
4
5
  //#region src/process/client.ts
5
6
  const createProcessClient = (opts) => {
@@ -1,31 +1,13 @@
1
- import { createInvalidInputError, createSDKError } from "../utils/errors.js";
2
- import { buildUserAgent } from "../utils/user-agent.js";
1
+ import { createSDKError } from "../utils/errors.js";
2
+ import { buildAuthHeaders, buildFormData } from "../shared/request.js";
3
3
 
4
4
  //#region src/process/request.ts
5
- async function fileInputToBlob(input) {
6
- if (input instanceof Blob || input instanceof File) return input;
7
- if (input instanceof ReadableStream) return new Response(input).blob();
8
- if (typeof input === "string" || input instanceof URL) {
9
- const url = typeof input === "string" ? input : input.toString();
10
- if (!url.startsWith("http://") && !url.startsWith("https://")) throw createInvalidInputError("URL must start with http:// or https://");
11
- const response = await fetch(url);
12
- if (!response.ok) throw createInvalidInputError(`Failed to fetch file from URL: ${response.statusText}`);
13
- return response.blob();
14
- }
15
- throw createInvalidInputError("Invalid file input type");
16
- }
17
5
  async function sendRequest({ baseUrl, apiKey, model, inputs, signal, integration }) {
18
- const formData = new FormData();
19
- for (const [key, value] of Object.entries(inputs)) if (value !== void 0 && value !== null) if (value instanceof Blob) formData.append(key, value);
20
- else if (typeof value === "object" && value !== null) formData.append(key, JSON.stringify(value));
21
- else formData.append(key, String(value));
6
+ const formData = buildFormData(inputs);
22
7
  const endpoint = `${baseUrl}${model.urlPath}`;
23
8
  const response = await fetch(endpoint, {
24
9
  method: "POST",
25
- headers: {
26
- "X-API-KEY": apiKey,
27
- "User-Agent": buildUserAgent(integration)
28
- },
10
+ headers: buildAuthHeaders(apiKey, integration),
29
11
  body: formData,
30
12
  signal
31
13
  });
@@ -37,4 +19,4 @@ async function sendRequest({ baseUrl, apiKey, model, inputs, signal, integration
37
19
  }
38
20
 
39
21
  //#endregion
40
- export { fileInputToBlob, sendRequest };
22
+ export { sendRequest };
@@ -142,4 +142,4 @@ type ProcessOptions<T extends ModelDefinition = ModelDefinition> = {
142
142
  signal?: AbortSignal;
143
143
  } & MergeDocumentedInputs<T>;
144
144
  //#endregion
145
- export { FileInput, ProcessOptions };
145
+ export { FileInput, InferModelInputs, ModelSpecificInputs, ProcessInputs, ProcessOptions };
@@ -0,0 +1,65 @@
1
+ import { ModelDefinition } from "../shared/model.js";
2
+ import { JobStatusResponse, JobSubmitResponse, QueueJobResult, QueueSubmitAndPollOptions, QueueSubmitOptions } from "./types.js";
3
+
4
+ //#region src/queue/client.d.ts
5
+ type QueueClient = {
6
+ /**
7
+ * Submit a job to the queue for async processing.
8
+ * Returns immediately with job_id and initial status.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const job = await client.queue.submit({
13
+ * model: models.video("lucy-pro-t2v"),
14
+ * prompt: "A cat playing piano"
15
+ * });
16
+ * console.log(job.job_id); // "job_abc123"
17
+ * ```
18
+ */
19
+ submit: <T extends ModelDefinition>(options: QueueSubmitOptions<T>) => Promise<JobSubmitResponse>;
20
+ /**
21
+ * Get the current status of a job.
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const status = await client.queue.status("job_abc123");
26
+ * console.log(status.status); // "pending" | "processing" | "completed" | "failed"
27
+ * ```
28
+ */
29
+ status: (jobId: string) => Promise<JobStatusResponse>;
30
+ /**
31
+ * Get the result of a completed job.
32
+ * Should only be called when job status is "completed".
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * const blob = await client.queue.result("job_abc123");
37
+ * videoElement.src = URL.createObjectURL(blob);
38
+ * ```
39
+ */
40
+ result: (jobId: string) => Promise<Blob>;
41
+ /**
42
+ * Submit a job and automatically poll until completion.
43
+ * Returns a result object with status (does not throw on failure).
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const result = await client.queue.submitAndPoll({
48
+ * model: models.video("lucy-pro-t2v"),
49
+ * prompt: "A beautiful sunset",
50
+ * onStatusChange: (job) => {
51
+ * console.log(`Job ${job.job_id}: ${job.status}`);
52
+ * }
53
+ * });
54
+ *
55
+ * if (result.status === "completed") {
56
+ * videoElement.src = URL.createObjectURL(result.data);
57
+ * } else {
58
+ * console.error("Job failed:", result.error);
59
+ * }
60
+ * ```
61
+ */
62
+ submitAndPoll: <T extends ModelDefinition>(options: QueueSubmitAndPollOptions<T>) => Promise<QueueJobResult>;
63
+ };
64
+ //#endregion
65
+ export { QueueClient };
@@ -0,0 +1,73 @@
1
+ import { createInvalidInputError } from "../utils/errors.js";
2
+ import { fileInputToBlob } from "../shared/request.js";
3
+ import { pollUntilComplete } from "./polling.js";
4
+ import { getJobContent, getJobStatus, submitJob } from "./request.js";
5
+
6
+ //#region src/queue/client.ts
7
+ const createQueueClient = (opts) => {
8
+ const { apiKey, baseUrl, integration } = opts;
9
+ const submit = async (options) => {
10
+ const { model, signal,...inputs } = options;
11
+ const parsedInputs = model.inputSchema.safeParse(inputs);
12
+ if (!parsedInputs.success) throw createInvalidInputError(`Invalid inputs for ${model.name}: ${parsedInputs.error.message}`);
13
+ const processedInputs = {};
14
+ for (const [key, value] of Object.entries(parsedInputs.data)) if (key === "data" || key === "start" || key === "end") processedInputs[key] = await fileInputToBlob(value);
15
+ else processedInputs[key] = value;
16
+ return submitJob({
17
+ baseUrl,
18
+ apiKey,
19
+ model,
20
+ inputs: processedInputs,
21
+ signal,
22
+ integration
23
+ });
24
+ };
25
+ const status = async (jobId) => {
26
+ return getJobStatus({
27
+ baseUrl,
28
+ apiKey,
29
+ jobId,
30
+ integration
31
+ });
32
+ };
33
+ const result = async (jobId) => {
34
+ return getJobContent({
35
+ baseUrl,
36
+ apiKey,
37
+ jobId,
38
+ integration
39
+ });
40
+ };
41
+ const submitAndPoll = async (options) => {
42
+ const { onStatusChange, signal,...submitOptions } = options;
43
+ const job = await submit(submitOptions);
44
+ if (onStatusChange) onStatusChange(job);
45
+ return pollUntilComplete({
46
+ checkStatus: () => getJobStatus({
47
+ baseUrl,
48
+ apiKey,
49
+ jobId: job.job_id,
50
+ signal,
51
+ integration
52
+ }),
53
+ getContent: () => getJobContent({
54
+ baseUrl,
55
+ apiKey,
56
+ jobId: job.job_id,
57
+ signal,
58
+ integration
59
+ }),
60
+ onStatusChange,
61
+ signal
62
+ });
63
+ };
64
+ return {
65
+ submit,
66
+ status,
67
+ result,
68
+ submitAndPoll
69
+ };
70
+ };
71
+
72
+ //#endregion
73
+ export { createQueueClient };
@@ -0,0 +1,35 @@
1
+ //#region src/queue/polling.ts
2
+ const POLLING_DEFAULTS = {
3
+ interval: 1500,
4
+ initialDelay: 500
5
+ };
6
+ /**
7
+ * Sleep for a given number of milliseconds.
8
+ */
9
+ function sleep(ms) {
10
+ return new Promise((resolve) => setTimeout(resolve, ms));
11
+ }
12
+ /**
13
+ * Poll until the job is completed or failed.
14
+ * No timeout - backend returns "failed" after 10 minutes.
15
+ */
16
+ async function pollUntilComplete({ checkStatus, getContent, onStatusChange, signal }) {
17
+ await sleep(POLLING_DEFAULTS.initialDelay);
18
+ while (true) {
19
+ if (signal?.aborted) throw new Error("Polling aborted");
20
+ const status = await checkStatus();
21
+ if (onStatusChange) onStatusChange(status);
22
+ if (status.status === "completed") return {
23
+ status: "completed",
24
+ data: await getContent()
25
+ };
26
+ if (status.status === "failed") return {
27
+ status: "failed",
28
+ error: "Job failed"
29
+ };
30
+ await sleep(POLLING_DEFAULTS.interval);
31
+ }
32
+ }
33
+
34
+ //#endregion
35
+ export { pollUntilComplete };
@@ -0,0 +1,61 @@
1
+ import { createQueueResultError, createQueueStatusError, createQueueSubmitError } from "../utils/errors.js";
2
+ import { buildAuthHeaders, buildFormData } from "../shared/request.js";
3
+
4
+ //#region src/queue/request.ts
5
+ /**
6
+ * Submit a job to the queue.
7
+ * POST /v1/jobs/{model}
8
+ */
9
+ async function submitJob({ baseUrl, apiKey, model, inputs, signal, integration }) {
10
+ const formData = buildFormData(inputs);
11
+ if (!model.queueUrlPath) throw createQueueSubmitError(`Model ${model.name} does not support queue processing`, 400);
12
+ const endpoint = `${baseUrl}${model.queueUrlPath}`;
13
+ const response = await fetch(endpoint, {
14
+ method: "POST",
15
+ headers: buildAuthHeaders(apiKey, integration),
16
+ body: formData,
17
+ signal
18
+ });
19
+ if (!response.ok) {
20
+ const errorText = await response.text().catch(() => "Unknown error");
21
+ throw createQueueSubmitError(`Failed to submit job: ${response.status} - ${errorText}`, response.status);
22
+ }
23
+ return response.json();
24
+ }
25
+ /**
26
+ * Get the status of a job.
27
+ * GET /v1/jobs/{job_id}
28
+ */
29
+ async function getJobStatus({ baseUrl, apiKey, jobId, signal, integration }) {
30
+ const endpoint = `${baseUrl}/v1/jobs/${jobId}`;
31
+ const response = await fetch(endpoint, {
32
+ method: "GET",
33
+ headers: buildAuthHeaders(apiKey, integration),
34
+ signal
35
+ });
36
+ if (!response.ok) {
37
+ const errorText = await response.text().catch(() => "Unknown error");
38
+ throw createQueueStatusError(`Failed to get job status: ${response.status} - ${errorText}`, response.status);
39
+ }
40
+ return response.json();
41
+ }
42
+ /**
43
+ * Get the content/result of a completed job.
44
+ * GET /v1/jobs/{job_id}/content
45
+ */
46
+ async function getJobContent({ baseUrl, apiKey, jobId, signal, integration }) {
47
+ const endpoint = `${baseUrl}/v1/jobs/${jobId}/content`;
48
+ const response = await fetch(endpoint, {
49
+ method: "GET",
50
+ headers: buildAuthHeaders(apiKey, integration),
51
+ signal
52
+ });
53
+ if (!response.ok) {
54
+ const errorText = await response.text().catch(() => "Unknown error");
55
+ throw createQueueResultError(`Failed to get job content: ${response.status} - ${errorText}`, response.status);
56
+ }
57
+ return response.blob();
58
+ }
59
+
60
+ //#endregion
61
+ export { getJobContent, getJobStatus, submitJob };
@@ -0,0 +1,79 @@
1
+ import { ModelDefinition } from "../shared/model.js";
2
+ import { FileInput, InferModelInputs, ModelSpecificInputs, ProcessInputs } from "../process/types.js";
3
+
4
+ //#region src/queue/types.d.ts
5
+
6
+ /**
7
+ * Job status values returned by the queue API.
8
+ */
9
+ type JobStatus = "pending" | "processing" | "completed" | "failed";
10
+ /**
11
+ * Response from POST /v1/jobs/{model} - job submission.
12
+ */
13
+ type JobSubmitResponse = {
14
+ job_id: string;
15
+ status: JobStatus;
16
+ };
17
+ /**
18
+ * Response from GET /v1/jobs/{job_id} - job status check.
19
+ */
20
+ type JobStatusResponse = {
21
+ job_id: string;
22
+ status: JobStatus;
23
+ };
24
+ /**
25
+ * Result from submitAndPoll - discriminated union for success/failure.
26
+ */
27
+ type QueueJobResult = {
28
+ status: "completed";
29
+ data: Blob;
30
+ } | {
31
+ status: "failed";
32
+ error: string;
33
+ };
34
+ /**
35
+ * Queue-specific inputs extending ProcessInputs.
36
+ * Re-exports ProcessInputs fields with queue-specific documentation.
37
+ */
38
+ interface QueueInputs extends ProcessInputs {
39
+ /**
40
+ * The data to use for generation (for image-to-image and video-to-video).
41
+ */
42
+ data?: FileInput;
43
+ /**
44
+ * The start frame image (for first-last-frame models).
45
+ */
46
+ start?: FileInput;
47
+ /**
48
+ * The end frame image (for first-last-frame models).
49
+ */
50
+ end?: FileInput;
51
+ }
52
+ type ModelSpecificQueueInputs<T extends ModelDefinition> = QueueInputs & ModelSpecificInputs<T>;
53
+ type PickDocumentedInputs<T extends ModelDefinition> = Pick<ModelSpecificQueueInputs<T>, keyof ModelSpecificQueueInputs<T> & keyof InferModelInputs<T>>;
54
+ type MergeDocumentedInputs<T extends ModelDefinition> = PickDocumentedInputs<T> & InferModelInputs<T>;
55
+ /**
56
+ * Options for queue.submit() - submit a job for async processing.
57
+ */
58
+ type QueueSubmitOptions<T extends ModelDefinition = ModelDefinition> = {
59
+ /**
60
+ * The model definition to use.
61
+ */
62
+ model: T;
63
+ /**
64
+ * Optional `AbortSignal` for canceling the request.
65
+ */
66
+ signal?: AbortSignal;
67
+ } & MergeDocumentedInputs<T>;
68
+ /**
69
+ * Options for queue.submitAndPoll() - submit and wait for completion.
70
+ */
71
+ type QueueSubmitAndPollOptions<T extends ModelDefinition = ModelDefinition> = QueueSubmitOptions<T> & {
72
+ /**
73
+ * Callback invoked when job status changes during polling.
74
+ * Receives the full job status response object.
75
+ */
76
+ onStatusChange?: (job: JobStatusResponse) => void;
77
+ };
78
+ //#endregion
79
+ export { JobStatus, JobStatusResponse, JobSubmitResponse, QueueJobResult, QueueSubmitAndPollOptions, QueueSubmitOptions };
@@ -15,6 +15,7 @@ declare const realTimeClientConnectOptionsSchema: z.ZodObject<{
15
15
  model: z.ZodObject<{
16
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.ZodUnion<readonly [z.ZodLiteral<"lucy-pro-t2i">, z.ZodLiteral<"lucy-pro-i2i">]>]>;
17
17
  urlPath: z.ZodString;
18
+ queueUrlPath: z.ZodOptional<z.ZodString>;
18
19
  fps: z.ZodNumber;
19
20
  width: z.ZodNumber;
20
21
  height: z.ZodNumber;
@@ -39,12 +40,12 @@ type RealTimeClient = {
39
40
  enhance
40
41
  }?: {
41
42
  enhance?: boolean;
42
- }) => void;
43
+ }) => Promise<void>;
43
44
  isConnected: () => boolean;
44
45
  getConnectionState: () => "connected" | "connecting" | "disconnected";
45
46
  disconnect: () => void;
46
- on: (event: keyof Events, listener: (...args: Events[keyof Events][]) => void) => void;
47
- off: (event: keyof Events, listener: (...args: Events[keyof Events][]) => void) => void;
47
+ on: <K extends keyof Events>(event: K, listener: (data: Events[K]) => void) => void;
48
+ off: <K extends keyof Events>(event: K, listener: (data: Events[K]) => void) => void;
48
49
  sessionId: string;
49
50
  };
50
51
  //#endregion
@@ -1,8 +1,9 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  //#region src/realtime/methods.ts
4
+ const PROMPT_TIMEOUT_MS = 15 * 1e3;
4
5
  const realtimeMethods = (webrtcManager) => {
5
- const setPrompt = (prompt, { enhance } = {}) => {
6
+ const setPrompt = async (prompt, { enhance } = {}) => {
6
7
  const parsedInput = z.object({
7
8
  prompt: z.string().min(1),
8
9
  enhance: z.boolean().optional().default(true)
@@ -11,11 +12,30 @@ const realtimeMethods = (webrtcManager) => {
11
12
  enhance
12
13
  });
13
14
  if (!parsedInput.success) throw parsedInput.error;
14
- webrtcManager.sendMessage({
15
- type: "prompt",
16
- prompt: parsedInput.data.prompt,
17
- enhance_prompt: parsedInput.data.enhance
18
- });
15
+ const emitter = webrtcManager.getWebsocketMessageEmitter();
16
+ let promptAckListener;
17
+ let timeoutId;
18
+ try {
19
+ const ackPromise = new Promise((resolve, reject) => {
20
+ promptAckListener = (promptAckMessage) => {
21
+ if (promptAckMessage.prompt === parsedInput.data.prompt) if (promptAckMessage.success) resolve();
22
+ else reject(promptAckMessage.error);
23
+ };
24
+ emitter.on("promptAck", promptAckListener);
25
+ });
26
+ webrtcManager.sendMessage({
27
+ type: "prompt",
28
+ prompt: parsedInput.data.prompt,
29
+ enhance_prompt: parsedInput.data.enhance
30
+ });
31
+ const timeoutPromise = new Promise((_, reject) => {
32
+ timeoutId = setTimeout(() => reject(/* @__PURE__ */ new Error("Prompt timed out")), PROMPT_TIMEOUT_MS);
33
+ });
34
+ await Promise.race([ackPromise, timeoutPromise]);
35
+ } finally {
36
+ if (promptAckListener) emitter.off("promptAck", promptAckListener);
37
+ if (timeoutId) clearTimeout(timeoutId);
38
+ }
19
39
  };
20
40
  return { setPrompt };
21
41
  };
@@ -1,4 +1,5 @@
1
1
  import { buildUserAgent } from "../utils/user-agent.js";
2
+ import mitt from "mitt";
2
3
 
3
4
  //#region src/realtime/webrtc-connection.ts
4
5
  const ICE_SERVERS = [{ urls: "stun:stun.l.google.com:19302" }];
@@ -8,6 +9,7 @@ var WebRTCConnection = class {
8
9
  localStream = null;
9
10
  connectionReject = null;
10
11
  state = "disconnected";
12
+ websocketMessagesEmitter = mitt();
11
13
  constructor(callbacks = {}) {
12
14
  this.callbacks = callbacks;
13
15
  }
@@ -105,6 +107,9 @@ var WebRTCConnection = class {
105
107
  if (turnConfig) await this.setupNewPeerConnection(turnConfig);
106
108
  break;
107
109
  }
110
+ case "prompt_ack":
111
+ this.websocketMessagesEmitter.emit("promptAck", msg);
112
+ break;
108
113
  }
109
114
  } catch (error) {
110
115
  console.error("[WebRTC] Error:", error);
@@ -55,6 +55,9 @@ var WebRTCManager = class {
55
55
  getConnectionState() {
56
56
  return this.connection.state;
57
57
  }
58
+ getWebsocketMessageEmitter() {
59
+ return this.connection.websocketMessagesEmitter;
60
+ }
58
61
  };
59
62
 
60
63
  //#endregion
@@ -93,6 +93,7 @@ type ModelInputSchemas = typeof modelInputSchemas;
93
93
  type ModelDefinition<T extends Model = Model> = {
94
94
  name: T;
95
95
  urlPath: string;
96
+ queueUrlPath?: string;
96
97
  fps: number;
97
98
  width: number;
98
99
  height: number;
@@ -116,6 +116,7 @@ const modelInputSchemas = {
116
116
  const modelDefinitionSchema = z.object({
117
117
  name: modelSchema,
118
118
  urlPath: z.string(),
119
+ queueUrlPath: z.string().optional(),
119
120
  fps: z.number().min(1),
120
121
  width: z.number().min(1),
121
122
  height: z.number().min(1),
@@ -151,6 +152,7 @@ const _models = {
151
152
  image: {
152
153
  "lucy-pro-t2i": {
153
154
  urlPath: "/v1/generate/lucy-pro-t2i",
155
+ queueUrlPath: "/v1/jobs/lucy-pro-t2i",
154
156
  name: "lucy-pro-t2i",
155
157
  fps: 25,
156
158
  width: 1280,
@@ -159,6 +161,7 @@ const _models = {
159
161
  },
160
162
  "lucy-pro-i2i": {
161
163
  urlPath: "/v1/generate/lucy-pro-i2i",
164
+ queueUrlPath: "/v1/jobs/lucy-pro-i2i",
162
165
  name: "lucy-pro-i2i",
163
166
  fps: 25,
164
167
  width: 1280,
@@ -169,6 +172,7 @@ const _models = {
169
172
  video: {
170
173
  "lucy-dev-i2v": {
171
174
  urlPath: "/v1/generate/lucy-dev-i2v",
175
+ queueUrlPath: "/v1/jobs/lucy-dev-i2v",
172
176
  name: "lucy-dev-i2v",
173
177
  fps: 25,
174
178
  width: 1280,
@@ -177,6 +181,7 @@ const _models = {
177
181
  },
178
182
  "lucy-fast-v2v": {
179
183
  urlPath: "/v1/generate/lucy-fast-v2v",
184
+ queueUrlPath: "/v1/jobs/lucy-fast-v2v",
180
185
  name: "lucy-fast-v2v",
181
186
  fps: 25,
182
187
  width: 1280,
@@ -185,6 +190,7 @@ const _models = {
185
190
  },
186
191
  "lucy-pro-t2v": {
187
192
  urlPath: "/v1/generate/lucy-pro-t2v",
193
+ queueUrlPath: "/v1/jobs/lucy-pro-t2v",
188
194
  name: "lucy-pro-t2v",
189
195
  fps: 25,
190
196
  width: 1280,
@@ -193,6 +199,7 @@ const _models = {
193
199
  },
194
200
  "lucy-pro-i2v": {
195
201
  urlPath: "/v1/generate/lucy-pro-i2v",
202
+ queueUrlPath: "/v1/jobs/lucy-pro-i2v",
196
203
  name: "lucy-pro-i2v",
197
204
  fps: 25,
198
205
  width: 1280,
@@ -201,6 +208,7 @@ const _models = {
201
208
  },
202
209
  "lucy-pro-v2v": {
203
210
  urlPath: "/v1/generate/lucy-pro-v2v",
211
+ queueUrlPath: "/v1/jobs/lucy-pro-v2v",
204
212
  name: "lucy-pro-v2v",
205
213
  fps: 25,
206
214
  width: 1280,
@@ -209,6 +217,7 @@ const _models = {
209
217
  },
210
218
  "lucy-pro-flf2v": {
211
219
  urlPath: "/v1/generate/lucy-pro-flf2v",
220
+ queueUrlPath: "/v1/jobs/lucy-pro-flf2v",
212
221
  name: "lucy-pro-flf2v",
213
222
  fps: 25,
214
223
  width: 1280,
@@ -217,6 +226,7 @@ const _models = {
217
226
  },
218
227
  "lucy-motion": {
219
228
  urlPath: "/v1/generate/lucy-motion",
229
+ queueUrlPath: "/v1/jobs/lucy-motion",
220
230
  name: "lucy-motion",
221
231
  fps: 25,
222
232
  width: 1280,
@@ -0,0 +1,41 @@
1
+ import { createInvalidInputError } from "../utils/errors.js";
2
+ import { buildUserAgent } from "../utils/user-agent.js";
3
+
4
+ //#region src/shared/request.ts
5
+ /**
6
+ * Convert various file input types to a Blob.
7
+ */
8
+ async function fileInputToBlob(input) {
9
+ if (input instanceof Blob || input instanceof File) return input;
10
+ if (input instanceof ReadableStream) return new Response(input).blob();
11
+ if (typeof input === "string" || input instanceof URL) {
12
+ const url = typeof input === "string" ? input : input.toString();
13
+ if (!url.startsWith("http://") && !url.startsWith("https://")) throw createInvalidInputError("URL must start with http:// or https://");
14
+ const response = await fetch(url);
15
+ if (!response.ok) throw createInvalidInputError(`Failed to fetch file from URL: ${response.statusText}`);
16
+ return response.blob();
17
+ }
18
+ throw createInvalidInputError("Invalid file input type");
19
+ }
20
+ /**
21
+ * Build common headers for API requests.
22
+ */
23
+ function buildAuthHeaders(apiKey, integration) {
24
+ return {
25
+ "X-API-KEY": apiKey,
26
+ "User-Agent": buildUserAgent(integration)
27
+ };
28
+ }
29
+ /**
30
+ * Build FormData from inputs object.
31
+ */
32
+ function buildFormData(inputs) {
33
+ const formData = new FormData();
34
+ for (const [key, value] of Object.entries(inputs)) if (value !== void 0 && value !== null) if (value instanceof Blob) formData.append(key, value);
35
+ else if (typeof value === "object" && value !== null) formData.append(key, JSON.stringify(value));
36
+ else formData.append(key, String(value));
37
+ return formData;
38
+ }
39
+
40
+ //#endregion
41
+ export { buildAuthHeaders, buildFormData, fileInputToBlob };
@@ -13,6 +13,10 @@ declare const ERROR_CODES: {
13
13
  readonly INVALID_INPUT: "INVALID_INPUT";
14
14
  readonly INVALID_OPTIONS: "INVALID_OPTIONS";
15
15
  readonly MODEL_NOT_FOUND: "MODEL_NOT_FOUND";
16
+ readonly QUEUE_SUBMIT_ERROR: "QUEUE_SUBMIT_ERROR";
17
+ readonly QUEUE_STATUS_ERROR: "QUEUE_STATUS_ERROR";
18
+ readonly QUEUE_RESULT_ERROR: "QUEUE_RESULT_ERROR";
19
+ readonly JOB_NOT_COMPLETED: "JOB_NOT_COMPLETED";
16
20
  };
17
21
  //#endregion
18
22
  export { DecartSDKError, ERROR_CODES };
@@ -6,7 +6,11 @@ const ERROR_CODES = {
6
6
  PROCESSING_ERROR: "PROCESSING_ERROR",
7
7
  INVALID_INPUT: "INVALID_INPUT",
8
8
  INVALID_OPTIONS: "INVALID_OPTIONS",
9
- MODEL_NOT_FOUND: "MODEL_NOT_FOUND"
9
+ MODEL_NOT_FOUND: "MODEL_NOT_FOUND",
10
+ QUEUE_SUBMIT_ERROR: "QUEUE_SUBMIT_ERROR",
11
+ QUEUE_STATUS_ERROR: "QUEUE_STATUS_ERROR",
12
+ QUEUE_RESULT_ERROR: "QUEUE_RESULT_ERROR",
13
+ JOB_NOT_COMPLETED: "JOB_NOT_COMPLETED"
10
14
  };
11
15
  function createSDKError(code, message, data, cause) {
12
16
  return {
@@ -31,6 +35,15 @@ function createInvalidInputError(message) {
31
35
  function createModelNotFoundError(model) {
32
36
  return createSDKError(ERROR_CODES.MODEL_NOT_FOUND, `Model ${model} not found`);
33
37
  }
38
+ function createQueueSubmitError(message, status) {
39
+ return createSDKError(ERROR_CODES.QUEUE_SUBMIT_ERROR, message, { status });
40
+ }
41
+ function createQueueStatusError(message, status) {
42
+ return createSDKError(ERROR_CODES.QUEUE_STATUS_ERROR, message, { status });
43
+ }
44
+ function createQueueResultError(message, status) {
45
+ return createSDKError(ERROR_CODES.QUEUE_RESULT_ERROR, message, { status });
46
+ }
34
47
 
35
48
  //#endregion
36
- export { ERROR_CODES, createInvalidApiKeyError, createInvalidBaseUrlError, createInvalidInputError, createModelNotFoundError, createSDKError, createWebrtcError };
49
+ export { ERROR_CODES, createInvalidApiKeyError, createInvalidBaseUrlError, createInvalidInputError, createModelNotFoundError, createQueueResultError, createQueueStatusError, createQueueSubmitError, createSDKError, createWebrtcError };
package/dist/version.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Injected at build time from package.json.
5
5
  * Falls back to '0.0.0-dev' in development.
6
6
  */
7
- const VERSION = "0.0.18";
7
+ const VERSION = "0.0.20";
8
8
 
9
9
  //#endregion
10
10
  export { VERSION };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decartai/sdk",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
4
4
  "description": "Decart's JavaScript SDK",
5
5
  "type": "module",
6
6
  "license": "MIT",