@decartai/sdk 0.0.65 → 0.0.67

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/README.md CHANGED
@@ -62,6 +62,23 @@ realtimeClient.setPrompt("Cyberpunk city");
62
62
  realtimeClient.disconnect();
63
63
  ```
64
64
 
65
+ #### Front-camera mirroring
66
+
67
+ Pre-flip the input stream:
68
+
69
+ ```ts
70
+ const realtimeClient = await client.realtime.connect(stream, {
71
+ model,
72
+ mirror: "auto", // or true to always mirror
73
+ // ...
74
+ });
75
+ ```
76
+
77
+ Options:
78
+ - `false` (default) — never mirror.
79
+ - `"auto"` — mirror when the input track reports `facingMode: "user"` (mobile front cameras).
80
+ - `true` — always mirror (e.g. desktop webcams).
81
+
65
82
  ### Async Processing (Queue API)
66
83
 
67
84
  For video generation jobs, use the queue API to submit jobs and poll for results:
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { LogLevel, Logger, createConsoleLogger, noopLogger } from "./utils/logger.js";
2
- import { CustomModelDefinition, ImageModelDefinition, ImageModels, Model, ModelDefinition, RealTimeModels, VideoModelDefinition, VideoModels, isImageModel, isRealtimeModel, isVideoModel, models } from "./shared/model.js";
2
+ import { CanonicalModel, CustomModelDefinition, ImageModelDefinition, ImageModels, ListedModelDefinition, Model, ModelDefinition, ModelKind, RealTimeModels, VideoModelDefinition, VideoModels, isCanonicalModel, isImageModel, isModel, isRealtimeModel, isVideoModel, listModels, modelAliases, models, resolveCanonicalModelAlias, resolveModelAlias } from "./shared/model.js";
3
3
  import { FileInput, ProcessOptions, ReactNativeFile } from "./process/types.js";
4
4
  import { ProcessClient } from "./process/client.js";
5
5
  import { JobStatus, JobStatusResponse, JobSubmitResponse, QueueJobResult, QueueSubmitAndPollOptions, QueueSubmitOptions } from "./queue/types.js";
@@ -134,4 +134,4 @@ declare const createDecartClient: (options?: DecartClientOptions) => {
134
134
  tokens: TokensClient;
135
135
  };
136
136
  //#endregion
137
- export { type ConnectionPhase, type ConnectionState, type CreateTokenOptions, type CreateTokenResponse, type CustomModelDefinition, DecartClientOptions, type DecartSDKError, type DiagnosticEvent, type DiagnosticEventName, type DiagnosticEvents, ERROR_CODES, type FileInput, type IceCandidateEvent, type IceStateEvent, type ImageModelDefinition, type ImageModels, type JobStatus, type JobStatusResponse, type JobSubmitResponse, type LogLevel, type Logger, type Model, type ModelDefinition, type ModelState, type PeerConnectionStateEvent, type PhaseTimingEvent, type ProcessClient, type ProcessOptions, type QueueClient, type QueueJobResult, type QueueSubmitAndPollOptions, type QueueSubmitOptions, type ReactNativeFile, type RealTimeClient, type RealTimeClientConnectOptions, type RealTimeClientInitialState, type Events as RealTimeEvents, type RealTimeModels, type RealTimeSubscribeClient, type ReconnectEvent, type SelectedCandidatePairEvent, type SetInput, type SignalingStateEvent, type SubscribeEvents, type SubscribeOptions, type TokensClient, type VideoModelDefinition, type VideoModels, type VideoStallEvent, type WebRTCStats, createConsoleLogger, createDecartClient, isImageModel, isRealtimeModel, isVideoModel, models, noopLogger };
137
+ export { type CanonicalModel, type ConnectionPhase, type ConnectionState, type CreateTokenOptions, type CreateTokenResponse, type CustomModelDefinition, DecartClientOptions, type DecartSDKError, type DiagnosticEvent, type DiagnosticEventName, type DiagnosticEvents, ERROR_CODES, type FileInput, type IceCandidateEvent, type IceStateEvent, type ImageModelDefinition, type ImageModels, type JobStatus, type JobStatusResponse, type JobSubmitResponse, type ListedModelDefinition, type LogLevel, type Logger, type Model, type ModelDefinition, type ModelKind, type ModelState, type PeerConnectionStateEvent, type PhaseTimingEvent, type ProcessClient, type ProcessOptions, type QueueClient, type QueueJobResult, type QueueSubmitAndPollOptions, type QueueSubmitOptions, type ReactNativeFile, type RealTimeClient, type RealTimeClientConnectOptions, type RealTimeClientInitialState, type Events as RealTimeEvents, type RealTimeModels, type RealTimeSubscribeClient, type ReconnectEvent, type SelectedCandidatePairEvent, type SetInput, type SignalingStateEvent, type SubscribeEvents, type SubscribeOptions, type TokensClient, type VideoModelDefinition, type VideoModels, type VideoStallEvent, type WebRTCStats, createConsoleLogger, createDecartClient, isCanonicalModel, isImageModel, isModel, isRealtimeModel, isVideoModel, listModels, modelAliases, models, noopLogger, resolveCanonicalModelAlias, resolveModelAlias };
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 { isImageModel, isRealtimeModel, isVideoModel, models } from "./shared/model.js";
4
+ import { isCanonicalModel, isImageModel, isModel, isRealtimeModel, isVideoModel, listModels, modelAliases, models, resolveCanonicalModelAlias, resolveModelAlias } 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";
@@ -88,4 +88,4 @@ const createDecartClient = (options = {}) => {
88
88
  };
89
89
 
90
90
  //#endregion
91
- export { ERROR_CODES, createConsoleLogger, createDecartClient, isImageModel, isRealtimeModel, isVideoModel, models, noopLogger };
91
+ export { ERROR_CODES, createConsoleLogger, createDecartClient, isCanonicalModel, isImageModel, isModel, isRealtimeModel, isVideoModel, listModels, modelAliases, models, noopLogger, resolveCanonicalModelAlias, resolveModelAlias };
@@ -36,6 +36,7 @@ declare const realTimeClientConnectOptionsSchema: z.ZodObject<{
36
36
  image: z.ZodOptional<z.ZodUnion<readonly [z.ZodCustom<Blob, Blob>, z.ZodCustom<File, File>, z.ZodString]>>;
37
37
  }, z.core.$strip>>;
38
38
  customizeOffer: z.ZodOptional<z.ZodCustom<z.core.$InferInnerFunctionTypeAsync<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>, z.core.$InferInnerFunctionTypeAsync<z.core.$ZodFunctionArgs, z.core.$ZodFunctionOut>>>;
39
+ mirror: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"auto">, z.ZodBoolean]>>;
39
40
  }, z.core.$strip>;
40
41
  type RealTimeClientConnectOptions = Omit<z.infer<typeof realTimeClientConnectOptionsSchema>, "model"> & {
41
42
  model: ModelDefinition | CustomModelDefinition;
@@ -3,6 +3,7 @@ import { modelDefinitionSchema } from "../shared/model.js";
3
3
  import { modelStateSchema } from "../shared/types.js";
4
4
  import { createEventBuffer } from "./event-buffer.js";
5
5
  import { realtimeMethods } from "./methods.js";
6
+ import { createMirroredStream, shouldMirrorTrack } from "./mirror-stream.js";
6
7
  import { decodeSubscribeToken, encodeSubscribeToken } from "./subscribe-client.js";
7
8
  import { NullTelemetryReporter, TelemetryReporter } from "./telemetry-reporter.js";
8
9
  import { WebRTCManager } from "./webrtc-manager.js";
@@ -56,7 +57,8 @@ const realTimeClientConnectOptionsSchema = z.object({
56
57
  model: modelDefinitionSchema,
57
58
  onRemoteStream: z.custom((val) => typeof val === "function", { message: "onRemoteStream must be a function" }),
58
59
  initialState: realTimeClientInitialStateSchema.optional(),
59
- customizeOffer: createAsyncFunctionSchema(z.function()).optional()
60
+ customizeOffer: createAsyncFunctionSchema(z.function()).optional(),
61
+ mirror: z.union([z.literal("auto"), z.boolean()]).optional()
60
62
  });
61
63
  const createRealTimeClient = (opts) => {
62
64
  const { baseUrl, apiKey, integration, logger } = opts;
@@ -64,7 +66,18 @@ const createRealTimeClient = (opts) => {
64
66
  const parsedOptions = realTimeClientConnectOptionsSchema.safeParse(options);
65
67
  if (!parsedOptions.success) throw parsedOptions.error;
66
68
  const { onRemoteStream, initialState } = parsedOptions.data;
67
- const inputStream = stream ?? new MediaStream();
69
+ const mirror = parsedOptions.data.mirror ?? false;
70
+ let inputStream = stream ?? new MediaStream();
71
+ let mirroredStream;
72
+ if (mirror !== false) try {
73
+ const firstVideoTrack = inputStream.getVideoTracks?.()[0];
74
+ if (firstVideoTrack && (mirror === true || shouldMirrorTrack(firstVideoTrack))) {
75
+ mirroredStream = createMirroredStream(inputStream, { fps: options.model.fps });
76
+ inputStream = mirroredStream.stream;
77
+ } else if (mirror === true && !firstVideoTrack) logger.warn("mirror: true requested but no video track was found on the input stream");
78
+ } catch (error) {
79
+ logger.warn("Failed to mirror input stream; falling back to un-mirrored input", { error: error instanceof Error ? error.message : String(error) });
80
+ }
68
81
  let webrtcManager;
69
82
  let telemetryReporter = new NullTelemetryReporter();
70
83
  let handleConnectionStateChange = null;
@@ -219,6 +232,7 @@ const createRealTimeClient = (opts) => {
219
232
  telemetryReporter.stop();
220
233
  stop();
221
234
  manager.cleanup();
235
+ mirroredStream?.dispose();
222
236
  },
223
237
  on: eventEmitter.on,
224
238
  off: eventEmitter.off,
@@ -239,6 +253,7 @@ const createRealTimeClient = (opts) => {
239
253
  } catch (error) {
240
254
  telemetryReporter.stop();
241
255
  webrtcManager?.cleanup();
256
+ mirroredStream?.dispose();
242
257
  throw error;
243
258
  }
244
259
  };
@@ -0,0 +1,116 @@
1
+ //#region src/realtime/mirror-stream.ts
2
+ function isMediaStreamTrackProcessorSupported() {
3
+ return typeof globalThis !== "undefined" && typeof globalThis.MediaStreamTrackProcessor === "function" && typeof globalThis.MediaStreamTrackGenerator === "function";
4
+ }
5
+ function shouldMirrorTrack(track) {
6
+ if (track.kind !== "video") return false;
7
+ let facingMode;
8
+ try {
9
+ facingMode = track.getSettings?.().facingMode;
10
+ } catch {
11
+ return false;
12
+ }
13
+ return facingMode === "user";
14
+ }
15
+ function createMirroredStream(input, opts) {
16
+ const [sourceVideo] = input.getVideoTracks();
17
+ const audioTracks = input.getAudioTracks();
18
+ if (!sourceVideo) return {
19
+ stream: input,
20
+ dispose: () => {},
21
+ impl: "noop"
22
+ };
23
+ if (isMediaStreamTrackProcessorSupported()) return createWithTrackProcessor(sourceVideo, audioTracks);
24
+ return createWithCanvas(sourceVideo, audioTracks, opts.fps);
25
+ }
26
+ function createWithTrackProcessor(sourceVideo, audioTracks) {
27
+ const Processor = globalThis.MediaStreamTrackProcessor;
28
+ const Generator = globalThis.MediaStreamTrackGenerator;
29
+ if (!new OffscreenCanvas(1, 1).getContext("2d")) throw new Error("createMirroredStream: OffscreenCanvas 2D context unavailable");
30
+ const processor = new Processor({ track: sourceVideo });
31
+ const generator = new Generator({ kind: "video" });
32
+ let canvas = new OffscreenCanvas(1, 1);
33
+ let ctx = canvas.getContext("2d");
34
+ const transform = new TransformStream({ transform(frame, controller) {
35
+ const w = frame.displayWidth;
36
+ const h = frame.displayHeight;
37
+ if (canvas.width !== w || canvas.height !== h) {
38
+ canvas = new OffscreenCanvas(w, h);
39
+ ctx = canvas.getContext("2d");
40
+ }
41
+ let flipped;
42
+ try {
43
+ ctx.save();
44
+ ctx.setTransform(-1, 0, 0, 1, w, 0);
45
+ ctx.drawImage(frame, 0, 0, w, h);
46
+ ctx.restore();
47
+ flipped = new VideoFrame(canvas, {
48
+ timestamp: frame.timestamp,
49
+ alpha: "discard"
50
+ });
51
+ controller.enqueue(flipped);
52
+ flipped = void 0;
53
+ } finally {
54
+ flipped?.close();
55
+ frame.close();
56
+ }
57
+ } });
58
+ processor.readable.pipeThrough(transform).pipeTo(generator.writable).catch(() => {});
59
+ const stream = new MediaStream([generator, ...audioTracks]);
60
+ let disposed = false;
61
+ return {
62
+ stream,
63
+ impl: "track-processor",
64
+ dispose: () => {
65
+ if (disposed) return;
66
+ disposed = true;
67
+ generator.stop();
68
+ }
69
+ };
70
+ }
71
+ function createWithCanvas(sourceVideo, audioTracks, fps) {
72
+ if (typeof document === "undefined") throw new Error("createMirroredStream requires a DOM environment (document is undefined)");
73
+ const canvas = document.createElement("canvas");
74
+ const ctx = canvas.getContext("2d");
75
+ if (!ctx) throw new Error("createMirroredStream: 2D canvas context unavailable");
76
+ if (typeof canvas.captureStream !== "function") throw new Error("createMirroredStream: canvas.captureStream unavailable");
77
+ const [flippedTrack] = canvas.captureStream(fps).getVideoTracks();
78
+ if (!flippedTrack) throw new Error("createMirroredStream: canvas.captureStream produced no video track");
79
+ const video = document.createElement("video");
80
+ video.muted = true;
81
+ video.playsInline = true;
82
+ video.autoplay = true;
83
+ video.srcObject = new MediaStream([sourceVideo]);
84
+ let disposed = false;
85
+ let rafHandle = null;
86
+ const draw = () => {
87
+ if (disposed) return;
88
+ const w = video.videoWidth;
89
+ const h = video.videoHeight;
90
+ if (w > 0 && h > 0) {
91
+ if (canvas.width !== w) canvas.width = w;
92
+ if (canvas.height !== h) canvas.height = h;
93
+ ctx.save();
94
+ ctx.setTransform(-1, 0, 0, 1, w, 0);
95
+ ctx.drawImage(video, 0, 0, w, h);
96
+ ctx.restore();
97
+ }
98
+ rafHandle = requestAnimationFrame(draw);
99
+ };
100
+ video.play().catch(() => {});
101
+ rafHandle = requestAnimationFrame(draw);
102
+ return {
103
+ stream: new MediaStream([flippedTrack, ...audioTracks]),
104
+ impl: "canvas",
105
+ dispose: () => {
106
+ if (disposed) return;
107
+ disposed = true;
108
+ if (rafHandle !== null) cancelAnimationFrame(rafHandle);
109
+ flippedTrack.stop();
110
+ video.srcObject = null;
111
+ }
112
+ };
113
+ }
114
+
115
+ //#endregion
116
+ export { createMirroredStream, shouldMirrorTrack };
@@ -1,6 +1,30 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  //#region src/shared/model.d.ts
4
+
5
+ declare const canonicalModelSchema: z.ZodEnum<{
6
+ "lucy-2.1": "lucy-2.1";
7
+ "lucy-2.1-vton": "lucy-2.1-vton";
8
+ "lucy-vton-2": "lucy-vton-2";
9
+ "lucy-restyle-2": "lucy-restyle-2";
10
+ "lucy-clip": "lucy-clip";
11
+ "lucy-image-2": "lucy-image-2";
12
+ }>;
13
+ type CanonicalModel = z.infer<typeof canonicalModelSchema>;
14
+ /**
15
+ * Map of deprecated model names to their canonical replacements.
16
+ * Old names still work but will log a deprecation warning.
17
+ */
18
+ declare const modelAliases: {
19
+ readonly mirage_v2: "lucy-restyle-2";
20
+ readonly "lucy-vton": "lucy-2.1-vton";
21
+ readonly "lucy-2.1-vton-2": "lucy-vton-2";
22
+ readonly "lucy-pro-v2v": "lucy-clip";
23
+ readonly "lucy-restyle-v2v": "lucy-restyle-2";
24
+ readonly "lucy-pro-i2i": "lucy-image-2";
25
+ };
26
+ /** @internal Test-only helper to reset deprecation warning tracking */
27
+
4
28
  declare const realtimeModels: z.ZodUnion<readonly [z.ZodLiteral<"lucy-2.1">, z.ZodLiteral<"lucy-2.1-vton">, z.ZodLiteral<"lucy-vton-2">, z.ZodLiteral<"lucy-restyle-2">, z.ZodLiteral<"lucy-latest">, z.ZodLiteral<"lucy-vton-latest">, z.ZodLiteral<"lucy-restyle-latest">, z.ZodLiteral<"mirage_v2">, z.ZodLiteral<"lucy-vton">, z.ZodLiteral<"lucy-2.1-vton-2">]>;
5
29
  declare const videoModels: z.ZodUnion<readonly [z.ZodLiteral<"lucy-clip">, z.ZodLiteral<"lucy-2.1">, z.ZodLiteral<"lucy-2.1-vton">, z.ZodLiteral<"lucy-vton-2">, z.ZodLiteral<"lucy-restyle-2">, z.ZodLiteral<"lucy-latest">, z.ZodLiteral<"lucy-vton-latest">, z.ZodLiteral<"lucy-restyle-latest">, z.ZodLiteral<"lucy-clip-latest">, z.ZodLiteral<"lucy-vton">, z.ZodLiteral<"lucy-2.1-vton-2">, z.ZodLiteral<"lucy-pro-v2v">, z.ZodLiteral<"lucy-restyle-v2v">]>;
6
30
  declare const imageModels: z.ZodUnion<readonly [z.ZodLiteral<"lucy-image-2">, z.ZodLiteral<"lucy-image-latest">, z.ZodLiteral<"lucy-pro-i2i">]>;
@@ -12,6 +36,20 @@ type ImageModels = z.infer<typeof imageModels>;
12
36
  declare function isRealtimeModel(model: string): model is RealTimeModels;
13
37
  declare function isVideoModel(model: string): model is VideoModels;
14
38
  declare function isImageModel(model: string): model is ImageModels;
39
+ declare function isModel(model: string): model is Model;
40
+ declare function isCanonicalModel(model: string): model is CanonicalModel;
41
+ /**
42
+ * Resolve deprecated aliases to canonical model names and pass accepted model names through unchanged.
43
+ * Latest aliases pass through unchanged because they are server-side moving targets. This is a pure normalization helper
44
+ * and does not emit deprecation warnings.
45
+ */
46
+ declare function resolveModelAlias(model: string): Model | undefined;
47
+ /**
48
+ * Resolve deprecated aliases and canonical inputs to stable canonical model names.
49
+ * Latest aliases are server-side moving targets, so they intentionally return undefined. This is a pure normalization
50
+ * helper and does not emit deprecation warnings.
51
+ */
52
+ declare function resolveCanonicalModelAlias(model: string): CanonicalModel | undefined;
15
53
  declare const modelInputSchemas: {
16
54
  readonly "lucy-clip": z.ZodObject<{
17
55
  prompt: z.ZodString;
@@ -313,6 +351,19 @@ type ImageModelDefinition = ModelDefinition<ImageModels> & {
313
351
  type VideoModelDefinition = ModelDefinition<VideoModels> & {
314
352
  queueUrlPath: string;
315
353
  };
354
+ type ModelKind = "realtime" | "video" | "image";
355
+ type ListedModelDefinition = ModelDefinition & {
356
+ kind: ModelKind;
357
+ };
358
+ /**
359
+ * List SDK model definitions by kind.
360
+ * When canonicalOnly is true, deprecated and latest aliases are excluded per kind. Models available in multiple kinds
361
+ * are returned once per kind with the same name and different kind values.
362
+ */
363
+ declare function listModels(options?: {
364
+ kind?: ModelKind;
365
+ canonicalOnly?: boolean;
366
+ }): ListedModelDefinition[];
316
367
  declare const models: {
317
368
  /**
318
369
  * Get a realtime streaming model identifier.
@@ -348,4 +399,4 @@ declare const models: {
348
399
  };
349
400
  };
350
401
  //#endregion
351
- export { CustomModelDefinition, ImageModelDefinition, ImageModels, Model, ModelDefinition, ModelInputSchemas, RealTimeModels, VideoModelDefinition, VideoModels, isImageModel, isRealtimeModel, isVideoModel, models };
402
+ export { CanonicalModel, CustomModelDefinition, ImageModelDefinition, ImageModels, ListedModelDefinition, Model, ModelDefinition, ModelInputSchemas, ModelKind, RealTimeModels, VideoModelDefinition, VideoModels, isCanonicalModel, isImageModel, isModel, isRealtimeModel, isVideoModel, listModels, modelAliases, models, resolveCanonicalModelAlias, resolveModelAlias };
@@ -2,11 +2,37 @@ import { createModelNotFoundError } from "../utils/errors.js";
2
2
  import { z } from "zod";
3
3
 
4
4
  //#region src/shared/model.ts
5
+ const CANONICAL_MODEL_NAMES = [
6
+ "lucy-2.1",
7
+ "lucy-2.1-vton",
8
+ "lucy-vton-2",
9
+ "lucy-restyle-2",
10
+ "lucy-clip",
11
+ "lucy-image-2"
12
+ ];
13
+ const CANONICAL_REALTIME_MODEL_NAMES = [
14
+ "lucy-2.1",
15
+ "lucy-2.1-vton",
16
+ "lucy-vton-2",
17
+ "lucy-restyle-2"
18
+ ];
19
+ const CANONICAL_VIDEO_MODEL_NAMES = [
20
+ "lucy-clip",
21
+ "lucy-2.1",
22
+ "lucy-2.1-vton",
23
+ "lucy-vton-2",
24
+ "lucy-restyle-2"
25
+ ];
26
+ const CANONICAL_IMAGE_MODEL_NAMES = ["lucy-image-2"];
27
+ const canonicalRealtimeModels = z.enum(CANONICAL_REALTIME_MODEL_NAMES);
28
+ const canonicalVideoModels = z.enum(CANONICAL_VIDEO_MODEL_NAMES);
29
+ const canonicalImageModels = z.enum(CANONICAL_IMAGE_MODEL_NAMES);
30
+ const canonicalModelSchema = z.enum(CANONICAL_MODEL_NAMES);
5
31
  /**
6
32
  * Map of deprecated model names to their canonical replacements.
7
33
  * Old names still work but will log a deprecation warning.
8
34
  */
9
- const MODEL_ALIASES = {
35
+ const modelAliases = {
10
36
  mirage_v2: "lucy-restyle-2",
11
37
  "lucy-vton": "lucy-2.1-vton",
12
38
  "lucy-2.1-vton-2": "lucy-vton-2",
@@ -16,7 +42,7 @@ const MODEL_ALIASES = {
16
42
  };
17
43
  const _warnedAliases = /* @__PURE__ */ new Set();
18
44
  function warnDeprecated(model) {
19
- const canonical = MODEL_ALIASES[model];
45
+ const canonical = modelAliases[model];
20
46
  if (canonical && !_warnedAliases.has(model)) {
21
47
  _warnedAliases.add(model);
22
48
  console.warn(`[Decart SDK] Model "${model}" is deprecated. Use "${canonical}" instead. See https://docs.platform.decart.ai/models for details.`);
@@ -68,6 +94,34 @@ function isVideoModel(model) {
68
94
  function isImageModel(model) {
69
95
  return imageModels.safeParse(model).success;
70
96
  }
97
+ function isModel(model) {
98
+ return modelSchema.safeParse(model).success;
99
+ }
100
+ function isCanonicalModel(model) {
101
+ return canonicalModelSchema.safeParse(model).success;
102
+ }
103
+ /**
104
+ * Resolve deprecated aliases to canonical model names and pass accepted model names through unchanged.
105
+ * Latest aliases pass through unchanged because they are server-side moving targets. This is a pure normalization helper
106
+ * and does not emit deprecation warnings.
107
+ */
108
+ function resolveModelAlias(model) {
109
+ const canonical = modelAliases[model];
110
+ if (canonical) return canonical;
111
+ const parsedModel = modelSchema.safeParse(model);
112
+ return parsedModel.success ? parsedModel.data : void 0;
113
+ }
114
+ /**
115
+ * Resolve deprecated aliases and canonical inputs to stable canonical model names.
116
+ * Latest aliases are server-side moving targets, so they intentionally return undefined. This is a pure normalization
117
+ * helper and does not emit deprecation warnings.
118
+ */
119
+ function resolveCanonicalModelAlias(model) {
120
+ const canonical = modelAliases[model];
121
+ if (canonical) return canonical;
122
+ const parsedModel = canonicalModelSchema.safeParse(model);
123
+ return parsedModel.success ? parsedModel.data : void 0;
124
+ }
71
125
  const fileInputSchema = z.union([
72
126
  z.instanceof(File),
73
127
  z.instanceof(Blob),
@@ -382,6 +436,29 @@ const _models = {
382
436
  }
383
437
  }
384
438
  };
439
+ const modelKinds = [
440
+ "realtime",
441
+ "video",
442
+ "image"
443
+ ];
444
+ const canonicalSchemasByKind = {
445
+ realtime: canonicalRealtimeModels,
446
+ video: canonicalVideoModels,
447
+ image: canonicalImageModels
448
+ };
449
+ /**
450
+ * List SDK model definitions by kind.
451
+ * When canonicalOnly is true, deprecated and latest aliases are excluded per kind. Models available in multiple kinds
452
+ * are returned once per kind with the same name and different kind values.
453
+ */
454
+ function listModels(options = {}) {
455
+ return (options.kind ? [options.kind] : modelKinds).flatMap((kind) => {
456
+ return Object.values(_models[kind]).filter((modelDefinition) => !options.canonicalOnly || canonicalSchemasByKind[kind].safeParse(modelDefinition.name).success).map((modelDefinition) => ({
457
+ ...modelDefinition,
458
+ kind
459
+ }));
460
+ });
461
+ }
385
462
  const models = {
386
463
  realtime: (model) => {
387
464
  warnDeprecated(model);
@@ -404,4 +481,4 @@ const models = {
404
481
  };
405
482
 
406
483
  //#endregion
407
- export { isImageModel, isRealtimeModel, isVideoModel, modelDefinitionSchema, models };
484
+ export { isCanonicalModel, isImageModel, isModel, isRealtimeModel, isVideoModel, listModels, modelAliases, modelDefinitionSchema, models, resolveCanonicalModelAlias, resolveModelAlias };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decartai/sdk",
3
- "version": "0.0.65",
3
+ "version": "0.0.67",
4
4
  "description": "Decart's JavaScript SDK",
5
5
  "type": "module",
6
6
  "license": "MIT",