Merge dev into shortcuts_settings

This commit is contained in:
igorrCarvalho 2024-05-03 20:01:22 -03:00
commit b698ca3fd2
19 changed files with 108 additions and 86 deletions

View file

@ -205,17 +205,12 @@ async def build_and_cache_graph_from_db(
flow_id: str,
session: Session,
chat_service: "ChatService",
graph: Optional[Graph] = None,
):
"""Build and cache the graph."""
flow: Optional[Flow] = session.get(Flow, flow_id)
if not flow or not flow.data:
raise ValueError("Invalid flow ID")
other_graph = Graph.from_payload(flow.data, flow_id)
if graph is None:
graph = other_graph
else:
graph = graph.update(other_graph)
graph = Graph.from_payload(flow.data, flow_id)
await chat_service.set_cache(flow_id, graph)
return graph

View file

@ -79,13 +79,8 @@ async def retrieve_vertices_order(
"""
try:
# First, we need to check if the flow_id is in the cache
graph = None
if not data:
if cache := await chat_service.get_cache(flow_id):
graph = cache.get("result")
graph = await build_and_cache_graph_from_db(
flow_id=flow_id, session=session, chat_service=chat_service, graph=graph
)
graph = await build_and_cache_graph_from_db(flow_id=flow_id, session=session, chat_service=chat_service)
else:
graph = await build_and_cache_graph_from_data(
flow_id=flow_id, graph_data=data.model_dump(), chat_service=chat_service

View file

@ -93,14 +93,14 @@ class APIRequest(CustomComponent):
self,
method: str,
urls: List[str],
_headers: Optional[Record] = None,
headers: Optional[Record] = None,
body: Optional[Record] = None,
timeout: int = 5,
) -> List[Record]:
if _headers is None:
headers = {}
if headers is None:
headers_dict = {}
else:
headers = _headers.data
headers_dict = headers.data
bodies = []
if body:
@ -114,7 +114,7 @@ class APIRequest(CustomComponent):
bodies += [None] * (len(urls) - len(bodies)) # type: ignore
async with httpx.AsyncClient() as client:
results = await asyncio.gather(
*[self.make_request(client, method, u, headers, rec, timeout) for u, rec in zip(urls, bodies)]
*[self.make_request(client, method, u, headers_dict, rec, timeout) for u, rec in zip(urls, bodies)]
)
self.status = results
return results

View file

@ -445,9 +445,16 @@ class Graph:
vertex = self.get_vertex(vertex_id)
vertex.set_state(state)
def mark_branch(self, vertex_id: str, state: str):
def mark_branch(self, vertex_id: str, state: str, visited: Optional[set] = None):
"""Marks a branch of the graph."""
if visited is None:
visited = set()
visited.add(vertex_id)
if vertex_id in visited:
return
self.mark_vertex(vertex_id, state)
for child_id in self.parent_child_map[vertex_id]:
self.mark_branch(child_id, state)

View file

@ -59,7 +59,7 @@ def get_id_from_search_string(search_string: str) -> Optional[str]:
Returns:
Optional[str]: The extracted ID, or None if no ID is found.
"""
possible_id = search_string
possible_id: Optional[str] = search_string
if "www.langflow.store/store/" in search_string:
possible_id = search_string.split("/")[-1]

View file

@ -5,6 +5,7 @@ import useAlertStore from "../../stores/alertStore";
import { useGlobalVariablesStore } from "../../stores/globalVariables";
import { useTypesStore } from "../../stores/typesStore";
import { ResponseErrorDetailAPI } from "../../types/api";
import { sortByName } from "../../utils/utils";
import ForwardedIconComponent from "../genericIconComponent";
import InputComponent from "../inputComponent";
import { Button } from "../ui/button";
@ -23,14 +24,19 @@ export default function AddNewVariableButton({ children }): JSX.Element {
const setErrorData = useAlertStore((state) => state.setErrorData);
const componentFields = useTypesStore((state) => state.ComponentFields);
const unavaliableFields = new Set(
Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields))
Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields)),
);
const availableFields = Array.from(componentFields).filter(
(field) => !unavaliableFields.has(field)
);
const availableFields = () => {
const fields = Array.from(componentFields).filter(
(field) => !unavaliableFields.has(field),
);
return sortByName(fields);
};
const addGlobalVariable = useGlobalVariablesStore(
(state) => state.addGlobalVariable
(state) => state.addGlobalVariable,
);
function handleSaveVariable() {
@ -113,7 +119,7 @@ export default function AddNewVariableButton({ children }): JSX.Element {
setSelectedOptions={(value) => setFields(value)}
selectedOptions={fields}
password={false}
options={availableFields}
options={availableFields()}
placeholder="Choose a field for the variable..."
id={"apply-to-fields"}
></InputComponent>

View file

@ -11,7 +11,6 @@ import cloneFLowWithParent from "../../utils/storeUtils";
import { cn, convertTestName } from "../../utils/utils";
import IconComponent from "../genericIconComponent";
import ShadTooltip from "../shadTooltipComponent";
import { Badge } from "../ui/badge";
import { Button } from "../ui/button";
import {
Card,
@ -170,8 +169,9 @@ export default function CollectionCardComponent({
<>
<Card
data-testid={`card-${convertTestName(data.name)}`}
//TODO check color schema
className={cn(
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:shadow-md",
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:bg-muted/50 hover:shadow-md hover:dark:bg-[#ffffff10]",
disabled ? "pointer-events-none opacity-50" : "",
onClick ? "cursor-pointer" : "",
)}
@ -239,6 +239,7 @@ export default function CollectionCardComponent({
{onDelete && data?.metadata === undefined && (
<button
className="z-50"
onClick={(e) => {
e.stopPropagation();
setOpenDelete(true);
@ -269,7 +270,7 @@ export default function CollectionCardComponent({
</span>
)}
<div className="flex w-full flex-1 flex-wrap gap-2">
{data.tags &&
{/* {data.tags &&
data.tags.length > 0 &&
data.tags.map((tag, index) => (
<Badge
@ -280,7 +281,7 @@ export default function CollectionCardComponent({
>
{tag.name}
</Badge>
))}
))} */}
</div>
</div>
@ -291,8 +292,8 @@ export default function CollectionCardComponent({
</div>
<CardFooter>
<div className="flex w-full items-center justify-between gap-2">
<div className="flex w-full flex-wrap items-end justify-between gap-2">
<div className="z-50 flex w-full items-center justify-between gap-2">
<div className="flex w-full flex-wrap items-end justify-end gap-2">
{playground && data?.metadata !== undefined ? (
<Button
disabled={loadingPlayground}
@ -300,7 +301,7 @@ export default function CollectionCardComponent({
tabIndex={-1}
variant="outline"
size="sm"
className="gap-2 whitespace-nowrap"
className="z-50 gap-2 whitespace-nowrap"
data-testid={"playground-flow-button-" + data.id}
onClick={(e) => {
e.preventDefault();
@ -436,15 +437,14 @@ export default function CollectionCardComponent({
</ShadTooltip>
</div>
)}
{button && button}
{playground && data?.metadata === undefined && (
<Button
disabled={loadingPlayground}
key={data.id}
tabIndex={-1}
variant="outline"
variant="primary"
size="sm"
className="gap-2 whitespace-nowrap"
className="gap-2 whitespace-nowrap bg-muted"
data-testid={"playground-flow-button-" + data.id}
onClick={(e) => {
e.preventDefault();

View file

@ -54,7 +54,7 @@ export default function FlowToolbar(): JSX.Element {
"relative inline-flex h-full w-full items-center justify-center gap-[4px] bg-muted px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-background hover:bg-hover ",
!hasApiKey || !validApiKey || !hasStore
? " button-disable text-muted-foreground "
: ""
: "",
)}
>
<ForwardedIconComponent
@ -63,14 +63,14 @@ export default function FlowToolbar(): JSX.Element {
"-m-0.5 -ml-1 h-6 w-6",
!hasApiKey || !validApiKey || !hasStore
? "extra-side-bar-save-disable"
: ""
: "",
)}
/>
Share
</button>
</ShareModal>
),
[hasApiKey, validApiKey, currentFlow, hasStore]
[hasApiKey, validApiKey, currentFlow, hasStore],
);
return (
@ -112,7 +112,7 @@ export default function FlowToolbar(): JSX.Element {
"message-button-icon h-5 w-5 fill-muted-foreground stroke-muted-foreground transition-all"
}
/>
Run
Playground
</div>
)}
</div>
@ -128,7 +128,7 @@ export default function FlowToolbar(): JSX.Element {
>
<div
className={classNames(
"relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-hover"
"relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-foreground transition-all duration-150 ease-in-out hover:bg-hover",
)}
>
<ForwardedIconComponent

View file

@ -69,7 +69,7 @@ function CsvOutputComponent({
if (file) {
const { rowData: data, colDefs: columns } = convertCSVToData(
file,
separator
separator,
);
setRowData(data);
setColDefs(columns);
@ -114,14 +114,14 @@ function CsvOutputComponent({
(params: any) => {
updateRowHeight(params);
},
[updateRowHeight]
[updateRowHeight],
);
const onGridSizeChanged = useCallback(
(params: any) => {
updateRowHeight(params);
},
[updateRowHeight]
[updateRowHeight],
);
return (
@ -167,6 +167,7 @@ function CsvOutputComponent({
onFirstDataRendered={onFirstDataRendered}
onGridSizeChanged={onGridSizeChanged}
scrollbarWidth={8}
overlayNoRowsTemplate="No data available"
/>
</div>
)}

View file

@ -5,6 +5,7 @@ import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react";
import { useDarkStore } from "../../stores/darkStore";
import "../../style/ag-theme-shadcn.css"; // Custom CSS applied to the grid
import { cn } from "../../utils/utils";
import { Card, CardContent } from "../ui/card";
const TableComponent = forwardRef<
ElementRef<typeof AgGridReact>,
@ -17,10 +18,18 @@ const TableComponent = forwardRef<
<div
className={cn(
dark ? "ag-theme-quartz-dark" : "ag-theme-quartz",
"ag-theme-shadcn flex h-full flex-col"
"ag-theme-shadcn flex h-full flex-col",
)} // applying the grid theme
>
<AgGridReact ref={ref} {...props} />
<Card x-chunk="dashboard-04-chunk-2" className="pt-4">
<CardContent>
<AgGridReact
overlayNoRowsTemplate="No data available"
ref={ref}
{...props}
/>
</CardContent>
</Card>
</div>
</div>
);

View file

@ -117,7 +117,7 @@ export default function IOModal({
return (
<BaseModal
size={selectedTab === 0 ? "large-thin" : "md-thin"}
size={selectedTab === 0 ? "sm-thin" : "md-thin"}
open={open}
setOpen={setOpen}
disable={disable}

View file

@ -79,6 +79,7 @@ interface BaseModalProps {
| "small-h-full"
| "medium-h-full"
| "md-thin"
| "sm-thin"
| "smaller-h-full";
disable?: boolean;
@ -154,6 +155,11 @@ function BaseModal({
height = "h-[70vh]";
break;
case "sm-thin":
minWidth = "min-w-[65vw]";
height = "h-[70vh]";
break;
case "large-h-full":
minWidth = "min-w-[80vw]";
break;

View file

@ -15,13 +15,13 @@ import { cn } from "../../../../utils/utils";
export default function GlobalVariablesPage() {
const globalVariablesEntries = useGlobalVariablesStore(
(state) => state.globalVariablesEntries
(state) => state.globalVariablesEntries,
);
const removeGlobalVariable = useGlobalVariablesStore(
(state) => state.removeGlobalVariable
(state) => state.removeGlobalVariable,
);
const globalVariables = useGlobalVariablesStore(
(state) => state.globalVariables
(state) => state.globalVariables,
);
const setErrorData = useAlertStore((state) => state.setErrorData);
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
@ -154,7 +154,7 @@ export default function GlobalVariablesPage() {
<IconComponent
name="Trash2"
className={cn(
"h-5 w-5 text-destructive group-disabled:text-primary"
"h-5 w-5 text-destructive group-disabled:text-primary",
)}
/>
</Button>
@ -174,6 +174,8 @@ export default function GlobalVariablesPage() {
}}
rowSelection="multiple"
suppressRowClickSelection={true}
domLayout="autoHeight"
pagination={false}
columnDefs={colDefs}
rowData={rowData}
/>

View file

@ -43,10 +43,6 @@ export default function ShortcutsPage() {
const combinationToEdit = shortcuts.filter((s) => s.name === selectedRows[0]);
const [open, setOpen] = useState(false);
const unavaliableShortcuts = useShortcutsStore(
(state) => state.unavailableShortcuts
);
useEffect(() => {
if (localStorage.getItem("langflow-shortcuts")) {
const savedShortcuts = localStorage.getItem("langflow-shortcuts");
@ -109,7 +105,7 @@ export default function ShortcutsPage() {
<TableComponent
onSelectionChanged={(event: SelectionChangedEvent) => {
setSelectedRows(
event.api.getSelectedRows().map((row) => row.name)
event.api.getSelectedRows().map((row) => row.name),
);
}}
suppressRowClickSelection={true}

View file

@ -9,7 +9,9 @@
body {
@apply bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
font-feature-settings:
"rlig" 1,
"calt" 1;
}
}
@ -1018,6 +1020,9 @@
.langflow-chat-span {
@apply text-lg text-foreground;
}
.card-filter {
@apply bg-background bg-fixed opacity-20;
}
.langflow-chat-desc {
@apply w-2/4 rounded-md border border-border bg-muted px-6 py-8;
}

View file

@ -16,7 +16,7 @@ type BuildVerticesParams = {
onBuildUpdate?: (
data: VertexBuildTypeAPI,
status: BuildStatus,
buildId: string
buildId: string,
) => void; // Replace any with the actual type if it's not any
onBuildComplete?: (allNodesValid: boolean) => void;
onBuildError?: (title, list, idList: VertexLayerElementType[]) => void;
@ -53,7 +53,7 @@ export async function updateVerticesOrder(
startNodeId?: string | null,
stopNodeId?: string | null,
nodes?: Node[],
edges?: Edge[]
edges?: Edge[],
): Promise<{
verticesLayers: VertexLayerElementType[][];
verticesIds: string[];
@ -69,7 +69,7 @@ export async function updateVerticesOrder(
startNodeId,
stopNodeId,
nodes,
edges
edges,
);
} catch (error: any) {
setErrorData({
@ -116,33 +116,29 @@ export async function buildVertices({
nodes,
edges,
}: BuildVerticesParams) {
let verticesBuild = useFlowStore.getState().verticesBuild;
// if startNodeId and stopNodeId are provided
// something is wrong
if (startNodeId && stopNodeId) {
return;
}
let verticesOrderResponse = await updateVerticesOrder(
flowId,
startNodeId,
stopNodeId,
nodes,
edges,
);
if (onValidateNodes) {
try {
onValidateNodes(verticesOrderResponse.verticesToRun);
} catch (e) {
useFlowStore.getState().setIsBuilding(false);
if (!verticesBuild || startNodeId || stopNodeId) {
let verticesOrderResponse = await updateVerticesOrder(
flowId,
startNodeId,
stopNodeId,
nodes,
edges
);
if (onValidateNodes) {
try {
onValidateNodes(verticesOrderResponse.verticesToRun);
} catch (e) {
useFlowStore.getState().setIsBuilding(false);
return;
}
return;
}
if (onGetOrderSuccess) onGetOrderSuccess();
verticesBuild = useFlowStore.getState().verticesBuild;
}
if (onGetOrderSuccess) onGetOrderSuccess();
let verticesBuild = useFlowStore.getState().verticesBuild;
const verticesIds = verticesBuild?.verticesIds!;
const verticesLayers = verticesBuild?.verticesLayers!;
@ -192,14 +188,14 @@ export async function buildVertices({
onBuildUpdate(
getInactiveVertexData(element.id),
BuildStatus.INACTIVE,
runId
runId,
);
}
if (element.reference) {
onBuildUpdate(
getInactiveVertexData(element.reference),
BuildStatus.INACTIVE,
runId
runId,
);
}
buildResults.push(false);
@ -224,7 +220,7 @@ export async function buildVertices({
if (stop) {
return;
}
})
}),
);
// Once the current layer is built, move to the next layer
currentLayerIndex += 1;
@ -267,7 +263,7 @@ async function buildVertex({
onBuildError!(
"Error Building Component",
[buildData.params],
verticesIds.map((id) => ({ id }))
verticesIds.map((id) => ({ id })),
);
stopBuild();
}
@ -278,7 +274,7 @@ async function buildVertex({
onBuildError!(
"Error Building Component",
[(error as AxiosError<any>).response?.data?.detail ?? "Unknown Error"],
verticesIds.map((id) => ({ id }))
verticesIds.map((id) => ({ id })),
);
stopBuild();
}

View file

@ -719,3 +719,7 @@ export function freezeObject(obj: any) {
export function convertTestName(name: string): string {
return name.replace(/ /g, "-").toLowerCase();
}
export function sortByName(stringList: string[]): string[] {
return stringList.sort((a, b) => a.localeCompare(b));
}

View file

@ -63,7 +63,7 @@ test("should interact with global variables", async ({ page }) => {
.nth(0)
.click();
await page.getByTestId("icon-Trash2").click();
await page.getByText("No Rows To Show").isVisible();
await page.getByText("No data available").isVisible();
});
test("should see shortcuts", async ({ page }) => {

View file

@ -393,13 +393,13 @@ def test_various_prompts(client, prompt, expected_input_variables):
def test_get_vertices_flow_not_found(client, logged_in_headers):
response = client.get("/api/v1/build/nonexistent_id/vertices", headers=logged_in_headers)
response = client.post("/api/v1/build/nonexistent_id/vertices", headers=logged_in_headers)
assert response.status_code == 500 # Or whatever status code you've set for invalid ID
def test_get_vertices(client, added_flow_with_prompt_and_history, logged_in_headers):
flow_id = added_flow_with_prompt_and_history["id"]
response = client.get(f"/api/v1/build/{flow_id}/vertices", headers=logged_in_headers)
response = client.post(f"/api/v1/build/{flow_id}/vertices", headers=logged_in_headers)
assert response.status_code == 200
assert "ids" in response.json()
# The response should contain the list in this order