From a9863a050e5acabfcbdbd54d1a8f13fb509dafbe Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:32:24 -0300 Subject: [PATCH] refactor: parameter component, edit node modal and api modal (#2928) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * changed column defs to receive handleOnNewValue and use it to change Advanced variable * Exported function isTargetHandleConnected * Refactored types of setNodeClass * Exported type of handleOnNewValue * Refactored useHandleNodeClass to only handle changes to node data * Refactored toggleCellRender to handle advanced values by itself with the handleOnNewValue * changed setNodeClass types and disabled hook * changed handleNodeClass on parametercomponent * changed handleNodeClass on the two components that used it * refactor: Update handleNodeClass to accept newNodeClass and type only * Refactor EditNodeModal component to use new handleNodeClass and columnDefs * Changed columnDefs to receive new handleNodeClass and not receive useless parameters * changed cellTypeStr to strRender * Refactored inputFileComponent to use the new handleOnNewValue * Refactored keypairList to convert values on the fly * added parameterRenderComponent that renders all parameters in one place * Exported parameterRenderComponent * Changed tableNodeCellRender to use ParameterRenderComponent * changed parameterredercomponent to receive name * changed strrendercomponent to receive name * changed nodecellrender to pass name and to check if value is undefined before assigning on templateData * chore: Refactor use-handle-new-value to handle undefined values in changes * Used ParameterRenderComponent at parameterComponent * Refactored parameterRenderComponent to include refresh button * Created refresh parameter component * Added nodeId to parameterrendercomponent call * Removed unused variable * Refactored refreshButton * Fixed parameters not showing when they dont have a value * Created a tweakComplnent that renders the tweak individually, with the same table as the EditNode * Created tweaks component, that renders all tweaks * Changed tableNodeCellRender to pass setNode as well * changed handleOnNewValue to use custom setNode if specified * added editNodeComponent that handles the table and rowData and columnDefs * changed editNodeModal to use editNodeComponent instead of table * changed columndefs type to handle setNode and hideVisibility optional parameters * changed useRowData to not use myData, and just use nodeClass * Changed codeTabsPropsType tweaks property to include only the ncessary * changed codeTabsComponent to use TweaksComponent and handle an internal nodes state * changed apiModal to handle the tweaks build by finding the differences between old and current node * fix bug on API modal that refresh tweaks table all the time * Created new tweaks store type * Added tweaks store with all of the logic needed to get the tweaks on the API page * refactored TweakComponent to hold an temporary state to prevent the table from rerendering * Added TweakComponent into TweaksComponent * Removed external state on tableAdvancedToggle, making every state come from the Stores * Removed external state from TableNodeCellRender and added isTweaks to choose which store to use * Added SetNode type on HandleOnNewValue * Added custom setNode to handleNodeClass * Removed unused logic from apiModal * removed unused old code * Changed type of getChangesTypes * Transformed string into object on get codes * Changed getNodesWithDefaultValue to return nodes that will appear on Tweaks, as well as just the allowed parameters * added hasTweaks to tabs * added check of template keys to update the local nodeClass state, allowing the table to be updated just when the number of parameters changes * passed isTweaks to columnDefs * removed unused state and passed isTweaks to value getters * Removed unused state * updated tabsArrayType with hasTweaks and removed unused types * Added local nodes tweaks state to the codetabscomponent and refactored conditions of display * removed unused console.log * changed advanced toggle to use parameterid given by the value * changed nodecellrender to use parameter id given by the value * passed parameter id by value to the renderers * removed nodeClass from columnDefs definition * Fixed isTargetHandleConnected returning error if field is undefined * Fix performance issues on edit node modal * Fixed scroll overflow issues on tweaksComponent * Revert "Revert "refactor: update template api, handleonnewvalue and handlenodeclass hooks (#2628)"" This reverts commit 236ae82cabba2fa1128f498d781099facd222b57. * 📝 (tweaksTest.spec.ts): remove redundant code and improve readability by simplifying the interaction with elements in the test case * added custom id for tests * ✨ (frontend): Add unique id prop to input components for better testability and accessibility. ✨ (Hierarchical Tasks Agent.spec.ts): Update dropdown element IDs to improve clarity and consistency ✨ (Memory Chatbot.spec.ts): Update dropdown element IDs to improve clarity and consistency ✨ (Sequential Tasks Agent.spec.ts): Update dropdown element IDs to improve clarity and consistency ✨ (chatInputOutputUser-shard-0.spec.ts): Update dropdown element IDs to improve clarity and consistency ✨ (chatInputOutputUser-shard-1.spec.ts): Update dropdown element IDs to improve clarity and consistency ✨ (chatInputOutputUser-shard-2.spec.ts): Update dropdown element IDs to improve clarity and consistency ✨ (decisionFlow.spec.ts): Update input list element IDs to improve clarity and consistency ✨ (decisionFlow.spec.ts): Update prompt area element IDs to improve clarity and consistency ✨ (decisionFlow.spec.ts): Update dropdown element IDs to improve clarity and consistency ✨ (freeze-path.spec.ts): Update dropdown element IDs to improve clarity and consistency ✨ (generalBugs-shard-0.spec.ts): Update dropdown element IDs to improve clarity and consistency ✨ (generalBugs-shard-1.spec.ts): Update dropdown element IDs to improve clarity and consistency ✨ (generalBugs-shard-3.spec.ts): Update dropdown element IDs to improve clarity and consistency 🔧 (logs.spec.ts, textInputOutput.spec.ts): update dropdown test selectors to match changes in the frontend codebase * Fixed prompt not holding the value when validating * Fixed range spec on int component * Fixed OpenAI Model max tokens range spec * ✨ (dropdownComponent.spec.ts): Update dropdown test cases to use more descriptive test IDs for better clarity and maintainability 📝 (fileUploadComponent.spec.ts): Add explicit wait for "Run Flow" button to ensure it is loaded before clicking 📝 (folders.spec.ts): Update file path in readFileSync function to use an absolute path for better reliability 📝 (freeze-path.spec.ts): Refactor test code to remove commented out code and improve readability by using more descriptive test IDs and removing unnecessary code snippets ✨ (inputListComponent.spec.ts): Update test file to use consistent naming convention for input elements in the InputListComponent 📝 (intComponent.spec.ts): Refactor test file to use consistent naming convention for input elements in the IntComponent ✨ (nestedComponent.spec.ts): Update test steps and interactions for nestedComponent to improve test coverage and accuracy. 📝 (promptModalComponent.spec.ts): Update test selectors for prompt modal component to improve consistency and readability 📝 (textAreaModalComponent.spec.ts): Update test selectors for text area modal component to improve consistency and readability 📝 (toggleComponent.spec.ts): Update test selectors for toggle component to improve consistency and readability * 🐛 (intComponent.spec.ts): fix incorrect value comparison in test for IntComponent 🐛 (intComponent.spec.ts): fix missing test step for clicking on a specific element in IntComponent test * 📝 (Vector Store.spec.ts): increase timeout for waiting for "built successfully" text to improve test reliability and prevent false negatives * Fixed folders test --------- Co-authored-by: anovazzi1 Co-authored-by: Gabriel Luiz Freitas Almeida Co-authored-by: cristhianzl --- .../langflow/components/models/OpenAIModel.py | 2 + src/frontend/package-lock.json | 602 +++++++++++++- src/frontend/package.json | 1 + .../components/parameterComponent/index.tsx | 392 +-------- .../CustomNodes/helpers/mutate-template.ts | 44 + .../hooks/use-fetch-data-on-mount.tsx | 63 +- .../hooks/use-handle-new-value.tsx | 147 ++-- .../hooks/use-handle-node-class.tsx | 29 +- .../hooks/use-handle-refresh-buttons.tsx | 47 -- .../components/tweakComponent/index.tsx | 49 ++ .../components/tweaksComponent/index.tsx | 16 + .../components/codeTabsComponent/index.tsx | 784 +----------------- .../components/inputFileComponent/index.tsx | 23 +- .../components/inputListComponent/index.tsx | 6 +- .../src/components/intComponent/index.tsx | 77 +- .../components/keypairListComponent/index.tsx | 36 +- .../refreshParameterComponent/index.tsx | 46 + .../component/strRenderComponent/index.tsx | 73 ++ .../parameterRenderComponent/index.tsx | 149 ++++ .../tableAdvancedToggleCellRender/index.tsx | 56 ++ .../tableNodeCellRender/cellTypeStr.tsx | 72 -- .../components/tableNodeCellRender/index.tsx | 272 +----- .../tableToggleCellRender/index.tsx | 24 - .../components/toggleShadComponent/index.tsx | 1 - .../src/components/ui/refreshButton.tsx | 12 +- .../queries/nodes/use-post-template-value.ts | 61 ++ src/frontend/src/modals/apiModal/index.tsx | 317 ++----- .../modals/apiModal/utils/build-content.tsx | 10 - .../src/modals/apiModal/utils/build-tweaks.ts | 8 - .../utils/check-can-build-tweak-object.ts | 13 - .../apiModal/utils/get-changes-types.ts | 4 +- .../modals/apiModal/utils/get-codes-obj.tsx | 40 - .../modals/apiModal/utils/get-curl-code.tsx | 8 +- .../modals/apiModal/utils/get-js-api-code.tsx | 12 +- .../utils/get-nodes-with-default-value.ts | 42 +- .../apiModal/utils/get-python-api-code.tsx | 11 +- .../modals/apiModal/utils/get-python-code.tsx | 11 +- .../modals/apiModal/utils/get-tabs-order.tsx | 16 - .../src/modals/apiModal/utils/get-value.ts | 29 - .../src/modals/apiModal/utils/tabs-array.tsx | 8 +- .../src/modals/codeAreaModal/index.tsx | 3 +- .../components/editNodeComponent/index.tsx | 49 ++ .../editNodeModal/hooks/use-column-defs.tsx | 50 +- .../hooks/use-handle-new-value.tsx | 84 -- .../hooks/use-handle-node-class.tsx | 39 - .../editNodeModal/hooks/use-row-data.tsx | 15 +- .../src/modals/editNodeModal/index.tsx | 156 ++-- .../src/modals/genericModal/index.tsx | 5 +- .../components/nodeToolbarComponent/index.tsx | 65 +- src/frontend/src/stores/tweaksStore.ts | 126 ++- src/frontend/src/types/components/index.ts | 60 +- .../src/types/zustand/tweaks/index.ts | 24 +- src/frontend/src/utils/reactflowUtils.ts | 5 +- .../tests/end-to-end/Basic Prompting.spec.ts | 2 +- .../tests/end-to-end/Blog Writer.spec.ts | 6 +- .../tests/end-to-end/Complex Agent.spec.ts | 2 +- .../tests/end-to-end/Document QA.spec.ts | 2 +- .../Hierarchical Tasks Agent.spec.ts | 4 +- .../tests/end-to-end/Memory Chatbot.spec.ts | 2 +- .../end-to-end/Sequential Tasks Agent.spec.ts | 2 +- .../tests/end-to-end/Vector Store.spec.ts | 6 +- .../chatInputOutputUser-shard-0.spec.ts | 2 +- .../chatInputOutputUser-shard-1.spec.ts | 2 +- .../chatInputOutputUser-shard-2.spec.ts | 4 +- .../tests/end-to-end/decisionFlow.spec.ts | 25 +- .../end-to-end/dropdownComponent.spec.ts | 26 +- .../end-to-end/fileUploadComponent.spec.ts | 7 + .../tests/end-to-end/freeze-path.spec.ts | 152 +--- .../end-to-end/generalBugs-shard-0.spec.ts | 2 +- .../end-to-end/generalBugs-shard-1.spec.ts | 2 +- .../end-to-end/generalBugs-shard-3.spec.ts | 2 +- .../end-to-end/inputListComponent.spec.ts | 34 +- .../tests/end-to-end/intComponent.spec.ts | 46 +- src/frontend/tests/end-to-end/logs.spec.ts | 2 +- .../tests/end-to-end/nestedComponent.spec.ts | 188 ++--- .../end-to-end/promptModalComponent.spec.ts | 49 +- .../end-to-end/textAreaModalComponent.spec.ts | 12 +- .../tests/end-to-end/textInputOutput.spec.ts | 2 +- .../tests/end-to-end/toggleComponent.spec.ts | 58 +- .../tests/end-to-end/tweaksTest.spec.ts | 14 +- 80 files changed, 2026 insertions(+), 2923 deletions(-) create mode 100644 src/frontend/src/CustomNodes/helpers/mutate-template.ts delete mode 100644 src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx create mode 100644 src/frontend/src/components/codeTabsComponent/components/tweakComponent/index.tsx create mode 100644 src/frontend/src/components/codeTabsComponent/components/tweaksComponent/index.tsx create mode 100644 src/frontend/src/components/parameterRenderComponent/component/refreshParameterComponent/index.tsx create mode 100644 src/frontend/src/components/parameterRenderComponent/component/strRenderComponent/index.tsx create mode 100644 src/frontend/src/components/parameterRenderComponent/index.tsx create mode 100644 src/frontend/src/components/tableComponent/components/tableAdvancedToggleCellRender/index.tsx delete mode 100644 src/frontend/src/components/tableComponent/components/tableNodeCellRender/cellTypeStr.tsx delete mode 100644 src/frontend/src/components/tableComponent/components/tableToggleCellRender/index.tsx create mode 100644 src/frontend/src/controllers/API/queries/nodes/use-post-template-value.ts delete mode 100644 src/frontend/src/modals/apiModal/utils/build-content.tsx delete mode 100644 src/frontend/src/modals/apiModal/utils/build-tweaks.ts delete mode 100644 src/frontend/src/modals/apiModal/utils/check-can-build-tweak-object.ts delete mode 100644 src/frontend/src/modals/apiModal/utils/get-codes-obj.tsx delete mode 100644 src/frontend/src/modals/apiModal/utils/get-tabs-order.tsx delete mode 100644 src/frontend/src/modals/apiModal/utils/get-value.ts create mode 100644 src/frontend/src/modals/editNodeModal/components/editNodeComponent/index.tsx delete mode 100644 src/frontend/src/modals/editNodeModal/hooks/use-handle-new-value.tsx delete mode 100644 src/frontend/src/modals/editNodeModal/hooks/use-handle-node-class.tsx diff --git a/src/backend/base/langflow/components/models/OpenAIModel.py b/src/backend/base/langflow/components/models/OpenAIModel.py index d621bbb9a..516c0aeba 100644 --- a/src/backend/base/langflow/components/models/OpenAIModel.py +++ b/src/backend/base/langflow/components/models/OpenAIModel.py @@ -1,6 +1,7 @@ import operator from functools import reduce +from langflow.field_typing.range_spec import RangeSpec from langchain_openai import ChatOpenAI from pydantic.v1 import SecretStr @@ -30,6 +31,7 @@ class OpenAIModelComponent(LCModelComponent): display_name="Max Tokens", advanced=True, info="The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + range_spec=RangeSpec(min=0, max=128000), ), DictInput(name="model_kwargs", display_name="Model Kwargs", advanced=True), BoolInput( diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 9e90168be..ff9d7e8c5 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "langflow", "version": "0.1.2", "dependencies": { + "@chakra-ui/number-input": "^2.1.2", "@headlessui/react": "^2.0.4", "@hookform/resolvers": "^3.6.0", "@million/lint": "^1.0.0-rc.26", @@ -765,6 +766,293 @@ "node": ">=6.9.0" } }, + "node_modules/@chakra-ui/anatomy": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/anatomy/-/anatomy-2.2.2.tgz", + "integrity": "sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg==", + "license": "MIT", + "peer": true + }, + "node_modules/@chakra-ui/color-mode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/color-mode/-/color-mode-2.2.0.tgz", + "integrity": "sha512-niTEA8PALtMWRI9wJ4LL0CSBDo8NBfLNp4GD6/0hstcm3IlbBHTVKxN6HwSaoNYfphDQLxCjT4yG+0BJA5tFpg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/react-use-safe-layout-effect": "2.1.0" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/counter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/counter/-/counter-2.1.0.tgz", + "integrity": "sha512-s6hZAEcWT5zzjNz2JIWUBzRubo9la/oof1W7EKZVVfPYHERnl5e16FmBC79Yfq8p09LQ+aqFKm/etYoJMMgghw==", + "license": "MIT", + "dependencies": { + "@chakra-ui/number-utils": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/form-control": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/form-control/-/form-control-2.2.0.tgz", + "integrity": "sha512-wehLC1t4fafCVJ2RvJQT2jyqsAwX7KymmiGqBu7nQoQz8ApTkGABWpo/QwDh3F/dBLrouHDoOvGmYTqft3Mirw==", + "license": "MIT", + "dependencies": { + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" + }, + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/icon": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/icon/-/icon-3.2.0.tgz", + "integrity": "sha512-xxjGLvlX2Ys4H0iHrI16t74rG9EBcpFvJ3Y3B7KMQTrnW34Kf7Da/UC8J67Gtx85mTHW020ml85SVPKORWNNKQ==", + "license": "MIT", + "dependencies": { + "@chakra-ui/shared-utils": "2.0.5" + }, + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/number-input": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/number-input/-/number-input-2.1.2.tgz", + "integrity": "sha512-pfOdX02sqUN0qC2ysuvgVDiws7xZ20XDIlcNhva55Jgm095xjm8eVdIBfNm3SFbSUNxyXvLTW/YQanX74tKmuA==", + "license": "MIT", + "dependencies": { + "@chakra-ui/counter": "2.1.0", + "@chakra-ui/form-control": "2.2.0", + "@chakra-ui/icon": "3.2.0", + "@chakra-ui/react-context": "2.1.0", + "@chakra-ui/react-types": "2.0.7", + "@chakra-ui/react-use-callback-ref": "2.1.0", + "@chakra-ui/react-use-event-listener": "2.1.0", + "@chakra-ui/react-use-interval": "2.1.0", + "@chakra-ui/react-use-merge-refs": "2.1.0", + "@chakra-ui/react-use-safe-layout-effect": "2.1.0", + "@chakra-ui/react-use-update-effect": "2.1.0", + "@chakra-ui/shared-utils": "2.0.5" + }, + "peerDependencies": { + "@chakra-ui/system": ">=2.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/number-utils": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@chakra-ui/number-utils/-/number-utils-2.0.7.tgz", + "integrity": "sha512-yOGxBjXNvLTBvQyhMDqGU0Oj26s91mbAlqKHiuw737AXHt0aPllOthVUqQMeaYLwLCjGMg0jtI7JReRzyi94Dg==", + "license": "MIT" + }, + "node_modules/@chakra-ui/object-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/object-utils/-/object-utils-2.1.0.tgz", + "integrity": "sha512-tgIZOgLHaoti5PYGPTwK3t/cqtcycW0owaiOXoZOcpwwX/vlVb+H1jFsQyWiiwQVPt9RkoSLtxzXamx+aHH+bQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@chakra-ui/react-context": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-context/-/react-context-2.1.0.tgz", + "integrity": "sha512-iahyStvzQ4AOwKwdPReLGfDesGG+vWJfEsn0X/NoGph/SkN+HXtv2sCfYFFR9k7bb+Kvc6YfpLlSuLvKMHi2+w==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-types/-/react-types-2.0.7.tgz", + "integrity": "sha512-12zv2qIZ8EHwiytggtGvo4iLT0APris7T0qaAWqzpUGS0cdUtR8W+V1BJ5Ocq+7tA6dzQ/7+w5hmXih61TuhWQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-callback-ref": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-callback-ref/-/react-use-callback-ref-2.1.0.tgz", + "integrity": "sha512-efnJrBtGDa4YaxDzDE90EnKD3Vkh5a1t3w7PhnRQmsphLy3g2UieasoKTlT2Hn118TwDjIv5ZjHJW6HbzXA9wQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-event-listener": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-event-listener/-/react-use-event-listener-2.1.0.tgz", + "integrity": "sha512-U5greryDLS8ISP69DKDsYcsXRtAdnTQT+jjIlRYZ49K/XhUR/AqVZCK5BkR1spTDmO9H8SPhgeNKI70ODuDU/Q==", + "license": "MIT", + "dependencies": { + "@chakra-ui/react-use-callback-ref": "2.1.0" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-interval": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-interval/-/react-use-interval-2.1.0.tgz", + "integrity": "sha512-8iWj+I/+A0J08pgEXP1J1flcvhLBHkk0ln7ZvGIyXiEyM6XagOTJpwNhiu+Bmk59t3HoV/VyvyJTa+44sEApuw==", + "license": "MIT", + "dependencies": { + "@chakra-ui/react-use-callback-ref": "2.1.0" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-merge-refs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.1.0.tgz", + "integrity": "sha512-lERa6AWF1cjEtWSGjxWTaSMvneccnAVH4V4ozh8SYiN9fSPZLlSG3kNxfNzdFvMEhM7dnP60vynF7WjGdTgQbQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-safe-layout-effect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.1.0.tgz", + "integrity": "sha512-Knbrrx/bcPwVS1TorFdzrK/zWA8yuU/eaXDkNj24IrKoRlQrSBFarcgAEzlCHtzuhufP3OULPkELTzz91b0tCw==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-use-update-effect": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-update-effect/-/react-use-update-effect-2.1.0.tgz", + "integrity": "sha512-ND4Q23tETaR2Qd3zwCKYOOS1dfssojPLJMLvUtUbW5M9uW1ejYWgGUobeAiOVfSplownG8QYMmHTP86p/v0lbA==", + "license": "MIT", + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/react-utils": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@chakra-ui/react-utils/-/react-utils-2.0.12.tgz", + "integrity": "sha512-GbSfVb283+YA3kA8w8xWmzbjNWk14uhNpntnipHCftBibl0lxtQ9YqMFQLwuFOO0U2gYVocszqqDWX+XNKq9hw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/utils": "2.0.15" + }, + "peerDependencies": { + "react": ">=18" + } + }, + "node_modules/@chakra-ui/shared-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@chakra-ui/shared-utils/-/shared-utils-2.0.5.tgz", + "integrity": "sha512-4/Wur0FqDov7Y0nCXl7HbHzCg4aq86h+SXdoUeuCMD3dSj7dpsVnStLYhng1vxvlbUnLpdF4oz5Myt3i/a7N3Q==", + "license": "MIT" + }, + "node_modules/@chakra-ui/styled-system": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/styled-system/-/styled-system-2.9.2.tgz", + "integrity": "sha512-To/Z92oHpIE+4nk11uVMWqo2GGRS86coeMmjxtpnErmWRdLcp1WVCVRAvn+ZwpLiNR+reWFr2FFqJRsREuZdAg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/shared-utils": "2.0.5", + "csstype": "^3.1.2", + "lodash.mergewith": "4.6.2" + } + }, + "node_modules/@chakra-ui/system": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/system/-/system-2.6.2.tgz", + "integrity": "sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/color-mode": "2.2.0", + "@chakra-ui/object-utils": "2.1.0", + "@chakra-ui/react-utils": "2.0.12", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/theme-utils": "2.0.21", + "@chakra-ui/utils": "2.0.15", + "react-fast-compare": "3.2.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0", + "@emotion/styled": "^11.0.0", + "react": ">=18" + } + }, + "node_modules/@chakra-ui/theme": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme/-/theme-3.3.1.tgz", + "integrity": "sha512-Hft/VaT8GYnItGCBbgWd75ICrIrIFrR7lVOhV/dQnqtfGqsVDlrztbSErvMkoPKt0UgAkd9/o44jmZ6X4U2nZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/anatomy": "2.2.2", + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/theme-tools": "2.1.2" + }, + "peerDependencies": { + "@chakra-ui/styled-system": ">=2.8.0" + } + }, + "node_modules/@chakra-ui/theme-tools": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme-tools/-/theme-tools-2.1.2.tgz", + "integrity": "sha512-Qdj8ajF9kxY4gLrq7gA+Azp8CtFHGO9tWMN2wfF9aQNgG9AuMhPrUzMq9AMQ0MXiYcgNq/FD3eegB43nHVmXVA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/anatomy": "2.2.2", + "@chakra-ui/shared-utils": "2.0.5", + "color2k": "^2.0.2" + }, + "peerDependencies": { + "@chakra-ui/styled-system": ">=2.0.0" + } + }, + "node_modules/@chakra-ui/theme-utils": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/@chakra-ui/theme-utils/-/theme-utils-2.0.21.tgz", + "integrity": "sha512-FjH5LJbT794r0+VSCXB3lT4aubI24bLLRWB+CuRKHijRvsOg717bRdUN/N1fEmEpFnRVrbewttWh/OQs0EWpWw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@chakra-ui/shared-utils": "2.0.5", + "@chakra-ui/styled-system": "2.9.2", + "@chakra-ui/theme": "3.3.1", + "lodash.mergewith": "4.6.2" + } + }, + "node_modules/@chakra-ui/utils": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@chakra-ui/utils/-/utils-2.0.15.tgz", + "integrity": "sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/lodash.mergewith": "4.6.7", + "css-box-model": "1.2.1", + "framesync": "6.1.2", + "lodash.mergewith": "4.6.2" + } + }, "node_modules/@clack/core": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.3.4.tgz", @@ -799,6 +1087,195 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/react": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/styled": { + "version": "11.11.5", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", + "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.2", + "@emotion/serialize": "^1.1.4", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==", + "license": "MIT", + "peer": true + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", + "license": "MIT", + "peer": true + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -4998,8 +5475,17 @@ "node_modules/@types/lodash": { "version": "4.17.5", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==", - "dev": true + "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==" + }, + "node_modules/@types/lodash.mergewith": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/@types/lodash.mergewith/-/lodash.mergewith-4.6.7.tgz", + "integrity": "sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/lodash": "*" + } }, "node_modules/@types/mathjax": { "version": "0.0.37", @@ -5028,6 +5514,13 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT", + "peer": true + }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", @@ -5402,6 +5895,49 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-macros/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/babel-plugin-macros/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/babel-plugin-syntax-hermes-parser": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.21.1.tgz", @@ -6358,6 +6894,13 @@ "color-support": "bin.js" } }, + "node_modules/color2k": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", + "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==", + "license": "MIT", + "peer": true + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -6461,6 +7004,16 @@ "node": ">= 8" } }, + "node_modules/css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "license": "MIT", + "peer": true, + "dependencies": { + "tiny-invariant": "^1.0.6" + } + }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -7571,6 +8124,13 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT", + "peer": true + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -7735,6 +8295,23 @@ } } }, + "node_modules/framesync": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.1.2.tgz", + "integrity": "sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "2.4.0" + } + }, + "node_modules/framesync/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "license": "0BSD", + "peer": true + }, "node_modules/fs-extra": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", @@ -9249,6 +9826,13 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "license": "MIT", + "peer": true + }, "node_modules/lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -12228,6 +12812,13 @@ "react": ">=16.13.1" } }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT", + "peer": true + }, "node_modules/react-hook-form": { "version": "7.52.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.52.0.tgz", @@ -13637,6 +14228,13 @@ "inline-style-parser": "0.1.1" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT", + "peer": true + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", diff --git a/src/frontend/package.json b/src/frontend/package.json index f4b13fd97..6505ee9b3 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -3,6 +3,7 @@ "version": "0.1.2", "private": true, "dependencies": { + "@chakra-ui/number-input": "^2.1.2", "@headlessui/react": "^2.0.4", "@hookform/resolvers": "^3.6.0", "@million/lint": "^1.0.0-rc.26", diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 52ac79087..1fcc009d3 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -1,42 +1,22 @@ -import TableNodeComponent from "@/components/TableNodeComponent"; +import useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class"; +import { ParameterRenderComponent } from "@/components/parameterRenderComponent"; +import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value"; +import useAlertStore from "@/stores/alertStore"; import { cloneDeep } from "lodash"; import { ReactNode, useEffect, useRef, useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; import { useUpdateNodeInternals } from "reactflow"; -import CodeAreaComponent from "../../../../components/codeAreaComponent"; -import DictComponent from "../../../../components/dictComponent"; -import Dropdown from "../../../../components/dropdownComponent"; -import FloatComponent from "../../../../components/floatComponent"; import { default as IconComponent } from "../../../../components/genericIconComponent"; -import InputFileComponent from "../../../../components/inputFileComponent"; -import InputGlobalComponent from "../../../../components/inputGlobalComponent"; -import InputListComponent from "../../../../components/inputListComponent"; -import IntComponent from "../../../../components/intComponent"; -import KeypairListComponent from "../../../../components/keypairListComponent"; -import { Multiselect } from "../../../../components/multiselectComponent"; -import PromptAreaComponent from "../../../../components/promptComponent"; import ShadTooltip from "../../../../components/shadTooltipComponent"; -import TextAreaComponent from "../../../../components/textAreaComponent"; -import ToggleShadComponent from "../../../../components/toggleShadComponent"; import { Button } from "../../../../components/ui/button"; -import { RefreshButton } from "../../../../components/ui/refreshButton"; import { LANGFLOW_SUPPORTED_TYPES } from "../../../../constants/constants"; import { Case } from "../../../../shared/components/caseComponent"; import useFlowStore from "../../../../stores/flowStore"; -import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import { useShortcutsStore } from "../../../../stores/shortcuts"; import { useTypesStore } from "../../../../stores/typesStore"; -import { APIClassType } from "../../../../types/api"; import { ParameterComponentType } from "../../../../types/components"; import { - debouncedHandleUpdateValues, - handleUpdateValues, -} from "../../../../utils/parameterUtils"; -import { - convertObjToArray, - convertValuesToNumbers, getGroupOutputNodeId, - hasDuplicateKeys, scapedJSONStringfy, } from "../../../../utils/reactflowUtils"; import { @@ -49,12 +29,9 @@ import { } from "../../../../utils/utils"; import useFetchDataOnMount from "../../../hooks/use-fetch-data-on-mount"; import useHandleOnNewValue from "../../../hooks/use-handle-new-value"; -import useHandleNodeClass from "../../../hooks/use-handle-node-class"; -import useHandleRefreshButtonPress from "../../../hooks/use-handle-refresh-buttons"; import OutputComponent from "../OutputComponent"; import HandleRenderComponent from "../handleRenderComponent"; import OutputModal from "../outputModal"; -import { TEXT_FIELD_TYPES } from "./constants"; export default function ParameterComponent({ left, @@ -81,10 +58,12 @@ export default function ParameterComponent({ const edges = useFlowStore((state) => state.edges); const setNode = useFlowStore((state) => state.setNode); const myData = useTypesStore((state) => state.data); - const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); - const [isLoading, setIsLoading] = useState(false); + const postTemplateValue = usePostTemplateValue({ + node: data.node!, + nodeId: data.id, + parameterId: name, + }); const updateNodeInternals = useUpdateNodeInternals(); - const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); const setFilterEdge = useFlowStore((state) => state.setFilterEdge); const [openOutputModal, setOpenOutputModal] = useState(false); const flowPool = useFlowStore((state) => state.flowPool); @@ -132,29 +111,12 @@ export default function ParameterComponent({ } } + const setErrorData = useAlertStore((state) => state.setErrorData); + const output = useShortcutsStore((state) => state.output); useHotkeys(output, handleOutputWShortcut, { preventDefault }); - const { handleOnNewValue: handleOnNewValueHook } = useHandleOnNewValue( - data, - name, - takeSnapshot, - handleUpdateValues, - debouncedHandleUpdateValues, - setNode, - setIsLoading, - ); - - const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass( - data, - name, - takeSnapshot, - setNode, - updateNodeInternals, - ); - - const { handleRefreshButtonPress: handleRefreshButtonPressHook } = - useHandleRefreshButtonPress(setIsLoading, setNode); + const { handleNodeClass } = useHandleNodeClass(data.id); let disabled = edges.some( @@ -168,27 +130,13 @@ export default function ParameterComponent({ edge.sourceHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id), ) ?? false; - const handleRefreshButtonPress = async (name, data) => { - handleRefreshButtonPressHook(name, data); - }; + const { handleOnNewValue } = useHandleOnNewValue({ + node: data.node!, + nodeId: data.id, + name, + }); - useFetchDataOnMount(data, name, handleUpdateValues, setNode, setIsLoading); - - const handleOnNewValue = async ( - newValue: string | string[] | boolean | Object[], - dbValue?: boolean, - skipSnapshot: boolean | undefined = false, - ): Promise => { - handleOnNewValueHook(newValue, dbValue, skipSnapshot); - }; - - const handleNodeClass = ( - newNodeClass: APIClassType, - code?: string, - type?: string, - ): void => { - handleNodeClassHook(newNodeClass, code, type); - }; + useFetchDataOnMount(data.node!, handleNodeClass, name, postTemplateValue); useEffect(() => { // @ts-ignore @@ -401,301 +349,21 @@ export default function ParameterComponent({ testIdComplement={`${data?.type?.toLowerCase()}-shownode`} /> )} - - -
- -
- -
-
- -
-
- -
- {data.node?.template[name]?.refresh_button && ( -
- -
- )} -
-
- -
-
- -
- {data.node?.template[name]?.refresh_button && ( -
- -
- )} -
-
-
-
- - -
- + {data.node?.template[name] !== undefined && ( + -
-
- - -
- -
-
- -
- -
-
- - -
-
- -
- {data.node?.template[name]?.refresh_button && ( -
- -
- )} -
-
- -
- -
-
- - -
- -
-
- - -
- { - data.node!.template[name].file_path = filePath; - }} - > -
-
- - -
- -
-
- - -
- -
-
- - -
- -
-
- - -
- { - const valueToNumbers = convertValuesToNumbers(newValue); - setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers)); - // if data.node?.template[name]?.list is true, then the value is an array of objects - // else we need to get the first object of the array - - if (data.node?.template[name]?.list) { - handleOnNewValue(valueToNumbers); - } else handleOnNewValue(valueToNumbers[0]); - }} - isList={data.node?.template[name]?.list ?? false} - /> -
-
+ )} + {openOutputModal && ( , + setErrorData, + ) => { + try { + const newNode = cloneDeep(node); + const newTemplate = await postTemplateValue.mutateAsync({ + value: newValue, + }); + if (newTemplate) { + newNode.template = newTemplate; + } + setNodeClass(newNode); + } catch (e) { + const error = e as ResponseErrorDetailAPI; + setErrorData({ + title: TITLE_ERROR_UPDATING_COMPONENT, + list: [error.response?.data?.detail || ERROR_UPDATING_COMPONENT], + }); + } + }, + SAVE_DEBOUNCE_TIME, +); diff --git a/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx b/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx index 497c560c2..6379202fd 100644 --- a/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-fetch-data-on-mount.tsx @@ -1,55 +1,40 @@ -import { cloneDeep } from "lodash"; -import { useEffect } from "react"; import { - ERROR_UPDATING_COMPONENT, - TITLE_ERROR_UPDATING_COMPONENT, -} from "../../constants/constants"; + APIClassType, + APITemplateType, + ResponseErrorDetailAPI, +} from "@/types/api"; +import { UseMutationResult } from "@tanstack/react-query"; +import { useEffect } from "react"; import useAlertStore from "../../stores/alertStore"; -import { ResponseErrorDetailAPI } from "../../types/api"; -import { NodeDataType } from "../../types/flow"; +import { mutateTemplate } from "../helpers/mutate-template"; const useFetchDataOnMount = ( - data: NodeDataType, + node: APIClassType, + setNodeClass: (node: APIClassType) => void, name: string, - handleUpdateValues: (name: string, data: NodeDataType) => Promise, - setNode: (id: string, callback: (oldNode: any) => any) => void, - setIsLoading: (value: boolean) => void, + postTemplateValue: UseMutationResult< + APITemplateType | undefined, + ResponseErrorDetailAPI, + any + >, ) => { const setErrorData = useAlertStore((state) => state.setErrorData); useEffect(() => { async function fetchData() { + const template = node.template[name]; if ( - (data.node?.template[name]?.real_time_refresh || - data.node?.template[name]?.refresh_button) && + (template?.real_time_refresh || template?.refresh_button) && // options can be undefined but not an empty array - (data.node?.template[name]?.options?.length ?? 0) === 0 + (template?.options?.length ?? 0) === 0 ) { - setIsLoading(true); - try { - let newTemplate = await handleUpdateValues(name, data); - - if (newTemplate) { - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - newNode.data = { - ...newNode.data, - }; - newNode.data.node.template = newTemplate; - return newNode; - }); - } - } catch (error) { - let responseError = error as ResponseErrorDetailAPI; - - setErrorData({ - title: TITLE_ERROR_UPDATING_COMPONENT, - list: [ - responseError?.response?.data?.detail ?? ERROR_UPDATING_COMPONENT, - ], - }); - } - setIsLoading(false); + mutateTemplate( + template?.value, + node, + setNodeClass, + postTemplateValue, + setErrorData, + ); } } fetchData(); diff --git a/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx b/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx index 5f294af5b..15b1c011a 100644 --- a/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-handle-new-value.tsx @@ -1,79 +1,100 @@ +import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value"; +import useAlertStore from "@/stores/alertStore"; +import useFlowStore from "@/stores/flowStore"; +import useFlowsManagerStore from "@/stores/flowsManagerStore"; +import { APIClassType, InputFieldType } from "@/types/api"; +import { NodeType } from "@/types/flow"; import { cloneDeep } from "lodash"; -import { - ERROR_UPDATING_COMPONENT, - TITLE_ERROR_UPDATING_COMPONENT, -} from "../../constants/constants"; -import useAlertStore from "../../stores/alertStore"; -import { ResponseErrorTypeAPI } from "../../types/api"; -import { NodeDataType } from "../../types/flow"; +import { mutateTemplate } from "../helpers/mutate-template"; + +export type handleOnNewValueType = ( + changes: Partial, + options?: { + skipSnapshot?: boolean; + setNodeClass?: (node: APIClassType) => void; + }, +) => void; + +const useHandleOnNewValue = ({ + node, + nodeId, + name, + setNode: setNodeExternal, +}: { + node: APIClassType; + nodeId: string; + name: string; + setNode?: ( + id: string, + update: NodeType | ((oldState: NodeType) => NodeType), + ) => void; +}) => { + const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); + + const setNode = setNodeExternal ?? useFlowStore((state) => state.setNode); -const useHandleOnNewValue = ( - data: NodeDataType, - name: string, - takeSnapshot: () => void, - handleUpdateValues: (name: string, data: NodeDataType) => Promise, - debouncedHandleUpdateValues: any, - setNode: (id: string, callback: (oldNode: any) => any) => void, - setIsLoading: (value: boolean) => void, -) => { const setErrorData = useAlertStore((state) => state.setErrorData); - const handleOnNewValue = async (newValue, dbValue, skipSnapshot = false) => { - const nodeTemplate = data.node!.template[name]; - const currentValue = nodeTemplate.value; + const postTemplateValue = usePostTemplateValue({ + parameterId: name, + nodeId: nodeId, + node: node, + }); - if (currentValue !== newValue && !skipSnapshot) { - takeSnapshot(); + const handleOnNewValue: handleOnNewValueType = async (changes, options?) => { + const newNode = cloneDeep(node); + const template = newNode.template; + + if (!template) { + setErrorData({ title: "Template not found in the component" }); + return; } + const parameter = template[name]; + + if (!parameter) { + setErrorData({ title: "Parameter not found in the template" }); + return; + } + + if (!options?.skipSnapshot) takeSnapshot(); + + Object.entries(changes).forEach(([key, value]) => { + if (value !== undefined) parameter[key] = value; + }); + const shouldUpdate = - data.node?.template[name].real_time_refresh && - !data.node?.template[name].refresh_button && - currentValue !== newValue; + parameter.real_time_refresh && !parameter.refresh_button; - const typeToDebounce = nodeTemplate.type; + const setNodeClass = (newNodeClass: APIClassType) => { + options?.setNodeClass && options.setNodeClass(newNodeClass); + setNode(nodeId, (oldNode) => { + const newData = cloneDeep(oldNode.data); + newData.node = newNodeClass; + return { + ...oldNode, + data: newData, + }; + }); + }; - nodeTemplate.value = newValue; - - let newTemplate; - if (shouldUpdate) { - setIsLoading(true); - try { - if (["int"].includes(typeToDebounce)) { - newTemplate = await handleUpdateValues(name, data); - } else { - newTemplate = await debouncedHandleUpdateValues(name, data); - } - } catch (error) { - let responseError = error as ResponseErrorTypeAPI; - setErrorData({ - title: TITLE_ERROR_UPDATING_COMPONENT, - list: [ - responseError?.response?.data?.detail.error ?? - ERROR_UPDATING_COMPONENT, - ], - }); - } - setIsLoading(false); + if (shouldUpdate && changes.value) { + mutateTemplate( + changes.value, + newNode, + setNodeClass, + postTemplateValue, + setErrorData, + ); } - setNode(data.id, (oldNode) => { - const newNode = cloneDeep(oldNode); - newNode.data = { - ...newNode.data, + setNode(nodeId, (oldNode) => { + const newData = cloneDeep(oldNode.data); + newData.node = newNode; + return { + ...oldNode, + data: newData, }; - - if (dbValue !== undefined) { - newNode.data.node.template[name].load_from_db = dbValue; - } - - if (data.node?.template[name].real_time_refresh && newTemplate) { - newNode.data.node.template = newTemplate; - } else { - newNode.data.node.template[name].value = newValue; - } - - return newNode; }); }; diff --git a/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx b/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx index 0827c0b4e..fd86f51d1 100644 --- a/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-handle-node-class.tsx @@ -1,37 +1,30 @@ +import useFlowStore from "@/stores/flowStore"; +import { NodeType } from "@/types/flow"; import { cloneDeep } from "lodash"; -import { NodeDataType } from "../../types/flow"; const useHandleNodeClass = ( - data: NodeDataType, - name: string, - takeSnapshot: () => void, - setNode: (id: string, callback: (oldNode: any) => any) => void, - updateNodeInternals: (id: string) => void, + nodeId: string, + setMyNode?: ( + id: string, + update: NodeType | ((oldState: NodeType) => NodeType), + ) => void, ) => { - const handleNodeClass = (newNodeClass, code, type?: string) => { - if (!data.node) return; - if (data.node!.template[name].value !== code) { - takeSnapshot(); - } + const setNode = setMyNode ?? useFlowStore((state) => state.setNode); - setNode(data.id, (oldNode) => { + const handleNodeClass = (newNodeClass, type?: string) => { + setNode(nodeId, (oldNode) => { let newNode = cloneDeep(oldNode); newNode.data = { ...newNode.data, - node: newNodeClass, - description: newNodeClass.description ?? data.node!.description, - display_name: newNodeClass.display_name ?? data.node!.display_name, + node: cloneDeep(newNodeClass), }; if (type) { newNode.data.type = type; } - newNode.data.node.template[name].value = code; return newNode; }); - - updateNodeInternals(data.id); }; return { handleNodeClass }; diff --git a/src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx b/src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx deleted file mode 100644 index 65345fcea..000000000 --- a/src/frontend/src/CustomNodes/hooks/use-handle-refresh-buttons.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { cloneDeep } from "lodash"; -import { - ERROR_UPDATING_COMPONENT, - TITLE_ERROR_UPDATING_COMPONENT, -} from "../../constants/constants"; -import useAlertStore from "../../stores/alertStore"; -import { ResponseErrorDetailAPI } from "../../types/api"; -import { handleUpdateValues } from "../../utils/parameterUtils"; - -const useHandleRefreshButtonPress = ( - setIsLoading: (value: boolean) => void, - setNode: (id: string, callback: (oldNode: any) => any) => void, -) => { - const setErrorData = useAlertStore((state) => state.setErrorData); - - const handleRefreshButtonPress = async (name, data) => { - setIsLoading(true); - try { - let newTemplate = await handleUpdateValues(name, data); - - if (newTemplate) { - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - newNode.data = { - ...newNode.data, - }; - newNode.data.node.template = newTemplate; - return newNode; - }); - } - } catch (error) { - let responseError = error as ResponseErrorDetailAPI; - - setErrorData({ - title: TITLE_ERROR_UPDATING_COMPONENT, - list: [ - responseError?.response?.data?.detail ?? ERROR_UPDATING_COMPONENT, - ], - }); - } - setIsLoading(false); - }; - - return { handleRefreshButtonPress }; -}; - -export default useHandleRefreshButtonPress; diff --git a/src/frontend/src/components/codeTabsComponent/components/tweakComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/components/tweakComponent/index.tsx new file mode 100644 index 000000000..91c1c5afb --- /dev/null +++ b/src/frontend/src/components/codeTabsComponent/components/tweakComponent/index.tsx @@ -0,0 +1,49 @@ +import AccordionComponent from "@/components/accordionComponent"; +import ShadTooltip from "@/components/shadTooltipComponent"; +import { EditNodeComponent } from "@/modals/editNodeModal/components/editNodeComponent"; +import { APIClassType } from "@/types/api"; +import { NodeType } from "@/types/flow"; +import { customStringify } from "@/utils/reactflowUtils"; +import { useEffect, useState } from "react"; + +export function TweakComponent({ + open, + node, +}: { + open: boolean; + node: NodeType; +}) { + const [nodeClass, setNodeClass] = useState( + node.data?.node, + ); + + useEffect(() => { + if ( + customStringify(Object.keys(node.data?.node?.template ?? {})) === + customStringify(Object.keys(nodeClass?.template ?? {})) + ) + return; + setNodeClass(node.data?.node); + }, [node.data?.node]); + return node && node.data && nodeClass ? ( + +
{node.data.node?.display_name}
+ + } + keyValue={node.data.id} + > + +
+ ) : ( + <> + ); +} diff --git a/src/frontend/src/components/codeTabsComponent/components/tweaksComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/components/tweaksComponent/index.tsx new file mode 100644 index 000000000..9078bda1d --- /dev/null +++ b/src/frontend/src/components/codeTabsComponent/components/tweaksComponent/index.tsx @@ -0,0 +1,16 @@ +import { useTweaksStore } from "@/stores/tweaksStore"; +import { NodeType } from "@/types/flow"; +import { TweakComponent } from "../tweakComponent"; + +export function TweaksComponent({ open }: { open: boolean }) { + const nodes = useTweaksStore((state) => state.nodes); + return ( +
+ {nodes?.map((node: NodeType, i) => ( +
+ +
+ ))} +
+ ); +} diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx index a7c80ebd6..239a3117e 100644 --- a/src/frontend/src/components/codeTabsComponent/index.tsx +++ b/src/frontend/src/components/codeTabsComponent/index.tsx @@ -1,82 +1,34 @@ -import { cloneDeep } from "lodash"; -import { useEffect, useState } from "react"; +import { useTweaksStore } from "@/stores/tweaksStore"; +import { useState } from "react"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; -import CodeAreaComponent from "../../components/codeAreaComponent"; -import Dropdown from "../../components/dropdownComponent"; -import FloatComponent from "../../components/floatComponent"; -import InputFileComponent from "../../components/inputFileComponent"; -import InputListComponent from "../../components/inputListComponent"; -import IntComponent from "../../components/intComponent"; -import PromptAreaComponent from "../../components/promptComponent"; -import TextAreaComponent from "../../components/textAreaComponent"; -import ToggleShadComponent from "../../components/toggleShadComponent"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "../../components/ui/table"; import { Tabs, TabsContent, TabsList, TabsTrigger, } from "../../components/ui/tabs"; -import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants"; -import getTabsOrder from "../../modals/apiModal/utils/get-tabs-order"; -import { Case } from "../../shared/components/caseComponent"; import { useDarkStore } from "../../stores/darkStore"; -import useFlowStore from "../../stores/flowStore"; import { codeTabsPropsType } from "../../types/components"; -import { - convertObjToArray, - convertValuesToNumbers, - hasDuplicateKeys, -} from "../../utils/reactflowUtils"; -import { classNames, cn } from "../../utils/utils"; -import AccordionComponent from "../accordionComponent"; -import DictComponent from "../dictComponent"; +import { cn } from "../../utils/utils"; import IconComponent from "../genericIconComponent"; -import InputComponent from "../inputComponent"; -import KeypairListComponent from "../keypairListComponent"; -import ShadTooltip from "../shadTooltipComponent"; import { Button } from "../ui/button"; import { Label } from "../ui/label"; import { Switch } from "../ui/switch"; +import { TweaksComponent } from "./components/tweaksComponent"; export default function CodeTabsComponent({ - flow, + open, tabs, activeTab, setActiveTab, isMessage, - tweaks, setActiveTweaks, activeTweaks, - allowExport = false, - isThereTweaks = false, - isThereWH = false, }: codeTabsPropsType) { const [isCopied, setIsCopied] = useState(false); - const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null); const dark = useDarkStore((state) => state.dark); - const unselectAll = useFlowStore((state) => state.unselectAll); - const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); - - useEffect(() => { - if (flow && flow["data"]!["nodes"]) { - setData(flow["data"]!["nodes"]); - } - }, [flow]); - - useEffect(() => { - if (tweaks && data) { - unselectAll(); - } - }, []); + const nodes = useTweaksStore((state) => state.nodes); const copyToClipboard = () => { if (!navigator.clipboard || !navigator.clipboard.writeText) { @@ -92,12 +44,6 @@ export default function CodeTabsComponent({ }); }; - const type = (node, templateParam) => { - return node.data.node.template[templateParam].type; - }; - - const tabsOrder = getTabsOrder(isThereWH, isThereTweaks); - return ( - {tweaks && ( -
2 ? "hidden" : "flex gap-2"}> - - -
- )} + {nodes.length > 0 && + tabs.find((tab) => tab.name.toLowerCase() === "tweaks") && + tabs[activeTab].hasTweaks && ( +
+ + +
+ )} - {Number(activeTab) < 5 && ( + {tabs[activeTab].name.toLowerCase !== "tweaks" && ( <> - - - - ); - }, -); + return ( + + + <> + + + {data.node?.display_name ?? data.type} +
+ + ID: {data.id} + +
+
+ + + + +
+ +
+
+
+ ); +}; export default EditNodeModal; diff --git a/src/frontend/src/modals/genericModal/index.tsx b/src/frontend/src/modals/genericModal/index.tsx index 78b00e16e..3e698feda 100644 --- a/src/frontend/src/modals/genericModal/index.tsx +++ b/src/frontend/src/modals/genericModal/index.tsx @@ -147,8 +147,9 @@ export default function GenericModal({ if ( JSON.stringify(apiReturn.data?.frontend_node) !== JSON.stringify({}) ) { - if (setNodeClass) - setNodeClass(apiReturn.data?.frontend_node, inputValue); + setValue(inputValue); + apiReturn.data.frontend_node.template.template.value = inputValue; + if (setNodeClass) setNodeClass(apiReturn.data?.frontend_node); setModalOpen(closeModal); setIsEdit(false); } diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index f3cbebf96..6cc4bb090 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -1,4 +1,7 @@ +import useHandleOnNewValue from "@/CustomNodes/hooks/use-handle-new-value"; +import useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class"; import { usePostRetrieveVertexOrder } from "@/controllers/API/queries/vertex"; +import { APIClassType } from "@/types/api"; import _, { cloneDeep } from "lodash"; import { useEffect, useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; @@ -22,7 +25,6 @@ import useFlowStore from "../../../../stores/flowStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import { useShortcutsStore } from "../../../../stores/shortcuts"; import { useStoreStore } from "../../../../stores/storeStore"; -import { APIClassType } from "../../../../types/api"; import { nodeToolbarPropsType } from "../../../../types/components"; import { FlowType } from "../../../../types/flow"; import { @@ -31,7 +33,7 @@ import { expandGroupNode, updateFlowPosition, } from "../../../../utils/reactflowUtils"; -import { classNames, cn, isThereModal } from "../../../../utils/utils"; +import { classNames, cn } from "../../../../utils/utils"; import isWrappedWithClass from "../PageComponent/utils/is-wrapped-with-class"; import ToolbarSelectItem from "./toolbarSelectItem"; @@ -384,57 +386,20 @@ export default function NodeToolbarComponent({ const setNode = useFlowStore((state) => state.setNode); - const handleOnNewValue = ( - newValue: string | string[] | boolean | Object[], - ): void => { - if (data.node!.template[name].value !== newValue) { - takeSnapshot(); - } + const { handleOnNewValue: handleOnNewValueHook } = useHandleOnNewValue({ + node: data.node!, + nodeId: data.id, + name, + }); - data.node!.template[name].value = newValue; // necessary to enable ctrl+z inside the input - - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - - newNode.data = { - ...newNode.data, - }; - - newNode.data.node.template[name].value = newValue; - - return newNode; - }); + const handleOnNewValue = (value: string | string[]) => { + handleOnNewValueHook({ value }); }; - const handleNodeClass = ( - newNodeClass: APIClassType, - code?: string, - type?: string, - ): void => { - if (!data.node) return; - if (data.node!.template[name].value !== code) { - takeSnapshot(); - } + const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass(data.id); - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - - newNode.data = { - ...newNode.data, - node: newNodeClass, - description: newNodeClass.description ?? data.node!.description, - display_name: newNodeClass.display_name ?? data.node!.display_name, - }; - - if (type) { - newNode.data.type = type; - } - - newNode.data.node.template[name].value = code; - - return newNode; - }); - updateNodeInternals(data.id); + const handleNodeClass = (newNodeClass: APIClassType, type: string) => { + handleNodeClassHook(newNodeClass, type); }; const [openModal, setOpenModal] = useState(false); @@ -748,9 +713,7 @@ export default function NodeToolbarComponent({ {showModalAdvanced && ( diff --git a/src/frontend/src/stores/tweaksStore.ts b/src/frontend/src/stores/tweaksStore.ts index cbf035b6f..f5ab24273 100644 --- a/src/frontend/src/stores/tweaksStore.ts +++ b/src/frontend/src/stores/tweaksStore.ts @@ -1,9 +1,127 @@ +import { getChangesType } from "@/modals/apiModal/utils/get-changes-types"; +import { + getCurlRunCode, + getCurlWebhookCode, +} from "@/modals/apiModal/utils/get-curl-code"; +import getJsApiCode from "@/modals/apiModal/utils/get-js-api-code"; +import { getNodesWithDefaultValue } from "@/modals/apiModal/utils/get-nodes-with-default-value"; +import getPythonApiCode from "@/modals/apiModal/utils/get-python-api-code"; +import getPythonCode from "@/modals/apiModal/utils/get-python-code"; +import getWidgetCode from "@/modals/apiModal/utils/get-widget-code"; +import { createTabsArray } from "@/modals/apiModal/utils/tabs-array"; +import { FlowType, NodeDataType } from "@/types/flow"; +import { customStringify } from "@/utils/reactflowUtils"; import { create } from "zustand"; import { TweaksStoreType } from "../types/zustand/tweaks"; +import useFlowStore from "./flowStore"; export const useTweaksStore = create((set, get) => ({ - tweak: [], - setTweak: (tweak) => set({ tweak }), - tweaksList: [], - setTweaksList: (tweaksList) => set({ tweaksList }), + activeTweaks: false, + setActiveTweaks: (activeTweaks: boolean) => { + set({ activeTweaks }), get().refreshTabs(); + }, + nodes: [], + setNodes: (change) => { + let newChange = typeof change === "function" ? change(get().nodes) : change; + + set({ + nodes: newChange, + }); + get().refreshTabs(); + }, + setNode: (id, change) => { + let newChange = + typeof change === "function" + ? change(get().nodes.find((node) => node.id === id)!) + : change; + get().setNodes((oldNodes) => + oldNodes.map((node) => { + if (node.id === id) { + if ((node.data as NodeDataType).node?.frozen) { + (newChange.data as NodeDataType).node!.frozen = false; + } + return newChange; + } + return node; + }), + ); + }, + getNode: (id: string) => { + return get().nodes.find((node) => node.id === id); + }, + autoLogin: false, + flow: null, + initialSetup: (autoLogin: boolean, flow: FlowType) => { + useFlowStore.getState().unselectAll(); + set({ + nodes: getNodesWithDefaultValue(flow?.data?.nodes ?? []), + autoLogin, + flow, + }); + get().refreshTabs(); + }, + tabs: [], + refreshTabs: () => { + const autoLogin = get().autoLogin; + const flow = get().flow; + const tweak = {}; + const nodes = get().nodes; + const originalNodes = flow?.data?.nodes; + if (!flow) return; + + nodes.forEach((node) => { + const originalNodeTemplate = originalNodes?.find((n) => n.id === node.id) + ?.data?.node?.template; + const nodeTemplate = node.data?.node?.template; + if (originalNodeTemplate && nodeTemplate) { + const currentTweak = {}; + Object.keys(nodeTemplate).forEach((name) => { + if ( + customStringify(nodeTemplate[name]) !== + customStringify(originalNodeTemplate[name]) || + get().activeTweaks + ) { + currentTweak[name] = getChangesType( + nodeTemplate[name].value, + nodeTemplate[name], + ); + } + }); + tweak[node.id] = currentTweak; + } + }); + + const pythonApiCode = getPythonApiCode(flow?.id, autoLogin, tweak); + const runCurlCode = getCurlRunCode( + flow?.id, + autoLogin, + tweak, + flow?.endpoint_name, + ); + const jsApiCode = getJsApiCode( + flow?.id, + autoLogin, + tweak, + flow?.endpoint_name, + ); + const webhookCurlCode = getCurlWebhookCode( + flow?.id, + autoLogin, + flow?.endpoint_name, + ); + const pythonCode = getPythonCode(flow?.name, tweak); + const widgetCode = getWidgetCode(flow?.id, flow?.name, autoLogin); + + const codesArray = [ + runCurlCode, + webhookCurlCode, + pythonApiCode, + jsApiCode, + pythonCode, + widgetCode, + ]; + set({ + tabs: createTabsArray(codesArray, !!flow.webhook, nodes.length > 0), + }); + }, })); diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 8253f9e91..a4caee44b 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -1,3 +1,4 @@ +import { handleOnNewValueType } from "@/CustomNodes/hooks/use-handle-new-value"; import { ReactElement, ReactNode, SetStateAction } from "react"; import { ReactFlowJsonObject } from "reactflow"; import { InputOutput } from "../../constants/enums"; @@ -89,6 +90,7 @@ export type InputListComponentType = { editNode?: boolean; componentName?: string; playgroundDisabled?: boolean; + id?: string; }; export type InputGlobalComponentType = { @@ -105,9 +107,9 @@ export type KeyPairListComponentType = { onChange: (value: Object[]) => void; disabled: boolean; editNode?: boolean; - duplicateKey?: boolean; editNodeModal?: boolean; isList?: boolean; + id?: string; }; export type DictComponentType = { @@ -159,7 +161,7 @@ export type outputComponentType = { export type PromptAreaComponentType = { field_name?: string; nodeClass?: APIClassType; - setNodeClass?: (value: APIClassType, code?: string) => void; + setNodeClass?: (value: APIClassType) => void; disabled: boolean; onChange: ( value: string[] | string, @@ -183,7 +185,7 @@ export type CodeAreaComponentType = { value: string; editNode?: boolean; nodeClass?: APIClassType; - setNodeClass?: (value: APIClassType, code?: string) => void; + setNodeClass?: (value: APIClassType, type: string) => void; dynamic?: boolean; id?: string; readonly?: boolean; @@ -194,15 +196,11 @@ export type CodeAreaComponentType = { export type FileComponentType = { IOInputProps?; disabled: boolean; - onChange: ( - value: string[] | string, - dbValue?: boolean, - skipSnapshot?: boolean, - ) => void; + handleOnNewValue: handleOnNewValueType; value: string; fileTypes: Array; - onFileChange: (value: string) => void; editNode?: boolean; + id?: string; }; export type DisclosureComponentType = { @@ -622,11 +620,7 @@ export type codeAreaModalPropsType = { setOpenModal?: (bool: boolean) => void; value: string; nodeClass: APIClassType | undefined; - setNodeClass: ( - Class: APIClassType, - code?: string, - type?: string, - ) => void | undefined; + setNodeClass: (Class: APIClassType, type: string) => void | undefined; children: ReactNode; dynamic?: boolean; readonly?: boolean; @@ -655,7 +649,7 @@ export type genericModalPropsType = { type: number; disabled?: boolean; nodeClass?: APIClassType; - setNodeClass?: (Class: APIClassType, code?: string) => void; + setNodeClass?: (Class: APIClassType, type?: string) => void; children: ReactNode; id?: string; readonly?: boolean; @@ -701,53 +695,25 @@ export type cardComponentPropsType = { button?: JSX.Element; }; -type tabsArrayType = { +export type tabsArrayType = { code: string; image: string; language: string; mode: string; name: string; description?: string; -}; - -type getValueNodeType = { - id: string; - node: NodeType; - type: string; - value: null; -}; - -type codeTabsFuncTempType = { - [key: string]: string | boolean; + hasTweaks?: boolean; }; export type codeTabsPropsType = { - isThereTweaks?: boolean; - isThereWH?: boolean; - flow?: FlowType; + open: boolean; tabs: Array; activeTab: string; setActiveTab: (value: string) => void; isMessage?: boolean; - tweaks?: { - tweak?: tweakType; - tweaksList?: Array; - buildContent?: (value: string) => ReactNode; - getValue?: ( - value: string, - node: NodeType, - template: InputFieldType, - tweak: tweakType, - ) => string; - buildTweakObject?: ( - tw: string, - changes: string | string[] | boolean | number | Object[] | Object, - template: InputFieldType, - ) => Promise; - }; + tweaksNodes?: Array; activeTweaks?: boolean; setActiveTweaks?: (value: boolean) => void; - allowExport?: boolean; }; export type crashComponentPropsType = { diff --git a/src/frontend/src/types/zustand/tweaks/index.ts b/src/frontend/src/types/zustand/tweaks/index.ts index 3eb0a5f73..ed36126b3 100644 --- a/src/frontend/src/types/zustand/tweaks/index.ts +++ b/src/frontend/src/types/zustand/tweaks/index.ts @@ -1,8 +1,22 @@ -import { tweakType } from "../../components"; +import { FlowType, NodeType } from "@/types/flow"; +import { tabsArrayType } from "../../components"; export type TweaksStoreType = { - tweak: tweakType; - setTweak: (tweak: tweakType) => void; - tweaksList: string[]; - setTweaksList: (tweaksList: string[]) => void; + activeTweaks: boolean; + setActiveTweaks: (activeTweaks: boolean) => void; + nodes: NodeType[]; + setNodes: ( + update: NodeType[] | ((oldState: NodeType[]) => NodeType[]), + skipSave?: boolean, + ) => void; + setNode: ( + id: string, + update: NodeType | ((oldState: NodeType) => NodeType), + ) => void; + getNode: (id: string) => NodeType | undefined; + tabs: tabsArrayType[]; + initialSetup: (autoLogin: boolean, flow: FlowType) => void; + refreshTabs: () => void; + autoLogin: boolean; + flow: FlowType | null; }; diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index 7f8bbebd3..3c2089f10 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -1,4 +1,4 @@ -import { cloneDeep, get } from "lodash"; +import { cloneDeep } from "lodash"; import { Connection, Edge, @@ -1033,7 +1033,7 @@ export function mergeNodeTemplates({ }); return template; } -function isTargetHandleConnected( +export function isTargetHandleConnected( edges: Edge[], key: string, field: InputFieldType, @@ -1042,6 +1042,7 @@ function isTargetHandleConnected( /* this function receives a flow and a handleId and check if there is a connection with this handle */ + if (!field) return true; if (field.proxy) { if ( edges.some( diff --git a/src/frontend/tests/end-to-end/Basic Prompting.spec.ts b/src/frontend/tests/end-to-end/Basic Prompting.spec.ts index d1776ce80..4d8a2e231 100644 --- a/src/frontend/tests/end-to-end/Basic Prompting.spec.ts +++ b/src/frontend/tests/end-to-end/Basic Prompting.spec.ts @@ -54,7 +54,7 @@ test("Basic Prompting (Hello, World)", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForTimeout(2000); diff --git a/src/frontend/tests/end-to-end/Blog Writer.spec.ts b/src/frontend/tests/end-to-end/Blog Writer.spec.ts index ea8564a90..5bda7cfc4 100644 --- a/src/frontend/tests/end-to-end/Blog Writer.spec.ts +++ b/src/frontend/tests/end-to-end/Blog Writer.spec.ts @@ -51,18 +51,18 @@ test("Blog Writer", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForTimeout(2000); await page - .getByTestId("input-list-input_urls-0") + .getByTestId("inputlist_str_urls_0") .nth(0) .fill( "https://www.natgeokids.com/uk/discover/animals/sea-life/turtle-facts/", ); await page - .getByTestId("input-list-input_urls-1") + .getByTestId("inputlist_str_urls_1") .nth(0) .fill("https://www.originaldiving.com/blog/top-ten-turtle-facts"); diff --git a/src/frontend/tests/end-to-end/Complex Agent.spec.ts b/src/frontend/tests/end-to-end/Complex Agent.spec.ts index a934bcd6c..56a5f09ab 100644 --- a/src/frontend/tests/end-to-end/Complex Agent.spec.ts +++ b/src/frontend/tests/end-to-end/Complex Agent.spec.ts @@ -70,7 +70,7 @@ test("Complex Agent", async ({ page }) => { .nth(i) .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").nth(i).click(); + await page.getByTestId("dropdown_str_model_name").nth(i).click(); await page.getByTestId("gpt-4o-1-option").last().click(); await page.waitForTimeout(1000); diff --git a/src/frontend/tests/end-to-end/Document QA.spec.ts b/src/frontend/tests/end-to-end/Document QA.spec.ts index 4480b5ca5..4b275cf68 100644 --- a/src/frontend/tests/end-to-end/Document QA.spec.ts +++ b/src/frontend/tests/end-to-end/Document QA.spec.ts @@ -51,7 +51,7 @@ test("Document QA", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForTimeout(2000); diff --git a/src/frontend/tests/end-to-end/Hierarchical Tasks Agent.spec.ts b/src/frontend/tests/end-to-end/Hierarchical Tasks Agent.spec.ts index f73f7917c..095fef27b 100644 --- a/src/frontend/tests/end-to-end/Hierarchical Tasks Agent.spec.ts +++ b/src/frontend/tests/end-to-end/Hierarchical Tasks Agent.spec.ts @@ -65,12 +65,12 @@ test("Hierarchical Tasks Agent", async ({ page }) => { .nth(1) .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").first().click(); + await page.getByTestId("dropdown_str_model_name").first().click(); await page.getByTestId("gpt-4o-1-option").first().click(); await page.waitForTimeout(2000); - await page.getByTestId("dropdown-model_name").last().click(); + await page.getByTestId("dropdown_str_model_name").last().click(); await page.getByTestId("gpt-4o-1-option").last().click(); await page.waitForTimeout(2000); diff --git a/src/frontend/tests/end-to-end/Memory Chatbot.spec.ts b/src/frontend/tests/end-to-end/Memory Chatbot.spec.ts index c43b58792..331add22d 100644 --- a/src/frontend/tests/end-to-end/Memory Chatbot.spec.ts +++ b/src/frontend/tests/end-to-end/Memory Chatbot.spec.ts @@ -53,7 +53,7 @@ test("Memory Chatbot", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForTimeout(2000); diff --git a/src/frontend/tests/end-to-end/Sequential Tasks Agent.spec.ts b/src/frontend/tests/end-to-end/Sequential Tasks Agent.spec.ts index d7ad815ac..6c2098bd5 100644 --- a/src/frontend/tests/end-to-end/Sequential Tasks Agent.spec.ts +++ b/src/frontend/tests/end-to-end/Sequential Tasks Agent.spec.ts @@ -60,7 +60,7 @@ test("Sequential Tasks Agent", async ({ page }) => { .first() .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForTimeout(2000); diff --git a/src/frontend/tests/end-to-end/Vector Store.spec.ts b/src/frontend/tests/end-to-end/Vector Store.spec.ts index 8f44d3261..d7bc60cae 100644 --- a/src/frontend/tests/end-to-end/Vector Store.spec.ts +++ b/src/frontend/tests/end-to-end/Vector Store.spec.ts @@ -96,21 +96,21 @@ test("Vector Store RAG", async ({ page }) => { await page.waitForTimeout(2000); await page.getByTestId("button_run_astra db").first().click(); - await page.waitForSelector("text=built successfully", { timeout: 30000 }); + await page.waitForSelector("text=built successfully", { timeout: 60000 * 2 }); await page.getByText("built successfully").last().click({ timeout: 30000, }); await page.getByTestId("button_run_chat output").click(); - await page.waitForSelector("text=built successfully", { timeout: 30000 }); + await page.waitForSelector("text=built successfully", { timeout: 60000 * 2 }); await page.getByText("built successfully").last().click({ timeout: 30000, }); await page.getByTestId("button_run_astra db").last().click(); - await page.waitForSelector("text=built successfully", { timeout: 30000 }); + await page.waitForSelector("text=built successfully", { timeout: 60000 * 2 }); await page.getByText("built successfully").last().click({ timeout: 30000, diff --git a/src/frontend/tests/end-to-end/chatInputOutputUser-shard-0.spec.ts b/src/frontend/tests/end-to-end/chatInputOutputUser-shard-0.spec.ts index 0a390a000..c1fdd0dc5 100644 --- a/src/frontend/tests/end-to-end/chatInputOutputUser-shard-0.spec.ts +++ b/src/frontend/tests/end-to-end/chatInputOutputUser-shard-0.spec.ts @@ -55,7 +55,7 @@ test("user must be able to send an image on chat", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForSelector("text=Chat Input", { timeout: 30000 }); diff --git a/src/frontend/tests/end-to-end/chatInputOutputUser-shard-1.spec.ts b/src/frontend/tests/end-to-end/chatInputOutputUser-shard-1.spec.ts index 55771b9ac..9017b1042 100644 --- a/src/frontend/tests/end-to-end/chatInputOutputUser-shard-1.spec.ts +++ b/src/frontend/tests/end-to-end/chatInputOutputUser-shard-1.spec.ts @@ -55,7 +55,7 @@ test("user must be able to see output inspection", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForTimeout(2000); diff --git a/src/frontend/tests/end-to-end/chatInputOutputUser-shard-2.spec.ts b/src/frontend/tests/end-to-end/chatInputOutputUser-shard-2.spec.ts index c2f558665..d0c103192 100644 --- a/src/frontend/tests/end-to-end/chatInputOutputUser-shard-2.spec.ts +++ b/src/frontend/tests/end-to-end/chatInputOutputUser-shard-2.spec.ts @@ -55,7 +55,7 @@ test("user must interact with chat with Input/Output", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForTimeout(2000); @@ -86,7 +86,7 @@ test("user must interact with chat with Input/Output", async ({ page }) => { await page.keyboard.press("Escape"); await page - .getByTestId("textarea-input_value") + .getByTestId("textarea_str_input_value") .nth(0) .fill( "testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!", diff --git a/src/frontend/tests/end-to-end/decisionFlow.spec.ts b/src/frontend/tests/end-to-end/decisionFlow.spec.ts index d5c903940..733290eaf 100644 --- a/src/frontend/tests/end-to-end/decisionFlow.spec.ts +++ b/src/frontend/tests/end-to-end/decisionFlow.spec.ts @@ -80,12 +80,14 @@ test("should create a flow with decision", async ({ page }) => { await page.getByTestId("input-list-plus-btn_texts-0").click(); await page - .getByTestId("input-list-input_texts-0") + .getByTestId("inputlist_str_texts_0") + .first() .fill("big news! langflow 1.0 is out"); await page - .getByTestId("input-list-input_texts-1") + .getByTestId("inputlist_str_texts_1") + .first() .fill("uhul that movie was awesome"); - await page.getByTestId("input-list-input_texts-2").fill("love you babe"); + await page.getByTestId("inputlist_str_texts_2").first().fill("love you babe"); await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); @@ -108,15 +110,12 @@ test("should create a flow with decision", async ({ page }) => { await page.getByTestId("input-list-plus-btn_texts-0").last().click(); await page.getByTestId("input-list-plus-btn_texts-0").last().click(); + await page.getByTestId("inputlist_str_texts_0").last().fill("oh my cat died"); await page - .getByTestId("input-list-input_texts-0") - .last() - .fill("oh my cat died"); - await page - .getByTestId("input-list-input_texts-1") + .getByTestId("inputlist_str_texts_1") .last() .fill("No one loves me"); - await page.getByTestId("input-list-input_texts-2").last().fill("not cool.."); + await page.getByTestId("inputlist_str_texts_2").last().fill("not cool.."); await page.getByPlaceholder("Search").click(); await page.getByPlaceholder("Search").fill("parse data"); @@ -348,8 +347,8 @@ test("should create a flow with decision", async ({ page }) => { await page.mouse.up(); //edit prompt - await page.getByTestId("prompt-input-template").first().click(); - await page.getByTestId("modal-prompt-input-template").first().fill(` + await page.getByTestId("promptarea_prompt_template").first().click(); + await page.getByTestId("modal-promptarea_prompt_template").first().fill(` {Condition} Answer with either TRUE or FALSE (and nothing else). @@ -551,7 +550,7 @@ AI: .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.getByLabel("fit view").click(); @@ -570,7 +569,7 @@ AI: await page.getByTestId("icon-LucideSend").click(); await page.waitForSelector("text=🥲", { - timeout: 100000, + timeout: 1200000, }); await page.getByText("🥲").isVisible(); diff --git a/src/frontend/tests/end-to-end/dropdownComponent.spec.ts b/src/frontend/tests/end-to-end/dropdownComponent.spec.ts index ced3ee3f8..8cf60c9d7 100644 --- a/src/frontend/tests/end-to-end/dropdownComponent.spec.ts +++ b/src/frontend/tests/end-to-end/dropdownComponent.spec.ts @@ -46,22 +46,24 @@ test("dropDownComponent", async ({ page }) => { await page.getByTitle("zoom out").click(); await page.getByTestId("title-Amazon Bedrock").click(); - await page.getByTestId("dropdown-model_id").click(); + await page.getByTestId("dropdown_str_model_id").click(); - await page.getByTestId("value-dropdown-dropdown-model_id").click(); + await page + .getByTestId("anthropic.claude-3-haiku-20240307-v1:0-10-option") + .click(); let value = await page - .getByTestId("value-dropdown-dropdown-model_id") + .getByTestId("anthropic.claude-3-haiku-20240307-v1:0-10-option") .first() .innerText(); if (value !== "anthropic.claude-3-haiku-20240307-v1:0") { expect(false).toBeTruthy(); } - await page.getByTestId("dropdown-model_id").click(); + await page.getByTestId("dropdown_str_model_id").click(); await page.getByText("anthropic.claude-v2").last().click(); - value = await page.getByTestId("dropdown-model_id").innerText(); + value = await page.getByTestId("dropdown_str_model_id").innerText(); if (value !== "anthropic.claude-v2:1") { expect(false).toBeTruthy(); } @@ -71,7 +73,9 @@ test("dropDownComponent", async ({ page }) => { await page.getByTestId("more-options-modal").click(); await page.getByTestId("edit-button-modal").click(); - value = await page.getByTestId("dropdown-edit-model_id").innerText(); + value = await page + .getByTestId("value-dropdown-dropdown_str_edit_model_id") + .innerText(); if (value !== "anthropic.claude-v2:1") { expect(false).toBeTruthy(); } @@ -136,17 +140,21 @@ test("dropDownComponent", async ({ page }) => { await page.locator('//*[@id="showmodel_id"]').isChecked(), ).toBeTruthy(); - await page.getByTestId("dropdown-edit-model_id").click(); + await page.getByTestId("value-dropdown-dropdown_str_edit_model_id").click(); await page.getByText("cohere").last().click(); - value = await page.getByTestId("dropdown-edit-model_id").innerText(); + value = await page + .getByTestId("value-dropdown-dropdown_str_edit_model_id") + .innerText(); if (value !== "cohere.embed-multilingual-v3") { expect(false).toBeTruthy(); } await page.getByText("Close").last().click(); - value = await page.getByTestId("dropdown-model_id").innerText(); + value = await page + .getByTestId("value-dropdown-dropdown_str_model_id") + .innerText(); if (value !== "cohere.embed-multilingual-v3") { expect(false).toBeTruthy(); } diff --git a/src/frontend/tests/end-to-end/fileUploadComponent.spec.ts b/src/frontend/tests/end-to-end/fileUploadComponent.spec.ts index afc9e0824..30bee33d8 100644 --- a/src/frontend/tests/end-to-end/fileUploadComponent.spec.ts +++ b/src/frontend/tests/end-to-end/fileUploadComponent.spec.ts @@ -147,8 +147,15 @@ test("should be able to upload a file", async ({ page }) => { await page.mouse.up(); await page.getByText("Playground", { exact: true }).click(); + + await page.waitForSelector("text=Run Flow", { + timeout: 30000, + }); + await page.getByText("Run Flow", { exact: true }).click(); + await page.waitForTimeout(3000); + const textOutput = await page.getByPlaceholder("Empty").first().inputValue(); expect(textOutput).toContain("this is a test file"); diff --git a/src/frontend/tests/end-to-end/freeze-path.spec.ts b/src/frontend/tests/end-to-end/freeze-path.spec.ts index 4d2b45c69..fe1b1273a 100644 --- a/src/frontend/tests/end-to-end/freeze-path.spec.ts +++ b/src/frontend/tests/end-to-end/freeze-path.spec.ts @@ -8,146 +8,6 @@ test("user must be able to freeze a path", async ({ page }) => { "OPENAI_API_KEY required to run this test", ); - // const codeOpenAI = ` - // import operator - // from functools import reduce - - // from langchain_openai import ChatOpenAI - // from pydantic.v1 import SecretStr - - // from langflow.base.constants import STREAM_INFO_TEXT - // from langflow.base.models.model import LCModelComponent - // from langflow.base.models.openai_constants import MODEL_NAMES - // from langflow.field_typing import LanguageModel - // from langflow.inputs import ( - // BoolInput, - // DictInput, - // DropdownInput, - // FloatInput, - // IntInput, - // MessageInput, - // SecretStrInput, - // StrInput, - // ) - - // class OpenAIModelComponent(LCModelComponent): - // display_name = "OpenAI" - // description = "Generates text using OpenAI LLMs." - // icon = "OpenAI" - // name = "OpenAIModel" - - // inputs = [ - // MessageInput(name="input_value", display_name="Input"), - // IntInput( - // name="max_tokens", - // display_name="Max Tokens", - // advanced=True, - // info="The maximum number of tokens to generate. Set to 0 for unlimited tokens.", - // ), - // DictInput(name="model_kwargs", display_name="Model Kwargs", advanced=True), - // BoolInput( - // name="json_mode", - // display_name="JSON Mode", - // advanced=True, - // info="If True, it will output JSON regardless of passing a schema.", - // ), - // DictInput( - // name="output_schema", - // is_list=True, - // display_name="Schema", - // advanced=True, - // info="The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.", - // ), - // DropdownInput( - // name="model_name", display_name="Model Name", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0] - // ), - // StrInput( - // name="openai_api_base", - // display_name="OpenAI API Base", - // advanced=True, - // info="The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.", - // ), - // SecretStrInput( - // name="api_key", - // display_name="OpenAI API Key", - // info="The OpenAI API Key to use for the OpenAI model.", - // advanced=False, - // value="OPENAI_API_KEY", - // ), - // FloatInput(name="temperature", display_name="Temperature", value=0.1), - // BoolInput(name="stream", display_name="Stream", info=STREAM_INFO_TEXT, advanced=True), - // StrInput( - // name="system_message", - // display_name="System Message", - // info="System message to pass to the model.", - // advanced=True, - // ), - // IntInput( - // name="seed", - // display_name="Seed", - // info="The seed controls the reproducibility of the job.", - // advanced=True, - // value=1, - // ), - // ] - - // def build_model(self) -> LanguageModel: # type: ignore[type-var] - // # self.output_schema is a list of dictionaries - // # let's convert it to a dictionary - // output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {}) - // openai_api_key = self.api_key - // temperature = self.temperature - // model_name: str = self.model_name - // max_tokens = self.max_tokens - // model_kwargs = self.model_kwargs or {} - // openai_api_base = self.openai_api_base or "https://api.openai.com/v1" - // json_mode = bool(output_schema_dict) or self.json_mode - // seed = self.seed - - // if openai_api_key: - // api_key = SecretStr(openai_api_key) - // else: - // api_key = None - // output = ChatOpenAI( - // max_tokens=max_tokens or None, - // model_kwargs=model_kwargs, - // model=model_name, - // base_url=openai_api_base, - // api_key=api_key, - // temperature=0.8, - // seed=seed, - // ) - // if json_mode: - // if output_schema_dict: - // output = output.with_structured_output(schema=output_schema_dict, method="json_mode") # type: ignore - // else: - // output = output.bind(response_format={"type": "json_object"}) # type: ignore - - // return output # type: ignore - - // def _get_exception_message(self, e: Exception): - // """ - // Get a message from an OpenAI exception. - - // Args: - // exception (Exception): The exception to get the message from. - - // Returns: - // str: The message from the exception. - // """ - - // try: - // from openai import BadRequestError - // except ImportError: - // return - // if isinstance(e, BadRequestError): - // message = e.body.get("message") # type: ignore - // if message: - // return message - // return - - // `; - if (!process.env.CI) { dotenv.config({ path: path.resolve(__dirname, "../../.env") }); } @@ -195,23 +55,15 @@ test("user must be able to freeze a path", async ({ page }) => { .fill(process.env.OPENAI_API_KEY ?? ""); await page - .getByTestId("textarea-input_value") + .getByTestId("textarea_str_input_value") .first() .fill( "say a random number between 1 and 100000 and a random animal that lives in the sea", ); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); - // await page.getByText("OpenAI").first().click(); - - // await page.getByTestId("code-button-modal").first().click(); - - // await page.locator("textarea").press("Control+a"); - // await page.locator("textarea").fill(codeOpenAI); - // await page.locator('//*[@id="checkAndSaveBtn"]').click(); - await page.waitForTimeout(2000); await page.getByTestId("float-input").fill("1.0"); diff --git a/src/frontend/tests/end-to-end/generalBugs-shard-0.spec.ts b/src/frontend/tests/end-to-end/generalBugs-shard-0.spec.ts index 8678a21b0..154e67736 100644 --- a/src/frontend/tests/end-to-end/generalBugs-shard-0.spec.ts +++ b/src/frontend/tests/end-to-end/generalBugs-shard-0.spec.ts @@ -92,7 +92,7 @@ test("erase button should clear the chat messages", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForTimeout(2000); diff --git a/src/frontend/tests/end-to-end/generalBugs-shard-1.spec.ts b/src/frontend/tests/end-to-end/generalBugs-shard-1.spec.ts index fe7829ba3..339f50b5c 100644 --- a/src/frontend/tests/end-to-end/generalBugs-shard-1.spec.ts +++ b/src/frontend/tests/end-to-end/generalBugs-shard-1.spec.ts @@ -52,7 +52,7 @@ test("should delete rows from table message", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForTimeout(2000); diff --git a/src/frontend/tests/end-to-end/generalBugs-shard-3.spec.ts b/src/frontend/tests/end-to-end/generalBugs-shard-3.spec.ts index 575b553f1..cef4b1951 100644 --- a/src/frontend/tests/end-to-end/generalBugs-shard-3.spec.ts +++ b/src/frontend/tests/end-to-end/generalBugs-shard-3.spec.ts @@ -103,7 +103,7 @@ test("should copy code from playground modal", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); const elementsChatInput = await page diff --git a/src/frontend/tests/end-to-end/inputListComponent.spec.ts b/src/frontend/tests/end-to-end/inputListComponent.spec.ts index 7992a429b..ed2d4a237 100644 --- a/src/frontend/tests/end-to-end/inputListComponent.spec.ts +++ b/src/frontend/tests/end-to-end/inputListComponent.spec.ts @@ -39,34 +39,28 @@ test("InputListComponent", async ({ page }) => { await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); - await page.getByTestId("input-list-input_urls-0").fill("test test test test"); + await page.getByTestId("inputlist_str_urls_0").fill("test test test test"); await page.getByTestId("input-list-plus-btn_urls-0").click(); await page.getByTestId("input-list-plus-btn_urls-0").click(); await page - .getByTestId("input-list-input_urls-1") + .getByTestId("inputlist_str_urls_1") .fill("test1 test1 test1 test1"); await page - .getByTestId("input-list-input_urls-2") + .getByTestId("inputlist_str_urls_2") .fill("test2 test2 test2 test2"); await page.getByTestId("div-generic-node").click(); await page.getByTestId("more-options-modal").click(); await page.getByTestId("edit-button-modal").click(); - const value0 = await page - .getByTestId("input-list-input-edit_urls-0") - .inputValue(); - const value1 = await page - .getByTestId("input-list-input-edit_urls-1") - .inputValue(); + const value0 = await page.getByTestId("inputlist_str_urls_0").inputValue(); + const value1 = await page.getByTestId("inputlist_str_urls_1").inputValue(); - const value2 = await page - .getByTestId("input-list-input-edit_urls-2") - .inputValue(); + const value2 = await page.getByTestId("inputlist_str_urls_2").inputValue(); if ( value0 !== "test test test test" || @@ -94,15 +88,15 @@ test("InputListComponent", async ({ page }) => { await page.getByTestId("input-list-plus-btn_urls-0").click(); await page.getByTestId("input-list-plus-btn_urls-0").click(); - await page.getByTestId("input-list-input_urls-0").fill("test test test test"); + await page.getByTestId("inputlist_str_urls_0").fill("test test test test"); await page - .getByTestId("input-list-input_urls-1") + .getByTestId("inputlist_str_urls_1") .fill("test1 test1 test1 test1"); await page - .getByTestId("input-list-input_urls-2") + .getByTestId("inputlist_str_urls_2") .fill("test2 test2 test2 test2"); await page - .getByTestId("input-list-input_urls-3") + .getByTestId("inputlist_str_urls_3") .fill("test3 test3 test3 test3"); await page.getByTestId("div-generic-node").click(); @@ -110,16 +104,16 @@ test("InputListComponent", async ({ page }) => { await page.getByTestId("edit-button-modal").click(); const value0Edit = await page - .getByTestId("input-list-input-edit_urls-0") + .getByTestId("inputlist_str_edit_urls_0") .inputValue(); const value1Edit = await page - .getByTestId("input-list-input-edit_urls-1") + .getByTestId("inputlist_str_edit_urls_1") .inputValue(); const value2Edit = await page - .getByTestId("input-list-input-edit_urls-2") + .getByTestId("inputlist_str_edit_urls_2") .inputValue(); const value3Edit = await page - .getByTestId("input-list-input-edit_urls-3") + .getByTestId("inputlist_str_edit_urls_3") .inputValue(); if ( diff --git a/src/frontend/tests/end-to-end/intComponent.spec.ts b/src/frontend/tests/end-to-end/intComponent.spec.ts index 82e687fbd..9cc346007 100644 --- a/src/frontend/tests/end-to-end/intComponent.spec.ts +++ b/src/frontend/tests/end-to-end/intComponent.spec.ts @@ -52,21 +52,19 @@ test("IntComponent", async ({ page }) => { await page.getByTestId("showmax_tokens").click(); await page.getByText("Close").last().click(); - await page.getByTestId("int-input-max_tokens").click(); - await page - .getByTestId("int-input-max_tokens") - .fill("123456789123456789123456789"); + await page.getByTestId("int_int_max_tokens").click(); + await page.getByTestId("int_int_max_tokens").fill("1020304050"); - let value = await page.getByTestId("int-input-max_tokens").inputValue(); + let value = await page.getByTestId("int_int_max_tokens").inputValue(); - if (value != "123456789123456789123456789") { + if (value != "1020304050") { expect(false).toBeTruthy(); } - await page.getByTestId("int-input-max_tokens").click(); - await page.getByTestId("int-input-max_tokens").fill("0"); + await page.getByTestId("int_int_max_tokens").click(); + await page.getByTestId("int_int_max_tokens").fill("0"); - value = await page.getByTestId("int-input-max_tokens").inputValue(); + value = await page.getByTestId("int_int_max_tokens").inputValue(); if (value != "0") { expect(false).toBeTruthy(); @@ -86,16 +84,14 @@ test("IntComponent", async ({ page }) => { await page.getByTestId("more-options-modal").click(); await page.getByTestId("edit-button-modal").click(); - value = await page.getByTestId("edit-int-input-max_tokens").inputValue(); + value = await page.getByTestId("int_int_edit_max_tokens").inputValue(); if (value != "0") { expect(false).toBeTruthy(); } - await page.getByTestId("edit-int-input-max_tokens").click(); - await page - .getByTestId("edit-int-input-max_tokens") - .fill("123456789123456789123456789"); + await page.getByTestId("int_int_edit_max_tokens").click(); + await page.getByTestId("int_int_edit_max_tokens").fill("60708090"); await page.locator('//*[@id="showmodel_kwargs"]').click(); expect( @@ -167,33 +163,29 @@ test("IntComponent", async ({ page }) => { await page.getByTestId("more-options-modal").click(); await page.getByTestId("edit-button-modal").click(); - await page.locator('//*[@id="showtimeout"]').click(); - expect( - await page.locator('//*[@id="showtimeout"]').isChecked(), - ).toBeTruthy(); - const valueEditNode = await page - .getByTestId("edit-int-input-max_tokens") + .getByTestId("int_int_max_tokens") .inputValue(); - if (valueEditNode != "123456789123456789123456789") { + if (valueEditNode != "128000") { expect(false).toBeTruthy(); } await page.getByText("Close").last().click(); - await page.getByTestId("int-input-max_tokens").click(); - await page.getByTestId("int-input-max_tokens").fill("3"); + await page.getByTestId("int_int_max_tokens").click(); + await page.getByTestId("int_int_max_tokens").fill("3"); - let value = await page.getByTestId("int-input-max_tokens").inputValue(); + let value = await page.getByTestId("int_int_max_tokens").inputValue(); if (value != "3") { expect(false).toBeTruthy(); } - await page.getByTestId("int-input-max_tokens").click(); - await page.getByTestId("int-input-max_tokens").fill("-3"); + await page.getByTestId("int_int_max_tokens").click(); + await page.getByTestId("int_int_max_tokens").fill("-3"); + await page.getByTestId("div-generic-node").click(); - value = await page.getByTestId("int-input-max_tokens").inputValue(); + value = await page.getByTestId("int_int_max_tokens").inputValue(); if (value != "0") { expect(false).toBeTruthy(); diff --git a/src/frontend/tests/end-to-end/logs.spec.ts b/src/frontend/tests/end-to-end/logs.spec.ts index 2ee2779cc..617910534 100644 --- a/src/frontend/tests/end-to-end/logs.spec.ts +++ b/src/frontend/tests/end-to-end/logs.spec.ts @@ -52,7 +52,7 @@ test("should able to see and interact with logs", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForTimeout(2000); diff --git a/src/frontend/tests/end-to-end/nestedComponent.spec.ts b/src/frontend/tests/end-to-end/nestedComponent.spec.ts index a7a413938..bcfdad821 100644 --- a/src/frontend/tests/end-to-end/nestedComponent.spec.ts +++ b/src/frontend/tests/end-to-end/nestedComponent.spec.ts @@ -28,151 +28,73 @@ test("NestedComponent", async ({ page }) => { }); await page.getByTestId("extended-disclosure").click(); await page.getByPlaceholder("Search").click(); - await page.getByPlaceholder("Search").fill("pinecone"); + await page.getByPlaceholder("Search").fill("api request"); await page.waitForTimeout(1000); await page - .getByTestId("vectorstoresPinecone") + .getByTestId("dataAPI Request") .first() .dragTo(page.locator('//*[@id="react-flow-id"]')); await page.click('//*[@id="react-flow-id"]'); + await page.getByTitle("fit view").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + + await page.getByTestId("dict_nesteddict_headers").first().click(); + await page + .getByText("{") + .last() + .hover() + .then(async () => { + await page.locator(".json-view--edit").first().click(); + await page.locator(".json-view--input").first().fill("keytest"); + await page.locator(".json-view--edit").first().click(); + + await page.locator(".json-view--edit").first().click(); + await page.locator(".json-view--input").first().fill("keytest1"); + await page.locator(".json-view--edit").first().click(); + + await page.locator(".json-view--edit").first().click(); + await page.locator(".json-view--input").first().fill("keytest2"); + await page.locator(".json-view--edit").first().click(); + }); + + await page + .locator(".json-view--pair") + .first() + .hover() + .then(async () => { + await page.locator(".json-view--edit").nth(2).click(); + await page.locator(".json-view--null").first().fill("proptest1"); + await page.locator(".json-view--edit").nth(2).click(); + }); + + await page.getByText("Save").last().click(); + + await page.getByTestId("div-generic-node").click(); + await page.getByTestId("more-options-modal").click(); await page.getByTestId("edit-button-modal").click(); - // showindex_name - await page.locator('//*[@id="showindex_name"]').click(); + await page.getByTestId("dict_nesteddict_edit_headers").first().click(); - expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), - ).toBeFalsy(); + expect(await page.getByText("keytest", { exact: true }).count()).toBe(1); + expect(await page.getByText("keytest1", { exact: true }).count()).toBe(1); + expect(await page.getByText("keytest2", { exact: true }).count()).toBe(1); + expect(await page.getByText("proptest1").count()).toBe(1); - // shownamespace - await page.locator('//*[@id="shownamespace"]').click(); + await page + .locator(".json-view--pair") + .first() + .hover() + .then(async () => { + await page.locator(".json-view--edit").nth(3).click(); + await page.locator(".json-view--edit").nth(2).click(); + }); - expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), - ).toBeFalsy(); - - // showpinecone_api_key - await page.locator('//*[@id="showpinecone_api_key"]').click(); - - expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), - ).toBeFalsy(); - - // showindex_name - await page.locator('//*[@id="showindex_name"]').click(); - - expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), - ).toBeTruthy(); - - // shownamespace - await page.locator('//*[@id="shownamespace"]').click(); - - expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), - ).toBeTruthy(); - - // showpinecone_api_key - await page.locator('//*[@id="showpinecone_api_key"]').click(); - - expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), - ).toBeTruthy(); - - // showindex_name - await page.locator('//*[@id="showindex_name"]').click(); - - expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), - ).toBeFalsy(); - - // shownamespace - await page.locator('//*[@id="shownamespace"]').click(); - - expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), - ).toBeFalsy(); - - // showpinecone_api_key - await page.locator('//*[@id="showpinecone_api_key"]').click(); - - expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), - ).toBeFalsy(); - - // showindex_name - await page.locator('//*[@id="showindex_name"]').click(); - - expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), - ).toBeTruthy(); - - // shownamespace - await page.locator('//*[@id="shownamespace"]').click(); - - expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), - ).toBeTruthy(); - - // showpinecone_api_key - await page.locator('//*[@id="showpinecone_api_key"]').click(); - - expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), - ).toBeTruthy(); - - // showindex_name - await page.locator('//*[@id="showindex_name"]').click(); - - expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), - ).toBeFalsy(); - - // shownamespace - await page.locator('//*[@id="shownamespace"]').click(); - - expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), - ).toBeFalsy(); - - // showpinecone_api_key - await page.locator('//*[@id="showpinecone_api_key"]').click(); - - expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), - ).toBeFalsy(); - - // showindex_name - await page.locator('//*[@id="showindex_name"]').click(); - - expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), - ).toBeTruthy(); - - // shownamespace - await page.locator('//*[@id="shownamespace"]').click(); - - expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), - ).toBeTruthy(); - - // showpinecone_api_key - await page.locator('//*[@id="showpinecone_api_key"]').click(); - - expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), - ).toBeTruthy(); - - //showtext_key - await page.locator('//*[@id="showtext_key"]').click(); - - expect( - await page.locator('//*[@id="showtext_key"]').isChecked(), - ).toBeTruthy(); - - await page.getByText("Close").last().click(); + expect(await page.getByText("keytest", { exact: true }).count()).toBe(0); + expect(await page.getByText("proptest1").count()).toBe(0); }); diff --git a/src/frontend/tests/end-to-end/promptModalComponent.spec.ts b/src/frontend/tests/end-to-end/promptModalComponent.spec.ts index fb24a243e..ed3febeed 100644 --- a/src/frontend/tests/end-to-end/promptModalComponent.spec.ts +++ b/src/frontend/tests/end-to-end/promptModalComponent.spec.ts @@ -41,14 +41,14 @@ test("PromptTemplateComponent", async ({ page }) => { await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); - await page.getByTestId("prompt-input-template").click(); + await page.getByTestId("promptarea_prompt_template").click(); await page - .getByTestId("modal-prompt-input-template") + .getByTestId("modal-promptarea_prompt_template") .fill("{prompt} example {prompt1}"); let value = await page - .getByTestId("modal-prompt-input-template") + .getByTestId("modal-promptarea_prompt_template") .inputValue(); if (value != "{prompt} example {prompt1}") { @@ -67,10 +67,10 @@ test("PromptTemplateComponent", async ({ page }) => { await page.getByTestId("genericModalBtnSave").click(); - await page.getByTestId("div-textarea-prompt").click(); - await page.getByTestId("textarea-prompt").fill("prompt_value_!@#!@#"); + await page.getByTestId("textarea_str_prompt").click(); + await page.getByTestId("textarea_str_prompt").fill("prompt_value_!@#!@#"); - value = await page.getByTestId("textarea-prompt").inputValue(); + value = await page.getByTestId("textarea_str_prompt").inputValue(); if (value != "prompt_value_!@#!@#") { expect(false).toBeTruthy(); @@ -79,7 +79,6 @@ test("PromptTemplateComponent", async ({ page }) => { await page.getByTestId("div-generic-node").click(); await page.getByTestId("more-options-modal").click(); - await page.getByTestId("save-button-modal").click(); const replace = await page.getByTestId("replace-button").isVisible(); @@ -88,18 +87,12 @@ test("PromptTemplateComponent", async ({ page }) => { await page.getByTestId("replace-button").click(); } - await page.getByTestId("div-textarea-prompt1").click(); + await page.getByTestId("textarea_str_prompt1").click(); await page - .getByTestId("textarea-prompt1") + .getByTestId("textarea_str_prompt1") .fill("prompt_name_test_123123!@#!@#"); - value = await page.getByTestId("textarea-prompt1").inputValue(); - - if (value != "prompt_name_test_123123!@#!@#") { - expect(false).toBeTruthy(); - } - - value = await page.getByTestId("textarea-prompt1").inputValue(); + value = await page.getByTestId("textarea_str_prompt1").inputValue(); if (value != "prompt_name_test_123123!@#!@#") { expect(false).toBeTruthy(); @@ -109,36 +102,38 @@ test("PromptTemplateComponent", async ({ page }) => { await page.getByTestId("edit-button-modal").click(); value = - (await page.locator('//*[@id="textarea-edit-prompt"]').textContent()) ?? ""; + (await page.locator('//*[@id="textarea_str_edit_prompt"]').textContent()) ?? + ""; if (value != "prompt_value_!@#!@#") { expect(false).toBeTruthy(); } value = - (await page.locator('//*[@id="textarea-edit-prompt1"]').textContent()) ?? - ""; + (await page + .locator('//*[@id="textarea_str_edit_prompt1"]') + .textContent()) ?? ""; if (value != "prompt_name_test_123123!@#!@#") { expect(false).toBeTruthy(); } value = await page - .locator('//*[@id="prompt-area-edit-template"]') + .locator('//*[@id="promptarea_prompt_edit_template"]') .innerText(); if (value != "{prompt} example {prompt1}") { expect(false).toBeTruthy(); } - await page.locator('//*[@id="textarea-edit-prompt1"]').click(); + await page.locator('//*[@id="textarea_str_edit_prompt1"]').click(); await page .getByTestId("text-area-modal") .fill("prompt_edit_test_12312312321!@#$"); await page.getByText("Finish Editing", { exact: true }).click(); - await page.locator('//*[@id="textarea-edit-prompt"]').click(); + await page.locator('//*[@id="textarea_str_edit_prompt"]').click(); await page .getByTestId("text-area-modal") .fill("prompt_edit_test_44444444444!@#$"); @@ -191,22 +186,24 @@ test("PromptTemplateComponent", async ({ page }) => { expect(await page.locator('//*[@id="showprompt1"]').isChecked()).toBeTruthy(); value = - (await page.locator('//*[@id="textarea-edit-prompt"]').textContent()) ?? ""; + (await page.locator('//*[@id="textarea_str_edit_prompt"]').textContent()) ?? + ""; if (value != "prompt_edit_test_44444444444!@#$") { expect(false).toBeTruthy(); } value = - (await page.locator('//*[@id="textarea-edit-prompt1"]').textContent()) ?? - ""; + (await page + .locator('//*[@id="textarea_str_edit_prompt1"]') + .textContent()) ?? ""; if (value != "prompt_edit_test_12312312321!@#$") { expect(false).toBeTruthy(); } value = await page - .locator('//*[@id="prompt-area-edit-template"]') + .locator('//*[@id="promptarea_prompt_edit_template"]') .innerText(); if (value != "{prompt} example {prompt1}") { diff --git a/src/frontend/tests/end-to-end/textAreaModalComponent.spec.ts b/src/frontend/tests/end-to-end/textAreaModalComponent.spec.ts index bedb4eb9f..77df22c34 100644 --- a/src/frontend/tests/end-to-end/textAreaModalComponent.spec.ts +++ b/src/frontend/tests/end-to-end/textAreaModalComponent.spec.ts @@ -52,9 +52,9 @@ test("TextAreaModalComponent", async ({ page }) => { await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); - await page.getByTestId("prompt-input-template").click(); + await page.getByTestId("promptarea_prompt_template").click(); - await page.getByTestId("modal-prompt-input-template").fill("{text}"); + await page.getByTestId("modal-promptarea_prompt_template").fill("{text}"); let valueBadgeOne = await page.locator('//*[@id="badge0"]').innerText(); if (valueBadgeOne != "text") { @@ -64,12 +64,12 @@ test("TextAreaModalComponent", async ({ page }) => { await page.getByTestId("genericModalBtnSave").click(); await page - .getByTestId("textarea-text") + .getByTestId("textarea_str_text") .fill( "test test test test test test test test test test test !@#%*)( 123456789101010101010101111111111 !!!!!!!!!!", ); - await page.getByTestId("textarea-text-ExternalLink").click(); + await page.getByTestId("textarea_str_text-ExternalLink").click(); await page.waitForTimeout(500); @@ -86,7 +86,9 @@ test("TextAreaModalComponent", async ({ page }) => { await page.getByTestId("genericModalBtnSave").click(); - const valueTextArea = await page.getByTestId("textarea-text").inputValue(); + const valueTextArea = await page + .getByTestId("textarea_str_text") + .inputValue(); if (valueTextArea != "test123123") { expect(false).toBeTruthy(); diff --git a/src/frontend/tests/end-to-end/textInputOutput.spec.ts b/src/frontend/tests/end-to-end/textInputOutput.spec.ts index c7c32621b..4ea2a258a 100644 --- a/src/frontend/tests/end-to-end/textInputOutput.spec.ts +++ b/src/frontend/tests/end-to-end/textInputOutput.spec.ts @@ -193,7 +193,7 @@ test("TextInputOutputComponent", async ({ page }) => { .getByTestId("popover-anchor-input-api_key") .fill(process.env.OPENAI_API_KEY ?? ""); - await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("dropdown_str_model_name").click(); await page.getByTestId("gpt-4o-1-option").click(); await page.waitForTimeout(2000); diff --git a/src/frontend/tests/end-to-end/toggleComponent.spec.ts b/src/frontend/tests/end-to-end/toggleComponent.spec.ts index 118095984..3eb111278 100644 --- a/src/frontend/tests/end-to-end/toggleComponent.spec.ts +++ b/src/frontend/tests/end-to-end/toggleComponent.spec.ts @@ -70,20 +70,30 @@ test("ToggleComponent", async ({ page }) => { await page.getByTitle("fit view").click(); - await page.getByTestId("toggle-load_hidden").click(); - expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeTruthy(); + await page.getByTestId("toggle_bool_load_hidden").click(); + expect( + await page.getByTestId("toggle_bool_load_hidden").isChecked(), + ).toBeTruthy(); - await page.getByTestId("toggle-load_hidden").click(); - expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeFalsy(); + await page.getByTestId("toggle_bool_load_hidden").click(); + expect( + await page.getByTestId("toggle_bool_load_hidden").isChecked(), + ).toBeFalsy(); - await page.getByTestId("toggle-load_hidden").click(); - expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeTruthy(); + await page.getByTestId("toggle_bool_load_hidden").click(); + expect( + await page.getByTestId("toggle_bool_load_hidden").isChecked(), + ).toBeTruthy(); - await page.getByTestId("toggle-load_hidden").click(); - expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeFalsy(); + await page.getByTestId("toggle_bool_load_hidden").click(); + expect( + await page.getByTestId("toggle_bool_load_hidden").isChecked(), + ).toBeFalsy(); - await page.getByTestId("toggle-load_hidden").click(); - expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeTruthy(); + await page.getByTestId("toggle_bool_load_hidden").click(); + expect( + await page.getByTestId("toggle_bool_load_hidden").isChecked(), + ).toBeTruthy(); await page.getByTestId("div-generic-node").click(); @@ -99,7 +109,9 @@ test("ToggleComponent", async ({ page }) => { await page.getByTestId("more-options-modal").click(); await page.getByTestId("edit-button-modal").click(); - expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeTruthy(); + expect( + await page.getByTestId("toggle_bool_load_hidden").isChecked(), + ).toBeTruthy(); await page.locator('//*[@id="showload_hidden"]').click(); expect( @@ -154,7 +166,7 @@ test("ToggleComponent", async ({ page }) => { await page.getByText("Close").last().click(); - const plusButtonLocator = page.getByTestId("toggle-load_hidden"); + const plusButtonLocator = page.getByTestId("toggle_bool_load_hidden"); const elementCount = await plusButtonLocator?.count(); if (elementCount === 0) { expect(true).toBeTruthy(); @@ -170,34 +182,34 @@ test("ToggleComponent", async ({ page }) => { ).toBeTruthy(); expect( - await page.getByTestId("toggle-edit-load_hidden").isChecked(), + await page.getByTestId("toggle_bool_load_hidden").isChecked(), ).toBeTruthy(); await page.getByText("Close").last().click(); - await page.getByTestId("toggle-load_hidden").click(); + await page.getByTestId("toggle_bool_load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle_bool_load_hidden").isChecked(), ).toBeFalsy(); - await page.getByTestId("toggle-load_hidden").click(); + await page.getByTestId("toggle_bool_load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle_bool_load_hidden").isChecked(), ).toBeTruthy(); - await page.getByTestId("toggle-load_hidden").click(); + await page.getByTestId("toggle_bool_load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle_bool_load_hidden").isChecked(), ).toBeFalsy(); - await page.getByTestId("toggle-load_hidden").click(); + await page.getByTestId("toggle_bool_load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle_bool_load_hidden").isChecked(), ).toBeTruthy(); - await page.getByTestId("toggle-load_hidden").click(); + await page.getByTestId("toggle_bool_load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle_bool_load_hidden").isChecked(), ).toBeFalsy(); } }); diff --git a/src/frontend/tests/end-to-end/tweaksTest.spec.ts b/src/frontend/tests/end-to-end/tweaksTest.spec.ts index 5fba18a44..b5a7fa49f 100644 --- a/src/frontend/tests/end-to-end/tweaksTest.spec.ts +++ b/src/frontend/tests/end-to-end/tweaksTest.spec.ts @@ -35,18 +35,14 @@ test("curl_api_generation", async ({ page, context }) => { .locator("div") .first() .click(); + + await page.waitForTimeout(1000); + await page - .getByRole("textbox", { name: "Type something..." }) - .first() - .click(); - await page - .getByRole("textbox", { name: "Type something..." }) - .first() - .press("Control+a"); - await page - .getByRole("textbox", { name: "Type something..." }) + .getByTestId("popover-anchor-input-openai_api_base-edit") .first() .fill("teste"); + await page.getByRole("tab", { name: "cURL" }).click(); await page.getByTestId("icon-Copy").click(); const handle2 = await page.evaluateHandle(() =>