@vaudience/aigentflow-flow-validator 0.22.0 → 0.24.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/PARITY.md +47 -1
- package/README.md +1 -1
- package/dist/cli.js +173 -23
- package/dist/index.cjs +172 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +172 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -433,7 +433,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
433
433
|
message: "Executor is required for each step",
|
|
434
434
|
code: "missing_required_field",
|
|
435
435
|
stepId: stepID,
|
|
436
|
-
suggestion: "Specify an executor URL (e.g., 'function://
|
|
436
|
+
suggestion: "Specify an executor URL (e.g., 'function://utils/log')"
|
|
437
437
|
});
|
|
438
438
|
} else if (executorText === null) {
|
|
439
439
|
issues.error({
|
|
@@ -449,7 +449,7 @@ function validateBasicStructure(flow, issues) {
|
|
|
449
449
|
|
|
450
450
|
// src/spec/aigentflow-spec.json
|
|
451
451
|
var aigentflow_spec_default = {
|
|
452
|
-
specVersion: "2.
|
|
452
|
+
specVersion: "2.831.0",
|
|
453
453
|
inputSchemaVersion: 1,
|
|
454
454
|
executorSchemes: [
|
|
455
455
|
"a2a",
|
|
@@ -946,6 +946,46 @@ var aigentflow_spec_default = {
|
|
|
946
946
|
stepFieldFormat: "steps.%s.query.%s",
|
|
947
947
|
loopStepFieldFormat: "steps.%s.loop.steps.%s.query.%s"
|
|
948
948
|
},
|
|
949
|
+
functionOperations: {
|
|
950
|
+
code: "unknown_function_operation",
|
|
951
|
+
scheme: "function",
|
|
952
|
+
messageFormat: "step %s uses function://%s, which is not a built-in function. Available: %s",
|
|
953
|
+
suggestionFormat: "Use one of the built-in function operations: %s. For custom logic use a script://, http:// or ai:// executor instead.",
|
|
954
|
+
operations: [
|
|
955
|
+
"binary-processor/validate-and-store",
|
|
956
|
+
"data/create",
|
|
957
|
+
"data/from-json",
|
|
958
|
+
"data/to-json",
|
|
959
|
+
"data/validate",
|
|
960
|
+
"demo/adult-processor",
|
|
961
|
+
"demo/calculator",
|
|
962
|
+
"demo/data-processor",
|
|
963
|
+
"demo/finalizer",
|
|
964
|
+
"demo/senior-processor",
|
|
965
|
+
"demo/youth-processor",
|
|
966
|
+
"document/finalize",
|
|
967
|
+
"document/prepare",
|
|
968
|
+
"json/validate",
|
|
969
|
+
"math/basic",
|
|
970
|
+
"math/calculate",
|
|
971
|
+
"math/sum",
|
|
972
|
+
"results-compiler/aggregate",
|
|
973
|
+
"system/info",
|
|
974
|
+
"text/analysis",
|
|
975
|
+
"text/count-characters",
|
|
976
|
+
"text/count-lines",
|
|
977
|
+
"text/count-occurrences",
|
|
978
|
+
"text/count-words",
|
|
979
|
+
"text/find-pattern",
|
|
980
|
+
"text/generate",
|
|
981
|
+
"text/replace-text",
|
|
982
|
+
"text/search",
|
|
983
|
+
"utils/datetime",
|
|
984
|
+
"utils/format-date",
|
|
985
|
+
"utils/log",
|
|
986
|
+
"utils/parse-date"
|
|
987
|
+
]
|
|
988
|
+
},
|
|
949
989
|
flowDataReservedKeys: {
|
|
950
990
|
code: "flow_data_key_reserved",
|
|
951
991
|
fieldFormat: "data.%s",
|
|
@@ -1980,9 +2020,51 @@ var TEMPLATE_DEFAULT_PIPED = {
|
|
|
1980
2020
|
messageFormat: aigentflow_spec_default.templateDefaultPiped.messageFormat,
|
|
1981
2021
|
suggestion: aigentflow_spec_default.templateDefaultPiped.suggestion
|
|
1982
2022
|
};
|
|
2023
|
+
var FUNCTION_OPERATIONS = {
|
|
2024
|
+
code: aigentflow_spec_default.functionOperations.code,
|
|
2025
|
+
scheme: aigentflow_spec_default.functionOperations.scheme,
|
|
2026
|
+
messageFormat: aigentflow_spec_default.functionOperations.messageFormat,
|
|
2027
|
+
suggestionFormat: aigentflow_spec_default.functionOperations.suggestionFormat,
|
|
2028
|
+
operations: new Set(aigentflow_spec_default.functionOperations.operations),
|
|
2029
|
+
sorted: aigentflow_spec_default.functionOperations.operations
|
|
2030
|
+
};
|
|
1983
2031
|
|
|
1984
2032
|
// src/validators/executors.ts
|
|
1985
2033
|
var SCHEME_SEPARATOR = "://";
|
|
2034
|
+
var UNKNOWN_SCHEME_BASE = (scheme) => `Unknown executor scheme '${scheme}'. It may be valid in a newer AIgentFlow release, or it could be a typo.`;
|
|
2035
|
+
var DECIDE_SCHEME = "decide";
|
|
2036
|
+
var DECIDE_HINT = "Decision models run through ai://{provider}/decide (for example ai://openai/decide), not through a scheme of their own.";
|
|
2037
|
+
var MAX_SCHEME_EDIT_DISTANCE = 2;
|
|
2038
|
+
function editDistance(a, b) {
|
|
2039
|
+
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
2040
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2041
|
+
const cur = [i];
|
|
2042
|
+
for (let j = 1; j <= b.length; j++) {
|
|
2043
|
+
cur[j] = Math.min(
|
|
2044
|
+
prev[j] + 1,
|
|
2045
|
+
cur[j - 1] + 1,
|
|
2046
|
+
prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
|
|
2047
|
+
);
|
|
2048
|
+
}
|
|
2049
|
+
prev = cur;
|
|
2050
|
+
}
|
|
2051
|
+
return prev[b.length];
|
|
2052
|
+
}
|
|
2053
|
+
function unknownSchemeMessage(scheme) {
|
|
2054
|
+
const lower = scheme.toLowerCase();
|
|
2055
|
+
const base = UNKNOWN_SCHEME_BASE(scheme);
|
|
2056
|
+
if (lower === DECIDE_SCHEME) return `${base} ${DECIDE_HINT}`;
|
|
2057
|
+
let best;
|
|
2058
|
+
let bestDist = MAX_SCHEME_EDIT_DISTANCE + 1;
|
|
2059
|
+
for (const known of [...EXECUTOR_SCHEMES].sort()) {
|
|
2060
|
+
const d = editDistance(lower, known);
|
|
2061
|
+
if (d < bestDist) {
|
|
2062
|
+
best = known;
|
|
2063
|
+
bestDist = d;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
return best === void 0 ? base : `${base} Did you mean '${best}'?`;
|
|
2067
|
+
}
|
|
1986
2068
|
function checkExecutor(executor, field, stepID, issues) {
|
|
1987
2069
|
const sepIdx = executor.indexOf(SCHEME_SEPARATOR);
|
|
1988
2070
|
const scheme = sepIdx > 0 ? executor.slice(0, sepIdx) : "";
|
|
@@ -2001,11 +2083,28 @@ function checkExecutor(executor, field, stepID, issues) {
|
|
|
2001
2083
|
if (!EXECUTOR_SCHEMES.has(scheme.toLowerCase())) {
|
|
2002
2084
|
issues.warn({
|
|
2003
2085
|
field,
|
|
2004
|
-
message:
|
|
2086
|
+
message: unknownSchemeMessage(scheme),
|
|
2005
2087
|
code: "unknown_executor_scheme",
|
|
2006
2088
|
stepId: stepID
|
|
2007
2089
|
});
|
|
2090
|
+
return;
|
|
2008
2091
|
}
|
|
2092
|
+
checkFunctionOperation(executor, field, stepID, templated, issues);
|
|
2093
|
+
}
|
|
2094
|
+
function checkFunctionOperation(executor, field, stepID, templated, issues) {
|
|
2095
|
+
if (templated) return;
|
|
2096
|
+
const m = EXECUTOR_URL_PATTERN.exec(executor);
|
|
2097
|
+
if (m === null || m[1] !== FUNCTION_OPERATIONS.scheme) return;
|
|
2098
|
+
const op = `${m[2]}/${m[3]}`;
|
|
2099
|
+
if (FUNCTION_OPERATIONS.operations.has(op)) return;
|
|
2100
|
+
const list = FUNCTION_OPERATIONS.sorted.join(", ");
|
|
2101
|
+
issues.error({
|
|
2102
|
+
field,
|
|
2103
|
+
message: FUNCTION_OPERATIONS.messageFormat.replace("%s", () => JSON.stringify(stepID)).replace("%s", () => op).replace("%s", () => list),
|
|
2104
|
+
code: FUNCTION_OPERATIONS.code,
|
|
2105
|
+
stepId: stepID,
|
|
2106
|
+
suggestion: FUNCTION_OPERATIONS.suggestionFormat.replace("%s", () => list)
|
|
2107
|
+
});
|
|
2009
2108
|
}
|
|
2010
2109
|
function validateExecutors(flow, issues) {
|
|
2011
2110
|
const steps = flow.steps;
|
|
@@ -2532,32 +2631,82 @@ function validateConnectivity(flow, issues) {
|
|
|
2532
2631
|
});
|
|
2533
2632
|
}
|
|
2534
2633
|
}
|
|
2535
|
-
|
|
2536
|
-
const recStack = /* @__PURE__ */ new Set();
|
|
2537
|
-
const hasCycle = (stepID) => {
|
|
2538
|
-
if (recStack.has(stepID)) return true;
|
|
2539
|
-
if (visited.has(stepID)) return false;
|
|
2540
|
-
visited.add(stepID);
|
|
2541
|
-
recStack.add(stepID);
|
|
2542
|
-
const step = steps[stepID];
|
|
2543
|
-
if (isRecord(step) && isRecord(step.next)) {
|
|
2544
|
-
const next = step.next;
|
|
2545
|
-
for (const target of cycleTargets(next, `steps.${stepID}.next`, issues)) {
|
|
2546
|
-
if (hasCycle(target)) return true;
|
|
2547
|
-
}
|
|
2548
|
-
}
|
|
2549
|
-
recStack.delete(stepID);
|
|
2550
|
-
return false;
|
|
2551
|
-
};
|
|
2552
|
-
if (hasCycle(start)) {
|
|
2634
|
+
if (cycleWithoutExit(steps, start, issues)) {
|
|
2553
2635
|
issues.warn({
|
|
2554
2636
|
field: "steps",
|
|
2555
|
-
message: "Potential infinite loop detected in step flow",
|
|
2637
|
+
message: "Potential infinite loop detected in step flow: a cycle of steps has no way out",
|
|
2556
2638
|
code: "potential_infinite_loop",
|
|
2557
|
-
suggestion: "
|
|
2639
|
+
suggestion: "Give one step in the cycle a `next.conditions` branch that leaves it (or end the flow there)"
|
|
2558
2640
|
});
|
|
2559
2641
|
}
|
|
2560
2642
|
}
|
|
2643
|
+
function cycleEdges(step, stepID, issues) {
|
|
2644
|
+
if (!isRecord(step) || !isRecord(step.next)) return [];
|
|
2645
|
+
const next = step.next;
|
|
2646
|
+
const nextPath = `steps.${stepID}.next`;
|
|
2647
|
+
const out = [];
|
|
2648
|
+
const def = issues.stringOf(next, "default", nextPath);
|
|
2649
|
+
if (def !== null && def !== "") out.push(def);
|
|
2650
|
+
if (Array.isArray(next.conditions)) {
|
|
2651
|
+
next.conditions.forEach((cond, i) => {
|
|
2652
|
+
if (!isRecord(cond)) return;
|
|
2653
|
+
const goto = issues.stringOf(cond, "goto", `${nextPath}.conditions[${i}]`);
|
|
2654
|
+
if (goto !== null && goto !== "") out.push(goto);
|
|
2655
|
+
});
|
|
2656
|
+
}
|
|
2657
|
+
return out;
|
|
2658
|
+
}
|
|
2659
|
+
function stepLeavesCycle(steps, stepID, inCycle, issues) {
|
|
2660
|
+
const step = steps[stepID];
|
|
2661
|
+
if (!isRecord(step) || !isRecord(step.next)) return true;
|
|
2662
|
+
const next = step.next;
|
|
2663
|
+
if (next.parallel !== void 0 && next.parallel !== null) return true;
|
|
2664
|
+
const def = issues.stringOf(next, "default", `steps.${stepID}.next`);
|
|
2665
|
+
if (def === null || def === "") return true;
|
|
2666
|
+
for (const target of cycleEdges(step, stepID, issues)) {
|
|
2667
|
+
if (isMarker(target) || !inCycle.has(target)) return true;
|
|
2668
|
+
}
|
|
2669
|
+
return false;
|
|
2670
|
+
}
|
|
2671
|
+
function cycleWithoutExit(steps, start, issues) {
|
|
2672
|
+
let index = 0;
|
|
2673
|
+
const indices = /* @__PURE__ */ new Map();
|
|
2674
|
+
const low = /* @__PURE__ */ new Map();
|
|
2675
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
2676
|
+
const stack = [];
|
|
2677
|
+
let found = false;
|
|
2678
|
+
const strong = (id) => {
|
|
2679
|
+
indices.set(id, index);
|
|
2680
|
+
low.set(id, index);
|
|
2681
|
+
index++;
|
|
2682
|
+
stack.push(id);
|
|
2683
|
+
onStack.add(id);
|
|
2684
|
+
for (const target of cycleEdges(steps[id], id, issues)) {
|
|
2685
|
+
if (isMarker(target) || !Object.prototype.hasOwnProperty.call(steps, target)) continue;
|
|
2686
|
+
if (!indices.has(target)) {
|
|
2687
|
+
strong(target);
|
|
2688
|
+
low.set(id, Math.min(low.get(id), low.get(target)));
|
|
2689
|
+
} else if (onStack.has(target)) {
|
|
2690
|
+
low.set(id, Math.min(low.get(id), indices.get(target)));
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
if (low.get(id) !== indices.get(id)) return;
|
|
2694
|
+
const component = [];
|
|
2695
|
+
for (; ; ) {
|
|
2696
|
+
const top = stack.pop();
|
|
2697
|
+
onStack.delete(top);
|
|
2698
|
+
component.push(top);
|
|
2699
|
+
if (top === id) break;
|
|
2700
|
+
}
|
|
2701
|
+
const inCycle = new Set(component);
|
|
2702
|
+
const cyclic = component.length > 1 || cycleEdges(steps[id], id, issues).includes(id);
|
|
2703
|
+
if (!cyclic) return;
|
|
2704
|
+
if (component.some((member) => stepLeavesCycle(steps, member, inCycle, issues))) return;
|
|
2705
|
+
found = true;
|
|
2706
|
+
};
|
|
2707
|
+
if (Object.prototype.hasOwnProperty.call(steps, start)) strong(start);
|
|
2708
|
+
return found;
|
|
2709
|
+
}
|
|
2561
2710
|
|
|
2562
2711
|
// src/validators/nextLogic.ts
|
|
2563
2712
|
var NEXT_NULL = "null";
|