@scenario-labs/sdk 3.3.0 → 3.4.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 +34 -0
- package/client.d.mts +4 -4
- package/client.d.mts.map +1 -1
- package/client.d.ts +4 -4
- package/client.d.ts.map +1 -1
- package/client.js.map +1 -1
- package/client.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 +2 -1
- package/index.js.map +1 -1
- package/index.mjs +1 -1
- package/index.mjs.map +1 -1
- package/lib/asset.d.mts +1 -1
- package/lib/asset.d.ts +1 -1
- package/lib/asset.js +1 -1
- package/lib/asset.mjs +1 -1
- package/lib/upload.d.mts +10 -5
- package/lib/upload.d.mts.map +1 -1
- package/lib/upload.d.ts +10 -5
- package/lib/upload.d.ts.map +1 -1
- package/lib/upload.js.map +1 -1
- package/lib/upload.mjs.map +1 -1
- package/lib/user_selection_limits.d.mts +13 -0
- package/lib/user_selection_limits.d.mts.map +1 -0
- package/lib/user_selection_limits.d.ts +13 -0
- package/lib/user_selection_limits.d.ts.map +1 -0
- package/lib/user_selection_limits.js +16 -0
- package/lib/user_selection_limits.js.map +1 -0
- package/lib/user_selection_limits.mjs +13 -0
- package/lib/user_selection_limits.mjs.map +1 -0
- package/lib/workflow.d.mts +4 -2
- package/lib/workflow.d.mts.map +1 -1
- package/lib/workflow.d.ts +4 -2
- package/lib/workflow.d.ts.map +1 -1
- package/lib/workflow.js +5 -1
- package/lib/workflow.js.map +1 -1
- package/lib/workflow.mjs +5 -1
- package/lib/workflow.mjs.map +1 -1
- package/lib/workflow_converter.d.mts +6 -0
- package/lib/workflow_converter.d.mts.map +1 -1
- package/lib/workflow_converter.d.ts +6 -0
- package/lib/workflow_converter.d.ts.map +1 -1
- package/lib/workflow_converter.js +155 -53
- package/lib/workflow_converter.js.map +1 -1
- package/lib/workflow_converter.mjs +155 -53
- package/lib/workflow_converter.mjs.map +1 -1
- package/lib/workflow_import_validator.d.mts +17 -2
- package/lib/workflow_import_validator.d.mts.map +1 -1
- package/lib/workflow_import_validator.d.ts +17 -2
- package/lib/workflow_import_validator.d.ts.map +1 -1
- package/lib/workflow_import_validator.js +100 -1
- package/lib/workflow_import_validator.js.map +1 -1
- package/lib/workflow_import_validator.mjs +99 -1
- package/lib/workflow_import_validator.mjs.map +1 -1
- package/lib/workflow_validator.d.mts +1 -1
- package/lib/workflow_validator.d.mts.map +1 -1
- package/lib/workflow_validator.d.ts +1 -1
- package/lib/workflow_validator.d.ts.map +1 -1
- package/lib/workflow_validator.js +10 -11
- package/lib/workflow_validator.js.map +1 -1
- package/lib/workflow_validator.mjs +7 -8
- package/lib/workflow_validator.mjs.map +1 -1
- package/package.json +1 -1
- package/resources/assets/assets.d.mts +126 -30
- package/resources/assets/assets.d.mts.map +1 -1
- package/resources/assets/assets.d.ts +126 -30
- package/resources/assets/assets.d.ts.map +1 -1
- package/resources/assets/assets.js +12 -0
- package/resources/assets/assets.js.map +1 -1
- package/resources/assets/assets.mjs +12 -0
- package/resources/assets/assets.mjs.map +1 -1
- package/resources/assets/index.d.mts +1 -1
- package/resources/assets/index.d.mts.map +1 -1
- package/resources/assets/index.d.ts +1 -1
- package/resources/assets/index.d.ts.map +1 -1
- package/resources/assets/index.js.map +1 -1
- package/resources/assets/index.mjs.map +1 -1
- package/resources/generate.d.mts +120 -9
- package/resources/generate.d.mts.map +1 -1
- package/resources/generate.d.ts +120 -9
- package/resources/generate.d.ts.map +1 -1
- package/resources/index.d.mts +2 -2
- package/resources/index.d.mts.map +1 -1
- package/resources/index.d.ts +2 -2
- package/resources/index.d.ts.map +1 -1
- package/resources/index.js.map +1 -1
- package/resources/index.mjs.map +1 -1
- package/resources/jobs.d.mts +36 -0
- package/resources/jobs.d.mts.map +1 -1
- package/resources/jobs.d.ts +36 -0
- package/resources/jobs.d.ts.map +1 -1
- package/resources/models/description.d.mts +4 -4
- package/resources/models/description.d.mts.map +1 -1
- package/resources/models/description.d.ts +4 -4
- package/resources/models/description.d.ts.map +1 -1
- package/resources/models/examples.d.mts +40 -6
- package/resources/models/examples.d.mts.map +1 -1
- package/resources/models/examples.d.ts +40 -6
- package/resources/models/examples.d.ts.map +1 -1
- package/resources/models/index.d.mts +1 -1
- package/resources/models/index.d.mts.map +1 -1
- package/resources/models/index.d.ts +1 -1
- package/resources/models/index.d.ts.map +1 -1
- package/resources/models/index.js.map +1 -1
- package/resources/models/index.mjs.map +1 -1
- package/resources/models/models.d.mts +1353 -199
- package/resources/models/models.d.mts.map +1 -1
- package/resources/models/models.d.ts +1353 -199
- package/resources/models/models.d.ts.map +1 -1
- package/resources/models/models.js +13 -0
- package/resources/models/models.js.map +1 -1
- package/resources/models/models.mjs +13 -0
- package/resources/models/models.mjs.map +1 -1
- package/resources/models/train.d.mts +36 -2
- package/resources/models/train.d.mts.map +1 -1
- package/resources/models/train.d.ts +36 -2
- package/resources/models/train.d.ts.map +1 -1
- package/resources/recommendations.d.mts +16 -5
- package/resources/recommendations.d.mts.map +1 -1
- package/resources/recommendations.d.ts +16 -5
- package/resources/recommendations.d.ts.map +1 -1
- package/resources/usages.d.mts +14 -2
- package/resources/usages.d.mts.map +1 -1
- package/resources/usages.d.ts +14 -2
- package/resources/usages.d.ts.map +1 -1
- package/resources/workflows.d.mts +152 -9
- package/resources/workflows.d.mts.map +1 -1
- package/resources/workflows.d.ts +152 -9
- package/resources/workflows.d.ts.map +1 -1
- package/resources/workflows.js +2 -3
- package/resources/workflows.js.map +1 -1
- package/resources/workflows.mjs +2 -3
- package/resources/workflows.mjs.map +1 -1
- package/src/client.ts +8 -0
- package/src/index.ts +1 -0
- package/src/lib/asset.ts +1 -1
- package/src/lib/upload.ts +10 -5
- package/src/lib/user_selection_limits.ts +14 -0
- package/src/lib/workflow.ts +12 -1
- package/src/lib/workflow_converter.ts +176 -55
- package/src/lib/workflow_import_validator.ts +116 -1
- package/src/lib/workflow_validator.ts +12 -8
- package/src/resources/assets/assets.ts +164 -4
- package/src/resources/assets/index.ts +2 -0
- package/src/resources/generate.ts +142 -0
- package/src/resources/index.ts +4 -0
- package/src/resources/jobs.ts +42 -0
- package/src/resources/models/description.ts +6 -0
- package/src/resources/models/examples.ts +48 -0
- package/src/resources/models/index.ts +2 -0
- package/src/resources/models/models.ts +1610 -114
- package/src/resources/models/train.ts +42 -0
- package/src/resources/recommendations.ts +18 -3
- package/src/resources/usages.ts +16 -0
- package/src/resources/workflows.ts +176 -9
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
|
@@ -8,10 +8,9 @@ exports.getAspectRatioModelInputNames = getAspectRatioModelInputNames;
|
|
|
8
8
|
exports.getAspectRatioBounds = getAspectRatioBounds;
|
|
9
9
|
exports.getDimensionsFromAspectRatio = getDimensionsFromAspectRatio;
|
|
10
10
|
exports.convertWorkflowEditorToFlow = convertWorkflowEditorToFlow;
|
|
11
|
+
const user_selection_limits_1 = require("./user_selection_limits.js");
|
|
11
12
|
const RUNNABLE_TYPES = new Set(['model', 'llm', 'forEach', 'forEachEnd']);
|
|
12
13
|
const MODEL_CUSTOM_INPUT_HANDLE_SUFFIX = '-source-custom';
|
|
13
|
-
const USER_SELECTION_MIN_SELECT = 1;
|
|
14
|
-
const USER_SELECTION_MAX_SELECT = 100;
|
|
15
14
|
const DEFAULT_SLICE_ASSETS_CHOOSE_MESSAGE = 'Select the items to continue';
|
|
16
15
|
exports.ASPECT_RATIO_PRESETS = [
|
|
17
16
|
'21:9',
|
|
@@ -35,7 +34,12 @@ function toCelIdentifier(name) {
|
|
|
35
34
|
return name.replaceAll('-', '_');
|
|
36
35
|
}
|
|
37
36
|
function escapeCELString(value) {
|
|
38
|
-
return value
|
|
37
|
+
return value
|
|
38
|
+
.replace(/\\/g, '\\\\')
|
|
39
|
+
.replace(/'/g, "\\'")
|
|
40
|
+
.replace(/\n/g, '\\n')
|
|
41
|
+
.replace(/\r/g, '\\r')
|
|
42
|
+
.replace(/\t/g, '\\t');
|
|
39
43
|
}
|
|
40
44
|
function formatTextAsCELString(text) {
|
|
41
45
|
return `'${escapeCELString(text)}'`;
|
|
@@ -160,6 +164,7 @@ function getForEachFlowListInputDescriptor(args) {
|
|
|
160
164
|
listProvider?.type === 'text' ||
|
|
161
165
|
listProvider?.type === 'llm' ||
|
|
162
166
|
listProvider?.type === 'transformText' ||
|
|
167
|
+
listProvider?.type === 'joinTexts' ||
|
|
163
168
|
(outputHandle?.type === 'text' && !!outputHandle.isArray);
|
|
164
169
|
return isText ?
|
|
165
170
|
{ name: `text${inputIndex}`, type: 'string_array' }
|
|
@@ -333,17 +338,41 @@ function getIsSliceAssetsItemsSourceHandle(nodeId, sourceHandle) {
|
|
|
333
338
|
return sourceHandle === `${nodeId}-source-items` || sourceHandle === `${nodeId}-source-assets`;
|
|
334
339
|
}
|
|
335
340
|
/**
|
|
336
|
-
*
|
|
337
|
-
* list
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
341
|
+
* True when `expression` treats `varName` as a CEL list via indexing, `size(x)`,
|
|
342
|
+
* or list macros (`.map` / `.filter` / …). This is a converter tolerance for
|
|
343
|
+
* flows that index a list producer (editor First/Last/Nth stores `x[0]` on
|
|
344
|
+
* `isArray` handles; agent/MCP/import flows may index without that flag). It is
|
|
345
|
+
* not Fawkes's preview rule: the webapp binds a list only when the handle has
|
|
346
|
+
* `isArray`. Intentionally narrow so Prompt Builder cannot be steered into
|
|
347
|
+
* list semantics through other CEL list idioms.
|
|
348
|
+
*/
|
|
349
|
+
function expressionUsesListVariable(expression, varName) {
|
|
350
|
+
const escaped = varName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
351
|
+
return (new RegExp(`${escaped}\\s*\\[`).test(expression) ||
|
|
352
|
+
new RegExp(`\\bsize\\s*\\(\\s*${escaped}\\s*\\)`).test(expression) ||
|
|
353
|
+
new RegExp(`${escaped}\\s*\\.\\s*(map|filter|all|exists|exists_one)\\s*\\(`).test(expression));
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Prompt Builder (transformText) binds upstream vars for CEL. List producers
|
|
357
|
+
* bind as `string_array` when the source handle is `isArray`, or when the
|
|
358
|
+
* expression indexes/uses the variable as a list (see
|
|
359
|
+
* `expressionUsesListVariable`). Otherwise bare refs stay `string` so the API
|
|
360
|
+
* collapses the list to its first item.
|
|
361
|
+
*
|
|
362
|
+
* Fawkes today omits `isArray` on Slice/Group Items on purpose: only `isArray`
|
|
363
|
+
* handles get an indexed insert (`x[0]`); every other list insert is bare `x`
|
|
364
|
+
* and must collapse to the first item. Adding `isArray` to those handles later
|
|
365
|
+
* requires migrating stored expressions (`x` → `x[0]`) in the same Fawkes
|
|
366
|
+
* release, or both preview and published flows break on `'…' + x`.
|
|
342
367
|
*/
|
|
343
|
-
function getPromptBuilderInputType(targetNode, outHandle, outHandleName) {
|
|
344
|
-
|
|
345
|
-
'
|
|
346
|
-
|
|
368
|
+
function getPromptBuilderInputType(targetNode, outHandle, outHandleName, expression, varName) {
|
|
369
|
+
if (getConcatListSourceDescriptor(targetNode, outHandle, outHandleName).inputType !== 'string_array') {
|
|
370
|
+
return 'string';
|
|
371
|
+
}
|
|
372
|
+
if (outHandle?.isArray || expressionUsesListVariable(expression, varName)) {
|
|
373
|
+
return 'string_array';
|
|
374
|
+
}
|
|
375
|
+
return 'string';
|
|
347
376
|
}
|
|
348
377
|
function getConcatListSourceDescriptor(targetNode, outHandle, outHandleName) {
|
|
349
378
|
const isArray = (targetNode.type === 'asset' && !!targetNode.data.isMultiple) ||
|
|
@@ -560,12 +589,43 @@ function applyAspectRatioInputs(args) {
|
|
|
560
589
|
});
|
|
561
590
|
}
|
|
562
591
|
}
|
|
563
|
-
function
|
|
592
|
+
function buildLoopGraph(nodes, edges) {
|
|
593
|
+
const nodeById = new Map(nodes.map((n) => [n.id, n]));
|
|
594
|
+
const isInternalForEachEdge = (e) => {
|
|
595
|
+
const target = nodeById.get(e.target);
|
|
596
|
+
return (nodeById.get(e.source)?.type === 'forEach' &&
|
|
597
|
+
target?.type === 'forEachEnd' &&
|
|
598
|
+
target.data.parentNodeId === e.source);
|
|
599
|
+
};
|
|
600
|
+
const push = (map, key, value) => {
|
|
601
|
+
const list = map.get(key);
|
|
602
|
+
if (list)
|
|
603
|
+
list.push(value);
|
|
604
|
+
else
|
|
605
|
+
map.set(key, [value]);
|
|
606
|
+
};
|
|
607
|
+
const graph = {
|
|
608
|
+
nodeById,
|
|
609
|
+
consumers: new Map(),
|
|
610
|
+
dataProviders: new Map(),
|
|
611
|
+
dataConsumers: new Map(),
|
|
612
|
+
};
|
|
613
|
+
for (const e of edges) {
|
|
614
|
+
push(graph.consumers, e.target, e.source);
|
|
615
|
+
if (isInternalForEachEdge(e) || nodeById.get(e.source)?.type === 'approval')
|
|
616
|
+
continue;
|
|
617
|
+
push(graph.dataProviders, e.source, e.target);
|
|
618
|
+
push(graph.dataConsumers, e.target, e.source);
|
|
619
|
+
}
|
|
620
|
+
return graph;
|
|
621
|
+
}
|
|
622
|
+
function getForEachBodyEditorNodeIds(forEachId, forEachEndId, edges, graph) {
|
|
564
623
|
const bodyNodeIds = new Set();
|
|
565
624
|
const queue = [];
|
|
566
625
|
const escaped = forEachId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
626
|
+
const itemHandle = new RegExp(`^${escaped}-output-\\d+$`);
|
|
567
627
|
for (const edge of edges) {
|
|
568
|
-
if (edge.target === forEachId && edge.targetHandle
|
|
628
|
+
if (edge.target === forEachId && edge.targetHandle && itemHandle.test(edge.targetHandle)) {
|
|
569
629
|
queue.push(edge.source);
|
|
570
630
|
}
|
|
571
631
|
}
|
|
@@ -574,13 +634,12 @@ function getForEachBodyEditorNodeIds(forEachId, forEachEndId, edges, nodes) {
|
|
|
574
634
|
if (nodeId === forEachEndId || bodyNodeIds.has(nodeId))
|
|
575
635
|
continue;
|
|
576
636
|
bodyNodeIds.add(nodeId);
|
|
577
|
-
for (const
|
|
578
|
-
if (
|
|
579
|
-
queue.push(
|
|
580
|
-
}
|
|
637
|
+
for (const consumer of graph.consumers.get(nodeId) ?? []) {
|
|
638
|
+
if (!bodyNodeIds.has(consumer) && consumer !== forEachEndId)
|
|
639
|
+
queue.push(consumer);
|
|
581
640
|
}
|
|
582
641
|
}
|
|
583
|
-
addRandomizeClosureToBody(bodyNodeIds, forEachEndId,
|
|
642
|
+
addRandomizeClosureToBody(bodyNodeIds, forEachEndId, graph);
|
|
584
643
|
return bodyNodeIds;
|
|
585
644
|
}
|
|
586
645
|
/**
|
|
@@ -594,28 +653,34 @@ function getForEachBodyEditorNodeIds(forEachId, forEachEndId, edges, nodes) {
|
|
|
594
653
|
* for an UNLOCKED randomize node we mutate `bodyNodeIds` in place to also include
|
|
595
654
|
* that node plus the deterministic chain down to the body, so the runtime clones
|
|
596
655
|
* it per iteration and each iteration re-draws. We only pull it in when *every*
|
|
597
|
-
* path out of it lands in the body — if it also feeds a
|
|
598
|
-
* cloning it would break that external consumer, so we
|
|
656
|
+
* path out of it lands in the body or in this loop's results — if it also feeds a
|
|
657
|
+
* node outside the loop, cloning it would break that external consumer, so we
|
|
658
|
+
* leave it shared.
|
|
659
|
+
*
|
|
660
|
+
* Loop nodes (any for-each or loop end outside the body) stop both walks: a
|
|
661
|
+
* loop's list is computed once, before the loop runs, so neither a loop nor what
|
|
662
|
+
* feeds its list may be cloned with another loop's body. A for-each in its own
|
|
663
|
+
* body would even expand again on every iteration. A node that reaches one stays
|
|
664
|
+
* shared.
|
|
599
665
|
*/
|
|
600
|
-
function addRandomizeClosureToBody(bodyNodeIds, forEachEndId,
|
|
601
|
-
const
|
|
666
|
+
function addRandomizeClosureToBody(bodyNodeIds, forEachEndId, graph) {
|
|
667
|
+
const isLoopNode = (id) => {
|
|
668
|
+
const type = graph.nodeById.get(id)?.type;
|
|
669
|
+
return type === 'forEach' || type === 'forEachEnd';
|
|
670
|
+
};
|
|
602
671
|
const isUnlockedRandomize = (id) => {
|
|
603
|
-
const data = nodeById.get(id)?.data;
|
|
604
|
-
return data
|
|
672
|
+
const data = graph.nodeById.get(id)?.data;
|
|
673
|
+
return !!data && getSliceAssetsMode(data) === 'randomize' && data.isSeedLocked !== true;
|
|
605
674
|
};
|
|
606
|
-
//
|
|
607
|
-
// on `source` reads an output handle on `target`.
|
|
608
|
-
const providersOf = (id) => edges.filter((e) => e.source === id).map((e) => e.target);
|
|
609
|
-
const consumersOf = (id) => edges.filter((e) => e.target === id).map((e) => e.source);
|
|
610
|
-
// Every node strictly upstream of the body (its transitive input providers).
|
|
675
|
+
// The nodes that feed the body without going through a loop node.
|
|
611
676
|
const upstream = new Set();
|
|
612
677
|
const upstreamQueue = [...bodyNodeIds];
|
|
613
678
|
while (upstreamQueue.length > 0) {
|
|
614
|
-
for (const provider of
|
|
615
|
-
if (
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
679
|
+
for (const provider of graph.dataProviders.get(upstreamQueue.pop()) ?? []) {
|
|
680
|
+
if (bodyNodeIds.has(provider) || upstream.has(provider) || isLoopNode(provider))
|
|
681
|
+
continue;
|
|
682
|
+
upstream.add(provider);
|
|
683
|
+
upstreamQueue.push(provider);
|
|
619
684
|
}
|
|
620
685
|
}
|
|
621
686
|
for (const randomId of upstream) {
|
|
@@ -627,18 +692,17 @@ function addRandomizeClosureToBody(bodyNodeIds, forEachEndId, edges, nodes) {
|
|
|
627
692
|
let escapesLoop = false;
|
|
628
693
|
while (chainQueue.length > 0) {
|
|
629
694
|
const id = chainQueue.pop();
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
const consumers =
|
|
634
|
-
|
|
635
|
-
|
|
695
|
+
// The body and this loop's results are where a per-iteration draw belongs.
|
|
696
|
+
if (bodyNodeIds.has(id) || id === forEachEndId || chain.has(id))
|
|
697
|
+
continue;
|
|
698
|
+
const consumers = graph.dataConsumers.get(id) ?? [];
|
|
699
|
+
// Another loop, or a dead end outside the loop: the draw must stay shared.
|
|
700
|
+
if (isLoopNode(id) || consumers.length === 0) {
|
|
701
|
+
escapesLoop = true;
|
|
636
702
|
break;
|
|
637
703
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
chainQueue.push(consumer);
|
|
641
|
-
}
|
|
704
|
+
chain.add(id);
|
|
705
|
+
chainQueue.push(...consumers);
|
|
642
706
|
}
|
|
643
707
|
if (!escapesLoop)
|
|
644
708
|
for (const id of chain)
|
|
@@ -850,13 +914,14 @@ function convertWorkflowEditorToFlow(params) {
|
|
|
850
914
|
const forEachBodyEditorNodeIds = new Map();
|
|
851
915
|
const forEachLoopFlowIds = new Map();
|
|
852
916
|
const forEachFlowItemRefs = new Map();
|
|
917
|
+
const loopGraph = buildLoopGraph(nodes, edges);
|
|
853
918
|
for (const node of sortedNodes) {
|
|
854
919
|
if (node.type !== 'forEach')
|
|
855
920
|
continue;
|
|
856
921
|
const endNode = nodes.find((n) => n.type === 'forEachEnd' && n.data.parentNodeId === node.id);
|
|
857
922
|
if (!endNode)
|
|
858
923
|
continue;
|
|
859
|
-
forEachBodyEditorNodeIds.set(node.id, getForEachBodyEditorNodeIds(node.id, endNode.id, edges,
|
|
924
|
+
forEachBodyEditorNodeIds.set(node.id, getForEachBodyEditorNodeIds(node.id, endNode.id, edges, loopGraph));
|
|
860
925
|
forEachLoopFlowIds.set(node.id, []);
|
|
861
926
|
}
|
|
862
927
|
// Pre-populate video firstFrame/lastFrame transform IDs for input nodes
|
|
@@ -1208,9 +1273,10 @@ function convertWorkflowEditorToFlow(params) {
|
|
|
1208
1273
|
const outHandle = targetNode.data.outputHandles?.find((h) => h.id === edge.targetHandle);
|
|
1209
1274
|
const outHandleName = outHandle?.name ?? 'output';
|
|
1210
1275
|
const targetFlowId = nodeIdToFlowIdMap.get(targetNode.id) ?? targetNode.id;
|
|
1276
|
+
const varName = `${targetFlowId}_${outHandleName}`;
|
|
1211
1277
|
flowInputs.push({
|
|
1212
|
-
name:
|
|
1213
|
-
type: getPromptBuilderInputType(targetNode, outHandle, outHandleName),
|
|
1278
|
+
name: varName,
|
|
1279
|
+
type: getPromptBuilderInputType(targetNode, outHandle, outHandleName, value, varName),
|
|
1214
1280
|
ref,
|
|
1215
1281
|
});
|
|
1216
1282
|
}
|
|
@@ -1253,6 +1319,37 @@ function convertWorkflowEditorToFlow(params) {
|
|
|
1253
1319
|
inputs: flowInputs.length > 0 ? flowInputs : undefined,
|
|
1254
1320
|
});
|
|
1255
1321
|
// ------------------------------------
|
|
1322
|
+
// JoinTexts -> join string list into a single string
|
|
1323
|
+
// ------------------------------------
|
|
1324
|
+
}
|
|
1325
|
+
else if (node.type === 'joinTexts') {
|
|
1326
|
+
const flowInputs = [];
|
|
1327
|
+
const conditionalHandleId = getConditionalInputHandleId(node.id);
|
|
1328
|
+
let listSourceVarName;
|
|
1329
|
+
for (const edge of incomingEdges) {
|
|
1330
|
+
if (edge.sourceHandle === conditionalHandleId)
|
|
1331
|
+
continue;
|
|
1332
|
+
const targetNode = nodes.find((n) => n.id === edge.target);
|
|
1333
|
+
if (!targetNode)
|
|
1334
|
+
continue;
|
|
1335
|
+
const ref = getSourceRef(edge, ctx);
|
|
1336
|
+
if (!ref)
|
|
1337
|
+
continue;
|
|
1338
|
+
const varName = toCelIdentifier(targetNode.id);
|
|
1339
|
+
flowInputs.push({ name: varName, type: 'string_array', ref });
|
|
1340
|
+
listSourceVarName = varName;
|
|
1341
|
+
}
|
|
1342
|
+
flowInputs.push(...collectConditionalConnectionInputs(incomingEdges, node.id, ctx));
|
|
1343
|
+
const delim = node.data.joinDelimiter ?? ', ';
|
|
1344
|
+
flow.push({
|
|
1345
|
+
id: flowId,
|
|
1346
|
+
type: 'transform',
|
|
1347
|
+
logic: {
|
|
1348
|
+
transform: `${listSourceVarName ?? '[]'}.join(${formatTextAsCELString(delim)})`,
|
|
1349
|
+
},
|
|
1350
|
+
inputs: flowInputs.length > 0 ? flowInputs : undefined,
|
|
1351
|
+
});
|
|
1352
|
+
// ------------------------------------
|
|
1256
1353
|
// GroupItems -> array concatenation
|
|
1257
1354
|
// ------------------------------------
|
|
1258
1355
|
}
|
|
@@ -1293,7 +1390,9 @@ function convertWorkflowEditorToFlow(params) {
|
|
|
1293
1390
|
else {
|
|
1294
1391
|
const { variables, flowInputs } = collectConcatVarsAndInputs(itemsEdges, ctx, getOutputHandleVarName);
|
|
1295
1392
|
const count = clampUserSelectionCount(parseInt(String(sliceData.count ?? ''), 10) || 1);
|
|
1296
|
-
const message = sliceData.message
|
|
1393
|
+
const message = typeof sliceData.message === 'string' ?
|
|
1394
|
+
sliceData.message.trim() || DEFAULT_SLICE_ASSETS_CHOOSE_MESSAGE
|
|
1395
|
+
: DEFAULT_SLICE_ASSETS_CHOOSE_MESSAGE;
|
|
1297
1396
|
const arrayType = getUserSelectionArrayType(flowInputs);
|
|
1298
1397
|
// Single array-typed source can feed user-selection directly; scalars must be wrapped.
|
|
1299
1398
|
if (variables.length === 1 && variables[0]?.isArray && flowInputs[0]?.ref) {
|
|
@@ -1425,6 +1524,9 @@ function convertWorkflowEditorToFlow(params) {
|
|
|
1425
1524
|
fieldsUsed.add(condition.field);
|
|
1426
1525
|
}
|
|
1427
1526
|
}
|
|
1527
|
+
// CEL conditions need hyphen→underscore identifiers, but the shared input-name
|
|
1528
|
+
// map must keep the real workflow input keys (`text-1`, not `text_1`).
|
|
1529
|
+
const celFieldNameMap = new Map();
|
|
1428
1530
|
const edgesIntoIfElse = edges.filter((e) => e.target === node.id);
|
|
1429
1531
|
for (const fieldKey of fieldsUsed) {
|
|
1430
1532
|
const resolved = resolveIfElseConditionField(fieldKey, nodes);
|
|
@@ -1437,7 +1539,7 @@ function convertWorkflowEditorToFlow(params) {
|
|
|
1437
1539
|
?.data.outputHandles?.find((h) => h.id === resolved.targetHandleId)?.name ?? 'output'
|
|
1438
1540
|
: 'output';
|
|
1439
1541
|
const celVarName = getOutputHandleVarName(fieldNodeId, outputHandleName);
|
|
1440
|
-
|
|
1542
|
+
celFieldNameMap.set(fieldKey, celVarName);
|
|
1441
1543
|
const edgeToIfElse = edgesIntoIfElse
|
|
1442
1544
|
.filter((e) => e.source === fieldNodeId)
|
|
1443
1545
|
.find((e) => {
|
|
@@ -1471,7 +1573,7 @@ function convertWorkflowEditorToFlow(params) {
|
|
|
1471
1573
|
}
|
|
1472
1574
|
const cases = [];
|
|
1473
1575
|
for (let i = 0; i < conditionBlocks.length; i++) {
|
|
1474
|
-
const condition = conditionBlockToCEL(conditionBlocks[i],
|
|
1576
|
+
const condition = conditionBlockToCEL(conditionBlocks[i], celFieldNameMap);
|
|
1475
1577
|
if (condition)
|
|
1476
1578
|
cases.push({ condition, value: String(i + 2) });
|
|
1477
1579
|
}
|
|
@@ -1578,7 +1680,7 @@ function getSliceAssetsMode(data) {
|
|
|
1578
1680
|
return data.isRandomize ? 'randomize' : 'default';
|
|
1579
1681
|
}
|
|
1580
1682
|
function clampUserSelectionCount(count) {
|
|
1581
|
-
return Math.min(
|
|
1683
|
+
return Math.min(user_selection_limits_1.USER_SELECTION_MAX_CANDIDATES, Math.max(user_selection_limits_1.USER_SELECTION_MIN_SELECT, count));
|
|
1582
1684
|
}
|
|
1583
1685
|
function getUserSelectionArrayType(flowInputs) {
|
|
1584
1686
|
const hasFile = flowInputs.some((input) => input.type === 'file' || input.type === 'file_array');
|