From cf7cd979dc9ccb6dd9d00448bb3424568fed33c4 Mon Sep 17 00:00:00 2001 From: gustavoschaedler Date: Fri, 23 Jun 2023 02:54:43 +0100 Subject: [PATCH 1/5] feat: Add ClassCodeExtractor and is_valid_class_template functions, and custom_component endpoint This commit adds a new file called `extract_info_from_class.py` containing a `ClassCodeExtractor` class that can extract information (imports, class details and functions) from a Python class code. It also adds a function called `is_valid_class_template` that checks if a given Python class code matches a certain template. Additionally, the commit adds a new endpoint in the `/custom_component` route of `endpoints.py` that returns a dictionary of all `langchain` types created by a specific creator. Finally, the commit adds a new section to `types.py` named `template_node`, which describes a default dictionary that can be used to define a new node for Langflow's template editor. --- .../langflow/api/extract_info_from_class.py | 70 ++++++ src/backend/langflow/api/v1/endpoints.py | 12 +- src/backend/langflow/api/v1/schemas.py | 8 +- src/backend/langflow/interface/types.py | 25 ++ src/backend/langflow/main.py | 237 ++++++++++++++++++ 5 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 src/backend/langflow/api/extract_info_from_class.py diff --git a/src/backend/langflow/api/extract_info_from_class.py b/src/backend/langflow/api/extract_info_from_class.py new file mode 100644 index 000000000..4dfb24e03 --- /dev/null +++ b/src/backend/langflow/api/extract_info_from_class.py @@ -0,0 +1,70 @@ +import ast + + +class ClassCodeExtractor: + def __init__(self, code): + self.code = code + self.data = { + "imports": [], + "class": { + "inherited_classes": "", + "name": "", + "init": "" + }, + "functions": [] + } + + def _handle_import(self, node): + for alias in node.names: + module_name = getattr(node, 'module', None) + self.data['imports'].append( + f"{module_name}.{alias.name}" if module_name else alias.name) + + def _handle_class(self, node): + self.data['class'].update({ + 'name': node.name, + 'inherited_classes': [ast.unparse(base) for base in node.bases] + }) + + for inner_node in node.body: + if isinstance(inner_node, ast.FunctionDef): + self._handle_function(inner_node) + + def _handle_function(self, node): + function_name = node.name + function_args_str = ast.unparse(node.args) + function_args = function_args_str.split( + ", ") if function_args_str else [] + + return_type = ast.unparse(node.returns) if node.returns else "None" + + function_data = { + "name": function_name, + "arguments": function_args, + "return_type": return_type + } + + if function_name == "__init__": + self.data['class']['init'] = function_args_str.split( + ", ") if function_args_str else [] + else: + self.data["functions"].append(function_data) + + def extract_class_info(self): + module = ast.parse(self.code) + + for node in module.body: + if isinstance(node, (ast.Import, ast.ImportFrom)): + self._handle_import(node) + elif isinstance(node, ast.ClassDef): + self._handle_class(node) + + return self.data + + +def is_valid_class_template(code: dict) -> bool: + class_name_ok = code["class"]["name"] == "PythonFunction" + function_run_exists = len( + [f for f in code["functions"] if f["name"] == "run"]) == 1 + + return (class_name_ok and function_run_exists) diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index 6fea926d7..4d3ac9f5e 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -7,9 +7,13 @@ from fastapi import APIRouter, Depends, HTTPException from langflow.api.v1.schemas import ( PredictRequest, PredictResponse, + CustomComponentResponse, ) -from langflow.interface.types import build_langchain_types_dict +from langflow.interface.types import ( + build_langchain_types_dict, + build_langchain_types_dict_by_creator +) from langflow.database.base import get_session from sqlmodel import Session @@ -62,3 +66,9 @@ def get_version(): from langflow import __version__ return {"version": __version__} + + +# @router.post("/custom_component", response_model=CustomComponentResponse, status_code=200) +@router.post("/custom_component", status_code=200) +def custom_component(code: dict): + return build_langchain_types_dict_by_creator("a") diff --git a/src/backend/langflow/api/v1/schemas.py b/src/backend/langflow/api/v1/schemas.py index fc2e8c69d..145ac9365 100644 --- a/src/backend/langflow/api/v1/schemas.py +++ b/src/backend/langflow/api/v1/schemas.py @@ -69,7 +69,8 @@ class ChatResponse(ChatMessage): @validator("type") def validate_message_type(cls, v): if v not in ["start", "stream", "end", "error", "info", "file"]: - raise ValueError("type must be start, stream, end, error, info, or file") + raise ValueError( + "type must be start, stream, end, error, info, or file") return v @@ -110,3 +111,8 @@ class StreamData(BaseModel): def __str__(self) -> str: return f"event: {self.event}\ndata: {json.dumps(self.data)}\n\n" + + +class CustomComponentResponse(BaseModel): + model: str = "" + step: str = "" diff --git a/src/backend/langflow/interface/types.py b/src/backend/langflow/interface/types.py index 085537756..ac1b59e6f 100644 --- a/src/backend/langflow/interface/types.py +++ b/src/backend/langflow/interface/types.py @@ -52,3 +52,28 @@ def build_langchain_types_dict(): # sourcery skip: dict-assign-update-to-union if created_types[creator.type_name].values(): all_types.update(created_types) return all_types + + +# sourcery skip: dict-assign-update-to-union +def build_langchain_types_dict_by_creator(creator: str): + """Build a dictionary of all langchain types""" + + all_types = {} + + creators = [ + chain_creator, + agent_creator, + prompt_creator, + llm_creator, + memory_creator, + tool_creator, + toolkits_creator, + wrapper_creator, + embedding_creator, + vectorstore_creator, + documentloader_creator, + textsplitter_creator, + utility_creator, + ] + + return chain_creator.to_dict()['chains']['ConversationChain'] diff --git a/src/backend/langflow/main.py b/src/backend/langflow/main.py index ad3217eb5..7f5644d69 100644 --- a/src/backend/langflow/main.py +++ b/src/backend/langflow/main.py @@ -4,6 +4,239 @@ from fastapi.middleware.cors import CORSMiddleware from langflow.api import router from langflow.database.base import create_db_and_tables +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.""" @@ -18,6 +251,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, From 5d430f93645fed70a372be0da23bd50316898157 Mon Sep 17 00:00:00 2001 From: gustavoschaedler Date: Tue, 27 Jun 2023 19:25:44 +0100 Subject: [PATCH 2/5] Refactor ClassCodeExtractor to extract the entrypoint function arguments and return type\nAdd validation of correct format for custom_component code\nAdd function to build a template for custom_component with its code as a field value. --- .../langflow/api/extract_info_from_class.py | 41 +++++++++++-- src/backend/langflow/api/v1/endpoints.py | 49 ++++++++++++++-- src/backend/langflow/api/v1/schemas.py | 4 ++ src/backend/langflow/interface/types.py | 58 ++++++++++++------- 4 files changed, 122 insertions(+), 30 deletions(-) diff --git a/src/backend/langflow/api/extract_info_from_class.py b/src/backend/langflow/api/extract_info_from_class.py index 4dfb24e03..0c930e09c 100644 --- a/src/backend/langflow/api/extract_info_from_class.py +++ b/src/backend/langflow/api/extract_info_from_class.py @@ -4,6 +4,7 @@ import ast class ClassCodeExtractor: def __init__(self, code): self.code = code + self.function_entrypoint_name = "build" self.data = { "imports": [], "class": { @@ -61,10 +62,40 @@ class ClassCodeExtractor: return self.data + def get_entrypoint_function_args_and_return_type(self): + data = self.extract_class_info() + functions = data.get("functions", []) -def is_valid_class_template(code: dict) -> bool: - class_name_ok = code["class"]["name"] == "PythonFunction" - function_run_exists = len( - [f for f in code["functions"] if f["name"] == "run"]) == 1 + build_function = next( + (f for f in functions if f["name"] == + self.function_entrypoint_name), None + ) - return (class_name_ok and function_run_exists) + funtion_args = build_function.get("arguments", None) + return_type = build_function.get("return_type", None) + + return funtion_args, return_type + + +def is_valid_class_template(code: dict): + function_entrypoint_name = "build" + return_type_valid_list = ["ChainCreator", "ToolCreator"] + + class_name = code.get("class", {}).get("name", None) + if not class_name: # this will also check for None, empty string, etc. + return False + + functions = code.get("functions", []) + # use a generator and next to find if a function matching the criteria exists + build_function = next( + (f for f in functions if f["name"] == function_entrypoint_name), None + ) + + if not build_function: + return False + + # Check if the return type of the build function is valid + if build_function.get("return_type") not in return_type_valid_list: + return False + + return True diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index 4d3ac9f5e..73ffc63a1 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -1,18 +1,22 @@ from langflow.database.models.flow import Flow from langflow.processing.process import process_graph_cached, process_tweaks from langflow.utils.logger import logger - +from langflow.api.extract_info_from_class import ( + ClassCodeExtractor, + is_valid_class_template +) from fastapi import APIRouter, Depends, HTTPException from langflow.api.v1.schemas import ( PredictRequest, PredictResponse, - CustomComponentResponse, + CustomComponentCode, + CustomComponentResponse ) from langflow.interface.types import ( build_langchain_types_dict, - build_langchain_types_dict_by_creator + build_langchain_template_custom_component ) from langflow.database.base import get_session from sqlmodel import Session @@ -70,5 +74,40 @@ def get_version(): # @router.post("/custom_component", response_model=CustomComponentResponse, status_code=200) @router.post("/custom_component", status_code=200) -def custom_component(code: dict): - return build_langchain_types_dict_by_creator("a") +def custom_component( + code: CustomComponentCode, + session: Session = Depends(get_session), +): + code_test = """ +from langflow.interface.chains.base import ChainCreator +from langflow.interface.tools.base import ToolCreator + + +class MyPythonClass(): + def __init__(self, title: str, author: str, year_published: int): + self.title = title + self.author = author + self.year_published = year_published + + def get_details(self): + return f"Title: {self.title}, Author: {self.author}, Year Published: {self.year_published}" + + def update_year_published(self, new_year: int): + self.year_published = new_year + print(f"The year of publication has been updated to {new_year}.") + + def build(self, name: str, id: int, other: str) -> ChainCreator: + return ChainCreator() +""" + + extractor = ClassCodeExtractor(code_test) + data = extractor.extract_class_info() + valid = is_valid_class_template(data) + + function_args, function_return_type = extractor.get_entrypoint_function_args_and_return_type() + + return build_langchain_template_custom_component( + code_test, + function_args, + function_return_type + ) diff --git a/src/backend/langflow/api/v1/schemas.py b/src/backend/langflow/api/v1/schemas.py index 145ac9365..c8b6e2856 100644 --- a/src/backend/langflow/api/v1/schemas.py +++ b/src/backend/langflow/api/v1/schemas.py @@ -116,3 +116,7 @@ class StreamData(BaseModel): class CustomComponentResponse(BaseModel): model: str = "" step: str = "" + + +class CustomComponentCode(BaseModel): + code: str diff --git a/src/backend/langflow/interface/types.py b/src/backend/langflow/interface/types.py index ac1b59e6f..17c1562e4 100644 --- a/src/backend/langflow/interface/types.py +++ b/src/backend/langflow/interface/types.py @@ -54,26 +54,44 @@ def build_langchain_types_dict(): # sourcery skip: dict-assign-update-to-union return all_types -# sourcery skip: dict-assign-update-to-union -def build_langchain_types_dict_by_creator(creator: str): - """Build a dictionary of all langchain types""" +def find_class_type(class_name, classes_dict): + return next( + ( + {"type": class_type, "class": class_name} + for class_type, class_list in classes_dict.items() + if class_name in class_list + ), + {"error": "class not found"}, + ) - all_types = {} - creators = [ - chain_creator, - agent_creator, - prompt_creator, - llm_creator, - memory_creator, - tool_creator, - toolkits_creator, - wrapper_creator, - embedding_creator, - vectorstore_creator, - documentloader_creator, - textsplitter_creator, - utility_creator, - ] +def build_langchain_template_custom_component(raw_code, function_args, function_return_type): + type_list = get_type_list() + type_and_class = find_class_type("Tool", type_list) - return chain_creator.to_dict()['chains']['ConversationChain'] + # Field with the Python code to allow update + code_field = { + "code": { + "required": True, + "placeholder": "", + "show": True, + "multiline": True, + "value": raw_code, + "password": False, + "name": "code", + "advanced": False, + "type": "code", + "list": False + } + } + + # TODO: Add extra fields + + # TODO: Build template result + template = chain_creator.to_dict()['chains']['ConversationChain'] + + template.get('template')['code'] = code_field.get('code') + + return template + # return globals()['tool_creator'].to_dict()[type_and_class['type']][type_and_class['class']] + # return chain_creator.to_dict()['chains']['ConversationChain'] From 2497561cd3fe634a61571764ec3c815089b933ec Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 27 Jun 2023 15:44:55 -0300 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=9A=80=20feat(API):=20add=20postCusto?= =?UTF-8?q?mComponent=20function=20to=20send=20custom=20component=20code?= =?UTF-8?q?=20to=20the=20server=20=F0=9F=9A=80=20feat(CodeAreaModal):=20re?= =?UTF-8?q?place=20UpdateTemplate=20function=20with=20postCustomComponent?= =?UTF-8?q?=20function=20to=20send=20custom=20component=20code=20to=20the?= =?UTF-8?q?=20server=20The=20UpdateTemplate=20function=20was=20not=20being?= =?UTF-8?q?=20used=20and=20was=20removed.=20The=20postCustomComponent=20fu?= =?UTF-8?q?nction=20was=20added=20to=20send=20custom=20component=20code=20?= =?UTF-8?q?to=20the=20server.=20The=20CodeAreaModal=20component=20was=20up?= =?UTF-8?q?dated=20to=20use=20the=20new=20postCustomComponent=20function?= =?UTF-8?q?=20instead=20of=20the=20UpdateTemplate=20function=20to=20send?= =?UTF-8?q?=20custom=20component=20code=20to=20the=20server.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/src/controllers/API/index.ts | 9 +++--- .../src/modals/codeAreaModal/index.tsx | 32 +++++++------------ 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index 60820bb7e..516e34201 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -3,7 +3,6 @@ import { PromptTypeAPI, errorsTypeAPI, InitTypeAPI, - TemplateVariableType, APITemplateType, } from "./../../types/api/index"; import { APIObjectType, sendAllProps } from "../../types/api/index"; @@ -322,6 +321,8 @@ export async function postBuildInit( return await axios.post(`/api/v1/build/init`, flow); } -export async function UpdateTemplate(type:string, template:APITemplateType):Promise>{ - return await axios.get(`/dynamic_node`); -} \ No newline at end of file +export async function postCustomComponent( + code: string +): Promise> { + return await axios.post(`/api/v1/custom_component`, { code }); +} diff --git a/src/frontend/src/modals/codeAreaModal/index.tsx b/src/frontend/src/modals/codeAreaModal/index.tsx index 451946b0e..536404fcc 100644 --- a/src/frontend/src/modals/codeAreaModal/index.tsx +++ b/src/frontend/src/modals/codeAreaModal/index.tsx @@ -1,5 +1,5 @@ -import { XMarkIcon, CommandLineIcon } from "@heroicons/react/24/outline"; -import { Fragment, useContext, useRef, useState } from "react"; +import { CommandLineIcon } from "@heroicons/react/24/outline"; +import { useContext, useRef, useState } from "react"; import { PopUpContext } from "../../contexts/popUpContext"; import AceEditor from "react-ace"; import "ace-builds/src-noconflict/mode-python"; @@ -8,9 +8,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 { UpdateTemplate, postValidateCode } from "../../controllers/API"; +import { postCustomComponent, postValidateCode } from "../../controllers/API"; import { alertContext } from "../../contexts/alertContext"; -import { TabsContext } from "../../contexts/tabsContext"; import { Dialog, DialogContent, @@ -22,18 +21,17 @@ import { } from "../../components/ui/dialog"; import { Button } from "../../components/ui/button"; import { CODE_PROMPT_DIALOG_SUBTITLE } from "../../constants"; -import Loading from "../../components/ui/loading"; import { APITemplateType } from "../../types/api"; export default function CodeAreaModal({ value, setValue, template, - setTemplate + setTemplate, }: { setValue: (value: string) => void; value: string; - template: APITemplateType, + template: APITemplateType; setTemplate: (template: APITemplateType) => void; }) { const [open, setOpen] = useState(true); @@ -88,19 +86,17 @@ export default function CodeAreaModal({ .catch((_) => { setLoading(false); setErrorData({ - title: - "There is something wrong with this code, please review it", - }) - } - ); - UpdateTemplate('code',template).then((apiReturn) => { + title: "There is something wrong with this code, please review it", + }); + }); + postCustomComponent(code).then((apiReturn) => { const data = apiReturn.data; if (data.template) { - console.log('updated') + console.log("updated"); setTemplate(data.template); setModalOpen(false); } - }) + }); } return ( @@ -137,11 +133,7 @@ export default function CodeAreaModal({ - From 08b20e18cb16ca86cfbb3c54c07a411bc2a0a11a Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 27 Jun 2023 16:26:27 -0300 Subject: [PATCH 4/5] feat(API): add postCustomComponent function to create custom components fix(modals): replace UpdateTemplate function with postCustomComponent function to create custom components --- src/frontend/src/controllers/API/index.ts | 7 +++++++ src/frontend/src/modals/codeAreaModal/index.tsx | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/controllers/API/index.ts b/src/frontend/src/controllers/API/index.ts index df285f419..217935d1f 100644 --- a/src/frontend/src/controllers/API/index.ts +++ b/src/frontend/src/controllers/API/index.ts @@ -325,3 +325,10 @@ export async function postBuildInit( export async function UpdateTemplate(type:string, template:APITemplateType):Promise>{ return await axios.get(`/dynamic_node`); } + +export async function postCustomComponent( + code: string, + apiClass: APIClassType +): Promise> { + return await axios.post(`/api/v1/custom_component`, { code }); +} \ No newline at end of file diff --git a/src/frontend/src/modals/codeAreaModal/index.tsx b/src/frontend/src/modals/codeAreaModal/index.tsx index a2015f5d2..f88bc7b38 100644 --- a/src/frontend/src/modals/codeAreaModal/index.tsx +++ b/src/frontend/src/modals/codeAreaModal/index.tsx @@ -92,10 +92,9 @@ export default function CodeAreaModal({ }) } ); - UpdateTemplate('code',nodeClass).then((apiReturn) => { + postCustomComponent('code',nodeClass).then((apiReturn) => { const data = apiReturn.data; if (data) { - console.log(data) setNodeClass(data); setModalOpen(false); } From f5211d4ef00c954580cafdb28aa0486e32351879 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 27 Jun 2023 16:40:00 -0300 Subject: [PATCH 5/5] bugfix: sendind string code --- src/frontend/src/modals/codeAreaModal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/modals/codeAreaModal/index.tsx b/src/frontend/src/modals/codeAreaModal/index.tsx index f88bc7b38..a0ff07ec1 100644 --- a/src/frontend/src/modals/codeAreaModal/index.tsx +++ b/src/frontend/src/modals/codeAreaModal/index.tsx @@ -92,7 +92,7 @@ export default function CodeAreaModal({ }) } ); - postCustomComponent('code',nodeClass).then((apiReturn) => { + postCustomComponent(code,nodeClass).then((apiReturn) => { const data = apiReturn.data; if (data) { setNodeClass(data);