@particle-academy/fancy-flow 0.13.0 → 0.15.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/README.md +48 -0
- package/dist/{capabilities-D-V9Rxv1.d.cts → capabilities-BEbLqnJU.d.cts} +1 -1
- package/dist/{capabilities-B6r1Snfj.d.ts → capabilities-CI3gw0C7.d.ts} +1 -1
- package/dist/chunk-UEOE6B52.js +39 -0
- package/dist/chunk-UEOE6B52.js.map +1 -0
- package/dist/{chunk-WEZJFMLV.js → chunk-ZQBWPYTI.js} +142 -19
- package/dist/chunk-ZQBWPYTI.js.map +1 -0
- package/dist/engine.cjs +299 -0
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.cts +190 -27
- package/dist/engine.d.ts +190 -27
- package/dist/engine.js +258 -2
- package/dist/engine.js.map +1 -1
- package/dist/index.cjs +181 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/llm/vercel-ai.d.cts +2 -2
- package/dist/llm/vercel-ai.d.ts +2 -2
- package/dist/pause-9iT4tCEV.d.cts +96 -0
- package/dist/pause-9iT4tCEV.d.ts +96 -0
- package/dist/registry/index.d.cts +8 -7
- package/dist/registry/index.d.ts +8 -7
- package/dist/registry.cjs +183 -17
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +2 -1
- package/dist/run-flow-ChBi6q-W.d.ts +35 -0
- package/dist/run-flow-VKm57Hcj.d.cts +35 -0
- package/dist/runtime/index.d.cts +3 -3
- package/dist/runtime/index.d.ts +3 -3
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/{types-H60tQAxr.d.cts → types-D_jOR3Z2.d.cts} +12 -1
- package/dist/{types-CnnKPnpt.d.ts → types-NerkPtHS.d.ts} +12 -1
- package/dist/{types-fc0fFKkf.d.cts → types-sOmpCitB.d.cts} +1 -1
- package/dist/{types-fc0fFKkf.d.ts → types-sOmpCitB.d.ts} +1 -1
- package/dist/ux.d.cts +3 -2
- package/dist/ux.d.ts +3 -2
- package/package.json +1 -1
- package/dist/chunk-WEZJFMLV.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -222,7 +222,7 @@ function getWorkflowResolver() {
|
|
|
222
222
|
return workflowResolver;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
// src/registry/llm-
|
|
225
|
+
// src/registry/llm-router.ts
|
|
226
226
|
function declaredRoutes(config) {
|
|
227
227
|
const raw = config.routes;
|
|
228
228
|
if (!Array.isArray(raw)) return [];
|
|
@@ -232,11 +232,11 @@ function resolveFallbackPort(routes, fallbackEnabled) {
|
|
|
232
232
|
if (fallbackEnabled) return "fallback";
|
|
233
233
|
return routes[0]?.port ?? "out";
|
|
234
234
|
}
|
|
235
|
-
var
|
|
235
|
+
var llmRouterExecutor = async (ctx) => {
|
|
236
236
|
const config = ctx.node.data?.config ?? {};
|
|
237
237
|
const routes = declaredRoutes(config);
|
|
238
238
|
if (routes.length === 0) {
|
|
239
|
-
ctx.abort("
|
|
239
|
+
ctx.abort("llm_router has no routes configured");
|
|
240
240
|
}
|
|
241
241
|
const client = getLlmClient();
|
|
242
242
|
{
|
|
@@ -262,7 +262,7 @@ var llmBranchExecutor = async (ctx) => {
|
|
|
262
262
|
type: "log",
|
|
263
263
|
nodeId: ctx.node.id,
|
|
264
264
|
level: "warn",
|
|
265
|
-
message: `
|
|
265
|
+
message: `llm_router: model returned "${port || "(nothing)"}", which is not a declared route. Routing to "${safe}".`
|
|
266
266
|
});
|
|
267
267
|
reason = reason ?? `unrecognised route "${port}"`;
|
|
268
268
|
port = safe;
|
|
@@ -602,6 +602,7 @@ var KINDS = [
|
|
|
602
602
|
{
|
|
603
603
|
name: "@particle-academy/user_input",
|
|
604
604
|
aliases: ["user_input", "@fancy/user_input"],
|
|
605
|
+
pausesForHuman: "input",
|
|
605
606
|
category: "human",
|
|
606
607
|
label: "User Input",
|
|
607
608
|
description: "Pause the flow until the user submits the configured form.",
|
|
@@ -643,6 +644,7 @@ var KINDS = [
|
|
|
643
644
|
{
|
|
644
645
|
name: "@particle-academy/rich_user_input",
|
|
645
646
|
aliases: ["rich_user_input", "@fancy/rich_user_input"],
|
|
647
|
+
pausesForHuman: "input",
|
|
646
648
|
category: "human",
|
|
647
649
|
label: "Rich User Input",
|
|
648
650
|
description: "Pause the flow on a fully authored page \u2014 content, required reading, multi-section forms.",
|
|
@@ -700,10 +702,13 @@ var KINDS = [
|
|
|
700
702
|
description: "Streaming adds a second port so a parent can show progress instead of a spinner."
|
|
701
703
|
},
|
|
702
704
|
{
|
|
703
|
-
type: "
|
|
705
|
+
type: "keyvalue",
|
|
704
706
|
key: "inputs",
|
|
705
707
|
label: "Input mapping",
|
|
706
|
-
description: "
|
|
708
|
+
description: "Values handed to the child's entry points. Omit to pass this node's inputs straight through.",
|
|
709
|
+
keyLabel: "Name",
|
|
710
|
+
valueLabel: "Value",
|
|
711
|
+
addLabel: "Add input"
|
|
707
712
|
},
|
|
708
713
|
{
|
|
709
714
|
type: "number",
|
|
@@ -727,7 +732,57 @@ var KINDS = [
|
|
|
727
732
|
inputs: [{ id: "in" }],
|
|
728
733
|
outputs: [{ id: "true", label: "true" }, { id: "false", label: "false" }],
|
|
729
734
|
configSchema: [
|
|
730
|
-
{
|
|
735
|
+
{
|
|
736
|
+
type: "select",
|
|
737
|
+
key: "match",
|
|
738
|
+
label: "Match",
|
|
739
|
+
default: "all",
|
|
740
|
+
options: [
|
|
741
|
+
{ value: "all", label: "All conditions (AND)" },
|
|
742
|
+
{ value: "any", label: "Any condition (OR)" }
|
|
743
|
+
]
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
type: "repeater",
|
|
747
|
+
key: "conditions",
|
|
748
|
+
label: "Conditions",
|
|
749
|
+
description: "Routes to `true` when these match, otherwise `false`.",
|
|
750
|
+
titleKey: "left",
|
|
751
|
+
addLabel: "Add condition",
|
|
752
|
+
minItems: 1,
|
|
753
|
+
fields: [
|
|
754
|
+
{ type: "expression", key: "left", label: "Value", example: "{{ $json.status }}", required: true },
|
|
755
|
+
{
|
|
756
|
+
type: "select",
|
|
757
|
+
key: "operator",
|
|
758
|
+
label: "Is",
|
|
759
|
+
default: "eq",
|
|
760
|
+
options: [
|
|
761
|
+
{ value: "eq", label: "equal to" },
|
|
762
|
+
{ value: "neq", label: "not equal to" },
|
|
763
|
+
{ value: "contains", label: "contains" },
|
|
764
|
+
{ value: "not_contains", label: "does not contain" },
|
|
765
|
+
{ value: "gt", label: "greater than" },
|
|
766
|
+
{ value: "gte", label: "greater than or equal to" },
|
|
767
|
+
{ value: "lt", label: "less than" },
|
|
768
|
+
{ value: "lte", label: "less than or equal to" },
|
|
769
|
+
{ value: "truthy", label: "true" },
|
|
770
|
+
{ value: "falsy", label: "false" },
|
|
771
|
+
{ value: "empty", label: "empty" },
|
|
772
|
+
{ value: "not_empty", label: "not empty" }
|
|
773
|
+
]
|
|
774
|
+
},
|
|
775
|
+
{ type: "text", key: "right", label: "Compared to", placeholder: "active" }
|
|
776
|
+
],
|
|
777
|
+
default: [{ left: "", operator: "eq", right: "" }]
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
type: "expression",
|
|
781
|
+
key: "condition",
|
|
782
|
+
label: "Raw expression (advanced)",
|
|
783
|
+
example: "{{ $json.active && $json.score > 10 }}",
|
|
784
|
+
description: "Escape hatch for logic the builder can't express. Overrides the conditions above when set."
|
|
785
|
+
}
|
|
731
786
|
]
|
|
732
787
|
},
|
|
733
788
|
{
|
|
@@ -817,12 +872,35 @@ var KINDS = [
|
|
|
817
872
|
description: "Reshape data with an expression.",
|
|
818
873
|
icon: "\u0192",
|
|
819
874
|
configSchema: [
|
|
875
|
+
{
|
|
876
|
+
type: "select",
|
|
877
|
+
key: "mode",
|
|
878
|
+
label: "Build the output",
|
|
879
|
+
default: "fields",
|
|
880
|
+
options: [
|
|
881
|
+
{ value: "fields", label: "Field by field" },
|
|
882
|
+
{ value: "expression", label: "One expression" }
|
|
883
|
+
]
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
type: "repeater",
|
|
887
|
+
key: "fields",
|
|
888
|
+
label: "Output fields",
|
|
889
|
+
description: "Each row becomes a key on the result.",
|
|
890
|
+
titleKey: "key",
|
|
891
|
+
addLabel: "Add field",
|
|
892
|
+
fields: [
|
|
893
|
+
{ type: "text", key: "key", label: "Key", required: true, placeholder: "name" },
|
|
894
|
+
{ type: "expression", key: "value", label: "Value", example: "{{ $json.first }}", required: true }
|
|
895
|
+
],
|
|
896
|
+
default: [{ key: "", value: "" }]
|
|
897
|
+
},
|
|
820
898
|
{
|
|
821
899
|
type: "expression",
|
|
822
900
|
key: "expression",
|
|
823
|
-
label: "Expression",
|
|
901
|
+
label: "Expression (advanced)",
|
|
824
902
|
example: "{{ { id: $json.id, name: $json.first + ' ' + $json.last } }}",
|
|
825
|
-
|
|
903
|
+
description: "Used when the mode above is set to one expression."
|
|
826
904
|
}
|
|
827
905
|
]
|
|
828
906
|
},
|
|
@@ -872,7 +950,15 @@ var KINDS = [
|
|
|
872
950
|
},
|
|
873
951
|
{ type: "text", key: "table", label: "Table / collection", required: true },
|
|
874
952
|
{ type: "text", key: "key", label: "Key" },
|
|
875
|
-
{
|
|
953
|
+
{
|
|
954
|
+
type: "keyvalue",
|
|
955
|
+
key: "where",
|
|
956
|
+
label: "Where",
|
|
957
|
+
description: "Field/value pairs to match. For query and list operations.",
|
|
958
|
+
keyLabel: "Field",
|
|
959
|
+
valueLabel: "Equals",
|
|
960
|
+
addLabel: "Add filter"
|
|
961
|
+
},
|
|
876
962
|
{ type: "expression", key: "value", label: "Value (set only)", example: "{{ $json }}" },
|
|
877
963
|
{ type: "credential", key: "store", label: "Data store", credentialType: "data_store" }
|
|
878
964
|
]
|
|
@@ -914,13 +1000,32 @@ var KINDS = [
|
|
|
914
1000
|
{ type: "expression", key: "prompt", label: "User prompt", example: "{{ $json.question }}", required: true },
|
|
915
1001
|
{ type: "number", key: "temperature", label: "Temperature", min: 0, max: 2, step: 0.1, default: 0.7 },
|
|
916
1002
|
{ type: "number", key: "max_tokens", label: "Max tokens", min: 1, max: 8192, default: 1024 },
|
|
917
|
-
{
|
|
1003
|
+
{
|
|
1004
|
+
type: "repeater",
|
|
1005
|
+
key: "tools",
|
|
1006
|
+
label: "Tools",
|
|
1007
|
+
description: "Tools the model may call.",
|
|
1008
|
+
titleKey: "name",
|
|
1009
|
+
addLabel: "Add tool",
|
|
1010
|
+
fields: [
|
|
1011
|
+
{ type: "text", key: "name", label: "Name", required: true, placeholder: "search_index" },
|
|
1012
|
+
{ type: "text", key: "description", label: "When to use it" },
|
|
1013
|
+
{
|
|
1014
|
+
type: "json",
|
|
1015
|
+
key: "input_schema",
|
|
1016
|
+
label: "Input schema",
|
|
1017
|
+
description: "JSON Schema for the tool's arguments."
|
|
1018
|
+
}
|
|
1019
|
+
]
|
|
1020
|
+
},
|
|
918
1021
|
{ type: "credential", key: "credential", label: "API credential", credentialType: "llm_credential" }
|
|
919
1022
|
]
|
|
920
1023
|
},
|
|
921
1024
|
{
|
|
922
|
-
name: "@particle-academy/
|
|
923
|
-
|
|
1025
|
+
name: "@particle-academy/llm_router",
|
|
1026
|
+
// Every id this node has ever shipped under keeps resolving — MOIC's saved
|
|
1027
|
+
// flows carry the bare `llm_branch`.
|
|
1028
|
+
aliases: ["llm_router", "llm_branch", "@fancy/llm_branch", "@fancy/llm_router"],
|
|
924
1029
|
category: "ai",
|
|
925
1030
|
label: "LLM Router",
|
|
926
1031
|
description: "Let a model choose which route the flow takes.",
|
|
@@ -932,7 +1037,7 @@ var KINDS = [
|
|
|
932
1037
|
// A shuttle, not an engine: it carries the routes out to whatever LLM
|
|
933
1038
|
// client the host registered and carries the choice back. No provider SDK
|
|
934
1039
|
// reaches core, so this stays a builtin without adding a dependency.
|
|
935
|
-
executor:
|
|
1040
|
+
executor: llmRouterExecutor,
|
|
936
1041
|
configSchema: [
|
|
937
1042
|
{
|
|
938
1043
|
type: "textarea",
|
|
@@ -1029,7 +1134,17 @@ var KINDS = [
|
|
|
1029
1134
|
configSchema: [
|
|
1030
1135
|
...HTTP_METHODS,
|
|
1031
1136
|
{ type: "text", key: "url", label: "URL", placeholder: "https://api.example.com/...", required: true },
|
|
1032
|
-
{
|
|
1137
|
+
{
|
|
1138
|
+
type: "keyvalue",
|
|
1139
|
+
key: "headers",
|
|
1140
|
+
label: "Headers",
|
|
1141
|
+
keyLabel: "Header",
|
|
1142
|
+
valueLabel: "Value",
|
|
1143
|
+
keyPlaceholder: "content-type",
|
|
1144
|
+
valuePlaceholder: "application/json",
|
|
1145
|
+
addLabel: "Add header",
|
|
1146
|
+
default: { "content-type": "application/json" }
|
|
1147
|
+
},
|
|
1033
1148
|
{ type: "json", key: "body", label: "Body" },
|
|
1034
1149
|
{ type: "credential", key: "auth", label: "Auth", credentialType: "api_credential" }
|
|
1035
1150
|
]
|
|
@@ -1043,7 +1158,14 @@ var KINDS = [
|
|
|
1043
1158
|
icon: "\u2197",
|
|
1044
1159
|
configSchema: [
|
|
1045
1160
|
{ type: "text", key: "url", label: "URL", required: true },
|
|
1046
|
-
{
|
|
1161
|
+
{
|
|
1162
|
+
type: "keyvalue",
|
|
1163
|
+
key: "headers",
|
|
1164
|
+
label: "Headers",
|
|
1165
|
+
keyLabel: "Header",
|
|
1166
|
+
valueLabel: "Value",
|
|
1167
|
+
addLabel: "Add header"
|
|
1168
|
+
},
|
|
1047
1169
|
{ type: "expression", key: "payload", label: "Payload", required: true, example: "{{ $json }}" }
|
|
1048
1170
|
]
|
|
1049
1171
|
},
|
|
@@ -1051,6 +1173,7 @@ var KINDS = [
|
|
|
1051
1173
|
{
|
|
1052
1174
|
name: "@particle-academy/human_approval",
|
|
1053
1175
|
aliases: ["human_approval", "@fancy/human_approval"],
|
|
1176
|
+
pausesForHuman: "approval",
|
|
1054
1177
|
category: "human",
|
|
1055
1178
|
label: "Human Approval",
|
|
1056
1179
|
description: "Pause until a human approves or denies.",
|
|
@@ -10988,6 +11111,42 @@ function importWorkflow(schema, options = {}) {
|
|
|
10988
11111
|
function workflowToBlob(schema) {
|
|
10989
11112
|
return new Blob([JSON.stringify(schema, null, 2)], { type: "application/json" });
|
|
10990
11113
|
}
|
|
11114
|
+
|
|
11115
|
+
// src/registry/pause.ts
|
|
11116
|
+
var PAUSE_PREFIX = "fancy-flow:pause:";
|
|
11117
|
+
var LEGACY_PAUSE_PREFIXES = [
|
|
11118
|
+
["awaiting-approval:", "approval"],
|
|
11119
|
+
["awaiting-input:", "input"]
|
|
11120
|
+
];
|
|
11121
|
+
function encodePause(signal) {
|
|
11122
|
+
const { nodeId, awaiting, detail } = signal;
|
|
11123
|
+
return PAUSE_PREFIX + JSON.stringify(detail === void 0 ? { nodeId, awaiting } : { nodeId, awaiting, detail });
|
|
11124
|
+
}
|
|
11125
|
+
function decodePause(reason) {
|
|
11126
|
+
if (typeof reason !== "string") return null;
|
|
11127
|
+
if (reason.startsWith(PAUSE_PREFIX)) {
|
|
11128
|
+
const body = reason.slice(PAUSE_PREFIX.length);
|
|
11129
|
+
try {
|
|
11130
|
+
const parsed = JSON.parse(body);
|
|
11131
|
+
if (typeof parsed?.nodeId !== "string" || typeof parsed?.awaiting !== "string") return null;
|
|
11132
|
+
return "detail" in parsed ? { nodeId: parsed.nodeId, awaiting: parsed.awaiting, detail: parsed.detail } : { nodeId: parsed.nodeId, awaiting: parsed.awaiting };
|
|
11133
|
+
} catch {
|
|
11134
|
+
return null;
|
|
11135
|
+
}
|
|
11136
|
+
}
|
|
11137
|
+
for (const [prefix, awaiting] of LEGACY_PAUSE_PREFIXES) {
|
|
11138
|
+
if (reason.startsWith(prefix)) {
|
|
11139
|
+
return { nodeId: reason.slice(prefix.length), awaiting };
|
|
11140
|
+
}
|
|
11141
|
+
}
|
|
11142
|
+
return null;
|
|
11143
|
+
}
|
|
11144
|
+
function isPause(reason) {
|
|
11145
|
+
return decodePause(reason) !== null;
|
|
11146
|
+
}
|
|
11147
|
+
function pauseForHuman(ctx, awaiting, detail) {
|
|
11148
|
+
return ctx.abort(encodePause({ nodeId: ctx.node.id, awaiting, detail }));
|
|
11149
|
+
}
|
|
10991
11150
|
function RegistryNodeInner(props) {
|
|
10992
11151
|
const kindName = props.data.kind ?? props.type;
|
|
10993
11152
|
const kind = ReactExports.useMemo(() => getNodeKind(kindName), [kindName]);
|
|
@@ -11639,12 +11798,14 @@ exports.FlowCanvas = FlowCanvas;
|
|
|
11639
11798
|
exports.FlowEditor = FlowEditor;
|
|
11640
11799
|
exports.FlowRunControls = FlowRunControls;
|
|
11641
11800
|
exports.FlowRunFeed = FlowRunFeed;
|
|
11801
|
+
exports.LEGACY_PAUSE_PREFIXES = LEGACY_PAUSE_PREFIXES;
|
|
11642
11802
|
exports.NodeConfigPanel = NodeConfigPanel;
|
|
11643
11803
|
exports.NodePalette = NodePalette;
|
|
11644
11804
|
exports.NodePort = NodePort;
|
|
11645
11805
|
exports.NodeShell = NodeShell;
|
|
11646
11806
|
exports.NoteNode = NoteNode;
|
|
11647
11807
|
exports.OutputNode = OutputNode2;
|
|
11808
|
+
exports.PAUSE_PREFIX = PAUSE_PREFIX;
|
|
11648
11809
|
exports.RegistryNode = RegistryNode;
|
|
11649
11810
|
exports.RichInputPreview = RichInputPreview;
|
|
11650
11811
|
exports.SubgraphNode = SubgraphNode;
|
|
@@ -11654,18 +11815,22 @@ exports.WORKFLOW_SCHEMA_VERSION = WORKFLOW_SCHEMA_VERSION;
|
|
|
11654
11815
|
exports.applyStatusesToNodes = applyStatusesToNodes;
|
|
11655
11816
|
exports.buildNodeTypes = buildNodeTypes;
|
|
11656
11817
|
exports.categoryAccent = categoryAccent;
|
|
11818
|
+
exports.decodePause = decodePause;
|
|
11657
11819
|
exports.defaultConfigFor = defaultConfigFor;
|
|
11658
11820
|
exports.defaultNodeTypes = defaultNodeTypes;
|
|
11659
11821
|
exports.defineNode = defineNode;
|
|
11822
|
+
exports.encodePause = encodePause;
|
|
11660
11823
|
exports.exportWorkflow = exportWorkflow;
|
|
11661
11824
|
exports.getNodeKind = getNodeKind;
|
|
11662
11825
|
exports.getRichInputAdapter = getRichInputAdapter;
|
|
11663
11826
|
exports.importWorkflow = importWorkflow;
|
|
11827
|
+
exports.isPause = isPause;
|
|
11664
11828
|
exports.isRichInputEnabled = isRichInputEnabled;
|
|
11665
11829
|
exports.listNodeKinds = listNodeKinds;
|
|
11666
11830
|
exports.onNodeKindsChanged = onNodeKindsChanged;
|
|
11667
11831
|
exports.onRichInputAdapterChanged = onRichInputAdapterChanged;
|
|
11668
11832
|
exports.paletteDropHandlers = paletteDropHandlers;
|
|
11833
|
+
exports.pauseForHuman = pauseForHuman;
|
|
11669
11834
|
exports.registerBuiltinKinds = registerBuiltinKinds;
|
|
11670
11835
|
exports.registerNodeKind = registerNodeKind;
|
|
11671
11836
|
exports.registerRichInputAdapter = registerRichInputAdapter;
|