Skip to content

Subflow Type Error & Loops and Cycles Ignore Edges #7

@hesprs

Description

@hesprs

Scenario

In my case, I simply want to execute a multi-node loop that exits when certain conditions are met.

However, I found that the edges seem to be ignored inside a loop.

Then I tried to workaround by using cyclic edges + conditional branching (which is discouraged by the documentation). But the workflow terminates before the loop.

Then I tried using a subflow so that I only need one node in the parent flow. When I did so, I got both type errors and runtime errors.

I followed everything relevant in the documentation about subflows and loops. If I truly dismissed something in the documentation, please point out.

How to Reproduce

Loop Error

import { createFlow, FlowRuntime, createDefaultContainer, UnsafeEvaluator } from 'flowcraft';

import { A, D, E, F } from './nodes.ts';

const flow = createFlow('myFlow')
	.node('A', A)
	.node('D', D)
	.node('E', E)
	.node('F', F)
	.edge('A', 'D')
	.edge('D', 'E')
	.edge('E', 'F')
        .loop('DEF', {
            startNodeId: 'D',
            endNodeId: 'F',
            condition: 'map1 !== 0 && map2 < 3',
        });

const container = createDefaultContainer({
	eventBus: {
		emit: (e) => {
			if (e.type === 'node:start')
				console.log(`run: ${e.payload.nodeId}`);
		},
	},
        evaluator: new UnsafeEvaluator(),
});
const runtime = new FlowRuntime(container);


export default async function run() {
	const blueprint = flow.toBlueprint();
	const functionRegistry = flow.getFunctionRegistry();
	await runtime.run(
		blueprint,
		{ input: 'input' },
		{ functionRegistry },
	);
}

Output: (the condition is mocked to be true for 3 times so that the loop should continue)

run: A
run: D
run: E
run: F
run: DEF
run: D

The execution always stops at the first node of the second loop cycle. (in this case, D)

Conditional Branching Error

import { createFlow, FlowRuntime, createDefaultContainer, UnsafeEvaluator } from 'flowcraft';

import { A, D, E, F, G } from './nodes.ts';

const flow = createFlow('myFlow')
	.node('A', A)
	.node('D', D)
	.node('E', E)
	.node('F', F)
        .node('G', G)
	.edge('A', 'D')
	.edge('D', 'E')
	.edge('E', 'F')
        .edge('F', 'D', { action: 'failure' })
        .edge('F', 'G', { action: 'success' });

const container = createDefaultContainer({
	eventBus: {
		emit: (e) => {
			if (e.type === 'node:start')
				console.log(`run: ${e.payload.nodeId}`);
		},
	},
        evaluator: new UnsafeEvaluator(),
});
const runtime = new FlowRuntime(container);


export default async function run() {
	const blueprint = flow.toBlueprint();
	const functionRegistry = flow.getFunctionRegistry();
	await runtime.run(
		blueprint,
		{ input: 'input' },
		{ functionRegistry },
	);
}

Output: (in my test, I mocked 3 'failure' and a final 'success', so it should loop for 3 times)

run: A

Only the nodes before the cycle are executed. (in this case, A only)

Subflow Error

Then I tried to use a subflow so that I could avoid multi-node loops. However, the situation is worse:

import { createFlow, FlowRuntime, createDefaultContainer, UnsafeEvaluator } from 'flowcraft';

import { A, D, E, F } from './nodes.ts';

const flow = createFlow('myFlow')
	.node('A', A)
        .node('sub', {
		uses: 'subflow',
		params: {
			blueprintId: 'mySubflow',
			// Map parent context keys to subflow context keys
			inputs: {
				map1: 'map1',
                                map2: 'map2',
			},
		},
	})
        .edge('A', 'sub')
	.loop('loop', {
		startNodeId: 'sub',
		endNodeId: 'sub',
		condition: 'map1 !== 0 && map2 < 3',
	});

const subflow = createFlow('mySubflow')
	.node('D', D)
	.node('E', E)
	.node('F', F)
	.edge('D', 'E')
	.edge('E', 'F');

const container = createDefaultContainer({
	eventBus: {
		emit: (e) => {
			if (e.type === 'node:start')
			console.log(`run: ${e.payload.nodeId}`);
		},
	},
	evaluator: new UnsafeEvaluator(),
	blueprints: { 'mySubflow': subflow.toBlueprint() },
});
const runtime = new FlowRuntime(container);

export default async function run() {
	const blueprint = flow.toBlueprint();
	const functionRegistry = flow.getFunctionRegistry();
	await runtime.run(
		blueprint,
		{ input: 'input' },
		{ functionRegistry },
	);
}

Then I got both type error and the subsequent runtime error, the type error is:

Image
Object literal may only specify known properties, and 'uses' does not exist in type 'NodeFunction<CTX, Record<string, any>, any, any, string> | NodeClass<CTX, Record<string, any>, any, any, string>'.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions