Skip to content

Commit f676136

Browse files
committed
split-case support for expresssions (optional) and openai call-tool support with streaming in a flow
1 parent 56bd27c commit f676136

File tree

8 files changed

+1278
-9
lines changed

8 files changed

+1278
-9
lines changed

examples-test-flows/openai-fetch-completion-as-stream-with-toolcall.json

Lines changed: 1180 additions & 0 deletions
Large diffs are not rendered by default.

libs/app-canvas/src/app/flow-app.element.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,7 @@ export class FlowAppElement extends AppElement<NodeInfo> {
16311631
}
16321632

16331633
if (
1634+
node.nodeInfo.hasNoFormPopup ||
16341635
getFollowNodeExecution() ||
16351636
(formElements.length <= 1 &&
16361637
!(

libs/visual-programming-system/src/types/base-node-info.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export interface BaseNodeInfo {
9292
initializeCompute?: () => void;
9393
showFormOnlyInPopup?: boolean;
9494
isSettingsPopup?: boolean;
95+
hasNoFormPopup?: boolean;
9596
formElements?: any[];
9697
delete?: () => void;
9798
formValues?: any;

libs/visual-programming-system/src/utils/getFormattedValue.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,19 @@ export const getFormattedValue = (
1616
}
1717
return '-';
1818
};
19+
20+
export const getFormattedVariableValue = (
21+
value: any,
22+
decimalCount: number,
23+
append: string
24+
) => {
25+
const appendText = `${append ? ` ${append}` : ''}`;
26+
if (typeof value === 'number') {
27+
return `${(value as number).toFixed(decimalCount)}${appendText}`;
28+
} else if (typeof value === 'string') {
29+
return `${value}${appendText}`;
30+
} else {
31+
return `${(value as any).toString()}${appendText}`;
32+
}
33+
return '-';
34+
};

libs/web-flow-executor/src/nodes/expression.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ export const getExpression: NodeTaskFactory<NodeInfo> = (
9595
currentValue = 0;
9696
executionRunCounter = 0;
9797
expressionCache[node.id] = undefined;
98+
if (errorNode) {
99+
(errorNode.domElement as unknown as HTMLElement).classList.add('hidden');
100+
}
98101
return;
99102
};
100103

libs/web-flow-executor/src/nodes/scoped-variable.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import {
33
createElement,
44
createNodeElement,
55
FormFieldType,
6-
getFormattedValue,
76
IElementNode,
87
InitialValues,
98
INodeComponent,
109
IRectNodeComponent,
1110
NodeTask,
11+
getFormattedVariableValue,
1212
} from '@devhelpr/visual-programming-system';
1313
import { NodeInfo } from '../types/node-info';
1414
import { showDictionaryData } from './data-viewers/dictionary';
@@ -492,7 +492,9 @@ export const getScopedVariable =
492492
if (fieldType === 'value') {
493493
if (htmlNode?.domElement) {
494494
(htmlNode?.domElement as unknown as HTMLElement).innerHTML =
495-
getLabelAsHtml(getFormattedValue(lastStoredDataState, 2, ''));
495+
getLabelAsHtml(
496+
getFormattedVariableValue(lastStoredDataState, 2, '')
497+
);
496498
}
497499
if (rect && rect.resize) {
498500
rect.resize(120);

libs/web-flow-executor/src/nodes/split-by-case.ts

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,28 @@ import { NodeInfo } from '../types/node-info';
1515

1616
import { RunCounter } from '../follow-path/run-counter';
1717
import { runNodeFromThumb } from '../flow-engine/flow-engine';
18+
import {
19+
compileExpressionAsInfo,
20+
runExpression,
21+
} from '@devhelpr/expression-compiler';
22+
import { getVariablePayloadInputUtils } from './variable-payload-input-utils.ts/variable-payload-input-utils';
23+
24+
function handleExpression(expression: string, payload: any) {
25+
const compiledExpression = compileExpressionAsInfo(expression);
26+
const expressionFunction = (
27+
new Function('payload', `${compiledExpression.script}`) as unknown as (
28+
payload?: any
29+
) => any
30+
).bind(compiledExpression.bindings);
31+
32+
const result = runExpression(
33+
expressionFunction,
34+
payload,
35+
false,
36+
compiledExpression.payloadProperties
37+
);
38+
return Boolean(result);
39+
}
1840

1941
export const getSplitByCase = (updated: () => void): NodeTask<NodeInfo> => {
2042
let node: IRectNodeComponent<NodeInfo>;
@@ -26,7 +48,7 @@ export const getSplitByCase = (updated: () => void): NodeTask<NodeInfo> => {
2648
const computeAsync = (
2749
input: string,
2850
loopIndex?: number,
29-
_payload?: any,
51+
payload?: any,
3052
_thumbName?: string,
3153
scopeId?: string,
3254
runCounterCompute?: RunCounter
@@ -47,10 +69,30 @@ export const getSplitByCase = (updated: () => void): NodeTask<NodeInfo> => {
4769
const case1 = node?.nodeInfo?.formValues?.['case1'] ?? '';
4870
const case2 = node?.nodeInfo?.formValues?.['case2'] ?? '';
4971
const case3 = node?.nodeInfo?.formValues?.['case3'] ?? '';
72+
const useExpression =
73+
node?.nodeInfo?.formValues?.['useExpression'] ?? false;
5074

5175
let thumbNode: IThumbNodeComponent<NodeInfo> | undefined = undefined;
5276
if (input !== 'true') {
53-
if (typeof inputAsString === 'string') {
77+
if (useExpression) {
78+
const payloadForExpression = getVariablePayloadInputUtils(
79+
input,
80+
payload,
81+
'string',
82+
-1,
83+
-1,
84+
scopeId,
85+
canvasAppInstance
86+
);
87+
88+
if (handleExpression(case1, payloadForExpression)) {
89+
thumbNode = node.thumbConnectors[0];
90+
} else if (handleExpression(case2, payloadForExpression)) {
91+
thumbNode = node.thumbConnectors[1];
92+
} else if (handleExpression(case3, payloadForExpression)) {
93+
thumbNode = node.thumbConnectors[2];
94+
}
95+
} else if (typeof inputAsString === 'string') {
5496
if (case1 && inputAsString === case1) {
5597
thumbNode = node.thumbConnectors[0];
5698
} else if (case2 && inputAsString === case2) {
@@ -101,7 +143,9 @@ export const getSplitByCase = (updated: () => void): NodeTask<NodeInfo> => {
101143
y: number,
102144
id?: string,
103145
initalValues?: InitialValues,
104-
containerNode?: IRectNodeComponent<NodeInfo>
146+
containerNode?: IRectNodeComponent<NodeInfo>,
147+
width?: number,
148+
height?: number
105149
) => {
106150
canvasAppInstance = canvasApp;
107151
const formElements = [
@@ -160,6 +204,25 @@ export const getSplitByCase = (updated: () => void): NodeTask<NodeInfo> => {
160204
case3: value,
161205
};
162206

207+
if (updated) {
208+
updated();
209+
}
210+
},
211+
},
212+
{
213+
fieldType: FormFieldType.Checkbox,
214+
fieldName: 'useExpression',
215+
label: 'Use expressions',
216+
value: initalValues?.['useExpression'] ?? 'false',
217+
onChange: (value: boolean) => {
218+
if (!node.nodeInfo) {
219+
return;
220+
}
221+
node.nodeInfo.formValues = {
222+
...node.nodeInfo.formValues,
223+
useExpression: value,
224+
};
225+
163226
if (updated) {
164227
updated();
165228
}
@@ -187,8 +250,8 @@ export const getSplitByCase = (updated: () => void): NodeTask<NodeInfo> => {
187250
const rect = canvasApp.createRect(
188251
x,
189252
y,
190-
200,
191-
110,
253+
width ?? 200,
254+
height ?? 110,
192255
undefined,
193256
[
194257
{
@@ -233,7 +296,7 @@ export const getSplitByCase = (updated: () => void): NodeTask<NodeInfo> => {
233296
{
234297
classNames: `bg-slate-500 p-4 rounded`,
235298
},
236-
false,
299+
true,
237300
undefined,
238301
undefined,
239302
id,
@@ -252,6 +315,9 @@ export const getSplitByCase = (updated: () => void): NodeTask<NodeInfo> => {
252315
node.nodeInfo.formElements = formElements;
253316
node.nodeInfo.computeAsync = computeAsync;
254317
node.nodeInfo.initializeCompute = initializeCompute;
318+
node.nodeInfo.showFormOnlyInPopup = false;
319+
node.nodeInfo.hasNoFormPopup = true;
320+
node.nodeInfo.isSettingsPopup = false;
255321
}
256322
return node;
257323
},

libs/web-flow-executor/src/nodes/user-text-input.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ export const getUserTextInput =
219219
x,
220220
y,
221221
width ?? 200,
222-
height ?? 100,
222+
height ?? 150,
223223
undefined,
224224
[
225225
{

0 commit comments

Comments
 (0)