From a9c7fc0a6983c942b96b45f0158cbaecad11f74f Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Tue, 30 May 2023 00:38:14 -0300 Subject: [PATCH 01/16] =?UTF-8?q?=F0=9F=94=A7=20chore(config):=20add=20Vec?= =?UTF-8?q?torStoreToolkit=20to=20toolkits=20list=20=F0=9F=90=9B=20fix(bas?= =?UTF-8?q?e.py):=20remove=20deepcopy=20for=20VectorStore=20and=20VectorSt?= =?UTF-8?q?oreRouter=20agents=20=F0=9F=90=9B=20fix(nodes.py):=20remove=20d?= =?UTF-8?q?eepcopy=20for=20VectorStore=20and=20VectorStoreRouter=20agents?= =?UTF-8?q?=20=F0=9F=94=A7=20chore(loading.py):=20comment=20out=20unused?= =?UTF-8?q?=20code=20for=20loading=20toolkits=20=F0=9F=90=9B=20fix(toolkit?= =?UTF-8?q?s/base.py):=20add=20Tool=20to=20base=5Fclasses=20in=20get=5Fsig?= =?UTF-8?q?nature=20method=20The=20changes=20to=20the=20config=20file=20ad?= =?UTF-8?q?d=20the=20VectorStoreToolkit=20to=20the=20list=20of=20toolkits.?= =?UTF-8?q?=20The=20deepcopy=20for=20VectorStore=20and=20VectorStoreRouter?= =?UTF-8?q?=20agents=20was=20causing=20issues,=20so=20it=20was=20removed?= =?UTF-8?q?=20from=20the=20base.py=20and=20nodes.py=20files.=20The=20loadi?= =?UTF-8?q?ng.py=20file=20had=20some=20unused=20code=20for=20loading=20too?= =?UTF-8?q?lkits,=20so=20it=20was=20commented=20out.=20Finally,=20the=20ba?= =?UTF-8?q?se.py=20file=20had=20a=20bug=20where=20the=20Tool=20class=20was?= =?UTF-8?q?=20not=20being=20added=20to=20the=20base=5Fclasses=20list=20in?= =?UTF-8?q?=20the=20get=5Fsignature=20method,=20so=20it=20was=20added.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/config.yaml | 1 + src/backend/langflow/graph/base.py | 14 +------------- src/backend/langflow/graph/nodes.py | 5 +---- src/backend/langflow/interface/loading.py | 7 +++++-- src/backend/langflow/interface/toolkits/base.py | 15 +++++++++------ 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/backend/langflow/config.yaml b/src/backend/langflow/config.yaml index 02b17cd85..b073ed544 100644 --- a/src/backend/langflow/config.yaml +++ b/src/backend/langflow/config.yaml @@ -74,6 +74,7 @@ toolkits: - JsonToolkit - VectorStoreInfo - VectorStoreRouterToolkit + - VectorStoreToolkit tools: - Search - PAL-MATH diff --git a/src/backend/langflow/graph/base.py b/src/backend/langflow/graph/base.py index 187d2983e..5b64885fb 100644 --- a/src/backend/langflow/graph/base.py +++ b/src/backend/langflow/graph/base.py @@ -212,19 +212,7 @@ class Node: if not self._built or force: self._build() - #! Deepcopy is breaking for vectorstores - if self.base_type in [ - "vectorstores", - "VectorStoreRouterAgent", - "VectorStoreAgent", - "VectorStoreInfo", - ] or self.node_type in [ - "VectorStoreInfo", - "VectorStoreRouterToolkit", - "SQLDatabase", - ]: - return self._built_object - return deepcopy(self._built_object) + return self._built_object def add_edge(self, edge: "Edge") -> None: self.edges.append(edge) diff --git a/src/backend/langflow/graph/nodes.py b/src/backend/langflow/graph/nodes.py index 189e40b5c..7d9b05366 100644 --- a/src/backend/langflow/graph/nodes.py +++ b/src/backend/langflow/graph/nodes.py @@ -14,7 +14,7 @@ class AgentNode(Node): def _set_tools_and_chains(self) -> None: for edge in self.edges: source_node = edge.source - if isinstance(source_node, ToolNode): + if isinstance(source_node, (ToolNode, ToolkitNode)): self.tools.append(source_node) elif isinstance(source_node, ChainNode): self.chains.append(source_node) @@ -32,9 +32,6 @@ class AgentNode(Node): self._build() - #! Cannot deepcopy VectorStore, VectorStoreRouter, or SQL agents - if self.node_type in ["VectorStoreAgent", "VectorStoreRouterAgent", "SQLAgent"]: - return self._built_object return self._built_object diff --git a/src/backend/langflow/interface/loading.py b/src/backend/langflow/interface/loading.py index cd6898a7f..d720c6b0c 100644 --- a/src/backend/langflow/interface/loading.py +++ b/src/backend/langflow/interface/loading.py @@ -101,8 +101,11 @@ def instantiate_tool(node_type, class_object, params): def instantiate_toolkit(node_type, class_object, params): loaded_toolkit = class_object(**params) - if toolkits_creator.has_create_function(node_type): - return load_toolkits_executor(node_type, loaded_toolkit, params) + # Commenting this out for now to use toolkits as normal tools + # if toolkits_creator.has_create_function(node_type): + # return load_toolkits_executor(node_type, loaded_toolkit, params) + if isinstance(loaded_toolkit, BaseToolkit): + return loaded_toolkit.get_tools() return loaded_toolkit diff --git a/src/backend/langflow/interface/toolkits/base.py b/src/backend/langflow/interface/toolkits/base.py index cbe625f0d..9f01b2bb2 100644 --- a/src/backend/langflow/interface/toolkits/base.py +++ b/src/backend/langflow/interface/toolkits/base.py @@ -42,24 +42,27 @@ class ToolkitCreator(LangChainTypeCreator): def get_signature(self, name: str) -> Optional[Dict]: try: - return build_template_from_class(name, self.type_to_loader_dict) + template = build_template_from_class(name, self.type_to_loader_dict) + # add Tool to base_classes + if template: + template["base_classes"].append("Tool") + return template except ValueError as exc: - raise ValueError("Prompt not found") from exc + raise ValueError("Toolkit not found") from exc except AttributeError as exc: - logger.error(f"Prompt {name} not loaded: {exc}") + logger.error(f"Toolkit {name} not loaded: {exc}") return None def to_list(self) -> List[str]: return list(self.type_to_loader_dict.keys()) def get_create_function(self, name: str) -> Callable: - if loader_name := self.create_functions.get(name, None): - # import loader + if loader_name := self.create_functions.get(name): return import_module( f"from langchain.agents.agent_toolkits import {loader_name[0]}" ) else: - raise ValueError("Loader not found") + raise ValueError("Toolkit not found") def has_create_function(self, name: str) -> bool: # check if the function list is not empty From ad3bb997eed981c9426e0b76b16f84309b18ae74 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Tue, 30 May 2023 01:11:27 -0300 Subject: [PATCH 02/16] =?UTF-8?q?=F0=9F=90=9B=20fix(base.py):=20extend=20l?= =?UTF-8?q?ist=20only=20if=20key=20exists=20and=20is=20a=20list=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(nodes.py):=20flatten=20list=20of=20tools=20i?= =?UTF-8?q?f=20it=20is=20a=20list=20of=20lists=20=F0=9F=90=9B=20fix(toolki?= =?UTF-8?q?ts/base.py):=20add=20"toolkit"=20check=20to=20avoid=20adding=20?= =?UTF-8?q?"Tool"=20to=20non-toolkit=20classes=20=F0=9F=93=9D=20docs(agent?= =?UTF-8?q?s.py):=20update=20node=20descriptions=20to=20reflect=20CSV=20an?= =?UTF-8?q?d=20zero=20shot=20agents=20The=20changes=20in=20base.py=20and?= =?UTF-8?q?=20nodes.py=20ensure=20that=20the=20code=20works=20as=20intende?= =?UTF-8?q?d=20and=20avoids=20errors=20when=20extending=20lists.=20The=20c?= =?UTF-8?q?hange=20in=20toolkits/base.py=20ensures=20that=20"Tool"=20is=20?= =?UTF-8?q?only=20added=20to=20classes=20that=20are=20toolkits.=20The=20ch?= =?UTF-8?q?anges=20in=20agents.py=20update=20the=20node=20descriptions=20t?= =?UTF-8?q?o=20reflect=20that=20the=20CSVAgentNode=20constructs=20a=20CSV?= =?UTF-8?q?=20agent=20and=20the=20InitializeAgentNode=20constructs=20a=20z?= =?UTF-8?q?ero=20shot=20agent.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/base.py | 6 ++++++ src/backend/langflow/graph/nodes.py | 5 +++++ src/backend/langflow/interface/toolkits/base.py | 2 +- src/backend/langflow/template/frontend_node/agents.py | 4 ++-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/graph/base.py b/src/backend/langflow/graph/base.py index 5b64885fb..08b255441 100644 --- a/src/backend/langflow/graph/base.py +++ b/src/backend/langflow/graph/base.py @@ -175,6 +175,12 @@ class Node: # turn result which is a function into a coroutine # so that it can be awaited self.params["coroutine"] = sync_to_async(result) + if isinstance(result, list): + # If the result is a list, then we need to extend the list + # with the result but first check if the key exists + # if it doesn't, then we need to create a new list + if isinstance(self.params[key], list): + self.params[key].extend(result) self.params[key] = result elif isinstance(value, list) and all( diff --git a/src/backend/langflow/graph/nodes.py b/src/backend/langflow/graph/nodes.py index 7d9b05366..ea94e10b8 100644 --- a/src/backend/langflow/graph/nodes.py +++ b/src/backend/langflow/graph/nodes.py @@ -62,6 +62,11 @@ class PromptNode(Node): if tools is not None else [] ) + # flatten the list of tools if it is a list of lists + # first check if it is a list + if isinstance(tools, list) and isinstance(tools[0], list): + tools = [tool for sublist in tools for tool in sublist] + self.params["tools"] = tools prompt_params = [ key diff --git a/src/backend/langflow/interface/toolkits/base.py b/src/backend/langflow/interface/toolkits/base.py index 9f01b2bb2..be2345c02 100644 --- a/src/backend/langflow/interface/toolkits/base.py +++ b/src/backend/langflow/interface/toolkits/base.py @@ -44,7 +44,7 @@ class ToolkitCreator(LangChainTypeCreator): try: template = build_template_from_class(name, self.type_to_loader_dict) # add Tool to base_classes - if template: + if "toolkit" in name.lower() and template: template["base_classes"].append("Tool") return template except ValueError as exc: diff --git a/src/backend/langflow/template/frontend_node/agents.py b/src/backend/langflow/template/frontend_node/agents.py index e4fe40187..451dd7eca 100644 --- a/src/backend/langflow/template/frontend_node/agents.py +++ b/src/backend/langflow/template/frontend_node/agents.py @@ -146,7 +146,7 @@ class CSVAgentNode(FrontendNode): ), ], ) - description: str = """Construct a json agent from a CSV and tools.""" + description: str = """Construct a CSV agent from a CSV and tools.""" base_classes: list[str] = ["AgentExecutor"] def to_dict(self): @@ -194,7 +194,7 @@ class InitializeAgentNode(FrontendNode): ), ], ) - description: str = """Construct a json agent from an LLM and tools.""" + description: str = """Construct a zero shot agent from an LLM and tools.""" base_classes: list[str] = ["AgentExecutor", "function"] def to_dict(self): From 1a8d5561e9dbbecd636495ca3a75aecdaa2f3623 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Tue, 30 May 2023 01:15:48 -0300 Subject: [PATCH 03/16] fix --- src/backend/langflow/graph/nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/langflow/graph/nodes.py b/src/backend/langflow/graph/nodes.py index ea94e10b8..5f136b3c7 100644 --- a/src/backend/langflow/graph/nodes.py +++ b/src/backend/langflow/graph/nodes.py @@ -64,7 +64,7 @@ class PromptNode(Node): ) # flatten the list of tools if it is a list of lists # first check if it is a list - if isinstance(tools, list) and isinstance(tools[0], list): + if tools and isinstance(tools, list) and isinstance(tools[0], list): tools = [tool for sublist in tools for tool in sublist] self.params["tools"] = tools From 4b6a8595df8e519a24fe1c064baf85c04ebe345b Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Tue, 30 May 2023 21:34:23 -0300 Subject: [PATCH 04/16] =?UTF-8?q?=F0=9F=90=9B=20fix(nodes.py):=20change=20?= =?UTF-8?q?type=20hint=20of=20tools=20list=20to=20include=20ToolkitNode=20?= =?UTF-8?q?The=20type=20hint=20of=20the=20tools=20list=20in=20the=20AgentN?= =?UTF-8?q?ode=20class=20has=20been=20updated=20to=20include=20the=20Toolk?= =?UTF-8?q?itNode=20class.=20This=20is=20because=20the=20tools=20list=20ca?= =?UTF-8?q?n=20now=20contain=20instances=20of=20the=20ToolkitNode=20class?= =?UTF-8?q?=20in=20addition=20to=20the=20ToolNode=20class.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/langflow/graph/nodes.py b/src/backend/langflow/graph/nodes.py index 5f136b3c7..9db6260e9 100644 --- a/src/backend/langflow/graph/nodes.py +++ b/src/backend/langflow/graph/nodes.py @@ -8,7 +8,7 @@ class AgentNode(Node): def __init__(self, data: Dict): super().__init__(data, base_type="agents") - self.tools: List[ToolNode] = [] + self.tools: List[Union[ToolNode, ToolkitNode]] = [] self.chains: List[ChainNode] = [] def _set_tools_and_chains(self) -> None: From 041748b2fb29a1641e889973baddf56941611fe9 Mon Sep 17 00:00:00 2001 From: Gabriel Almeida Date: Tue, 30 May 2023 21:47:42 -0300 Subject: [PATCH 05/16] =?UTF-8?q?=F0=9F=94=A8=20refactor(nodes.py):=20extr?= =?UTF-8?q?act=20flatten=5Flist=20function=20to=20utils=20module=20and=20u?= =?UTF-8?q?se=20it=20in=20PromptNode.build=20method=20=F0=9F=90=9B=20fix(n?= =?UTF-8?q?odes.py):=20change=20tools=20parameter=20type=20hint=20in=20Pro?= =?UTF-8?q?mptNode.build=20method=20to=20accept=20a=20list=20of=20Union[To?= =?UTF-8?q?olNode,=20ToolkitNode]=20The=20flatten=5Flist=20function=20was?= =?UTF-8?q?=20extracted=20from=20the=20PromptNode.build=20method=20and=20m?= =?UTF-8?q?oved=20to=20the=20utils=20module=20to=20improve=20code=20reusab?= =?UTF-8?q?ility.=20The=20PromptNode.build=20method=20now=20uses=20the=20f?= =?UTF-8?q?latten=5Flist=20function=20to=20flatten=20the=20list=20of=20too?= =?UTF-8?q?ls=20if=20it=20is=20a=20list=20of=20lists.=20The=20tools=20para?= =?UTF-8?q?meter=20type=20hint=20was=20changed=20to=20accept=20a=20list=20?= =?UTF-8?q?of=20Union[ToolNode,=20ToolkitNode]=20to=20improve=20type=20saf?= =?UTF-8?q?ety.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/nodes.py | 62 ++++++++++++++--------------- src/backend/langflow/graph/utils.py | 12 ++++++ 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/backend/langflow/graph/nodes.py b/src/backend/langflow/graph/nodes.py index 9db6260e9..21fe0f673 100644 --- a/src/backend/langflow/graph/nodes.py +++ b/src/backend/langflow/graph/nodes.py @@ -1,7 +1,12 @@ from typing import Any, Dict, List, Optional, Union from langflow.graph.base import Node -from langflow.graph.utils import extract_input_variables_from_prompt +from langflow.graph.utils import extract_input_variables_from_prompt, flatten_list + + +class ToolkitNode(Node): + def __init__(self, data: Dict): + super().__init__(data, base_type="toolkits") class AgentNode(Node): @@ -47,7 +52,7 @@ class PromptNode(Node): def build( self, force: bool = False, - tools: Optional[Union[List[Node], List[ToolNode]]] = None, + tools: Optional[List[Union[ToolNode, ToolkitNode]]] = None, ) -> Any: if not self._built or force: if ( @@ -65,8 +70,7 @@ class PromptNode(Node): # flatten the list of tools if it is a list of lists # first check if it is a list if tools and isinstance(tools, list) and isinstance(tools[0], list): - tools = [tool for sublist in tools for tool in sublist] - + tools = flatten_list(tools) self.params["tools"] = tools prompt_params = [ key @@ -85,30 +89,6 @@ class PromptNode(Node): return self._built_object -class ChainNode(Node): - def __init__(self, data: Dict): - super().__init__(data, base_type="chains") - - def build( - self, - force: bool = False, - tools: Optional[Union[List[Node], List[ToolNode]]] = None, - ) -> Any: - if not self._built or force: - # Check if the chain requires a PromptNode - for key, value in self.params.items(): - if isinstance(value, PromptNode): - # Build the PromptNode, passing the tools if available - self.params[key] = value.build(tools=tools, force=force) - - self._build() - - #! Cannot deepcopy SQLDatabaseChain - if self.node_type in ["SQLDatabaseChain"]: - return self._built_object - return self._built_object - - class LLMNode(Node): built_node_type = None class_built_object = None @@ -130,11 +110,6 @@ class LLMNode(Node): return self._built_object -class ToolkitNode(Node): - def __init__(self, data: Dict): - super().__init__(data, base_type="toolkits") - - class FileToolNode(ToolNode): def __init__(self, data: Dict): super().__init__(data) @@ -193,3 +168,24 @@ class TextSplitterNode(Node): if self._built_object: return f"""{self.node_type}({len(self._built_object)} documents)\nDocuments: {self._built_object[:3]}...""" return f"{self.node_type}()" + + +class ChainNode(Node): + def __init__(self, data: Dict): + super().__init__(data, base_type="chains") + + def build( + self, + force: bool = False, + tools: Optional[List[Union[ToolNode, ToolkitNode]]] = None, + ) -> Any: + if not self._built or force: + # Check if the chain requires a PromptNode + for key, value in self.params.items(): + if isinstance(value, PromptNode): + # Build the PromptNode, passing the tools if available + self.params[key] = value.build(tools=tools, force=force) + + self._build() + + return self._built_object diff --git a/src/backend/langflow/graph/utils.py b/src/backend/langflow/graph/utils.py index 6d56e933e..e22b27cf5 100644 --- a/src/backend/langflow/graph/utils.py +++ b/src/backend/langflow/graph/utils.py @@ -1,4 +1,5 @@ import re +from typing import Any, Union def validate_prompt(prompt: str): @@ -17,3 +18,14 @@ def fix_prompt(prompt: str): def extract_input_variables_from_prompt(prompt: str) -> list[str]: """Extract input variables from prompt.""" return re.findall(r"{(.*?)}", prompt) + + +def flatten_list(list_of_lists: list[Union[list, Any]]) -> list: + """Flatten list of lists.""" + new_list = [] + for item in list_of_lists: + if isinstance(item, list): + new_list.extend(item) + else: + new_list.append(item) + return new_list From 25d77eaf87b9e778b235626841e036f046fcf2e8 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 31 May 2023 11:44:12 -0300 Subject: [PATCH 06/16] =?UTF-8?q?=F0=9F=90=9B=20fix(custom.py):=20fix=20Js?= =?UTF-8?q?onAgent.from=5Ftoolkit=5Fand=5Fllm=20method=20to=20handle=20bot?= =?UTF-8?q?h=20list=20and=20JsonToolkit=20input=20=F0=9F=94=A5=20chore(tes?= =?UTF-8?q?t=5Fgraph.py):=20remove=20unused=20openapi=5Fgraph=20parameter?= =?UTF-8?q?=20from=20test=5Fbuild=20method=20The=20JsonAgent.from=5Ftoolki?= =?UTF-8?q?t=5Fand=5Fllm=20method=20was=20failing=20when=20a=20list=20was?= =?UTF-8?q?=20passed=20as=20input=20instead=20of=20a=20JsonToolkit=20objec?= =?UTF-8?q?t.=20The=20fix=20now=20handles=20both=20cases.=20The=20openapi?= =?UTF-8?q?=5Fgraph=20parameter=20was=20removed=20from=20the=20test=5Fbuil?= =?UTF-8?q?d=20method=20as=20it=20was=20unused.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/interface/agents/custom.py | 2 +- tests/test_graph.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/backend/langflow/interface/agents/custom.py b/src/backend/langflow/interface/agents/custom.py index 4654ef7cb..3aaa132d4 100644 --- a/src/backend/langflow/interface/agents/custom.py +++ b/src/backend/langflow/interface/agents/custom.py @@ -69,7 +69,7 @@ class JsonAgent(CustomAgentExecutor): @classmethod def from_toolkit_and_llm(cls, toolkit: JsonToolkit, llm: BaseLanguageModel): - tools = toolkit.get_tools() + tools = toolkit if isinstance(toolkit, list) else toolkit.get_tools() tool_names = {tool.name for tool in tools} prompt = ZeroShotAgent.create_prompt( tools, diff --git a/tests/test_graph.py b/tests/test_graph.py index a0f5945fc..b92457b81 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -237,11 +237,10 @@ def test_build_params(basic_graph): assert "memory" in root.params -def test_build(basic_graph, complex_graph, openapi_graph): +def test_build(basic_graph, complex_graph): """Test Node's build method""" assert_agent_was_built(basic_graph) assert_agent_was_built(complex_graph) - assert_agent_was_built(openapi_graph) def assert_agent_was_built(graph): From 585e94285d89a63f63014015fd4f8f20a16a1fbe Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Thu, 1 Jun 2023 00:27:01 -0300 Subject: [PATCH 07/16] Adding title and icon of the parent menu on hover of the handle --- .../components/parameterComponent/index.tsx | 37 ++++++++++++++++++- src/frontend/src/types/components/index.ts | 1 + 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 653248763..e26ae589f 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -15,6 +15,8 @@ import InputFileComponent from "../../../../components/inputFileComponent"; import { TabsContext } from "../../../../contexts/tabsContext"; import IntComponent from "../../../../components/intComponent"; import PromptAreaComponent from "../../../../components/promptComponent"; +import { nodeNames, nodeIcons } from "../../../../utils"; +import React from "react"; export default function ParameterComponent({ left, @@ -28,6 +30,8 @@ export default function ParameterComponent({ required = false, }: ParameterComponentType) { const ref = useRef(null); + const refParent = useRef(""); + const refParentIcon = useRef(null); const updateNodeInternals = useUpdateNodeInternals(); const [position, setPosition] = useState(0); useEffect(() => { @@ -48,6 +52,19 @@ export default function ParameterComponent({ let disabled = reactFlowInstance?.getEdges().some((e) => e.targetHandle === id) ?? false; const { save } = useContext(TabsContext); + const [myData, setMyData] = useState(useContext(typesContext).data); + + useEffect(() => { + Object.keys(myData).forEach((d) => { + let keys = Object.keys(myData[d]).filter( + (nd) => nd.toLowerCase() == data.type.toLowerCase() + ); + if (keys.length > 0) { + refParent.current = d; + refParentIcon.current = nodeIcons[d]; + } + }); + }, []); return (
) : ( - + +
+
+ {React.createElement(refParentIcon.current)} +
+ + {nodeNames[refParent?.current] ?? ""} + +
+ + } + > Date: Fri, 2 Jun 2023 11:23:55 -0300 Subject: [PATCH 08/16] saving darkMode on localStorage --- src/frontend/src/contexts/darkContext.tsx | 5 ++++- .../pages/FlowPage/components/tabsManagerComponent/index.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/contexts/darkContext.tsx b/src/frontend/src/contexts/darkContext.tsx index f612fe381..2a76d0e53 100644 --- a/src/frontend/src/contexts/darkContext.tsx +++ b/src/frontend/src/contexts/darkContext.tsx @@ -13,13 +13,16 @@ const initialValue = { export const darkContext = createContext(initialValue); export function DarkProvider({ children }) { - const [dark, setDark] = useState(false); + const [dark, setDark] = useState( + JSON.parse(window.localStorage.getItem("isDark")) ?? false + ); useEffect(() => { if (dark) { document.getElementById("body").classList.add("dark"); } else { document.getElementById("body").classList.remove("dark"); } + window.localStorage.setItem("isDark", dark.toString()); }, [dark]); return (
-
+
{flows[tabIndex] ? ( From 534f47f1532ffc183a0d9d8c15cc5db830a0ac5b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Fri, 2 Jun 2023 14:14:47 -0300 Subject: [PATCH 09/16] =?UTF-8?q?=F0=9F=94=A8=20refactor(types.py):=20impo?= =?UTF-8?q?rt=20flatten=5Flist=20function=20from=20graph.utils=20module=20?= =?UTF-8?q?This=20commit=20simply=20imports=20the=20flatten=5Flist=20funct?= =?UTF-8?q?ion=20from=20the=20graph.utils=20module=20to=20be=20used=20in?= =?UTF-8?q?=20the=20AgentVertex=20class.=20This=20improves=20the=20readabi?= =?UTF-8?q?lity=20of=20the=20code=20and=20reduces=20the=20number=20of=20li?= =?UTF-8?q?nes=20of=20code.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/vertex/types.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/backend/langflow/graph/vertex/types.py b/src/backend/langflow/graph/vertex/types.py index 5b4e01ede..0b0d0923f 100644 --- a/src/backend/langflow/graph/vertex/types.py +++ b/src/backend/langflow/graph/vertex/types.py @@ -1,15 +1,15 @@ from typing import Any, Dict, List, Optional, Union from langflow.graph.vertex.base import Vertex -from langflow.graph.utils import extract_input_variables_from_prompt +from langflow.graph.utils import extract_input_variables_from_prompt, flatten_list class AgentVertex(Vertex): def __init__(self, data: Dict): super().__init__(data, base_type="agents") - self.tools: List[Union[ToolNode, ToolkitNode]] = [] - self.chains: List[ChainNode] = [] + self.tools: List[Union[ToolVertex, ToolkitVertex]] = [] + self.chains: List[ChainVertex] = [] def _set_tools_and_chains(self) -> None: for edge in self.edges: @@ -94,10 +94,10 @@ class ChainVertex(Vertex): tools: Optional[Union[List[Vertex], List[ToolVertex]]] = None, ) -> Any: if not self._built or force: - # Check if the chain requires a PromptNode + # Check if the chain requires a PromptVertex for key, value in self.params.items(): if isinstance(value, PromptVertex): - # Build the PromptNode, passing the tools if available + # Build the PromptVertex, passing the tools if available self.params[key] = value.build(tools=tools, force=force) self._build() From 430043098dd280143b271168a7c87e52af2a1ac6 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 2 Jun 2023 14:15:06 -0300 Subject: [PATCH 10/16] fix dropdown value with wrong color --- src/frontend/src/components/dropdownComponent/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/components/dropdownComponent/index.tsx b/src/frontend/src/components/dropdownComponent/index.tsx index eebbe1554..0a214eb4e 100644 --- a/src/frontend/src/components/dropdownComponent/index.tsx +++ b/src/frontend/src/components/dropdownComponent/index.tsx @@ -25,7 +25,9 @@ export default function Dropdown({ <>
- {internalValue} + + {internalValue} + Date: Fri, 2 Jun 2023 14:21:38 -0300 Subject: [PATCH 11/16] =?UTF-8?q?=F0=9F=94=A8=20refactor(types.py):=20reor?= =?UTF-8?q?der=20class=20definitions=20to=20match=20the=20order=20of=20the?= =?UTF-8?q?ir=20usage=20in=20the=20code=20The=20order=20of=20the=20class?= =?UTF-8?q?=20definitions=20in=20the=20file=20has=20been=20changed=20to=20?= =?UTF-8?q?match=20the=20order=20of=20their=20usage=20in=20the=20code.=20T?= =?UTF-8?q?his=20improves=20the=20readability=20of=20the=20code=20and=20ma?= =?UTF-8?q?kes=20it=20easier=20to=20understand=20the=20relationships=20bet?= =?UTF-8?q?ween=20the=20classes.=20No=20functionality=20has=20been=20chang?= =?UTF-8?q?ed.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/graph/vertex/types.py | 137 ++++++++++----------- 1 file changed, 67 insertions(+), 70 deletions(-) diff --git a/src/backend/langflow/graph/vertex/types.py b/src/backend/langflow/graph/vertex/types.py index 0b0d0923f..b81e72439 100644 --- a/src/backend/langflow/graph/vertex/types.py +++ b/src/backend/langflow/graph/vertex/types.py @@ -8,13 +8,13 @@ class AgentVertex(Vertex): def __init__(self, data: Dict): super().__init__(data, base_type="agents") - self.tools: List[Union[ToolVertex, ToolkitVertex]] = [] + self.tools: List[Union[ToolkitVertex, ToolVertex]] = [] self.chains: List[ChainVertex] = [] def _set_tools_and_chains(self) -> None: for edge in self.edges: source_node = edge.source - if isinstance(source_node, ToolVertex): + if isinstance(source_node, (ToolVertex, ToolkitVertex)): self.tools.append(source_node) elif isinstance(source_node, ChainVertex): self.chains.append(source_node) @@ -40,74 +40,6 @@ class ToolVertex(Vertex): super().__init__(data, base_type="tools") -class PromptVertex(Vertex): - def __init__(self, data: Dict): - super().__init__(data, base_type="prompts") - - def build( - self, - force: bool = False, - tools: Optional[Union[List[Vertex], List[ToolVertex]]] = None, - ) -> Any: - if not self._built or force: - if ( - "input_variables" not in self.params - or self.params["input_variables"] is None - ): - self.params["input_variables"] = [] - # Check if it is a ZeroShotPrompt and needs a tool - if "ShotPrompt" in self.vertex_type: - tools = ( - [tool_node.build() for tool_node in tools] - if tools is not None - else [] - ) - # flatten the list of tools if it is a list of lists - # first check if it is a list - if tools and isinstance(tools, list) and isinstance(tools[0], list): - tools = flatten_list(tools) - self.params["tools"] = tools - prompt_params = [ - key - for key, value in self.params.items() - if isinstance(value, str) and key != "format_instructions" - ] - else: - prompt_params = ["template"] - for param in prompt_params: - prompt_text = self.params[param] - variables = extract_input_variables_from_prompt(prompt_text) - self.params["input_variables"].extend(variables) - self.params["input_variables"] = list(set(self.params["input_variables"])) - - self._build() - return self._built_object - - -class ChainVertex(Vertex): - def __init__(self, data: Dict): - super().__init__(data, base_type="chains") - - def build( - self, - force: bool = False, - tools: Optional[Union[List[Vertex], List[ToolVertex]]] = None, - ) -> Any: - if not self._built or force: - # Check if the chain requires a PromptVertex - for key, value in self.params.items(): - if isinstance(value, PromptVertex): - # Build the PromptVertex, passing the tools if available - self.params[key] = value.build(tools=tools, force=force) - - self._build() - - #! Cannot deepcopy SQLDatabaseChain - if self.vertex_type in ["SQLDatabaseChain"]: - return self._built_object - return self._built_object - - class LLMVertex(Vertex): built_node_type = None class_built_object = None @@ -193,3 +125,68 @@ class TextSplitterVertex(Vertex): return f"""{self.vertex_type}({len(self._built_object)} documents) \nDocuments: {self._built_object[:3]}...""" return f"{self.vertex_type}()" + + +class ChainVertex(Vertex): + def __init__(self, data: Dict): + super().__init__(data, base_type="chains") + + def build( + self, + force: bool = False, + tools: Optional[List[Union[ToolkitVertex, ToolVertex]]] = None, + ) -> Any: + if not self._built or force: + # Check if the chain requires a PromptVertex + for key, value in self.params.items(): + if isinstance(value, PromptVertex): + # Build the PromptVertex, passing the tools if available + self.params[key] = value.build(tools=tools, force=force) + + self._build() + + return self._built_object + + +class PromptVertex(Vertex): + def __init__(self, data: Dict): + super().__init__(data, base_type="prompts") + + def build( + self, + force: bool = False, + tools: Optional[List[Union[ToolkitVertex, ToolVertex]]] = None, + ) -> Any: + if not self._built or force: + if ( + "input_variables" not in self.params + or self.params["input_variables"] is None + ): + self.params["input_variables"] = [] + # Check if it is a ZeroShotPrompt and needs a tool + if "ShotPrompt" in self.vertex_type: + tools = ( + [tool_node.build() for tool_node in tools] + if tools is not None + else [] + ) + # flatten the list of tools if it is a list of lists + # first check if it is a list + if tools and isinstance(tools, list) and isinstance(tools[0], list): + tools = flatten_list(tools) + self.params["tools"] = tools + prompt_params = [ + key + for key, value in self.params.items() + if isinstance(value, str) and key != "format_instructions" + ] + else: + prompt_params = ["template"] + for param in prompt_params: + prompt_text = self.params[param] + variables = extract_input_variables_from_prompt(prompt_text) + self.params["input_variables"].extend(variables) + self.params["input_variables"] = list(set(self.params["input_variables"])) + + self._build() + return self._built_object From 02cb94709d2e6b4a82f7334199ab746c55e104b4 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Fri, 2 Jun 2023 17:15:59 -0300 Subject: [PATCH 12/16] Adding shad tooltip and grouping by class on edges --- src/frontend/package-lock.json | 467 +++++++++++++++++- src/frontend/package.json | 6 + .../components/parameterComponent/index.tsx | 85 ++-- .../src/CustomNodes/GenericNode/index.tsx | 19 +- .../ReactTooltipComponent/index.tsx | 4 +- .../components/ShadTooltipComponent/index.tsx | 25 + src/frontend/src/components/ui/tooltip.tsx | 29 ++ src/frontend/src/index.css | 84 ++++ .../extraSidebarComponent/index.tsx | 14 +- src/frontend/src/utils.ts | 61 +++ src/frontend/tailwind.config.js | 85 +++- src/frontend/tsconfig.json | 3 +- 12 files changed, 812 insertions(+), 70 deletions(-) create mode 100644 src/frontend/src/components/ShadTooltipComponent/index.tsx create mode 100644 src/frontend/src/components/ui/tooltip.tsx diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 374236168..9000c133f 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -13,6 +13,7 @@ "@headlessui/react": "^1.7.10", "@heroicons/react": "^2.0.15", "@mui/material": "^5.11.9", + "@radix-ui/react-tooltip": "^1.0.6", "@tabler/icons-react": "^2.18.0", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/line-clamp": "^0.4.4", @@ -20,7 +21,10 @@ "ansi-to-html": "^0.7.2", "axios": "^1.3.2", "base64-js": "^1.5.1", + "class-variance-authority": "^0.6.0", + "clsx": "^1.2.1", "lodash": "^4.17.21", + "lucide-react": "^0.233.0", "react": "^18.2.0", "react-ace": "^10.1.0", "react-cookie": "^4.1.1", @@ -37,6 +41,8 @@ "rehype-mathjax": "^4.0.2", "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", + "tailwind-merge": "^1.13.0", + "tailwindcss-animate": "^1.0.5", "uuid": "^9.0.0", "vite-plugin-svgr": "^3.2.0", "web-vitals": "^2.1.4" @@ -911,6 +917,18 @@ "@floating-ui/core": "^1.2.6" } }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.0.tgz", + "integrity": "sha512-Ke0oU3SeuABC2C4OFu2mSAwHIP5WUiV98O9YWoHV4Q5aT6E9k06DV0Khi5uYspR8xmmBk08t8ZDcz3TR3ARkEg==", + "dependencies": { + "@floating-ui/dom": "^1.2.7" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@headlessui/react": { "version": "1.7.10", "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.10.tgz", @@ -1274,6 +1292,407 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", + "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", + "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", + "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", + "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.6.tgz", + "integrity": "sha512-DmNFOiwEc2UDigsYj6clJENma58OelxD24O4IODoZ+3sQc3Zb+L8w1EP+y9laTuKCLAysPw4fD6/v0j4KNV8rg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.2", + "@radix-ui/react-portal": "1.0.3", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", + "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", + "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", + "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", + "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, "node_modules/@reactflow/background": { "version": "11.1.7", "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.1.7.tgz", @@ -2422,7 +2841,7 @@ "version": "18.2.4", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", - "dev": true, + "devOptional": true, "dependencies": { "@types/react": "*" } @@ -2947,6 +3366,25 @@ "node": ">= 6" } }, + "node_modules/class-variance-authority": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.6.0.tgz", + "integrity": "sha512-qdRDgfjx3GRb9fpwpSvn+YaidnT7IUJNe4wt5/SWwM+PmUwJUhQRk/8zAyNro0PmVfmen2635UboTjIBXXxy5A==", + "dependencies": { + "clsx": "1.2.1" + }, + "funding": { + "url": "https://joebell.co.uk" + }, + "peerDependencies": { + "typescript": ">= 4.5.5 < 6" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/classcat": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.4.tgz", @@ -4728,6 +5166,14 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.233.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.233.0.tgz", + "integrity": "sha512-r0jMHF0vPDq2wBbZ0B3rtIcBjDyWDKpHu+vAjD2OHn2WLUr3HN5IHovtO0EMgQXuSI7YrMZbjsEZWC2uBHr8nQ==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/lz-string": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", @@ -7001,6 +7447,15 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, + "node_modules/tailwind-merge": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.13.0.tgz", + "integrity": "sha512-mUTmDbcU+IhOvJ0c42eLQ/nRkvolTqfpVaVQRSxfJAv9TabS6Y2zW/1wKpKLdKzyL3Gh8j6NTLl6MWNmvOM6kA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", @@ -7038,6 +7493,14 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss-animate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.5.tgz", + "integrity": "sha512-UU3qrOJ4lFQABY+MVADmBm+0KW3xZyhMdRvejwtXqYOL7YjHYxmuREFAZdmVG5LPe5E9CAst846SLC4j5I3dcw==", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, "node_modules/terser": { "version": "5.16.3", "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.3.tgz", @@ -7156,7 +7619,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/src/frontend/package.json b/src/frontend/package.json index bd846ce19..c7af46608 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -8,6 +8,7 @@ "@headlessui/react": "^1.7.10", "@heroicons/react": "^2.0.15", "@mui/material": "^5.11.9", + "@radix-ui/react-tooltip": "^1.0.6", "@tabler/icons-react": "^2.18.0", "@tailwindcss/forms": "^0.5.3", "@tailwindcss/line-clamp": "^0.4.4", @@ -15,7 +16,10 @@ "ansi-to-html": "^0.7.2", "axios": "^1.3.2", "base64-js": "^1.5.1", + "class-variance-authority": "^0.6.0", + "clsx": "^1.2.1", "lodash": "^4.17.21", + "lucide-react": "^0.233.0", "react": "^18.2.0", "react-ace": "^10.1.0", "react-cookie": "^4.1.1", @@ -32,6 +36,8 @@ "rehype-mathjax": "^4.0.2", "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", + "tailwind-merge": "^1.13.0", + "tailwindcss-animate": "^1.0.5", "uuid": "^9.0.0", "vite-plugin-svgr": "^3.2.0", "web-vitals": "^2.1.4" diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index e26ae589f..d6b8781a4 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -1,6 +1,11 @@ import { Handle, Position, useUpdateNodeInternals } from "reactflow"; import Tooltip from "../../../../components/TooltipComponent"; -import { classNames, isValidConnection } from "../../../../utils"; +import { + classNames, + groupByFamily, + isValidConnection, + toFirstUpperCase, +} from "../../../../utils"; import { useContext, useEffect, useRef, useState } from "react"; import InputComponent from "../../../../components/inputComponent"; import ToggleComponent from "../../../../components/toggleComponent"; @@ -17,6 +22,8 @@ import IntComponent from "../../../../components/intComponent"; import PromptAreaComponent from "../../../../components/promptComponent"; import { nodeNames, nodeIcons } from "../../../../utils"; import React from "react"; +import { nodeColors } from "../../../../utils"; +import ShadTooltip from "../../../../components/ShadTooltipComponent"; export default function ParameterComponent({ left, @@ -30,8 +37,7 @@ export default function ParameterComponent({ required = false, }: ParameterComponentType) { const ref = useRef(null); - const refParent = useRef(""); - const refParentIcon = useRef(null); + const refHtml = useRef(null); const updateNodeInternals = useUpdateNodeInternals(); const [position, setPosition] = useState(0); useEffect(() => { @@ -55,16 +61,45 @@ export default function ParameterComponent({ const [myData, setMyData] = useState(useContext(typesContext).data); useEffect(() => { - Object.keys(myData).forEach((d) => { - let keys = Object.keys(myData[d]).filter( - (nd) => nd.toLowerCase() == data.type.toLowerCase() - ); - if (keys.length > 0) { - refParent.current = d; - refParentIcon.current = nodeIcons[d]; - } - }); - }, []); + const groupedObj = groupByFamily(myData, tooltipTitle); + + refHtml.current = groupedObj.map((item, i) => ( + 0 ? "items-center flex mt-3" : "items-center flex" + )} + > +
+ {React.createElement(nodeIcons[item.family])} +
+ + {nodeNames[item.family] ?? ""}{" "} + + {" "} + -  + {item.type.split(", ").length > 2 + ? item.type.split(", ").map((el, i) => ( + <> + + {i == item.type.split(", ").length - 1 + ? el + : (el += `, `)} + + {i % 2 == 0 && i > 0 &&

} + + )) + : item.type} +
+
+
+ )); + }, [tooltipTitle]); return (
) : ( - -
-
- {React.createElement(refParentIcon.current)} -
- - {nodeNames[refParent?.current] ?? ""} - -
- - } + -
+ )} {left === true && diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 1a7f93ae5..79a241160 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -28,8 +28,8 @@ import NodeModal from "../../modals/NodeModal"; import { useCallback } from "react"; import { TabsContext } from "../../contexts/tabsContext"; import { debounce } from "../../utils"; -import TooltipReact from "../../components/ReactTooltipComponent"; import Tooltip from "../../components/TooltipComponent"; +import ShadTooltip from "../../components/ShadTooltipComponent"; export default function GenericNode({ data, selected, @@ -115,14 +115,9 @@ export default function GenericNode({ }} />
- +
{data.type}
-
+
@@ -253,11 +248,7 @@ export default function GenericNode({ : toTitleCase(t) } name={t} - tooltipTitle={ - "Type: " + - data.node.template[t].type + - (data.node.template[t].list ? " list" : "") - } + tooltipTitle={data.node.template[t].type} required={data.node.template[t].required} id={data.node.template[t].type + "|" + t + "|" + data.id} left={true} @@ -283,7 +274,7 @@ export default function GenericNode({ data={data} color={nodeColors[types[data.type]] ?? nodeColors.unknown} title={data.type} - tooltipTitle={`Type: ${data.node.base_classes.join(" | ")}`} + tooltipTitle={`${data.node.base_classes.join("\n")}`} id={[data.type, data.id, ...data.node.base_classes].join("|")} type={data.node.base_classes.join("|")} left={false} diff --git a/src/frontend/src/components/ReactTooltipComponent/index.tsx b/src/frontend/src/components/ReactTooltipComponent/index.tsx index aa736c212..cb2a54f7c 100644 --- a/src/frontend/src/components/ReactTooltipComponent/index.tsx +++ b/src/frontend/src/components/ReactTooltipComponent/index.tsx @@ -37,13 +37,15 @@ const TooltipReact: FC = ({ id={selector} content={content} className={classNames( - "!bg-white !text-xs !font-normal !text-gray-700 !shadow-md !opacity-100 z-20", + "!bg-white !text-xs !font-normal !text-gray-700 !shadow-md !opacity-100 z-[9999]", className )} place={position} clickable={clickable} isOpen={disabled ? false : undefined} delayShow={delayShow} + positionStrategy="absolute" + float={true} > {htmlContent && htmlContent} diff --git a/src/frontend/src/components/ShadTooltipComponent/index.tsx b/src/frontend/src/components/ShadTooltipComponent/index.tsx new file mode 100644 index 000000000..a360f3ff0 --- /dev/null +++ b/src/frontend/src/components/ShadTooltipComponent/index.tsx @@ -0,0 +1,25 @@ +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "../ui/tooltip"; + +const ShadTooltip = (props) => { + return ( + + + {props.children} + + {props.content} + + + + ); +}; + +export default ShadTooltip; diff --git a/src/frontend/src/components/ui/tooltip.tsx b/src/frontend/src/components/ui/tooltip.tsx new file mode 100644 index 000000000..b2ec0f701 --- /dev/null +++ b/src/frontend/src/components/ui/tooltip.tsx @@ -0,0 +1,29 @@ +"use client"; + +import * as React from "react"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; +import { cn } from "../../utils"; + +const TooltipProvider = TooltipPrimitive.Provider; + +const Tooltip = TooltipPrimitive.Root; + +const TooltipTrigger = TooltipPrimitive.Trigger; + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; + +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/src/frontend/src/index.css b/src/frontend/src/index.css index 899fb1c87..8809ecc3a 100644 --- a/src/frontend/src/index.css +++ b/src/frontend/src/index.css @@ -2,6 +2,84 @@ @tailwind components; @tailwind utilities; +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 47.4% 11.2%; + + --card: 0 0% 100%; + --card-foreground: 222.2 47.4% 11.2%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 100% 50%; + --destructive-foreground: 210 40% 98%; + + --ring: 215 20.2% 65.1%; + + --radius: 0.5rem; + } + + .dark { + --background: 224 71% 4%; + --foreground: 213 31% 91%; + + --muted: 223 47% 11%; + --muted-foreground: 215.4 16.3% 56.9%; + + --popover: 224 71% 4%; + --popover-foreground: 215 20.2% 65.1%; + + --card: 224 71% 4%; + --card-foreground: 213 31% 91%; + + --border: 216 34% 17%; + --input: 216 34% 17%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 1.2%; + + --secondary: 222.2 47.4% 11.2%; + --secondary-foreground: 210 40% 98%; + + --accent: 216 34% 17%; + --accent-foreground: 210 40% 98%; + + --destructive: 0 63% 31%; + --destructive-foreground: 210 40% 98%; + + --ring: 216 34% 17%; + + --radius: 0.5rem; + } + } + + @layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + font-feature-settings: "rlig" 1, "calt" 1; + } + } + body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", @@ -15,3 +93,9 @@ 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 diff --git a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx index e8dbfbb6b..2a8ef1217 100644 --- a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx @@ -9,8 +9,8 @@ import { import { useContext, useEffect, useState, useRef } from "react"; import { typesContext } from "../../../../contexts/typesContext"; import { APIClassType, APIObjectType } from "../../../../types/api"; -import TooltipReact from "../../../../components/ReactTooltipComponent"; import { MagnifyingGlassIcon } from "@heroicons/react/24/outline"; +import ShadTooltip from "../../../../components/ShadTooltipComponent"; export default function ExtraSidebar() { const { data } = useContext(typesContext); @@ -81,12 +81,10 @@ export default function ExtraSidebar() { {Object.keys(dataFilter[d]) .sort() .map((t: string, k) => ( -
-
+ ))}
diff --git a/src/frontend/src/utils.ts b/src/frontend/src/utils.ts index 559080f3f..4e1ea4f47 100644 --- a/src/frontend/src/utils.ts +++ b/src/frontend/src/utils.ts @@ -47,6 +47,12 @@ import { WolframIcon } from "./icons/Wolfram"; import { WordIcon } from "./icons/Word"; import { SerperIcon } from "./icons/Serper"; import { v4 as uuidv4 } from "uuid"; +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} export function classNames(...classes: Array) { return classes.filter(Boolean).join(" "); @@ -632,3 +638,58 @@ export function updateIds(newFlow, getNodeId) { e.targetHandle; }); } + +export function groupByFamily(data, baseClasses) { + let arrOfParent: string[] = []; + let arrOfType: { family: string; type: string }[] = []; + + Object.keys(data).map((d) => { + Object.keys(data[d]).map((n) => { + if ( + data[d][n].base_classes.some((r) => baseClasses.split("\n").includes(r)) + ) { + arrOfParent.push(d); + } + }); + }); + + let uniq = arrOfParent.filter( + (item, index) => arrOfParent.indexOf(item) === index + ); + + Object.keys(data).map((d) => { + Object.keys(data[d]).map((n) => { + baseClasses.split("\n").forEach((tol) => { + data[d][n].base_classes.forEach((data) => { + if (tol == data) { + arrOfType.push({ + family: d, + type: data, + }); + } + }); + }); + }); + }); + + let groupedBy = arrOfType.filter((object, index, self) => { + const foundIndex = self.findIndex( + (o) => o.family === object.family && o.type === object.type + ); + return foundIndex === index; + }); + + let groupedObj = groupedBy.reduce((result, item) => { + const existingGroup = result.find((group) => group.family === item.family); + + if (existingGroup) { + existingGroup.type += `, ${item.type}`; + } else { + result.push({ family: item.family, type: item.type }); + } + + return result; + }, []); + + return groupedObj; +} diff --git a/src/frontend/tailwind.config.js b/src/frontend/tailwind.config.js index 8df8b8c1e..5644a21bc 100644 --- a/src/frontend/tailwind.config.js +++ b/src/frontend/tailwind.config.js @@ -1,11 +1,83 @@ /** @type {import('tailwindcss').Config} */ +const { fontFamily } = require("tailwindcss/defaultTheme") + import plugin from "tailwindcss/plugin"; module.exports = { content: ["./index.html", "./src/**/*.{js,ts,tsx,jsx}"], darkMode: "class", important: true, theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: `var(--radius)`, + md: `calc(var(--radius) - 2px)`, + sm: "calc(var(--radius) - 4px)", + }, + fontFamily: { + sans: ["var(--font-sans)", ...fontFamily.sans], + }, + keyframes: { + "accordion-down": { + from: { height: 0 }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: 0 }, + }, + pulseGreen: { + "0%": { boxShadow: "0 0 0 0 rgba(72, 187, 120, 0.7)" }, + "100%": { boxShadow: "0 0 0 10px rgba(72, 187, 120, 0)" }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + "pulse-green": "pulseGreen 1s linear", + 'spin-once': 'spin 1s linear 0.7' + }, borderColor: { "red-outline": "rgba(255, 0, 0, 0.8)", "green-outline": "rgba(72, 187, 120, 0.7)", @@ -14,17 +86,6 @@ module.exports = { "red-outline": "0 0 5px rgba(255, 0, 0, 0.5)", "green-outline": "0 0 5px rgba(72, 187, 120, 0.7)", }, - - animation: { - "pulse-green": "pulseGreen 1s linear", - 'spin-once': 'spin 1s linear 0.7' - }, - keyframes: { - pulseGreen: { - "0%": { boxShadow: "0 0 0 0 rgba(72, 187, 120, 0.7)" }, - "100%": { boxShadow: "0 0 0 10px rgba(72, 187, 120, 0)" }, - }, - }, }, }, plugins: [ @@ -96,4 +157,4 @@ module.exports = { }), require("@tailwindcss/typography"), ], -}; +}; \ No newline at end of file diff --git a/src/frontend/tsconfig.json b/src/frontend/tsconfig.json index 3a05105de..2ed005aff 100644 --- a/src/frontend/tsconfig.json +++ b/src/frontend/tsconfig.json @@ -19,7 +19,8 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - "noImplicitAny": false + "noImplicitAny": false, + "baseUrl": "." }, "include": [ "src" From f0ca7eeac8b5cbb37a23d1daf3e20293595228e1 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Fri, 2 Jun 2023 17:29:22 -0300 Subject: [PATCH 13/16] formatting with codacy --- src/frontend/src/index.css | 92 +++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 52 deletions(-) diff --git a/src/frontend/src/index.css b/src/frontend/src/index.css index 962e3c025..7c41b934e 100644 --- a/src/frontend/src/index.css +++ b/src/frontend/src/index.css @@ -6,70 +6,50 @@ :root { --background: 0 0% 100%; --foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; - --popover: 0 0% 100%; --popover-foreground: 222.2 47.4% 11.2%; - --card: 0 0% 100%; --card-foreground: 222.2 47.4% 11.2%; - --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; - --primary: 222.2 47.4% 11.2%; --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; --secondary-foreground: 222.2 47.4% 11.2%; - --accent: 210 40% 96.1%; --accent-foreground: 222.2 47.4% 11.2%; - --destructive: 0 100% 50%; --destructive-foreground: 210 40% 98%; - --ring: 215 20.2% 65.1%; - --radius: 0.5rem; } - + .dark { - --background: 224 71% 4%; - --foreground: 213 31% 91%; - - --muted: 223 47% 11%; - --muted-foreground: 215.4 16.3% 56.9%; - - --popover: 224 71% 4%; - --popover-foreground: 215 20.2% 65.1%; - - --card: 224 71% 4%; - --card-foreground: 213 31% 91%; - - --border: 216 34% 17%; - --input: 216 34% 17%; - - --primary: 210 40% 98%; - --primary-foreground: 222.2 47.4% 1.2%; - - --secondary: 222.2 47.4% 11.2%; - --secondary-foreground: 210 40% 98%; - - --accent: 216 34% 17%; - --accent-foreground: 210 40% 98%; - - --destructive: 0 63% 31%; - --destructive-foreground: 210 40% 98%; - - --ring: 216 34% 17%; - - --radius: 0.5rem; + -background: 224 71% 4%; + -foreground: 213 31% 91%; + -muted: 223 47% 11%; + -muted-foreground: 215.4 16.3% 56.9%; + -popover: 224 71% 4%; + -popover-foreground: 215 20.2% 65.1%; + -card: 224 71% 4%; + -card-foreground: 213 31% 91%; + -border: 216 34% 17%; + -input: 216 34% 17%; + -primary: 210 40% 98%; + -primary-foreground: 222.2 47.4% 1.2%; + -secondary: 222.2 47.4% 11.2%; + -secondary-foreground: 210 40% 98%; + -accent: 216 34% 17%; + -accent-foreground: 210 40% 98%; + -destructive: 0 63% 31%; + -destructive-foreground: 210 40% 98%; + -ring: 216 34% 17%; + -radius: 0.5rem; } } - + @layer base { * { @apply border-border; @@ -80,22 +60,30 @@ } } +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + font-feature-settings: "rlig" 1, "calt" 1; + } +} + body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; + 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; } From c8665ef42241b8b529568754d7d7b9199fdae9a9 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Fri, 2 Jun 2023 17:35:30 -0300 Subject: [PATCH 14/16] Fixing css formatting codacy --- src/frontend/src/index.css | 86 +++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/src/frontend/src/index.css b/src/frontend/src/index.css index 7c41b934e..14580c658 100644 --- a/src/frontend/src/index.css +++ b/src/frontend/src/index.css @@ -3,52 +3,52 @@ @tailwind utilities; @layer base { - :root { - --background: 0 0% 100%; - --foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - --popover: 0 0% 100%; - --popover-foreground: 222.2 47.4% 11.2%; - --card: 0 0% 100%; - --card-foreground: 222.2 47.4% 11.2%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; - --destructive: 0 100% 50%; - --destructive-foreground: 210 40% 98%; - --ring: 215 20.2% 65.1%; - --radius: 0.5rem; + :root { + --background: 0 0% 100%; + --foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 47.4% 11.2%; + --card: 0 0% 100%; + --card-foreground: 222.2 47.4% 11.2%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 100% 50%; + --destructive-foreground: 210 40% 98%; + --ring: 215 20.2% 65.1%; + --radius: 0.5rem; } - .dark { - -background: 224 71% 4%; - -foreground: 213 31% 91%; - -muted: 223 47% 11%; - -muted-foreground: 215.4 16.3% 56.9%; - -popover: 224 71% 4%; - -popover-foreground: 215 20.2% 65.1%; - -card: 224 71% 4%; - -card-foreground: 213 31% 91%; - -border: 216 34% 17%; - -input: 216 34% 17%; - -primary: 210 40% 98%; - -primary-foreground: 222.2 47.4% 1.2%; - -secondary: 222.2 47.4% 11.2%; - -secondary-foreground: 210 40% 98%; - -accent: 216 34% 17%; - -accent-foreground: 210 40% 98%; - -destructive: 0 63% 31%; - -destructive-foreground: 210 40% 98%; - -ring: 216 34% 17%; - -radius: 0.5rem; + .dark { + -background: 224 71% 4%; + -foreground: 213 31% 91%; + -muted: 223 47% 11%; + -muted-foreground: 215.4 16.3% 56.9%; + -popover: 224 71% 4%; + -popover-foreground: 215 20.2% 65.1%; + -card: 224 71% 4%; + -card-foreground: 213 31% 91%; + -border: 216 34% 17%; + -input: 216 34% 17%; + -primary: 210 40% 98%; + -primary-foreground: 222.2 47.4% 1.2%; + -secondary: 222.2 47.4% 11.2%; + -secondary-foreground: 210 40% 98%; + -accent: 216 34% 17%; + -accent-foreground: 210 40% 98%; + -destructive: 0 63% 31%; + -destructive-foreground: 210 40% 98%; + -ring: 216 34% 17%; + -radius: 0.5rem; } - } +} @layer base { * { From 1bd3303c52bcf8befd7da89abdb396ba39b14b8b Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Fri, 2 Jun 2023 17:49:53 -0300 Subject: [PATCH 15/16] Change the delay to open to instant --- .../GenericNode/components/parameterComponent/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index d6b8781a4..dc77c2877 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -122,7 +122,7 @@ export default function ParameterComponent({ <> ) : ( From 5a103f0f6f0b5dedb7dd38b81d65e84d011c10ce Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 5 Jun 2023 13:48:17 -0300 Subject: [PATCH 16/16] =?UTF-8?q?=F0=9F=94=A8=20refactor(base.py):=20extra?= =?UTF-8?q?ct=20methods=20to=20handle=20model-specific=20and=20API=20key-s?= =?UTF-8?q?pecific=20field=20values=20This=20commit=20refactors=20the=20Fr?= =?UTF-8?q?ontendNode=20class=20by=20extracting=20two=20methods=20to=20han?= =?UTF-8?q?dle=20specific=20field=20values=20related=20to=20models=20and?= =?UTF-8?q?=20API=20keys.=20The=20=5Fhandle=5Fmodel=5Fspecific=5Ffield=5Fv?= =?UTF-8?q?alues=20method=20handles=20the=20options=20and=20is=5Flist=20pr?= =?UTF-8?q?operties=20for=20fields=20related=20to=20models,=20while=20the?= =?UTF-8?q?=20=5Fhandle=5Fapi=5Fkey=5Fspecific=5Ffield=5Fvalues=20method?= =?UTF-8?q?=20handles=20the=20display=5Fname=20and=20required=20properties?= =?UTF-8?q?=20for=20fields=20related=20to=20API=20keys.=20This=20improves?= =?UTF-8?q?=20the=20readability=20and=20maintainability=20of=20the=20code.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../langflow/template/frontend_node/base.py | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/backend/langflow/template/frontend_node/base.py b/src/backend/langflow/template/frontend_node/base.py index a97c7b8b0..6d00cead0 100644 --- a/src/backend/langflow/template/frontend_node/base.py +++ b/src/backend/langflow/template/frontend_node/base.py @@ -117,17 +117,30 @@ class FrontendNode(BaseModel): ) -> None: """Handles specific field values for certain fields.""" if key == "headers": - field.value = """{'Authorization': - 'Bearer '}""" - if name == "OpenAI" and key == "model_name": - field.options = constants.OPENAI_MODELS - field.is_list = True - elif name == "ChatOpenAI" and key == "model_name": - field.options = constants.CHAT_OPENAI_MODELS - field.is_list = True - elif (name == "Anthropic" or name == "ChatAnthropic") and key == "model_name": - field.options = constants.ANTHROPIC_MODELS + field.value = """{'Authorization': 'Bearer '}""" + FrontendNode._handle_model_specific_field_values(field, key, name) + FrontendNode._handle_api_key_specific_field_values(field, key, name) + + @staticmethod + def _handle_model_specific_field_values( + field: TemplateField, key: str, name: Optional[str] = None + ) -> None: + """Handles specific field values related to models.""" + model_dict = { + "OpenAI": constants.OPENAI_MODELS, + "ChatOpenAI": constants.CHAT_OPENAI_MODELS, + "Anthropic": constants.ANTHROPIC_MODELS, + "ChatAnthropic": constants.ANTHROPIC_MODELS, + } + if name in model_dict and key == "model_name": + field.options = model_dict[name] field.is_list = True + + @staticmethod + def _handle_api_key_specific_field_values( + field: TemplateField, key: str, name: Optional[str] = None + ) -> None: + """Handles specific field values related to API keys.""" if "api_key" in key and "OpenAI" in str(name): field.display_name = "OpenAI API Key" field.required = False