From 7cc14e83b83972411c0fcd9bb8a432f9edb988f8 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 13 Jun 2023 12:48:52 -0300 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=9A=80=20feat(endpoints.py):=20add=20?= =?UTF-8?q?authentication=20to=20predict=20endpoint=20using=20HTTPBearer?= =?UTF-8?q?=20=F0=9F=90=9B=20fix(endpoints.py):=20change=20predict=20endpo?= =?UTF-8?q?int=20to=20use=20Flow=20object=20instead=20of=20flow=5Fid=20?= =?UTF-8?q?=F0=9F=90=9B=20fix(endpoints.py):=20add=20support=20for=20proce?= =?UTF-8?q?ssing=20tweaks=20in=20predict=20endpoint=20The=20predict=20endp?= =?UTF-8?q?oint=20now=20requires=20authentication=20using=20HTTPBearer.=20?= =?UTF-8?q?The=20flow=5Fid=20is=20now=20extracted=20from=20the=20bearer=20?= =?UTF-8?q?token=20instead=20of=20being=20passed=20as=20a=20parameter.=20T?= =?UTF-8?q?his=20improves=20security=20as=20the=20flow=5Fid=20is=20not=20e?= =?UTF-8?q?xposed=20in=20the=20URL.=20The=20predict=20endpoint=20now=20use?= =?UTF-8?q?s=20the=20Flow=20object=20instead=20of=20the=20flow=5Fid=20to?= =?UTF-8?q?=20retrieve=20the=20graph=20data.=20This=20improves=20code=20re?= =?UTF-8?q?adability=20and=20reduces=20the=20number=20of=20database=20quer?= =?UTF-8?q?ies.=20The=20predict=20endpoint=20now=20supports=20processing?= =?UTF-8?q?=20tweaks,=20which=20allows=20for=20more=20flexibility=20in=20t?= =?UTF-8?q?he=20processing=20of=20messages.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 34 ++++++++++++++++++------ 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index e38906fbf..e99fb723f 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -1,9 +1,10 @@ from langflow.database.models.flow import Flow -from langflow.processing.process import process_graph_cached +from langflow.processing.process import process_graph_cached, process_tweaks from langflow.utils.logger import logger from importlib.metadata import version from fastapi import APIRouter, Depends, HTTPException +from fastapi.security import HTTPBearer from langflow.api.v1.schemas import ( PredictRequest, @@ -17,23 +18,40 @@ from sqlmodel import Session # build router router = APIRouter(tags=["Base"]) +security = HTTPBearer() + + +def get_flow_from_token( + bearer: HTTPBearer = Depends(security), session: Session = Depends(get_session) +) -> str: + # Extract the token, which is the flow_id in this case + flow_id = bearer.credentials + # Check if the flow_id exists in the database + flow = session.get(Flow, flow_id) + if flow is None: + raise HTTPException(status_code=401, detail="Invalid token") + return flow + @router.get("/all") def get_all(): return build_langchain_types_dict() -@router.post("/predict/{flow_id}", status_code=200, response_model=PredictResponse) +@router.post("/predict", response_model=PredictResponse) async def get_load( predict_request: PredictRequest, - flow_id: str, - session: Session = Depends(get_session), + flow: Flow = Depends(get_flow_from_token), ): + """ + Endpoint to process a message using the flow passed in the bearer token. + """ + try: - flow_obj = session.get(Flow, flow_id) - if flow_obj is None: - raise ValueError(f"Flow {flow_id} not found") - graph_data = flow_obj.data + graph_data = flow.data + if predict_request.tweaks: + graph_data = process_tweaks(graph_data, predict_request.tweaks) + response = process_graph_cached(graph_data, predict_request.message) return PredictResponse( result=response.get("result", ""), From faf44eca0e5ac0cd588c977f4bfe1ee58daaff37 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 13 Jun 2023 12:49:11 -0300 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=94=A7=20refactor(schemas.py):=20add?= =?UTF-8?q?=20optional=20tweaks=20field=20to=20PredictRequest=20schema=20T?= =?UTF-8?q?he=20PredictRequest=20schema=20now=20includes=20an=20optional?= =?UTF-8?q?=20tweaks=20field,=20which=20is=20a=20dictionary=20of=20diction?= =?UTF-8?q?aries.=20This=20field=20allows=20for=20additional=20customizati?= =?UTF-8?q?on=20of=20the=20prediction=20request,=20such=20as=20specifying?= =?UTF-8?q?=20tool=20names=20or=20descriptions.=20The=20tweaks=20field=20i?= =?UTF-8?q?s=20optional,=20and=20if=20not=20provided,=20the=20default=20va?= =?UTF-8?q?lue=20is=20an=20empty=20dictionary.=20The=20schema=5Fextra=20at?= =?UTF-8?q?tribute=20has=20also=20been=20updated=20to=20include=20an=20exa?= =?UTF-8?q?mple=20of=20the=20new=20tweaks=20field.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/schemas.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/backend/langflow/api/v1/schemas.py b/src/backend/langflow/api/v1/schemas.py index d573a2ae2..aae4a1df3 100644 --- a/src/backend/langflow/api/v1/schemas.py +++ b/src/backend/langflow/api/v1/schemas.py @@ -1,6 +1,6 @@ -from typing import Any, Dict, List, Union +from typing import Any, Dict, List, Optional, Union from langflow.database.models.flow import FlowCreate, FlowRead -from pydantic import BaseModel, validator +from pydantic import BaseModel, Field, validator class GraphData(BaseModel): @@ -23,6 +23,23 @@ class PredictRequest(BaseModel): """Predict request schema.""" message: str + tweaks: Optional[Dict[str, Dict[str, str]]] = Field(default_factory=dict) + + class Config: + schema_extra = { + "example": { + "message": "Hello, how are you?", + "tweaks": { + "dndnode_986363f0-4677-4035-9f38-74b94af5dd78": { + "name": "A tool name", + "description": "A tool description", + }, + "dndnode_986363f0-4677-4035-9f38-74b94af57378": { + "template": "A {template}", + }, + }, + } + } class PredictResponse(BaseModel): From 90a1dd9795ef297a072c9603f6f08eb670d876ce Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 13 Jun 2023 12:49:22 -0300 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=94=A7=20chore(process.py):=20refacto?= =?UTF-8?q?r=20process=5Ftweaks=20function=20to=20improve=20readability=20?= =?UTF-8?q?and=20maintainability=20The=20process=5Ftweaks=20function=20has?= =?UTF-8?q?=20been=20refactored=20to=20improve=20readability=20and=20maint?= =?UTF-8?q?ainability.=20The=20function=20now=20takes=20in=20two=20paramet?= =?UTF-8?q?ers,=20graph=5Fdata=20and=20tweaks,=20and=20returns=20the=20mod?= =?UTF-8?q?ified=20graph=5Fdata.=20The=20tweaks=20parameter=20is=20a=20dic?= =?UTF-8?q?tionary=20of=20dictionaries,=20where=20the=20key=20is=20the=20n?= =?UTF-8?q?ode=20id=20and=20the=20value=20is=20a=20dictionary=20of=20the?= =?UTF-8?q?=20tweaks.=20The=20function=20processes=20the=20graph=20data=20?= =?UTF-8?q?to=20add=20the=20tweaks=20by=20iterating=20over=20the=20nodes?= =?UTF-8?q?=20and=20checking=20if=20the=20node=20id=20is=20in=20the=20twea?= =?UTF-8?q?ks=20dictionary.=20If=20it=20is,=20the=20function=20applies=20t?= =?UTF-8?q?he=20tweaks=20to=20the=20node=20by=20updating=20the=20template?= =?UTF-8?q?=20data=20with=20the=20new=20values.=20The=20function=20also=20?= =?UTF-8?q?prints=20a=20message=20to=20the=20console=20to=20indicate=20tha?= =?UTF-8?q?t=20a=20tweak=20has=20been=20applied.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/processing/process.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 3b8852e00..ae0bf28b1 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -170,3 +170,25 @@ def load_flow_from_json(path: str, build=True): fix_memory_inputs(langchain_object) return langchain_object return graph + + +def process_tweaks(graph_data: dict, tweaks: dict): + """This function is used to tweak the graph data using the node id and the tweaks dict""" + # the tweaks dict is a dict of dicts + # the key is the node id and the value is a dict of the tweaks + # the dict of tweaks contains the name of a certain parameter and the value to be tweaked + + # We need to process the graph data to add the tweaks + nodes = graph_data["data"]["nodes"] + for node in nodes: + node_id = node["id"] + if node_id in tweaks: + node_tweaks = tweaks[node_id] + template_data = node["data"]["node"]["template"] + for tweak_name, tweake_value in node_tweaks.items(): + if tweak_name in template_data: + template_data[tweak_name]["value"] = tweake_value + print( + f"Something changed in node {node_id} with tweak {tweak_name} and value {tweake_value}" + ) + return graph_data From 2d3428307ef4fe8a0473b102f8328cae483fbd82 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 13 Jun 2023 12:49:41 -0300 Subject: [PATCH 4/5] =?UTF-8?q?=F0=9F=94=A8=20refactor(constants.tsx):=20r?= =?UTF-8?q?emove=20flowId=20from=20API=5FURL=20and=20add=20it=20to=20the?= =?UTF-8?q?=20headers=20as=20an=20Authorization=20token=20The=20flowId=20i?= =?UTF-8?q?s=20now=20passed=20as=20an=20Authorization=20token=20in=20the?= =?UTF-8?q?=20headers=20of=20the=20API=20request.=20This=20improves=20secu?= =?UTF-8?q?rity=20as=20the=20flowId=20is=20not=20exposed=20in=20the=20URL.?= =?UTF-8?q?=20The=20API=5FURL=20now=20only=20contains=20the=20base=20URL?= =?UTF-8?q?=20without=20the=20flowId.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/constants.tsx | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/frontend/src/constants.tsx b/src/frontend/src/constants.tsx index 82d256c6e..9671ab5c2 100644 --- a/src/frontend/src/constants.tsx +++ b/src/frontend/src/constants.tsx @@ -46,15 +46,28 @@ export const TEXT_DIALOG_SUBTITLE = "Edit you text."; export const getPythonApiCode = (flowId: string): string => { return `import requests - FLOW_ID = "${flowId}" - API_URL = f"${window.location.protocol}//${window.location.host}/predict/{FLOW_ID}" +FLOW_ID = "${flowId}" +API_URL = f"${window.location.protocol}//${window.location.host}/predict" - def predict(message): - payload = {'message': message} - response = requests.post(API_URL, json=payload) - return response.json() +def run_flow(message, tweaks=None): - print(predict("Your message"))`; + if tweaks: + payload = {'message': message, 'tweaks': tweaks} + else: + payload = {'message': message} + + headers = {'Authorization': + f'Bearer {FLOW_ID}', + 'Content-Type': 'application/json' + } + + response = requests.post(API_URL, json=payload) + return response.json() + +# Setup any tweaks you want to apply to the flow +tweaks = {} # {"nodeId": {"key": "value"}, "nodeId2": {"key": "value"}} + +print(run_flow("Your message", tweaks=tweaks))`; }; /** @@ -65,8 +78,9 @@ export const getPythonApiCode = (flowId: string): string => { export const getCurlCode = (flowId: string): string => { return `curl -X POST \\ -H "Content-Type: application/json" \\ + -H "Authorization: Bearer ${flowId}" \\ -d '{"message": "Your message"}' \\ - ${window.location.protocol}//${window.location.host}/predict/${flowId}`; + ${window.location.protocol}//${window.location.host}/predict`; }; /** From 0259bf4acc08da4beb37407f16f305d21351496e Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 13 Jun 2023 19:53:00 -0300 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=8E=A8=20style(endpoints.py):=20renam?= =?UTF-8?q?e=20get=5Fload=20function=20to=20predict=5Fflow=20for=20better?= =?UTF-8?q?=20semantics=20The=20function=20name=20was=20changed=20to=20pre?= =?UTF-8?q?dict=5Fflow=20to=20better=20reflect=20the=20functionality=20of?= =?UTF-8?q?=20the=20endpoint.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/endpoints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index e99fb723f..35a14b822 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -39,7 +39,7 @@ def get_all(): @router.post("/predict", response_model=PredictResponse) -async def get_load( +async def predict_flow( predict_request: PredictRequest, flow: Flow = Depends(get_flow_from_token), ):