@scenario-labs/sdk 2.0.0 → 2.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.
- package/CHANGELOG.md +40 -0
- package/api-promise.d.mts +1 -0
- package/api-promise.d.mts.map +1 -1
- package/api-promise.d.ts +1 -0
- package/api-promise.d.ts.map +1 -1
- package/api-promise.mjs +1 -0
- package/api-promise.mjs.map +1 -1
- package/client.d.mts.map +1 -1
- package/client.d.ts.map +1 -1
- package/client.mjs.map +1 -1
- package/core/api-promise.d.mts.map +1 -1
- package/core/api-promise.d.ts.map +1 -1
- package/core/api-promise.mjs.map +1 -1
- package/core/error.mjs.map +1 -1
- package/core/pagination.d.mts.map +1 -1
- package/core/pagination.d.ts.map +1 -1
- package/core/pagination.mjs.map +1 -1
- package/core/resource.d.mts.map +1 -1
- package/core/resource.d.ts.map +1 -1
- package/core/uploads.d.mts.map +1 -1
- package/core/uploads.d.ts.map +1 -1
- package/core/uploads.mjs.map +1 -1
- package/error.d.mts +1 -0
- package/error.d.mts.map +1 -1
- package/error.d.ts +1 -0
- package/error.d.ts.map +1 -1
- package/error.mjs +1 -0
- package/error.mjs.map +1 -1
- package/index.d.mts +1 -1
- package/index.d.mts.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +6 -1
- package/index.js.map +1 -1
- package/index.mjs +1 -1
- package/index.mjs.map +1 -1
- package/internal/detect-platform.mjs.map +1 -1
- package/internal/headers.mjs.map +1 -1
- package/internal/parse.d.mts.map +1 -1
- package/internal/parse.d.ts.map +1 -1
- package/internal/parse.mjs.map +1 -1
- package/internal/qs/formats.d.mts.map +1 -1
- package/internal/qs/formats.d.ts.map +1 -1
- package/internal/qs/index.d.mts +2 -2
- package/internal/qs/index.d.mts.map +1 -1
- package/internal/qs/index.d.ts +2 -2
- package/internal/qs/index.d.ts.map +1 -1
- package/internal/qs/index.mjs.map +1 -1
- package/internal/qs/stringify.d.mts.map +1 -1
- package/internal/qs/stringify.d.ts.map +1 -1
- package/internal/qs/stringify.mjs.map +1 -1
- package/internal/qs/utils.d.mts.map +1 -1
- package/internal/qs/utils.d.ts.map +1 -1
- package/internal/qs/utils.mjs.map +1 -1
- package/internal/request-options.d.mts.map +1 -1
- package/internal/request-options.d.ts.map +1 -1
- package/internal/shims.d.mts +6 -0
- package/internal/shims.d.mts.map +1 -1
- package/internal/shims.d.ts +6 -0
- package/internal/shims.d.ts.map +1 -1
- package/internal/to-file.d.mts.map +1 -1
- package/internal/to-file.d.ts.map +1 -1
- package/internal/to-file.mjs.map +1 -1
- package/internal/types.d.mts +4 -4
- package/internal/types.d.mts.map +1 -1
- package/internal/types.d.ts +4 -4
- package/internal/types.d.ts.map +1 -1
- package/internal/uploads.d.mts.map +1 -1
- package/internal/uploads.d.ts.map +1 -1
- package/internal/uploads.mjs.map +1 -1
- package/internal/utils/base64.mjs.map +1 -1
- package/internal/utils/log.d.mts.map +1 -1
- package/internal/utils/log.d.ts.map +1 -1
- package/internal/utils/log.mjs.map +1 -1
- package/internal/utils/path.mjs.map +1 -1
- package/internal/utils/query.mjs.map +1 -1
- package/internal/utils/values.mjs.map +1 -1
- package/internal/utils.d.mts.map +1 -1
- package/internal/utils.d.ts.map +1 -1
- package/internal/utils.mjs.map +1 -1
- package/lib/asset.d.mts.map +1 -1
- package/lib/asset.d.ts.map +1 -1
- package/lib/asset.mjs.map +1 -1
- package/lib/generate.d.mts.map +1 -1
- package/lib/generate.d.ts.map +1 -1
- package/lib/generate.mjs.map +1 -1
- package/lib/job.d.mts.map +1 -1
- package/lib/job.d.ts.map +1 -1
- package/lib/job.mjs.map +1 -1
- package/lib/models.d.mts.map +1 -1
- package/lib/models.d.ts.map +1 -1
- package/lib/models.mjs.map +1 -1
- package/lib/scenario.d.mts.map +1 -1
- package/lib/scenario.d.ts.map +1 -1
- package/lib/scenario.mjs.map +1 -1
- package/lib/upload.d.mts.map +1 -1
- package/lib/upload.d.ts.map +1 -1
- package/lib/upload.mjs.map +1 -1
- package/lib/workflow.d.mts.map +1 -1
- package/lib/workflow.d.ts.map +1 -1
- package/lib/workflow.mjs.map +1 -1
- package/lib/workflow_converter.d.mts +58 -2
- package/lib/workflow_converter.d.mts.map +1 -1
- package/lib/workflow_converter.d.ts +58 -2
- package/lib/workflow_converter.d.ts.map +1 -1
- package/lib/workflow_converter.js +350 -35
- package/lib/workflow_converter.js.map +1 -1
- package/lib/workflow_converter.mjs +345 -35
- package/lib/workflow_converter.mjs.map +1 -1
- package/lib/workflow_import_validator.d.mts +1 -1
- package/lib/workflow_import_validator.d.mts.map +1 -1
- package/lib/workflow_import_validator.d.ts +1 -1
- package/lib/workflow_import_validator.d.ts.map +1 -1
- package/lib/workflow_import_validator.js +1 -0
- package/lib/workflow_import_validator.js.map +1 -1
- package/lib/workflow_import_validator.mjs +1 -0
- package/lib/workflow_import_validator.mjs.map +1 -1
- package/package.json +1 -1
- package/pagination.d.mts +1 -0
- package/pagination.d.mts.map +1 -1
- package/pagination.d.ts +1 -0
- package/pagination.d.ts.map +1 -1
- package/pagination.mjs +1 -0
- package/pagination.mjs.map +1 -1
- package/resource.d.mts +1 -0
- package/resource.d.mts.map +1 -1
- package/resource.d.ts +1 -0
- package/resource.d.ts.map +1 -1
- package/resource.mjs +1 -0
- package/resource.mjs.map +1 -1
- package/resources/assets/assets.d.mts +289 -135
- package/resources/assets/assets.d.mts.map +1 -1
- package/resources/assets/assets.d.ts +289 -135
- package/resources/assets/assets.d.ts.map +1 -1
- package/resources/assets/assets.js.map +1 -1
- package/resources/assets/assets.mjs.map +1 -1
- package/resources/assets/download.d.mts.map +1 -1
- package/resources/assets/download.d.ts.map +1 -1
- package/resources/assets/download.mjs.map +1 -1
- package/resources/assets/index.d.mts.map +1 -1
- package/resources/assets/index.d.ts.map +1 -1
- package/resources/assets/index.mjs.map +1 -1
- package/resources/assets.d.mts.map +1 -1
- package/resources/assets.d.ts.map +1 -1
- package/resources/assets.mjs.map +1 -1
- package/resources/collections/assets.d.mts.map +1 -1
- package/resources/collections/assets.d.ts.map +1 -1
- package/resources/collections/assets.mjs.map +1 -1
- package/resources/collections/collections.d.mts.map +1 -1
- package/resources/collections/collections.d.ts.map +1 -1
- package/resources/collections/collections.mjs.map +1 -1
- package/resources/collections/index.d.mts.map +1 -1
- package/resources/collections/index.d.ts.map +1 -1
- package/resources/collections/index.mjs.map +1 -1
- package/resources/collections/models.d.mts.map +1 -1
- package/resources/collections/models.d.ts.map +1 -1
- package/resources/collections/models.mjs.map +1 -1
- package/resources/collections.d.mts.map +1 -1
- package/resources/collections.d.ts.map +1 -1
- package/resources/collections.mjs.map +1 -1
- package/resources/generate.d.mts +86 -48
- package/resources/generate.d.mts.map +1 -1
- package/resources/generate.d.ts +86 -48
- package/resources/generate.d.ts.map +1 -1
- package/resources/generate.mjs.map +1 -1
- package/resources/index.d.mts.map +1 -1
- package/resources/index.d.ts.map +1 -1
- package/resources/index.mjs.map +1 -1
- package/resources/jobs.d.mts +6 -6
- package/resources/jobs.d.mts.map +1 -1
- package/resources/jobs.d.ts +6 -6
- package/resources/jobs.d.ts.map +1 -1
- package/resources/jobs.mjs.map +1 -1
- package/resources/models/description.d.mts +54 -4
- package/resources/models/description.d.mts.map +1 -1
- package/resources/models/description.d.ts +54 -4
- package/resources/models/description.d.ts.map +1 -1
- package/resources/models/description.mjs.map +1 -1
- package/resources/models/examples.d.mts +74 -36
- package/resources/models/examples.d.mts.map +1 -1
- package/resources/models/examples.d.ts +74 -36
- package/resources/models/examples.d.ts.map +1 -1
- package/resources/models/examples.mjs.map +1 -1
- package/resources/models/index.d.mts.map +1 -1
- package/resources/models/index.d.ts.map +1 -1
- package/resources/models/index.mjs.map +1 -1
- package/resources/models/models.d.mts +21 -21
- package/resources/models/models.d.mts.map +1 -1
- package/resources/models/models.d.ts +21 -21
- package/resources/models/models.d.ts.map +1 -1
- package/resources/models/models.js.map +1 -1
- package/resources/models/models.mjs.map +1 -1
- package/resources/models/train.d.mts +8 -8
- package/resources/models/train.d.mts.map +1 -1
- package/resources/models/train.d.ts +8 -8
- package/resources/models/train.d.ts.map +1 -1
- package/resources/models/train.mjs.map +1 -1
- package/resources/models/training-images.d.mts.map +1 -1
- package/resources/models/training-images.d.ts.map +1 -1
- package/resources/models/training-images.mjs.map +1 -1
- package/resources/models.d.mts.map +1 -1
- package/resources/models.d.ts.map +1 -1
- package/resources/models.mjs.map +1 -1
- package/resources/oscu.d.mts.map +1 -1
- package/resources/oscu.d.ts.map +1 -1
- package/resources/oscu.mjs.map +1 -1
- package/resources/pricing/index.d.mts.map +1 -1
- package/resources/pricing/index.d.ts.map +1 -1
- package/resources/pricing/index.mjs.map +1 -1
- package/resources/pricing/oscu.d.mts.map +1 -1
- package/resources/pricing/oscu.d.ts.map +1 -1
- package/resources/pricing/oscu.mjs.map +1 -1
- package/resources/pricing/pricing.d.mts.map +1 -1
- package/resources/pricing/pricing.d.ts.map +1 -1
- package/resources/pricing/pricing.mjs.map +1 -1
- package/resources/pricing.d.mts.map +1 -1
- package/resources/pricing.d.ts.map +1 -1
- package/resources/pricing.mjs.map +1 -1
- package/resources/search.d.mts +6 -0
- package/resources/search.d.mts.map +1 -1
- package/resources/search.d.ts +6 -0
- package/resources/search.d.ts.map +1 -1
- package/resources/search.mjs.map +1 -1
- package/resources/tags.d.mts.map +1 -1
- package/resources/tags.d.ts.map +1 -1
- package/resources/tags.mjs.map +1 -1
- package/resources/uploads.d.mts +4 -4
- package/resources/uploads.d.mts.map +1 -1
- package/resources/uploads.d.ts +4 -4
- package/resources/uploads.d.ts.map +1 -1
- package/resources/uploads.mjs.map +1 -1
- package/resources/usages.d.mts +31 -6
- package/resources/usages.d.mts.map +1 -1
- package/resources/usages.d.ts +31 -6
- package/resources/usages.d.ts.map +1 -1
- package/resources/usages.mjs.map +1 -1
- package/resources/workflows.d.mts +36 -36
- package/resources/workflows.d.mts.map +1 -1
- package/resources/workflows.d.ts +36 -36
- package/resources/workflows.d.ts.map +1 -1
- package/resources/workflows.mjs.map +1 -1
- package/resources.d.mts.map +1 -1
- package/resources.d.ts.map +1 -1
- package/resources.mjs.map +1 -1
- package/src/index.ts +9 -0
- package/src/lib/workflow_converter.ts +487 -28
- package/src/lib/workflow_import_validator.ts +2 -0
- package/src/resources/assets/assets.ts +376 -118
- package/src/resources/generate.ts +106 -44
- package/src/resources/jobs.ts +6 -6
- package/src/resources/models/description.ts +68 -2
- package/src/resources/models/examples.ts +94 -32
- package/src/resources/models/models.ts +21 -14
- package/src/resources/models/train.ts +8 -6
- package/src/resources/search.ts +7 -0
- package/src/resources/uploads.ts +4 -4
- package/src/resources/usages.ts +41 -4
- package/src/resources/workflows.ts +36 -36
- package/src/version.ts +1 -1
- package/uploads.d.mts +1 -0
- package/uploads.d.mts.map +1 -1
- package/uploads.d.ts +1 -0
- package/uploads.d.ts.map +1 -1
- package/uploads.mjs +1 -0
- package/uploads.mjs.map +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
// Converts a workflow editor state (nodes + edges) to a flow array suitable for the Scenario API.
|
|
2
2
|
// Ported from fawkes useConvertWorkflowToApp — pure function, no React dependencies.
|
|
3
|
+
const RUNNABLE_TYPES = new Set(['model', 'llm', 'forEach', 'forEachEnd']);
|
|
4
|
+
export const ASPECT_RATIO_PRESETS = [
|
|
5
|
+
'16:9',
|
|
6
|
+
'3:2',
|
|
7
|
+
'4:3',
|
|
8
|
+
'5:4',
|
|
9
|
+
'1:1',
|
|
10
|
+
'4:5',
|
|
11
|
+
'3:4',
|
|
12
|
+
'2:3',
|
|
13
|
+
'9:16',
|
|
14
|
+
];
|
|
3
15
|
// ---- Utility functions ----
|
|
4
16
|
function nodeIdToFlowId(nodeId) {
|
|
5
17
|
return nodeId.replace(/\s/g, '');
|
|
@@ -30,6 +42,96 @@ function convertValueToList(value) {
|
|
|
30
42
|
function getConditionalInputHandleId(nodeId) {
|
|
31
43
|
return `${nodeId}-source-conditional`;
|
|
32
44
|
}
|
|
45
|
+
function handleTypeIncludes(handleType, value) {
|
|
46
|
+
if (Array.isArray(handleType))
|
|
47
|
+
return handleType.includes(value);
|
|
48
|
+
return handleType === value;
|
|
49
|
+
}
|
|
50
|
+
function parseAspectRatio(aspectRatio) {
|
|
51
|
+
if (!aspectRatio)
|
|
52
|
+
return undefined;
|
|
53
|
+
const [rawWidth, rawHeight] = aspectRatio.split(':');
|
|
54
|
+
const width = Number(rawWidth);
|
|
55
|
+
const height = Number(rawHeight);
|
|
56
|
+
if (!Number.isFinite(width) ||
|
|
57
|
+
!Number.isFinite(height) ||
|
|
58
|
+
width <= 0 ||
|
|
59
|
+
height <= 0 ||
|
|
60
|
+
!Number.isInteger(width) ||
|
|
61
|
+
!Number.isInteger(height)) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
return { width, height };
|
|
65
|
+
}
|
|
66
|
+
function getGreatestCommonDivisor(a, b) {
|
|
67
|
+
let x = Math.abs(Math.trunc(a));
|
|
68
|
+
let y = Math.abs(Math.trunc(b));
|
|
69
|
+
while (y) {
|
|
70
|
+
const temp = y;
|
|
71
|
+
y = x % y;
|
|
72
|
+
x = temp;
|
|
73
|
+
}
|
|
74
|
+
return x || 1;
|
|
75
|
+
}
|
|
76
|
+
function makeNumberAMultipleOfX(value, multipleOf) {
|
|
77
|
+
if (multipleOf <= 0)
|
|
78
|
+
return Math.round(value);
|
|
79
|
+
return Math.round(value / multipleOf) * multipleOf;
|
|
80
|
+
}
|
|
81
|
+
function clamp(value, min, max) {
|
|
82
|
+
return Math.min(Math.max(value, min), max);
|
|
83
|
+
}
|
|
84
|
+
export function normalizeAspectRatio(aspectRatio) {
|
|
85
|
+
const parsed = parseAspectRatio(aspectRatio);
|
|
86
|
+
if (!parsed)
|
|
87
|
+
return undefined;
|
|
88
|
+
const divisor = getGreatestCommonDivisor(parsed.width, parsed.height);
|
|
89
|
+
return `${parsed.width / divisor}:${parsed.height / divisor}`;
|
|
90
|
+
}
|
|
91
|
+
export function getAspectRatioModelInputNames(model) {
|
|
92
|
+
if (!model)
|
|
93
|
+
return {};
|
|
94
|
+
const widthInputName = model.uiConfig?.resolutionComponent?.widthInput ??
|
|
95
|
+
model.inputs?.find((input) => input.name === 'width')?.name;
|
|
96
|
+
const heightInputName = model.uiConfig?.resolutionComponent?.heightInput ??
|
|
97
|
+
model.inputs?.find((input) => input.name === 'height')?.name;
|
|
98
|
+
const aspectRatioInputName = model.inputs?.find((input) => input.name === 'aspectRatio')?.name;
|
|
99
|
+
return { widthInputName, heightInputName, aspectRatioInputName };
|
|
100
|
+
}
|
|
101
|
+
export function getAspectRatioBounds(model, { widthInputName, heightInputName }) {
|
|
102
|
+
if (!model || !widthInputName || !heightInputName)
|
|
103
|
+
return undefined;
|
|
104
|
+
const widthInput = model.inputs?.find((input) => input.name === widthInputName);
|
|
105
|
+
const heightInput = model.inputs?.find((input) => input.name === heightInputName);
|
|
106
|
+
const presets = model.uiConfig?.resolutionComponent?.presets;
|
|
107
|
+
const widthPresets = (presets ?? [])
|
|
108
|
+
.map((preset) => preset.width)
|
|
109
|
+
.filter((value) => typeof value === 'number');
|
|
110
|
+
const heightPresets = (presets ?? [])
|
|
111
|
+
.map((preset) => preset.height)
|
|
112
|
+
.filter((value) => typeof value === 'number');
|
|
113
|
+
const multipleOf = widthInput?.step ?? 8;
|
|
114
|
+
return {
|
|
115
|
+
maxWidth: widthInput?.max ?? (widthPresets.length > 0 ? Math.max(...widthPresets) : 2048),
|
|
116
|
+
minWidth: widthInput?.min ?? (widthPresets.length > 0 ? Math.min(...widthPresets) : 64),
|
|
117
|
+
maxHeight: heightInput?.max ?? (heightPresets.length > 0 ? Math.max(...heightPresets) : 2048),
|
|
118
|
+
minHeight: heightInput?.min ?? (heightPresets.length > 0 ? Math.min(...heightPresets) : 64),
|
|
119
|
+
multipleOf,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export function getDimensionsFromAspectRatio(args) {
|
|
123
|
+
const parsed = parseAspectRatio(args.aspectRatio);
|
|
124
|
+
if (!parsed)
|
|
125
|
+
return undefined;
|
|
126
|
+
const ratio = parsed.width / parsed.height;
|
|
127
|
+
let width = ratio >= 1 ? args.bounds.maxWidth : Math.round(args.bounds.maxHeight * ratio);
|
|
128
|
+
let height = ratio >= 1 ? Math.round(args.bounds.maxWidth / ratio) : args.bounds.maxHeight;
|
|
129
|
+
width = makeNumberAMultipleOfX(width, args.bounds.multipleOf);
|
|
130
|
+
height = makeNumberAMultipleOfX(height, args.bounds.multipleOf);
|
|
131
|
+
width = clamp(width, args.bounds.minWidth, args.bounds.maxWidth);
|
|
132
|
+
height = clamp(height, args.bounds.minHeight, args.bounds.maxHeight);
|
|
133
|
+
return { width, height };
|
|
134
|
+
}
|
|
33
135
|
function getForEachFlowListInputDescriptor(args) {
|
|
34
136
|
const { listProvider, outputHandle, inputIndex } = args;
|
|
35
137
|
const isText = listProvider?.type === 'splitText' ||
|
|
@@ -257,11 +359,11 @@ function toStaticInput(name, type, value) {
|
|
|
257
359
|
return undefined;
|
|
258
360
|
return { name, type, value: value };
|
|
259
361
|
}
|
|
260
|
-
function
|
|
362
|
+
function getIsFormArray(value) {
|
|
261
363
|
return (Array.isArray(value) &&
|
|
262
364
|
value.every((item) => typeof item === 'object' && item !== null && !Array.isArray(item)));
|
|
263
365
|
}
|
|
264
|
-
function
|
|
366
|
+
function getIsModelCompose(model) {
|
|
265
367
|
return !!model?.id && (model.tags ?? []).includes('compose');
|
|
266
368
|
}
|
|
267
369
|
function getIsConditionallyExecuted(targetNodeId, nodes, edges) {
|
|
@@ -271,6 +373,87 @@ function getIsConditionallyExecuted(targetNodeId, nodes, edges) {
|
|
|
271
373
|
return (e.source === targetNodeId && e.sourceHandle === conditionalHandleId && sourceNode?.type === 'ifElse');
|
|
272
374
|
});
|
|
273
375
|
}
|
|
376
|
+
function buildDimensionLookupCEL(args) {
|
|
377
|
+
let expression = String(args.fallback);
|
|
378
|
+
for (let i = args.presets.length - 1; i >= 0; i--) {
|
|
379
|
+
const preset = args.presets[i];
|
|
380
|
+
expression = `trim(${args.variable}) == '${preset.aspectRatio}' ? ${preset[args.dimension]} : ${expression}`;
|
|
381
|
+
}
|
|
382
|
+
return expression;
|
|
383
|
+
}
|
|
384
|
+
function applyAspectRatioInputs(args) {
|
|
385
|
+
const modelInputNames = getAspectRatioModelInputNames(args.model);
|
|
386
|
+
const modelBounds = getAspectRatioBounds(args.model, modelInputNames);
|
|
387
|
+
const { widthInputName, heightInputName, aspectRatioInputName } = modelInputNames;
|
|
388
|
+
if (aspectRatioInputName) {
|
|
389
|
+
const transformId = `${args.flowId}_aspectRatio_value`;
|
|
390
|
+
args.flow.push({
|
|
391
|
+
id: transformId,
|
|
392
|
+
type: 'transform',
|
|
393
|
+
logic: { transform: 'trim(aspectRatio)' },
|
|
394
|
+
inputs: [{ name: 'aspectRatio', type: 'string', ref: args.aspectRatioRef }],
|
|
395
|
+
});
|
|
396
|
+
setOrPushFlowInput(args.flowInputs, args.flowInputs.findIndex((inp) => inp.name === aspectRatioInputName), {
|
|
397
|
+
name: aspectRatioInputName,
|
|
398
|
+
type: 'string',
|
|
399
|
+
ref: { node: transformId, name: 'all' },
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
if (widthInputName && heightInputName && modelBounds) {
|
|
403
|
+
const presetDimensions = ASPECT_RATIO_PRESETS.flatMap((preset) => {
|
|
404
|
+
const dims = getDimensionsFromAspectRatio({ aspectRatio: preset, bounds: modelBounds });
|
|
405
|
+
if (!dims)
|
|
406
|
+
return [];
|
|
407
|
+
const normalized = normalizeAspectRatio(preset);
|
|
408
|
+
if (!normalized)
|
|
409
|
+
return [];
|
|
410
|
+
return [{ aspectRatio: normalized, ...dims }];
|
|
411
|
+
});
|
|
412
|
+
const fallbackDimensions = presetDimensions.find((preset) => preset.aspectRatio === '1:1') ??
|
|
413
|
+
presetDimensions[0] ?? {
|
|
414
|
+
width: modelBounds.maxWidth,
|
|
415
|
+
height: modelBounds.maxHeight,
|
|
416
|
+
};
|
|
417
|
+
const widthTransformId = `${args.flowId}_aspectRatio_width`;
|
|
418
|
+
const heightTransformId = `${args.flowId}_aspectRatio_height`;
|
|
419
|
+
args.flow.push({
|
|
420
|
+
id: widthTransformId,
|
|
421
|
+
type: 'transform',
|
|
422
|
+
logic: {
|
|
423
|
+
transform: buildDimensionLookupCEL({
|
|
424
|
+
variable: 'aspectRatio',
|
|
425
|
+
presets: presetDimensions,
|
|
426
|
+
dimension: 'width',
|
|
427
|
+
fallback: fallbackDimensions.width,
|
|
428
|
+
}),
|
|
429
|
+
},
|
|
430
|
+
inputs: [{ name: 'aspectRatio', type: 'string', ref: args.aspectRatioRef }],
|
|
431
|
+
});
|
|
432
|
+
args.flow.push({
|
|
433
|
+
id: heightTransformId,
|
|
434
|
+
type: 'transform',
|
|
435
|
+
logic: {
|
|
436
|
+
transform: buildDimensionLookupCEL({
|
|
437
|
+
variable: 'aspectRatio',
|
|
438
|
+
presets: presetDimensions,
|
|
439
|
+
dimension: 'height',
|
|
440
|
+
fallback: fallbackDimensions.height,
|
|
441
|
+
}),
|
|
442
|
+
},
|
|
443
|
+
inputs: [{ name: 'aspectRatio', type: 'string', ref: args.aspectRatioRef }],
|
|
444
|
+
});
|
|
445
|
+
setOrPushFlowInput(args.flowInputs, args.flowInputs.findIndex((inp) => inp.name === widthInputName), {
|
|
446
|
+
name: widthInputName,
|
|
447
|
+
type: 'number',
|
|
448
|
+
ref: { node: widthTransformId, name: 'all' },
|
|
449
|
+
});
|
|
450
|
+
setOrPushFlowInput(args.flowInputs, args.flowInputs.findIndex((inp) => inp.name === heightInputName), {
|
|
451
|
+
name: heightInputName,
|
|
452
|
+
type: 'number',
|
|
453
|
+
ref: { node: heightTransformId, name: 'all' },
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
}
|
|
274
457
|
function getForEachBodyEditorNodeIds(forEachId, forEachEndId, edges) {
|
|
275
458
|
const bodyNodeIds = new Set();
|
|
276
459
|
const queue = [];
|
|
@@ -322,7 +505,6 @@ function getNodeOutputValue(node, targetHandleId) {
|
|
|
322
505
|
}
|
|
323
506
|
return undefined;
|
|
324
507
|
}
|
|
325
|
-
const RUNNABLE_TYPES = new Set(['model', 'llm', 'forEach', 'forEachEnd']);
|
|
326
508
|
function internalGetConnectedNodes(nodes, edges, startNodeId, direction, isStoppingAtRunnableNodes) {
|
|
327
509
|
const nodesById = new Map(nodes.map((n) => {
|
|
328
510
|
const meta = { id: n.id, type: n.type };
|
|
@@ -362,42 +544,44 @@ function internalGetConnectedNodes(nodes, edges, startNodeId, direction, isStopp
|
|
|
362
544
|
const idSet = new Set(ids);
|
|
363
545
|
return nodes.filter((n) => idSet.has(n.id));
|
|
364
546
|
}
|
|
547
|
+
function resolveIfElseConditionField(field, candidateNodes) {
|
|
548
|
+
const byExactId = candidateNodes.find((n) => n.id === field);
|
|
549
|
+
if (byExactId)
|
|
550
|
+
return { nodeId: field };
|
|
551
|
+
for (const node of candidateNodes) {
|
|
552
|
+
for (const handle of node.data.outputHandles ?? []) {
|
|
553
|
+
if (`${node.id}_${handle.name}` === field) {
|
|
554
|
+
return { nodeId: node.id, targetHandleId: handle.id };
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return undefined;
|
|
559
|
+
}
|
|
365
560
|
function normalizeNodes(nodes) {
|
|
366
561
|
return nodes.map((node) => {
|
|
367
562
|
if (node.type !== 'model' && node.type !== 'llm')
|
|
368
563
|
return node;
|
|
369
|
-
const hasType = (t, v) => Array.isArray(t) ? t.includes(v) : t === v;
|
|
370
564
|
const inputHandles = (node.data.inputHandles ?? [])
|
|
371
565
|
.flatMap((h) => (h.subHandles ? h.subHandles : [h]))
|
|
372
|
-
.filter((h) =>
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
566
|
+
.filter((h) => handleTypeIncludes(h.type, 'conditional') ||
|
|
567
|
+
handleTypeIncludes(h.type, 'aspectRatio') ||
|
|
568
|
+
handleTypeIncludes(h.type, 'prompt') ||
|
|
569
|
+
handleTypeIncludes(h.type, 'image') ||
|
|
570
|
+
handleTypeIncludes(h.type, 'video') ||
|
|
571
|
+
handleTypeIncludes(h.type, '3d') ||
|
|
572
|
+
handleTypeIncludes(h.type, 'audio') ||
|
|
378
573
|
(node.type === 'llm' && h.name === 'textInputs'));
|
|
379
574
|
return { ...node, data: { ...node.data, inputHandles } };
|
|
380
575
|
});
|
|
381
576
|
}
|
|
382
|
-
// ---- Main function ----
|
|
383
|
-
/**
|
|
384
|
-
* Converts a workflow editor state (nodes, edges, inputKeys) into a flow array
|
|
385
|
-
* ready to be passed to `client.workflows.update({ flow: ... })`.
|
|
386
|
-
*
|
|
387
|
-
* @example
|
|
388
|
-
* ```ts
|
|
389
|
-
* const flow = convertWorkflowEditorToFlow({ nodes, edges, inputKeys, getModel });
|
|
390
|
-
* await client.workflows.update(workflowId, { flow: flow as WorkflowUpdateParams.Flow[] });
|
|
391
|
-
* ```
|
|
392
|
-
*/
|
|
393
577
|
export function convertWorkflowEditorToFlow(params) {
|
|
394
578
|
const { edges, inputKeys } = params;
|
|
395
579
|
const nodes = normalizeNodes(params.nodes);
|
|
396
580
|
const inputNodeIds = nodes.filter((n) => !!n.data.isInput).map((n) => n.id);
|
|
397
|
-
const selectedOutputNodes = nodes.filter((n) => (n.type === 'model' || n.type === 'forEachEnd') && !!n.data.isOutput);
|
|
581
|
+
const selectedOutputNodes = nodes.filter((n) => (n.type === 'model' || n.type === 'forEachEnd' || n.type === 'llm') && !!n.data.isOutput);
|
|
398
582
|
const selectedOutputNodeIds = selectedOutputNodes.map((n) => n.id);
|
|
399
583
|
const selectedOutputModelNodeIds = new Set(selectedOutputNodes
|
|
400
|
-
.filter((n) => n.type === 'model')
|
|
584
|
+
.filter((n) => n.type === 'model' || n.type === 'llm')
|
|
401
585
|
.map((n) => n.id));
|
|
402
586
|
// Collect all nodes in branches that lead to selected outputs
|
|
403
587
|
const nodesInSelectedBranches = new Set();
|
|
@@ -431,6 +615,12 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
431
615
|
.filter((n) => !inputNodeIds.includes(n.id) && n.type !== 'forEachEnd')
|
|
432
616
|
.map((n) => [n.id, nodeIdToFlowId(n.id)]));
|
|
433
617
|
const nodeIdToInputNameMap = new Map(inputNodeIds.map((id) => [id, nodeIdToFlowId(id)]));
|
|
618
|
+
const approvalByApprovedNodeId = buildApprovalByApprovedNodeIdMap({
|
|
619
|
+
nodes,
|
|
620
|
+
edges,
|
|
621
|
+
nodesInSelectedBranches,
|
|
622
|
+
selectedOutputNodeIds,
|
|
623
|
+
});
|
|
434
624
|
const nodeHandleTransformMap = new Map();
|
|
435
625
|
const ctx = {
|
|
436
626
|
nodes,
|
|
@@ -504,6 +694,7 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
504
694
|
const model = remoteModel === 'unknown' ? undefined : remoteModel;
|
|
505
695
|
const modelInputs = model?.inputs;
|
|
506
696
|
const conditionalHandleId = getConditionalInputHandleId(node.id);
|
|
697
|
+
const aspectRatioEdges = [];
|
|
507
698
|
const edgesByInput = new Map();
|
|
508
699
|
for (const edge of incomingEdges) {
|
|
509
700
|
if (edge.sourceHandle === conditionalHandleId)
|
|
@@ -511,6 +702,10 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
511
702
|
const handle = getInputHandle(edge, node.data.inputHandles);
|
|
512
703
|
if (!handle?.name)
|
|
513
704
|
continue;
|
|
705
|
+
if (handleTypeIncludes(handle.type, 'aspectRatio')) {
|
|
706
|
+
aspectRatioEdges.push(edge);
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
514
709
|
const existing = edgesByInput.get(handle.name);
|
|
515
710
|
if (existing)
|
|
516
711
|
existing.push(edge);
|
|
@@ -523,7 +718,7 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
523
718
|
continue;
|
|
524
719
|
const modelInput = modelInputs?.find((inp) => inp.name === key);
|
|
525
720
|
if (modelInput?.type === 'inputs_array') {
|
|
526
|
-
if (!
|
|
721
|
+
if (!getIsFormArray(value))
|
|
527
722
|
continue;
|
|
528
723
|
const nestedInputs = modelInput.inputs ?? [];
|
|
529
724
|
const items = [];
|
|
@@ -544,7 +739,7 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
544
739
|
itemInputs.push(inp);
|
|
545
740
|
}
|
|
546
741
|
}
|
|
547
|
-
const isValid =
|
|
742
|
+
const isValid = getIsModelCompose(model) ? getHasComposeLayerSourceInput(itemInputs) : itemInputs.length > 0;
|
|
548
743
|
if (isValid)
|
|
549
744
|
items.push(itemInputs);
|
|
550
745
|
}
|
|
@@ -562,6 +757,18 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
562
757
|
}
|
|
563
758
|
}
|
|
564
759
|
}
|
|
760
|
+
if (aspectRatioEdges.length > 0 && model) {
|
|
761
|
+
const aspectRatioRef = getSourceRef(aspectRatioEdges[0], ctx);
|
|
762
|
+
if (aspectRatioRef) {
|
|
763
|
+
applyAspectRatioInputs({
|
|
764
|
+
flow,
|
|
765
|
+
flowInputs,
|
|
766
|
+
flowId,
|
|
767
|
+
aspectRatioRef,
|
|
768
|
+
model,
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
}
|
|
565
772
|
for (const [inputName, inputEdges] of edgesByInput) {
|
|
566
773
|
const existingIndex = flowInputs.findIndex((inp) => inp.name === inputName);
|
|
567
774
|
const modelInput = modelInputs?.find((inp) => inp.name === inputName);
|
|
@@ -622,7 +829,7 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
622
829
|
const isArr = targetInputType === 'file_array';
|
|
623
830
|
const isRequired = (targetNode?.type === 'asset' &&
|
|
624
831
|
!!targetNode.data.isRequired) ||
|
|
625
|
-
|
|
832
|
+
getIsModelCompose(model);
|
|
626
833
|
transformVariables.push({ name: targetNodeKey, isArray: isArr, isRequired });
|
|
627
834
|
if (!targetNode?.data.isInput &&
|
|
628
835
|
(targetNode?.type === 'asset' || targetNode?.type === 'text')) {
|
|
@@ -693,7 +900,9 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
693
900
|
type: 'custom-model',
|
|
694
901
|
modelId: nodeData.modelId,
|
|
695
902
|
inputs: flowInputs.length > 0 ? flowInputs : undefined,
|
|
696
|
-
includeOutputsInWorkflowJob: node.type === 'model' && selectedOutputModelNodeIds.has(node.id) ?
|
|
903
|
+
includeOutputsInWorkflowJob: (node.type === 'model' || node.type === 'llm') && selectedOutputModelNodeIds.has(node.id) ?
|
|
904
|
+
true
|
|
905
|
+
: undefined,
|
|
697
906
|
});
|
|
698
907
|
ensureInputVideoTransformsAdded();
|
|
699
908
|
if (node.type === 'model' && node.data.type === 'video') {
|
|
@@ -756,6 +965,19 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
756
965
|
addVideoFirstLastFrameTransforms(node.id, flowId, node, edges, flow, nodeHandleTransformMap);
|
|
757
966
|
}
|
|
758
967
|
// ------------------------------------
|
|
968
|
+
// AspectRatio -> transform with ratio literal
|
|
969
|
+
// ------------------------------------
|
|
970
|
+
}
|
|
971
|
+
else if (node.type === 'aspectRatio') {
|
|
972
|
+
const flowInputs = collectConditionalConnectionInputs(incomingEdges, node.id, ctx);
|
|
973
|
+
const value = node.data.output || '1:1';
|
|
974
|
+
flow.push({
|
|
975
|
+
id: flowId,
|
|
976
|
+
type: 'transform',
|
|
977
|
+
logic: { transform: `'${escapeCELString(value)}'` },
|
|
978
|
+
inputs: flowInputs.length > 0 ? flowInputs : undefined,
|
|
979
|
+
});
|
|
980
|
+
// ------------------------------------
|
|
759
981
|
// TransformText -> transform (CEL)
|
|
760
982
|
// ------------------------------------
|
|
761
983
|
}
|
|
@@ -834,7 +1056,7 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
834
1056
|
continue;
|
|
835
1057
|
const outHandle = targetNode.data.outputHandles?.find((h) => h.id === edge.targetHandle);
|
|
836
1058
|
const outHandleName = outHandle?.name ?? 'output';
|
|
837
|
-
const varName =
|
|
1059
|
+
const varName = `${targetNode.id}_${outHandleName}`;
|
|
838
1060
|
const isArr = (targetNode.type === 'asset' && !!targetNode.data.isMultiple) ||
|
|
839
1061
|
targetNode.type === 'groupItems' ||
|
|
840
1062
|
targetNode.type === 'sliceAssets' ||
|
|
@@ -943,17 +1165,47 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
943
1165
|
fieldsUsed.add(condition.field);
|
|
944
1166
|
}
|
|
945
1167
|
}
|
|
946
|
-
|
|
1168
|
+
const edgesIntoIfElse = edges.filter((e) => e.target === node.id);
|
|
1169
|
+
for (const fieldKey of fieldsUsed) {
|
|
1170
|
+
const resolved = resolveIfElseConditionField(fieldKey, nodes);
|
|
1171
|
+
const fieldNodeId = resolved?.nodeId;
|
|
1172
|
+
if (!fieldNodeId)
|
|
1173
|
+
continue;
|
|
1174
|
+
const outputHandleName = resolved?.targetHandleId !== undefined ?
|
|
1175
|
+
nodes
|
|
1176
|
+
.find((n) => n.id === fieldNodeId)
|
|
1177
|
+
?.data.outputHandles?.find((h) => h.id === resolved.targetHandleId)?.name ?? 'output'
|
|
1178
|
+
: 'output';
|
|
1179
|
+
const celVarName = getOutputHandleVarName(fieldNodeId, outputHandleName);
|
|
1180
|
+
nodeIdToInputNameMap.set(fieldKey, celVarName);
|
|
1181
|
+
const edgeToIfElse = edgesIntoIfElse
|
|
1182
|
+
.filter((e) => e.source === fieldNodeId)
|
|
1183
|
+
.find((e) => {
|
|
1184
|
+
if (resolved.targetHandleId === undefined)
|
|
1185
|
+
return true;
|
|
1186
|
+
return e.sourceHandle === resolved.targetHandleId;
|
|
1187
|
+
}) ?? edgesIntoIfElse.find((e) => e.source === fieldNodeId);
|
|
947
1188
|
if (inputNodeIds.includes(fieldNodeId)) {
|
|
948
|
-
const
|
|
949
|
-
if (
|
|
950
|
-
|
|
1189
|
+
const workflowInputName = nodeIdToInputNameMap.get(fieldNodeId);
|
|
1190
|
+
if (workflowInputName) {
|
|
1191
|
+
const ref = edgeToIfElse ?
|
|
1192
|
+
getSourceRef(edgeToIfElse, ctx)
|
|
1193
|
+
: { node: 'workflow', name: workflowInputName };
|
|
1194
|
+
flowInputs.push({
|
|
1195
|
+
name: celVarName,
|
|
1196
|
+
type: 'string',
|
|
1197
|
+
ref: ref ?? { node: 'workflow', name: workflowInputName },
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
951
1200
|
}
|
|
952
1201
|
else {
|
|
1202
|
+
const refFromEdge = edgeToIfElse ? getSourceRef(edgeToIfElse, ctx) : undefined;
|
|
953
1203
|
const targetFlowId = nodeIdToFlowIdMap.get(fieldNodeId);
|
|
954
|
-
if (
|
|
955
|
-
flowInputs.push({ name:
|
|
956
|
-
|
|
1204
|
+
if (refFromEdge) {
|
|
1205
|
+
flowInputs.push({ name: celVarName, type: 'string', ref: refFromEdge });
|
|
1206
|
+
}
|
|
1207
|
+
else if (targetFlowId) {
|
|
1208
|
+
flowInputs.push({ name: celVarName, type: 'string', ref: { node: targetFlowId, name: 'all' } });
|
|
957
1209
|
}
|
|
958
1210
|
}
|
|
959
1211
|
}
|
|
@@ -987,6 +1239,64 @@ export function convertWorkflowEditorToFlow(params) {
|
|
|
987
1239
|
for (const [forEachId, forEachFlowItem] of forEachFlowItemRefs) {
|
|
988
1240
|
forEachFlowItem.loopBodyNodeIds = forEachLoopFlowIds.get(forEachId) ?? [];
|
|
989
1241
|
}
|
|
1242
|
+
applyApprovalsToFlow({ flow, edges, approvalByApprovedNodeId, nodeIdToFlowIdMap });
|
|
990
1243
|
return flow;
|
|
991
1244
|
}
|
|
1245
|
+
function buildApprovalByApprovedNodeIdMap({ nodes, edges, nodesInSelectedBranches, selectedOutputNodeIds, }) {
|
|
1246
|
+
const result = new Map();
|
|
1247
|
+
for (const approvalNode of nodes) {
|
|
1248
|
+
if (approvalNode.type !== 'approval')
|
|
1249
|
+
continue;
|
|
1250
|
+
const approvalEdge = edges.find((e) => e.source === approvalNode.id && e.sourceHandle === `${approvalNode.id}-source-approval`);
|
|
1251
|
+
if (!approvalEdge)
|
|
1252
|
+
continue;
|
|
1253
|
+
const approvedNodeId = approvalEdge.target;
|
|
1254
|
+
if (!nodesInSelectedBranches.has(approvedNodeId) && !selectedOutputNodeIds.includes(approvedNodeId))
|
|
1255
|
+
continue;
|
|
1256
|
+
const approvedNode = nodes.find((n) => n.id === approvedNodeId);
|
|
1257
|
+
if (!approvedNode)
|
|
1258
|
+
continue;
|
|
1259
|
+
const approvedFlowId = approvedNode.type === 'forEachEnd' ?
|
|
1260
|
+
approvedNode.data.parentNodeId ?
|
|
1261
|
+
nodeIdToFlowId(approvedNode.data.parentNodeId)
|
|
1262
|
+
: undefined
|
|
1263
|
+
: nodeIdToFlowId(approvedNodeId);
|
|
1264
|
+
if (!approvedFlowId)
|
|
1265
|
+
continue;
|
|
1266
|
+
result.set(approvedNodeId, {
|
|
1267
|
+
approvalFlowId: nodeIdToFlowId(approvalNode.id),
|
|
1268
|
+
approvedFlowId,
|
|
1269
|
+
message: approvalNode.data.message ?? '',
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
return result;
|
|
1273
|
+
}
|
|
1274
|
+
function applyApprovalsToFlow({ flow, edges, approvalByApprovedNodeId, nodeIdToFlowIdMap, }) {
|
|
1275
|
+
if (approvalByApprovedNodeId.size === 0)
|
|
1276
|
+
return;
|
|
1277
|
+
const flowIdToWorkflowNodeId = new Map([...nodeIdToFlowIdMap.entries()].map(([nodeId, flowId]) => [flowId, nodeId]));
|
|
1278
|
+
for (const flowItem of flow) {
|
|
1279
|
+
const workflowNodeId = flowIdToWorkflowNodeId.get(flowItem.id);
|
|
1280
|
+
if (!workflowNodeId)
|
|
1281
|
+
continue;
|
|
1282
|
+
const dependsOn = new Set(flowItem.dependsOn ?? []);
|
|
1283
|
+
const incomingEdges = edges.filter((e) => e.source === workflowNodeId &&
|
|
1284
|
+
!e.data?.isForEachEndEdge);
|
|
1285
|
+
for (const edge of incomingEdges) {
|
|
1286
|
+
const approval = approvalByApprovedNodeId.get(edge.target);
|
|
1287
|
+
if (approval)
|
|
1288
|
+
dependsOn.add(approval.approvalFlowId);
|
|
1289
|
+
}
|
|
1290
|
+
if (dependsOn.size > 0)
|
|
1291
|
+
flowItem.dependsOn = [...dependsOn].sort();
|
|
1292
|
+
}
|
|
1293
|
+
for (const approvalInfo of approvalByApprovedNodeId.values()) {
|
|
1294
|
+
flow.push({
|
|
1295
|
+
id: approvalInfo.approvalFlowId,
|
|
1296
|
+
type: 'user-approval',
|
|
1297
|
+
dependsOn: [approvalInfo.approvedFlowId],
|
|
1298
|
+
label: approvalInfo.message,
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
992
1302
|
//# sourceMappingURL=workflow_converter.mjs.map
|