forked from langflow-ai/langflow
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: update all outdated components at once (langflow-ai#4763)
* update components to update * Added update all components * Update the logic for updating all components * Added dismiss functionality * Removed node from components to update when updated * ✨ (list/index.tsx): add data-testid attribute to list card component for testing purposes ✨ (reactflow): create edges to connect different nodes for data flow in the chatbot application. 📝 (Prompt): Update prompt template with dynamic variables for better customization and flexibility. 📝 (code): update code in ChatInput component to import necessary modules and classes for chat inputs handling ♻️ (code): refactor code in ChatInput component to improve readability and maintainability by organizing imports and defining class attributes clearly 📝 (input.py): Update input fields display names and information for better clarity and understanding 📝 (input.py): Update file input field to support multiple file types and be a list of files 📝 (input.py): Update sender options to be more descriptive as "Machine" and "User" instead of constants 📝 (input.py): Update sender_name input field information to clarify it is the name of the sender 📝 (input.py): Update session_id input field information to explain its purpose and usage 📝 (input.py): Update files input field information to clarify it is for files to be sent with the message 📝 (input.py): Update input_value input field information to clarify it is the text message to be passed as input 📝 (input.py): Update should_store_message input field information to explain its purpose of storing messages in history 📝 (input.py): Update message_response method to handle storing messages based on conditions and updating status 📝 (metadata): Update metadata fields in ChatInput component for better clarity and consistency 📝 (OpenAIModel): Add OpenAI API Key field to the template for configuring the OpenAI model usage 📝 (LCModelComponent): Update OpenAIModelComponent inputs and add support for new features and configurations to enhance text generation capabilities. 📝 (file.py): Update comments and documentation for better clarity and understanding of the code ♻️ (file.py): Refactor code to improve readability and maintainability by restructuring the logic and removing unnecessary code blocks 📝 (schema.json): Update schema for the Output of the model to enable JSON mode and improve functionality 📝 (ChatOutput): Display a chat message in the Playground for better user interaction and experience 📝 (ChatOutput): Update ChatOutput class inputs and outputs structure for better organization and readability. ✨ (frontend): Add a new file 'outdated_flow.json' to store outdated flow data for frontend tests. ✨ (outdated-actions.spec.ts): add test to ensure user can update outdated components in the application * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes * fix tests --------- Co-authored-by: cristhianzl <[email protected]> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Gabriel Luiz Freitas Almeida <[email protected]>
- Loading branch information
1 parent
9681faa
commit 329fd60
Showing
7 changed files
with
289 additions
and
7 deletions.
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
src/frontend/src/CustomNodes/hooks/use-update-all-nodes.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { cloneDeep } from "lodash"; | ||
import { useCallback } from "react"; | ||
import { APIClassType } from "../../types/api"; | ||
import { NodeType } from "../../types/flow"; | ||
|
||
export type UpdateNodesType = { | ||
nodeId: string; | ||
newNode: APIClassType; | ||
code: string; | ||
name: string; | ||
type?: string; | ||
}; | ||
|
||
const useUpdateAllNodes = ( | ||
setNodes: (callback: (oldNodes: NodeType[]) => NodeType[]) => void, | ||
updateNodeInternals: (nodeId: string) => void, | ||
) => { | ||
const updateAllNodes = useCallback( | ||
(updates: UpdateNodesType[]) => { | ||
setNodes((oldNodes) => { | ||
const newNodes = cloneDeep(oldNodes); | ||
|
||
updates.forEach(({ nodeId, newNode, code, name, type }) => { | ||
const nodeIndex = newNodes.findIndex((n) => n.id === nodeId); | ||
if (nodeIndex === -1) return; | ||
|
||
const updatedNode = newNodes[nodeIndex]; | ||
updatedNode.data = { | ||
...updatedNode.data, | ||
node: { | ||
...newNode, | ||
description: | ||
newNode.description ?? updatedNode.data.node?.description, | ||
display_name: | ||
newNode.display_name ?? updatedNode.data.node?.display_name, | ||
edited: false, | ||
}, | ||
}; | ||
|
||
if (type) { | ||
updatedNode.data.type = type; | ||
} | ||
|
||
updatedNode.data.node!.template[name].value = code; | ||
updateNodeInternals(nodeId); | ||
}); | ||
|
||
return newNodes; | ||
}); | ||
}, | ||
[setNodes, updateNodeInternals], | ||
); | ||
|
||
return updateAllNodes; | ||
}; | ||
|
||
export default useUpdateAllNodes; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
src/frontend/src/pages/FlowPage/components/UpdateAllComponents/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import { ForwardedIconComponent } from "@/components/genericIconComponent"; | ||
import { Button } from "@/components/ui/button"; | ||
import { usePostValidateComponentCode } from "@/controllers/API/queries/nodes/use-post-validate-component-code"; | ||
import { processNodeAdvancedFields } from "@/CustomNodes/helpers/process-node-advanced-fields"; | ||
import useUpdateAllNodes, { | ||
UpdateNodesType, | ||
} from "@/CustomNodes/hooks/use-update-all-nodes"; | ||
import useAlertStore from "@/stores/alertStore"; | ||
import useFlowsManagerStore from "@/stores/flowsManagerStore"; | ||
import useFlowStore from "@/stores/flowStore"; | ||
import { useTypesStore } from "@/stores/typesStore"; | ||
import { cn } from "@/utils/utils"; | ||
import { useState } from "react"; | ||
import { useUpdateNodeInternals } from "reactflow"; | ||
|
||
export default function UpdateAllComponents() { | ||
const { componentsToUpdate, nodes, edges, setNodes } = useFlowStore(); | ||
const updateNodeInternals = useUpdateNodeInternals(); | ||
const templates = useTypesStore((state) => state.templates); | ||
const setErrorData = useAlertStore((state) => state.setErrorData); | ||
const [loadingUpdate, setLoadingUpdate] = useState(false); | ||
|
||
const { mutateAsync: validateComponentCode } = usePostValidateComponentCode(); | ||
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); | ||
|
||
const updateAllNodes = useUpdateAllNodes(setNodes, updateNodeInternals); | ||
|
||
const [dismissed, setDismissed] = useState(false); | ||
|
||
const handleUpdateAllComponents = () => { | ||
setLoadingUpdate(true); | ||
takeSnapshot(); | ||
|
||
let updatedCount = 0; | ||
const updates: UpdateNodesType[] = []; | ||
|
||
const updatePromises = componentsToUpdate.map((nodeId) => { | ||
const node = nodes.find((n) => n.id === nodeId); | ||
if (!node) return Promise.resolve(); | ||
|
||
const thisNodeTemplate = templates[node.data.type]?.template; | ||
if (!thisNodeTemplate?.code) return Promise.resolve(); | ||
|
||
const currentCode = thisNodeTemplate.code.value; | ||
|
||
return new Promise((resolve) => { | ||
validateComponentCode({ | ||
code: currentCode, | ||
frontend_node: node.data.node, | ||
}) | ||
.then(({ data: resData, type }) => { | ||
if (resData && type) { | ||
const newNode = processNodeAdvancedFields(resData, edges, nodeId); | ||
|
||
updates.push({ | ||
nodeId, | ||
newNode, | ||
code: currentCode, | ||
name: "code", | ||
type, | ||
}); | ||
|
||
updatedCount++; | ||
} | ||
resolve(null); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
resolve(null); | ||
}); | ||
}); | ||
}); | ||
|
||
Promise.all(updatePromises) | ||
.then(() => { | ||
if (updatedCount > 0) { | ||
// Batch update all nodes at once | ||
updateAllNodes(updates); | ||
|
||
useAlertStore.getState().setSuccessData({ | ||
title: `Successfully updated ${updatedCount} component${ | ||
updatedCount > 1 ? "s" : "" | ||
}`, | ||
}); | ||
} | ||
}) | ||
.catch((error) => { | ||
setErrorData({ | ||
title: "Error updating components", | ||
list: [ | ||
"There was an error updating the components.", | ||
"If the error persists, please report it on our Discord or GitHub.", | ||
], | ||
}); | ||
console.error(error); | ||
}) | ||
.finally(() => { | ||
setLoadingUpdate(false); | ||
}); | ||
}; | ||
|
||
if (componentsToUpdate.length === 0) return null; | ||
|
||
return ( | ||
<div | ||
className={cn( | ||
"absolute bottom-2 left-1/2 z-50 flex w-[500px] -translate-x-1/2 items-center gap-8 rounded-lg bg-warning px-4 py-2 text-sm font-medium text-warning-foreground shadow-md transition-all ease-in", | ||
dismissed && "translate-y-[120%]", | ||
)} | ||
> | ||
<div className="flex items-center gap-3"> | ||
<ForwardedIconComponent | ||
name="AlertTriangle" | ||
className="!h-[18px] !w-[18px] shrink-0" | ||
strokeWidth={1.5} | ||
/> | ||
<span> | ||
{componentsToUpdate.length} component | ||
{componentsToUpdate.length > 1 ? "s" : ""} are ready to update | ||
</span> | ||
</div> | ||
<div className="flex items-center gap-4"> | ||
<Button | ||
variant="link" | ||
size="icon" | ||
className="shrink-0 text-sm text-warning-foreground" | ||
onClick={() => { | ||
setDismissed(true); | ||
}} | ||
> | ||
Dismiss | ||
</Button> | ||
<Button | ||
variant="warning" | ||
size="sm" | ||
className="shrink-0" | ||
onClick={handleUpdateAllComponents} | ||
loading={loadingUpdate} | ||
> | ||
Update All | ||
</Button> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
src/frontend/tests/extended/features/outdated-actions.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import { expect, test } from "@playwright/test"; | ||
import { readFileSync } from "fs"; | ||
|
||
test("user must be able to update outdated components", async ({ page }) => { | ||
await page.goto("/"); | ||
|
||
let modalCount = 0; | ||
try { | ||
const modalTitleElement = await page?.getByTestId("modal-title"); | ||
if (modalTitleElement) { | ||
modalCount = await modalTitleElement.count(); | ||
} | ||
} catch (error) { | ||
modalCount = 0; | ||
} | ||
|
||
while (modalCount === 0) { | ||
await page.getByText("New Flow", { exact: true }).click(); | ||
await page.waitForTimeout(3000); | ||
modalCount = await page.getByTestId("modal-title")?.count(); | ||
} | ||
await page.locator("span").filter({ hasText: "Close" }).first().click(); | ||
|
||
await page.locator("span").filter({ hasText: "My Collection" }).isVisible(); | ||
// Read your file into a buffer. | ||
const jsonContent = readFileSync("tests/assets/outdated_flow.json", "utf-8"); | ||
|
||
// Create the DataTransfer and File | ||
const dataTransfer = await page.evaluateHandle((data) => { | ||
const dt = new DataTransfer(); | ||
// Convert the buffer to a hex array | ||
const file = new File([data], "outdated_flow.json", { | ||
type: "application/json", | ||
}); | ||
dt.items.add(file); | ||
return dt; | ||
}, jsonContent); | ||
|
||
// Now dispatch | ||
await page.getByTestId("cards-wrapper").dispatchEvent("drop", { | ||
dataTransfer, | ||
}); | ||
|
||
await page.waitForTimeout(3000); | ||
|
||
await page.getByTestId("list-card").first().click(); | ||
|
||
await page.waitForSelector("text=components are ready to update", { | ||
timeout: 30000, | ||
state: "visible", | ||
}); | ||
|
||
let outdatedComponents = await page.getByTestId("icon-AlertTriangle").count(); | ||
expect(outdatedComponents).toBeGreaterThan(0); | ||
|
||
await page.getByText("Update All", { exact: true }).click(); | ||
|
||
await page.waitForTimeout(3000); | ||
|
||
outdatedComponents = await page.getByTestId("icon-AlertTriangle").count(); | ||
expect(outdatedComponents).toBe(0); | ||
}); |