@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/esm/index.js CHANGED
@@ -195,13 +195,16 @@ var FlowGramNode = /* @__PURE__ */ ((FlowGramNode22) => {
195
195
  FlowGramNode22["Start"] = "start";
196
196
  FlowGramNode22["End"] = "end";
197
197
  FlowGramNode22["LLM"] = "llm";
198
- FlowGramNode22["code"] = "code";
198
+ FlowGramNode22["Code"] = "code";
199
199
  FlowGramNode22["Condition"] = "condition";
200
200
  FlowGramNode22["Loop"] = "loop";
201
201
  FlowGramNode22["Comment"] = "comment";
202
202
  FlowGramNode22["Group"] = "group";
203
203
  FlowGramNode22["BlockStart"] = "block-start";
204
204
  FlowGramNode22["BlockEnd"] = "block-end";
205
+ FlowGramNode22["HTTP"] = "http";
206
+ FlowGramNode22["Break"] = "break";
207
+ FlowGramNode22["Continue"] = "continue";
205
208
  return FlowGramNode22;
206
209
  })(FlowGramNode || {});
207
210
  var ConditionOperation = /* @__PURE__ */ ((ConditionOperation2) => {
@@ -221,6 +224,15 @@ var ConditionOperation = /* @__PURE__ */ ((ConditionOperation2) => {
221
224
  ConditionOperation2["IS_FALSE"] = "is_false";
222
225
  return ConditionOperation2;
223
226
  })(ConditionOperation || {});
227
+ var HTTPBodyType = /* @__PURE__ */ ((HTTPBodyType2) => {
228
+ HTTPBodyType2["None"] = "none";
229
+ HTTPBodyType2["FormData"] = "form-data";
230
+ HTTPBodyType2["XWwwFormUrlencoded"] = "x-www-form-urlencoded";
231
+ HTTPBodyType2["RawText"] = "raw-text";
232
+ HTTPBodyType2["JSON"] = "JSON";
233
+ HTTPBodyType2["Binary"] = "binary";
234
+ return HTTPBodyType2;
235
+ })(HTTPBodyType || {});
224
236
  var IEngine = Symbol.for("Engine");
225
237
  var IExecutor = Symbol.for("Executor");
226
238
  var WorkflowStatus = /* @__PURE__ */ ((WorkflowStatus2) => {
@@ -599,6 +611,12 @@ var LoopExecutor = class {
599
611
  } catch (e) {
600
612
  throw new Error(`Loop block execute error`);
601
613
  }
614
+ if (this.isBreak(subContext)) {
615
+ break;
616
+ }
617
+ if (this.isContinue(subContext)) {
618
+ continue;
619
+ }
602
620
  const blockOutput = this.getBlockOutput(context, subContext);
603
621
  blockOutputs.push(blockOutput);
604
622
  }
@@ -682,6 +700,12 @@ var LoopExecutor = class {
682
700
  const loopOutputsDeclare = loopNodeData.loopOutputs ?? {};
683
701
  return loopOutputsDeclare;
684
702
  }
703
+ isBreak(subContext) {
704
+ return subContext.cache.get("loop-break") === true;
705
+ }
706
+ isContinue(subContext) {
707
+ return subContext.cache.get("loop-continue") === true;
708
+ }
685
709
  };
686
710
 
687
711
  // src/nodes/llm/index.ts
@@ -751,6 +775,227 @@ var LLMExecutor = class {
751
775
  }
752
776
  };
753
777
 
778
+ // src/nodes/http/index.ts
779
+ var HTTPExecutor = class {
780
+ constructor() {
781
+ this.type = FlowGramNode.HTTP;
782
+ }
783
+ async execute(context) {
784
+ const inputs = this.parseInputs(context);
785
+ const response = await this.request(inputs);
786
+ const responseHeaders = {};
787
+ response.headers.forEach((value, key) => {
788
+ responseHeaders[key] = value;
789
+ });
790
+ const responseBody = await response.text();
791
+ return {
792
+ outputs: {
793
+ headers: responseHeaders,
794
+ statusCode: response.status,
795
+ body: responseBody
796
+ }
797
+ };
798
+ }
799
+ async request(inputs) {
800
+ const { method, url, headers, params, bodyType, body, retryTimes, timeout } = inputs;
801
+ const urlWithParams = this.buildUrlWithParams(url, params);
802
+ const requestOptions = {
803
+ method,
804
+ headers: this.prepareHeaders(headers, bodyType),
805
+ signal: AbortSignal.timeout(timeout)
806
+ };
807
+ if (method !== "GET" && method !== "HEAD" && body) {
808
+ requestOptions.body = this.prepareBody(body, bodyType);
809
+ }
810
+ let lastError = null;
811
+ for (let attempt = 0; attempt <= retryTimes; attempt++) {
812
+ try {
813
+ const response = await fetch(urlWithParams, requestOptions);
814
+ return response;
815
+ } catch (error) {
816
+ lastError = error;
817
+ if (attempt < retryTimes) {
818
+ await new Promise((resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3));
819
+ }
820
+ }
821
+ }
822
+ throw lastError || new Error("HTTP request failed after all retry attempts");
823
+ }
824
+ parseInputs(context) {
825
+ const httpNode = context.node;
826
+ const method = httpNode.data.api.method;
827
+ const urlVariable = context.runtime.state.parseTemplate(httpNode.data.api.url);
828
+ if (!urlVariable) {
829
+ throw new Error("HTTP url is required");
830
+ }
831
+ const url = urlVariable.value;
832
+ const headers = context.runtime.state.parseInputs({
833
+ values: httpNode.data.headersValues,
834
+ declare: httpNode.data.headers
835
+ });
836
+ const params = context.runtime.state.parseInputs({
837
+ values: httpNode.data.paramsValues,
838
+ declare: httpNode.data.params
839
+ });
840
+ const body = this.parseBody(context);
841
+ const retryTimes = httpNode.data.timeout.retryTimes;
842
+ const timeout = httpNode.data.timeout.timeout;
843
+ const inputs = {
844
+ method,
845
+ url,
846
+ headers,
847
+ params,
848
+ bodyType: body.bodyType,
849
+ body: body.body,
850
+ retryTimes,
851
+ timeout
852
+ };
853
+ context.snapshot.update({
854
+ inputs: JSON.parse(JSON.stringify(inputs))
855
+ });
856
+ return inputs;
857
+ }
858
+ parseBody(context) {
859
+ const httpNode = context.node;
860
+ const bodyType = httpNode.data.body.bodyType;
861
+ if (bodyType === HTTPBodyType.None) {
862
+ return {
863
+ bodyType,
864
+ body: ""
865
+ };
866
+ }
867
+ if (bodyType === HTTPBodyType.JSON) {
868
+ if (!httpNode.data.body.json) {
869
+ throw new Error("HTTP json body is required");
870
+ }
871
+ const jsonVariable = context.runtime.state.parseTemplate(httpNode.data.body.json);
872
+ if (!jsonVariable) {
873
+ throw new Error("HTTP json body is required");
874
+ }
875
+ return {
876
+ bodyType,
877
+ body: jsonVariable.value
878
+ };
879
+ }
880
+ if (bodyType === HTTPBodyType.FormData) {
881
+ if (!httpNode.data.body.formData || !httpNode.data.body.formDataValues) {
882
+ throw new Error("HTTP form-data body is required");
883
+ }
884
+ const formData = context.runtime.state.parseInputs({
885
+ values: httpNode.data.body.formDataValues,
886
+ declare: httpNode.data.body.formData
887
+ });
888
+ return {
889
+ bodyType,
890
+ body: JSON.stringify(formData)
891
+ };
892
+ }
893
+ if (bodyType === HTTPBodyType.RawText) {
894
+ if (!httpNode.data.body.json) {
895
+ throw new Error("HTTP json body is required");
896
+ }
897
+ const jsonVariable = context.runtime.state.parseTemplate(httpNode.data.body.json);
898
+ if (!jsonVariable) {
899
+ throw new Error("HTTP json body is required");
900
+ }
901
+ return {
902
+ bodyType,
903
+ body: jsonVariable.value
904
+ };
905
+ }
906
+ if (bodyType === HTTPBodyType.Binary) {
907
+ if (!httpNode.data.body.binary) {
908
+ throw new Error("HTTP binary body is required");
909
+ }
910
+ const binaryVariable = context.runtime.state.parseTemplate(httpNode.data.body.binary);
911
+ if (!binaryVariable) {
912
+ throw new Error("HTTP binary body is required");
913
+ }
914
+ return {
915
+ bodyType,
916
+ body: binaryVariable.value
917
+ };
918
+ }
919
+ if (bodyType === HTTPBodyType.XWwwFormUrlencoded) {
920
+ if (!httpNode.data.body.xWwwFormUrlencoded || !httpNode.data.body.xWwwFormUrlencodedValues) {
921
+ throw new Error("HTTP x-www-form-urlencoded body is required");
922
+ }
923
+ const xWwwFormUrlencoded = context.runtime.state.parseInputs({
924
+ values: httpNode.data.body.xWwwFormUrlencodedValues,
925
+ declare: httpNode.data.body.xWwwFormUrlencoded
926
+ });
927
+ return {
928
+ bodyType,
929
+ body: JSON.stringify(xWwwFormUrlencoded)
930
+ };
931
+ }
932
+ throw new Error(`HTTP invalid body type "${bodyType}"`);
933
+ }
934
+ buildUrlWithParams(url, params) {
935
+ const urlObj = new URL(url);
936
+ Object.entries(params).forEach(([key, value]) => {
937
+ if (value !== void 0 && value !== null && value !== "") {
938
+ urlObj.searchParams.set(key, value);
939
+ }
940
+ });
941
+ return urlObj.toString();
942
+ }
943
+ prepareHeaders(headers, bodyType) {
944
+ const preparedHeaders = { ...headers };
945
+ if (!preparedHeaders["Content-Type"] && !preparedHeaders["content-type"]) {
946
+ switch (bodyType) {
947
+ case HTTPBodyType.JSON:
948
+ preparedHeaders["Content-Type"] = "application/json";
949
+ break;
950
+ case HTTPBodyType.FormData:
951
+ break;
952
+ case HTTPBodyType.XWwwFormUrlencoded:
953
+ preparedHeaders["Content-Type"] = "application/x-www-form-urlencoded";
954
+ break;
955
+ case HTTPBodyType.RawText:
956
+ preparedHeaders["Content-Type"] = "text/plain";
957
+ break;
958
+ case HTTPBodyType.Binary:
959
+ preparedHeaders["Content-Type"] = "application/octet-stream";
960
+ break;
961
+ }
962
+ }
963
+ return preparedHeaders;
964
+ }
965
+ prepareBody(body, bodyType) {
966
+ switch (bodyType) {
967
+ case HTTPBodyType.JSON:
968
+ return body;
969
+ case HTTPBodyType.FormData:
970
+ const formData = new FormData();
971
+ try {
972
+ const data = JSON.parse(body);
973
+ Object.entries(data).forEach(([key, value]) => {
974
+ formData.append(key, String(value));
975
+ });
976
+ } catch (error) {
977
+ throw new Error("Invalid FormData body format");
978
+ }
979
+ return formData;
980
+ case HTTPBodyType.XWwwFormUrlencoded:
981
+ try {
982
+ const data = JSON.parse(body);
983
+ const params = new URLSearchParams();
984
+ Object.entries(data).forEach(([key, value]) => {
985
+ params.append(key, String(value));
986
+ });
987
+ return params.toString();
988
+ } catch (error) {
989
+ throw new Error("Invalid x-www-form-urlencoded body format");
990
+ }
991
+ case HTTPBodyType.RawText:
992
+ case HTTPBodyType.Binary:
993
+ default:
994
+ return body;
995
+ }
996
+ }
997
+ };
998
+
754
999
  // src/nodes/end/index.ts
755
1000
  var EndExecutor = class {
756
1001
  constructor() {
@@ -786,6 +1031,19 @@ var BlockEndExecutor = class {
786
1031
  }
787
1032
  };
788
1033
 
1034
+ // src/nodes/continue/index.ts
1035
+ var ContinueExecutor = class {
1036
+ constructor() {
1037
+ this.type = FlowGramNode.Continue;
1038
+ }
1039
+ async execute(context) {
1040
+ context.runtime.cache.set("loop-continue", true);
1041
+ return {
1042
+ outputs: {}
1043
+ };
1044
+ }
1045
+ };
1046
+
789
1047
  // src/nodes/condition/index.ts
790
1048
  import { isNil as isNil9 } from "lodash-es";
791
1049
 
@@ -1077,7 +1335,7 @@ var ConditionExecutor = class {
1077
1335
  `Condition left type "${condition.leftType}" has no operator "${condition.operator}"`
1078
1336
  );
1079
1337
  }
1080
- if (ruleType !== condition.rightType) {
1338
+ if (!WorkflowRuntimeType.isTypeEqual(ruleType, condition.rightType)) {
1081
1339
  return false;
1082
1340
  }
1083
1341
  return true;
@@ -1092,6 +1350,81 @@ var ConditionExecutor = class {
1092
1350
  }
1093
1351
  };
1094
1352
 
1353
+ // src/nodes/code/index.ts
1354
+ var CodeExecutor = class {
1355
+ constructor() {
1356
+ this.type = FlowGramNode.Code;
1357
+ }
1358
+ async execute(context) {
1359
+ const inputs = this.parseInputs(context);
1360
+ if (inputs.script.language === "javascript") {
1361
+ return this.javascript(inputs);
1362
+ }
1363
+ throw new Error(`Unsupported code language "${inputs.script.language}"`);
1364
+ }
1365
+ parseInputs(context) {
1366
+ const codeNode = context.node;
1367
+ const params = context.inputs;
1368
+ const { language, content } = codeNode.data.script;
1369
+ if (!content) {
1370
+ throw new Error("Code content is required");
1371
+ }
1372
+ return {
1373
+ params,
1374
+ script: {
1375
+ language,
1376
+ content
1377
+ }
1378
+ };
1379
+ }
1380
+ async javascript(inputs) {
1381
+ const { params = {}, script } = inputs;
1382
+ try {
1383
+ const executeCode = new Function(
1384
+ "params",
1385
+ `
1386
+ 'use strict';
1387
+
1388
+ ${script.content}
1389
+
1390
+ // Ensure main function exists
1391
+ if (typeof main !== 'function') {
1392
+ throw new Error('main function is required in the script');
1393
+ }
1394
+
1395
+ // Execute main function with params
1396
+ return main({ params });
1397
+ `
1398
+ );
1399
+ const timeoutPromise = new Promise((_, reject) => {
1400
+ setTimeout(() => {
1401
+ reject(new Error("Code execution timeout: exceeded 1 minute"));
1402
+ }, 1e3 * 60);
1403
+ });
1404
+ const result = await Promise.race([executeCode(params), timeoutPromise]);
1405
+ const outputs = result && typeof result === "object" && !Array.isArray(result) ? result : { result };
1406
+ return {
1407
+ outputs
1408
+ };
1409
+ } catch (error) {
1410
+ throw new Error(`Code execution failed: ${error.message}`);
1411
+ }
1412
+ }
1413
+ };
1414
+
1415
+ // src/nodes/break/index.ts
1416
+ var BreakExecutor = class {
1417
+ constructor() {
1418
+ this.type = FlowGramNode.Break;
1419
+ }
1420
+ async execute(context) {
1421
+ context.runtime.cache.set("loop-break", true);
1422
+ return {
1423
+ outputs: {}
1424
+ };
1425
+ }
1426
+ };
1427
+
1095
1428
  // src/nodes/index.ts
1096
1429
  var WorkflowRuntimeNodeExecutors = [
1097
1430
  StartExecutor,
@@ -1100,7 +1433,11 @@ var WorkflowRuntimeNodeExecutors = [
1100
1433
  ConditionExecutor,
1101
1434
  LoopExecutor,
1102
1435
  BlockStartExecutor,
1103
- BlockEndExecutor
1436
+ BlockEndExecutor,
1437
+ HTTPExecutor,
1438
+ CodeExecutor,
1439
+ BreakExecutor,
1440
+ ContinueExecutor
1104
1441
  ];
1105
1442
 
1106
1443
  // src/domain/validation/validators/cycle-detection.ts
@@ -1519,6 +1856,29 @@ var WorkflowRuntimeMessageCenter = class {
1519
1856
  }
1520
1857
  };
1521
1858
 
1859
+ // src/domain/cache/index.ts
1860
+ var WorkflowRuntimeCache = class {
1861
+ init() {
1862
+ this.map = /* @__PURE__ */ new Map();
1863
+ }
1864
+ dispose() {
1865
+ this.map.clear();
1866
+ }
1867
+ get(key) {
1868
+ return this.map.get(key);
1869
+ }
1870
+ set(key, value) {
1871
+ this.map.set(key, value);
1872
+ return this;
1873
+ }
1874
+ delete(key) {
1875
+ return this.map.delete(key);
1876
+ }
1877
+ has(key) {
1878
+ return this.map.has(key);
1879
+ }
1880
+ };
1881
+
1522
1882
  // src/domain/variable/variable-store/index.ts
1523
1883
  import { get, set } from "lodash-es";
1524
1884
 
@@ -1744,26 +2104,10 @@ var WorkflowRuntimeState = class {
1744
2104
  getNodeInputs(node) {
1745
2105
  const inputsDeclare = node.declare.inputs;
1746
2106
  const inputsValues = node.declare.inputsValues;
1747
- if (!inputsDeclare || !inputsValues) {
1748
- return {};
1749
- }
1750
- return Object.entries(inputsValues).reduce((prev, [key, inputValue]) => {
1751
- const typeInfo = inputsDeclare.properties?.[key];
1752
- if (!typeInfo) {
1753
- return prev;
1754
- }
1755
- const expectType = typeInfo.type;
1756
- const result = this.parseValue(inputValue);
1757
- if (!result) {
1758
- return prev;
1759
- }
1760
- const { value, type } = result;
1761
- if (!WorkflowRuntimeType.isTypeEqual(type, expectType)) {
1762
- return prev;
1763
- }
1764
- prev[key] = value;
1765
- return prev;
1766
- }, {});
2107
+ return this.parseInputs({
2108
+ values: inputsValues,
2109
+ declare: inputsDeclare
2110
+ });
1767
2111
  }
1768
2112
  setNodeOutputs(params) {
1769
2113
  const { node, outputs } = params;
@@ -1787,6 +2131,29 @@ var WorkflowRuntimeState = class {
1787
2131
  });
1788
2132
  });
1789
2133
  }
2134
+ parseInputs(params) {
2135
+ const { values, declare } = params;
2136
+ if (!declare || !values) {
2137
+ return {};
2138
+ }
2139
+ return Object.entries(values).reduce((prev, [key, inputValue]) => {
2140
+ const typeInfo = declare.properties?.[key];
2141
+ if (!typeInfo) {
2142
+ return prev;
2143
+ }
2144
+ const expectType = typeInfo.type;
2145
+ const result = this.parseValue(inputValue);
2146
+ if (!result) {
2147
+ return prev;
2148
+ }
2149
+ const { value, type } = result;
2150
+ if (!WorkflowRuntimeType.isTypeEqual(type, expectType)) {
2151
+ return prev;
2152
+ }
2153
+ prev[key] = value;
2154
+ return prev;
2155
+ }, {});
2156
+ }
1790
2157
  parseRef(ref) {
1791
2158
  if (ref?.type !== "ref") {
1792
2159
  throw new Error(`Invalid ref value: ${ref}`);
@@ -2320,6 +2687,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2320
2687
  constructor(data) {
2321
2688
  this.subContexts = [];
2322
2689
  this.id = uuid();
2690
+ this.cache = data.cache;
2323
2691
  this.document = data.document;
2324
2692
  this.variableStore = data.variableStore;
2325
2693
  this.state = data.state;
@@ -2331,6 +2699,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2331
2699
  }
2332
2700
  init(params) {
2333
2701
  const { schema, inputs } = params;
2702
+ this.cache.init();
2334
2703
  this.document.init(schema);
2335
2704
  this.variableStore.init();
2336
2705
  this.state.init();
@@ -2345,6 +2714,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2345
2714
  subContext.dispose();
2346
2715
  });
2347
2716
  this.subContexts = [];
2717
+ this.cache.dispose();
2348
2718
  this.document.dispose();
2349
2719
  this.variableStore.dispose();
2350
2720
  this.state.dispose();
@@ -2355,10 +2725,12 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2355
2725
  this.reporter.dispose();
2356
2726
  }
2357
2727
  sub() {
2728
+ const cache = new WorkflowRuntimeCache();
2358
2729
  const variableStore = new WorkflowRuntimeVariableStore();
2359
2730
  variableStore.setParent(this.variableStore);
2360
2731
  const state = new WorkflowRuntimeState(variableStore);
2361
2732
  const contextData = {
2733
+ cache,
2362
2734
  document: this.document,
2363
2735
  ioCenter: this.ioCenter,
2364
2736
  snapshotCenter: this.snapshotCenter,
@@ -2370,11 +2742,13 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2370
2742
  };
2371
2743
  const subContext = new _WorkflowRuntimeContext(contextData);
2372
2744
  this.subContexts.push(subContext);
2745
+ subContext.cache.init();
2373
2746
  subContext.variableStore.init();
2374
2747
  subContext.state.init();
2375
2748
  return subContext;
2376
2749
  }
2377
2750
  static create() {
2751
+ const cache = new WorkflowRuntimeCache();
2378
2752
  const document = new WorkflowRuntimeDocument();
2379
2753
  const variableStore = new WorkflowRuntimeVariableStore();
2380
2754
  const state = new WorkflowRuntimeState(variableStore);
@@ -2389,6 +2763,7 @@ var WorkflowRuntimeContext = class _WorkflowRuntimeContext {
2389
2763
  messageCenter
2390
2764
  );
2391
2765
  return new _WorkflowRuntimeContext({
2766
+ cache,
2392
2767
  document,
2393
2768
  variableStore,
2394
2769
  state,
@@ -2446,7 +2821,8 @@ var WorkflowRuntimeEngine = class {
2446
2821
  node,
2447
2822
  inputs,
2448
2823
  runtime: context,
2449
- container: WorkflowRuntimeContainer.instance
2824
+ container: WorkflowRuntimeContainer.instance,
2825
+ snapshot
2450
2826
  });
2451
2827
  if (context.statusCenter.workflow.terminated) {
2452
2828
  return;
@@ -2527,7 +2903,13 @@ var WorkflowRuntimeEngine = class {
2527
2903
  }
2528
2904
  async executeNext(params) {
2529
2905
  const { context, node, nextNodes } = params;
2530
- if (node.type === FlowGramNode.End || node.type === FlowGramNode.BlockEnd) {
2906
+ const terminatingNodeTypes = [
2907
+ FlowGramNode.End,
2908
+ FlowGramNode.BlockEnd,
2909
+ FlowGramNode.Break,
2910
+ FlowGramNode.Continue
2911
+ ];
2912
+ if (terminatingNodeTypes.includes(node.type)) {
2531
2913
  return;
2532
2914
  }
2533
2915
  if (nextNodes.length === 0) {