From df3ce5a3d885cd97868c488fa5c7b0eeccf6bdb6 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Tue, 26 Sep 2023 12:07:31 -0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(parameterComponent):=20add?= =?UTF-8?q?=20support=20for=20'int'=20type=20in=20conditional=20rendering?= =?UTF-8?q?=20to=20display=20a=20component=20=E2=9C=A8=20feat(parameterCom?= =?UTF-8?q?ponent):=20add=20support=20for=20'dict'=20and=20'NestedDict'=20?= =?UTF-8?q?types=20in=20conditional=20rendering=20to=20display=20a=20compo?= =?UTF-8?q?nent=20=F0=9F=90=9B=20fix(codeTabsComponent):=20import=20missin?= =?UTF-8?q?g=20functions=20from=20reactflowUtils=20to=20fix=20build=20erro?= =?UTF-8?q?r=20=E2=9C=A8=20feat(codeTabsComponent):=20add=20support=20for?= =?UTF-8?q?=20'dict'=20and=20'NestedDict'=20types=20in=20conditional=20ren?= =?UTF-8?q?dering=20to=20display=20a=20component=20=E2=9C=A8=20feat(codeTa?= =?UTF-8?q?bsComponent):=20add=20DictComponent=20and=20KeypairListComponen?= =?UTF-8?q?t=20to=20handle=20'dict'=20and=20'NestedDict'=20types=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(ApiModal):=20import=20missing=20function=20f?= =?UTF-8?q?rom=20reactflowUtils=20to=20fix=20build=20error=20=E2=9C=A8=20f?= =?UTF-8?q?eat(ApiModal):=20add=20support=20for=20'dict'=20and=20'NestedDi?= =?UTF-8?q?ct'=20types=20in=20conditional=20rendering=20to=20display=20a?= =?UTF-8?q?=20component=20=F0=9F=90=9B=20fix(types):=20add=20Object=20type?= =?UTF-8?q?=20to=20buildTweakObject=20function=20in=20codeTabsPropsType=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(reactflowUtils):=20add=20missing=20convertAr?= =?UTF-8?q?rayToObj=20function=20to=20convert=20array=20of=20objects=20to?= =?UTF-8?q?=20object=20=E2=9C=A8=20feat(reactflowUtils):=20add=20convertAr?= =?UTF-8?q?rayToObj=20function=20to=20convert=20array=20of=20objects=20to?= =?UTF-8?q?=20object?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 fix(utils.ts): fix indentation and formatting issues in groupByFamily function 🐛 fix(utils.ts): fix indentation and formatting issues in buildTweakObject function 🐛 fix(utils.ts): fix indentation and formatting issues in getPythonApiCode function 🐛 fix(utils.ts): fix indentation and formatting issues in getCurlCode function 🐛 fix(utils.ts): fix indentation and formatting issues in getPythonCode function 🐛 fix(utils.ts): fix indentation and formatting issues in getChatComponentCode function --- .../components/parameterComponent/index.tsx | 4 +- .../components/codeTabsComponent/index.tsx | 137 +++++++++++++++++- src/frontend/src/modals/ApiModal/index.tsx | 12 +- src/frontend/src/types/components/index.ts | 26 ++-- src/frontend/src/utils/reactflowUtils.ts | 24 ++- src/frontend/src/utils/utils.ts | 57 +++----- 6 files changed, 201 insertions(+), 59 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index cef56c343..68c975764 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -268,9 +268,9 @@ export default function ParameterComponent({ type === "code" || type === "prompt" || type === "file" || + type === "int" || type === "dict" || - type === "NestedDict" || - type === "int") && + type === "NestedDict") && !optionalHandle ? ( <> ) : ( diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx index e29c8762b..5500de48e 100644 --- a/src/frontend/src/components/codeTabsComponent/index.tsx +++ b/src/frontend/src/components/codeTabsComponent/index.tsx @@ -31,9 +31,16 @@ import { import { darkContext } from "../../contexts/darkContext"; import { typesContext } from "../../contexts/typesContext"; import { codeTabsPropsType } from "../../types/components"; -import { unselectAllNodes } from "../../utils/reactflowUtils"; +import { + convertObjToArray, + convertValuesToNumbers, + hasDuplicateKeys, + unselectAllNodes, +} from "../../utils/reactflowUtils"; import { classNames } from "../../utils/utils"; +import DictComponent from "../dictComponent"; import IconComponent from "../genericIconComponent"; +import KeypairListComponent from "../keypairListComponent"; export default function CodeTabsComponent({ flow, @@ -48,6 +55,7 @@ export default function CodeTabsComponent({ const [openAccordion, setOpenAccordion] = useState([]); const { dark } = useContext(darkContext); const { reactFlowInstance } = useContext(typesContext); + const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); useEffect(() => { if (flow && flow["data"]!["nodes"]) { @@ -248,7 +256,11 @@ export default function CodeTabsComponent({ node.data.node.template[templateField] .type === "file" || node.data.node.template[templateField] - .type === "int") + .type === "int" || + node.data.node.template[templateField] + .type === "dict" || + node.data.node.template[templateField] + .type === "NestedDict") ) .map((templateField, indx) => { return ( @@ -725,6 +737,127 @@ export default function CodeTabsComponent({ /> + ) : node.data.node.template[ + templateField + ].type === "dict" ? ( + +
+ { + const valueToNumbers = + convertValuesToNumbers( + target + ); + node.data.node!.template[ + templateField + ].value = valueToNumbers; + setErrorDuplicateKey( + hasDuplicateKeys( + valueToNumbers + ) + ); + setData((old) => { + let newInputList = + cloneDeep(old); + newInputList![ + i + ].data.node.template[ + templateField + ].value = target; + return newInputList; + }); + tweaks.buildTweakObject!( + node["data"]["id"], + target, + node.data.node.template[ + templateField + ] + ); + }} + /> +
+
+ ) : node.data.node.template[ + templateField + ].type === "NestedDict" ? ( + +
+ { + setData((old) => { + let newInputList = + cloneDeep(old); + newInputList![ + i + ].data.node.template[ + templateField + ].value = target; + return newInputList; + }); + tweaks.buildTweakObject!( + node["data"]["id"], + target, + node.data.node.template[ + templateField + ] + ); + }} + /> +
+
) : node.data.node.template[ templateField ].type === "Any" ? ( diff --git a/src/frontend/src/modals/ApiModal/index.tsx b/src/frontend/src/modals/ApiModal/index.tsx index e2ffa00ca..49962500a 100644 --- a/src/frontend/src/modals/ApiModal/index.tsx +++ b/src/frontend/src/modals/ApiModal/index.tsx @@ -19,7 +19,7 @@ import { TabsContext } from "../../contexts/tabsContext"; import { TemplateVariableType } from "../../types/api"; import { tweakType, uniqueTweakType } from "../../types/components"; import { FlowType, NodeType } from "../../types/flow/index"; -import { buildTweaks } from "../../utils/reactflowUtils"; +import { buildTweaks, convertArrayToObj } from "../../utils/reactflowUtils"; import { getCurlCode, getPythonApiCode, @@ -105,7 +105,9 @@ const ApiModal = forwardRef( node.data.node.template[templateField].type === "code" || node.data.node.template[templateField].type === "prompt" || node.data.node.template[templateField].type === "file" || - node.data.node.template[templateField].type === "int") + node.data.node.template[templateField].type === "int" || + node.data.node.template[templateField].type === "dict" || + node.data.node.template[templateField].type === "NestedDict") ) .map((n, i) => { arrNodesWithValues.push(node["id"]); @@ -118,7 +120,7 @@ const ApiModal = forwardRef( } function buildTweakObject( tw: string, - changes: string | string[] | boolean | number, + changes: string | string[] | boolean | number | Object[] | Object, template: TemplateVariableType ) { if (typeof changes === "string" && template.type === "float") { @@ -131,6 +133,10 @@ const ApiModal = forwardRef( changes = changes?.filter((x) => x !== ""); } + if (template.type === "dict") { + changes = convertArrayToObj(changes); + } + const existingTweak = tweak.current.find((element) => element.hasOwnProperty(tw) ); diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index be442a4d1..5db600627 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -134,18 +134,18 @@ export type TooltipComponentType = { children: ReactElement; title: string | ReactElement; placement?: - | "bottom-end" - | "bottom-start" - | "bottom" - | "left-end" - | "left-start" - | "left" - | "right-end" - | "right-start" - | "right" - | "top-end" - | "top-start" - | "top"; + | "bottom-end" + | "bottom-start" + | "bottom" + | "left-end" + | "left-start" + | "left" + | "right-end" + | "right-start" + | "right" + | "top-end" + | "top-start" + | "top"; }; export type ProgressBarType = { @@ -549,7 +549,7 @@ export type codeTabsPropsType = { ) => string; buildTweakObject?: ( tw: string, - changes: string | string[] | boolean | number, + changes: string | string[] | boolean | number | Object[] | Object, template: TemplateVariableType ) => string | void; }; diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index df8c784c4..b9eca1588 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -220,8 +220,7 @@ export function validateNode( ) ) { errors.push( - `${type} is missing ${ - template.display_name || toNormalCase(template[t].name) + `${type} is missing ${template.display_name || toNormalCase(template[t].name) }.` ); } else if ( @@ -234,14 +233,12 @@ export function validateNode( ) { if (hasDuplicateKeys(template[t].value)) errors.push( - `${type} (${ - template.display_name || template[t].name + `${type} (${template.display_name || template[t].name }) contains duplicate keys with the same values.` ); if (hasEmptyKey(template[t].value)) errors.push( - `${type} (${ - template.display_name || template[t].name + `${type} (${template.display_name || template[t].name }) field must not be empty.` ); } @@ -325,6 +322,21 @@ export function convertObjToArray(singleObject) { return arrConverted; } +export function convertArrayToObj(arrayOfObjects) { + if (!Array.isArray(arrayOfObjects)) return arrayOfObjects; + + let objConverted = {}; + for (const obj of arrayOfObjects) { + for (const key in obj) { + if (obj.hasOwnProperty(key)) { + objConverted[key] = obj[key]; + } + } + } + return objConverted; +} + + export function hasDuplicateKeys(array) { const keys = {}; for (const obj of array) { diff --git a/src/frontend/src/utils/utils.ts b/src/frontend/src/utils/utils.ts index 111b1ce51..8ef65d140 100644 --- a/src/frontend/src/utils/utils.ts +++ b/src/frontend/src/utils/utils.ts @@ -186,13 +186,13 @@ export function groupByFamily( return left ? arrOfPossibleOutputs.map((output) => ({ - family: output.category, - type: output.full ? "" : output.nodes.join(", "), - })) + family: output.category, + type: output.full ? "" : output.nodes.join(", "), + })) : arrOfPossibleInputs.map((input) => ({ - family: input.category, - type: input.full ? "" : input.nodes.join(", "), - })); + family: input.category, + type: input.full ? "" : input.nodes.join(", "), + })); } export function buildInputs(tabsState: TabsState, id: string): string { @@ -262,7 +262,7 @@ export function buildTweakObject(tweak: tweakType) { for (let kp in el[key]) { try { el[key][kp] = JSON.parse(el[key][kp]); - } catch {} + } catch { } } }); }); @@ -314,21 +314,18 @@ export function getPythonApiCode( return `import requests from typing import Optional -BASE_API_URL = "${window.location.protocol}//${ - window.location.host - }/api/v1/process" +BASE_API_URL = "${window.location.protocol}//${window.location.host + }/api/v1/process" FLOW_ID = "${flowId}" # You can tweak the flow by adding a tweaks dictionary # e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}} -TWEAKS = ${ - tweak && tweak.length > 0 +TWEAKS = ${tweak && tweak.length > 0 ? buildTweakObject(tweak) : JSON.stringify(tweaks, null, 2) - } + } -def run_flow(inputs: dict, flow_id: str, tweaks: Optional[dict] = None${ - !isAuth ? `, apiKey: str=""` : "" - }) -> dict: +def run_flow(inputs: dict, flow_id: str, tweaks: Optional[dict] = None${!isAuth ? `, apiKey: str=""` : "" + }) -> dict: """ Run a flow with a given message and optional tweaks. @@ -350,9 +347,8 @@ def run_flow(inputs: dict, flow_id: str, tweaks: Optional[dict] = None${ # Setup any tweaks you want to apply to the flow inputs = ${inputs} ${!isAuth ? `api_key = ""` : ""} -print(run_flow(inputs, flow_id=FLOW_ID, tweaks=TWEAKS${ - !isAuth ? `, apiKey=api_key` : "" - }))`; +print(run_flow(inputs, flow_id=FLOW_ID, tweaks=TWEAKS${!isAuth ? `, apiKey=api_key` : "" + }))`; } /** @@ -371,16 +367,14 @@ export function getCurlCode( const inputs = buildInputs(tabsState!, flow.id); return `curl -X POST \\ - ${window.location.protocol}//${ - window.location.host - }/api/v1/process/${flowId} \\ + ${window.location.protocol}//${window.location.host + }/api/v1/process/${flowId} \\ -H 'Content-Type: application/json'\\ ${!isAuth ? `-H 'api-key: '\\` : ""} - -d '{"inputs": ${inputs}, "tweaks": ${ - tweak && tweak.length > 0 + -d '{"inputs": ${inputs}, "tweaks": ${tweak && tweak.length > 0 ? buildTweakObject(tweak) : JSON.stringify(tweaks, null, 2) - }}'`; + }}'`; } /** @@ -397,11 +391,10 @@ export function getPythonCode( const tweaks = buildTweaks(flow); const inputs = buildInputs(tabsState!, flow.id); return `from langflow import load_flow_from_json -TWEAKS = ${ - tweak && tweak.length > 0 +TWEAKS = ${tweak && tweak.length > 0 ? buildTweakObject(tweak) : JSON.stringify(tweaks, null, 2) - } + } flow = load_flow_from_json("${flowName}.json", tweaks=TWEAKS) # Now you can use it like any chain inputs = ${inputs} @@ -431,18 +424,16 @@ chat_input_field: Input key that you want the chat to send the user message with `; }