From d02d11213e0fd76f9f38f488e8b571b985de0302 Mon Sep 17 00:00:00 2001 From: Jeremy Naccache Date: Wed, 31 May 2023 17:00:18 +0200 Subject: [PATCH 01/12] fix(chatModal/index.ts) updated validation condition for required properties of nodes, to allow False values to be accepted --- src/frontend/src/modals/chatModal/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/modals/chatModal/index.tsx b/src/frontend/src/modals/chatModal/index.tsx index c6a86daaf..46d62266b 100644 --- a/src/frontend/src/modals/chatModal/index.tsx +++ b/src/frontend/src/modals/chatModal/index.tsx @@ -290,7 +290,9 @@ export default function ChatModal({ errors.concat( template[t].required && template[t].show && - (!template[t].value || template[t].value === "") && + (template[t].value === undefined || + template[t].value === null || + template[t].value === "") && !reactFlowInstance .getEdges() .some( From 893dc22fc41aa2dc4773fb05af38c520c0ea7abd Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Wed, 31 May 2023 21:21:51 -0300 Subject: [PATCH 02/12] Change the icon to move just in the last message --- src/frontend/src/modals/chatModal/chatMessage/index.tsx | 5 ++++- src/frontend/src/modals/chatModal/index.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/modals/chatModal/chatMessage/index.tsx b/src/frontend/src/modals/chatModal/chatMessage/index.tsx index 2db84adbc..85ebb61af 100644 --- a/src/frontend/src/modals/chatModal/chatMessage/index.tsx +++ b/src/frontend/src/modals/chatModal/chatMessage/index.tsx @@ -16,10 +16,13 @@ import Convert from "ansi-to-html"; export default function ChatMessage({ chat, lockChat, + lastMessage }: { chat: ChatMessageType; lockChat: boolean; + lastMessage: boolean; }) { + const convert = new Convert({ newline: true }); const [message, setMessage] = useState(""); const imgRef = useRef(null); @@ -48,7 +51,7 @@ export default function ChatMessage({ "absolute transition-opacity duration-500 scale-150 " + (lockChat ? "opacity-100" : "opacity-0") } - src={AiIcon} + src={lastMessage ? AiIcon : AiIconStill} /> {chatHistory.length > 0 ? ( chatHistory.map((c, i) => ( - + )) ) : (
From 6332c4c0840f8e355bb862b09300bff3b924abcb Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Wed, 31 May 2023 21:22:42 -0300 Subject: [PATCH 03/12] Changing the icon to move just in the last message on chat --- src/frontend/src/modals/chatModal/chatMessage/index.tsx | 3 +-- src/frontend/src/modals/chatModal/index.tsx | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/modals/chatModal/chatMessage/index.tsx b/src/frontend/src/modals/chatModal/chatMessage/index.tsx index 85ebb61af..003c21694 100644 --- a/src/frontend/src/modals/chatModal/chatMessage/index.tsx +++ b/src/frontend/src/modals/chatModal/chatMessage/index.tsx @@ -16,13 +16,12 @@ import Convert from "ansi-to-html"; export default function ChatMessage({ chat, lockChat, - lastMessage + lastMessage, }: { chat: ChatMessageType; lockChat: boolean; lastMessage: boolean; }) { - const convert = new Convert({ newline: true }); const [message, setMessage] = useState(""); const imgRef = useRef(null); diff --git a/src/frontend/src/modals/chatModal/index.tsx b/src/frontend/src/modals/chatModal/index.tsx index 1c17a21e0..cf2b52aac 100644 --- a/src/frontend/src/modals/chatModal/index.tsx +++ b/src/frontend/src/modals/chatModal/index.tsx @@ -416,7 +416,12 @@ export default function ChatModal({ > {chatHistory.length > 0 ? ( chatHistory.map((c, i) => ( - + )) ) : (
From 9e4f898f54bdc18f99a06313466906d9555306a0 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Wed, 31 May 2023 21:38:06 -0300 Subject: [PATCH 04/12] Changing the mouse cursor on Canva's React Flow --- src/frontend/src/index.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/frontend/src/index.css b/src/frontend/src/index.css index 899fb1c87..84ede36f1 100644 --- a/src/frontend/src/index.css +++ b/src/frontend/src/index.css @@ -15,3 +15,11 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } + +/* +The style below sets the cursor property of the element with the class .react-flow__pane to the default cursor. +The cursor: default; property value restores the browser's default cursor style for the targeted element. By applying this style, the element will no longer have a custom cursor appearance such as "grab" or any other custom cursor defined elsewhere in the application. Instead, it will revert to the default cursor style determined by the browser, typically an arrow-shaped cursor. +*/ +.react-flow__pane{ + cursor: default; +} \ No newline at end of file From d9cae6b211a82a849ce03c42ad784b49b4e30641 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Wed, 31 May 2023 22:00:03 -0300 Subject: [PATCH 05/12] fixing identation warnings --- src/frontend/src/index.css | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/frontend/src/index.css b/src/frontend/src/index.css index 84ede36f1..f4981616c 100644 --- a/src/frontend/src/index.css +++ b/src/frontend/src/index.css @@ -16,10 +16,8 @@ code { monospace; } -/* -The style below sets the cursor property of the element with the class .react-flow__pane to the default cursor. -The cursor: default; property value restores the browser's default cursor style for the targeted element. By applying this style, the element will no longer have a custom cursor appearance such as "grab" or any other custom cursor defined elsewhere in the application. Instead, it will revert to the default cursor style determined by the browser, typically an arrow-shaped cursor. -*/ -.react-flow__pane{ +/*The style below sets the cursor property of the element with the class .react-flow__pane to the default cursor. +The cursor: default; property value restores the browser's default cursor style for the targeted element. By applying this style, the element will no longer have a custom cursor appearance such as "grab" or any other custom cursor defined elsewhere in the application. Instead, it will revert to the default cursor style determined by the browser, typically an arrow-shaped cursor.*/ +.react-flow__pane { cursor: default; -} \ No newline at end of file +} \ No newline at end of file From 344b94f9b5d523e13edf6f4b61c4142af94ff24e Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Wed, 31 May 2023 22:06:49 -0300 Subject: [PATCH 06/12] fixing warning identation --- src/frontend/src/index.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/index.css b/src/frontend/src/index.css index f4981616c..32f378e63 100644 --- a/src/frontend/src/index.css +++ b/src/frontend/src/index.css @@ -16,8 +16,8 @@ code { monospace; } -/*The style below sets the cursor property of the element with the class .react-flow__pane to the default cursor. -The cursor: default; property value restores the browser's default cursor style for the targeted element. By applying this style, the element will no longer have a custom cursor appearance such as "grab" or any other custom cursor defined elsewhere in the application. Instead, it will revert to the default cursor style determined by the browser, typically an arrow-shaped cursor.*/ +/* The style below sets the cursor property of the element with the class .react-flow__pane to the default cursor. +The cursor: default; property value restores the browser's default cursor style for the targeted element. By applying this style, the element will no longer have a custom cursor appearance such as "grab" or any other custom cursor defined elsewhere in the application. Instead, it will revert to the default cursor style determined by the browser, typically an arrow-shaped cursor. */ .react-flow__pane { cursor: default; -} \ No newline at end of file + } From f27d067609da99e7cd0f1db93b8f20601e7f83d8 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Wed, 31 May 2023 22:16:16 -0300 Subject: [PATCH 07/12] fixing identation warnings spaces --- src/frontend/src/index.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/index.css b/src/frontend/src/index.css index 32f378e63..3fa44bc31 100644 --- a/src/frontend/src/index.css +++ b/src/frontend/src/index.css @@ -19,5 +19,5 @@ code { /* The style below sets the cursor property of the element with the class .react-flow__pane to the default cursor. The cursor: default; property value restores the browser's default cursor style for the targeted element. By applying this style, the element will no longer have a custom cursor appearance such as "grab" or any other custom cursor defined elsewhere in the application. Instead, it will revert to the default cursor style determined by the browser, typically an arrow-shaped cursor. */ .react-flow__pane { - cursor: default; - } + cursor: default; +} From 2ee0244834c0ad56d5b15de169f318805886354e Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Wed, 31 May 2023 22:20:12 -0300 Subject: [PATCH 08/12] fixing identation warnings spaces - new --- src/frontend/src/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/index.css b/src/frontend/src/index.css index 3fa44bc31..67f44ca1c 100644 --- a/src/frontend/src/index.css +++ b/src/frontend/src/index.css @@ -19,5 +19,5 @@ code { /* The style below sets the cursor property of the element with the class .react-flow__pane to the default cursor. The cursor: default; property value restores the browser's default cursor style for the targeted element. By applying this style, the element will no longer have a custom cursor appearance such as "grab" or any other custom cursor defined elsewhere in the application. Instead, it will revert to the default cursor style determined by the browser, typically an arrow-shaped cursor. */ .react-flow__pane { - cursor: default; + cursor: default; } From a9e1fb4eb729eb38ed03037677eb702865664a29 Mon Sep 17 00:00:00 2001 From: Deepankar Mahapatro Date: Thu, 1 Jun 2023 09:58:25 +0530 Subject: [PATCH 09/12] build: push lcserve image as public --- Makefile | 2 +- poetry.lock | 10 ++++++---- pyproject.toml | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index fdb09a13e..15337f65b 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ lcserve_push: make build_frontend @version=$$(poetry version --short); \ lc-serve push --app langflow.lcserve:app --app-dir . \ - --image-name langflow --image-tag $${version} --verbose + --image-name langflow --image-tag $${version} --verbose --public lcserve_deploy: @:$(if $(uses),,$(error `uses` is not set. Please run `make uses=... lcserve_deploy`)) diff --git a/poetry.lock b/poetry.lock index a414aab16..d02b5952d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. [[package]] name = "aiofiles" @@ -1096,7 +1096,9 @@ files = [ {file = "duckdb-0.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b2707096d6df4321044fcde2c9f04da632d11a8be60957fd09d49a42fae71a29"}, {file = "duckdb-0.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b27df1b70ae74d2c88efb5ffca8490954fdc678099509a9c4404ca30acc53426"}, {file = "duckdb-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75a97c800271b52dd0f37696d074c50576dcb4b2750b6115932a98696a268070"}, + {file = "duckdb-0.8.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:804cac261a5e016506a6d67838a65d19b06a237f7949f1704f0e800eb708286a"}, {file = "duckdb-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6b9abca7fa6713e1d031c18485343b4de99742c7e1b85c10718aa2f31a4e2c6"}, + {file = "duckdb-0.8.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:51aa6d606d49072abcfeb3be209eb559ac94c1b5e70f58ac3adbb94aca9cd69f"}, {file = "duckdb-0.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7c8dc769aaf2be0a1c57995ca657e5b92c1c56fc8437edb720ca6cab571adf14"}, {file = "duckdb-0.8.0-cp311-cp311-win32.whl", hash = "sha256:c4207d18b42387c4a035846d8878eb967070198be8ac26fd77797ce320d1a400"}, {file = "duckdb-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:0c392257547c20794c3072fcbca99a49ef0a49974005d755e93893e2b4875267"}, @@ -2374,13 +2376,13 @@ text-helpers = ["chardet (>=5.1.0,<6.0.0)"] [[package]] name = "langchain-serve" -version = "0.0.38" +version = "0.0.39" description = "Langchain Serve - serve your langchain apps on Jina AI Cloud." category = "main" optional = true python-versions = "*" files = [ - {file = "langchain-serve-0.0.38.tar.gz", hash = "sha256:649b8e26eebe6b33960c081b388fb7118acbfdc00f97dd935a580ab88aca53d6"}, + {file = "langchain-serve-0.0.39.tar.gz", hash = "sha256:9854cb65d4bc4003eec334e4b0c7ff7625984f595ea46e1aa60c443a1978f47d"}, ] [package.dependencies] @@ -6178,4 +6180,4 @@ deploy = ["langchain-serve"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "9ce165d2decf2190d7ce69be608872b3ed9abe705a276045623706d01665754b" +content-hash = "ad6b2111e54888347e033f2df354220c8a00667375e19e02d129dabba50adafa" diff --git a/pyproject.toml b/pyproject.toml index 79d09b422..9c4928dd7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ psycopg2-binary = "^2.9.6" pyarrow = "^11.0.0" tiktoken = "^0.3.3" wikipedia = "^1.4.0" -langchain-serve = { version = "^0.0.38", optional = true } +langchain-serve = { version = "^0.0.39", optional = true } qdrant-client = "^1.2.0" websockets = "^11.0.3" weaviate-client = "^3.19.2" From 2285304e095e77ca3bbc2e929c88b6bb0dcd14fa Mon Sep 17 00:00:00 2001 From: Deepankar Mahapatro Date: Thu, 1 Jun 2023 10:56:39 +0530 Subject: [PATCH 10/12] build: update lcserve version to support public --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index d02b5952d..5e53d5282 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2376,13 +2376,13 @@ text-helpers = ["chardet (>=5.1.0,<6.0.0)"] [[package]] name = "langchain-serve" -version = "0.0.39" +version = "0.0.40" description = "Langchain Serve - serve your langchain apps on Jina AI Cloud." category = "main" optional = true python-versions = "*" files = [ - {file = "langchain-serve-0.0.39.tar.gz", hash = "sha256:9854cb65d4bc4003eec334e4b0c7ff7625984f595ea46e1aa60c443a1978f47d"}, + {file = "langchain-serve-0.0.40.tar.gz", hash = "sha256:c60b173fcf0b682fbb70d34e8f485ce168e2229f55cb5c4ffbc26a5206af1c06"}, ] [package.dependencies] @@ -6180,4 +6180,4 @@ deploy = ["langchain-serve"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "ad6b2111e54888347e033f2df354220c8a00667375e19e02d129dabba50adafa" +content-hash = "2bf357ad30f79c68751b34c991b4a73767ceb628657f4133228d4eb487d8a6fb" diff --git a/pyproject.toml b/pyproject.toml index 9c4928dd7..78104f327 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ psycopg2-binary = "^2.9.6" pyarrow = "^11.0.0" tiktoken = "^0.3.3" wikipedia = "^1.4.0" -langchain-serve = { version = "^0.0.39", optional = true } +langchain-serve = { version = ">0.0.39", optional = true } qdrant-client = "^1.2.0" websockets = "^11.0.3" weaviate-client = "^3.19.2" From 5b5eea98955c6fb7ba0614b783fd1109a6eed7ce Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 1 Jun 2023 15:07:56 -0300 Subject: [PATCH 11/12] =?UTF-8?q?=F0=9F=9A=80=20feat(langflow):=20reorgani?= =?UTF-8?q?ze=20graph=20package=20to=20improve=20modularity=20and=20mainta?= =?UTF-8?q?inability=20The=20changes=20include:=20-=20Moved=20the=20`Edge`?= =?UTF-8?q?=20class=20to=20a=20new=20`edge`=20package=20-=20Moved=20the=20?= =?UTF-8?q?`Graph`=20class=20to=20a=20new=20`graph`=20package=20-=20Moved?= =?UTF-8?q?=20the=20`Node`=20class=20to=20a=20new=20`node`=20package=20-?= =?UTF-8?q?=20Moved=20the=20`VectorStoreNode`=20class=20to=20the=20`node/t?= =?UTF-8?q?ypes.py`=20module=20-=20Moved=20the=20`Edge`,=20`Graph`,=20and?= =?UTF-8?q?=20`Node`=20classes=20to=20their=20respective=20`base.py`=20mod?= =?UTF-8?q?ules=20-=20Added=20an=20`=5F=5Finit=5F=5F.py`=20file=20to=20eac?= =?UTF-8?q?h=20package=20to=20allow=20for=20importing=20of=20classes=20-?= =?UTF-8?q?=20Added=20a=20`constants.py`=20module=20to=20the=20`graph`=20p?= =?UTF-8?q?ackage=20to=20store=20constants=20used=20in=20the=20`Graph`=20c?= =?UTF-8?q?lass=20-=20Refactored=20the=20`Graph`=20class=20to=20use=20the?= =?UTF-8?q?=20new=20`Node`=20and=20`Edge`=20classes=20-=20Refactored=20the?= =?UTF-8?q?=20`Graph`=20class=20to=20use=20a=20dictionary=20to=20map=20nod?= =?UTF-8?q?e=20types=20to=20their=20respective=20classes=20-=20Refactored?= =?UTF-8?q?=20the=20`Graph`=20class=20to=20remove=20invalid=20nodes=20from?= =?UTF-8?q?=20the=20graph=20-=20Refactored=20the=20`Graph`=20class=20to=20?= =?UTF-8?q?handle=20the=20LLM=20node=20within=20the=20graph=20-=20Refactor?= =?UTF-8?q?ed=20the=20`Graph`=20class=20to=20build=20the=20nodes=20before?= =?UTF-8?q?=20building=20the=20edges=20-=20Refactored=20the=20`Graph`=20cl?= =?UTF-8?q?ass=20to=20use=20the=20`get=5Fnode`=20method=20to=20find=20node?= =?UTF-8?q?s=20by=20id=20-=20Refactored=20the=20`Graph`=20class=20to=20use?= =?UTF-8?q?=20the=20`get=5Fnode=5Fneighbors`=20method=20to=20find=20the=20?= =?UTF-8?q?neighbors=20of=20a=20node=20-=20Refactored=20the=20`Graph`=20cl?= =?UTF-8?q?ass=20to=20use=20the=20`get=5Fchildren=5Fby=5Fnode=5Ftype`=20me?= =?UTF-8?q?thod=20to=20find=20the=20children=20of=20a=20node=20based=20on?= =?UTF-8?q?=20the=20node=20type?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These changes improve the modularity and maintainability of the `langflow` package by separating the classes into their respective packages and modules. The changes also make it easier to add new node types to the `Graph` class by using a dictionary to map node types to their respective classes. 🚀 feat(node): add Node class to represent a node in the graph 🚀 feat(constants.py): add DIRECT_TYPES constant to represent direct types in a node's template The Node class represents a node in the graph and is responsible for parsing the data and building the module. The DIRECT_TYPES constant is a list of direct types in a node's template. 🚧 chore(types.py): add import statements for typing and Node classes This commit adds import statements for the typing module and the Node class to the types.py file. This is necessary for the code to run properly as it uses these classes and modules. 🚧 chore(loading.py): remove unnecessary import statement This commit removes an unnecessary import statement from the loading.py file. The import statement was causing a circular import error and was not needed for the code to run properly. 🚧 chore(run.py): update import statement for Graph class This commit updates the import statement for the Graph class in the run.py file. The import statement was outdated and was causing an import error. 🚧 chore(conftest.py): update import statement for Graph class This commit updates the import statement for the Graph class in the conftest.py file. The import statement was outdated and was causing an import error. 🚧 chore(test_graph.py): update import statements for Node and Edge classes This commit updates the import statements for the Node and Edge classes in the test_graph.py file. The import statements were outdated and were causing import errors. --- src/backend/langflow/api/validate.py | 2 +- src/backend/langflow/graph/__init__.py | 37 ++++++++- src/backend/langflow/graph/edge/__init__.py | 0 src/backend/langflow/graph/edge/base.py | 52 ++++++++++++ src/backend/langflow/graph/graph/__init__.py | 0 .../graph/{graph.py => graph/base.py} | 81 ++++++++----------- src/backend/langflow/graph/graph/constants.py | 49 +++++++++++ src/backend/langflow/graph/node/__init__.py | 0 src/backend/langflow/graph/{ => node}/base.py | 73 +++-------------- .../langflow/graph/{ => node}/constants.py | 0 .../graph/{nodes.py => node/types.py} | 2 +- src/backend/langflow/interface/loading.py | 2 +- src/backend/langflow/interface/run.py | 2 +- tests/conftest.py | 2 +- tests/test_graph.py | 6 +- 15 files changed, 189 insertions(+), 119 deletions(-) create mode 100644 src/backend/langflow/graph/edge/__init__.py create mode 100644 src/backend/langflow/graph/edge/base.py create mode 100644 src/backend/langflow/graph/graph/__init__.py rename src/backend/langflow/graph/{graph.py => graph/base.py} (64%) create mode 100644 src/backend/langflow/graph/graph/constants.py create mode 100644 src/backend/langflow/graph/node/__init__.py rename src/backend/langflow/graph/{ => node}/base.py (81%) rename src/backend/langflow/graph/{ => node}/constants.py (100%) rename src/backend/langflow/graph/{nodes.py => node/types.py} (99%) diff --git a/src/backend/langflow/api/validate.py b/src/backend/langflow/api/validate.py index 0e2a7752c..53a7ee350 100644 --- a/src/backend/langflow/api/validate.py +++ b/src/backend/langflow/api/validate.py @@ -9,7 +9,7 @@ from langflow.api.base import ( PromptValidationResponse, validate_prompt, ) -from langflow.graph.nodes import VectorStoreNode +from langflow.graph.node.types import VectorStoreNode from langflow.interface.run import build_graph from langflow.utils.logger import logger from langflow.utils.validate import validate_code diff --git a/src/backend/langflow/graph/__init__.py b/src/backend/langflow/graph/__init__.py index 097b7a695..44859da02 100644 --- a/src/backend/langflow/graph/__init__.py +++ b/src/backend/langflow/graph/__init__.py @@ -1,4 +1,35 @@ -from langflow.graph.base import Edge, Node -from langflow.graph.graph import Graph +from langflow.graph.edge.base import Edge +from langflow.graph.graph.base import Graph +from langflow.graph.node.base import Node +from langflow.graph.node.types import ( + AgentNode, + ChainNode, + DocumentLoaderNode, + EmbeddingNode, + LLMNode, + MemoryNode, + PromptNode, + TextSplitterNode, + ToolNode, + ToolkitNode, + VectorStoreNode, + WrapperNode, +) -__all__ = ["Graph", "Node", "Edge"] +__all__ = [ + "Graph", + "Node", + "Edge", + "AgentNode", + "ChainNode", + "DocumentLoaderNode", + "EmbeddingNode", + "LLMNode", + "MemoryNode", + "PromptNode", + "TextSplitterNode", + "ToolNode", + "ToolkitNode", + "VectorStoreNode", + "WrapperNode", +] diff --git a/src/backend/langflow/graph/edge/__init__.py b/src/backend/langflow/graph/edge/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backend/langflow/graph/edge/base.py b/src/backend/langflow/graph/edge/base.py new file mode 100644 index 000000000..2bf5a1ba4 --- /dev/null +++ b/src/backend/langflow/graph/edge/base.py @@ -0,0 +1,52 @@ +from langflow.utils.logger import logger +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from langflow.graph.node.base import Node + + +class Edge: + def __init__(self, source: "Node", target: "Node"): + self.source: "Node" = source + self.target: "Node" = target + self.validate_edge() + + def validate_edge(self) -> None: + # Validate that the outputs of the source node are valid inputs + # for the target node + self.source_types = self.source.output + self.target_reqs = self.target.required_inputs + self.target.optional_inputs + # Both lists contain strings and sometimes a string contains the value we are + # looking for e.g. comgin_out=["Chain"] and target_reqs=["LLMChain"] + # so we need to check if any of the strings in source_types is in target_reqs + self.valid = any( + output in target_req + for output in self.source_types + for target_req in self.target_reqs + ) + # Get what type of input the target node is expecting + + self.matched_type = next( + ( + output + for output in self.source_types + for target_req in self.target_reqs + if output in target_req + ), + None, + ) + no_matched_type = self.matched_type is None + if no_matched_type: + logger.debug(self.source_types) + logger.debug(self.target_reqs) + if no_matched_type: + raise ValueError( + f"Edge between {self.source.node_type} and {self.target.node_type} " + f"has no matched type" + ) + + def __repr__(self) -> str: + return ( + f"Edge(source={self.source.id}, target={self.target.id}, valid={self.valid}" + f", matched_type={self.matched_type})" + ) diff --git a/src/backend/langflow/graph/graph/__init__.py b/src/backend/langflow/graph/graph/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backend/langflow/graph/graph.py b/src/backend/langflow/graph/graph/base.py similarity index 64% rename from src/backend/langflow/graph/graph.py rename to src/backend/langflow/graph/graph/base.py index b289d5c31..3ba67837f 100644 --- a/src/backend/langflow/graph/graph.py +++ b/src/backend/langflow/graph/graph/base.py @@ -1,38 +1,20 @@ from typing import Dict, List, Type, Union -from langflow.graph.base import Edge, Node -from langflow.graph.nodes import ( - AgentNode, - ChainNode, - DocumentLoaderNode, - EmbeddingNode, +from langflow.graph.edge.base import Edge +from langflow.graph.graph.constants import NODE_TYPE_MAP +from langflow.graph.node.base import Node +from langflow.graph.node.types import ( FileToolNode, LLMNode, - MemoryNode, - PromptNode, - TextSplitterNode, ToolkitNode, - ToolNode, - VectorStoreNode, - WrapperNode, ) -from langflow.interface.agents.base import agent_creator -from langflow.interface.chains.base import chain_creator -from langflow.interface.document_loaders.base import documentloader_creator -from langflow.interface.embeddings.base import embedding_creator -from langflow.interface.llms.base import llm_creator -from langflow.interface.memories.base import memory_creator -from langflow.interface.prompts.base import prompt_creator -from langflow.interface.text_splitters.base import textsplitter_creator -from langflow.interface.toolkits.base import toolkits_creator -from langflow.interface.tools.base import tool_creator from langflow.interface.tools.constants import FILE_TOOLS -from langflow.interface.vector_store.base import vectorstore_creator -from langflow.interface.wrappers.base import wrapper_creator from langflow.utils import payload class Graph: + """A class representing a graph of nodes and edges.""" + def __init__( self, nodes: List[Dict[str, Union[str, Dict[str, Union[str, List[str]]]]]], @@ -43,6 +25,7 @@ class Graph: self._build_graph() def _build_graph(self) -> None: + """Builds the graph from the nodes and edges.""" self.nodes = self._build_nodes() self.edges = self._build_edges() for edge in self.edges: @@ -51,17 +34,25 @@ class Graph: # This is a hack to make sure that the LLM node is sent to # the toolkit node + self._build_node_params() + # remove invalid nodes + self._remove_invalid_nodes() + + def _build_node_params(self) -> None: + """Identifies and handles the LLM node within the graph.""" llm_node = None for node in self.nodes: node._build_params() - if isinstance(node, LLMNode): llm_node = node - for node in self.nodes: - if isinstance(node, ToolkitNode): - node.params["llm"] = llm_node - # remove invalid nodes + if llm_node: + for node in self.nodes: + if isinstance(node, ToolkitNode): + node.params["llm"] = llm_node + + def _remove_invalid_nodes(self) -> None: + """Removes invalid nodes from the graph.""" self.nodes = [ node for node in self.nodes @@ -70,19 +61,23 @@ class Graph: ] def _validate_node(self, node: Node) -> bool: + """Validates a node.""" # All nodes that do not have edges are invalid return len(node.edges) > 0 def get_node(self, node_id: str) -> Union[None, Node]: + """Returns a node by id.""" return next((node for node in self.nodes if node.id == node_id), None) def get_nodes_with_target(self, node: Node) -> List[Node]: + """Returns the nodes connected to a node.""" connected_nodes: List[Node] = [ edge.source for edge in self.edges if edge.target == node ] return connected_nodes def build(self) -> List[Node]: + """Builds the graph.""" # Get root node root_node = payload.get_root_node(self) if root_node is None: @@ -90,6 +85,7 @@ class Graph: return root_node.build() def get_node_neighbors(self, node: Node) -> Dict[Node, int]: + """Returns the neighbors of a node.""" neighbors: Dict[Node, int] = {} for edge in self.edges: if edge.source == node: @@ -105,6 +101,7 @@ class Graph: return neighbors def _build_edges(self) -> List[Edge]: + """Builds the edges of the graph.""" # Edge takes two nodes as arguments, so we need to build the nodes first # and then build the edges # if we can't find a node, we raise an error @@ -121,30 +118,15 @@ class Graph: return edges def _get_node_class(self, node_type: str, node_lc_type: str) -> Type[Node]: - node_type_map: Dict[str, Type[Node]] = { - **{t: PromptNode for t in prompt_creator.to_list()}, - **{t: AgentNode for t in agent_creator.to_list()}, - **{t: ChainNode for t in chain_creator.to_list()}, - **{t: ToolNode for t in tool_creator.to_list()}, - **{t: ToolkitNode for t in toolkits_creator.to_list()}, - **{t: WrapperNode for t in wrapper_creator.to_list()}, - **{t: LLMNode for t in llm_creator.to_list()}, - **{t: MemoryNode for t in memory_creator.to_list()}, - **{t: EmbeddingNode for t in embedding_creator.to_list()}, - **{t: VectorStoreNode for t in vectorstore_creator.to_list()}, - **{t: DocumentLoaderNode for t in documentloader_creator.to_list()}, - **{t: TextSplitterNode for t in textsplitter_creator.to_list()}, - } - + """Returns the node class based on the node type.""" if node_type in FILE_TOOLS: return FileToolNode - if node_type in node_type_map: - return node_type_map[node_type] - if node_lc_type in node_type_map: - return node_type_map[node_lc_type] - return Node + if node_type in NODE_TYPE_MAP: + return NODE_TYPE_MAP[node_type] + return NODE_TYPE_MAP[node_lc_type] if node_lc_type in NODE_TYPE_MAP else Node def _build_nodes(self) -> List[Node]: + """Builds the nodes of the graph.""" nodes: List[Node] = [] for node in self._nodes: node_data = node["data"] @@ -157,6 +139,7 @@ class Graph: return nodes def get_children_by_node_type(self, node: Node, node_type: str) -> List[Node]: + """Returns the children of a node based on the node type.""" children = [] node_types = [node.data["type"]] if "node" in node.data: diff --git a/src/backend/langflow/graph/graph/constants.py b/src/backend/langflow/graph/graph/constants.py new file mode 100644 index 000000000..f5bc9b8e3 --- /dev/null +++ b/src/backend/langflow/graph/graph/constants.py @@ -0,0 +1,49 @@ +from langflow.graph.node.base import Node +from langflow.graph.node.types import ( + AgentNode, + ChainNode, + DocumentLoaderNode, + EmbeddingNode, + LLMNode, + MemoryNode, + PromptNode, + TextSplitterNode, + ToolNode, + ToolkitNode, + VectorStoreNode, + WrapperNode, +) +from langflow.interface.agents.base import agent_creator +from langflow.interface.chains.base import chain_creator +from langflow.interface.document_loaders.base import documentloader_creator +from langflow.interface.embeddings.base import embedding_creator +from langflow.interface.llms.base import llm_creator +from langflow.interface.memories.base import memory_creator +from langflow.interface.prompts.base import prompt_creator +from langflow.interface.text_splitters.base import textsplitter_creator +from langflow.interface.toolkits.base import toolkits_creator +from langflow.interface.tools.base import tool_creator +from langflow.interface.vector_store.base import vectorstore_creator +from langflow.interface.wrappers.base import wrapper_creator + + +from typing import Dict, Type + + +DIRECT_TYPES = ["str", "bool", "code", "int", "float", "Any", "prompt"] + + +NODE_TYPE_MAP: Dict[str, Type[Node]] = { + **{t: PromptNode for t in prompt_creator.to_list()}, + **{t: AgentNode for t in agent_creator.to_list()}, + **{t: ChainNode for t in chain_creator.to_list()}, + **{t: ToolNode for t in tool_creator.to_list()}, + **{t: ToolkitNode for t in toolkits_creator.to_list()}, + **{t: WrapperNode for t in wrapper_creator.to_list()}, + **{t: LLMNode for t in llm_creator.to_list()}, + **{t: MemoryNode for t in memory_creator.to_list()}, + **{t: EmbeddingNode for t in embedding_creator.to_list()}, + **{t: VectorStoreNode for t in vectorstore_creator.to_list()}, + **{t: DocumentLoaderNode for t in documentloader_creator.to_list()}, + **{t: TextSplitterNode for t in textsplitter_creator.to_list()}, +} diff --git a/src/backend/langflow/graph/node/__init__.py b/src/backend/langflow/graph/node/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backend/langflow/graph/base.py b/src/backend/langflow/graph/node/base.py similarity index 81% rename from src/backend/langflow/graph/base.py rename to src/backend/langflow/graph/node/base.py index cc5e2902b..5076deb9c 100644 --- a/src/backend/langflow/graph/base.py +++ b/src/backend/langflow/graph/node/base.py @@ -1,27 +1,27 @@ -# Description: Graph class for building a graph of nodes and edges -# Insights: -# - Defer prompts building to the last moment or when they have all the tools -# - Build each inner agent first, then build the outer agent - -import contextlib -import inspect -import types -import warnings -from typing import Any, Dict, List, Optional - from langflow.cache import base as cache_utils -from langflow.graph.constants import DIRECT_TYPES +from langflow.graph.node.constants import DIRECT_TYPES from langflow.interface import loading from langflow.interface.listing import ALL_TYPES_DICT from langflow.utils.logger import logger from langflow.utils.util import sync_to_async +import contextlib +import inspect +import types +import warnings +from typing import Any, Dict, List, Optional +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from langflow.graph.edge.base import Edge + + class Node: def __init__(self, data: Dict, base_type: Optional[str] = None) -> None: self.id: str = data["id"] self._data = data - self.edges: List[Edge] = [] + self.edges: List["Edge"] = [] self.base_type: Optional[str] = base_type self._parse_data() self._built_object = None @@ -227,50 +227,3 @@ class Node: def _built_object_repr(self): return repr(self._built_object) - - -class Edge: - def __init__(self, source: "Node", target: "Node"): - self.source: "Node" = source - self.target: "Node" = target - self.validate_edge() - - def validate_edge(self) -> None: - # Validate that the outputs of the source node are valid inputs - # for the target node - self.source_types = self.source.output - self.target_reqs = self.target.required_inputs + self.target.optional_inputs - # Both lists contain strings and sometimes a string contains the value we are - # looking for e.g. comgin_out=["Chain"] and target_reqs=["LLMChain"] - # so we need to check if any of the strings in source_types is in target_reqs - self.valid = any( - output in target_req - for output in self.source_types - for target_req in self.target_reqs - ) - # Get what type of input the target node is expecting - - self.matched_type = next( - ( - output - for output in self.source_types - for target_req in self.target_reqs - if output in target_req - ), - None, - ) - no_matched_type = self.matched_type is None - if no_matched_type: - logger.debug(self.source_types) - logger.debug(self.target_reqs) - if no_matched_type: - raise ValueError( - f"Edge between {self.source.node_type} and {self.target.node_type} " - f"has no matched type" - ) - - def __repr__(self) -> str: - return ( - f"Edge(source={self.source.id}, target={self.target.id}, valid={self.valid}" - f", matched_type={self.matched_type})" - ) diff --git a/src/backend/langflow/graph/constants.py b/src/backend/langflow/graph/node/constants.py similarity index 100% rename from src/backend/langflow/graph/constants.py rename to src/backend/langflow/graph/node/constants.py diff --git a/src/backend/langflow/graph/nodes.py b/src/backend/langflow/graph/node/types.py similarity index 99% rename from src/backend/langflow/graph/nodes.py rename to src/backend/langflow/graph/node/types.py index 189e40b5c..9b25fd6ee 100644 --- a/src/backend/langflow/graph/nodes.py +++ b/src/backend/langflow/graph/node/types.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List, Optional, Union -from langflow.graph.base import Node +from langflow.graph.node.base import Node from langflow.graph.utils import extract_input_variables_from_prompt diff --git a/src/backend/langflow/interface/loading.py b/src/backend/langflow/interface/loading.py index 69c697823..a3799be16 100644 --- a/src/backend/langflow/interface/loading.py +++ b/src/backend/langflow/interface/loading.py @@ -12,6 +12,7 @@ from langchain.agents.load_tools import ( _LLM_TOOLS, ) from langchain.agents.loading import load_agent_from_config +from langflow.graph import Graph from langchain.agents.tools import Tool from langchain.base_language import BaseLanguageModel from langchain.callbacks.base import BaseCallbackManager @@ -164,7 +165,6 @@ def instantiate_utility(node_type, class_object, params): def load_flow_from_json(path: str, build=True): """Load flow from json file""" # This is done to avoid circular imports - from langflow.graph import Graph with open(path, "r", encoding="utf-8") as f: flow_graph = json.load(f) diff --git a/src/backend/langflow/interface/run.py b/src/backend/langflow/interface/run.py index d24b6a0dc..c2483416f 100644 --- a/src/backend/langflow/interface/run.py +++ b/src/backend/langflow/interface/run.py @@ -6,7 +6,7 @@ from langchain.schema import AgentAction from langflow.api.callback import AsyncStreamingLLMCallbackHandler, StreamingLLMCallbackHandler # type: ignore from langflow.cache.base import compute_dict_hash, load_cache, memoize_dict -from langflow.graph.graph import Graph +from langflow.graph import Graph from langflow.utils.logger import logger diff --git a/tests/conftest.py b/tests/conftest.py index 870c48a32..d0af2ad84 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,7 @@ import json from pathlib import Path from typing import AsyncGenerator +from langflow.graph.graph.base import Graph import pytest from fastapi.testclient import TestClient from httpx import AsyncClient @@ -46,7 +47,6 @@ def client(): def get_graph(_type="basic"): """Get a graph from a json file""" - from langflow.graph.graph import Graph if _type == "basic": path = pytest.BASIC_EXAMPLE_PATH diff --git a/tests/test_graph.py b/tests/test_graph.py index a0f5945fc..cdbe0ba93 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -1,10 +1,12 @@ from typing import Type, Union +from langflow.graph.edge.base import Edge +from langflow.graph.node.base import Node import pytest from langchain.chains.base import Chain from langchain.llms.fake import FakeListLLM -from langflow.graph import Edge, Graph, Node -from langflow.graph.nodes import ( +from langflow.graph import Graph +from langflow.graph.node.types import ( AgentNode, ChainNode, FileToolNode, From da5b15fa6899e9a461d72b6d6806a2575f183914 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Thu, 1 Jun 2023 17:47:27 -0300 Subject: [PATCH 12/12] Bringing the menu options opened on search --- .../pages/FlowPage/components/DisclosureComponent/index.tsx | 5 +++-- .../FlowPage/components/extraSidebarComponent/index.tsx | 3 +++ src/frontend/src/types/components/index.ts | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/pages/FlowPage/components/DisclosureComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/DisclosureComponent/index.tsx index 31bf78156..6878ad3c4 100644 --- a/src/frontend/src/pages/FlowPage/components/DisclosureComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/DisclosureComponent/index.tsx @@ -5,6 +5,7 @@ import { DisclosureComponentType } from "../../../../types/components"; export default function DisclosureComponent({ button: { title, Icon, buttons = [] }, children, + openDisc, }: DisclosureComponentType) { return ( @@ -27,14 +28,14 @@ export default function DisclosureComponent({
- + {children} diff --git a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx index e8dbfbb6b..deeed6c70 100644 --- a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx @@ -15,6 +15,7 @@ import { MagnifyingGlassIcon } from "@heroicons/react/24/outline"; export default function ExtraSidebar() { const { data } = useContext(typesContext); const [dataFilter, setFilterData] = useState(data); + const [search, setSearch] = useState(""); function onDragStart( event: React.DragEvent, @@ -58,6 +59,7 @@ export default function ExtraSidebar() { className="dark:text-white focus:outline-none block w-full rounded-md py-1.5 ps-3 pr-9 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 sm:text-sm sm:leading-6 dark:ring-0 dark:bg-[#2d3747] dark:focus:outline-none" onChange={(e) => { handleSearchInput(e.target.value); + setSearch(e.target.value); }} />
@@ -71,6 +73,7 @@ export default function ExtraSidebar() { .map((d: keyof APIObjectType, i) => Object.keys(dataFilter[d]).length > 0 ? ( >;