Skip to content

Commit 05a8da8

Browse files
committed
feat: optimize dropdown filtering and output resolution
misc: remove commented out code feat: add refresh button and sort flows by updated_at date from most to least recent ruff (flow.py imports) improve fn contracts in runflow and improve flow id retrieval logic based on graph exec context add dynamic outputs and optimize db lookups add flow cache and db query for getting a single flow by id or name cache run outputs and add refresh context to build config misc misc use ids for flow retrieval misc fix missing flow_id bug add unit and integration tests add input field flag to persist hidden fields at runtime move unit tests and change input and output display names chore: update component index fix: fix tool mode when flow has multiple inputs by dynamically creating resolvers chore: update component index ruff (run_flow and tests) add resolvers to outputs map for non tool mode runtime fix tests (current flow excluded in db fetch) mypy (helpers/flow.py) chore: update component index remove unused code and clean up comments fix: persist user messages in chat-based flows via session injection chore: update component index empty string fallback for sessionid in chat.py chore: update component index chore: update component index cache invalidation with timestamps misc add cache invalidation chore: update component index chore: update comp idx ruff (run_flow.py) change session_id input type to MessageTextInput chore: update component index chore: update component index chore: update component index chore: update component index sync starter projects with main chore: update component index
1 parent 348b1b8 commit 05a8da8

26 files changed

Lines changed: 3414 additions & 197 deletions

File tree

src/backend/base/langflow/helpers/flow.py

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
from fastapi import HTTPException
77
from lfx.log.logger import logger
88
from pydantic.v1 import BaseModel, Field, create_model
9-
from sqlmodel import select
9+
from sqlalchemy.orm import aliased
10+
from sqlmodel import asc, desc, select
1011

1112
from langflow.schema.schema import INPUT_FIELD_NAME
1213
from langflow.services.database.models.flow.model import Flow, FlowRead
@@ -19,14 +20,17 @@
1920
from lfx.graph.schema import RunOutputs
2021
from lfx.graph.vertex.base import Vertex
2122

22-
from langflow.schema.data import Data
23+
from langflow.schema.data import Data
2324

2425
INPUT_TYPE_MAP = {
2526
"ChatInput": {"type_hint": "Optional[str]", "default": '""'},
2627
"TextInput": {"type_hint": "Optional[str]", "default": '""'},
2728
"JSONInput": {"type_hint": "Optional[dict]", "default": "{}"},
2829
}
29-
30+
SORT_DISPATCHER = {
31+
"asc": asc,
32+
"desc": desc,
33+
}
3034

3135
async def list_flows(*, user_id: str | None = None) -> list[Data]:
3236
if not user_id:
@@ -44,6 +48,122 @@ async def list_flows(*, user_id: str | None = None) -> list[Data]:
4448
raise ValueError(msg) from e
4549

4650

51+
async def list_flows_by_flow_folder(
52+
*,
53+
user_id: str | None = None,
54+
flow_id: str | None = None,
55+
order_params: dict | None = {"column": "updated_at", "direction": "desc"} # noqa: B006
56+
) -> list[Data]:
57+
if not user_id:
58+
msg = "Session is invalid"
59+
raise ValueError(msg)
60+
if not flow_id:
61+
msg = "Flow ID is required"
62+
raise ValueError(msg)
63+
try:
64+
async with session_scope() as session:
65+
uuid_user_id = UUID(user_id) if isinstance(user_id, str) else user_id
66+
uuid_flow_id = UUID(flow_id) if isinstance(flow_id, str) else flow_id
67+
# get all flows belonging to the specified user
68+
# and inside the same folder as the specified flow
69+
flow_ = aliased(Flow) # flow table alias, used to retrieve the folder
70+
stmt = (select(Flow.id, Flow.name, Flow.updated_at)
71+
.join(flow_, Flow.folder_id == flow_.folder_id)
72+
.where(flow_.id == uuid_flow_id)
73+
.where(flow_.user_id == uuid_user_id)
74+
.where(Flow.user_id == uuid_user_id)
75+
.where(Flow.id != uuid_flow_id)
76+
)
77+
# sort flows by the specified column and direction
78+
if order_params is not None:
79+
sort_col = getattr(Flow, order_params.get("column", "updated_at"), Flow.updated_at)
80+
sort_dir = SORT_DISPATCHER.get(order_params.get("direction", "desc"), desc)
81+
stmt = stmt.order_by(sort_dir(sort_col))
82+
83+
flows = (await session.exec(stmt)).all()
84+
return [Data(data=dict(flow._mapping)) for flow in flows] # noqa: SLF001
85+
except Exception as e:
86+
msg = f"Error listing flows: {e}"
87+
raise ValueError(msg) from e
88+
89+
90+
async def list_flows_by_folder_id(
91+
*,
92+
user_id: str | None = None,
93+
folder_id: str | None = None,
94+
order_params: dict | None = {"column": "updated_at", "direction": "desc"} # noqa: B006
95+
) -> list[Data]:
96+
if not user_id:
97+
msg = "Session is invalid"
98+
raise ValueError(msg)
99+
if not folder_id:
100+
msg = "Folder ID is required"
101+
raise ValueError(msg)
102+
103+
try:
104+
async with session_scope() as session:
105+
uuid_user_id = UUID(user_id) if isinstance(user_id, str) else user_id
106+
uuid_folder_id = UUID(folder_id) if isinstance(folder_id, str) else folder_id
107+
stmt = (select(Flow.id, Flow.name, Flow.updated_at)
108+
.where(Flow.user_id == uuid_user_id)
109+
.where(Flow.folder_id == uuid_folder_id)
110+
)
111+
if order_params is not None:
112+
sort_col = getattr(Flow, order_params.get("column", "updated_at"), Flow.updated_at)
113+
sort_dir = SORT_DISPATCHER.get(order_params.get("direction", "desc"), desc)
114+
stmt = stmt.order_by(sort_dir(sort_col))
115+
116+
flows = (await session.exec(stmt)).all()
117+
return [Data(data=dict(flow._mapping)) for flow in flows] # noqa: SLF001
118+
except Exception as e:
119+
msg = f"Error listing flows: {e}"
120+
raise ValueError(msg) from e
121+
122+
123+
async def get_flow_by_id_or_name(
124+
*,
125+
user_id: str | None = None,
126+
flow_id: str | None = None,
127+
flow_name: str | None = None,
128+
) -> Data | None:
129+
if not user_id:
130+
msg = "Session is invalid"
131+
raise ValueError(msg)
132+
if not (flow_id or flow_name):
133+
msg = "Flow ID or Flow Name is required"
134+
raise ValueError(msg)
135+
136+
# set user provided flow id or flow name.
137+
# if both are provided, flow_id is used.
138+
attr, val = None, None
139+
if flow_name:
140+
attr = "name"
141+
val = flow_name
142+
if flow_id:
143+
attr = "id"
144+
val = flow_id
145+
if not (attr and val):
146+
msg = "Flow id or Name is required"
147+
raise ValueError(msg)
148+
try:
149+
async with session_scope() as session:
150+
uuid_user_id = UUID(user_id) if isinstance(user_id, str) else user_id # type: ignore[assignment]
151+
uuid_flow_id_or_name = val # type: ignore[assignment]
152+
if isinstance(val, str) and attr == "id":
153+
uuid_flow_id_or_name = UUID(val) # type: ignore[assignment]
154+
stmt = (
155+
select(Flow)
156+
.where(Flow.user_id == uuid_user_id)
157+
.where(getattr(Flow, attr) == uuid_flow_id_or_name)
158+
)
159+
flow = (await session.exec(stmt)).first()
160+
return flow.to_data() if flow else None
161+
162+
except Exception as e:
163+
msg = f"Error getting flow by id: {e}"
164+
raise ValueError(msg) from e
165+
166+
47167
async def load_flow(
48168
user_id: str, flow_id: str | None = None, flow_name: str | None = None, tweaks: dict | None = None
49169
) -> Graph:

src/backend/base/langflow/services/database/models/flow/model.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ def to_data(self):
203203
"name": serialized.pop("name"),
204204
"description": serialized.pop("description"),
205205
"updated_at": serialized.pop("updated_at"),
206+
"folder_id": serialized.pop("folder_id"),
206207
}
207208
return Data(data=data)
208209

src/backend/tests/integration/base/__init__.py

Whitespace-only changes.

src/backend/tests/integration/base/tools/__init__.py

Whitespace-only changes.

src/backend/tests/integration/base/tools/run_flow/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)