@particle-academy/fancy-flow 0.13.0 → 0.14.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/dist/index.cjs CHANGED
@@ -222,7 +222,7 @@ function getWorkflowResolver() {
222
222
  return workflowResolver;
223
223
  }
224
224
 
225
- // src/registry/llm-branch.ts
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 llmBranchExecutor = async (ctx) => {
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("llm_branch has no routes configured");
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: `llm_branch: model returned "${port || "(nothing)"}", which is not a declared route. Routing to "${safe}".`
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;
@@ -700,10 +700,13 @@ var KINDS = [
700
700
  description: "Streaming adds a second port so a parent can show progress instead of a spinner."
701
701
  },
702
702
  {
703
- type: "json",
703
+ type: "keyvalue",
704
704
  key: "inputs",
705
705
  label: "Input mapping",
706
- description: "Entry-point inputs for the child run. Omit to pass this node's inputs straight through."
706
+ description: "Values handed to the child's entry points. Omit to pass this node's inputs straight through.",
707
+ keyLabel: "Name",
708
+ valueLabel: "Value",
709
+ addLabel: "Add input"
707
710
  },
708
711
  {
709
712
  type: "number",
@@ -727,7 +730,57 @@ var KINDS = [
727
730
  inputs: [{ id: "in" }],
728
731
  outputs: [{ id: "true", label: "true" }, { id: "false", label: "false" }],
729
732
  configSchema: [
730
- { type: "expression", key: "condition", label: "Condition", example: "{{ $json.active }}", required: true }
733
+ {
734
+ type: "select",
735
+ key: "match",
736
+ label: "Match",
737
+ default: "all",
738
+ options: [
739
+ { value: "all", label: "All conditions (AND)" },
740
+ { value: "any", label: "Any condition (OR)" }
741
+ ]
742
+ },
743
+ {
744
+ type: "repeater",
745
+ key: "conditions",
746
+ label: "Conditions",
747
+ description: "Routes to `true` when these match, otherwise `false`.",
748
+ titleKey: "left",
749
+ addLabel: "Add condition",
750
+ minItems: 1,
751
+ fields: [
752
+ { type: "expression", key: "left", label: "Value", example: "{{ $json.status }}", required: true },
753
+ {
754
+ type: "select",
755
+ key: "operator",
756
+ label: "Is",
757
+ default: "eq",
758
+ options: [
759
+ { value: "eq", label: "equal to" },
760
+ { value: "neq", label: "not equal to" },
761
+ { value: "contains", label: "contains" },
762
+ { value: "not_contains", label: "does not contain" },
763
+ { value: "gt", label: "greater than" },
764
+ { value: "gte", label: "greater than or equal to" },
765
+ { value: "lt", label: "less than" },
766
+ { value: "lte", label: "less than or equal to" },
767
+ { value: "truthy", label: "true" },
768
+ { value: "falsy", label: "false" },
769
+ { value: "empty", label: "empty" },
770
+ { value: "not_empty", label: "not empty" }
771
+ ]
772
+ },
773
+ { type: "text", key: "right", label: "Compared to", placeholder: "active" }
774
+ ],
775
+ default: [{ left: "", operator: "eq", right: "" }]
776
+ },
777
+ {
778
+ type: "expression",
779
+ key: "condition",
780
+ label: "Raw expression (advanced)",
781
+ example: "{{ $json.active && $json.score > 10 }}",
782
+ description: "Escape hatch for logic the builder can't express. Overrides the conditions above when set."
783
+ }
731
784
  ]
732
785
  },
733
786
  {
@@ -817,12 +870,35 @@ var KINDS = [
817
870
  description: "Reshape data with an expression.",
818
871
  icon: "\u0192",
819
872
  configSchema: [
873
+ {
874
+ type: "select",
875
+ key: "mode",
876
+ label: "Build the output",
877
+ default: "fields",
878
+ options: [
879
+ { value: "fields", label: "Field by field" },
880
+ { value: "expression", label: "One expression" }
881
+ ]
882
+ },
883
+ {
884
+ type: "repeater",
885
+ key: "fields",
886
+ label: "Output fields",
887
+ description: "Each row becomes a key on the result.",
888
+ titleKey: "key",
889
+ addLabel: "Add field",
890
+ fields: [
891
+ { type: "text", key: "key", label: "Key", required: true, placeholder: "name" },
892
+ { type: "expression", key: "value", label: "Value", example: "{{ $json.first }}", required: true }
893
+ ],
894
+ default: [{ key: "", value: "" }]
895
+ },
820
896
  {
821
897
  type: "expression",
822
898
  key: "expression",
823
- label: "Expression",
899
+ label: "Expression (advanced)",
824
900
  example: "{{ { id: $json.id, name: $json.first + ' ' + $json.last } }}",
825
- required: true
901
+ description: "Used when the mode above is set to one expression."
826
902
  }
827
903
  ]
828
904
  },
@@ -872,7 +948,15 @@ var KINDS = [
872
948
  },
873
949
  { type: "text", key: "table", label: "Table / collection", required: true },
874
950
  { type: "text", key: "key", label: "Key" },
875
- { type: "json", key: "where", label: "Where (JSON)", description: "For query/list operations." },
951
+ {
952
+ type: "keyvalue",
953
+ key: "where",
954
+ label: "Where",
955
+ description: "Field/value pairs to match. For query and list operations.",
956
+ keyLabel: "Field",
957
+ valueLabel: "Equals",
958
+ addLabel: "Add filter"
959
+ },
876
960
  { type: "expression", key: "value", label: "Value (set only)", example: "{{ $json }}" },
877
961
  { type: "credential", key: "store", label: "Data store", credentialType: "data_store" }
878
962
  ]
@@ -914,13 +998,32 @@ var KINDS = [
914
998
  { type: "expression", key: "prompt", label: "User prompt", example: "{{ $json.question }}", required: true },
915
999
  { type: "number", key: "temperature", label: "Temperature", min: 0, max: 2, step: 0.1, default: 0.7 },
916
1000
  { type: "number", key: "max_tokens", label: "Max tokens", min: 1, max: 8192, default: 1024 },
917
- { type: "json", key: "tools", label: "Tools (JSON)", description: "Optional Anthropic-style tool definitions." },
1001
+ {
1002
+ type: "repeater",
1003
+ key: "tools",
1004
+ label: "Tools",
1005
+ description: "Tools the model may call.",
1006
+ titleKey: "name",
1007
+ addLabel: "Add tool",
1008
+ fields: [
1009
+ { type: "text", key: "name", label: "Name", required: true, placeholder: "search_index" },
1010
+ { type: "text", key: "description", label: "When to use it" },
1011
+ {
1012
+ type: "json",
1013
+ key: "input_schema",
1014
+ label: "Input schema",
1015
+ description: "JSON Schema for the tool's arguments."
1016
+ }
1017
+ ]
1018
+ },
918
1019
  { type: "credential", key: "credential", label: "API credential", credentialType: "llm_credential" }
919
1020
  ]
920
1021
  },
921
1022
  {
922
- name: "@particle-academy/llm_branch",
923
- aliases: ["llm_branch", "@fancy/llm_branch"],
1023
+ name: "@particle-academy/llm_router",
1024
+ // Every id this node has ever shipped under keeps resolving — MOIC's saved
1025
+ // flows carry the bare `llm_branch`.
1026
+ aliases: ["llm_router", "llm_branch", "@fancy/llm_branch", "@fancy/llm_router"],
924
1027
  category: "ai",
925
1028
  label: "LLM Router",
926
1029
  description: "Let a model choose which route the flow takes.",
@@ -932,7 +1035,7 @@ var KINDS = [
932
1035
  // A shuttle, not an engine: it carries the routes out to whatever LLM
933
1036
  // client the host registered and carries the choice back. No provider SDK
934
1037
  // reaches core, so this stays a builtin without adding a dependency.
935
- executor: llmBranchExecutor,
1038
+ executor: llmRouterExecutor,
936
1039
  configSchema: [
937
1040
  {
938
1041
  type: "textarea",
@@ -1029,7 +1132,17 @@ var KINDS = [
1029
1132
  configSchema: [
1030
1133
  ...HTTP_METHODS,
1031
1134
  { type: "text", key: "url", label: "URL", placeholder: "https://api.example.com/...", required: true },
1032
- { type: "json", key: "headers", label: "Headers", default: { "content-type": "application/json" } },
1135
+ {
1136
+ type: "keyvalue",
1137
+ key: "headers",
1138
+ label: "Headers",
1139
+ keyLabel: "Header",
1140
+ valueLabel: "Value",
1141
+ keyPlaceholder: "content-type",
1142
+ valuePlaceholder: "application/json",
1143
+ addLabel: "Add header",
1144
+ default: { "content-type": "application/json" }
1145
+ },
1033
1146
  { type: "json", key: "body", label: "Body" },
1034
1147
  { type: "credential", key: "auth", label: "Auth", credentialType: "api_credential" }
1035
1148
  ]
@@ -1043,7 +1156,14 @@ var KINDS = [
1043
1156
  icon: "\u2197",
1044
1157
  configSchema: [
1045
1158
  { type: "text", key: "url", label: "URL", required: true },
1046
- { type: "json", key: "headers", label: "Headers" },
1159
+ {
1160
+ type: "keyvalue",
1161
+ key: "headers",
1162
+ label: "Headers",
1163
+ keyLabel: "Header",
1164
+ valueLabel: "Value",
1165
+ addLabel: "Add header"
1166
+ },
1047
1167
  { type: "expression", key: "payload", label: "Payload", required: true, example: "{{ $json }}" }
1048
1168
  ]
1049
1169
  },