Skip to content

Commit

Permalink
Merge branch 'main' into feat/generategraphscript
Browse files Browse the repository at this point in the history
  • Loading branch information
ogabrielluiz authored Aug 7, 2024
2 parents e6b3861 + 8d7f948 commit 870933b
Show file tree
Hide file tree
Showing 68 changed files with 944 additions and 848 deletions.
12 changes: 12 additions & 0 deletions src/backend/base/langflow/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,15 @@ def parse_exception(exc):
if hasattr(exc, "body"):
return exc.body["message"]
return str(exc)


def get_suggestion_message(outdated_components: list[str]) -> str:
"""Get the suggestion message for the outdated components."""
count = len(outdated_components)
if count == 0:
return "The flow contains no outdated components."
elif count == 1:
return f"The flow contains 1 outdated component. We recommend updating the following component: {outdated_components[0]}."
else:
components = ", ".join(outdated_components)
return f"The flow contains {count} outdated components. We recommend updating the following components: {components}."
7 changes: 4 additions & 3 deletions src/backend/base/langflow/api/v1/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
)
from langflow.custom.custom_component.component import Component
from langflow.custom.utils import build_custom_component_template, get_instance_name
from langflow.exceptions.api import InvalidChatInputException
from langflow.exceptions.api import APIException, InvalidChatInputException
from langflow.graph.graph.base import Graph
from langflow.graph.schema import RunOutputs
from langflow.helpers.flow import get_flow_by_id_or_endpoint_name
Expand Down Expand Up @@ -260,7 +260,7 @@ async def simplified_run_flow(
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
else:
logger.exception(exc)
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)) from exc
raise APIException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=exc, flow=flow) from exc
except InvalidChatInputException as exc:
logger.error(exc)
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
Expand All @@ -275,7 +275,8 @@ async def simplified_run_flow(
runErrorMessage=str(exc),
),
)
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)) from exc
logger.exception(exc)
raise APIException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=exc, flow=flow) from exc


@router.post("/webhook/{flow_id_or_name}", response_model=dict, status_code=HTTPStatus.ACCEPTED)
Expand Down
8 changes: 5 additions & 3 deletions src/backend/base/langflow/components/prototypes/FlowTool.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from typing import Any, List, Optional


from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.base.tools.flow_tool import FlowTool
from langflow.field_typing import Tool
from langflow.graph.graph.base import Graph
from langflow.helpers.flow import get_flow_inputs
from langflow.io import BoolInput, DropdownInput, StrInput, Output
from langflow.io import BoolInput, DropdownInput, Output, StrInput
from langflow.schema import Data
from langflow.schema.dotdict import dotdict

Expand Down Expand Up @@ -73,7 +72,10 @@ def update_build_config(self, build_config: dotdict, field_value: Any, field_nam

def build_tool(self) -> Tool:
FlowTool.update_forward_refs()
flow_data = self.get_flow(self.flow_name)
if "flow_name" not in self._attributes or not self._attributes["flow_name"]:
raise ValueError("Flow name is required")
flow_name = self._attributes["flow_name"]
flow_data = self.get_flow(flow_name)
if not flow_data:
raise ValueError("Flow not found.")
graph = Graph.from_payload(flow_data.data["data"])
Expand Down
6 changes: 5 additions & 1 deletion src/backend/base/langflow/components/prototypes/RunFlow.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,12 @@ def update_build_config(self, build_config: dotdict, field_value: Any, field_nam
]

async def generate_results(self) -> List[Data]:
if "flow_name" not in self._attributes or not self._attributes["flow_name"]:
raise ValueError("Flow name is required")
flow_name = self._attributes["flow_name"]

results: List[Optional[RunOutputs]] = await self.run_flow(
inputs={"input_value": self.input_value}, flow_name=self.flow_name, tweaks=self.tweaks
inputs={"input_value": self.input_value}, flow_name=flow_name, tweaks=self.tweaks
)
if isinstance(results, list):
data = []
Expand Down
4 changes: 2 additions & 2 deletions src/backend/base/langflow/components/prototypes/SubFlow.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ async def generate_results(self) -> List[Data]:
if node not in tweaks.keys():
tweaks[node] = {}
tweaks[node][name] = self._attributes[field]

flow_name = self._attributes.get("flow_name")
run_outputs = await self.run_flow(
tweaks=tweaks,
flow_name=self.flow_name,
flow_name=flow_name,
output_type="all",
)
data: list[Data] = []
Expand Down
32 changes: 32 additions & 0 deletions src/backend/base/langflow/exceptions/api.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,34 @@
from fastapi import HTTPException
from langflow.api.utils import get_suggestion_message
from langflow.services.database.models.flow.model import Flow
from langflow.services.database.models.flow.utils import get_outdated_components
from pydantic import BaseModel


class InvalidChatInputException(Exception):
pass


# create a pidantic documentation for this class
class ExceptionBody(BaseModel):
message: str | list[str]
traceback: str | list[str] | None = None
description: str | list[str] | None = None
code: str | None = None
suggestion: str | list[str] | None = None


class APIException(HTTPException):
def __init__(self, exception: Exception, flow: Flow | None = None, status_code: int = 500):
body = self.build_exception_body(exception, flow)
super().__init__(status_code=status_code, detail=body.model_dump_json())

@staticmethod
def build_exception_body(exc: str | list[str] | Exception, flow: Flow | None) -> ExceptionBody:
body = {"message": str(exc)}
if flow:
outdated_components = get_outdated_components(flow)
if outdated_components:
body["suggestion"] = get_suggestion_message(outdated_components)
excep = ExceptionBody(**body)
return excep
6 changes: 5 additions & 1 deletion src/backend/base/langflow/graph/graph/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ def add_component_edge(self, source_id: str, output_input_tuple: Tuple[str, str]
if not isinstance(target_vertex, ComponentVertex):
raise ValueError(f"Target vertex {target_id} is not a component vertex.")
output_name, input_name = output_input_tuple
if source_vertex._custom_component is None:
raise ValueError(f"Source vertex {source_id} does not have a custom component.")
if target_vertex._custom_component is None:
raise ValueError(f"Target vertex {target_id} does not have a custom component.")
edge_data: EdgeData = {
"source": source_id,
"target": target_id,
Expand Down Expand Up @@ -1434,7 +1438,7 @@ def _get_vertex_class(self, node_type: str, node_base_type: str, node_id: str) -

if node_type in lazy_load_vertex_dict.VERTEX_TYPE_MAP:
return lazy_load_vertex_dict.VERTEX_TYPE_MAP[node_type]
raise ValueError(f"Vertex type {node_type} not found")
return Vertex

def _build_vertices(self) -> List["Vertex"]:
"""Builds the vertices of the graph."""
Expand Down
2 changes: 2 additions & 0 deletions src/backend/base/langflow/graph/vertex/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,8 @@ def _build_params(self):
params[field_name] = [unescape_string(v) for v in val]
elif isinstance(val, str):
params[field_name] = unescape_string(val)
elif isinstance(val, Data):
params[field_name] = unescape_string(val.get_text())
elif field.get("type") == "bool" and val is not None:
if isinstance(val, bool):
params[field_name] = val
Expand Down
2 changes: 2 additions & 0 deletions src/backend/base/langflow/graph/vertex/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ def extract_messages_from_artifacts(self, artifacts: Dict[str, Any]) -> List[dic
) and not isinstance(artifact, Message):
continue
message_dict = artifact if isinstance(artifact, dict) else artifact.model_dump()
if not message_dict.get("text"):
continue
try:
messages.append(
ChatOutputResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@
"show": true,
"title_case": false,
"type": "code",
"value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(\n self,\n ) -> Message:\n prompt = await Message.from_template_and_variables(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"\n This function is called after the code validation is done.\n \"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n"
"value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(\n self,\n ) -> Message:\n prompt = await Message.from_template_and_variables(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"\n This function is called after the code validation is done.\n \"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n"
},
"template": {
"advanced": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@
"show": true,
"title_case": false,
"type": "code",
"value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(\n self,\n ) -> Message:\n prompt = await Message.from_template_and_variables(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"\n This function is called after the code validation is done.\n \"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n"
"value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(\n self,\n ) -> Message:\n prompt = await Message.from_template_and_variables(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"\n This function is called after the code validation is done.\n \"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n"
},
"instructions": {
"advanced": false,
Expand Down
Loading

0 comments on commit 870933b

Please sign in to comment.