diff --git a/src/backend/langflow/main.py b/src/backend/langflow/main.py index 2a1293f2e..12d70bfb4 100644 --- a/src/backend/langflow/main.py +++ b/src/backend/langflow/main.py @@ -5,6 +5,239 @@ from langflow.api import router from langflow.database.base import create_db_and_tables from langflow.interface.utils import setup_llm_caching +template_node = { + "template": { + "code": { + "required": True, + "placeholder": "", + "show": True, + "multiline": True, + "value": "\ndef my_user_python_function(text: str) -> str:\n \"\"\"This is a default python function that returns the input text\"\"\"\n return text.upper()\n", + "password": False, + "name": "code", + "advanced": False, + "type": "code", + "list": False + }, + "lc_kwargs": { + "required": False, + "placeholder": "", + "show": False, + "multiline": False, + "password": False, + "name": "lc_kwargs", + "advanced": True, + "type": "code", + "list": False + }, + "verbose": { + "required": False, + "placeholder": "", + "show": False, + "multiline": False, + "value": False, + "password": False, + "name": "verbose", + "advanced": False, + "type": "bool", + "list": False + }, + "callbacks": { + "required": False, + "placeholder": "", + "show": False, + "multiline": False, + "password": False, + "name": "callbacks", + "advanced": False, + "type": "langchain.callbacks.base.BaseCallbackHandler", + "list": True + }, + "tags": { + "required": False, + "placeholder": "", + "show": False, + "multiline": False, + "password": False, + "name": "tags", + "advanced": False, + "type": "str", + "list": True + }, + "client": { + "required": False, + "placeholder": "", + "show": False, + "multiline": False, + "password": False, + "name": "client", + "advanced": False, + "type": "Any", + "list": False + }, + "model_name": { + "required": False, + "placeholder": "", + "show": True, + "multiline": False, + "value": "gpt-3.5-turbo", + "password": False, + "options": [ + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo", + "gpt-3.5-turbo-16k-0613", + "gpt-3.5-turbo-16k", + "gpt-4-0613", + "gpt-4-32k-0613", + "gpt-4", + "gpt-4-32k" + ], + "name": "model_name", + "advanced": False, + "type": "str", + "list": True + }, + "temperature": { + "required": False, + "placeholder": "", + "show": True, + "multiline": False, + "value": 0.7, + "password": False, + "name": "temperature", + "advanced": False, + "type": "float", + "list": False + }, + "model_kwargs": { + "required": False, + "placeholder": "", + "show": True, + "multiline": False, + "password": False, + "name": "model_kwargs", + "advanced": True, + "type": "code", + "list": False + }, + "openai_api_key": { + "required": False, + "placeholder": "", + "show": True, + "multiline": False, + "value": "", + "password": True, + "name": "openai_api_key", + "display_name": "OpenAI API Key", + "advanced": False, + "type": "str", + "list": False + }, + "openai_api_base": { + "required": False, + "placeholder": "", + "show": True, + "multiline": False, + "password": False, + "name": "openai_api_base", + "display_name": "OpenAI API Base", + "advanced": False, + "type": "str", + "list": False + }, + "openai_organization": { + "required": False, + "placeholder": "", + "show": False, + "multiline": False, + "password": False, + "name": "openai_organization", + "display_name": "OpenAI Organization", + "advanced": False, + "type": "str", + "list": False + }, + "openai_proxy": { + "required": False, + "placeholder": "", + "show": False, + "multiline": False, + "password": False, + "name": "openai_proxy", + "display_name": "OpenAI Proxy", + "advanced": False, + "type": "str", + "list": False + }, + "request_timeout": { + "required": False, + "placeholder": "", + "show": False, + "multiline": False, + "password": False, + "name": "request_timeout", + "advanced": False, + "type": "float", + "list": False + }, + "max_retries": { + "required": False, + "placeholder": "", + "show": False, + "multiline": False, + "value": 6, + "password": False, + "name": "max_retries", + "advanced": False, + "type": "int", + "list": False + }, + "streaming": { + "required": False, + "placeholder": "", + "show": False, + "multiline": False, + "value": False, + "password": False, + "name": "streaming", + "advanced": False, + "type": "bool", + "list": False + }, + "n": { + "required": False, + "placeholder": "", + "show": False, + "multiline": False, + "value": 1, + "password": False, + "name": "n", + "advanced": False, + "type": "int", + "list": False + }, + "max_tokens": { + "required": False, + "placeholder": "", + "show": True, + "multiline": False, + "password": True, + "name": "max_tokens", + "advanced": False, + "type": "int", + "list": False + }, + "_type": "ChatOpenAI" + }, + "base_classes": [ + "BaseChatModel", + "Serializable", + "BaseLanguageModel", + "ChatOpenAI" + ], + "description": "Wrapper around OpenAI Chat large language models." +} + def create_app(): """Create the FastAPI app and include the router.""" @@ -19,6 +252,10 @@ def create_app(): def get_health(): return {"status": "OK"} + @app.get("/dynamic_node") + def get_dynamic_nome(): + return template_node + app.add_middleware( CORSMiddleware, allow_origins=origins, diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 4347b88ca..21ee312b4 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -229,6 +229,8 @@ export default function ParameterComponent({ ) : left === true && type === "code" ? ( {data.node = nodeClass}} + nodeClass={data.node} disabled={disabled} value={data.node.template[name].value ?? ""} onChange={handleOnNewValue} diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 1668b1d4e..9db5e444d 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -78,7 +78,7 @@ export default function GenericNode({ } useEffect(() => {}, [closePopUp, data.node.template]); - + console.log({data}) return ( <> diff --git a/src/frontend/src/components/codeAreaComponent/index.tsx b/src/frontend/src/components/codeAreaComponent/index.tsx index 1730904e2..6b9cc7725 100644 --- a/src/frontend/src/components/codeAreaComponent/index.tsx +++ b/src/frontend/src/components/codeAreaComponent/index.tsx @@ -2,7 +2,7 @@ import { useContext, useEffect, useState } from "react"; import { PopUpContext } from "../../contexts/popUpContext"; import CodeAreaModal from "../../modals/codeAreaModal"; import TextAreaModal from "../../modals/textAreaModal"; -import { TextAreaComponentType } from "../../types/components"; +import { CodeAreaComponentType, TextAreaComponentType } from "../../types/components"; import { INPUT_STYLE } from "../../constants"; import { ExternalLink } from "lucide-react"; @@ -11,7 +11,9 @@ export default function CodeAreaComponent({ onChange, disabled, editNode = false, -}: TextAreaComponentType) { + nodeClass, + setNodeClass, +}: CodeAreaComponentType) { const [myValue, setMyValue] = useState(value); const { openPopUp } = useContext(PopUpContext); useEffect(() => { @@ -37,6 +39,8 @@ export default function CodeAreaComponent({ openPopUp( { setMyValue(t); onChange(t); @@ -59,7 +63,9 @@ export default function CodeAreaComponent({ onClick={() => { openPopUp( { setMyValue(t); onChange(t); diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index e6848d1d4..9a605859b 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -336,3 +336,9 @@ export async function uploadFile( formData.append("file", file); return await axios.post(`/api/v1/upload/${id}`, formData); } +export async function UpdateTemplate( + type: string, + nodeClass: APIClassType +): Promise> { + return await axios.get(`/dynamic_node`); +} diff --git a/src/frontend/src/modals/codeAreaModal/index.tsx b/src/frontend/src/modals/codeAreaModal/index.tsx index f6e4b8e1c..353b7eda1 100644 --- a/src/frontend/src/modals/codeAreaModal/index.tsx +++ b/src/frontend/src/modals/codeAreaModal/index.tsx @@ -1,4 +1,4 @@ -import { Fragment, useContext, useRef, useState } from "react"; +import { useContext, useRef, useState } from "react"; import { PopUpContext } from "../../contexts/popUpContext"; import AceEditor from "react-ace"; import "ace-builds/src-noconflict/mode-python"; @@ -7,9 +7,8 @@ import "ace-builds/src-noconflict/theme-twilight"; import "ace-builds/src-noconflict/ext-language_tools"; // import "ace-builds/webpack-resolver"; import { darkContext } from "../../contexts/darkContext"; -import { postValidateCode } from "../../controllers/API"; +import { UpdateTemplate, postValidateCode } from "../../controllers/API"; import { alertContext } from "../../contexts/alertContext"; -import { TabsContext } from "../../contexts/tabsContext"; import { Dialog, DialogContent, @@ -22,16 +21,22 @@ import { import { Button } from "../../components/ui/button"; import { CODE_PROMPT_DIALOG_SUBTITLE } from "../../constants"; import { TerminalSquare } from "lucide-react"; +import { APIClassType } from "../../types/api"; export default function CodeAreaModal({ value, setValue, + nodeClass, + setNodeClass, }: { setValue: (value: string) => void; value: string; + nodeClass: APIClassType; + setNodeClass: (Class: APIClassType) => void; }) { const [open, setOpen] = useState(true); const [code, setCode] = useState(value); + const [loading, setLoading] = useState(false); const { dark } = useContext(darkContext); const { setErrorData, setSuccessData } = useContext(alertContext); const { closePopUp } = useContext(PopUpContext); @@ -44,6 +49,56 @@ export default function CodeAreaModal({ }, 300); } } + + function handleClick() { + setLoading(true); + postValidateCode(code) + .then((apiReturn) => { + setLoading(false); + if (apiReturn.data) { + let importsErrors = apiReturn.data.imports.errors; + let funcErrors = apiReturn.data.function.errors; + if (funcErrors.length === 0 && importsErrors.length === 0) { + setSuccessData({ + title: "Code is ready to run", + }); + // setValue(code); + } else { + if (funcErrors.length !== 0) { + setErrorData({ + title: "There is an error in your function", + list: funcErrors, + }); + } + if (importsErrors.length !== 0) { + setErrorData({ + title: "There is an error in your imports", + list: importsErrors, + }); + } + } + } else { + setErrorData({ + title: "Something went wrong, please try again", + }); + } + }) + .catch((_) => { + setLoading(false); + setErrorData({ + title: "There is something wrong with this code, please review it", + }); + }); + UpdateTemplate("code", nodeClass).then((apiReturn) => { + const data = apiReturn.data; + if (data) { + console.log(data); + setNodeClass(data); + setModalOpen(false); + } + }); + } + return ( @@ -78,49 +133,8 @@ export default function CodeAreaModal({ - diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 630526193..b72a7e003 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -7,6 +7,7 @@ import { } from "react"; import { NodeDataType } from "../flow/index"; import { typesContextType } from "../typesContext"; +import { APIClassType, APITemplateType } from "../api"; export type InputComponentType = { value: string; disabled?: boolean; @@ -56,6 +57,15 @@ export type TextAreaComponentType = { editNode?: boolean; }; +export type CodeAreaComponentType = { + disabled: boolean; + onChange: (value: string[] | string) => void; + value: string; + editNode?: boolean; + nodeClass: APIClassType; + setNodeClass: (value: APIClassType) => void; +}; + export type FileComponentType = { disabled: boolean; onChange: (value: string[] | string) => void;