Merge branch 'feature/store' of github.com:logspace-ai/langflow into feature/store

This commit is contained in:
cristhianzl 2023-12-04 20:48:20 -03:00
commit 3192290f1b
9 changed files with 495 additions and 415 deletions

View file

@ -15,7 +15,7 @@ from langflow.api.v1.schemas import (
) )
from langflow.interface.custom.custom_component import CustomComponent from langflow.interface.custom.custom_component import CustomComponent
from langflow.interface.custom.directory_reader import DirectoryReader from langflow.interface.custom.directory_reader import DirectoryReader
from langflow.interface.types import build_langchain_template_custom_component, create_and_validate_component from langflow.interface.types import build_custom_component_template, create_and_validate_component
from langflow.processing.process import process_graph_cached, process_tweaks from langflow.processing.process import process_graph_cached, process_tweaks
from langflow.services.auth.utils import api_key_security, get_current_active_user from langflow.services.auth.utils import api_key_security, get_current_active_user
from langflow.services.cache.utils import save_uploaded_file from langflow.services.cache.utils import save_uploaded_file
@ -216,7 +216,7 @@ async def custom_component(
): ):
component = create_and_validate_component(raw_code.code) component = create_and_validate_component(raw_code.code)
built_frontend_node = build_langchain_template_custom_component(component, user_id=user.id) built_frontend_node = build_custom_component_template(component, user_id=user.id)
built_frontend_node = update_frontend_node_with_template_values(built_frontend_node, raw_code) built_frontend_node = update_frontend_node_with_template_values(built_frontend_node, raw_code)
return built_frontend_node return built_frontend_node
@ -224,7 +224,7 @@ async def custom_component(
@router.post("/custom_component/reload", status_code=HTTPStatus.OK) @router.post("/custom_component/reload", status_code=HTTPStatus.OK)
async def reload_custom_component(path: str, user: User = Depends(get_current_active_user)): async def reload_custom_component(path: str, user: User = Depends(get_current_active_user)):
from langflow.interface.types import build_langchain_template_custom_component from langflow.interface.types import build_custom_component_template
try: try:
reader = DirectoryReader("") reader = DirectoryReader("")
@ -234,7 +234,7 @@ async def reload_custom_component(path: str, user: User = Depends(get_current_ac
extractor = CustomComponent(code=content) extractor = CustomComponent(code=content)
extractor.validate() extractor.validate()
return build_langchain_template_custom_component(extractor, user_id=user.id) return build_custom_component_template(extractor, user_id=user.id)
except Exception as exc: except Exception as exc:
raise HTTPException(status_code=400, detail=str(exc)) raise HTTPException(status_code=400, detail=str(exc))
@ -246,6 +246,6 @@ async def custom_component_update(
): ):
component = create_and_validate_component(raw_code.code) component = create_and_validate_component(raw_code.code)
component_node = build_langchain_template_custom_component(component, user_id=user.id, update_field=raw_code.field) component_node = build_custom_component_template(component, user_id=user.id, update_field=raw_code.field)
# Update the field # Update the field
return component_node return component_node

View file

@ -8,6 +8,8 @@ from uuid import UUID
from cachetools import LRUCache, cached from cachetools import LRUCache, cached
from fastapi import HTTPException from fastapi import HTTPException
from loguru import logger
from langflow.interface.agents.base import agent_creator from langflow.interface.agents.base import agent_creator
from langflow.interface.chains.base import chain_creator from langflow.interface.chains.base import chain_creator
from langflow.interface.custom.custom_component import CustomComponent from langflow.interface.custom.custom_component import CustomComponent
@ -31,7 +33,6 @@ from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.constants import CLASSES_TO_REMOVE from langflow.template.frontend_node.constants import CLASSES_TO_REMOVE
from langflow.template.frontend_node.custom_components import CustomComponentFrontendNode from langflow.template.frontend_node.custom_components import CustomComponentFrontendNode
from langflow.utils.util import get_base_classes from langflow.utils.util import get_base_classes
from loguru import logger
# Used to get the base_classes list # Used to get the base_classes list
@ -335,7 +336,7 @@ def add_output_types(frontend_node, return_types: List[str]):
frontend_node.get("output_types").append(return_type) frontend_node.get("output_types").append(return_type)
def build_langchain_template_custom_component( def build_custom_component_template(
custom_component: CustomComponent, custom_component: CustomComponent,
user_id: Optional[Union[str, UUID]] = None, user_id: Optional[Union[str, UUID]] = None,
update_field: Optional[str] = None, update_field: Optional[str] = None,
@ -394,61 +395,97 @@ def build_and_validate_all_files(reader: DirectoryReader, file_list):
def build_valid_menu(valid_components): def build_valid_menu(valid_components):
"""Build the valid menu""" """Build the valid menu."""
valid_menu = {} valid_menu = {}
logger.debug("------------------- VALID COMPONENTS -------------------") logger.debug("------------------- VALID COMPONENTS -------------------")
for menu_item in valid_components["menu"]: for menu_item in valid_components["menu"]:
menu_name = menu_item["name"] menu_name = menu_item["name"]
valid_menu[menu_name] = {} valid_menu[menu_name] = build_menu_items(menu_item)
# menu_path = menu_item["path"] return valid_menu
def build_menu_items(menu_item):
"""Build menu items for a given menu."""
menu_items = {}
for component in menu_item["components"]: for component in menu_item["components"]:
logger.debug(f"Building component: {component.get('name'), component.get('output_types')}")
try: try:
component_name = component["name"] component_name, component_template = build_component(component)
menu_items[component_name] = component_template
logger.debug(f"Added {component_name} to valid menu.")
except Exception as exc:
logger.error(f"Error loading Component: {component['output_types']}")
logger.exception(f"Error while building custom component {component['output_types']}: {exc}")
return menu_items
def build_component(component):
"""Build a single component."""
logger.debug(f"Building component: {component.get('name'), component.get('output_types')}")
component_name = determine_component_name(component)
component_template = create_component_template(component)
return component_name, component_template
def determine_component_name(component):
"""Determine the name of the component."""
component_output_types = component["output_types"]
if len(component_output_types) == 1:
return component_output_types[0]
else:
file_name = component.get("file").split(".")[0]
return "".join(word.capitalize() for word in file_name.split("_")) if "_" in file_name else file_name
def create_component_template(component):
"""Create a template for a component."""
component_code = component["code"] component_code = component["code"]
component_output_types = component["output_types"] component_output_types = component["output_types"]
component_extractor = CustomComponent(code=component_code) component_extractor = CustomComponent(code=component_code)
component_extractor.validate() component_extractor.validate()
component_template = build_langchain_template_custom_component(component_extractor) component_template = build_custom_component_template(component_extractor)
component_template["output_types"] = component_output_types component_template["output_types"] = component_output_types
# full_path = f"{menu_path}/{component.get('file')}" return component_template
# component_template["full_path"] = full_path
if len(component_output_types) == 1:
component_name = component_output_types[0]
else:
file_name = component.get("file").split(".")[0]
if "_" in file_name:
# turn .py file into camelcase
component_name = "".join([word.capitalize() for word in file_name.split("_")])
else:
component_name = file_name
valid_menu[menu_name][component_name] = component_template
logger.debug(f"Added {component_name} to valid menu to {menu_name}")
except Exception as exc:
logger.error(f"Error loading Component: {component['output_types']}")
logger.exception(f"Error while building custom component {component_output_types}: {exc}")
return valid_menu
def build_invalid_menu(invalid_components): def build_invalid_menu(invalid_components):
"""Build the invalid menu""" """Build the invalid menu."""
if invalid_components.get("menu"): if not invalid_components.get("menu"):
return {}
logger.debug("------------------- INVALID COMPONENTS -------------------") logger.debug("------------------- INVALID COMPONENTS -------------------")
invalid_menu = {} invalid_menu = {}
for menu_item in invalid_components["menu"]: for menu_item in invalid_components["menu"]:
menu_name = menu_item["name"] menu_name = menu_item["name"]
invalid_menu[menu_name] = {} invalid_menu[menu_name] = build_invalid_menu_items(menu_item)
return invalid_menu
def build_invalid_menu_items(menu_item):
"""Build invalid menu items for a given menu."""
menu_items = {}
for component in menu_item["components"]: for component in menu_item["components"]:
try: try:
component_name = component["name"] component_name, component_template = build_invalid_component(component)
component_code = component["code"] menu_items[component_name] = component_template
logger.debug(f"Added {component_name} to invalid menu.")
except Exception as exc:
logger.exception(f"Error while creating custom component [{component_name}]: {str(exc)}")
return menu_items
def build_invalid_component(component):
"""Build a single invalid component."""
component_name = component["name"]
component_template = create_invalid_component_template(component, component_name)
log_invalid_component_details(component)
return component_name, component_template
def create_invalid_component_template(component, component_name):
"""Create a template for an invalid component."""
component_code = component["code"]
component_template = ( component_template = (
CustomComponentFrontendNode( CustomComponentFrontendNode(
description="ERROR - Check your Python Code", description="ERROR - Check your Python Code",
@ -459,18 +496,15 @@ def build_invalid_menu(invalid_components):
) )
component_template["error"] = component.get("error", None) component_template["error"] = component.get("error", None)
component_template.get("template").get("code")["value"] = component_code
return component_template
def log_invalid_component_details(component):
"""Log details of an invalid component."""
logger.debug(component) logger.debug(component)
logger.debug(f"Component Path: {component.get('path', None)}") logger.debug(f"Component Path: {component.get('path', None)}")
logger.debug(f"Component Error: {component.get('error', None)}") logger.debug(f"Component Error: {component.get('error', None)}")
component_template.get("template").get("code")["value"] = component_code
invalid_menu[menu_name][component_name] = component_template
logger.debug(f"Added {component_name} to invalid menu to {menu_name}")
except Exception as exc:
logger.exception(f"Error while creating custom component [{component_name}]: {str(exc)}")
return invalid_menu
def get_new_key(dictionary, original_key): def get_new_key(dictionary, original_key):
@ -496,7 +530,7 @@ def merge_nested_dicts_with_renaming(dict1, dict2):
return dict1 return dict1
def build_langchain_custom_component_list_from_path(path: str): def build_custom_component_list_from_path(path: str):
"""Build a list of custom components for the langchain from a given path""" """Build a list of custom components for the langchain from a given path"""
file_list = load_files_from_path(path) file_list = load_files_from_path(path)
reader = DirectoryReader(path, False) reader = DirectoryReader(path, False)
@ -510,34 +544,35 @@ def build_langchain_custom_component_list_from_path(path: str):
def get_all_types_dict(settings_service): def get_all_types_dict(settings_service):
"""Get all types dictionary combining native and custom components."""
native_components = build_langchain_types_dict() native_components = build_langchain_types_dict()
# custom_components is a list of dicts custom_components_from_file = build_custom_components(settings_service)
# need to merge all the keys into one dict return merge_nested_dicts_with_renaming(native_components, custom_components_from_file)
custom_components_from_file: dict[str, Any] = {}
if settings_service.settings.COMPONENTS_PATH:
def build_custom_components(settings_service):
"""Build custom components from the specified paths."""
if not settings_service.settings.COMPONENTS_PATH:
return {}
logger.info(f"Building custom components from {settings_service.settings.COMPONENTS_PATH}") logger.info(f"Building custom components from {settings_service.settings.COMPONENTS_PATH}")
custom_components_from_file = {}
custom_component_dicts = [] processed_paths = set()
processed_paths = []
for path in settings_service.settings.COMPONENTS_PATH: for path in settings_service.settings.COMPONENTS_PATH:
if str(path) in processed_paths: path_str = str(path)
if path_str in processed_paths:
continue continue
custom_component_dict = build_langchain_custom_component_list_from_path(str(path))
custom_component_dicts.append(custom_component_dict)
processed_paths.append(str(path))
logger.info(f"Loading {len(custom_component_dicts)} category(ies)") custom_component_dict = build_custom_component_list_from_path(path_str)
for custom_component_dict in custom_component_dicts: if custom_component_dict:
# custom_component_dict is a dict of dicts category = next(iter(custom_component_dict))
if not custom_component_dict:
continue
category = list(custom_component_dict.keys())[0]
logger.info(f"Loading {len(custom_component_dict[category])} component(s) from category {category}") logger.info(f"Loading {len(custom_component_dict[category])} component(s) from category {category}")
custom_components_from_file = merge_nested_dicts_with_renaming( custom_components_from_file = merge_nested_dicts_with_renaming(
custom_components_from_file, custom_component_dict custom_components_from_file, custom_component_dict
) )
processed_paths.add(path_str)
return merge_nested_dicts_with_renaming(native_components, custom_components_from_file) return custom_components_from_file
def merge_nested_dicts(dict1, dict2): def merge_nested_dicts(dict1, dict2):

View file

@ -498,11 +498,13 @@ class StoreService(Service):
comp_count = metadata.get("filter_count", 0) comp_count = metadata.get("filter_count", 0)
except HTTPStatusError as exc: except HTTPStatusError as exc:
if exc.response.status_code == 403: if exc.response.status_code == 403:
raise ForbiddenError("You are not authorized to access this public resource") raise ForbiddenError("You are not authorized to access this public resource") from exc
elif exc.response.status_code == 401: elif exc.response.status_code == 401:
raise APIKeyError("You are not authorized to access this resource. Please check your API key.") raise APIKeyError(
"You are not authorized to access this resource. Please check your API key."
) from exc
except Exception as exc: except Exception as exc:
raise ValueError(f"Unexpected error: {exc}") raise ValueError(f"Unexpected error: {exc}") from exc
try: try:
if result and not metadata: if result and not metadata:
if len(result) >= limit: if len(result) >= limit:

View file

@ -21,7 +21,7 @@ export default function DeleteConfirmationModal({
}) { }) {
return ( return (
<Dialog> <Dialog>
<DialogTrigger>{children}</DialogTrigger> <DialogTrigger tabIndex={-1}>{children}</DialogTrigger>
<DialogContent> <DialogContent>
<DialogHeader> <DialogHeader>
<DialogTitle> <DialogTitle>

View file

@ -37,6 +37,7 @@ import {
convertObjToArray, convertObjToArray,
convertValuesToNumbers, convertValuesToNumbers,
hasDuplicateKeys, hasDuplicateKeys,
scapedJSONStringfy,
} from "../../utils/reactflowUtils"; } from "../../utils/reactflowUtils";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
@ -62,10 +63,6 @@ const EditNodeModal = forwardRef(
const { setTabsState, tabId } = useContext(FlowsContext); const { setTabsState, tabId } = useContext(FlowsContext);
const { reactFlowInstance } = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
let disabled =
reactFlowInstance
?.getEdges()
.some((edge) => edge.targetHandle === data.id) ?? false;
function changeAdvanced(n) { function changeAdvanced(n) {
setMyData((old) => { setMyData((old) => {
@ -150,14 +147,37 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam].type myData.node.template[templateParam].type
) )
) )
.map((templateParam, index) => ( .map((templateParam, index) => {
let id = {
inputTypes:
data.node!.template[templateParam].input_types,
type: data.node!.template[templateParam].type,
id: data.id,
fieldName: templateParam,
};
let disabled =
reactFlowInstance?.getEdges().some(
(edge) =>
edge.targetHandle ===
scapedJSONStringfy(
data.node!.template[templateParam].proxy
? {
...id,
proxy:
data.node?.template[templateParam]
.proxy,
}
: id
)
) ?? false;
return (
<TableRow key={index} className="h-10"> <TableRow key={index} className="h-10">
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3"> <TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
<ShadTooltip <ShadTooltip
content={ content={
myData.node?.template[templateParam].proxy myData.node?.template[templateParam].proxy
? myData.node?.template[templateParam].proxy ? myData.node?.template[templateParam]
?.id .proxy?.id
: null : null
} }
> >
@ -166,7 +186,8 @@ const EditNodeModal = forwardRef(
.display_name .display_name
? myData.node.template[templateParam] ? myData.node.template[templateParam]
.display_name .display_name
: myData.node?.template[templateParam].name} : myData.node?.template[templateParam]
.name}
</span> </span>
</ShadTooltip> </ShadTooltip>
</TableCell> </TableCell>
@ -175,7 +196,8 @@ const EditNodeModal = forwardRef(
"str" && "str" &&
!myData.node.template[templateParam].options ? ( !myData.node.template[templateParam].options ? (
<div className="mx-auto"> <div className="mx-auto">
{myData.node.template[templateParam].list ? ( {myData.node.template[templateParam]
.list ? (
<InputListComponent <InputListComponent
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
@ -185,11 +207,15 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam] myData.node.template[templateParam]
.value === "" .value === ""
? [""] ? [""]
: myData.node.template[templateParam] : myData.node.template[
.value templateParam
].value
} }
onChange={(value: string[]) => { onChange={(value: string[]) => {
handleOnNewValue(value, templateParam); handleOnNewValue(
value,
templateParam
);
}} }}
/> />
) : myData.node.template[templateParam] ) : myData.node.template[templateParam]
@ -202,8 +228,13 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam] myData.node.template[templateParam]
.value ?? "" .value ?? ""
} }
onChange={(value: string | string[]) => { onChange={(
handleOnNewValue(value, templateParam); value: string | string[]
) => {
handleOnNewValue(
value,
templateParam
);
}} }}
/> />
) : ( ) : (
@ -220,13 +251,16 @@ const EditNodeModal = forwardRef(
.value ?? "" .value ?? ""
} }
onChange={(value) => { onChange={(value) => {
handleOnNewValue(value, templateParam); handleOnNewValue(
value,
templateParam
);
}} }}
/> />
)} )}
</div> </div>
) : myData.node?.template[templateParam].type === ) : myData.node?.template[templateParam]
"NestedDict" ? ( .type === "NestedDict" ? (
<div className=" w-full"> <div className=" w-full">
<DictComponent <DictComponent
disabled={disabled} disabled={disabled}
@ -245,13 +279,16 @@ const EditNodeModal = forwardRef(
myData.node!.template[ myData.node!.template[
templateParam templateParam
].value = newValue; ].value = newValue;
handleOnNewValue(newValue, templateParam); handleOnNewValue(
newValue,
templateParam
);
}} }}
id="editnode-div-dict-input" id="editnode-div-dict-input"
/> />
</div> </div>
) : myData.node?.template[templateParam].type === ) : myData.node?.template[templateParam]
"dict" ? ( .type === "dict" ? (
<div <div
className={classNames( className={classNames(
"max-h-48 w-full overflow-auto custom-scroll", "max-h-48 w-full overflow-auto custom-scroll",
@ -265,14 +302,15 @@ const EditNodeModal = forwardRef(
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
myData.node!.template[templateParam].value myData.node!.template[templateParam]
?.length === 0 || .value?.length === 0 ||
!myData.node!.template[templateParam] !myData.node!.template[templateParam]
.value .value
? [{ "": "" }] ? [{ "": "" }]
: convertObjToArray( : convertObjToArray(
myData.node!.template[templateParam] myData.node!.template[
.value templateParam
].value
) )
} }
duplicateKey={errorDuplicateKey} duplicateKey={errorDuplicateKey}
@ -292,15 +330,16 @@ const EditNodeModal = forwardRef(
}} }}
/> />
</div> </div>
) : myData.node?.template[templateParam].type === ) : myData.node?.template[templateParam]
"bool" ? ( .type === "bool" ? (
<div className="ml-auto"> <div className="ml-auto">
{" "} {" "}
<ToggleShadComponent <ToggleShadComponent
id={"toggle-edit-" + index} id={"toggle-edit-" + index}
disabled={disabled} disabled={disabled}
enabled={ enabled={
myData.node.template[templateParam].value myData.node.template[templateParam]
.value
} }
setEnabled={(isEnabled) => { setEnabled={(isEnabled) => {
handleOnNewValue( handleOnNewValue(
@ -311,8 +350,8 @@ const EditNodeModal = forwardRef(
size="small" size="small"
/> />
</div> </div>
) : myData.node?.template[templateParam].type === ) : myData.node?.template[templateParam]
"float" ? ( .type === "float" ? (
<div className="mx-auto"> <div className="mx-auto">
<FloatComponent <FloatComponent
disabled={disabled} disabled={disabled}
@ -326,9 +365,10 @@ const EditNodeModal = forwardRef(
}} }}
/> />
</div> </div>
) : myData.node?.template[templateParam].type === ) : myData.node?.template[templateParam]
"str" && .type === "str" &&
myData.node.template[templateParam].options ? ( myData.node.template[templateParam]
.options ? (
<div className="mx-auto"> <div className="mx-auto">
<Dropdown <Dropdown
numberOfOptions={nodeLength} numberOfOptions={nodeLength}
@ -347,8 +387,8 @@ const EditNodeModal = forwardRef(
id={"dropdown-edit-" + index} id={"dropdown-edit-" + index}
></Dropdown> ></Dropdown>
</div> </div>
) : myData.node?.template[templateParam].type === ) : myData.node?.template[templateParam]
"int" ? ( .type === "int" ? (
<div className="mx-auto"> <div className="mx-auto">
<IntComponent <IntComponent
id={"int-input-" + index} id={"int-input-" + index}
@ -363,8 +403,8 @@ const EditNodeModal = forwardRef(
}} }}
/> />
</div> </div>
) : myData.node?.template[templateParam].type === ) : myData.node?.template[templateParam]
"file" ? ( .type === "file" ? (
<div className="mx-auto"> <div className="mx-auto">
<InputFileComponent <InputFileComponent
editNode={true} editNode={true}
@ -387,11 +427,13 @@ const EditNodeModal = forwardRef(
}} }}
></InputFileComponent> ></InputFileComponent>
</div> </div>
) : myData.node?.template[templateParam].type === ) : myData.node?.template[templateParam]
"prompt" ? ( .type === "prompt" ? (
<div className="mx-auto"> <div className="mx-auto">
<PromptAreaComponent <PromptAreaComponent
readonly={myData.node?.flow ? true : false} readonly={
myData.node?.flow ? true : false
}
field_name={templateParam} field_name={templateParam}
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
@ -409,8 +451,8 @@ const EditNodeModal = forwardRef(
id={"prompt-area-edit" + index} id={"prompt-area-edit" + index}
/> />
</div> </div>
) : myData.node?.template[templateParam].type === ) : myData.node?.template[templateParam]
"code" ? ( .type === "code" ? (
<div className="mx-auto"> <div className="mx-auto">
<CodeAreaComponent <CodeAreaComponent
readonly={ readonly={
@ -440,8 +482,8 @@ const EditNodeModal = forwardRef(
id={"code-area-edit" + index} id={"code-area-edit" + index}
/> />
</div> </div>
) : myData.node?.template[templateParam].type === ) : myData.node?.template[templateParam]
"Any" ? ( .type === "Any" ? (
"-" "-"
) : ( ) : (
<div className="hidden"></div> <div className="hidden"></div>
@ -467,7 +509,8 @@ const EditNodeModal = forwardRef(
</div> </div>
</TableCell> </TableCell>
</TableRow> </TableRow>
))} );
})}
</TableBody> </TableBody>
</Table> </Table>
</div> </div>

View file

@ -1,5 +1,5 @@
import { useContext, useEffect, useState } from "react"; import { useContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import PaginatorComponent from "../../../../components/PaginatorComponent"; import PaginatorComponent from "../../../../components/PaginatorComponent";
import CollectionCardComponent from "../../../../components/cardComponent"; import CollectionCardComponent from "../../../../components/cardComponent";
import CardsWrapComponent from "../../../../components/cardsWrapComponent"; import CardsWrapComponent from "../../../../components/cardsWrapComponent";
@ -142,20 +142,20 @@ export default function ComponentsComponent({
disabled={isLoading} disabled={isLoading}
button={ button={
!is_component ? ( !is_component ? (
<Link to={"/flow/" + item.id}>
<Button <Button
tabIndex={-1}
variant="outline" variant="outline"
size="sm" size="sm"
className="whitespace-nowrap " className="whitespace-nowrap "
onClick={() => {
navigate("/flow/" + item.id);
}}
> >
<IconComponent <IconComponent
name="ExternalLink" name="ExternalLink"
className="main-page-nav-button" className="main-page-nav-button select-none"
/> />
Edit Flow Edit Flow
</Button> </Button>
</Link>
) : ( ) : (
<></> <></>
) )

View file

@ -305,7 +305,7 @@ def added_vector_store(client, json_vector_store, logged_in_headers):
@pytest.fixture @pytest.fixture
def test_component_code(): def test_component_code():
path = Path(__file__).parent.absolute() / "data" / "test_component.py" path = Path(__file__).parent.absolute() / "data" / "component_test.py"
# load the content as a string # load the content as a string
with open(path, "r") as f: with open(path, "r") as f:
return f.read() return f.read()

View file

@ -8,7 +8,7 @@ from fastapi import HTTPException
from langflow.interface.custom.base import CustomComponent from langflow.interface.custom.base import CustomComponent
from langflow.interface.custom.code_parser import CodeParser, CodeSyntaxError from langflow.interface.custom.code_parser import CodeParser, CodeSyntaxError
from langflow.interface.custom.component import Component, ComponentCodeNullError from langflow.interface.custom.component import Component, ComponentCodeNullError
from langflow.interface.types import build_langchain_template_custom_component, create_and_validate_component from langflow.interface.types import build_custom_component_template, create_and_validate_component
from langflow.services.database.models.flow import Flow, FlowCreate from langflow.services.database.models.flow import Flow, FlowCreate
code_default = """ code_default = """
@ -539,13 +539,13 @@ def test_create_and_validate_component_valid_code(test_component_code):
def test_build_langchain_template_custom_component_valid_code(test_component_code): def test_build_langchain_template_custom_component_valid_code(test_component_code):
component = create_and_validate_component(test_component_code) component = create_and_validate_component(test_component_code)
frontend_node = build_langchain_template_custom_component(component) frontend_node = build_custom_component_template(component)
assert isinstance(frontend_node, dict) assert isinstance(frontend_node, dict)
template = frontend_node["template"] template = frontend_node["template"]
assert isinstance(template, dict) assert isinstance(template, dict)
assert "param" in template assert "param" in template
param_options = template["param"]["options"] param_options = template["param"]["options"]
# Now run it again with an update field # Now run it again with an update field
frontend_node = build_langchain_template_custom_component(component, update_field="param") frontend_node = build_custom_component_template(component, update_field="param")
new_param_options = frontend_node["template"]["param"]["options"] new_param_options = frontend_node["template"]["param"]["options"]
assert param_options != new_param_options assert param_options != new_param_options