@sogni-ai/sogni-client 5.1.0 → 5.2.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 (68) hide show
  1. package/AGENTS.md +2 -1
  2. package/CHANGELOG.md +18 -0
  3. package/README.md +26 -1
  4. package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/index.d.ts +1 -0
  5. package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/index.js +12 -0
  6. package/dist/ApiClient/WebSocketClient/BrowserWebSocketClient/index.js.map +1 -1
  7. package/dist/ApiClient/WebSocketClient/events.d.ts +11 -0
  8. package/dist/Chat/index.d.ts +3 -1
  9. package/dist/Chat/index.js +17 -4
  10. package/dist/Chat/index.js.map +1 -1
  11. package/dist/Chat/modelRouting.d.ts +2 -0
  12. package/dist/Chat/modelRouting.js +6 -2
  13. package/dist/Chat/modelRouting.js.map +1 -1
  14. package/dist/Chat/types.d.ts +40 -0
  15. package/dist/Projects/createJobRequestMessage.js +1 -0
  16. package/dist/Projects/createJobRequestMessage.js.map +1 -1
  17. package/dist/Projects/index.js +3 -2
  18. package/dist/Projects/index.js.map +1 -1
  19. package/dist/Projects/types/ModelOptions.d.ts +1 -0
  20. package/dist/Projects/types/ModelOptions.js +2 -1
  21. package/dist/Projects/types/ModelOptions.js.map +1 -1
  22. package/dist/Projects/types/ModelTiersRaw.d.ts +1 -0
  23. package/dist/Projects/types/ModelTiersRaw.js.map +1 -1
  24. package/dist/Projects/types/events.d.ts +3 -0
  25. package/dist/Projects/types/index.d.ts +29 -9
  26. package/dist/Projects/types/index.js.map +1 -1
  27. package/dist/index.d.ts +3 -3
  28. package/dist/index.js.map +1 -1
  29. package/dist/lib/validation.d.ts +6 -0
  30. package/dist/lib/validation.js +40 -7
  31. package/dist/lib/validation.js.map +1 -1
  32. package/dist/version.d.ts +1 -1
  33. package/dist/version.js +1 -1
  34. package/dist-esm/ApiClient/WebSocketClient/BrowserWebSocketClient/index.js +12 -0
  35. package/dist-esm/ApiClient/WebSocketClient/BrowserWebSocketClient/index.js.map +1 -1
  36. package/dist-esm/Chat/index.js +17 -4
  37. package/dist-esm/Chat/index.js.map +1 -1
  38. package/dist-esm/Chat/modelRouting.js +6 -2
  39. package/dist-esm/Chat/modelRouting.js.map +1 -1
  40. package/dist-esm/Projects/createJobRequestMessage.js +2 -1
  41. package/dist-esm/Projects/createJobRequestMessage.js.map +1 -1
  42. package/dist-esm/Projects/index.js +3 -2
  43. package/dist-esm/Projects/index.js.map +1 -1
  44. package/dist-esm/Projects/types/ModelOptions.js +2 -1
  45. package/dist-esm/Projects/types/ModelOptions.js.map +1 -1
  46. package/dist-esm/Projects/types/ModelTiersRaw.js.map +1 -1
  47. package/dist-esm/Projects/types/index.js.map +1 -1
  48. package/dist-esm/index.js.map +1 -1
  49. package/dist-esm/lib/validation.js +39 -7
  50. package/dist-esm/lib/validation.js.map +1 -1
  51. package/dist-esm/version.js +1 -1
  52. package/llms-full.txt +18 -4
  53. package/llms.txt +10 -2
  54. package/package.json +1 -1
  55. package/src/ApiClient/WebSocketClient/BrowserWebSocketClient/index.ts +11 -0
  56. package/src/ApiClient/WebSocketClient/events.ts +12 -0
  57. package/src/Chat/index.ts +29 -1
  58. package/src/Chat/modelRouting.ts +15 -2
  59. package/src/Chat/types.ts +49 -0
  60. package/src/Projects/createJobRequestMessage.ts +3 -1
  61. package/src/Projects/index.ts +3 -2
  62. package/src/Projects/types/ModelOptions.ts +3 -1
  63. package/src/Projects/types/ModelTiersRaw.ts +1 -0
  64. package/src/Projects/types/events.ts +4 -0
  65. package/src/Projects/types/index.ts +29 -9
  66. package/src/index.ts +8 -1
  67. package/src/lib/validation.ts +44 -7
  68. package/src/version.ts +1 -1
package/src/Chat/index.ts CHANGED
@@ -21,6 +21,8 @@ import {
21
21
  HostedChatCompletionParams,
22
22
  ConfirmChatRunCostParams,
23
23
  HostedChatCompletionResult,
24
+ HostedToolExecutionParams,
25
+ HostedToolExecutionResult,
24
26
  LLMCostEstimation,
25
27
  LLMEstimateResponse,
26
28
  LLMModelInfo,
@@ -249,6 +251,7 @@ class ChatApi extends ApiGroup<ChatApiEvents> {
249
251
  */
250
252
  hosted: {
251
253
  create: (params: HostedChatCompletionParams) => Promise<HostedChatCompletionResult>;
254
+ executeTool: (params: HostedToolExecutionParams) => Promise<HostedToolExecutionResult>;
252
255
  };
253
256
 
254
257
  /**
@@ -295,7 +298,8 @@ class ChatApi extends ApiGroup<ChatApiEvents> {
295
298
  create: this.createCompletion.bind(this) as any
296
299
  };
297
300
  this.hosted = {
298
- create: this.createHostedCompletion.bind(this)
301
+ create: this.createHostedCompletion.bind(this),
302
+ executeTool: this.executeHostedTool.bind(this)
299
303
  };
300
304
  this.runs = {
301
305
  create: this.createChatRun.bind(this),
@@ -531,6 +535,9 @@ class ChatApi extends ApiGroup<ChatApiEvents> {
531
535
  task_profile: params.task_profile ?? params.taskProfile,
532
536
  media_references: params.media_references ?? params.mediaReferences,
533
537
  api_media_references: params.api_media_references ?? params.apiMediaReferences,
538
+ ...(params.safe_content_filter !== undefined || params.safeContentFilter !== undefined
539
+ ? { safe_content_filter: params.safe_content_filter ?? params.safeContentFilter }
540
+ : {}),
534
541
  ...(chatTemplateKwargs && { chat_template_kwargs: chatTemplateKwargs }),
535
542
  ...(params.response_format && { response_format: params.response_format })
536
543
  },
@@ -559,6 +566,24 @@ class ChatApi extends ApiGroup<ChatApiEvents> {
559
566
  }
560
567
  }
561
568
 
569
+ private async executeHostedTool(
570
+ params: HostedToolExecutionParams
571
+ ): Promise<HostedToolExecutionResult> {
572
+ return this.client.rest.post<HostedToolExecutionResult>(
573
+ '/v1/creative-agent/tools/execute',
574
+ {
575
+ tool: params.tool,
576
+ arguments: params.arguments,
577
+ app_source: params.app_source ?? params.appSource ?? this.client.appSource,
578
+ token_type: params.token_type ?? params.tokenType,
579
+ ...(params.safe_content_filter !== undefined || params.safeContentFilter !== undefined
580
+ ? { safe_content_filter: params.safe_content_filter ?? params.safeContentFilter }
581
+ : {})
582
+ },
583
+ { timeoutMs: 300000 }
584
+ );
585
+ }
586
+
562
587
  private async chatRunFetch(path: string, options: RequestInit = {}): Promise<Response> {
563
588
  const url = new URL(path, this.client.rest.baseUrl).toString();
564
589
  const authenticated = await this.client.auth.authenticateRequest(options);
@@ -793,6 +818,9 @@ class ChatApi extends ApiGroup<ChatApiEvents> {
793
818
  presence_penalty: params.presence_penalty,
794
819
  stop: params.stop,
795
820
  tokenType: params.tokenType,
821
+ ...(params.safeContentFilter !== undefined
822
+ ? { safeContentFilter: params.safeContentFilter }
823
+ : {}),
796
824
  ...(params.billingMode && { billingMode: params.billingMode }),
797
825
  tools: params.tools,
798
826
  tool_choice: params.tool_choice,
@@ -77,7 +77,9 @@ export const PREFERRED_MODEL_IDS = {
77
77
  flux2: 'flux2_dev_fp8',
78
78
  chromaFlash: 'chroma-v.46-flash_fp8',
79
79
  zTurbo: 'z_image_turbo_bf16',
80
- krea2Turbo: 'krea2_turbo_fp8_scaled'
80
+ krea2Turbo: 'krea2_turbo_fp8_scaled',
81
+ krea2IdentityEdit: 'krea2_identity_edit_v1_2',
82
+ darkBeastKrea2IdentityEdit: 'dark_beast_krea2_identity_edit_v1_2'
81
83
  },
82
84
  video: {
83
85
  t2v: 'ltx23-22b-fp8_t2v_distilled',
@@ -180,7 +182,16 @@ const EDIT_IMAGE_MODEL_SELECTORS: Record<string, string> = {
180
182
  ),
181
183
  'qwen-lightning': 'qwen_image_edit_2511_fp8_lightning',
182
184
  qwen: 'qwen_image_edit_2511_fp8',
183
- flux2: PREFERRED_MODEL_IDS.image.flux2
185
+ flux2: PREFERRED_MODEL_IDS.image.flux2,
186
+ 'krea-2-identity-edit': PREFERRED_MODEL_IDS.image.krea2IdentityEdit,
187
+ 'krea2-identity-edit': PREFERRED_MODEL_IDS.image.krea2IdentityEdit,
188
+ 'krea-identity-edit': PREFERRED_MODEL_IDS.image.krea2IdentityEdit,
189
+ 'krea-2-identity-edit-lora': PREFERRED_MODEL_IDS.image.krea2IdentityEdit,
190
+ 'krea-2-identity-edit-lora-v1-2': PREFERRED_MODEL_IDS.image.krea2IdentityEdit,
191
+ 'dark-beast-krea-2-identity-edit': PREFERRED_MODEL_IDS.image.darkBeastKrea2IdentityEdit,
192
+ 'dark-beast-krea2-identity-edit': PREFERRED_MODEL_IDS.image.darkBeastKrea2IdentityEdit,
193
+ 'dark-beast-krea-2-identity-edit-lora': PREFERRED_MODEL_IDS.image.darkBeastKrea2IdentityEdit,
194
+ 'dark-beast-krea-2-identity-edit-lora-v1-2': PREFERRED_MODEL_IDS.image.darkBeastKrea2IdentityEdit
184
195
  };
185
196
 
186
197
  const TEXT_VIDEO_MODEL_SELECTORS: Record<string, string> = {
@@ -383,6 +394,8 @@ export function isEditImageModel(modelId: string): boolean {
383
394
  return (
384
395
  modelId === PREFERRED_MODEL_IDS.image.gptImage2 ||
385
396
  modelId.startsWith('qwen_image_edit_') ||
397
+ modelId === PREFERRED_MODEL_IDS.image.krea2IdentityEdit ||
398
+ modelId === PREFERRED_MODEL_IDS.image.darkBeastKrea2IdentityEdit ||
386
399
  modelId.startsWith('flux2_') ||
387
400
  modelId.includes('kontext')
388
401
  );
package/src/Chat/types.ts CHANGED
@@ -108,6 +108,12 @@ export interface ChatCompletionParams {
108
108
  stop?: string | string[];
109
109
  /** Token type to use for billing. Defaults to 'sogni'. */
110
110
  tokenType?: 'sogni' | 'spark';
111
+ /**
112
+ * Whether the caller wants Sogni's safe-content filter applied for this
113
+ * request context. `false` allows backend routing to adult-capable text LLMs
114
+ * for prompt expansion when the request itself is text-only.
115
+ */
116
+ safeContentFilter?: boolean;
111
117
  /**
112
118
  * Billing mode for this request. `'subscription'` bills the job against the
113
119
  * active subscription entitlement, `'tokens'` forces token billing, and
@@ -192,6 +198,8 @@ export interface ChatRequestMessage {
192
198
  presence_penalty?: number;
193
199
  stop?: string | string[];
194
200
  tokenType?: 'sogni' | 'spark';
201
+ /** Safe-content filter state forwarded to the socket server. */
202
+ safeContentFilter?: boolean;
195
203
  /** Billing mode read by the socket server (`data.billingMode`) — see {@link ChatCompletionParams.billingMode}. */
196
204
  billingMode?: BillingMode;
197
205
  tools?: ToolDefinition[];
@@ -270,6 +278,43 @@ export interface HostedChatCompletionResult {
270
278
  sogni_tool_results?: Record<string, unknown>[];
271
279
  }
272
280
 
281
+ export type HostedSynchronousToolName =
282
+ | 'enhance_prompt'
283
+ | 'compose_script'
284
+ | 'compose_lyrics'
285
+ | 'compose_instrumental'
286
+ | 'compose_workflow'
287
+ | 'compose_workflow_template';
288
+
289
+ export interface HostedToolExecutionParams {
290
+ /** Synchronous hosted composition/planning tool to execute directly. */
291
+ tool: HostedSynchronousToolName | string;
292
+ /** Exact JSON arguments for the selected tool. */
293
+ arguments: Record<string, unknown>;
294
+ /** Optional source label for this hosted REST request. Defaults to the client appSource when configured. */
295
+ app_source?: string;
296
+ /** camelCase alias for {@link HostedToolExecutionParams.app_source}. */
297
+ appSource?: string;
298
+ /** Token type to use for hosted REST billing. */
299
+ token_type?: 'sogni' | 'spark' | 'auto';
300
+ /** camelCase alias for {@link HostedToolExecutionParams.token_type}. */
301
+ tokenType?: 'sogni' | 'spark' | 'auto';
302
+ /** Public snake_case safe-content filter state. */
303
+ safe_content_filter?: boolean;
304
+ /** camelCase alias for {@link HostedToolExecutionParams.safe_content_filter}. */
305
+ safeContentFilter?: boolean;
306
+ }
307
+
308
+ export interface HostedToolExecutionResult {
309
+ status: 'success';
310
+ data: {
311
+ toolCallId: string;
312
+ tool: string;
313
+ result: Record<string, unknown>;
314
+ message: string | null;
315
+ };
316
+ }
317
+
273
318
  export type HostedChatCompletionParams = Omit<
274
319
  ChatCompletionParams,
275
320
  'stream' | 'autoExecuteTools' | 'onToolCall' | 'onToolProgress' | 'maxToolRounds'
@@ -292,6 +337,10 @@ export type HostedChatCompletionParams = Omit<
292
337
  mediaReferences?: unknown[];
293
338
  api_media_references?: unknown[];
294
339
  apiMediaReferences?: unknown[];
340
+ /** Public snake_case safe-content filter state. */
341
+ safe_content_filter?: boolean;
342
+ /** camelCase alias for {@link HostedChatCompletionParams.safe_content_filter}. */
343
+ safeContentFilter?: boolean;
295
344
  };
296
345
 
297
346
  /**
@@ -21,7 +21,8 @@ import {
21
21
  isComfyModel,
22
22
  validateVideoDuration,
23
23
  validateSampler,
24
- validateScheduler
24
+ validateScheduler,
25
+ validateVae
25
26
  } from '../lib/validation.js';
26
27
  import {
27
28
  getVideoWorkflowType,
@@ -420,6 +421,7 @@ function applyImageParams(
420
421
  // ComfyUI models use comfySampler/comfyScheduler fields
421
422
  keyFrame.comfySampler = validateSampler(params.sampler, options);
422
423
  keyFrame.comfyScheduler = validateScheduler(params.scheduler, options);
424
+ keyFrame.vae = validateVae(params.vae, options);
423
425
  } else {
424
426
  // Legacy Forge models use scheduler/timeStepSpacing fields
425
427
  keyFrame.scheduler = validateSampler(params.sampler, options);
@@ -287,7 +287,8 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
287
287
  workerName: data.workerName,
288
288
  positivePrompt: data.positivePrompt,
289
289
  negativePrompt: data.negativePrompt,
290
- jobIndex: data.jobIndex
290
+ jobIndex: data.jobIndex,
291
+ preparation: data.preparation
291
292
  });
292
293
  return;
293
294
  case 'jobStarted': {
@@ -698,7 +699,7 @@ class ProjectsApi extends ApiGroup<ProjectApiEvents> {
698
699
  await this.uploadCNImage(project.id, data.controlNet.image);
699
700
  }
700
701
 
701
- // Context images (GPT Image 2 supports up to 16; Flux.2 Dev supports up to 6; Qwen Image Edit Plus supports up to 3; Flux Kontext supports up to 2)
702
+ // Context images (GPT Image 2 supports up to 16; Flux.2 Dev supports up to 6; Qwen Image Edit supports up to 3; Krea 2 Identity Edit and Flux Kontext support up to 2)
702
703
  if (data.contextImages?.length) {
703
704
  const maxContextImages = getMaxContextImages(data.modelId);
704
705
  if (data.contextImages.length > maxContextImages) {
@@ -31,6 +31,7 @@ export interface ImageModelOptions {
31
31
  guidance: NumRange;
32
32
  scheduler: Options<string>;
33
33
  sampler: Options<string>;
34
+ vae?: Options<string>;
34
35
  }
35
36
 
36
37
  export interface VideoModelOptions {
@@ -99,7 +100,8 @@ export function mapComfyImageTier(tier: ComfyImageTier): ImageModelOptions {
99
100
  steps: mapRange(tier.steps),
100
101
  guidance: mapRange(tier.guidance),
101
102
  scheduler: mapOptions(tier.comfyScheduler, schedulerValueToAlias),
102
- sampler: mapOptions(tier.comfySampler, samplerValueToAlias)
103
+ sampler: mapOptions(tier.comfySampler, samplerValueToAlias),
104
+ vae: tier.vae ? mapOptions(tier.vae) : undefined
103
105
  };
104
106
  }
105
107
 
@@ -10,6 +10,7 @@ export interface ComfyImageTier {
10
10
  guidance: NumericDefaults;
11
11
  steps: NumericDefaults;
12
12
  type: 'image';
13
+ vae?: StringDefaults;
13
14
  }
14
15
 
15
16
  export function isComfyImageTier(t: ModelTier): t is ComfyImageTier {
@@ -1,5 +1,8 @@
1
1
  import { AvailableModel } from './index.js';
2
2
  import ErrorData from '../../types/ErrorData.js';
3
+ import type { JobPreparation } from '../../ApiClient/WebSocketClient/events.js';
4
+
5
+ export type { JobPreparation } from '../../ApiClient/WebSocketClient/events.js';
3
6
 
4
7
  export interface ProjectEventBase {
5
8
  projectId: string;
@@ -32,6 +35,7 @@ export interface JobInitiating extends JobEventBase {
32
35
  positivePrompt?: string;
33
36
  negativePrompt?: string;
34
37
  jobIndex?: number;
38
+ preparation?: JobPreparation;
35
39
  }
36
40
 
37
41
  export interface JobStarted extends JobEventBase {
@@ -100,17 +100,31 @@ export interface BaseProjectParams {
100
100
  */
101
101
  appSource?: string;
102
102
  /**
103
- * Array of LoRA IDs to apply.
104
- * Available LoRAs are model-specific. The worker will download the LoRA
105
- * if not already present on the persistent volume.
106
- * LoRA IDs are resolved to filenames via the worker config API.
107
- * Example: ['multiple_angles']
103
+ * LoRA IDs to apply, in the order they should be chained.
104
+ *
105
+ * Which LoRAs are available depends on the model; the Krea 2 family carries
106
+ * the largest set. Workers download a LoRA on first use, so the first render
107
+ * with an uncached one takes longer to start.
108
+ *
109
+ * Order is significant. The LoRAs are applied in sequence and the same set in
110
+ * a different order produces a measurably different image, because these
111
+ * models run fp8-quantized and the patches do not commute.
112
+ *
113
+ * Up to 8 per render. IDs are resolved to filenames by the worker.
114
+ * Example: ['krea2-detail-enhancer', 'krea2-amateur']
108
115
  */
109
116
  loras?: string[];
110
117
  /**
111
- * Array of LoRA strengths corresponding to each LoRA in the loras array.
112
- * Values should be between 0.0 and 2.0. Defaults to 1.0 if not specified.
113
- * Example: [0.9]
118
+ * Strength for each entry in `loras`, positionally matched. Defaults to 1.0.
119
+ *
120
+ * Not restricted to positive values. Most Krea 2 LoRAs are bipolar sliders
121
+ * where a negative strength applies the inverse effect and 0 does nothing -
122
+ * Warm Light warms at 2 and cools at -2. Each LoRA has its own valid range
123
+ * and its author's recommended band; values outside the valid range are
124
+ * clamped server-side, and pushing past the recommended band usually costs
125
+ * detail rather than adding effect.
126
+ *
127
+ * Example: [3, -2]
114
128
  */
115
129
  loraStrengths?: number[];
116
130
  }
@@ -378,7 +392,8 @@ export interface ImageProjectParams extends BaseProjectParams {
378
392
  * Context images for multi-reference image generation.
379
393
  * GPT Image 2 supports up to 16 context images.
380
394
  * Flux.2 Dev supports up to 6 context images.
381
- * Qwen Image Edit Plus supports up to 3 context images.
395
+ * Qwen Image Edit supports up to 3 context images.
396
+ * Krea 2 Identity Edit supports up to 2 context images.
382
397
  * Flux Kontext supports up to 2 context images.
383
398
  */
384
399
  contextImages?: InputMedia[];
@@ -392,6 +407,11 @@ export interface ImageProjectParams extends BaseProjectParams {
392
407
  * to get the list of available schedulers.
393
408
  */
394
409
  scheduler?: string;
410
+ /**
411
+ * VAE filename, available options depend on the model. Use `sogni.projects.getModelOptions(modelId)`
412
+ * to get the list of available VAEs.
413
+ */
414
+ vae?: string;
395
415
  /**
396
416
  * Size preset ID to use. You can query available size presets
397
417
  * from `sogni.projects.sizePresets(network, modelId)`
package/src/index.ts CHANGED
@@ -42,7 +42,7 @@ import {
42
42
  CostEstimation,
43
43
  InputMedia
44
44
  } from './Projects/types/index.js';
45
- import type { ProjectEvent, JobEvent } from './Projects/types/events.js';
45
+ import type { ProjectEvent, JobEvent, JobPreparation } from './Projects/types/events.js';
46
46
  import type { RawProject } from './Projects/types/RawProject.js';
47
47
  import type { Balances, Reward, TxHistoryEntry } from './Account/types.js';
48
48
  import type {
@@ -92,6 +92,9 @@ import {
92
92
  HostedChatCompletionChoice,
93
93
  HostedChatCompletionMessage,
94
94
  HostedCreativeWorkflowReference,
95
+ HostedSynchronousToolName,
96
+ HostedToolExecutionParams,
97
+ HostedToolExecutionResult,
95
98
  TextContentPart,
96
99
  ImageUrlContentPart,
97
100
  TokenUsage as ChatTokenUsage,
@@ -199,6 +202,9 @@ export type {
199
202
  HostedChatCompletionParams,
200
203
  HostedChatCompletionResult,
201
204
  HostedCreativeWorkflowReference,
205
+ HostedSynchronousToolName,
206
+ HostedToolExecutionParams,
207
+ HostedToolExecutionResult,
202
208
  CreativeWorkflowArtifact,
203
209
  CreativeWorkflowEvent,
204
210
  CreativeWorkflowRecord,
@@ -303,6 +309,7 @@ export type {
303
309
  CostEstimation,
304
310
  ProjectEvent,
305
311
  JobEvent,
312
+ JobPreparation,
306
313
  RawProject,
307
314
  ToastMessage,
308
315
  DataEntity,
@@ -12,28 +12,37 @@ const EXTENDED_IMAGE_SIZE_MODEL_IDS = new Set([
12
12
  'flux2_dev_fp8'
13
13
  ]);
14
14
 
15
+ const KREA_IDENTITY_EDIT_MODEL_IDS = new Set([
16
+ 'krea2_identity_edit_v1_2',
17
+ 'dark_beast_krea2_identity_edit_v1_2'
18
+ ]);
19
+
15
20
  interface ImageSizeValidationOptions {
16
21
  modelId?: string;
17
22
  propertyName?: string;
18
23
  }
19
24
 
20
- function getMaxCustomImageSize(modelId?: string): number {
25
+ function getCustomImageSizeBounds(modelId?: string): { min: number; max: number } {
26
+ if (modelId && KREA_IDENTITY_EDIT_MODEL_IDS.has(modelId)) {
27
+ return { min: 512, max: 2048 };
28
+ }
21
29
  if (modelId === 'gpt-image-2') {
22
- return 3840;
30
+ return { min: 256, max: 3840 };
23
31
  }
24
32
  if (modelId && EXTENDED_IMAGE_SIZE_MODEL_IDS.has(modelId)) {
25
- return 2560;
33
+ return { min: 256, max: 2560 };
26
34
  }
27
- return 2048;
35
+ return { min: 256, max: 2048 };
28
36
  }
29
37
 
30
38
  export function validateCustomImageSize(
31
39
  value: any,
32
40
  { modelId, propertyName = 'Width and height' }: ImageSizeValidationOptions = {}
33
41
  ): number {
42
+ const bounds = getCustomImageSizeBounds(modelId);
34
43
  return validateNumber(value, {
35
- min: 256,
36
- max: getMaxCustomImageSize(modelId),
44
+ min: bounds.min,
45
+ max: bounds.max,
37
46
  propertyName
38
47
  });
39
48
  }
@@ -106,7 +115,16 @@ export function validateTeacacheThreshold(value?: number): number | undefined {
106
115
  }
107
116
 
108
117
  export function isComfyModel(modelId: string): boolean {
109
- const COMFY_PREFIXES = ['z_image_', 'krea2_', 'qwen_image_', 'flux2_', 'wan_', 'ace_step'];
118
+ const COMFY_PREFIXES = [
119
+ 'z_image_',
120
+ 'dark_beast_z_image_',
121
+ 'krea2_',
122
+ 'dark_beast_krea2_',
123
+ 'qwen_image_',
124
+ 'flux2_',
125
+ 'wan_',
126
+ 'ace_step'
127
+ ];
110
128
  return COMFY_PREFIXES.some((prefix) => modelId.startsWith(prefix));
111
129
  }
112
130
 
@@ -115,6 +133,7 @@ export function isComfyModel(modelId: string): boolean {
115
133
  * - GPT Image 2: 16 images
116
134
  * - Flux.2 Dev: 6 images
117
135
  * - Qwen Image Edit: 3 images
136
+ * - Krea 2 Identity Edit: 2 images
118
137
  * - Flux Kontext: 2 images
119
138
  * - Default: 3 images
120
139
  */
@@ -128,6 +147,9 @@ export function getMaxContextImages(modelId: string): number {
128
147
  if (modelId.startsWith('qwen_image_')) {
129
148
  return 3;
130
149
  }
150
+ if (KREA_IDENTITY_EDIT_MODEL_IDS.has(modelId)) {
151
+ return 2;
152
+ }
131
153
  if (modelId.includes('kontext')) {
132
154
  return 2;
133
155
  }
@@ -175,3 +197,18 @@ export function validateScheduler(value: string | undefined, options: ModelOptio
175
197
  `Invalid scheduler ${value}. Must be one of "${options.scheduler.allowed.join('", "')}".`
176
198
  );
177
199
  }
200
+
201
+ /**
202
+ * Validate a model-specific VAE value against allowed options.
203
+ * Returns the validated value unchanged; sogni-socket passes the filename to ComfyUI.
204
+ */
205
+ export function validateVae(value: string | undefined, options: ModelOptions) {
206
+ if (!('vae' in options) || !options.vae?.allowed.length || !value) {
207
+ return null;
208
+ }
209
+ return validateOption(
210
+ value,
211
+ options.vae.allowed,
212
+ `Invalid VAE ${value}. Must be one of "${options.vae.allowed.join('", "')}".`
213
+ );
214
+ }
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // AUTO-GENERATED by scripts/generate-version.mjs — do not edit.
2
2
  // Source of truth: package.json version.
3
- export const LIB_VERSION = '5.1.0';
3
+ export const LIB_VERSION = '5.2.0';