Skip to content

Commit 696e00b

Browse files
committed
openai using fetch api , new raw-json node-type and memory-only apikey configuration
1 parent edbcb89 commit 696e00b

File tree

7 files changed

+364
-26
lines changed

7 files changed

+364
-26
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
{
2+
"schemaType": "flow",
3+
"schemaVersion": "0.0.1",
4+
"id": "1234",
5+
"flows": {
6+
"flow": {
7+
"flowType": "flow",
8+
"nodes": [
9+
{
10+
"id": "8f4ac174-5c8e-45da-8ad4-a81a40f4f666",
11+
"x": 3287.947143039206,
12+
"y": 1054.04668496768,
13+
"width": 200,
14+
"height": 155,
15+
"nodeType": "Shape",
16+
"nodeInfo": {
17+
"type": "fetch",
18+
"formValues": {
19+
"url": "https://api.openai.com/v1/chat/completions",
20+
"headers": "Authorization: Bearer [openai-key]",
21+
"http-method": "post"
22+
},
23+
"isSettingsPopup": true
24+
}
25+
},
26+
{
27+
"id": "3cf44538-9913-4aa2-aade-a7c619910f45",
28+
"x": 2871.977300992098,
29+
"y": 1064.5111606731102,
30+
"width": 161.1015625,
31+
"height": 136,
32+
"nodeType": "Shape",
33+
"nodeInfo": {
34+
"type": "json-node",
35+
"formValues": {
36+
"json": "{\n \"messages\": [\n { \n \"role\": \"user\", \n \"content\": \"Tell me a joke\" \n }\n ],\n \"temperature\": 0.6,\n \"model\": \"gpt-4-turbo\",\n \"max_tokens\": 30\n}"
37+
},
38+
"nodeCannotBeReplaced": true,
39+
"showFormOnlyInPopup": true,
40+
"useInCompositionOnly": false,
41+
"keepPopupOpenAfterUpdate": true,
42+
"isRunOnStart": true
43+
}
44+
},
45+
{
46+
"id": "d186a002-218b-47dc-aae8-07522a3da6ed",
47+
"x": 3670.3006812979065,
48+
"y": 1053.8064850250428,
49+
"width": 119.99989187093509,
50+
"height": 271.9999386533287,
51+
"nodeType": "Shape",
52+
"nodeInfo": {
53+
"type": "show-input",
54+
"formValues": {
55+
"name": "",
56+
"data-type": "default"
57+
},
58+
"initializeOnStartFlow": true,
59+
"isSettingsPopup": true
60+
}
61+
},
62+
{
63+
"id": "1a35b609-3594-4529-bec1-2b50b205aa0d",
64+
"x": 3033.078863492098,
65+
"y": 1132.5111606731102,
66+
"endX": 3287.947143039206,
67+
"endY": 1131.54668496768,
68+
"startNodeId": "3cf44538-9913-4aa2-aade-a7c619910f45",
69+
"endNodeId": "8f4ac174-5c8e-45da-8ad4-a81a40f4f666",
70+
"startThumbName": "input",
71+
"endThumbName": "input",
72+
"lineType": "BezierCubic",
73+
"nodeType": "Connection",
74+
"layer": 1,
75+
"nodeInfo": {}
76+
},
77+
{
78+
"id": "a16c5c54-c867-41af-a922-9a014786c513",
79+
"x": 3487.947143039206,
80+
"y": 1084.04668496768,
81+
"endX": 3670.3006812979065,
82+
"endY": 1083.8064850250428,
83+
"startNodeId": "8f4ac174-5c8e-45da-8ad4-a81a40f4f666",
84+
"endNodeId": "d186a002-218b-47dc-aae8-07522a3da6ed",
85+
"startThumbName": "output",
86+
"endThumbName": "input",
87+
"lineType": "BezierCubic",
88+
"nodeType": "Connection",
89+
"layer": 1,
90+
"nodeInfo": {}
91+
}
92+
]
93+
}
94+
},
95+
"compositions": {}
96+
}

libs/app-canvas/src/app/components/node-sidebar-menu.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
setFollowNodeExecution,
2323
} from '../follow-path/followNodeExecution';
2424
import { NodeInfo } from '@devhelpr/web-flow-executor';
25+
import { createInputDialog } from '../utils/create-input-dialog';
2526

2627
export class NodeSidebarMenuComponent extends Component<
2728
AppNavComponentsProps<NodeInfo>
@@ -39,6 +40,7 @@ export class NodeSidebarMenuComponent extends Component<
3940
switchLayerButton: HTMLButtonElement | null = null;
4041
toggleDependencyConnections: HTMLButtonElement | null = null;
4142
followNodeExecution: HTMLButtonElement | null = null;
43+
apikeysButton: HTMLButtonElement | null = null;
4244
showDependencyConnections = false;
4345

4446
constructor(
@@ -54,6 +56,7 @@ export class NodeSidebarMenuComponent extends Component<
5456
<button title="Toggle between layer 1 and 2" class="${navBarButtonNomargin} flex items-center justify-center w-[32px] h-[32px] mb-1"><span class="icon icon-layers text-[22px]"></span></button>
5557
<button title="Show node dependencies" class="${navBarButtonNomargin} flex items-center justify-center w-[32px] h-[32px] mb-1"><span class="icon icon-arrow_forward text-[22px]"></span></button>
5658
<button title="Follow node execution" class="${navBarButtonNomargin} flex items-center justify-center w-[32px] h-[32px] hidden">F</button>
59+
<button title="API Keys(just openai for now)" class="${navBarButtonNomargin} flex items-center justify-center w-[32px] h-[32px] mb-1"><span class="icon icon-vpn_key text-[22px]"></span></button>
5760
5861
<children></children>
5962
</div>`
@@ -85,6 +88,9 @@ export class NodeSidebarMenuComponent extends Component<
8588
this.followNodeExecution = this.toggleDependencyConnections
8689
?.nextSibling as HTMLButtonElement;
8790

91+
this.apikeysButton = this.followNodeExecution
92+
?.nextSibling as HTMLButtonElement;
93+
8894
this.settingsNodeButton.addEventListener(
8995
'click',
9096
this.onClickSettingsNode
@@ -113,13 +119,16 @@ export class NodeSidebarMenuComponent extends Component<
113119
this.onClickFollowNodeExecution
114120
);
115121

122+
this.apikeysButton.addEventListener('click', this.onClickAPIKeys);
123+
116124
this.renderList.push(
117125
this.settingsNodeButton,
118126
this.placeOnLayer1Button,
119127
this.placeOnLayer2Button,
120128
this.switchLayerButton,
121129
this.toggleDependencyConnections,
122-
this.followNodeExecution
130+
this.followNodeExecution,
131+
this.apikeysButton
123132
);
124133
this.rootElement.append(this.element);
125134
}
@@ -353,6 +362,42 @@ export class NodeSidebarMenuComponent extends Component<
353362
return false;
354363
};
355364

365+
onClickAPIKeys = (event: Event) => {
366+
event.preventDefault();
367+
if (!this.rootAppElement) {
368+
return false;
369+
}
370+
const canvasApp = this.props.getCanvasApp();
371+
if (!canvasApp) {
372+
return false;
373+
}
374+
const openAIKey = canvasApp.getTempData('openai-key') ?? '';
375+
createInputDialog(
376+
this.rootAppElement,
377+
'Openai-key',
378+
openAIKey,
379+
(_name) => {
380+
return {
381+
valid: true,
382+
};
383+
},
384+
{
385+
isPassword: true,
386+
}
387+
).then((value) => {
388+
if (value === false) {
389+
return;
390+
}
391+
const canvasApp = this.props.getCanvasApp();
392+
if (!canvasApp) {
393+
return;
394+
}
395+
canvasApp.setTempData('openai-key', value);
396+
console.log('openai-key value', value);
397+
});
398+
return false;
399+
};
400+
356401
render() {
357402
super.render();
358403

libs/app-canvas/src/app/utils/create-input-dialog.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,23 @@ export const createInputDialog = (
77
rootElement: HTMLElement,
88
nameLabel: string,
99
defaultValue?: string,
10-
onValidate?: (value: string) => { valid: boolean; message?: string }
10+
onValidate?: (value: string) => { valid: boolean; message?: string },
11+
settings?: {
12+
isPassword?: boolean;
13+
}
1114
) => {
1215
return new Promise<string | false>((resolve, reject) => {
1316
//let wasTouched = false;
1417
let submitWasClicked = false;
1518
const nameId = crypto.randomUUID();
1619
const formElement = createElementFromHtml(
17-
`<div class="input-dialog-form flex flex-col">
20+
`<div class="input-dialog-form flex flex-col p-8">
1821
<form cmethod="dialog" class="form row-1">
1922
<div class="flex flex-col w-full my-2">
2023
<label for="${nameId}__input">${nameLabel} *</label>
21-
<input id="${nameId}__input" class="form-input w-full" name="${nameId}" required value="${
22-
defaultValue ?? ''
23-
}"></input>
24+
<input id="${nameId}__input" class="form-input w-full" name="${nameId}" type="${
25+
settings?.isPassword ? 'password' : 'text'
26+
}" required value="${defaultValue ?? ''}"></input>
2427
<div class="form-error"></div>
2528
</div>
2629
<div class="flex w-full flex-row justify-end gap-2">

libs/visual-programming-system/src/canvas-app/flow-core.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ export interface IFlowCore {
123123
commandName: string,
124124
data: any
125125
) => void;
126+
127+
setTempData: (key: string, value: any) => void;
128+
getTempData: (key: string) => any;
126129
}
127130

128131
export class FlowCore implements IFlowCore {
@@ -144,6 +147,7 @@ export class FlowCore implements IFlowCore {
144147
protected nodeSetStateHandlers: Record<string, SetNodeStatedHandler> = {};
145148
protected nodeGetStateHandlers: Record<string, GetNodeStatedHandler> = {};
146149
protected tempVariables: Record<string, any> = {};
150+
protected tempData: Record<string, any> = {};
147151
protected onNodeMessage: ((keyName: string, value: any) => void) | undefined =
148152
undefined;
149153

@@ -436,4 +440,11 @@ export class FlowCore implements IFlowCore {
436440
this.commandHandlers[name].execute(commandName, data);
437441
}
438442
};
443+
444+
setTempData = (key: string, value: any) => {
445+
this.tempData[key] = value;
446+
};
447+
getTempData = (key: string) => {
448+
return this.tempData[key];
449+
};
439450
}

libs/web-flow-executor/src/node-task-registry/canvas-node-task-registry.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ import { loadCSVFile, loadCSVFileNodeName } from '../nodes/load-csv-file';
210210
import { filterNodeName, getFilter } from '../nodes/filter';
211211
import { loadJSONFile, loadJSONFileNodeName } from '../nodes/load-json-file';
212212
import { getReduce, reduceNodeName } from '../nodes/reduce';
213+
import { getRawJsonNode, jsonNodeName } from '../nodes/raw-json';
213214

214215
export const canvasNodeTaskRegistry: NodeTypeRegistry<NodeInfo> = {};
215216
export const canvasNodeTaskRegistryLabels: Record<string, string> = {};
@@ -442,6 +443,8 @@ export const setupCanvasNodeTaskRegistry = (
442443
registerExpressionFunctionNodeName,
443444
getRegisterExpressionFunctionNode
444445
);
446+
447+
registerNodeFactory(jsonNodeName, getRawJsonNode);
445448
}
446449

447450
registerExternalNodes?.(registerNodeFactory);

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

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,20 @@ export const getFetch: NodeTaskFactory<NodeInfo> = (
109109
node?.nodeInfo?.formValues?.['http-method'] ?? 'post';
110110

111111
const headers = new Headers();
112+
const headersString = node?.nodeInfo?.formValues?.['headers'] ?? '';
113+
if (headersString) {
114+
const headersArray = headersString.split('\n');
115+
const openAIKey = canvasAppInstance?.isContextOnly
116+
? process.env?.['openai_api_key'] ?? ''
117+
: canvasAppInstance?.getTempData('openai-key') ?? '';
118+
headersArray.forEach((header: string) => {
119+
const headerArray = header.split(':');
120+
if (headerArray.length === 2) {
121+
const value = headerArray[1].replace('[openai-key]', openAIKey);
122+
headers.append(headerArray[0], value);
123+
}
124+
});
125+
}
112126
if (responseType === 'json') {
113127
headers.append('Content-Type', 'application/json');
114128
}
@@ -183,25 +197,6 @@ export const getFetch: NodeTaskFactory<NodeInfo> = (
183197
const url = initalValues?.['url'] ?? '';
184198

185199
canvasAppInstance = canvasApp;
186-
const formElements = [
187-
{
188-
fieldType: FormFieldType.Text,
189-
fieldName: 'url',
190-
value: url ?? '',
191-
onChange: (value: string) => {
192-
if (!node.nodeInfo) {
193-
return;
194-
}
195-
node.nodeInfo.formValues = {
196-
...node.nodeInfo.formValues,
197-
url: value,
198-
};
199-
if (updated) {
200-
updated();
201-
}
202-
},
203-
},
204-
];
205200

206201
const jsxComponentWrapper = createElement(
207202
'div',
@@ -308,7 +303,23 @@ export const getFetch: NodeTaskFactory<NodeInfo> = (
308303
node = rect.nodeComponent;
309304
if (node.nodeInfo) {
310305
node.nodeInfo.formElements = [
311-
...formElements,
306+
{
307+
fieldType: FormFieldType.Text,
308+
fieldName: 'url',
309+
value: url ?? '',
310+
onChange: (value: string) => {
311+
if (!node.nodeInfo) {
312+
return;
313+
}
314+
node.nodeInfo.formValues = {
315+
...node.nodeInfo.formValues,
316+
url: value,
317+
};
318+
if (updated) {
319+
updated();
320+
}
321+
},
322+
},
312323
{
313324
fieldType: FormFieldType.Select,
314325
fieldName: 'http-method',
@@ -357,6 +368,24 @@ export const getFetch: NodeTaskFactory<NodeInfo> = (
357368
}
358369
},
359370
},
371+
{
372+
fieldType: FormFieldType.TextArea,
373+
fieldName: 'headers',
374+
label: 'Headers',
375+
value: initalValues?.['headers'] ?? '',
376+
onChange: (value: string) => {
377+
if (!node || !node.nodeInfo) {
378+
return;
379+
}
380+
node.nodeInfo.formValues = {
381+
...node.nodeInfo.formValues,
382+
['headers']: value,
383+
};
384+
if (updated) {
385+
updated();
386+
}
387+
},
388+
},
360389
];
361390
node.nodeInfo.computeAsync = computeAsync;
362391
node.nodeInfo.initializeCompute = initializeCompute;

0 commit comments

Comments
 (0)