Merge branch 'zustand/io/migration' of personal:logspace-ai/langflow into zustand/io/migration

This commit is contained in:
anovazzi1 2024-03-07 15:36:34 -03:00
commit cf4733e5a9
6 changed files with 99 additions and 26 deletions

View file

@ -83,7 +83,8 @@ The CustomComponent class serves as the foundation for creating custom component
| _`file_types: List[str]`_ | This is a requirement if the _`field_type`_ is _file_. Defines which file types will be accepted. For example, _json_, _yaml_ or _yml_. | | _`file_types: List[str]`_ | This is a requirement if the _`field_type`_ is _file_. Defines which file types will be accepted. For example, _json_, _yaml_ or _yml_. |
| _`range_spec: langflow.field_typing.RangeSpec`_ | This is a requirement if the _`field_type`_ is _`float`_. Defines the range of values accepted and the step size. If none is defined, the default is _`[-1, 1, 0.1]`_. | | _`range_spec: langflow.field_typing.RangeSpec`_ | This is a requirement if the _`field_type`_ is _`float`_. Defines the range of values accepted and the step size. If none is defined, the default is _`[-1, 1, 0.1]`_. |
| _`title_case: bool`_ | Formats the name of the field when _`display_name`_ is not defined. Set it to False to keep the name as you set it in the _`build`_ method. | | _`title_case: bool`_ | Formats the name of the field when _`display_name`_ is not defined. Set it to False to keep the name as you set it in the _`build`_ method. |
| _`refresh: bool`_ | If set to True a button will appear to the right of the field, and when clicked, it will call the _`update_build_config`_ method which takes in the _`build_config`_, the name of the field (_`field_name`_) and the latest value of the field (_`field_value`_). This is useful when you want to update the _`build_config`_ based on the value of the field. | | _`refresh_button: bool`_ | If set to True a button will appear to the right of the field, and when clicked, it will call the _`update_build_config`_ method which takes in the _`build_config`_, the name of the field (_`field_name`_) and the latest value of the field (_`field_value`_). This is useful when you want to update the _`build_config`_ based on the value of the field. |
| _`real_time_refresh: bool`_ | If set to True, the _`update_build_config`_ method will be called every time the field value changes. |
<Admonition type="info" label="Tip"> <Admonition type="info" label="Tip">

View file

@ -468,7 +468,9 @@ def update_field_dict(
call: bool = False, call: bool = False,
): ):
"""Update the field dictionary by calling options() or value() if they are callable""" """Update the field dictionary by calling options() or value() if they are callable"""
if "refresh" in field_dict: if ("real_time_refresh" in field_dict or "refresh_button" in field_dict) and any(
(field_dict["real_time_refresh"], field_dict["refresh_button"])
):
if call: if call:
try: try:
dd_build_config = dotdict(build_config) dd_build_config = dotdict(build_config)
@ -481,7 +483,6 @@ def update_field_dict(
raise UpdateBuildConfigError( raise UpdateBuildConfigError(
f"Error while running update_build_config: {str(exc)}" f"Error while running update_build_config: {str(exc)}"
) from exc ) from exc
field_dict["refresh"] = True
# Let's check if "range_spec" is a RangeSpec object # Let's check if "range_spec" is a RangeSpec object
if "rangeSpec" in field_dict and isinstance(field_dict["rangeSpec"], RangeSpec): if "rangeSpec" in field_dict and isinstance(field_dict["rangeSpec"], RangeSpec):

View file

@ -65,10 +65,17 @@ class TemplateField(BaseModel):
info: Optional[str] = "" info: Optional[str] = ""
"""Additional information about the field to be shown in the tooltip. Defaults to an empty string.""" """Additional information about the field to be shown in the tooltip. Defaults to an empty string."""
refresh: Optional[bool] = None real_time_refresh: Optional[bool] = None
"""Specifies if the field should be refreshed. Defaults to False.""" """Specifies if the field should have real time refresh. `refresh_button` must be False. Defaults to None."""
range_spec: Optional[RangeSpec] = Field(default=None, serialization_alias="rangeSpec") refresh_button: Optional[bool] = None
"""Specifies if the field should have a refresh button. Defaults to False."""
refresh_button_text: Optional[str] = None
"""Specifies the text for the refresh button. Defaults to None."""
range_spec: Optional[RangeSpec] = Field(
default=None, serialization_alias="rangeSpec"
)
"""Range specification for the field. Defaults to None.""" """Range specification for the field. Defaults to None."""
title_case: bool = False title_case: bool = False
@ -117,6 +124,10 @@ class TemplateField(BaseModel):
if not isinstance(value, list): if not isinstance(value, list):
raise ValueError("file_types must be a list") raise ValueError("file_types must be a list")
return [ return [
(f".{file_type}" if isinstance(file_type, str) and not file_type.startswith(".") else file_type) (
f".{file_type}"
if isinstance(file_type, str) and not file_type.startswith(".")
else file_type
)
for file_type in value for file_type in value
] ]

View file

@ -88,10 +88,36 @@ export default function ParameterComponent({
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const handleRefreshButtonPress = async (name, data) => {
setIsLoading(true);
try {
let newTemplate = await handleUpdateValues(name, data);
if (newTemplate) {
setNode(data.id, (oldNode) => {
let newNode = cloneDeep(oldNode);
newNode.data = {
...newNode.data,
};
newNode.data.node.template = newTemplate;
return newNode;
});
}
} catch (error) {
let responseError = error as ResponseErrorTypeAPI;
setErrorData({
title: "Error while updating the Component",
list: [responseError.response.data.detail.error ?? "Unknown error"],
});
}
setIsLoading(false);
renderTooltips();
};
useEffect(() => { useEffect(() => {
async function fetchData() { async function fetchData() {
if ( if (
data.node?.template[name]?.refresh && (data.node?.template[name]?.real_time_refresh ||
data.node?.template[name]?.refresh_button) &&
// options can be undefined but not an empty array // options can be undefined but not an empty array
(data.node?.template[name]?.options?.length ?? 0) === 0 (data.node?.template[name]?.options?.length ?? 0) === 0
) { ) {
@ -128,7 +154,8 @@ export default function ParameterComponent({
takeSnapshot(); takeSnapshot();
} }
const shouldUpdate = const shouldUpdate =
data.node?.template[name].refresh && data.node?.template[name].real_time_refresh &&
!data.node?.template[name].refresh_button &&
data.node!.template[name].value !== newValue; data.node!.template[name].value !== newValue;
data.node!.template[name].value = newValue; // necessary to enable ctrl+z inside the input data.node!.template[name].value = newValue; // necessary to enable ctrl+z inside the input
@ -154,7 +181,7 @@ export default function ParameterComponent({
...newNode.data, ...newNode.data,
}; };
if (data.node?.template[name].refresh && newTemplate) { if (data.node?.template[name].real_time_refresh && newTemplate) {
newNode.data.node.template = newTemplate; newNode.data.node.template = newTemplate;
} else newNode.data.node.template[name].value = newValue; } else newNode.data.node.template[name].value = newValue;
@ -458,7 +485,7 @@ export default function ParameterComponent({
} }
onChange={handleOnNewValue} onChange={handleOnNewValue}
/> />
{/* {data.node?.template[name].refresh && ( {/* {data.node?.template[name].refresh_button && (
<div className="w-1/6"> <div className="w-1/6">
<RefreshButton <RefreshButton
isLoading={isLoading} isLoading={isLoading}
@ -466,26 +493,47 @@ export default function ParameterComponent({
name={name} name={name}
data={data} data={data}
className="extra-side-bar-buttons ml-2 mt-1" className="extra-side-bar-buttons ml-2 mt-1"
handleUpdateValues={handleUpdateValues} handleUpdateValues={handleRefreshButtonPress}
id={"refresh-button-" + name} id={"refresh-button-" + name}
/> />
</div> </div>
)} */} )} */}
</div> </div>
) : data.node?.template[name].multiline ? ( ) : data.node?.template[name].multiline ? (
<TextAreaComponent <div className="mt-2 flex w-full flex-col ">
disabled={disabled} <div className="flex-grow">
value={data.node.template[name].value ?? ""} <TextAreaComponent
onChange={handleOnNewValue} disabled={disabled}
id={"textarea-" + data.node.template[name].name} value={data.node.template[name].value ?? ""}
data-testid={"textarea-" + data.node.template[name].name} onChange={handleOnNewValue}
/> id={"textarea-" + data.node.template[name].name}
data-testid={"textarea-" + data.node.template[name].name}
/>
</div>
{data.node?.template[name].refresh_button && (
<div className="flex-grow">
<RefreshButton
isLoading={isLoading}
disabled={disabled}
name={name}
data={data}
button_text={
data.node?.template[name].refresh_button_text ??
"Refresh"
}
className="extra-side-bar-buttons mt-1"
handleUpdateValues={handleRefreshButtonPress}
id={"refresh-button-" + name}
/>
</div>
)}
</div>
) : ( ) : (
<div className="mt-2 flex w-full items-center"> <div className="mt-2 flex w-full items-center">
<div <div
className={ className={
"flex-grow " + "flex-grow " +
(data.node?.template[name].refresh ? "w-5/6" : "") (data.node?.template[name].refresh_button ? "w-5/6" : "")
} }
> >
<InputComponent <InputComponent
@ -496,15 +544,19 @@ export default function ParameterComponent({
onChange={handleOnNewValue} onChange={handleOnNewValue}
/> />
</div> </div>
{data.node?.template[name].refresh && ( {data.node?.template[name].refresh_button && (
<div className="w-1/6"> <div className="w-1/6">
<RefreshButton <RefreshButton
isLoading={isLoading} isLoading={isLoading}
disabled={disabled} disabled={disabled}
name={name} name={name}
data={data} data={data}
button_text={
data.node?.template[name].refresh_button_text ??
"Refresh"
}
className="extra-side-bar-buttons ml-2 mt-1" className="extra-side-bar-buttons ml-2 mt-1"
handleUpdateValues={handleUpdateValues} handleUpdateValues={handleRefreshButtonPress}
id={"refresh-button-" + name} id={"refresh-button-" + name}
/> />
</div> </div>
@ -535,7 +587,7 @@ export default function ParameterComponent({
) : left === true && ) : left === true &&
type === "str" && type === "str" &&
(data.node?.template[name].options || (data.node?.template[name].options ||
data.node?.template[name]?.refresh) ? ( data.node?.template[name]?.real_time_refresh) ? (
// TODO: Improve CSS // TODO: Improve CSS
<div className="mt-2 flex w-full items-center"> <div className="mt-2 flex w-full items-center">
<div className="w-5/6 flex-grow"> <div className="w-5/6 flex-grow">
@ -548,15 +600,18 @@ export default function ParameterComponent({
id={"dropdown-" + name} id={"dropdown-" + name}
/> />
</div> </div>
{data.node?.template[name].refresh && ( {data.node?.template[name].refresh_button && (
<div className="w-1/6"> <div className="w-1/6">
<RefreshButton <RefreshButton
isLoading={isLoading} isLoading={isLoading}
disabled={disabled} disabled={disabled}
name={name} name={name}
data={data} data={data}
button_text={
data.node?.template[name].refresh_button_text ?? "Refresh"
}
className="extra-side-bar-buttons ml-2 mt-1" className="extra-side-bar-buttons ml-2 mt-1"
handleUpdateValues={handleUpdateValues} handleUpdateValues={handleRefreshButtonPress}
id={"refresh-button-" + name} id={"refresh-button-" + name}
/> />
</div> </div>

View file

@ -7,6 +7,7 @@ function RefreshButton({
isLoading, isLoading,
disabled, disabled,
name, name,
button_text,
data, data,
handleUpdateValues, handleUpdateValues,
className, className,
@ -15,6 +16,7 @@ function RefreshButton({
isLoading: boolean; isLoading: boolean;
disabled: boolean; disabled: boolean;
name: string; name: string;
button_text: string;
data: NodeDataType; data: NodeDataType;
className?: string; className?: string;
handleUpdateValues: (name: string, data: NodeDataType) => void; handleUpdateValues: (name: string, data: NodeDataType) => void;
@ -43,6 +45,7 @@ function RefreshButton({
onClick={handleClick} onClick={handleClick}
id={id} id={id}
> >
<span className="mr-1">{button_text}</span>
<IconComponent <IconComponent
name={isLoading ? "Loader2" : "RefreshCcw"} name={isLoading ? "Loader2" : "RefreshCcw"}
className={iconClassName} className={iconClassName}

View file

@ -55,7 +55,9 @@ export type TemplateVariableType = {
input_types?: Array<string>; input_types?: Array<string>;
display_name?: string; display_name?: string;
name?: string; name?: string;
refresh?: boolean; real_time_refresh?: boolean;
refresh_button?: boolean;
refresh_button_text?: string;
[key: string]: any; [key: string]: any;
}; };
export type sendAllProps = { export type sendAllProps = {