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.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.services.auth.utils import api_key_security, get_current_active_user
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)
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)
return built_frontend_node
@ -224,7 +224,7 @@ async def custom_component(
@router.post("/custom_component/reload", status_code=HTTPStatus.OK)
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:
reader = DirectoryReader("")
@ -234,7 +234,7 @@ async def reload_custom_component(path: str, user: User = Depends(get_current_ac
extractor = CustomComponent(code=content)
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:
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_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
return component_node

View file

@ -8,6 +8,8 @@ from uuid import UUID
from cachetools import LRUCache, cached
from fastapi import HTTPException
from loguru import logger
from langflow.interface.agents.base import agent_creator
from langflow.interface.chains.base import chain_creator
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.custom_components import CustomComponentFrontendNode
from langflow.utils.util import get_base_classes
from loguru import logger
# 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)
def build_langchain_template_custom_component(
def build_custom_component_template(
custom_component: CustomComponent,
user_id: Optional[Union[str, UUID]] = None,
update_field: Optional[str] = None,
@ -394,85 +395,118 @@ def build_and_validate_all_files(reader: DirectoryReader, file_list):
def build_valid_menu(valid_components):
"""Build the valid menu"""
"""Build the valid menu."""
valid_menu = {}
logger.debug("------------------- VALID COMPONENTS -------------------")
for menu_item in valid_components["menu"]:
menu_name = menu_item["name"]
valid_menu[menu_name] = {}
# menu_path = menu_item["path"]
for component in menu_item["components"]:
logger.debug(f"Building component: {component.get('name'), component.get('output_types')}")
try:
component_name = component["name"]
component_code = component["code"]
component_output_types = component["output_types"]
component_extractor = CustomComponent(code=component_code)
component_extractor.validate()
component_template = build_langchain_template_custom_component(component_extractor)
component_template["output_types"] = component_output_types
# full_path = f"{menu_path}/{component.get('file')}"
# 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}")
valid_menu[menu_name] = build_menu_items(menu_item)
return valid_menu
def build_menu_items(menu_item):
"""Build menu items for a given menu."""
menu_items = {}
for component in menu_item["components"]:
try:
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_output_types = component["output_types"]
component_extractor = CustomComponent(code=component_code)
component_extractor.validate()
component_template = build_custom_component_template(component_extractor)
component_template["output_types"] = component_output_types
return component_template
def build_invalid_menu(invalid_components):
"""Build the invalid menu"""
if invalid_components.get("menu"):
logger.debug("------------------- INVALID COMPONENTS -------------------")
"""Build the invalid menu."""
if not invalid_components.get("menu"):
return {}
logger.debug("------------------- INVALID COMPONENTS -------------------")
invalid_menu = {}
for menu_item in invalid_components["menu"]:
menu_name = menu_item["name"]
invalid_menu[menu_name] = {}
for component in menu_item["components"]:
try:
component_name = component["name"]
component_code = component["code"]
component_template = (
CustomComponentFrontendNode(
description="ERROR - Check your Python Code",
display_name=f"ERROR - {component_name}",
)
.to_dict()
.get(type(CustomComponent()).__name__)
)
component_template["error"] = component.get("error", None)
logger.debug(component)
logger.debug(f"Component Path: {component.get('path', 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)}")
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"]:
try:
component_name, component_template = build_invalid_component(component)
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 = (
CustomComponentFrontendNode(
description="ERROR - Check your Python Code",
display_name=f"ERROR - {component_name}",
)
.to_dict()
.get(type(CustomComponent()).__name__)
)
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(f"Component Path: {component.get('path', None)}")
logger.debug(f"Component Error: {component.get('error', None)}")
def get_new_key(dictionary, original_key):
counter = 1
new_key = original_key + " (" + str(counter) + ")"
@ -496,7 +530,7 @@ def merge_nested_dicts_with_renaming(dict1, dict2):
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"""
file_list = load_files_from_path(path)
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):
"""Get all types dictionary combining native and custom components."""
native_components = build_langchain_types_dict()
# custom_components is a list of dicts
# need to merge all the keys into one dict
custom_components_from_file: dict[str, Any] = {}
if settings_service.settings.COMPONENTS_PATH:
logger.info(f"Building custom components from {settings_service.settings.COMPONENTS_PATH}")
custom_components_from_file = build_custom_components(settings_service)
return merge_nested_dicts_with_renaming(native_components, custom_components_from_file)
custom_component_dicts = []
processed_paths = []
for path in settings_service.settings.COMPONENTS_PATH:
if str(path) in processed_paths:
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)")
for custom_component_dict in custom_component_dicts:
# custom_component_dict is a dict of dicts
if not custom_component_dict:
continue
category = list(custom_component_dict.keys())[0]
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}")
custom_components_from_file = {}
processed_paths = set()
for path in settings_service.settings.COMPONENTS_PATH:
path_str = str(path)
if path_str in processed_paths:
continue
custom_component_dict = build_custom_component_list_from_path(path_str)
if custom_component_dict:
category = next(iter(custom_component_dict))
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, 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):

View file

@ -498,11 +498,13 @@ class StoreService(Service):
comp_count = metadata.get("filter_count", 0)
except HTTPStatusError as exc:
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:
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:
raise ValueError(f"Unexpected error: {exc}")
raise ValueError(f"Unexpected error: {exc}") from exc
try:
if result and not metadata:
if len(result) >= limit:

View file

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

View file

@ -37,6 +37,7 @@ import {
convertObjToArray,
convertValuesToNumbers,
hasDuplicateKeys,
scapedJSONStringfy,
} from "../../utils/reactflowUtils";
import { classNames } from "../../utils/utils";
import BaseModal from "../baseModal";
@ -62,10 +63,6 @@ const EditNodeModal = forwardRef(
const { setTabsState, tabId } = useContext(FlowsContext);
const { reactFlowInstance } = useContext(typesContext);
let disabled =
reactFlowInstance
?.getEdges()
.some((edge) => edge.targetHandle === data.id) ?? false;
function changeAdvanced(n) {
setMyData((old) => {
@ -150,52 +147,329 @@ const EditNodeModal = forwardRef(
myData.node.template[templateParam].type
)
)
.map((templateParam, index) => (
<TableRow key={index} className="h-10">
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
<ShadTooltip
content={
myData.node?.template[templateParam].proxy
? myData.node?.template[templateParam].proxy
?.id
: null
}
>
<span>
{myData.node?.template[templateParam]
.display_name
? myData.node.template[templateParam]
.display_name
: myData.node?.template[templateParam].name}
</span>
</ShadTooltip>
</TableCell>
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
{myData.node?.template[templateParam].type ===
"str" &&
!myData.node.template[templateParam].options ? (
<div className="mx-auto">
{myData.node.template[templateParam].list ? (
<InputListComponent
editNode={true}
.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">
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
<ShadTooltip
content={
myData.node?.template[templateParam].proxy
? myData.node?.template[templateParam]
.proxy?.id
: null
}
>
<span>
{myData.node?.template[templateParam]
.display_name
? myData.node.template[templateParam]
.display_name
: myData.node?.template[templateParam]
.name}
</span>
</ShadTooltip>
</TableCell>
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
{myData.node?.template[templateParam].type ===
"str" &&
!myData.node.template[templateParam].options ? (
<div className="mx-auto">
{myData.node.template[templateParam]
.list ? (
<InputListComponent
editNode={true}
disabled={disabled}
value={
!myData.node.template[templateParam]
.value ||
myData.node.template[templateParam]
.value === ""
? [""]
: myData.node.template[
templateParam
].value
}
onChange={(value: string[]) => {
handleOnNewValue(
value,
templateParam
);
}}
/>
) : myData.node.template[templateParam]
.multiline ? (
<TextAreaComponent
id={"textarea-edit-" + index}
disabled={disabled}
editNode={true}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(
value: string | string[]
) => {
handleOnNewValue(
value,
templateParam
);
}}
/>
) : (
<InputComponent
id={"input-" + index}
editNode={true}
disabled={disabled}
password={
myData.node.template[templateParam]
.password ?? false
}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value) => {
handleOnNewValue(
value,
templateParam
);
}}
/>
)}
</div>
) : myData.node?.template[templateParam]
.type === "NestedDict" ? (
<div className=" w-full">
<DictComponent
disabled={disabled}
editNode={true}
value={
!myData.node.template[templateParam]
.value ||
myData.node.template[templateParam]
.value === ""
? [""]
: myData.node.template[templateParam]
myData.node!.template[
templateParam
]?.value?.toString() === "{}"
? {
yourkey: "value",
}
: myData.node!.template[templateParam]
.value
}
onChange={(value: string[]) => {
onChange={(newValue) => {
myData.node!.template[
templateParam
].value = newValue;
handleOnNewValue(
newValue,
templateParam
);
}}
id="editnode-div-dict-input"
/>
</div>
) : myData.node?.template[templateParam]
.type === "dict" ? (
<div
className={classNames(
"max-h-48 w-full overflow-auto custom-scroll",
myData.node!.template[templateParam].value
?.length > 1
? "my-3"
: ""
)}
>
<KeypairListComponent
disabled={disabled}
editNode={true}
value={
myData.node!.template[templateParam]
.value?.length === 0 ||
!myData.node!.template[templateParam]
.value
? [{ "": "" }]
: convertObjToArray(
myData.node!.template[
templateParam
].value
)
}
duplicateKey={errorDuplicateKey}
onChange={(newValue) => {
const valueToNumbers =
convertValuesToNumbers(newValue);
myData.node!.template[
templateParam
].value = valueToNumbers;
setErrorDuplicateKey(
hasDuplicateKeys(valueToNumbers)
);
handleOnNewValue(
valueToNumbers,
templateParam
);
}}
/>
</div>
) : myData.node?.template[templateParam]
.type === "bool" ? (
<div className="ml-auto">
{" "}
<ToggleShadComponent
id={"toggle-edit-" + index}
disabled={disabled}
enabled={
myData.node.template[templateParam]
.value
}
setEnabled={(isEnabled) => {
handleOnNewValue(
isEnabled,
templateParam
);
}}
size="small"
/>
</div>
) : myData.node?.template[templateParam]
.type === "float" ? (
<div className="mx-auto">
<FloatComponent
disabled={disabled}
editNode={true}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value) => {
handleOnNewValue(value, templateParam);
}}
/>
) : myData.node.template[templateParam]
.multiline ? (
<TextAreaComponent
id={"textarea-edit-" + index}
</div>
) : myData.node?.template[templateParam]
.type === "str" &&
myData.node.template[templateParam]
.options ? (
<div className="mx-auto">
<Dropdown
numberOfOptions={nodeLength}
editNode={true}
options={
myData.node.template[templateParam]
.options
}
onSelect={(value) =>
handleOnNewValue(value, templateParam)
}
value={
myData.node.template[templateParam]
.value ?? "Choose an option"
}
id={"dropdown-edit-" + index}
></Dropdown>
</div>
) : myData.node?.template[templateParam]
.type === "int" ? (
<div className="mx-auto">
<IntComponent
id={"int-input-" + index}
disabled={disabled}
editNode={true}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value) => {
handleOnNewValue(value, templateParam);
}}
/>
</div>
) : myData.node?.template[templateParam]
.type === "file" ? (
<div className="mx-auto">
<InputFileComponent
editNode={true}
disabled={disabled}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam);
}}
fileTypes={
myData.node.template[templateParam]
.fileTypes
}
onFileChange={(filePath: string) => {
data.node!.template[
templateParam
].file_path = filePath;
}}
></InputFileComponent>
</div>
) : myData.node?.template[templateParam]
.type === "prompt" ? (
<div className="mx-auto">
<PromptAreaComponent
readonly={
myData.node?.flow ? true : false
}
field_name={templateParam}
editNode={true}
disabled={disabled}
nodeClass={myData.node}
setNodeClass={(nodeClass) => {
myData.node = nodeClass;
}}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam);
}}
id={"prompt-area-edit" + index}
/>
</div>
) : myData.node?.template[templateParam]
.type === "code" ? (
<div className="mx-auto">
<CodeAreaComponent
readonly={
myData.node?.flow &&
myData.node.template[templateParam]
.dynamic
? true
: false
}
dynamic={
data.node!.template[templateParam]
.dynamic ?? false
}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
}}
nodeClass={data.node}
disabled={disabled}
editNode={true}
value={
@ -205,269 +479,38 @@ const EditNodeModal = forwardRef(
onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam);
}}
id={"code-area-edit" + index}
/>
) : (
<InputComponent
id={"input-" + index}
editNode={true}
disabled={disabled}
password={
myData.node.template[templateParam]
.password ?? false
}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value) => {
handleOnNewValue(value, templateParam);
}}
/>
)}
</div>
) : myData.node?.template[templateParam].type ===
"NestedDict" ? (
<div className=" w-full">
<DictComponent
disabled={disabled}
editNode={true}
value={
myData.node!.template[
templateParam
]?.value?.toString() === "{}"
? {
yourkey: "value",
}
: myData.node!.template[templateParam]
.value
}
onChange={(newValue) => {
myData.node!.template[
templateParam
].value = newValue;
handleOnNewValue(newValue, templateParam);
}}
id="editnode-div-dict-input"
/>
</div>
) : myData.node?.template[templateParam].type ===
"dict" ? (
<div
className={classNames(
"max-h-48 w-full overflow-auto custom-scroll",
myData.node!.template[templateParam].value
?.length > 1
? "my-3"
: ""
)}
>
<KeypairListComponent
disabled={disabled}
editNode={true}
value={
myData.node!.template[templateParam].value
?.length === 0 ||
!myData.node!.template[templateParam]
.value
? [{ "": "" }]
: convertObjToArray(
myData.node!.template[templateParam]
.value
)
}
duplicateKey={errorDuplicateKey}
onChange={(newValue) => {
const valueToNumbers =
convertValuesToNumbers(newValue);
myData.node!.template[
templateParam
].value = valueToNumbers;
setErrorDuplicateKey(
hasDuplicateKeys(valueToNumbers)
);
handleOnNewValue(
valueToNumbers,
templateParam
);
}}
/>
</div>
) : myData.node?.template[templateParam].type ===
"bool" ? (
<div className="ml-auto">
{" "}
</div>
) : myData.node?.template[templateParam]
.type === "Any" ? (
"-"
) : (
<div className="hidden"></div>
)}
</TableCell>
<TableCell className="p-0 text-right">
<div className="items-center text-center">
<ToggleShadComponent
id={"toggle-edit-" + index}
disabled={disabled}
enabled={
myData.node.template[templateParam].value
id={
"show" +
myData.node?.template[templateParam].name
}
setEnabled={(isEnabled) => {
handleOnNewValue(
isEnabled,
templateParam
);
enabled={
!myData.node?.template[templateParam]
.advanced
}
setEnabled={(e) => {
changeAdvanced(templateParam);
}}
disabled={disabled}
size="small"
/>
</div>
) : myData.node?.template[templateParam].type ===
"float" ? (
<div className="mx-auto">
<FloatComponent
disabled={disabled}
editNode={true}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value) => {
handleOnNewValue(value, templateParam);
}}
/>
</div>
) : myData.node?.template[templateParam].type ===
"str" &&
myData.node.template[templateParam].options ? (
<div className="mx-auto">
<Dropdown
numberOfOptions={nodeLength}
editNode={true}
options={
myData.node.template[templateParam]
.options
}
onSelect={(value) =>
handleOnNewValue(value, templateParam)
}
value={
myData.node.template[templateParam]
.value ?? "Choose an option"
}
id={"dropdown-edit-" + index}
></Dropdown>
</div>
) : myData.node?.template[templateParam].type ===
"int" ? (
<div className="mx-auto">
<IntComponent
id={"int-input-" + index}
disabled={disabled}
editNode={true}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value) => {
handleOnNewValue(value, templateParam);
}}
/>
</div>
) : myData.node?.template[templateParam].type ===
"file" ? (
<div className="mx-auto">
<InputFileComponent
editNode={true}
disabled={disabled}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam);
}}
fileTypes={
myData.node.template[templateParam]
.fileTypes
}
onFileChange={(filePath: string) => {
data.node!.template[
templateParam
].file_path = filePath;
}}
></InputFileComponent>
</div>
) : myData.node?.template[templateParam].type ===
"prompt" ? (
<div className="mx-auto">
<PromptAreaComponent
readonly={myData.node?.flow ? true : false}
field_name={templateParam}
editNode={true}
disabled={disabled}
nodeClass={myData.node}
setNodeClass={(nodeClass) => {
myData.node = nodeClass;
}}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam);
}}
id={"prompt-area-edit" + index}
/>
</div>
) : myData.node?.template[templateParam].type ===
"code" ? (
<div className="mx-auto">
<CodeAreaComponent
readonly={
myData.node?.flow &&
myData.node.template[templateParam]
.dynamic
? true
: false
}
dynamic={
data.node!.template[templateParam]
.dynamic ?? false
}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
}}
nodeClass={data.node}
disabled={disabled}
editNode={true}
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam);
}}
id={"code-area-edit" + index}
/>
</div>
) : myData.node?.template[templateParam].type ===
"Any" ? (
"-"
) : (
<div className="hidden"></div>
)}
</TableCell>
<TableCell className="p-0 text-right">
<div className="items-center text-center">
<ToggleShadComponent
id={
"show" +
myData.node?.template[templateParam].name
}
enabled={
!myData.node?.template[templateParam]
.advanced
}
setEnabled={(e) => {
changeAdvanced(templateParam);
}}
disabled={disabled}
size="small"
/>
</div>
</TableCell>
</TableRow>
))}
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>

View file

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

View file

@ -305,7 +305,7 @@ def added_vector_store(client, json_vector_store, logged_in_headers):
@pytest.fixture
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
with open(path, "r") as f:
return f.read()

View file

@ -8,7 +8,7 @@ from fastapi import HTTPException
from langflow.interface.custom.base import CustomComponent
from langflow.interface.custom.code_parser import CodeParser, CodeSyntaxError
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
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):
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)
template = frontend_node["template"]
assert isinstance(template, dict)
assert "param" in template
param_options = template["param"]["options"]
# 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"]
assert param_options != new_param_options