diff --git a/src/backend/langflow/api/utils.py b/src/backend/langflow/api/utils.py index cd7daa5d2..bd566896d 100644 --- a/src/backend/langflow/api/utils.py +++ b/src/backend/langflow/api/utils.py @@ -26,8 +26,9 @@ def remove_api_keys(flow: dict): def build_input_keys_response(langchain_object): """Build the input keys response.""" + input_keys_response = { - "input_keys": langchain_object.input_keys, + "input_keys": {key: "" for key in langchain_object.input_keys}, "memory_keys": [], } # If the object has memory, that memory will have a memory_variables attribute @@ -44,4 +45,7 @@ def build_input_keys_response(langchain_object): # Add memory variables to memory_keys input_keys_response["memory_keys"] = langchain_object.memory.memory_variables + if hasattr(langchain_object, "prompt"): + input_keys_response["template"] = langchain_object.prompt.template + return input_keys_response diff --git a/src/backend/langflow/api/v1/schemas.py b/src/backend/langflow/api/v1/schemas.py index 6448f07bb..eac732575 100644 --- a/src/backend/langflow/api/v1/schemas.py +++ b/src/backend/langflow/api/v1/schemas.py @@ -43,7 +43,7 @@ class ChatMessage(BaseModel): """Chat message schema.""" is_bot: bool = False - message: Union[str, None] = None + message: Union[str, None, dict] = None type: str = "human" diff --git a/src/backend/langflow/api/v1/validate.py b/src/backend/langflow/api/v1/validate.py index 802ee3e3d..1223029b8 100644 --- a/src/backend/langflow/api/v1/validate.py +++ b/src/backend/langflow/api/v1/validate.py @@ -31,21 +31,36 @@ def post_validate_code(code: Code): def post_validate_prompt(prompt: ValidatePromptRequest): try: input_variables = validate_prompt(prompt.template) + # Reinitialize custom_fields + old_custom_fields = prompt.frontend_node.custom_fields.copy() + prompt.frontend_node.custom_fields = [] + # Add new variables to the template for variable in input_variables: try: template_field = TemplateField( - name=variable, field_type="str", show=True, advanced=False + name=variable, + field_type="str", + show=True, + advanced=False, + input_types=["BaseLoader"], ) prompt.frontend_node.template[variable] = template_field.to_dict() prompt.frontend_node.custom_fields.append(variable) - for key in prompt.frontend_node.template: - if key not in input_variables: - prompt.frontend_node.template.pop(key, None) + except Exception as exc: logger.exception(exc) raise HTTPException(status_code=500, detail=str(exc)) from exc + # Remove variables that are not in the template anymore + for variable in old_custom_fields: + if variable not in input_variables: + try: + prompt.frontend_node.template.pop(variable, None) + except Exception as exc: + logger.exception(exc) + raise HTTPException(status_code=500, detail=str(exc)) from exc + return PromptValidationResponse( input_variables=input_variables, frontend_node=prompt.frontend_node, diff --git a/src/backend/langflow/chat/manager.py b/src/backend/langflow/chat/manager.py index 4a1b8e77c..856ee226e 100644 --- a/src/backend/langflow/chat/manager.py +++ b/src/backend/langflow/chat/manager.py @@ -111,9 +111,9 @@ class ChatManager: self, client_id: str, payload: Dict, langchain_object: Any ): # Process the graph data and chat message - chat_message = payload.pop("message", "") - chat_message = ChatMessage(message=chat_message) - self.chat_history.add_message(client_id, chat_message) + chat_inputs = payload.pop("inputs", "") + chat_inputs = ChatMessage(message=chat_inputs) + self.chat_history.add_message(client_id, chat_inputs) # graph_data = payload start_resp = ChatResponse(message=None, type="start", intermediate_steps="") @@ -126,7 +126,7 @@ class ChatManager: result, intermediate_steps = await process_graph( langchain_object=langchain_object, - chat_message=chat_message, + chat_inputs=chat_inputs, websocket=self.active_connections[client_id], ) except Exception as e: diff --git a/src/backend/langflow/chat/utils.py b/src/backend/langflow/chat/utils.py index 2e2ee367f..5047438f2 100644 --- a/src/backend/langflow/chat/utils.py +++ b/src/backend/langflow/chat/utils.py @@ -7,7 +7,7 @@ from langflow.utils.logger import logger async def process_graph( langchain_object, - chat_message: ChatMessage, + chat_inputs: ChatMessage, websocket: WebSocket, ): langchain_object = try_setting_streaming_options(langchain_object, websocket) @@ -23,7 +23,7 @@ async def process_graph( try: logger.debug("Generating result and thought") result, intermediate_steps = await get_result_and_steps( - langchain_object, chat_message.message or "", websocket=websocket + langchain_object, chat_inputs.message or "", websocket=websocket ) logger.debug("Generated result and intermediate_steps") return result, intermediate_steps diff --git a/src/backend/langflow/processing/base.py b/src/backend/langflow/processing/base.py index 97b0d5be0..ca86d1a83 100644 --- a/src/backend/langflow/processing/base.py +++ b/src/backend/langflow/processing/base.py @@ -6,23 +6,12 @@ from langflow.processing.process import fix_memory_inputs, format_actions from langflow.utils.logger import logger -async def get_result_and_steps(langchain_object, message: str, **kwargs): +async def get_result_and_steps(langchain_object, inputs: dict, **kwargs): """Get result and thought from extracted json""" try: if hasattr(langchain_object, "verbose"): langchain_object.verbose = True - chat_input = None - memory_key = "" - if hasattr(langchain_object, "memory") and langchain_object.memory is not None: - memory_key = langchain_object.memory.memory_key - - if hasattr(langchain_object, "input_keys"): - for key in langchain_object.input_keys: - if key not in [memory_key, "chat_history"]: - chat_input = {key: message} - else: - chat_input = message # type: ignore if hasattr(langchain_object, "return_intermediate_steps"): # https://github.com/hwchase17/langchain/issues/2068 @@ -33,12 +22,12 @@ async def get_result_and_steps(langchain_object, message: str, **kwargs): fix_memory_inputs(langchain_object) try: async_callbacks = [AsyncStreamingLLMCallbackHandler(**kwargs)] - output = await langchain_object.acall(chat_input, callbacks=async_callbacks) + output = await langchain_object.acall(inputs, callbacks=async_callbacks) except Exception as exc: # make the error message more informative logger.debug(f"Error: {str(exc)}") sync_callbacks = [StreamingLLMCallbackHandler(**kwargs)] - output = langchain_object(chat_input, callbacks=sync_callbacks) + output = langchain_object(inputs, callbacks=sync_callbacks) intermediate_steps = ( output.get("intermediate_steps", []) if isinstance(output, dict) else [] diff --git a/src/backend/langflow/template/field/base.py b/src/backend/langflow/template/field/base.py index a9c18ff63..b73a516b4 100644 --- a/src/backend/langflow/template/field/base.py +++ b/src/backend/langflow/template/field/base.py @@ -21,6 +21,7 @@ class TemplateFieldCreator(BaseModel, ABC): name: str = "" display_name: Optional[str] = None advanced: bool = False + input_types: list[str] = [] def to_dict(self): result = self.dict() diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 9db5e444d..97c2aac88 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -78,7 +78,6 @@ export default function GenericNode({ } useEffect(() => {}, [closePopUp, data.node.template]); - console.log({data}) return ( <> diff --git a/src/frontend/src/components/ProgressBarComponent/index.tsx b/src/frontend/src/components/ProgressBarComponent/index.tsx deleted file mode 100644 index 08e432b3b..000000000 --- a/src/frontend/src/components/ProgressBarComponent/index.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { ReactElement, useContext, useEffect, useRef, useState } from "react"; -import { ProgressBarType } from "../../types/components"; -import { Progress } from "../../components/ui/progress"; -import { progressContext } from "../../contexts/ProgressContext"; -import { setInterval } from "timers/promises"; - -export default function ProgressBarComponent({ - value, - children, -}: ProgressBarType) { - const ref = useRef(0); - const reff = useRef(); - const { progress } = useContext(progressContext); - - useEffect(() => { - ref.current = progress * 100; - console.log(progress); - }, [progress]); - - return ; -} diff --git a/src/frontend/src/components/chatComponent/formTrigger/index.tsx b/src/frontend/src/components/chatComponent/formTrigger/index.tsx deleted file mode 100644 index c281a92e3..000000000 --- a/src/frontend/src/components/chatComponent/formTrigger/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Transition } from "@headlessui/react"; -import { FormInput, MessagesSquare } from "lucide-react"; - -import { alertContext } from "../../../contexts/alertContext"; -import { useContext } from "react"; - -export default function FormTrigger({ open, setOpen, isBuilt }) { - const { setErrorData } = useContext(alertContext); - - function handleClick() { - if (isBuilt) { - setOpen(true); - } else { - setErrorData({ - title: "Flow not built", - list: ["Please build the flow before chatting"], - }); - } - } - - return ( - -
-
- -
-
-
- ); -} diff --git a/src/frontend/src/components/chatComponent/index.tsx b/src/frontend/src/components/chatComponent/index.tsx index 741cf4ec8..cd3c9c789 100644 --- a/src/frontend/src/components/chatComponent/index.tsx +++ b/src/frontend/src/components/chatComponent/index.tsx @@ -3,11 +3,9 @@ import { useNodes } from "reactflow"; import { ChatType } from "../../types/chat"; import ChatTrigger from "./chatTrigger"; import BuildTrigger from "./buildTrigger"; -import ChatModal from "../../modals/chatModal"; import { getBuildStatus } from "../../controllers/API"; import { NodeType } from "../../types/flow"; -import FormTrigger from "./formTrigger"; import FormModal from "../../modals/formModal"; export default function Chat({ flow }: ChatType) { @@ -46,10 +44,11 @@ export default function Chat({ flow }: ChatType) { const prevNodesRef = useRef(); const nodes = useNodes(); useEffect(() => { + const prevNodes = prevNodesRef.current; const currentNodes = nodes.map( - (node: NodeType) => node.data.node.template.value - ); + (node: NodeType) => node.data.node.template + ); if ( prevNodes && @@ -66,15 +65,13 @@ export default function Chat({ flow }: ChatType) { {isBuilt ? (
- - - - + +
) : ( { - // console.log(event); if ( event.key !== "Backspace" && event.key !== "Enter" && diff --git a/src/frontend/src/components/promptComponent/index.tsx b/src/frontend/src/components/promptComponent/index.tsx index 9e85868ed..59c4227ef 100644 --- a/src/frontend/src/components/promptComponent/index.tsx +++ b/src/frontend/src/components/promptComponent/index.tsx @@ -73,6 +73,8 @@ export default function PromptAreaComponent({ setMyValue(t); onChange(t); }} + nodeClass={nodeClass} + setNodeClass={setNodeClass} /> ); }} diff --git a/src/frontend/src/components/ui/badge.tsx b/src/frontend/src/components/ui/badge.tsx index d8f6ca740..1516bf273 100644 --- a/src/frontend/src/components/ui/badge.tsx +++ b/src/frontend/src/components/ui/badge.tsx @@ -3,18 +3,25 @@ import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "../../utils"; const badgeVariants = cva( - "inline-flex items-center border rounded-full px-2.5 h-6 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + "inline-flex items-center border rounded-full px-2.5 font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", { variants: { variant: { default: "bg-primary hover:bg-primary/80 border-transparent text-primary-foreground", + gray: + "bg-border hover:bg-border/80 text-secondary-foreground", secondary: "bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground", destructive: "bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground", outline: "text-foreground", }, + size: { + sm: "h-4 text-xs", + md: "h-5 text-sm", + lg: "h-6 text-base", + } }, defaultVariants: { variant: "default", @@ -26,9 +33,9 @@ export interface BadgeProps extends React.HTMLAttributes, VariantProps {} -function Badge({ className, variant, ...props }: BadgeProps) { +function Badge({ className, variant, size, ...props }: BadgeProps) { return ( -
+
); } diff --git a/src/frontend/src/constants.tsx b/src/frontend/src/constants.tsx index f569046ea..76b345ed5 100644 --- a/src/frontend/src/constants.tsx +++ b/src/frontend/src/constants.tsx @@ -22,6 +22,13 @@ export const SETTINGS_DIALOG_SUBTITLE = "Edit details about your project."; export const CODE_DIALOG_SUBTITLE = "Export your flow to use it with this code."; +/** + * The base text for subtitle of Chat Form + * @constant + */ +export const CHAT_FORM_DIALOG_SUBTITLE = + "Set up the input variables defined in prompt templates. Interact with agents and chains."; + /** * The base text for subtitle of Edit Node Dialog * @constant diff --git a/src/frontend/src/contexts/tabsContext.tsx b/src/frontend/src/contexts/tabsContext.tsx index 0987d27db..b847f4ecd 100644 --- a/src/frontend/src/contexts/tabsContext.tsx +++ b/src/frontend/src/contexts/tabsContext.tsx @@ -87,12 +87,9 @@ export function TabsProvider({ children }: { children: ReactNode }) { Saveflows.forEach((flow) => { if (flow.data && flow.data?.nodes) flow.data?.nodes.forEach((node) => { - // console.log(node.data.type); //looking for file fields to prevent saving the content and breaking the flow for exceeding the the data limite for local storage Object.keys(node.data.node.template).forEach((key) => { - // console.log(node.data.node.template[key].type); if (node.data.node.template[key].type === "file") { - // console.log(node.data.node.template[key]); node.data.node.template[key].content = null; node.data.node.template[key].value = ""; } diff --git a/src/frontend/src/modals/chatModal/chatInput/index.tsx b/src/frontend/src/modals/chatModal/chatInput/index.tsx deleted file mode 100644 index 69d382748..000000000 --- a/src/frontend/src/modals/chatModal/chatInput/index.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { classNames } from "../../../utils"; -import { useContext, useEffect, useRef, useState } from "react"; -import { TabsContext } from "../../../contexts/tabsContext"; -import { INPUT_STYLE } from "../../../constants"; -import { Lock, Send } from "lucide-react"; - -export default function ChatInput({ - lockChat, - chatValue, - sendMessage, - setChatValue, - inputRef, -}) { - useEffect(() => { - if (!lockChat && inputRef.current) { - inputRef.current.focus(); - } - }, [lockChat, inputRef]); - - useEffect(() => { - if (inputRef.current) { - inputRef.current.style.height = "inherit"; // Reset the height - inputRef.current.style.height = `${inputRef.current.scrollHeight}px`; // Set it to the scrollHeight - } - }, [chatValue]); - - return ( -
- -
- - - ))} - {tabsState[id.current].formKeysData.memory_keys.map((i, k) => ( - -
-
{i}
- Memory Key -
-
- ))} - -
-
-
-
- {chatHistory.length > 0 ? ( - chatHistory.map((c, i) => ( - - )) - ) : ( -
- - πŸ‘‹{" "} - - LangFlow Chat - - -
-
- - Start a conversation and click the agent's thoughts{" "} - - - {" "} - to inspect the chaining process. - -
-
- )} -
+
+
+
+ + + Input Variables +
-
-
- + + {tabsState[id.current].formKeysData.input_keys.map((i, k) => ( + + {i} + +
+
+ + + handleOnCheckedChange(value, k) + } + size="small" + disabled={false} + /> +
+ +
+
+
+ ))} + {tabsState[id.current].formKeysData.memory_keys.map((i, k) => ( + +
+
{i}
+ Used as Memory Key +
+
+ ))} +
+
+
+
+
+ +
+
+ {chatHistory.length > 0 ? ( + chatHistory.map((c, i) => ( + + )) + ) : ( +
+ + πŸ‘‹{" "} + + LangFlow Chat + + +
+
+ + Start a conversation and click the agent's thoughts{" "} + + + {" "} + to inspect the chaining process. + +
+
+ )} +
+
+
+
+ +
-
- + + )} ); } diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index 70d1268f0..57709d9fc 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -49,7 +49,6 @@ const NodeToolbarComponent = (props) => { )} onClick={(event) => { event.preventDefault(); - // console.log(reactFlowInstance.getNode(props.data.id)); paste( { nodes: [reactFlowInstance.getNode(props.data.id)], diff --git a/src/frontend/src/types/api/index.ts b/src/frontend/src/types/api/index.ts index d47663d18..ecd79240e 100644 --- a/src/frontend/src/types/api/index.ts +++ b/src/frontend/src/types/api/index.ts @@ -23,6 +23,7 @@ export type TemplateVariableType = { show: boolean; multiline?: boolean; value?: any; + input_types?: Array; [key: string]: any; }; export type sendAllProps = { @@ -31,7 +32,7 @@ export type sendAllProps = { name: string; description: string; viewport: Viewport; - message: any; + inputs: any; chatHistory: { message: string; isSend: boolean }[]; }; diff --git a/src/frontend/src/utils.ts b/src/frontend/src/utils.ts index 46b9c51cd..df8a8a61a 100644 --- a/src/frontend/src/utils.ts +++ b/src/frontend/src/utils.ts @@ -656,7 +656,6 @@ export function isValidConnection( export function removeApiKeys(flow: FlowType): FlowType { let cleanFLow = _.cloneDeep(flow); - console.log(cleanFLow); cleanFLow.data.nodes.forEach((node) => { for (const key in node.data.node.template) { if (node.data.node.template[key].password) {