From 8d6149a12472e19dbfe050e4ccb670c3c06143c7 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 15 Jun 2023 18:03:28 -0300 Subject: [PATCH 1/8] refactor(PageComponent): add support for setting tabsState.isPending to true when nodes or edges change to indicate that the flow is being edited feat(PageComponent): import NodeChange from reactflow and add onNodesChangeMod function to handle nodes change and set tabsState.isPending to true --- .../components/PageComponent/index.tsx | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 4a88719b4..c85aa373c 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -1,4 +1,4 @@ -import _ from "lodash"; +import _, { set } from "lodash"; import { useContext, useRef, useState, useEffect, useCallback } from "react"; import ReactFlow, { OnSelectionChangeParams, @@ -15,6 +15,7 @@ import ReactFlow, { updateEdge, Background, Controls, + NodeChange, } from "reactflow"; import GenericNode from "../../../../CustomNodes/GenericNode"; import Chat from "../../../../components/chatComponent"; @@ -43,7 +44,8 @@ export default function Page({ flow }: { flow: FlowType }) { lastCopiedSelection, setLastCopiedSelection, tabsState, - saveFlow + saveFlow, + setTabsState } = useContext(TabsContext); const { types, reactFlowInstance, setReactFlowInstance, templates } = useContext(typesContext); @@ -145,8 +147,25 @@ export default function Page({ flow }: { flow: FlowType }) { let newX = _.cloneDeep(x); return newX; }); + setTabsState((prev)=>{ + let newState = _.cloneDeep(prev); + newState[flow.id].isPending = true; + return newState; + }) }, - [onEdgesChange, setNodes] + [onEdgesChange, setNodes,setTabsState,flow.id] + ); + + const onNodesChangeMod = useCallback( + (s: NodeChange[]) => { + onNodesChange(s); + setTabsState((prev)=>{ + let newState = _.cloneDeep(prev); + newState[flow.id].isPending = true; + return newState; + }) + }, + [onNodesChange,setTabsState,flow.id] ); const onConnect = useCallback( @@ -345,7 +364,7 @@ export default function Page({ flow }: { flow: FlowType }) { onPaneMouseLeave={() => { setDisableCopyPaste(true); }} - onNodesChange={onNodesChange} + onNodesChange={onNodesChangeMod} onEdgesChange={onEdgesChangeMod} onConnect={onConnect} disableKeyboardA11y={true} From 3d2632aa5f3584ea192f40ec50f1155f6b359f08 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 15 Jun 2023 18:12:55 -0300 Subject: [PATCH 2/8] refactor(PageComponent): refactor setTabsState calls to use tabId instead of flow.id to improve semantics and avoid bugs --- .../components/PageComponent/index.tsx | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index c85aa373c..b62cca5af 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -45,7 +45,8 @@ export default function Page({ flow }: { flow: FlowType }) { setLastCopiedSelection, tabsState, saveFlow, - setTabsState + setTabsState, + tabId } = useContext(TabsContext); const { types, reactFlowInstance, setReactFlowInstance, templates } = useContext(typesContext); @@ -147,25 +148,31 @@ export default function Page({ flow }: { flow: FlowType }) { let newX = _.cloneDeep(x); return newX; }); - setTabsState((prev)=>{ - let newState = _.cloneDeep(prev); - newState[flow.id].isPending = true; - return newState; - }) + setTabsState((prev) => { + return { + ...prev, + [tabId]: { + isPending: true, + }, + }; + }); }, - [onEdgesChange, setNodes,setTabsState,flow.id] + [onEdgesChange, setNodes,setTabsState,tabId] ); const onNodesChangeMod = useCallback( (s: NodeChange[]) => { onNodesChange(s); - setTabsState((prev)=>{ - let newState = _.cloneDeep(prev); - newState[flow.id].isPending = true; - return newState; - }) + setTabsState((prev) => { + return { + ...prev, + [tabId]: { + isPending: true, + }, + }; + }); }, - [onNodesChange,setTabsState,flow.id] + [onNodesChange,setTabsState,tabId] ); const onConnect = useCallback( From 9909416694ce0c593d3c9ddbcb4709eb77963a87 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 15 Jun 2023 18:14:23 -0300 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=93=9D=20docs(constants.tsx):=20add?= =?UTF-8?q?=20documentation=20for=20TWEAKS=20constant=20The=20TWEAKS=20con?= =?UTF-8?q?stant=20is=20a=20dictionary=20that=20can=20be=20used=20to=20twe?= =?UTF-8?q?ak=20the=20flow.=20It=20is=20an=20optional=20parameter=20that?= =?UTF-8?q?=20can=20be=20passed=20to=20the=20run=5Fflow=20function.=20The?= =?UTF-8?q?=20dictionary=20should=20be=20in=20the=20format=20{"OpenAI-XXXX?= =?UTF-8?q?X":=20{"model=5Fname":=20"gpt-4"}}.=20This=20commit=20adds=20do?= =?UTF-8?q?cumentation=20to=20the=20code=20to=20make=20it=20easier=20for?= =?UTF-8?q?=20developers=20to=20understand=20how=20to=20use=20the=20TWEAKS?= =?UTF-8?q?=20constant.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/constants.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/src/constants.tsx b/src/frontend/src/constants.tsx index 55a4123ce..e32336578 100644 --- a/src/frontend/src/constants.tsx +++ b/src/frontend/src/constants.tsx @@ -68,6 +68,8 @@ BASE_API_URL = "${window.location.protocol}//${ window.location.host }/ap1/v1/predict" FLOW_ID = "${flowId}" +# You can tweak the flow by adding a tweaks dictionary +# e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}} TWEAKS = ${JSON.stringify(tweaks, null, 2)} def run_flow(message: str, flow_id: str, tweaks: dict = None) -> dict: From 99ce6430101b14db3900784370d9f65f7ba026fc Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 15 Jun 2023 18:14:48 -0300 Subject: [PATCH 4/8] =?UTF-8?q?=F0=9F=90=9B=20fix(llms.py):=20add=20condit?= =?UTF-8?q?ion=20to=20show=20password=20field=20for=20'tokens'=20field=20n?= =?UTF-8?q?ame=20The=20condition=20to=20show=20the=20password=20field=20wa?= =?UTF-8?q?s=20not=20working=20for=20the=20'tokens'=20field=20name.=20The?= =?UTF-8?q?=20condition=20has=20been=20updated=20to=20include=20'tokens'?= =?UTF-8?q?=20in=20the=20field=20name=20and=20show=20the=20password=20fiel?= =?UTF-8?q?d.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/template/frontend_node/llms.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/backend/langflow/template/frontend_node/llms.py b/src/backend/langflow/template/frontend_node/llms.py index d2af037dc..272e42c7f 100644 --- a/src/backend/langflow/template/frontend_node/llms.py +++ b/src/backend/langflow/template/frontend_node/llms.py @@ -46,7 +46,10 @@ class LLMFrontendNode(FrontendNode): if field.name in SHOW_FIELDS: field.show = True - if "api" in field.name and ("key" in field.name or "token" in field.name): + if "api" in field.name and ( + "key" in field.name + or ("token" in field.name and "tokens" not in field.name) + ): field.password = True field.show = True # Required should be False to support From da24690bf70b9f1b424c0529a495818dce701df6 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 15 Jun 2023 18:16:02 -0300 Subject: [PATCH 5/8] =?UTF-8?q?=F0=9F=94=92=20chore(flows.py):=20add=20sup?= =?UTF-8?q?port=20for=20removing=20API=20keys=20from=20flow=20data=20if=20?= =?UTF-8?q?save=5Fapi=5Fkeys=20setting=20is=20False=20The=20`remove=5Fapi?= =?UTF-8?q?=5Fkeys`=20function=20is=20now=20called=20on=20the=20`flow=5Fda?= =?UTF-8?q?ta`=20dictionary=20if=20the=20`save=5Fapi=5Fkeys`=20setting=20i?= =?UTF-8?q?s=20False.=20This=20ensures=20that=20sensitive=20information=20?= =?UTF-8?q?is=20not=20saved=20in=20the=20database.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/flows.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/backend/langflow/api/v1/flows.py b/src/backend/langflow/api/v1/flows.py index b707f5e05..6534d83e6 100644 --- a/src/backend/langflow/api/v1/flows.py +++ b/src/backend/langflow/api/v1/flows.py @@ -1,5 +1,7 @@ from typing import List from uuid import UUID +from langflow import settings +from langflow.api.utils import remove_api_keys from langflow.api.v1.schemas import FlowListCreate, FlowListRead from langflow.database.models.flow import ( Flow, @@ -54,10 +56,13 @@ def update_flow( *, session: Session = Depends(get_session), flow_id: UUID, flow: FlowUpdate ): """Update a flow.""" + db_flow = session.get(Flow, flow_id) if not db_flow: raise HTTPException(status_code=404, detail="Flow not found") flow_data = flow.dict(exclude_unset=True) + if settings.save_api_keys: + flow_data = remove_api_keys(flow_data) for key, value in flow_data.items(): setattr(db_flow, key, value) session.add(db_flow) From 7d61e3dd16aeded5ed8313503a88f721a6497d96 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 15 Jun 2023 18:16:23 -0300 Subject: [PATCH 6/8] =?UTF-8?q?=F0=9F=86=95=20feat(utils.py):=20add=20func?= =?UTF-8?q?tion=20to=20remove=20API=20keys=20from=20flow=20data=20This=20c?= =?UTF-8?q?ommit=20adds=20a=20new=20function=20called=20`remove=5Fapi=5Fke?= =?UTF-8?q?ys`=20to=20the=20`utils.py`=20file.=20The=20function=20takes=20?= =?UTF-8?q?in=20a=20dictionary=20representing=20a=20flow=20and=20removes?= =?UTF-8?q?=20any=20API=20keys=20from=20the=20flow=20data.=20The=20functio?= =?UTF-8?q?n=20iterates=20through=20each=20node=20in=20the=20flow=20and=20?= =?UTF-8?q?checks=20if=20the=20node=20contains=20any=20API=20keys.=20If=20?= =?UTF-8?q?an=20API=20key=20is=20found,=20the=20function=20sets=20the=20va?= =?UTF-8?q?lue=20of=20the=20key=20to=20`None`.=20This=20function=20is=20us?= =?UTF-8?q?eful=20for=20removing=20sensitive=20information=20from=20flow?= =?UTF-8?q?=20data=20before=20it=20is=20stored=20or=20transmitted.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/utils.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/backend/langflow/api/utils.py diff --git a/src/backend/langflow/api/utils.py b/src/backend/langflow/api/utils.py new file mode 100644 index 000000000..bfd9e3da5 --- /dev/null +++ b/src/backend/langflow/api/utils.py @@ -0,0 +1,24 @@ +API_WORDS = ["api", "key", "token"] + + +def has_api_terms(word: str): + return "api" in word and ( + "key" in word or ("token" in word and "tokens" not in word) + ) + + +def remove_api_keys(flow: dict): + """Remove api keys from flow data.""" + if flow.get("data") and flow["data"].get("nodes"): + for node in flow["data"]["nodes"]: + node_data = node.get("data").get("node") + template = node_data.get("template") + for value in template.values(): + if ( + isinstance(value, dict) + and has_api_terms(value["name"]) + and value.get("password") + ): + value["value"] = None + + return flow From e76917b53efa4de4d4d6eaa7a3037caeefffc1df Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 15 Jun 2023 18:22:12 -0300 Subject: [PATCH 7/8] =?UTF-8?q?=F0=9F=94=A7=20refactor(=5F=5Fmain=5F=5F.py?= =?UTF-8?q?):=20refactor=20update=5Fsettings=20function=20to=20accept=20sa?= =?UTF-8?q?ve=5Fapi=5Fkeys=20parameter=20=E2=9C=A8=20feat(=5F=5Fmain=5F=5F?= =?UTF-8?q?.py):=20add=20save=5Fapi=5Fkeys=20parameter=20to=20serve=20comm?= =?UTF-8?q?and=20to=20allow=20users=20to=20save=20API=20keys=20in=20their?= =?UTF-8?q?=20projects=20The=20update=5Fsettings=20function=20now=20accept?= =?UTF-8?q?s=20a=20save=5Fapi=5Fkeys=20parameter,=20which=20allows=20the?= =?UTF-8?q?=20user=20to=20specify=20whether=20or=20not=20to=20save=20API?= =?UTF-8?q?=20keys=20in=20their=20projects.=20The=20serve=20command=20now?= =?UTF-8?q?=20has=20a=20save=5Fapi=5Fkeys=20parameter=20that=20defaults=20?= =?UTF-8?q?to=20True,=20allowing=20users=20to=20save=20API=20keys=20in=20t?= =?UTF-8?q?heir=20projects.=20This=20feature=20improves=20the=20user=20exp?= =?UTF-8?q?erience=20by=20allowing=20them=20to=20save=20API=20keys=20for?= =?UTF-8?q?=20future=20use.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/__main__.py | 18 +++++++++++++++--- src/backend/langflow/settings.py | 11 +++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/backend/langflow/__main__.py b/src/backend/langflow/__main__.py index 9296a4c64..0a02fe376 100644 --- a/src/backend/langflow/__main__.py +++ b/src/backend/langflow/__main__.py @@ -27,12 +27,19 @@ def get_number_of_workers(workers=None): return workers -def update_settings(config: str, dev: bool = False, database_url: Optional[str] = None): +def update_settings( + config: str, + dev: bool = False, + database_url: Optional[str] = None, + save_api_keys: bool = True, +): """Update the settings from a config file.""" if config: settings.update_from_yaml(config, dev=dev) if database_url: - settings.update_database_url(database_url) + settings.update_settings(database_url=database_url) + if save_api_keys: + settings.update_settings(save_api_keys=save_api_keys) def serve_on_jcloud(): @@ -107,6 +114,9 @@ def serve( open_browser: bool = typer.Option( True, help="Open the browser after starting the server." ), + save_api_keys: bool = typer.Option( + True, help="Save API keys in your projects for future use." + ), ): """ Run the Langflow server. @@ -132,7 +142,9 @@ def serve( load_dotenv(env_file) configure(log_level=log_level, log_file=log_file) - update_settings(config, dev=dev, database_url=database_url) + update_settings( + config, dev=dev, database_url=database_url, save_api_keys=save_api_keys + ) app = create_app() # get the directory of the current file if not path: diff --git a/src/backend/langflow/settings.py b/src/backend/langflow/settings.py index 1f304b67d..400a1e3e1 100644 --- a/src/backend/langflow/settings.py +++ b/src/backend/langflow/settings.py @@ -1,5 +1,5 @@ import os -from typing import List, Optional +from typing import List import yaml from pydantic import BaseSettings, root_validator @@ -21,6 +21,7 @@ class Settings(BaseSettings): utilities: List[str] = [] dev: bool = False database_url: str = "sqlite:///./langflow.db" + save_api_keys: bool = True class Config: validate_assignment = True @@ -47,10 +48,12 @@ class Settings(BaseSettings): self.textsplitters = new_settings.textsplitters or [] self.utilities = new_settings.utilities or [] self.dev = dev + self.save_api_keys = new_settings.save_api_keys - def update_database_url(self, database_url: Optional[str] = None): - if database_url: - self.database_url = database_url + def update_settings(self, **kwargs): + for key, value in kwargs.items(): + if hasattr(self, key): + setattr(self, key, value) def save_settings_to_yaml(settings: Settings, file_path: str): From 2addbf2e0cd32567b587cdd334953a8167959d25 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 15 Jun 2023 19:29:29 -0300 Subject: [PATCH 8/8] =?UTF-8?q?=F0=9F=94=A7=20chore(settings.py):=20change?= =?UTF-8?q?=20save=5Fapi=5Fkeys=20to=20remove=5Fapi=5Fkeys=20to=20improve?= =?UTF-8?q?=20semantics=20The=20`save=5Fapi=5Fkeys`=20variable=20has=20bee?= =?UTF-8?q?n=20renamed=20to=20`remove=5Fapi=5Fkeys`=20to=20improve=20the?= =?UTF-8?q?=20semantics=20of=20the=20code.=20The=20new=20variable=20name?= =?UTF-8?q?=20better=20reflects=20the=20functionality=20of=20the=20code,?= =?UTF-8?q?=20which=20is=20to=20remove=20API=20keys=20from=20the=20project?= =?UTF-8?q?s=20saved=20in=20the=20database.=20=F0=9F=94=A7=20chore(=5F=5Fm?= =?UTF-8?q?ain=5F=5F.py):=20change=20save=5Fapi=5Fkeys=20to=20remove=5Fapi?= =?UTF-8?q?=5Fkeys=20to=20improve=20semantics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/__main__.py | 12 ++++++------ src/backend/langflow/api/v1/flows.py | 4 ++-- src/backend/langflow/settings.py | 3 +-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/backend/langflow/__main__.py b/src/backend/langflow/__main__.py index 0a02fe376..91f68d711 100644 --- a/src/backend/langflow/__main__.py +++ b/src/backend/langflow/__main__.py @@ -31,15 +31,15 @@ def update_settings( config: str, dev: bool = False, database_url: Optional[str] = None, - save_api_keys: bool = True, + remove_api_keys: bool = False, ): """Update the settings from a config file.""" if config: settings.update_from_yaml(config, dev=dev) if database_url: settings.update_settings(database_url=database_url) - if save_api_keys: - settings.update_settings(save_api_keys=save_api_keys) + if remove_api_keys: + settings.update_settings(remove_api_keys=remove_api_keys) def serve_on_jcloud(): @@ -114,8 +114,8 @@ def serve( open_browser: bool = typer.Option( True, help="Open the browser after starting the server." ), - save_api_keys: bool = typer.Option( - True, help="Save API keys in your projects for future use." + remove_api_keys: bool = typer.Option( + False, help="Remove API keys from the projects saved in the database." ), ): """ @@ -143,7 +143,7 @@ def serve( configure(log_level=log_level, log_file=log_file) update_settings( - config, dev=dev, database_url=database_url, save_api_keys=save_api_keys + config, dev=dev, database_url=database_url, remove_api_keys=remove_api_keys ) app = create_app() # get the directory of the current file diff --git a/src/backend/langflow/api/v1/flows.py b/src/backend/langflow/api/v1/flows.py index 6534d83e6..c440fc18b 100644 --- a/src/backend/langflow/api/v1/flows.py +++ b/src/backend/langflow/api/v1/flows.py @@ -1,6 +1,6 @@ from typing import List from uuid import UUID -from langflow import settings +from langflow.settings import settings from langflow.api.utils import remove_api_keys from langflow.api.v1.schemas import FlowListCreate, FlowListRead from langflow.database.models.flow import ( @@ -61,7 +61,7 @@ def update_flow( if not db_flow: raise HTTPException(status_code=404, detail="Flow not found") flow_data = flow.dict(exclude_unset=True) - if settings.save_api_keys: + if not settings.remove_api_keys: flow_data = remove_api_keys(flow_data) for key, value in flow_data.items(): setattr(db_flow, key, value) diff --git a/src/backend/langflow/settings.py b/src/backend/langflow/settings.py index 400a1e3e1..9d6ac3fa9 100644 --- a/src/backend/langflow/settings.py +++ b/src/backend/langflow/settings.py @@ -21,7 +21,7 @@ class Settings(BaseSettings): utilities: List[str] = [] dev: bool = False database_url: str = "sqlite:///./langflow.db" - save_api_keys: bool = True + remove_api_keys: bool = False class Config: validate_assignment = True @@ -48,7 +48,6 @@ class Settings(BaseSettings): self.textsplitters = new_settings.textsplitters or [] self.utilities = new_settings.utilities or [] self.dev = dev - self.save_api_keys = new_settings.save_api_keys def update_settings(self, **kwargs): for key, value in kwargs.items():