diff --git a/src/backend/langflow/__main__.py b/src/backend/langflow/__main__.py index a08ae9fb0..3a110f380 100644 --- a/src/backend/langflow/__main__.py +++ b/src/backend/langflow/__main__.py @@ -55,7 +55,7 @@ def display_results(results): def update_settings( config: str, - cache: str, + cache: Optional[str] = None, dev: bool = False, remove_api_keys: bool = False, components_path: Optional[Path] = None, @@ -153,10 +153,10 @@ def run( log_file: Path = typer.Option( "logs/langflow.log", help="Path to the log file.", envvar="LANGFLOW_LOG_FILE" ), - cache: str = typer.Option( + cache: Optional[str] = typer.Option( envvar="LANGFLOW_LANGCHAIN_CACHE", help="Type of cache to use. (InMemoryCache, SQLiteCache)", - default="SQLiteCache", + default=None, ), jcloud: bool = typer.Option(False, help="Deploy on Jina AI Cloud"), dev: bool = typer.Option(False, help="Run in development mode (may contain bugs)"), diff --git a/src/backend/langflow/api/v1/validate.py b/src/backend/langflow/api/v1/validate.py index 457db5bd3..65fb66bd2 100644 --- a/src/backend/langflow/api/v1/validate.py +++ b/src/backend/langflow/api/v1/validate.py @@ -58,6 +58,16 @@ def post_validate_prompt(prompt_request: ValidatePromptRequest): def get_old_custom_fields(prompt_request): try: + if ( + len(prompt_request.frontend_node.custom_fields) == 1 + and prompt_request.name == "" + ): + # If there is only one custom field and the name is empty string + # then we are dealing with the first prompt request after the node was created + prompt_request.name = list( + prompt_request.frontend_node.custom_fields.keys() + )[0] + old_custom_fields = prompt_request.frontend_node.custom_fields[ prompt_request.name ].copy() diff --git a/src/backend/langflow/interface/types.py b/src/backend/langflow/interface/types.py index 230efd7a0..fa9967649 100644 --- a/src/backend/langflow/interface/types.py +++ b/src/backend/langflow/interface/types.py @@ -290,31 +290,42 @@ def add_base_classes(frontend_node, return_types: List[str]): def build_langchain_template_custom_component(custom_component: CustomComponent): """Build a custom component template for the langchain""" - logger.debug("Building custom component template") - frontend_node = build_frontend_node(custom_component) + try: + logger.debug("Building custom component template") + frontend_node = build_frontend_node(custom_component) - if frontend_node is None: - return None - logger.debug("Built base frontend node") - template_config = custom_component.build_template_config + if frontend_node is None: + return None + logger.debug("Built base frontend node") + template_config = custom_component.build_template_config - update_attributes(frontend_node, template_config) - logger.debug("Updated attributes") - field_config = build_field_config(custom_component) - logger.debug("Built field config") - add_extra_fields( - frontend_node, field_config, custom_component.get_function_entrypoint_args - ) - logger.debug("Added extra fields") - frontend_node = add_code_field( - frontend_node, custom_component.code, field_config.get("code", {}) - ) - logger.debug("Added code field") - add_base_classes( - frontend_node, custom_component.get_function_entrypoint_return_type - ) - logger.debug("Added base classes") - return frontend_node + update_attributes(frontend_node, template_config) + logger.debug("Updated attributes") + field_config = build_field_config(custom_component) + logger.debug("Built field config") + add_extra_fields( + frontend_node, field_config, custom_component.get_function_entrypoint_args + ) + logger.debug("Added extra fields") + frontend_node = add_code_field( + frontend_node, custom_component.code, field_config.get("code", {}) + ) + logger.debug("Added code field") + add_base_classes( + frontend_node, custom_component.get_function_entrypoint_return_type + ) + logger.debug("Added base classes") + return frontend_node + except Exception as exc: + raise HTTPException( + status_code=400, + detail={ + "error": ( + "Invalid type convertion. Please check your code and try again." + ), + "traceback": traceback.format_exc(), + }, + ) from exc def load_files_from_path(path: str): diff --git a/src/backend/langflow/interface/utils.py b/src/backend/langflow/interface/utils.py index 75e854e16..5bf44e203 100644 --- a/src/backend/langflow/interface/utils.py +++ b/src/backend/langflow/interface/utils.py @@ -77,9 +77,16 @@ def set_langchain_cache(settings): import langchain from langflow.interface.importing.utils import import_class - cache_type = os.getenv("LANGFLOW_LANGCHAIN_CACHE") - cache_class = import_class(f"langchain.cache.{cache_type or settings.CACHE}") + if cache_type := os.getenv("LANGFLOW_LANGCHAIN_CACHE"): + try: + cache_class = import_class( + f"langchain.cache.{cache_type or settings.CACHE}" + ) - logger.debug(f"Setting up LLM caching with {cache_class.__name__}") - langchain.llm_cache = cache_class() - logger.info(f"LLM caching setup with {cache_class.__name__}") + logger.debug(f"Setting up LLM caching with {cache_class.__name__}") + langchain.llm_cache = cache_class() + logger.info(f"LLM caching setup with {cache_class.__name__}") + except ImportError: + logger.warning(f"Could not import {cache_type}. ") + else: + logger.info("No LLM cache set.") diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py index 50dfcad60..911496d23 100644 --- a/src/backend/langflow/processing/process.py +++ b/src/backend/langflow/processing/process.py @@ -146,9 +146,8 @@ def generate_result(langchain_object: Union[Chain, VectorStore], inputs: dict): elif isinstance(langchain_object, Document): result = langchain_object.dict() else: - raise ValueError( - f"Unknown langchain_object type: {type(langchain_object).__name__}" - ) + logger.warning(f"Unknown langchain_object type: {type(langchain_object)}") + result = langchain_object return result diff --git a/src/backend/langflow/services/settings/base.py b/src/backend/langflow/services/settings/base.py index 30f04e112..86cbf64e5 100644 --- a/src/backend/langflow/services/settings/base.py +++ b/src/backend/langflow/services/settings/base.py @@ -37,7 +37,7 @@ class Settings(BaseSettings): DEV: bool = False DATABASE_URL: Optional[str] = None - CACHE: str = "InMemoryCache" + CACHE: Optional[str] = None REMOVE_API_KEYS: bool = False COMPONENTS_PATH: List[str] = [] diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 08eb261a6..6abdfd3b4 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -359,11 +359,16 @@ export default function ParameterComponent({ field_name={name} setNodeClass={(nodeClass) => { data.node = nodeClass; + const clone = cloneDeep(data); + clone.node = nodeClass; + setData(clone); }} nodeClass={data.node} disabled={disabled} value={data.node?.template[name].value ?? ""} - onChange={handleOnNewValue} + onChange={(e) => { + handleOnNewValue(e); + }} /> ) : left === true && type === "NestedDict" ? ( diff --git a/src/frontend/src/components/inputListComponent/index.tsx b/src/frontend/src/components/inputListComponent/index.tsx index d936173c3..06d2f0f6a 100644 --- a/src/frontend/src/components/inputListComponent/index.tsx +++ b/src/frontend/src/components/inputListComponent/index.tsx @@ -19,7 +19,9 @@ export default function InputListComponent({ }, [disabled]); // @TODO Recursive Character Text Splitter - the value might be in string format, whereas the InputListComponent specifically requires an array format. To ensure smooth operation and prevent potential errors, it's crucial that we handle the conversion from a string to an array with the string as its element. - typeof value === "string" ? (value = [value]) : (value = value); + if (typeof value === "string") { + value = [value]; + } return (
{ if (disabled) { onChange(""); diff --git a/src/frontend/src/icons/GradientSparkles/index.tsx b/src/frontend/src/icons/GradientSparkles/index.tsx index 55e8d3977..6be7e7576 100644 --- a/src/frontend/src/icons/GradientSparkles/index.tsx +++ b/src/frontend/src/icons/GradientSparkles/index.tsx @@ -1,3 +1,4 @@ +import { InfinityIcon } from "lucide-react"; import { forwardRef } from "react"; export const GradientSparkles = forwardRef< @@ -14,26 +15,7 @@ export const GradientSparkles = forwardRef< - {/* this svg comes from the source code of lucide, - we do not use the import because it crashes the ui (why? no one knows...). - source code from the used svg: - https://github.com/lucide-icons/lucide/blob/a4076db69b52ff0debc383f76d4d671c3bad5345/icons/infinity.svg?short_path=f79de91 - */} - - - + ); }); diff --git a/src/frontend/src/modals/EditNodeModal/index.tsx b/src/frontend/src/modals/EditNodeModal/index.tsx index bee154905..b4023b11f 100644 --- a/src/frontend/src/modals/EditNodeModal/index.tsx +++ b/src/frontend/src/modals/EditNodeModal/index.tsx @@ -1,5 +1,12 @@ import { cloneDeep } from "lodash"; -import { ReactNode, forwardRef, useContext, useEffect, useState } from "react"; +import { + ReactNode, + forwardRef, + useContext, + useEffect, + useRef, + useState, +} from "react"; import CodeAreaComponent from "../../components/codeAreaComponent"; import DictComponent from "../../components/dictComponent"; import Dropdown from "../../components/dropdownComponent"; @@ -51,37 +58,29 @@ const EditNodeModal = forwardRef( ref ) => { const [modalOpen, setModalOpen] = useState(false); - const [myData, setMyData] = useState(data); const { setTabsState, tabId } = useContext(TabsContext); const { reactFlowInstance } = useContext(typesContext); + const myData = useRef(data); let disabled = reactFlowInstance ?.getEdges() .some((edge) => edge.targetHandle === data.id) ?? false; - function changeAdvanced(templateParam: string): void { - setMyData((old) => { - let newData = cloneDeep(old); - newData.node!.template[templateParam].advanced = - !newData.node!.template[templateParam].advanced; - return newData; - }); + function changeAdvanced(n) { + let newData = cloneDeep(data); + newData.node!.template[n].advanced = !newData.node!.template[n].advanced; + myData.current = newData; } - const handleOnNewValue = ( - newValue: string | string[] | boolean, - name: string - ) => { - setMyData((old) => { - let newData = cloneDeep(old); - newData.node!.template[name].value = newValue; - return newData; - }); + const handleOnNewValue = (newValue: any, name) => { + let newData = cloneDeep(data); + newData.node!.template[name].value = newValue; + myData.current = newData; }; useEffect(() => { - setMyData(data); // reset data to what it is on node when opening modal + myData.current = data; }, [modalOpen]); const [obj, setObj] = useState({ @@ -104,9 +103,17 @@ const EditNodeModal = forwardRef( ] as Object[]); return ( - + { + let newData = cloneDeep(data); + myData.current = newData; + }} + > {children} - + {myData.type} ID: {myData.id} @@ -141,58 +148,65 @@ const EditNodeModal = forwardRef( - {Object.keys(myData.node!.template) + {Object.keys(myData.current.node!.template) .filter( (templateParam) => templateParam.charAt(0) !== "_" && - myData.node?.template[templateParam].show && - (myData.node.template[templateParam].type === - "str" || - myData.node.template[templateParam].type === - "bool" || - myData.node.template[templateParam].type === - "float" || - myData.node.template[templateParam].type === - "code" || - myData.node.template[templateParam].type === - "prompt" || - myData.node.template[templateParam].type === - "file" || - myData.node.template[templateParam].type === - "int") + myData.current.node?.template[templateParam].show && + (myData.current.node.template[templateParam] + .type === "str" || + myData.current.node.template[templateParam] + .type === "bool" || + myData.current.node.template[templateParam] + .type === "float" || + myData.current.node.template[templateParam] + .type === "code" || + myData.current.node.template[templateParam] + .type === "prompt" || + myData.current.node.template[templateParam] + .type === "file" || + myData.current.node.template[templateParam] + .type === "int") ) .map((templateParam, index) => ( - {myData.node?.template[templateParam].name - ? myData.node.template[templateParam].name - : myData.node?.template[templateParam] + {myData.current.node?.template[templateParam].name + ? myData.current.node.template[templateParam] + .name + : myData.current.node?.template[templateParam] .display_name} - {myData.node?.template[templateParam].type === - "str" && - !myData.node.template[templateParam].options ? ( + {myData.current.node?.template[templateParam] + .type === "str" && + !myData.current.node.template[templateParam] + .options ? (
- {myData.node.template[templateParam].list ? ( + {myData.current.node.template[templateParam] + .list ? ( { handleOnNewValue(value, templateParam); }} /> - ) : myData.node?.template[templateParam] - .type === "NestedDict" ? ( + ) : myData.current.node?.template[ + templateParam + ].type === "NestedDict" ? (
- ) : myData.node?.template[templateParam] - .type === "dict" ? ( + ) : myData.current.node?.template[ + templateParam + ].type === "dict" ? (
- ) : myData.node.template[templateParam] - .multiline ? ( + ) : myData.current.node.template[ + templateParam + ].multiline ? ( { handleOnNewValue(value, templateParam); @@ -255,12 +274,14 @@ const EditNodeModal = forwardRef( editNode={true} disabled={disabled} password={ - myData.node.template[templateParam] - .password ?? false + myData.current.node.template[ + templateParam + ].password ?? false } value={ - myData.node.template[templateParam] - .value ?? "" + myData.current.node.template[ + templateParam + ].value ?? "" } onChange={(value) => { handleOnNewValue(value, templateParam); @@ -268,14 +289,16 @@ const EditNodeModal = forwardRef( /> )}
- ) : myData.node?.template[templateParam].type === - "bool" ? ( + ) : myData.current.node?.template[templateParam] + .type === "bool" ? (
{" "} { handleOnNewValue( @@ -286,76 +309,84 @@ const EditNodeModal = forwardRef( size="small" />
- ) : myData.node?.template[templateParam].type === - "float" ? ( + ) : myData.current.node?.template[templateParam] + .type === "float" ? (
{ handleOnNewValue(value, templateParam); }} />
- ) : myData.node?.template[templateParam].type === - "str" && - myData.node.template[templateParam].options ? ( + ) : myData.current.node?.template[templateParam] + .type === "str" && + myData.current.node.template[templateParam] + .options ? (
handleOnNewValue(value, templateParam) } value={ - myData.node.template[templateParam] - .value ?? "Choose an option" + myData.current.node.template[ + templateParam + ].value ?? "Choose an option" } >
- ) : myData.node?.template[templateParam].type === - "int" ? ( + ) : myData.current.node?.template[templateParam] + .type === "int" ? (
{ handleOnNewValue(value, templateParam); }} />
- ) : myData.node?.template[templateParam].type === - "file" ? ( + ) : myData.current.node?.template[templateParam] + .type === "file" ? (
{ handleOnNewValue(value, templateParam); }} fileTypes={ - myData.node.template[templateParam] - .fileTypes + myData.current.node.template[ + templateParam + ].fileTypes } suffixes={ - myData.node.template[templateParam] - .suffixes + myData.current.node.template[ + templateParam + ].suffixes } onFileChange={(filePath: string) => { data.node!.template[ @@ -364,28 +395,29 @@ const EditNodeModal = forwardRef( }} >
- ) : myData.node?.template[templateParam].type === - "prompt" ? ( + ) : myData.current.node?.template[templateParam] + .type === "prompt" ? (
{ - myData.node = nodeClass; + myData.current.node = nodeClass; }} value={ - myData.node.template[templateParam] - .value ?? "" + myData.current.node.template[ + templateParam + ].value ?? "" } onChange={(value: string | string[]) => { handleOnNewValue(value, templateParam); }} />
- ) : myData.node?.template[templateParam].type === - "code" ? ( + ) : myData.current.node?.template[templateParam] + .type === "code" ? (
{ handleOnNewValue(value, templateParam); }} />
- ) : myData.node?.template[templateParam].type === - "Any" ? ( + ) : myData.current.node?.template[templateParam] + .type === "Any" ? ( "-" ) : (
@@ -418,8 +451,9 @@ const EditNodeModal = forwardRef(
changeAdvanced(templateParam) diff --git a/src/frontend/src/modals/baseModal/index.tsx b/src/frontend/src/modals/baseModal/index.tsx index 69de5e166..dc2008e68 100644 --- a/src/frontend/src/modals/baseModal/index.tsx +++ b/src/frontend/src/modals/baseModal/index.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from "react"; +import { ReactNode, useEffect } from "react"; import React from "react"; import { @@ -68,12 +68,16 @@ interface BaseModalProps { | "large-h-full" | "small-h-full" | "medium-h-full"; + + disable?: boolean; + onChangeOpenModal?: (open: boolean) => void; } function BaseModal({ open, setOpen, children, size = "large", + onChangeOpenModal, }: BaseModalProps) { const headerChild = React.Children.toArray(children).find( (child) => (child as React.ReactElement).type === Header @@ -127,6 +131,12 @@ function BaseModal({ break; } + useEffect(() => { + if (onChangeOpenModal) { + onChangeOpenModal(open); + } + }, [open]); + //UPDATE COLORS AND STYLE CLASSSES return ( diff --git a/src/frontend/src/modals/codeAreaModal/index.tsx b/src/frontend/src/modals/codeAreaModal/index.tsx index 835e493cb..903fb85b5 100644 --- a/src/frontend/src/modals/codeAreaModal/index.tsx +++ b/src/frontend/src/modals/codeAreaModal/index.tsx @@ -164,18 +164,18 @@ export default function CodeAreaModal({
-
+

{error?.detail?.error}

-
+                
                   {error?.detail?.traceback}
-                
+
diff --git a/src/frontend/src/modals/genericModal/index.tsx b/src/frontend/src/modals/genericModal/index.tsx index bfd8a8213..baebd67ba 100644 --- a/src/frontend/src/modals/genericModal/index.tsx +++ b/src/frontend/src/modals/genericModal/index.tsx @@ -122,9 +122,15 @@ export default function GenericModal({ function validatePrompt(closeModal: boolean): void { //nodeClass is always null on tweaks + postValidatePrompt(field_name, inputValue, nodeClass!) .then((apiReturn) => { if (apiReturn.data) { + setValue(inputValue); + apiReturn.data.frontend_node["template"]["template"]["value"] = + inputValue; + setNodeClass!(apiReturn?.data?.frontend_node); + let inputVariables = apiReturn.data.input_variables ?? []; if (inputVariables && inputVariables.length === 0) { setIsEdit(true); @@ -164,7 +170,11 @@ export default function GenericModal({ const [modalOpen, setModalOpen] = useState(false); return ( - + {}} + open={modalOpen} + setOpen={setModalOpen} + > {children} { @@ -291,9 +301,7 @@ export default function GenericModal({ setModalOpen(false); break; case TypeModal.PROMPT: - !inputValue || inputValue === "" - ? setModalOpen(false) - : validatePrompt(false); + validatePrompt(false); break; default: diff --git a/src/frontend/src/style/classes.css b/src/frontend/src/style/classes.css index cd5d504f2..1e274135d 100644 --- a/src/frontend/src/style/classes.css +++ b/src/frontend/src/style/classes.css @@ -29,7 +29,6 @@ pre { animation: slideUp 300ms ease-out; } - .gradient-end { animation: gradient-motion-end 3s infinite forwards; } @@ -50,4 +49,22 @@ select:-webkit-autofill:focus { -webkit-box-shadow: 0 0 0px 1000px #fff6d0 inset; box-shadow: 0 0 0px 1000px #fff6d0 inset; color: black; -} \ No newline at end of file +} +.ace_scrollbar::-webkit-scrollbar { + height: 8px; + width: 8px; +} + +.ace_scrollbar::-webkit-scrollbar-track { + background-color: #f1f1f1; +} + +.ace_scrollbar::-webkit-scrollbar-thumb { + background-color: #ccc; + border-radius: 999px; +} + +.ace_scrollbar::-webkit-scrollbar-thumb:hover { + background-color: #bbb; + border-radius: 999px; +} diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 1d0825f7f..f92a54fd8 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -79,6 +79,16 @@ export type TextAreaComponentType = { editNode?: boolean; }; +export type PromptAreaComponentType = { + field_name?: string; + nodeClass?: APIClassType; + setNodeClass?: (value: APIClassType) => void; + disabled: boolean; + onChange: (value: string[] | string) => void; + value: string; + editNode?: boolean; +}; + export type CodeAreaComponentType = { disabled: boolean; onChange: (value: string[] | string) => void;