From 3ddd123b6622cfdbb1217f3ae3cd7c1643888b04 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Fri, 27 Sep 2024 12:19:35 -0300 Subject: [PATCH] feat: add dynamic metadata to components + search on sidebar (#3945) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ (frontend_node/base.py): add metadata attribute to store additional information for the component node * ✨ (constants.py): add 'metadata' attribute to NODE_FORMAT_ATTRIBUTES list to include additional metadata information for nodes in the application. * 📝 (attributes.py): add new function getattr_return_dict to return a dictionary if the value is of type dict, otherwise return an empty dictionary. Update ATTR_FUNC_MAPPING to include the new function for the "metadata" attribute. * update basic examples * 📝 (extraSidebarComponent/index.tsx): refactor handleSearchInput function to improve code readability and maintainability 🐛 (extraSidebarComponent/index.tsx): fix searchInMetadata function to correctly handle nested objects in metadata for search functionality * 📝 (component.py): add _metadata attribute to CONFIG_ATTRIBUTES list to include it in the configuration attributes --- src/backend/base/langflow/base/constants.py | 2 +- .../base/langflow/custom/attributes.py | 7 +++ .../custom/custom_component/component.py | 3 +- .../starter_projects/Agent Flow.json | 5 +++ .../Basic Prompting (Hello, World).json | 4 ++ .../starter_projects/Blog Writer.json | 5 +++ .../starter_projects/Complex Agent.json | 15 +++++++ .../starter_projects/Document QA.json | 6 +++ .../starter_projects/Hierarchical Agent.json | 11 +++++ .../starter_projects/Memory Chatbot.json | 5 +++ .../starter_projects/Sequential Agent.json | 10 +++++ .../Travel Planning Agents.json | 5 +++ .../starter_projects/Vector Store RAG.json | 11 +++++ .../langflow/template/frontend_node/base.py | 2 + .../extraSidebarComponent/index.tsx | 43 +++++++++++++------ 15 files changed, 120 insertions(+), 14 deletions(-) diff --git a/src/backend/base/langflow/base/constants.py b/src/backend/base/langflow/base/constants.py index 319899847..966046431 100644 --- a/src/backend/base/langflow/base/constants.py +++ b/src/backend/base/langflow/base/constants.py @@ -11,7 +11,7 @@ import orjson STREAM_INFO_TEXT = "Stream the response from the model. Streaming works only in Chat." -NODE_FORMAT_ATTRIBUTES = ["beta", "icon", "display_name", "description", "output_types", "edited"] +NODE_FORMAT_ATTRIBUTES = ["beta", "icon", "display_name", "description", "output_types", "edited", "metadata"] FIELD_FORMAT_ATTRIBUTES = [ diff --git a/src/backend/base/langflow/custom/attributes.py b/src/backend/base/langflow/custom/attributes.py index 98e81bde7..864f23edb 100644 --- a/src/backend/base/langflow/custom/attributes.py +++ b/src/backend/base/langflow/custom/attributes.py @@ -49,6 +49,12 @@ def getattr_return_list_of_values_from_dict(value): return [] +def getattr_return_dict(value): + if isinstance(value, dict): + return value + return {} + + ATTR_FUNC_MAPPING: dict[str, Callable] = { "display_name": getattr_return_str, "description": getattr_return_str, @@ -63,4 +69,5 @@ ATTR_FUNC_MAPPING: dict[str, Callable] = { "_inputs": getattr_return_list_of_values_from_dict, "outputs": getattr_return_list_of_object, "inputs": getattr_return_list_of_object, + "metadata": getattr_return_dict, } diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 3c6a38153..cfc0ca4e4 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -31,7 +31,7 @@ if TYPE_CHECKING: from langflow.inputs.inputs import InputTypes BACKWARDS_COMPATIBLE_ATTRIBUTES = ["user_id", "vertex", "tracing_service"] -CONFIG_ATTRIBUTES = ["_display_name", "_description", "_icon", "_name"] +CONFIG_ATTRIBUTES = ["_display_name", "_description", "_icon", "_name", "_metadata"] class Component(CustomComponent): @@ -40,6 +40,7 @@ class Component(CustomComponent): code_class_base_inheritance: ClassVar[str] = "Component" _output_logs: dict[str, Log] = {} _current_output: str = "" + _metadata: dict = {} def __init__(self, **kwargs): # if key starts with _ it is a config diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Agent Flow.json b/src/backend/base/langflow/initial_setup/starter_projects/Agent Flow.json index 947638cba..73f191b11 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Agent Flow.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Agent Flow.json @@ -159,6 +159,7 @@ "frozen": false, "icon": "ChatInput", "lf_version": "1.0.16", + "metadata": {}, "output_types": [], "outputs": [ { @@ -380,6 +381,7 @@ "frozen": false, "icon": "ChatOutput", "lf_version": "1.0.16", + "metadata": {}, "output_types": [], "outputs": [ { @@ -588,6 +590,7 @@ "frozen": false, "icon": "OpenAI", "lf_version": "1.0.16", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1166,6 +1169,7 @@ "frozen": false, "icon": "calculator", "lf_version": "1.0.16", + "metadata": {}, "official": false, "output_types": [], "outputs": [ @@ -1277,6 +1281,7 @@ "code" ], "frozen": false, + "metadata": {}, "output_types": [], "outputs": [ { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json index a194ef163..453f78fa4 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json @@ -108,6 +108,7 @@ ], "frozen": false, "icon": "ChatInput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -323,6 +324,7 @@ ], "frozen": false, "icon": "prompts", + "metadata": {}, "output_types": [], "outputs": [ { @@ -443,6 +445,7 @@ ], "frozen": false, "icon": "ChatOutput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -644,6 +647,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json b/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json index 0b72f14f4..2f33eb015 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json @@ -156,6 +156,7 @@ ], "frozen": false, "icon": "layout-template", + "metadata": {}, "output_types": [], "outputs": [ { @@ -287,6 +288,7 @@ ], "frozen": false, "icon": "braces", + "metadata": {}, "output_types": [], "outputs": [ { @@ -423,6 +425,7 @@ ], "frozen": false, "icon": "prompts", + "metadata": {}, "output_types": [], "outputs": [ { @@ -664,6 +667,7 @@ ], "frozen": false, "icon": "ChatOutput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -865,6 +869,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json index 5030fda0c..702f273e4 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json @@ -629,6 +629,7 @@ ], "frozen": false, "icon": "CrewAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -874,6 +875,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1164,6 +1166,7 @@ ], "frozen": false, "icon": "ChatOutput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1349,6 +1352,7 @@ ], "frozen": false, "icon": "CrewAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1488,6 +1492,7 @@ ], "frozen": false, "icon": "CrewAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1742,6 +1747,7 @@ ], "frozen": false, "icon": "CrewAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -2001,6 +2007,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -2295,6 +2302,7 @@ "is_composition": null, "is_input": null, "is_output": null, + "metadata": {}, "name": "", "output_types": [], "outputs": [ @@ -2409,6 +2417,7 @@ ], "frozen": false, "icon": "ChatInput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -2616,6 +2625,7 @@ "edited": false, "field_order": [], "frozen": false, + "metadata": {}, "output_types": [], "outputs": [ { @@ -2733,6 +2743,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -3147,6 +3158,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -3585,6 +3597,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -3881,6 +3894,7 @@ "is_composition": null, "is_input": null, "is_output": null, + "metadata": {}, "name": "", "output_types": [], "outputs": [ @@ -4047,6 +4061,7 @@ "search_params" ], "frozen": false, + "metadata": {}, "output_types": [], "outputs": [ { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json b/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json index f73898c80..f73456a22 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json @@ -161,6 +161,7 @@ ], "frozen": false, "icon": "prompts", + "metadata": {}, "output_types": [], "outputs": [ { @@ -305,6 +306,7 @@ ], "frozen": false, "icon": "ChatInput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -521,6 +523,7 @@ ], "frozen": false, "icon": "ChatOutput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -722,6 +725,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1009,6 +1013,7 @@ ], "frozen": false, "icon": "braces", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1141,6 +1146,7 @@ ], "frozen": false, "icon": "file-text", + "metadata": {}, "output_types": [], "outputs": [ { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json index 41b0c3465..e3aeea0a8 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json @@ -326,6 +326,7 @@ ], "frozen": false, "icon": "CrewAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -574,6 +575,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -868,6 +870,7 @@ ], "frozen": false, "icon": "ChatOutput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1058,6 +1061,7 @@ ], "frozen": false, "icon": "CrewAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1202,6 +1206,7 @@ ], "frozen": false, "icon": "CrewAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1460,6 +1465,7 @@ ], "frozen": false, "icon": "CrewAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1721,6 +1727,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -2019,6 +2026,7 @@ "is_composition": null, "is_input": null, "is_output": null, + "metadata": {}, "name": "", "output_types": [], "outputs": [ @@ -2137,6 +2145,7 @@ ], "frozen": false, "icon": "ChatInput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -2356,6 +2365,7 @@ ], "frozen": false, "icon": "CrewAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -2606,6 +2616,7 @@ "search_params" ], "frozen": false, + "metadata": {}, "output_types": [], "outputs": [ { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json b/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json index 7c4dd0260..b774e5c78 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json @@ -135,6 +135,7 @@ ], "frozen": false, "icon": "prompts", + "metadata": {}, "output_types": [], "outputs": [ { @@ -279,6 +280,7 @@ ], "frozen": false, "icon": "ChatInput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -502,6 +504,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -792,6 +795,7 @@ ], "frozen": false, "icon": "ChatOutput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -984,6 +988,7 @@ ], "frozen": false, "icon": "message-square-more", + "metadata": {}, "output_types": [], "outputs": [ { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json index 975094d3b..e9c93824f 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json @@ -390,6 +390,7 @@ "frozen": false, "icon": "CrewAI", "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { @@ -587,6 +588,7 @@ "frozen": false, "icon": "OpenAI", "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { @@ -878,6 +880,7 @@ "frozen": false, "icon": "ChatOutput", "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1161,6 +1164,7 @@ "frozen": false, "icon": "prompts", "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1281,6 +1285,7 @@ "frozen": false, "icon": "prompts", "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1401,6 +1406,7 @@ "frozen": false, "icon": "prompts", "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1528,6 +1534,7 @@ "frozen": false, "icon": "CrewAI", "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1878,6 +1885,7 @@ "frozen": false, "icon": "CrewAI", "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { @@ -2228,6 +2236,7 @@ "frozen": false, "icon": "CrewAI", "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { @@ -2562,6 +2571,7 @@ "field_order": [], "frozen": false, "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json b/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json index 251a2c550..5ff9b1228 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json @@ -399,6 +399,7 @@ "frozen": false, "icon": "ChatInput", "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { @@ -620,6 +621,7 @@ "frozen": false, "icon": "ChatOutput", "lf_version": "1.0.15", + "metadata": {}, "output_types": [], "outputs": [ { @@ -827,6 +829,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1405,6 +1408,7 @@ ], "frozen": false, "lf_version": "1.0.15", + "metadata": {}, "official": false, "output_types": [], "outputs": [ @@ -2265,6 +2269,7 @@ "frozen": false, "icon": "calculator", "lf_version": "1.0.15", + "metadata": {}, "official": false, "output_types": [], "outputs": [ diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json index 1bf063006..7a9b5360f 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json @@ -291,6 +291,7 @@ ], "frozen": false, "icon": "ChatInput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -523,6 +524,7 @@ ], "frozen": false, "icon": "AstraDB", + "metadata": {}, "output_types": [], "outputs": [ { @@ -994,6 +996,7 @@ ], "frozen": false, "icon": "braces", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1130,6 +1133,7 @@ ], "frozen": false, "icon": "prompts", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1274,6 +1278,7 @@ ], "frozen": false, "icon": "ChatOutput", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1465,6 +1470,7 @@ ], "frozen": false, "icon": "scissors-line-dashed", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1609,6 +1615,7 @@ ], "frozen": false, "icon": "file-text", + "metadata": {}, "output_types": [], "outputs": [ { @@ -1757,6 +1764,7 @@ ], "frozen": false, "icon": "AstraDB", + "metadata": {}, "output_types": [], "outputs": [ { @@ -2246,6 +2254,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -2699,6 +2708,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { @@ -3144,6 +3154,7 @@ ], "frozen": false, "icon": "OpenAI", + "metadata": {}, "output_types": [], "outputs": [ { diff --git a/src/backend/base/langflow/template/frontend_node/base.py b/src/backend/base/langflow/template/frontend_node/base.py index 4077d4a8c..c62d72733 100644 --- a/src/backend/base/langflow/template/frontend_node/base.py +++ b/src/backend/base/langflow/template/frontend_node/base.py @@ -53,6 +53,8 @@ class FrontendNode(BaseModel): """Error message for the frontend node.""" edited: bool = False """Whether the frontend node has been edited.""" + metadata: dict = {} + """Metadata for the component node.""" def set_documentation(self, documentation: str) -> None: """Sets the documentation of the frontend node.""" diff --git a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx index 5d48f1577..46b04fbb6 100644 --- a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx @@ -46,29 +46,48 @@ export default function ExtraSidebar(): JSX.Element { event.dataTransfer.setDragImage(crt, 0, 0); event.dataTransfer.setData("genericNode", JSON.stringify(data)); } - function normalizeString(str: string): string { return str.toLowerCase().replace(/_/g, " ").replace(/\s+/g, ""); } - // Handle showing components after use search input + function searchInMetadata(metadata: any, searchTerm: string): boolean { + if (!metadata || typeof metadata !== "object") return false; + + return Object.entries(metadata).some(([key, value]) => { + if (typeof value === "string") { + return ( + normalizeString(key).includes(searchTerm) || + normalizeString(value).includes(searchTerm) + ); + } + if (typeof value === "object") { + return searchInMetadata(value, searchTerm); + } + return false; + }); + } + function handleSearchInput(e: string) { if (e === "") { setFilterData(data); return; } + + const searchTerm = normalizeString(e); + setFilterData((_) => { - let ret = {}; - Object.keys(data).forEach((d: keyof APIObjectType, i) => { + let ret: APIObjectType = {}; + Object.keys(data).forEach((d: keyof APIObjectType) => { ret[d] = {}; - let keys = Object.keys(data[d]).filter( - (nd) => - normalizeString(nd).includes(normalizeString(e)) || - normalizeString(data[d][nd].display_name).includes( - normalizeString(e), - ) || - normalizeString(d.toString()).includes(normalizeString(e)), - ); + let keys = Object.keys(data[d]).filter((nd) => { + const item = data[d][nd]; + return ( + normalizeString(nd).includes(searchTerm) || + normalizeString(item.display_name).includes(searchTerm) || + normalizeString(d.toString()).includes(searchTerm) || + (item.metadata && searchInMetadata(item.metadata, searchTerm)) + ); + }); keys.forEach((element) => { ret[d][element] = data[d][element]; });