@flowgram.ai/runtime-js 0.2.27 → 0.2.29

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.js CHANGED
@@ -236,13 +236,16 @@ var FlowGramNode = /* @__PURE__ */ ((FlowGramNode22) => {
236
236
  FlowGramNode22["Start"] = "start";
237
237
  FlowGramNode22["End"] = "end";
238
238
  FlowGramNode22["LLM"] = "llm";
239
- FlowGramNode22["code"] = "code";
239
+ FlowGramNode22["Code"] = "code";
240
240
  FlowGramNode22["Condition"] = "condition";
241
241
  FlowGramNode22["Loop"] = "loop";
242
242
  FlowGramNode22["Comment"] = "comment";
243
243
  FlowGramNode22["Group"] = "group";
244
244
  FlowGramNode22["BlockStart"] = "block-start";
245
245
  FlowGramNode22["BlockEnd"] = "block-end";
246
+ FlowGramNode22["HTTP"] = "http";
247
+ FlowGramNode22["Break"] = "break";
248
+ FlowGramNode22["Continue"] = "continue";
246
249
  return FlowGramNode22;
247
250
  })(FlowGramNode || {});
248
251
  var ConditionOperation = /* @__PURE__ */ ((ConditionOperation2) => {
@@ -262,6 +265,15 @@ var ConditionOperation = /* @__PURE__ */ ((ConditionOperation2) => {
262
265
  ConditionOperation2["IS_FALSE"] = "is_false";
263
266
  return ConditionOperation2;
264
267
  })(ConditionOperation || {});
268
+ var HTTPBodyType = /* @__PURE__ */ ((HTTPBodyType2) => {
269
+ HTTPBodyType2["None"] = "none";
270
+ HTTPBodyType2["FormData"] = "form-data";
271
+ HTTPBodyType2["XWwwFormUrlencoded"] = "x-www-form-urlencoded";
272
+ HTTPBodyType2["RawText"] = "raw-text";
273
+ HTTPBodyType2["JSON"] = "JSON";
274
+ HTTPBodyType2["Binary"] = "binary";
275
+ return HTTPBodyType2;
276
+ })(HTTPBodyType || {});
265
277
  var IEngine = Symbol.for("Engine");
266
278
  var IExecutor = Symbol.for("Executor");
267
279
  var WorkflowStatus = /* @__PURE__ */ ((WorkflowStatus2) => {
@@ -640,6 +652,12 @@ var LoopExecutor = class {
640
652
  } catch (e) {
641
653
  throw new Error(`Loop block execute error`);
642
654
  }
655
+ if (this.isBreak(subContext)) {
656
+ break;
657
+ }
658
+ if (this.isContinue(subContext)) {
659
+ continue;
660
+ }
643
661
  const blockOutput = this.getBlockOutput(context, subContext);
644
662
  blockOutputs.push(blockOutput);
645
663
  }
@@ -723,6 +741,12 @@ var LoopExecutor = class {
723
741
  const loopOutputsDeclare = loopNodeData.loopOutputs ?? {};
724
742
  return loopOutputsDeclare;
725
743
  }
744
+ isBreak(subContext) {
745
+ return subContext.cache.get("loop-break") === true;
746
+ }
747
+ isContinue(subContext) {
748
+ return subContext.cache.get("loop-continue") === true;
749
+ }
726
750
  };
727
751
 
728
752
  // src/nodes/llm/index.ts
@@ -792,6 +816,227 @@ var LLMExecutor = class {
792
816
  }
793
817
  };
794
818
 
819
+ // src/nodes/http/index.ts
820
+ var HTTPExecutor = class {
821
+ constructor() {
822
+ this.type = FlowGramNode.HTTP;
823
+ }
824
+ async execute(context) {
825
+ const inputs = this.parseInputs(context);
826
+ const response = await this.request(inputs);
827
+ const responseHeaders = {};
828
+ response.headers.forEach((value, key) => {
829
+ responseHeaders[key] = value;
830
+ });
831
+ const responseBody = await response.text();
832
+ return {
833
+ outputs: {
834
+ headers: responseHeaders,
835
+ statusCode: response.status,
836
+ body: responseBody
837
+ }
838
+ };
839
+ }
840
+ async request(inputs) {
841
+ const { method, url, headers, params, bodyType, body, retryTimes, timeout } = inputs;
842
+ const urlWithParams = this.buildUrlWithParams(url, params);
843
+ const requestOptions = {
844
+ method,
845
+ headers: this.prepareHeaders(headers, bodyType),
846
+ signal: AbortSignal.timeout(timeout)
847
+ };
848
+ if (method !== "GET" && method !== "HEAD" && body) {
849
+ requestOptions.body = this.prepareBody(body, bodyType);
850
+ }
851
+ let lastError = null;
852
+ for (let attempt = 0; attempt <= retryTimes; attempt++) {
853
+ try {
854
+ const response = await fetch(urlWithParams, requestOptions);
855
+ return response;
856
+ } catch (error) {
857
+ lastError = error;
858
+ if (attempt < retryTimes) {
859
+ await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
860
+ }
861
+ }
862
+ }
863
+ throw lastError || new Error("HTTP request failed after all retry attempts");
864
+ }
865
+ parseInputs(context) {
866
+ const httpNode = context.node;
867
+ const method = httpNode.data.api.method;
868
+ const urlVariable = context.runtime.state.parseTemplate(httpNode.data.api.url);
869
+ if (!urlVariable) {
870
+ throw new Error("HTTP url is required");
871
+ }
872
+ const url = urlVariable.value;
873
+ const headers = context.runtime.state.parseInputs({
874
+ values: httpNode.data.headersValues,
875
+ declare: httpNode.data.headers
876
+ });
877
+ const params = context.runtime.state.parseInputs({
878
+ values: httpNode.data.paramsValues,
879
+ declare: httpNode.data.params
880
+ });
881
+ const body = this.parseBody(context);
882
+ const retryTimes = httpNode.data.timeout.retryTimes;
883
+ const timeout = httpNode.data.timeout.timeout;
884
+ const inputs = {
885
+ method,
886
+ url,
887
+ headers,
888
+ params,
889
+ bodyType: body.bodyType,
890
+ body: body.body,
891
+ retryTimes,
892
+ timeout
893
+ };
894
+ context.snapshot.update({
895
+ inputs: JSON.parse(JSON.stringify(inputs))
896
+ });
897
+ return inputs;
898
+ }
899
+ parseBody(context) {
900
+ const httpNode = context.node;
901
+ const bodyType = httpNode.data.body.bodyType;
902
+ if (bodyType === HTTPBodyType.None) {
903
+ return {
904
+ bodyType,
905
+ body: ""
906
+ };
907
+ }
908
+ if (bodyType === HTTPBodyType.JSON) {
909
+ if (!httpNode.data.body.json) {
910
+ throw new Error("HTTP json body is required");
911
+ }
912
+ const jsonVariable = context.runtime.state.parseTemplate(httpNode.data.body.json);
913
+ if (!jsonVariable) {
914
+ throw new Error("HTTP json body is required");
915
+ }
916
+ return {
917
+ bodyType,
918
+ body: jsonVariable.value
919
+ };
920
+ }
921
+ if (bodyType === HTTPBodyType.FormData) {
922
+ if (!httpNode.data.body.formData || !httpNode.data.body.formDataValues) {
923
+ throw new Error("HTTP form-data body is required");
924
+ }
925
+ const formData = context.runtime.state.parseInputs({
926
+ values: httpNode.data.body.formDataValues,
927
+ declare: httpNode.data.body.formData
928
+ });
929
+ return {
930
+ bodyType,
931
+ body: JSON.stringify(formData)
932
+ };
933
+ }
934
+ if (bodyType === HTTPBodyType.RawText) {
935
+ if (!httpNode.data.body.json) {
936
+ throw new Error("HTTP json body is required");
937
+ }
938
+ const jsonVariable = context.runtime.state.parseTemplate(httpNode.data.body.json);
939
+ if (!jsonVariable) {
940
+ throw new Error("HTTP json body is required");
941
+ }
942
+ return {
943
+ bodyType,
944
+ body: jsonVariable.value
945
+ };
946
+ }
947
+ if (bodyType === HTTPBodyType.Binary) {
948
+ if (!httpNode.data.body.binary) {
949
+ throw new Error("HTTP binary body is required");
950
+ }
951
+ const binaryVariable = context.runtime.state.parseTemplate(httpNode.data.body.binary);
952
+ if (!binaryVariable) {
953
+ throw new Error("HTTP binary body is required");
954
+ }
955
+ return {
956
+ bodyType,
957
+ body: binaryVariable.value
958
+ };
959
+ }
960
+ if (bodyType === HTTPBodyType.XWwwFormUrlencoded) {
961
+ if (!httpNode.data.body.xWwwFormUrlencoded || !httpNode.data.body.xWwwFormUrlencodedValues) {
962
+ throw new Error("HTTP x-www-form-urlencoded body is required");
963
+ }
964
+ const xWwwFormUrlencoded = context.runtime.state.parseInputs({
965
+ values: httpNode.data.body.xWwwFormUrlencodedValues,
966
+ declare: httpNode.data.body.xWwwFormUrlencoded
967
+ });
968
+ return {
969
+ bodyType,
970
+ body: JSON.stringify(xWwwFormUrlencoded)
971
+ };
972
+ }
973
+ throw new Error(`HTTP invalid body type "${bodyType}"`);
974
+ }
975
+ buildUrlWithParams(url, params) {
976
+ const urlObj = new URL(url);
977
+ Object.entries(params).forEach(([key, value]) => {
978
+ if (value !== void 0 && value !== null && value !== "") {
979
+ urlObj.searchParams.set(key, value);
980
+ }
981
+ });
982
+ return urlObj.toString();
983
+ }
984
+ prepareHeaders(headers, bodyType) {
985
+ const preparedHeaders = { ...headers };
986
+ if (!preparedHeaders["Content-Type"] && !preparedHeaders["content-type"]) {
987
+ switch (bodyType) {
988
+ case HTTPBodyType.JSON:
989
+ preparedHeaders["Content-Type"] = "application/json";
990
+ break;
991
+ case HTTPBodyType.FormData:
992
+ break;
993
+ case HTTPBodyType.XWwwFormUrlencoded:
994
+ preparedHeaders["Content-Type"] = "application/x-www-form-urlencoded";
995
+ break;
996
+ case HTTPBodyType.RawText:
997
+ preparedHeaders["Content-Type"] = "text/plain";
998
+ break;
999
+ case HTTPBodyType.Binary:
1000
+ preparedHeaders["Content-Type"] = "application/octet-stream";
1001
+ break;
1002
+ }
1003
+ }
1004
+ return preparedHeaders;
1005
+ }
1006
+ prepareBody(body, bodyType) {
1007
+ switch (bodyType) {
1008
+ case HTTPBodyType.JSON:
1009
+ return body;
1010
+ case HTTPBodyType.FormData:
1011
+ const formData = new FormData();
1012
+ try {
1013
+ const data = JSON.parse(body);
1014
+ Object.entries(data).forEach(([key, value]) => {
1015
+ formData.append(key, String(value));
1016
+ });
1017
+ } catch (error) {
1018
+ throw new Error("Invalid FormData body format");
1019
+ }
1020
+ return formData;
1021
+ case HTTPBodyType.XWwwFormUrlencoded:
1022
+ try {
1023
+ const data = JSON.parse(body);
1024
+ const params = new URLSearchParams();
1025
+ Object.entries(data).forEach(([key, value]) => {
1026
+ params.append(key, String(value));
1027
+ });
1028
+ return params.toString();
1029
+ } catch (error) {
1030
+ throw new Error("Invalid x-www-form-urlencoded body format");
1031
+ }
1032
+ case HTTPBodyType.RawText:
1033
+ case HTTPBodyType.Binary:
1034
+ default:
1035
+ return body;
1036
+ }
1037
+ }
1038
+ };
1039
+
795
1040
  // src/nodes/end/index.ts
796
1041
  var EndExecutor = class {
797
1042
  constructor() {
@@ -827,6 +1072,19 @@ var BlockEndExecutor = class {
827
1072
  }
828
1073
  };
829
1074
 
1075
+ // src/nodes/continue/index.ts
1076
+ var ContinueExecutor = class {
1077
+ constructor() {
1078
+ this.type = FlowGramNode.Continue;
1079
+ }
1080
+ async execute(context) {
1081
+ context.runtime.cache.set("loop-continue", true);
1082
+ return {
1083
+ outputs: {}
1084
+ };
1085
+ }
1086
+ };
1087
+
830
1088
  // src/nodes/condition/index.ts
831
1089
  var import_lodash_es9 = require("lodash-es");
832
1090
 
@@ -1118,7 +1376,7 @@ var ConditionExecutor = class {
1118
1376
  `Condition left type "${condition.leftType}" has no operator "${condition.operator}"`
1119
1377
  );
1120
1378
  }
1121
- if (ruleType !== condition.rightType) {
1379
+ if (!WorkflowRuntimeType.isTypeEqual(ruleType, condition.rightType)) {
1122
1380
  return false;
1123
1381
  }
1124
1382
  return true;
@@ -1133,6 +1391,81 @@ var ConditionExecutor = class {
1133
1391
  }
1134
1392
  };
1135
1393
 
1394
+ // src/nodes/code/index.ts
1395
+ var CodeExecutor = class {
1396
+ constructor() {
1397
+ this.type = FlowGramNode.Code;
1398
+ }
1399
+ async execute(context) {
1400
+ const inputs = this.parseInputs(context);
1401
+ if (inputs.script.language === "javascript") {
1402
+ return this.javascript(inputs);
1403
+ }
1404
+ throw new Error(`Unsupported code language "${inputs.script.language}"`);
1405
+ }
1406
+ parseInputs(context) {
1407
+ const codeNode = context.node;
1408
+ const params = context.inputs;
1409
+ const { language, content } = codeNode.data.script;
1410
+ if (!content) {
1411
+ throw new Error("Code content is required");
1412
+ }
1413
+ return {
1414
+ params,
1415
+ script: {
1416
+ language,
1417
+ content
1418
+ }
1419
+ };
1420
+ }
1421
+ async javascript(inputs) {
1422
+ const { params = {}, script } = inputs;
1423
+ try {
1424
+ const executeCode = new Function(
1425
+ "params",
1426
+ `
1427
+ 'use strict';
1428
+
1429
+ ${script.content}
1430
+
1431
+ // Ensure main function exists
1432
+ if (typeof main !== 'function') {
1433
+ throw new Error('main function is required in the script');
1434
+ }
1435
+
1436
+ // Execute main function with params
1437
+ return main({ params });
1438
+ `
1439
+ );
1440
+ const timeoutPromise = new Promise((_, reject) => {
1441
+ setTimeout(() => {
1442
+ reject(new Error("Code execution timeout: exceeded 1 minute"));
1443
+ }, 1e3 * 60);
1444
+ });
1445
+ const result = await Promise.race([executeCode(params), timeoutPromise]);
1446
+ const outputs = result && typeof result === "object" && !Array.isArray(result) ? result : { result };
1447
+ return {
1448
+ outputs
1449
+ };
1450
+ } catch (error) {
1451
+ throw new Error(`Code execution failed: ${error.message}`);
1452
+ }
1453
+ }
1454
+ };
1455
+
1456
+ // src/nodes/break/index.ts
1457
+ var BreakExecutor = class {
1458
+ constructor() {
1459
+ this.type = FlowGramNode.Break;
1460
+ }
1461
+ async execute(context) {
1462
+ context.runtime.cache.set("loop-break", true);
1463
+ return {
1464
+ outputs: {}
1465
+ };
1466
+ }
1467
+ };
1468
+
1136
1469
  // src/nodes/index.ts
1137
1470
  var WorkflowRuntimeNodeExecutors = [
1138
1471
  StartExecutor,
@@ -1141,7 +1474,11 @@ var WorkflowRuntimeNodeExecutors = [
1141
1474
  ConditionExecutor,
1142
1475
  LoopExecutor,
1143
1476
  BlockStartExecutor,
1144
- BlockEndExecutor
1477
+ BlockEndExecutor,
1478
+ HTTPExecutor,
1479
+ CodeExecutor,
1480
+ BreakExecutor,
1481
+ ContinueExecutor
1145
1482
  ];
1146
1483
 
1147
1484
  // src/domain/validation/validators/cycle-detection.ts
@@ -1560,6 +1897,29 @@ var WorkflowRuntimeMessageCenter = class {
1560
1897
  }
1561
1898
  };
1562
1899
 
1900
+ // src/domain/cache/index.ts
1901
+ var WorkflowRuntimeCache = class {
1902
+ init() {
1903
+ this.map = /* @__PURE__ */ new Map();
1904
+ }
1905
+ dispose() {
1906
+ this.map.clear();
1907
+ }
1908
+ get(key) {
1909
+ return this.map.get(key);
1910
+ }
1911
+ set(key, value) {
1912
+ this.map.set(key, value);
1913
+ return this;
1914
+ }
1915
+ delete(key) {
1916
+ return this.map.delete(key);
1917
+ }
1918
+ has(key) {
1919
+ return this.map.has(key);
1920
+ }
1921
+ };
1922
+
1563
1923
  // src/domain/variable/variable-store/index.ts
1564
1924
  var import_lodash_es10 = require("lodash-es");
1565
1925
 
@@ -1785,26 +2145,10 @@ var WorkflowRuntimeState = class {
1785
2145
  getNodeInputs(node) {
1786
2146
  const inputsDeclare = node.declare.inputs;
1787
2147
  const inputsValues = node.declare.inputsValues;
1788
- if (!inputsDeclare || !inputsValues) {
1789
- return {};
1790
- }
1791
- return Object.entries(inputsValues).reduce((prev, [key, inputValue]) => {
1792
- const typeInfo = inputsDeclare.properties?.[key];
1793
- if (!typeInfo) {
1794
- return prev;
1795
- }
1796
- const expectType = typeInfo.type;
1797
- const result = this.parseValue(inputValue);
1798
- if (!result) {
1799
- return prev;
1800
- }
1801
- const { value, type } = result;
1802
- if (!WorkflowRuntimeType.isTypeEqual(type, expectType)) {
1803
- return prev;
1804
- }
1805
- prev[key] = value;
1806
- return prev;
1807
- }, {});
2148
+ return this.parseInputs({
2149
+ values: inputsValues,
2150
+ declare: inputsDeclare
2151
+ });
1808
2152
  }
1809
2153
  setNodeOutputs(params) {
1810
2154
  const { node, outputs } = params;
@@ -1828,6 +2172,29 @@ var WorkflowRuntimeState = class {
1828
2172
  });
1829
2173
  });
1830
2174
  }
2175
+ parseInputs(params) {
2176
+ const { values, declare } = params;
2177
+ if (!declare || !values) {
2178
+ return {};
2179
+ }
2180
+ return Object.entries(values).reduce((prev, [key, inputValue]) => {
2181
+ const typeInfo = declare.properties?.[key];
2182
+ if (!typeInfo) {
2183
+ return prev;
2184
+ }
2185
+ const expectType = typeInfo.type;
2186
+ const result = this.parseValue(inputValue);
2187
+ if (!result) {
2188
+ return prev;
2189
+ }
2190
+ const { value, type } = result;
2191
+ if (!WorkflowRuntimeType.isTypeEqual(type, expectType)) {
2192
+ return prev;
2193
+ }
2194
+ prev[key] = value;
2195
+ return prev;
2196
+ }, {});
2197
+ }
1831
2198
  parseRef(ref) {
1832
2199
  if (ref?.type !== "ref") {
1833
2200
  throw new Error(`Invalid ref value: ${ref}`);
@@ -2361,6 +2728,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2361
2728
  constructor(data) {
2362
2729
  this.subContexts = [];
2363
2730
  this.id = uuid();
2731
+ this.cache = data.cache;
2364
2732
  this.document = data.document;
2365
2733
  this.variableStore = data.variableStore;
2366
2734
  this.state = data.state;
@@ -2372,6 +2740,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2372
2740
  }
2373
2741
  init(params) {
2374
2742
  const { schema, inputs } = params;
2743
+ this.cache.init();
2375
2744
  this.document.init(schema);
2376
2745
  this.variableStore.init();
2377
2746
  this.state.init();
@@ -2386,6 +2755,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2386
2755
  subContext.dispose();
2387
2756
  });
2388
2757
  this.subContexts = [];
2758
+ this.cache.dispose();
2389
2759
  this.document.dispose();
2390
2760
  this.variableStore.dispose();
2391
2761
  this.state.dispose();
@@ -2396,10 +2766,12 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2396
2766
  this.reporter.dispose();
2397
2767
  }
2398
2768
  sub() {
2769
+ const cache = new WorkflowRuntimeCache();
2399
2770
  const variableStore = new WorkflowRuntimeVariableStore();
2400
2771
  variableStore.setParent(this.variableStore);
2401
2772
  const state = new WorkflowRuntimeState(variableStore);
2402
2773
  const contextData = {
2774
+ cache,
2403
2775
  document: this.document,
2404
2776
  ioCenter: this.ioCenter,
2405
2777
  snapshotCenter: this.snapshotCenter,
@@ -2411,11 +2783,13 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2411
2783
  };
2412
2784
  const subContext = new _WorkflowRuntimeContext(contextData);
2413
2785
  this.subContexts.push(subContext);
2786
+ subContext.cache.init();
2414
2787
  subContext.variableStore.init();
2415
2788
  subContext.state.init();
2416
2789
  return subContext;
2417
2790
  }
2418
2791
  static create() {
2792
+ const cache = new WorkflowRuntimeCache();
2419
2793
  const document = new WorkflowRuntimeDocument();
2420
2794
  const variableStore = new WorkflowRuntimeVariableStore();
2421
2795
  const state = new WorkflowRuntimeState(variableStore);
@@ -2430,6 +2804,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2430
2804
  messageCenter
2431
2805
  );
2432
2806
  return new _WorkflowRuntimeContext({
2807
+ cache,
2433
2808
  document,
2434
2809
  variableStore,
2435
2810
  state,
@@ -2487,7 +2862,8 @@ var WorkflowRuntimeEngine = class {
2487
2862
  node,
2488
2863
  inputs,
2489
2864
  runtime: context,
2490
- container: WorkflowRuntimeContainer.instance
2865
+ container: WorkflowRuntimeContainer.instance,
2866
+ snapshot
2491
2867
  });
2492
2868
  if (context.statusCenter.workflow.terminated) {
2493
2869
  return;
@@ -2568,7 +2944,13 @@ var WorkflowRuntimeEngine = class {
2568
2944
  }
2569
2945
  async executeNext(params) {
2570
2946
  const { context, node, nextNodes } = params;
2571
- if (node.type === FlowGramNode.End || node.type === FlowGramNode.BlockEnd) {
2947
+ const terminatingNodeTypes = [
2948
+ FlowGramNode.End,
2949
+ FlowGramNode.BlockEnd,
2950
+ FlowGramNode.Break,
2951
+ FlowGramNode.Continue
2952
+ ];
2953
+ if (terminatingNodeTypes.includes(node.type)) {
2572
2954
  return;
2573
2955
  }
2574
2956
  if (nextNodes.length === 0) {