Merge remote-tracking branch 'origin/dev' into authentication

This commit is contained in:
gustavoschaedler 2023-08-09 21:46:08 +01:00
commit f065a46950
36 changed files with 865 additions and 564 deletions

View file

@ -1,7 +1,7 @@
from typing import TYPE_CHECKING
from langflow.utils.logger import logger
from contextlib import contextmanager
from alembic.util.exc import CommandError
from sqlmodel import Session
if TYPE_CHECKING:
@ -13,7 +13,23 @@ def initialize_database():
from langflow.services import service_manager, ServiceType
database_manager = service_manager.get(ServiceType.DATABASE_MANAGER)
database_manager.run_migrations()
try:
database_manager.run_migrations()
except CommandError as exc:
if "Can't locate revision identified by" not in str(exc):
raise exc
# This means there's wrong revision in the DB
# We need to delete the alembic_version table
# and run the migrations again
logger.warning(
"Wrong revision in DB, deleting alembic_version table and running migrations again"
)
with session_getter(database_manager) as session:
session.execute("DROP TABLE alembic_version")
database_manager.run_migrations()
except Exception as exc:
logger.error(f"Error running migrations: {exc}")
raise RuntimeError("Error running migrations") from exc
database_manager.create_db_and_tables()
logger.debug("Database initialized")

View file

@ -30,7 +30,6 @@ class DocumentLoaderFrontNode(FrontendNode):
"UnstructuredEmailLoader": build_file_field(
suffixes=[".eml"], fileTypes=["eml"]
),
"SlackDirectoryLoader": build_file_field(suffixes=[".zip"], fileTypes=["zip"]),
"EverNoteLoader": build_file_field(suffixes=[".xml"], fileTypes=["xml"]),
"FacebookChatLoader": build_file_field(suffixes=[".json"], fileTypes=["json"]),
"BSHTMLLoader": build_file_field(suffixes=[".html"], fileTypes=["html"]),
@ -105,7 +104,30 @@ class DocumentLoaderFrontNode(FrontendNode):
advanced=False,
)
)
elif self.template.type_name in {"SlackDirectoryLoader"}:
self.template.add_field(
TemplateField(
field_type="file",
required=True,
show=True,
name="zip_path",
value="",
display_name="Path to zip file",
suffixes=[".zip"],
file_types=["zip"],
)
)
self.template.add_field(
TemplateField(
field_type="str",
required=False,
show=True,
name="workspace_url",
value="",
display_name="Workspace URL",
advanced=False,
)
)
elif self.template.type_name in self.file_path_templates:
self.template.add_field(self.file_path_templates[self.template.type_name])
elif self.template.type_name in {

View file

@ -1,5 +1,11 @@
import { cloneDeep } from "lodash";
import React, { useContext, useEffect, useRef, useState } from "react";
import React, {
ReactNode,
useContext,
useEffect,
useRef,
useState,
} from "react";
import { Handle, Position, useUpdateNodeInternals } from "reactflow";
import ShadTooltip from "../../../../components/ShadTooltipComponent";
import CodeAreaComponent from "../../../../components/codeAreaComponent";
@ -17,6 +23,7 @@ import { TOOLTIP_EMPTY } from "../../../../constants/constants";
import { TabsContext } from "../../../../contexts/tabsContext";
import { typesContext } from "../../../../contexts/typesContext";
import { ParameterComponentType } from "../../../../types/components";
import { TabsState } from "../../../../types/tabs";
import { isValidConnection } from "../../../../utils/reactflowUtils";
import {
nodeColors,
@ -38,15 +45,15 @@ export default function ParameterComponent({
required = false,
optionalHandle = null,
info = "",
}: ParameterComponentType) {
const ref = useRef(null);
const refHtml = useRef(null);
const infoHtml = useRef(null);
}: ParameterComponentType): JSX.Element {
const ref = useRef<HTMLDivElement>(null);
const refHtml = useRef<HTMLDivElement & ReactNode>(null);
const infoHtml = useRef<HTMLDivElement & ReactNode>(null);
const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0);
const { setTabsState, tabId, save, flows } = useContext(TabsContext);
const flow = flows.find((f) => f.id === tabId).data?.nodes ?? null;
const flow = flows.find((flow) => flow.id === tabId)?.data?.nodes ?? null;
// Update component position
useEffect(() => {
@ -62,16 +69,18 @@ export default function ParameterComponent({
const { reactFlowInstance } = useContext(typesContext);
let disabled =
reactFlowInstance?.getEdges().some((e) => e.targetHandle === id) ?? false;
reactFlowInstance?.getEdges().some((edge) => edge.targetHandle === id) ??
false;
const { data: myData } = useContext(typesContext);
const handleOnNewValue = (newValue: any) => {
const handleOnNewValue = (newValue: string | string[] | boolean): void => {
let newData = cloneDeep(data);
newData.node.template[name].value = newValue;
newData.node!.template[name].value = newValue;
setData(newData);
// Set state to pending
setTabsState((prev) => {
//@ts-ignore
setTabsState((prev: TabsState) => {
return {
...prev,
[tabId]: {
@ -86,10 +95,11 @@ export default function ParameterComponent({
useEffect(() => {
if (name === "openai_api_base") console.log(info);
// @ts-ignore
infoHtml.current = (
<div className="h-full w-full word-break-break-word">
{info.split("\n").map((line, i) => (
<p key={i} className="block">
<div className="h-full w-full break-words">
{info.split("\n").map((line, index) => (
<p key={index} className="block">
{line}
</p>
))}
@ -98,18 +108,19 @@ export default function ParameterComponent({
}, [info]);
function renderTooltips() {
let groupedObj = groupByFamily(myData, tooltipTitle, left, flow);
let groupedObj = groupByFamily(myData, tooltipTitle!, left, flow!);
if (groupedObj && groupedObj.length > 0) {
refHtml.current = groupedObj.map((item, i) => {
//@ts-ignore
refHtml.current = groupedObj.map((item, index) => {
const Icon: any =
nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"];
return (
<span
key={i}
key={index}
className={classNames(
i > 0 ? "mt-2 flex items-center" : "flex items-center"
index > 0 ? "mt-2 flex items-center" : "flex items-center"
)}
>
<div
@ -132,10 +143,10 @@ export default function ParameterComponent({
{" "}
{item.type === "" ? "" : " - "}
{item.type.split(", ").length > 2
? item.type.split(", ").map((el, i) => (
<React.Fragment key={el + i}>
? item.type.split(", ").map((el, index) => (
<React.Fragment key={el + index}>
<span>
{i === item.type.split(", ").length - 1
{index === item.type.split(", ").length - 1
? el
: (el += `, `)}
</span>
@ -148,6 +159,7 @@ export default function ParameterComponent({
);
});
} else {
//@ts-ignore
refHtml.current = <span>{TOOLTIP_EMPTY}</span>;
}
}
@ -207,7 +219,7 @@ export default function ParameterComponent({
position={left ? Position.Left : Position.Right}
id={id}
isValidConnection={(connection) =>
isValidConnection(connection, reactFlowInstance)
isValidConnection(connection, reactFlowInstance!)
}
className={classNames(
left ? "-ml-0.5 " : "-mr-0.5 ",
@ -223,9 +235,9 @@ export default function ParameterComponent({
{left === true &&
type === "str" &&
!data.node.template[name].options ? (
!data.node?.template[name].options ? (
<div className="mt-2 w-full">
{data.node.template[name].list ? (
{data.node?.template[name].list ? (
<InputListComponent
disabled={disabled}
value={
@ -236,7 +248,7 @@ export default function ParameterComponent({
}
onChange={handleOnNewValue}
/>
) : data.node.template[name].multiline ? (
) : data.node?.template[name].multiline ? (
<TextAreaComponent
disabled={disabled}
value={data.node.template[name].value ?? ""}
@ -245,8 +257,8 @@ export default function ParameterComponent({
) : (
<InputComponent
disabled={disabled}
password={data.node.template[name].password ?? false}
value={data.node.template[name].value ?? ""}
password={data.node?.template[name].password ?? false}
value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue}
/>
)}
@ -255,9 +267,9 @@ export default function ParameterComponent({
<div className="mt-2 w-full">
<ToggleShadComponent
disabled={disabled}
enabled={data.node.template[name].value ?? false}
setEnabled={(t) => {
handleOnNewValue(t);
enabled={data.node?.template[name].value ?? false}
setEnabled={(isEnabled) => {
handleOnNewValue(isEnabled);
}}
size="large"
/>
@ -266,13 +278,13 @@ export default function ParameterComponent({
<div className="mt-2 w-full">
<FloatComponent
disabled={disabled}
value={data.node.template[name].value ?? ""}
value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue}
/>
</div>
) : left === true &&
type === "str" &&
data.node.template[name].options ? (
data.node?.template[name].options ? (
<div className="mt-2 w-full">
<Dropdown
options={data.node.template[name].options}
@ -283,13 +295,13 @@ export default function ParameterComponent({
) : left === true && type === "code" ? (
<div className="mt-2 w-full">
<CodeAreaComponent
dynamic={data.node.template[name].dynamic ?? false}
dynamic={data.node?.template[name].dynamic ?? false}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
}}
nodeClass={data.node}
disabled={disabled}
value={data.node.template[name].value ?? ""}
value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue}
/>
</div>
@ -297,12 +309,12 @@ export default function ParameterComponent({
<div className="mt-2 w-full">
<InputFileComponent
disabled={disabled}
value={data.node.template[name].value ?? ""}
value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue}
fileTypes={data.node.template[name].fileTypes}
suffixes={data.node.template[name].suffixes}
onFileChange={(t: string) => {
data.node.template[name].file_path = t;
fileTypes={data.node?.template[name].fileTypes}
suffixes={data.node?.template[name].suffixes}
onFileChange={(filePath: string) => {
data.node!.template[name].file_path = filePath;
save();
}}
></InputFileComponent>
@ -311,7 +323,7 @@ export default function ParameterComponent({
<div className="mt-2 w-full">
<IntComponent
disabled={disabled}
value={data.node.template[name].value ?? ""}
value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue}
/>
</div>
@ -324,7 +336,7 @@ export default function ParameterComponent({
}}
nodeClass={data.node}
disabled={disabled}
value={data.node.template[name].value ?? ""}
value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue}
/>
</div>

View file

@ -159,52 +159,59 @@ export default function GenericNode({
<>
{Object.keys(data.node.template)
.filter((t) => t.charAt(0) !== "_")
.map((t: string, idx) => (
.filter((templateField) => templateField.charAt(0) !== "_")
.map((templateField: string, idx) => (
<div key={idx}>
{data.node.template[t].show &&
!data.node.template[t].advanced ? (
{data.node.template[templateField].show &&
!data.node.template[templateField].advanced ? (
<ParameterComponent
key={
(data.node.template[t].input_types?.join(";") ??
data.node.template[t].type) +
(data.node.template[templateField].input_types?.join(
";"
) ?? data.node.template[templateField].type) +
"|" +
t +
templateField +
"|" +
data.id
}
data={data}
setData={setData}
color={
nodeColors[types[data.node.template[t].type]] ??
nodeColors[data.node.template[t].type] ??
nodeColors[
types[data.node.template[templateField].type]
] ??
nodeColors[data.node.template[templateField].type] ??
nodeColors.unknown
}
title={
data.node.template[t].display_name
? data.node.template[t].display_name
: data.node.template[t].name
? toTitleCase(data.node.template[t].name)
: toTitleCase(t)
data.node.template[templateField].display_name
? data.node.template[templateField].display_name
: data.node.template[templateField].name
? toTitleCase(data.node.template[templateField].name)
: toTitleCase(templateField)
}
info={data.node.template[t].info}
name={t}
info={data.node.template[templateField].info}
name={templateField}
tooltipTitle={
data.node.template[t].input_types?.join("\n") ??
data.node.template[t].type
data.node.template[templateField].input_types?.join(
"\n"
) ?? data.node.template[templateField].type
}
required={data.node.template[t].required}
required={data.node.template[templateField].required}
id={
(data.node.template[t].input_types?.join(";") ??
data.node.template[t].type) +
(data.node.template[templateField].input_types?.join(
";"
) ?? data.node.template[templateField].type) +
"|" +
t +
templateField +
"|" +
data.id
}
left={true}
type={data.node.template[t].type}
optionalHandle={data.node.template[t].input_types}
type={data.node.template[templateField].type}
optionalHandle={
data.node.template[templateField].input_types
}
/>
) : (
<></>

View file

@ -22,9 +22,9 @@ export default function AlertDropdown({ children }: AlertDropdownType) {
return (
<Popover
open={open}
onOpenChange={(k) => {
setOpen(k);
if (k) setNotificationCenter(false);
onOpenChange={(target) => {
setOpen(target);
if (target) setNotificationCenter(false);
}}
>
<PopoverTrigger>{children}</PopoverTrigger>

View file

@ -55,12 +55,12 @@ export const EditFlowSettings: React.FC<InputProps> = ({
};
const [desc, setDesc] = useState(
flows.find((f) => f.id === tabId).description
flows.find((flow) => flow.id === tabId).description
);
const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
flows.find((f) => f.id === tabId).description = event.target.value;
setDesc(flows.find((f) => f.id === tabId).description);
flows.find((flow) => flow.id === tabId).description = event.target.value;
setDesc(flows.find((flow) => flow.id === tabId).description);
setDescription(event.target.value);
};

View file

@ -34,9 +34,9 @@ export default function CodeAreaComponent({
value={myValue}
nodeClass={nodeClass}
setNodeClass={setNodeClass}
setValue={(t: string) => {
setMyValue(t);
onChange(t);
setValue={(value: string) => {
setMyValue(value);
onChange(value);
}}
>
<div className="flex w-full items-center">

View file

@ -205,13 +205,15 @@ export default function CodeTabsComponent({
: "overflow-hidden"
)}
>
{data.map((t: any, index) => (
{data.map((node: any, index) => (
<div className="px-3" key={index}>
{tweaks.tweaksList.current.includes(t["data"]["id"]) && (
{tweaks.tweaksList.current.includes(
node["data"]["id"]
) && (
<AccordionComponent
trigger={t["data"]["id"]}
trigger={node["data"]["id"]}
open={openAccordion}
keyValue={t["data"]["id"]}
keyValue={node["data"]["id"]}
>
<div className="api-modal-table-arrangement">
<Table className="table-fixed bg-muted outline-1">
@ -226,78 +228,92 @@ export default function CodeTabsComponent({
</TableRow>
</TableHeader>
<TableBody className="p-0">
{Object.keys(t["data"]["node"]["template"])
{Object.keys(node["data"]["node"]["template"])
.filter(
(n) =>
n.charAt(0) !== "_" &&
t.data.node.template[n].show &&
(t.data.node.template[n].type === "str" ||
t.data.node.template[n].type ===
"bool" ||
t.data.node.template[n].type ===
"float" ||
t.data.node.template[n].type ===
"code" ||
t.data.node.template[n].type ===
"prompt" ||
t.data.node.template[n].type ===
"file" ||
t.data.node.template[n].type === "int")
(templateField) =>
templateField.charAt(0) !== "_" &&
node.data.node.template[templateField]
.show &&
(node.data.node.template[templateField]
.type === "str" ||
node.data.node.template[templateField]
.type === "bool" ||
node.data.node.template[templateField]
.type === "float" ||
node.data.node.template[templateField]
.type === "code" ||
node.data.node.template[templateField]
.type === "prompt" ||
node.data.node.template[templateField]
.type === "file" ||
node.data.node.template[templateField]
.type === "int")
)
.map((n, i) => {
.map((templateField, index) => {
return (
<TableRow
key={i}
key={index}
className="h-10 dark:border-b-muted"
>
<TableCell className="p-0 text-center text-sm text-foreground">
{n}
{templateField}
</TableCell>
<TableCell className="p-0 text-xs text-foreground">
<div className="m-auto w-[250px]">
{t.data.node.template[n].type ===
"str" &&
!t.data.node.template[n].options ? (
{node.data.node.template[
templateField
].type === "str" &&
!node.data.node.template[
templateField
].options ? (
<div className="mx-auto">
{t.data.node.template[n]
.list ? (
{node.data.node.template[
templateField
].list ? (
<InputListComponent
editNode={true}
disabled={false}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? [""]
: t.data.node.template[
n
: node.data.node
.template[
templateField
].value
}
onChange={(k) => {
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
) : t.data.node.template[n]
.multiline ? (
) : node.data.node.template[
templateField
].multiline ? (
<ShadTooltip
content={tweaks.buildContent(
t.data.node.template[n]
.value
node.data.node.template[
templateField
].value
)}
>
<div>
@ -305,33 +321,38 @@ export default function CodeTabsComponent({
disabled={false}
editNode={true}
value={
!t.data.node.template[
n
!node.data.node
.template[
templateField
].value ||
t.data.node.template[
n
node.data.node
.template[
templateField
].value === ""
? ""
: t.data.node
.template[n]
.value
: node.data.node
.template[
templateField
].value
}
onChange={(k) => {
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node
.template[n]
node["data"]["id"],
target,
node.data.node
.template[
templateField
]
);
}}
/>
@ -342,47 +363,55 @@ export default function CodeTabsComponent({
editNode={true}
disabled={false}
password={
t.data.node.template[n]
.password ?? false
node.data.node.template[
templateField
].password ?? false
}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: t.data.node.template[
n
: node.data.node
.template[
templateField
].value
}
onChange={(k) => {
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
)}
</div>
) : t.data.node.template[n].type ===
"bool" ? (
) : node.data.node.template[
templateField
].type === "bool" ? (
<div className="ml-auto">
{" "}
<ToggleShadComponent
enabled={
t.data.node.template[n]
.value
node.data.node.template[
templateField
].value
}
setEnabled={(e) => {
setData((old) => {
@ -391,31 +420,37 @@ export default function CodeTabsComponent({
newInputList[
index
].data.node.template[
n
templateField
].value = e;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
node["data"]["id"],
e,
t.data.node.template[n]
node.data.node.template[
templateField
]
);
}}
size="small"
disabled={false}
/>
</div>
) : t.data.node.template[n].type ===
"file" ? (
) : node.data.node.template[
templateField
].type === "file" ? (
<ShadTooltip
content={tweaks.buildContent(
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: t.data.node.template[n]
.value
: node.data.node.template[
templateField
].value
)}
>
<div className="mx-auto">
@ -423,147 +458,176 @@ export default function CodeTabsComponent({
editNode={true}
disabled={false}
value={
t.data.node.template[n]
.value ?? ""
node.data.node.template[
templateField
].value ?? ""
}
onChange={(k: any) => {}}
onChange={(
target: any
) => {}}
fileTypes={
t.data.node.template[n]
.fileTypes
node.data.node.template[
templateField
].fileTypes
}
suffixes={
t.data.node.template[n]
.suffixes
node.data.node.template[
templateField
].suffixes
}
onFileChange={(
value: any
) => {
t.data.node.template[
n
node.data.node.template[
templateField
].file_path = value;
}}
></InputFileComponent>
</div>
</ShadTooltip>
) : t.data.node.template[n].type ===
"float" ? (
) : node.data.node.template[
templateField
].type === "float" ? (
<div className="mx-auto">
<FloatComponent
disabled={false}
editNode={true}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: t.data.node.template[n]
.value
: node.data.node.template[
templateField
].value
}
onChange={(k) => {
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
</div>
) : t.data.node.template[n].type ===
"str" &&
t.data.node.template[n]
.options ? (
) : node.data.node.template[
templateField
].type === "str" &&
node.data.node.template[
templateField
].options ? (
<div className="mx-auto">
<Dropdown
editNode={true}
apiModal={true}
options={
t.data.node.template[n]
.options
node.data.node.template[
templateField
].options
}
onSelect={(k) => {
onSelect={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: t.data.node.template[n]
.value
: node.data.node.template[
templateField
].value
}
></Dropdown>
</div>
) : t.data.node.template[n].type ===
"int" ? (
) : node.data.node.template[
templateField
].type === "int" ? (
<div className="mx-auto">
<IntComponent
disabled={false}
editNode={true}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: t.data.node.template[n]
.value
: node.data.node.template[
templateField
].value
}
onChange={(k) => {
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
</div>
) : t.data.node.template[n].type ===
"prompt" ? (
) : node.data.node.template[
templateField
].type === "prompt" ? (
<ShadTooltip
content={tweaks.buildContent(
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: t.data.node.template[n]
.value
: node.data.node.template[
templateField
].value
)}
>
<div className="mx-auto">
@ -571,44 +635,53 @@ export default function CodeTabsComponent({
editNode={true}
disabled={false}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: t.data.node.template[
n
: node.data.node
.template[
templateField
].value
}
onChange={(k) => {
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
</div>
</ShadTooltip>
) : t.data.node.template[n].type ===
"code" ? (
) : node.data.node.template[
templateField
].type === "code" ? (
<ShadTooltip
content={tweaks.buildContent(
tweaks.getValue(
t.data.node.template[n]
.value,
t.data,
t.data.node.template[n]
node.data.node.template[
templateField
].value,
node.data,
node.data.node.template[
templateField
]
)
)}
>
@ -617,37 +690,43 @@ export default function CodeTabsComponent({
disabled={false}
editNode={true}
value={
!t.data.node.template[n]
.value ||
t.data.node.template[n]
.value === ""
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: t.data.node.template[
n
: node.data.node
.template[
templateField
].value
}
onChange={(k) => {
onChange={(target) => {
setData((old) => {
let newInputList =
cloneDeep(old);
newInputList[
index
].data.node.template[
n
].value = k;
templateField
].value = target;
return newInputList;
});
tweaks.buildTweakObject(
t["data"]["id"],
k,
t.data.node.template[n]
node["data"]["id"],
target,
node.data.node.template[
templateField
]
);
}}
/>
</div>
</ShadTooltip>
) : t.data.node.template[n].type ===
"Any" ? (
) : node.data.node.template[
templateField
].type === "Any" ? (
"-"
) : (
<div className="hidden"></div>

View file

@ -25,12 +25,12 @@ export default function FloatComponent({
type="number"
step={step}
min={min}
onInput={(e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.value < min.toString()) {
e.target.value = min.toString();
onInput={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.value < min.toString()) {
event.target.value = min.toString();
}
if (e.target.value > max.toString()) {
e.target.value = max.toString();
if (event.target.value > max.toString()) {
event.target.value = max.toString();
}
}}
max={max}
@ -40,8 +40,8 @@ export default function FloatComponent({
placeholder={
editNode ? "Number 0 to 1" : "Type a number from zero to one"
}
onChange={(e) => {
onChange(e.target.value);
onChange={(event) => {
onChange(event.target.value);
}}
/>
</div>

View file

@ -34,9 +34,8 @@ export default function Header() {
<Link to="/">
<span className="ml-4 text-2xl"></span>
</Link>
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
<MenuBar flows={flows} tabId={tabId} />
)}
{flows.findIndex((flow) => tabId === flow.id) !== -1 &&
tabId !== "" && <MenuBar flows={flows} tabId={tabId} />}
</div>
<div className="round-button-div">
<Link to="/">

View file

@ -31,8 +31,8 @@ export default function InputComponent({
password && !editNode ? "pr-10" : ""
)}
placeholder={password && editNode ? "Key" : "Type something..."}
onChange={(e) => {
onChange(e.target.value);
onChange={(event) => {
onChange(event.target.value);
}}
/>
{password && (

View file

@ -49,11 +49,11 @@ export default function InputFileComponent({
input.style.display = "none"; // Hidden from view
input.multiple = false; // Allow only one file selection
input.onchange = (e: Event) => {
input.onchange = (event: Event) => {
setLoading(true);
// Get the selected file
const file = (e.target as HTMLInputElement).files?.[0];
const file = (event.target as HTMLInputElement).files?.[0];
// Check if the file type is correct
if (file && checkFileType(file.name)) {

View file

@ -25,18 +25,18 @@ export default function InputListComponent({
"flex flex-col gap-3"
)}
>
{value.map((i, idx) => {
{value.map((singleValue, idx) => {
return (
<div key={idx} className="flex w-full gap-3">
<Input
disabled={disabled}
type="text"
value={i}
value={singleValue}
className={editNode ? "input-edit-node" : ""}
placeholder="Type something..."
onChange={(e) => {
onChange={(event) => {
let newInputList = _.cloneDeep(value);
newInputList[idx] = e.target.value;
newInputList[idx] = event.target.value;
onChange(newInputList);
}}
/>

View file

@ -41,17 +41,17 @@ export default function IntComponent({
type="number"
step="1"
min={min}
onInput={(e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.value < min.toString()) {
e.target.value = min.toString();
onInput={(event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.value < min.toString()) {
event.target.value = min.toString();
}
}}
value={value ?? ""}
className={editNode ? "input-edit-node" : ""}
disabled={disabled}
placeholder={editNode ? "Integer number" : "Type an integer number"}
onChange={(e) => {
onChange(e.target.value);
onChange={(event) => {
onChange(event.target.value);
}}
/>
</div>

View file

@ -39,8 +39,8 @@ export default function PromptAreaComponent({
value={value}
buttonText="Check & Save"
modalTitle="Edit Prompt"
setValue={(t: string) => {
onChange(t);
setValue={(value: string) => {
onChange(value);
}}
nodeClass={nodeClass}
setNodeClass={setNodeClass}

View file

@ -25,8 +25,8 @@ export default function TextAreaComponent({
disabled={disabled}
className={editNode ? "input-edit-node" : ""}
placeholder={"Type something..."}
onChange={(e) => {
onChange(e.target.value);
onChange={(event) => {
onChange(event.target.value);
}}
/>
<div>
@ -35,8 +35,8 @@ export default function TextAreaComponent({
buttonText="Finishing Editing"
modalTitle="Edit Text"
value={value}
setValue={(t: string) => {
onChange(t);
setValue={(value: string) => {
onChange(value);
}}
>
{!editNode && (

View file

@ -18,8 +18,8 @@ export default function ToggleComponent({
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
<Switch
checked={enabled}
onChange={(x: boolean) => {
setEnabled(x);
onChange={(isEnabled: boolean) => {
setEnabled(isEnabled);
}}
className={classNames(
enabled ? "bg-primary" : "bg-input",

View file

@ -35,8 +35,8 @@ export default function ToggleShadComponent({
disabled={disabled}
className=""
checked={enabled}
onCheckedChange={(x: boolean) => {
setEnabled(x);
onCheckedChange={(isEnabled: boolean) => {
setEnabled(isEnabled);
}}
></Switch>
</div>

View file

@ -15,8 +15,8 @@ export default function RenameLabel(props) {
useEffect(() => {
if (isRename) {
setMyValue(props.value);
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
setIsRename(false);
props.setValue("");
}
@ -67,8 +67,8 @@ export default function RenameLabel(props) {
}
}}
value={myValue}
onChange={(e) => {
setMyValue(e.target.value);
onChange={(event) => {
setMyValue(event.target.value);
}}
/>
) : (

View file

@ -508,3 +508,84 @@ export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
"/api/v1/custom_component",
"/api/v1/validate/prompt",
];
export const tabsCode = [];
export function tabsArray(codes: string[], method: number) {
if (!method) return;
if (method === 0) {
return [
{
name: "cURL",
mode: "bash",
image: "https://curl.se/logo/curl-symbol-transparent.png",
language: "sh",
code: codes[0],
},
{
name: "Python API",
mode: "python",
image:
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
language: "py",
code: codes[1],
},
{
name: "Python Code",
mode: "python",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[2],
},
{
name: "Chat Widget HTML",
description:
"Insert this code anywhere in your &lt;body&gt; tag. To use with react and other libs, check our <a class='link-color' href='https://langflow.org/guidelines/widget'>documentation</a>.",
mode: "html",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[3],
},
];
}
return [
{
name: "cURL",
mode: "bash",
image: "https://curl.se/logo/curl-symbol-transparent.png",
language: "sh",
code: codes[0],
},
{
name: "Python API",
mode: "python",
image:
"https://images.squarespace-cdn.com/content/v1/5df3d8c5d2be5962e4f87890/1628015119369-OY4TV3XJJ53ECO0W2OLQ/Python+API+Training+Logo.png?format=1000w",
language: "py",
code: codes[1],
},
{
name: "Python Code",
mode: "python",
language: "py",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
code: codes[2],
},
{
name: "Chat Widget HTML",
description:
"Insert this code anywhere in your &lt;body&gt; tag. To use with react and other libs, check our <a class='link-color' href='https://langflow.org/guidelines/widget'>documentation</a>.",
mode: "html",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[3],
},
{
name: "Tweaks",
mode: "python",
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
language: "py",
code: codes[4],
},
];
}

View file

@ -316,11 +316,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const input = document.createElement("input");
input.type = "file";
// add a change event listener to the file input
input.onchange = (e: Event) => {
input.onchange = (event: Event) => {
// check if the file type is application/json
if ((e.target as HTMLInputElement).files[0].type === "application/json") {
if (
(event.target as HTMLInputElement).files[0].type === "application/json"
) {
// get the file from the file input
const file = (e.target as HTMLInputElement).files[0];
const file = (event.target as HTMLInputElement).files[0];
// read the file as text
const formData = new FormData();
formData.append("file", file);
@ -359,12 +361,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
let idsMap = {};
let nodes = reactFlowInstance.getNodes();
let edges = reactFlowInstance.getEdges();
selectionInstance.nodes.forEach((n) => {
if (n.position.y < minimumY) {
minimumY = n.position.y;
selectionInstance.nodes.forEach((node) => {
if (node.position.y < minimumY) {
minimumY = node.position.y;
}
if (n.position.x < minimumX) {
minimumX = n.position.x;
if (node.position.x < minimumX) {
minimumX = node.position.x;
}
});
@ -372,43 +374,43 @@ export function TabsProvider({ children }: { children: ReactNode }) {
? { x: position.paneX + position.x, y: position.paneY + position.y }
: reactFlowInstance.project({ x: position.x, y: position.y });
selectionInstance.nodes.forEach((n: NodeType) => {
selectionInstance.nodes.forEach((node: NodeType) => {
// Generate a unique node ID
let newId = getNodeId(n.data.type);
idsMap[n.id] = newId;
let newId = getNodeId(node.data.type);
idsMap[node.id] = newId;
// Create a new node object
const newNode: NodeType = {
id: newId,
type: "genericNode",
position: {
x: insidePosition.x + n.position.x - minimumX,
y: insidePosition.y + n.position.y - minimumY,
x: insidePosition.x + node.position.x - minimumX,
y: insidePosition.y + node.position.y - minimumY,
},
data: {
..._.cloneDeep(n.data),
..._.cloneDeep(node.data),
id: newId,
},
};
// Add the new node to the list of nodes in state
nodes = nodes
.map((e) => ({ ...e, selected: false }))
.map((node) => ({ ...node, selected: false }))
.concat({ ...newNode, selected: false });
});
reactFlowInstance.setNodes(nodes);
selectionInstance.edges.forEach((e) => {
let source = idsMap[e.source];
let target = idsMap[e.target];
let sourceHandleSplitted = e.sourceHandle.split("|");
selectionInstance.edges.forEach((edge) => {
let source = idsMap[edge.source];
let target = idsMap[edge.target];
let sourceHandleSplitted = edge.sourceHandle.split("|");
let sourceHandle =
sourceHandleSplitted[0] +
"|" +
source +
"|" +
sourceHandleSplitted.slice(2).join("|");
let targetHandleSplitted = e.targetHandle.split("|");
let targetHandleSplitted = edge.targetHandle.split("|");
let targetHandle =
targetHandleSplitted.slice(0, -1).join("|") + "|" + target;
let id =
@ -433,7 +435,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
animated: targetHandle.split("|")[0] === "Text",
selected: false,
},
edges.map((e) => ({ ...e, selected: false }))
edges.map((edge) => ({ ...edge, selected: false }))
);
});
reactFlowInstance.setEdges(edges);

View file

@ -88,12 +88,12 @@ export function TypesProvider({ children }: { children: ReactNode }) {
function deleteNode(idx: string) {
reactFlowInstance.setNodes(
reactFlowInstance.getNodes().filter((n: Node) => n.id !== idx)
reactFlowInstance.getNodes().filter((node: Node) => node.id !== idx)
);
reactFlowInstance.setEdges(
reactFlowInstance
.getEdges()
.filter((ns) => ns.source !== idx && ns.target !== idx)
.filter((edge) => edge.source !== idx && edge.target !== idx)
);
}
return (

View file

@ -45,14 +45,18 @@ export function UndoRedoProvider({ children }) {
const [past, setPast] = useState<HistoryItem[][]>(flows.map(() => []));
const [future, setFuture] = useState<HistoryItem[][]>(flows.map(() => []));
const [tabIndex, setTabIndex] = useState(
flows.findIndex((f) => f.id === tabId)
flows.findIndex((flow) => flow.id === tabId)
);
useEffect(() => {
// whenever the flows variable changes, we need to add one array to the past and future states
setPast((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
setFuture((old) => flows.map((f, i) => (old[i] ? old[i] : [])));
setTabIndex(flows.findIndex((f) => f.id === tabId));
setPast((old) =>
flows.map((flow, index) => (old[index] ? old[index] : []))
);
setFuture((old) =>
flows.map((flow, index) => (old[index] ? old[index] : []))
);
setTabIndex(flows.findIndex((flow) => flow.id === tabId));
}, [flows, tabId]);
const { setNodes, setEdges, getNodes, getEdges } = useReactFlow();

View file

@ -181,22 +181,22 @@ const ApiModal = forwardRef(
function filterNodes() {
let arrNodesWithValues = [];
flow["data"]["nodes"].forEach((t) => {
Object.keys(t["data"]["node"]["template"])
flow["data"]["nodes"].forEach((node) => {
Object.keys(node["data"]["node"]["template"])
.filter(
(n) =>
n.charAt(0) !== "_" &&
t.data.node.template[n].show &&
(t.data.node.template[n].type === "str" ||
t.data.node.template[n].type === "bool" ||
t.data.node.template[n].type === "float" ||
t.data.node.template[n].type === "code" ||
t.data.node.template[n].type === "prompt" ||
t.data.node.template[n].type === "file" ||
t.data.node.template[n].type === "int")
(templateField) =>
templateField.charAt(0) !== "_" &&
node.data.node.template[templateField].show &&
(node.data.node.template[templateField].type === "str" ||
node.data.node.template[templateField].type === "bool" ||
node.data.node.template[templateField].type === "float" ||
node.data.node.template[templateField].type === "code" ||
node.data.node.template[templateField].type === "prompt" ||
node.data.node.template[templateField].type === "file" ||
node.data.node.template[templateField].type === "int")
)
.map((n, i) => {
arrNodesWithValues.push(t["id"]);
arrNodesWithValues.push(node["id"]);
});
});

View file

@ -49,13 +49,15 @@ const EditNodeModal = forwardRef(
const { reactFlowInstance } = useContext(typesContext);
let disabled =
reactFlowInstance?.getEdges().some((e) => e.targetHandle === data.id) ??
false;
reactFlowInstance
?.getEdges()
.some((edge) => edge.targetHandle === data.id) ?? false;
function changeAdvanced(n) {
function changeAdvanced(templateParam) {
setMyData((old) => {
let newData = cloneDeep(old);
newData.node.template[n].advanced = !newData.node.template[n].advanced;
newData.node.template[templateParam].advanced =
!newData.node.template[templateParam].advanced;
return newData;
});
}
@ -112,51 +114,65 @@ const EditNodeModal = forwardRef(
<TableBody className="p-0">
{Object.keys(myData.node.template)
.filter(
(t) =>
t.charAt(0) !== "_" &&
myData.node.template[t].show &&
(myData.node.template[t].type === "str" ||
myData.node.template[t].type === "bool" ||
myData.node.template[t].type === "float" ||
myData.node.template[t].type === "code" ||
myData.node.template[t].type === "prompt" ||
myData.node.template[t].type === "file" ||
myData.node.template[t].type === "int")
(templateParam) =>
templateParam.charAt(0) !== "_" &&
myData.node.template[templateParam].show &&
(myData.node.template[templateParam].type ===
"str" ||
myData.node.template[templateParam].type ===
"bool" ||
myData.node.template[templateParam].type ===
"float" ||
myData.node.template[templateParam].type ===
"code" ||
myData.node.template[templateParam].type ===
"prompt" ||
myData.node.template[templateParam].type ===
"file" ||
myData.node.template[templateParam].type ===
"int")
)
.map((n, i) => (
<TableRow key={i} className="h-10">
.map((templateParam, index) => (
<TableRow key={index} className="h-10">
<TableCell className="truncate p-0 text-center text-sm text-foreground sm:px-3">
{myData.node.template[n].name
? myData.node.template[n].name
: myData.node.template[n].display_name}
{myData.node.template[templateParam].name
? myData.node.template[templateParam].name
: myData.node.template[templateParam]
.display_name}
</TableCell>
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
{myData.node.template[n].type === "str" &&
!myData.node.template[n].options ? (
{myData.node.template[templateParam].type ===
"str" &&
!myData.node.template[templateParam].options ? (
<div className="mx-auto">
{myData.node.template[n].list ? (
{myData.node.template[templateParam].list ? (
<InputListComponent
editNode={true}
disabled={disabled}
value={
!myData.node.template[n].value ||
myData.node.template[n].value === ""
!myData.node.template[templateParam]
.value ||
myData.node.template[templateParam]
.value === ""
? [""]
: myData.node.template[n].value
: myData.node.template[templateParam]
.value
}
onChange={(t: string[]) => {
handleOnNewValue(t, n);
onChange={(value: string[]) => {
handleOnNewValue(value, templateParam);
}}
/>
) : myData.node.template[n].multiline ? (
) : myData.node.template[templateParam]
.multiline ? (
<TextAreaComponent
disabled={disabled}
editNode={true}
value={
myData.node.template[n].value ?? ""
myData.node.template[templateParam]
.value ?? ""
}
onChange={(t: string) => {
handleOnNewValue(t, n);
onChange={(value: string) => {
handleOnNewValue(value, templateParam);
}}
/>
) : (
@ -164,105 +180,142 @@ const EditNodeModal = forwardRef(
editNode={true}
disabled={disabled}
password={
myData.node.template[n].password ??
false
myData.node.template[templateParam]
.password ?? false
}
value={
myData.node.template[n].value ?? ""
myData.node.template[templateParam]
.value ?? ""
}
onChange={(t) => {
handleOnNewValue(t, n);
onChange={(value) => {
handleOnNewValue(value, templateParam);
}}
/>
)}
</div>
) : myData.node.template[n].type === "bool" ? (
) : myData.node.template[templateParam].type ===
"bool" ? (
<div className="ml-auto">
{" "}
<ToggleShadComponent
disabled={disabled}
enabled={myData.node.template[n].value}
setEnabled={(t) => {
handleOnNewValue(t, n);
enabled={
myData.node.template[templateParam].value
}
setEnabled={(isEnabled) => {
handleOnNewValue(
isEnabled,
templateParam
);
}}
size="small"
/>
</div>
) : myData.node.template[n].type === "float" ? (
) : myData.node.template[templateParam].type ===
"float" ? (
<div className="mx-auto">
<FloatComponent
disabled={disabled}
editNode={true}
value={myData.node.template[n].value ?? ""}
onChange={(t) => {
handleOnNewValue(t, n);
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value) => {
handleOnNewValue(value, templateParam);
}}
/>
</div>
) : myData.node.template[n].type === "str" &&
myData.node.template[n].options ? (
) : myData.node.template[templateParam].type ===
"str" &&
myData.node.template[templateParam].options ? (
<div className="mx-auto">
<Dropdown
numberOfOptions={nodeLength}
editNode={true}
options={myData.node.template[n].options}
onSelect={(t) => handleOnNewValue(t, n)}
options={
myData.node.template[templateParam]
.options
}
onSelect={(value) =>
handleOnNewValue(value, templateParam)
}
value={
myData.node.template[n].value ??
"Choose an option"
myData.node.template[templateParam]
.value ?? "Choose an option"
}
></Dropdown>
</div>
) : myData.node.template[n].type === "int" ? (
) : myData.node.template[templateParam].type ===
"int" ? (
<div className="mx-auto">
<IntComponent
disabled={disabled}
editNode={true}
value={myData.node.template[n].value ?? ""}
onChange={(t) => {
handleOnNewValue(t, n);
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value) => {
handleOnNewValue(value, templateParam);
}}
/>
</div>
) : myData.node.template[n].type === "file" ? (
) : myData.node.template[templateParam].type ===
"file" ? (
<div className="mx-auto">
<InputFileComponent
editNode={true}
disabled={disabled}
value={myData.node.template[n].value ?? ""}
onChange={(t: string) => {
handleOnNewValue(t, n);
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string) => {
handleOnNewValue(value, templateParam);
}}
fileTypes={
myData.node.template[n].fileTypes
myData.node.template[templateParam]
.fileTypes
}
suffixes={myData.node.template[n].suffixes}
onFileChange={(t: string) => {
data.node.template[n].file_path = t;
suffixes={
myData.node.template[templateParam]
.suffixes
}
onFileChange={(filePath: string) => {
data.node.template[
templateParam
].file_path = filePath;
}}
></InputFileComponent>
</div>
) : myData.node.template[n].type === "prompt" ? (
) : myData.node.template[templateParam].type ===
"prompt" ? (
<div className="mx-auto">
<PromptAreaComponent
field_name={n}
field_name={templateParam}
editNode={true}
disabled={disabled}
nodeClass={myData.node}
setNodeClass={(nodeClass) => {
myData.node = nodeClass;
}}
value={myData.node.template[n].value ?? ""}
onChange={(t: string) => {
handleOnNewValue(t, n);
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string) => {
handleOnNewValue(value, templateParam);
}}
/>
</div>
) : myData.node.template[n].type === "code" ? (
) : myData.node.template[templateParam].type ===
"code" ? (
<div className="mx-auto">
<CodeAreaComponent
dynamic={
data.node.template[n].dynamic ?? false
data.node.template[templateParam]
.dynamic ?? false
}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
@ -270,13 +323,17 @@ const EditNodeModal = forwardRef(
nodeClass={data.node}
disabled={disabled}
editNode={true}
value={myData.node.template[n].value ?? ""}
onChange={(t: string) => {
handleOnNewValue(t, n);
value={
myData.node.template[templateParam]
.value ?? ""
}
onChange={(value: string) => {
handleOnNewValue(value, templateParam);
}}
/>
</div>
) : myData.node.template[n].type === "Any" ? (
) : myData.node.template[templateParam].type ===
"Any" ? (
"-"
) : (
<div className="hidden"></div>
@ -285,8 +342,13 @@ const EditNodeModal = forwardRef(
<TableCell className="p-0 text-right">
<div className="items-center text-center">
<ToggleShadComponent
enabled={!myData.node.template[n].advanced}
setEnabled={(e) => changeAdvanced(n)}
enabled={
!myData.node.template[templateParam]
.advanced
}
setEnabled={(e) =>
changeAdvanced(templateParam)
}
disabled={disabled}
size="small"
/>

View file

@ -12,10 +12,12 @@ const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
const { flows, tabId, updateFlow, downloadFlow, saveFlow } =
useContext(TabsContext);
const [checked, setChecked] = useState(false);
const [name, setName] = useState(flows.find((f) => f.id === tabId).name);
const [name, setName] = useState(
flows.find((flow) => flow.id === tabId).name
);
const [invalidName, setInvalidName] = useState(false);
const [description, setDescription] = useState(
flows.find((f) => f.id === tabId).description
flows.find((flow) => flow.id === tabId).description
);
const [open, setOpen] = useState(false);
return (
@ -59,13 +61,13 @@ const ExportModal = forwardRef((props: { children: ReactNode }, ref) => {
onClick={() => {
if (checked)
downloadFlow(
flows.find((f) => f.id === tabId),
flows.find((flow) => flow.id === tabId),
name,
description
);
else
downloadFlow(
removeApiKeys(flows.find((f) => f.id === tabId)),
removeApiKeys(flows.find((flow) => flow.id === tabId)),
name,
description
);

View file

@ -19,14 +19,16 @@ export default function FlowSettingsModal({
const { flows, tabId, updateFlow, setTabsState, saveFlow } =
useContext(TabsContext);
const maxLength = 50;
const [name, setName] = useState(flows.find((f) => f.id === tabId).name);
const [name, setName] = useState(
flows.find((flow) => flow.id === tabId).name
);
const [description, setDescription] = useState(
flows.find((f) => f.id === tabId).description
flows.find((flow) => flow.id === tabId).description
);
const [invalidName, setInvalidName] = useState(false);
function handleClick() {
let savedFlow = flows.find((f) => f.id === tabId);
let savedFlow = flows.find((flow) => flow.id === tabId);
savedFlow.name = name;
savedFlow.description = description;
saveFlow(savedFlow);

View file

@ -46,8 +46,8 @@ export default function ChatInput({
}`,
}}
value={lockChat ? "Thinking..." : chatValue}
onChange={(e) => {
setChatValue(e.target.value);
onChange={(event) => {
setChatValue(event.target.value);
}}
className={classNames(
lockChat

View file

@ -46,7 +46,7 @@ export default function FormModal({
const handleKeys = formKeysData.handle_keys;
const keyToUse = Object.keys(inputKeys).find(
(k) => !handleKeys.some((j) => j === k) && inputKeys[k] === ""
(key) => !handleKeys.some((j) => j === key) && inputKeys[key] === ""
);
return inputKeys[keyToUse];
@ -70,9 +70,9 @@ export default function FormModal({
const [chatKey, setChatKey] = useState(() => {
if (tabsState[flow.id]?.formKeysData?.input_keys) {
return Object.keys(tabsState[flow.id].formKeysData.input_keys).find(
(k) =>
!tabsState[flow.id].formKeysData.handle_keys.some((j) => j === k) &&
tabsState[flow.id].formKeysData.input_keys[k] === ""
(key) =>
!tabsState[flow.id].formKeysData.handle_keys.some((j) => j === key) &&
tabsState[flow.id].formKeysData.input_keys[key] === ""
);
}
// TODO: return a sensible default
@ -425,13 +425,13 @@ export default function FormModal({
{tabsState[id.current]?.formKeysData?.input_keys
? Object.keys(
tabsState[id.current].formKeysData.input_keys
).map((i, k) => (
<div className="file-component-accordion-div" key={k}>
).map((key, index) => (
<div className="file-component-accordion-div" key={index}>
<AccordionComponent
trigger={
<div className="file-component-badge-div">
<Badge variant="gray" size="md">
{i}
{key}
</Badge>
<div
@ -441,24 +441,26 @@ export default function FormModal({
}}
>
<ToggleShadComponent
enabled={chatKey === i}
enabled={chatKey === key}
setEnabled={(value) =>
handleOnCheckedChange(value, i)
handleOnCheckedChange(value, key)
}
size="small"
disabled={tabsState[
id.current
].formKeysData.handle_keys.some((t) => t === i)}
].formKeysData.handle_keys.some(
(t) => t === key
)}
/>
</div>
</div>
}
key={k}
keyValue={i}
key={index}
keyValue={key}
>
<div className="file-component-tab-column">
{tabsState[id.current].formKeysData.handle_keys.some(
(t) => t === i
(t) => t === key
) && (
<div className="font-normal text-muted-foreground ">
Source: Component
@ -467,18 +469,18 @@ export default function FormModal({
<Textarea
className="custom-scroll"
value={
tabsState[id.current].formKeysData.input_keys[i]
tabsState[id.current].formKeysData.input_keys[key]
}
onChange={(e) => {
setTabsState((old) => {
let newTabsState = _.cloneDeep(old);
newTabsState[
id.current
].formKeysData.input_keys[i] = e.target.value;
].formKeysData.input_keys[key] = e.target.value;
return newTabsState;
});
}}
disabled={chatKey === i}
disabled={chatKey === key}
placeholder="Enter text..."
></Textarea>
</div>
@ -486,35 +488,37 @@ export default function FormModal({
</div>
))
: null}
{tabsState[id.current].formKeysData.memory_keys.map((i, k) => (
<div className="file-component-accordion-div" key={k}>
<AccordionComponent
trigger={
<div className="file-component-badge-div">
<Badge variant="gray" size="md">
{i}
</Badge>
<div className="-mb-1">
<ToggleShadComponent
enabled={chatKey === i}
setEnabled={() => {}}
size="small"
disabled={true}
/>
{tabsState[id.current].formKeysData.memory_keys.map(
(key, index) => (
<div className="file-component-accordion-div" key={index}>
<AccordionComponent
trigger={
<div className="file-component-badge-div">
<Badge variant="gray" size="md">
{key}
</Badge>
<div className="-mb-1">
<ToggleShadComponent
enabled={chatKey === key}
setEnabled={() => {}}
size="small"
disabled={true}
/>
</div>
</div>
}
key={index}
keyValue={key}
>
<div className="file-component-tab-column">
<div className="font-normal text-muted-foreground ">
Source: Memory
</div>
</div>
}
key={k}
keyValue={i}
>
<div className="file-component-tab-column">
<div className="font-normal text-muted-foreground ">
Source: Memory
</div>
</div>
</AccordionComponent>
</div>
))}
</AccordionComponent>
</div>
)
)}
</div>
<div className="eraser-column-arrangement">
<div className="eraser-size">
@ -534,14 +538,14 @@ export default function FormModal({
</div>
<div ref={messagesRef} className="chat-message-div">
{chatHistory.length > 0 ? (
chatHistory.map((c, i) => (
chatHistory.map((chat, index) => (
<ChatMessage
lockChat={lockChat}
chat={c}
chat={chat}
lastMessage={
chatHistory.length - 1 === i ? true : false
chatHistory.length - 1 === index ? true : false
}
key={i}
key={index}
/>
))
) : (

View file

@ -208,9 +208,9 @@ export default function GenericModal({
setIsEdit(false);
}}
autoFocus
onChange={(e) => {
setInputValue(e.target.value);
checkVariables(e.target.value);
onChange={(event) => {
setInputValue(event.target.value);
checkVariables(event.target.value);
}}
placeholder="Type message here."
/>
@ -221,8 +221,8 @@ export default function GenericModal({
ref={ref}
className="form-input h-full w-full rounded-lg focus-visible:ring-1"
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
onChange={(event) => {
setInputValue(event.target.value);
}}
placeholder="Type message here."
/>

View file

@ -18,9 +18,9 @@ export default function DisclosureComponent({
<span className="components-disclosure-title">{title}</span>
</div>
<div className="components-disclosure-div">
{buttons.map((x, index) => (
<button key={index} onClick={x.onClick}>
{x.Icon}
{buttons.map((btn, index) => (
<button key={index} onClick={btn.onClick}>
{btn.Icon}
</button>
))}
<div>

View file

@ -141,10 +141,10 @@ export default function Page({ flow }: { flow: FlowType }) {
}, [setExtraComponent, setExtraNavigation]);
const onEdgesChangeMod = useCallback(
(s: EdgeChange[]) => {
onEdgesChange(s);
setNodes((x) => {
let newX = _.cloneDeep(x);
(change: EdgeChange[]) => {
onEdgesChange(change);
setNodes((node) => {
let newX = _.cloneDeep(node);
return newX;
});
setTabsState((prev) => {
@ -161,8 +161,8 @@ export default function Page({ flow }: { flow: FlowType }) {
);
const onNodesChangeMod = useCallback(
(s: NodeChange[]) => {
onNodesChange(s);
(change: NodeChange[]) => {
onNodesChange(change);
setTabsState((prev) => {
return {
...prev,
@ -193,8 +193,8 @@ export default function Page({ flow }: { flow: FlowType }) {
eds
)
);
setNodes((x) => {
let newX = _.cloneDeep(x);
setNodes((node) => {
let newX = _.cloneDeep(node);
return newX;
});
},
@ -219,7 +219,7 @@ export default function Page({ flow }: { flow: FlowType }) {
const onDragOver = useCallback((event: React.DragEvent) => {
event.preventDefault();
if (event.dataTransfer.types.some((t) => t === "nodedata")) {
if (event.dataTransfer.types.some((types) => types === "nodedata")) {
event.dataTransfer.dropEffect = "move";
} else {
event.dataTransfer.dropEffect = "copy";
@ -229,7 +229,7 @@ export default function Page({ flow }: { flow: FlowType }) {
const onDrop = useCallback(
(event: React.DragEvent) => {
event.preventDefault();
if (event.dataTransfer.types.some((t) => t === "nodedata")) {
if (event.dataTransfer.types.some((types) => types === "nodedata")) {
takeSnapshot();
// Get the current bounds of the ReactFlow wrapper element
@ -281,7 +281,7 @@ export default function Page({ flow }: { flow: FlowType }) {
// Add the new node to the list of nodes in state
}
setNodes((nds) => nds.concat(newNode));
} else if (event.dataTransfer.types.some((t) => t === "Files")) {
} else if (event.dataTransfer.types.some((types) => types === "Files")) {
takeSnapshot();
uploadFlow(false, event.dataTransfer.files.item(0));
}
@ -303,7 +303,10 @@ export default function Page({ flow }: { flow: FlowType }) {
takeSnapshot();
setEdges(
edges.filter(
(ns) => !mynodes.some((n) => ns.source === n.id || ns.target === n.id)
(edge) =>
!mynodes.some(
(node) => edge.source === node.id || edge.target === node.id
)
)
);
},
@ -326,7 +329,7 @@ export default function Page({ flow }: { flow: FlowType }) {
const onEdgeUpdateEnd = useCallback((_, edge) => {
if (!edgeUpdateSuccessful.current) {
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
setEdges((eds) => eds.filter((edg) => edg.id !== edge.id));
}
edgeUpdateSuccessful.current = true;

View file

@ -56,7 +56,7 @@ export default function ExtraSidebar() {
return ret;
});
}
const flow = flows.find((f) => f.id === tabId);
const flow = flows.find((flow) => flow.id === tabId);
useEffect(() => {
// show components with error on load
let errors = [];
@ -143,10 +143,10 @@ export default function ExtraSidebar() {
id="search"
placeholder="Search"
className="nopan nodrag noundo nocopy input-search"
onChange={(e) => {
handleSearchInput(e.target.value);
onChange={(event) => {
handleSearchInput(event.target.value);
// Set search input state
setSearch(e.target.value);
setSearch(event.target.value);
}}
/>
<div className="search-icon">
@ -161,42 +161,43 @@ export default function ExtraSidebar() {
<div className="side-bar-components-div-arrangement">
{Object.keys(dataFilter)
.sort()
.map((d: keyof APIObjectType, i) =>
Object.keys(dataFilter[d]).length > 0 ? (
.map((SBSectionName: keyof APIObjectType, index) =>
Object.keys(dataFilter[SBSectionName]).length > 0 ? (
<DisclosureComponent
openDisc={search.length == 0 ? false : true}
key={i}
key={index}
button={{
title: nodeNames[d] ?? nodeNames.unknown,
Icon: nodeIconsLucide[d] ?? nodeIconsLucide.unknown,
title: nodeNames[SBSectionName] ?? nodeNames.unknown,
Icon:
nodeIconsLucide[SBSectionName] ?? nodeIconsLucide.unknown,
}}
>
<div className="side-bar-components-gap">
{Object.keys(dataFilter[d])
{Object.keys(dataFilter[SBSectionName])
.sort()
.map((t: string, k) => (
.map((SBItemName: string, index) => (
<ShadTooltip
content={data[d][t].display_name}
content={data[SBSectionName][SBItemName].display_name}
side="right"
key={k}
key={index}
>
<div key={k} data-tooltip-id={t}>
<div key={index} data-tooltip-id={SBItemName}>
<div
draggable={!data[d][t].error}
draggable={!data[SBSectionName][SBItemName].error}
className={
"side-bar-components-border bg-background" +
(data[d][t].error
(data[SBSectionName][SBItemName].error
? " cursor-not-allowed select-none"
: "")
}
style={{
borderLeftColor:
nodeColors[d] ?? nodeColors.unknown,
nodeColors[SBSectionName] ?? nodeColors.unknown,
}}
onDragStart={(event) =>
onDragStart(event, {
type: t,
node: data[d][t],
type: SBItemName,
node: data[SBSectionName][SBItemName],
})
}
onDragEnd={() => {
@ -209,7 +210,7 @@ export default function ExtraSidebar() {
>
<div className="side-bar-components-div-form">
<span className="side-bar-components-text">
{data[d][t].display_name}
{data[SBSectionName][SBItemName].display_name}
</span>
<IconComponent
name="Menu"
@ -223,7 +224,7 @@ export default function ExtraSidebar() {
</div>
</DisclosureComponent>
) : (
<div key={i}></div>
<div key={index}></div>
)
)}
</div>

View file

@ -9,17 +9,17 @@ import { classNames } from "../../../../utils/utils";
export default function NodeToolbarComponent({ data, setData, deleteNode }) {
const [nodeLength, setNodeLength] = useState(
Object.keys(data.node.template).filter(
(t) =>
t.charAt(0) !== "_" &&
data.node.template[t].show &&
(data.node.template[t].type === "str" ||
data.node.template[t].type === "bool" ||
data.node.template[t].type === "float" ||
data.node.template[t].type === "code" ||
data.node.template[t].type === "prompt" ||
data.node.template[t].type === "file" ||
data.node.template[t].type === "Any" ||
data.node.template[t].type === "int")
(templateField) =>
templateField.charAt(0) !== "_" &&
data.node.template[templateField].show &&
(data.node.template[templateField].type === "str" ||
data.node.template[templateField].type === "bool" ||
data.node.template[templateField].type === "float" ||
data.node.template[templateField].type === "code" ||
data.node.template[templateField].type === "prompt" ||
data.node.template[templateField].type === "file" ||
data.node.template[templateField].type === "Any" ||
data.node.template[templateField].type === "int")
).length
);

View file

@ -15,7 +15,7 @@ export function cleanEdges({
const sourceNode = nodes.find((node) => node.id === edge.source);
const targetNode = nodes.find((node) => node.id === edge.target);
if (!sourceNode || !targetNode) {
newEdges = newEdges.filter((e) => e.id !== edge.id);
newEdges = newEdges.filter((edg) => edg.id !== edge.id);
}
// check if the source and target handle still exists
if (sourceNode && targetNode) {
@ -41,7 +41,7 @@ export function cleanEdges({
...sourceNode.data.node.base_classes,
].join("|");
if (id !== sourceHandle) {
newEdges = newEdges.filter((e) => e.id !== edge.id);
newEdges = newEdges.filter((edg) => edg.id !== edge.id);
}
}
}
@ -57,15 +57,15 @@ export function isValidConnection(
targetHandle
.split("|")[0]
.split(";")
.some((n) => n === sourceHandle.split("|")[0]) ||
.some((target) => target === sourceHandle.split("|")[0]) ||
sourceHandle
.split("|")
.slice(2)
.some((t) =>
.some((target) =>
targetHandle
.split("|")[0]
.split(";")
.some((n) => n === t)
.some((n) => n === target)
) ||
targetHandle.split("|")[0] === "str"
) {
@ -129,35 +129,35 @@ export function updateTemplate(
export function updateIds(newFlow, getNodeId) {
let idsMap = {};
newFlow.nodes.forEach((n: NodeType) => {
newFlow.nodes.forEach((node: NodeType) => {
// Generate a unique node ID
let newId = getNodeId(n.data.type);
idsMap[n.id] = newId;
n.id = newId;
n.data.id = newId;
let newId = getNodeId(node.data.type);
idsMap[node.id] = newId;
node.id = newId;
node.data.id = newId;
// Add the new node to the list of nodes in state
});
newFlow.edges.forEach((e) => {
e.source = idsMap[e.source];
e.target = idsMap[e.target];
let sourceHandleSplitted = e.sourceHandle.split("|");
e.sourceHandle =
newFlow.edges.forEach((edge) => {
edge.source = idsMap[edge.source];
edge.target = idsMap[edge.target];
let sourceHandleSplitted = edge.sourceHandle.split("|");
edge.sourceHandle =
sourceHandleSplitted[0] +
"|" +
e.source +
edge.source +
"|" +
sourceHandleSplitted.slice(2).join("|");
let targetHandleSplitted = e.targetHandle.split("|");
e.targetHandle =
targetHandleSplitted.slice(0, -1).join("|") + "|" + e.target;
e.id =
let targetHandleSplitted = edge.targetHandle.split("|");
edge.targetHandle =
targetHandleSplitted.slice(0, -1).join("|") + "|" + edge.target;
edge.id =
"reactflow__edge-" +
e.source +
e.sourceHandle +
edge.source +
edge.sourceHandle +
"-" +
e.target +
e.targetHandle;
edge.target +
edge.targetHandle;
});
}
@ -169,10 +169,10 @@ export function buildTweaks(flow) {
}
export function validateNode(
n: NodeType,
node: NodeType,
reactFlowInstance: ReactFlowInstance
): Array<string> {
if (!n.data?.node?.template || !Object.keys(n.data.node.template)) {
if (!node.data?.node?.template || !Object.keys(node.data.node.template)) {
return [
"We've noticed a potential issue with a node in the flow. Please review it and, if necessary, submit a bug report with your exported flow file. Thank you for your help!",
];
@ -181,7 +181,7 @@ export function validateNode(
const {
type,
node: { template },
} = n.data;
} = node.data;
return Object.keys(template).reduce(
(errors: Array<string>, t) =>
@ -194,9 +194,9 @@ export function validateNode(
!reactFlowInstance
.getEdges()
.some(
(e) =>
e.targetHandle.split("|")[1] === t &&
e.targetHandle.split("|")[2] === n.id
(edge) =>
edge.targetHandle.split("|")[1] === t &&
edge.targetHandle.split("|")[2] === node.id
)
? [
`${type} is missing ${

View file

@ -103,11 +103,14 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
"int",
]);
const checkBaseClass = (t: any) =>
t.type &&
t.show &&
((!excludeTypes.has(t.type) && baseClassesSet.has(t.type)) ||
(t.input_types && t.input_types.some((x) => baseClassesSet.has(x))));
const checkBaseClass = (template: any) =>
template.type &&
template.show &&
((!excludeTypes.has(template.type) && baseClassesSet.has(template.type)) ||
(template.input_types &&
template.input_types.some((inputType) =>
baseClassesSet.has(inputType)
)));
if (flow) {
for (const node of flow) {
@ -119,7 +122,9 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
Object.values(nodeData.node.template).some(checkBaseClass),
hasBaseClassInBaseClasses:
foundNode?.hasBaseClassInBaseClasses ||
nodeData.node.base_classes.some((t) => baseClassesSet.has(t)),
nodeData.node.base_classes.some((baseClass) =>
baseClassesSet.has(baseClass)
),
});
}
}
@ -135,8 +140,8 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
hasBaseClassInTemplate: Object.values(node.template).some(
checkBaseClass
),
hasBaseClassInBaseClasses: node.base_classes.some((t) =>
baseClassesSet.has(t)
hasBaseClassInBaseClasses: node.base_classes.some((baseClass) =>
baseClassesSet.has(baseClass)
),
};
checkedNodes.set(n, foundNode);
@ -162,13 +167,13 @@ export function groupByFamily(data, baseClasses, left, flow?: NodeType[]) {
}
return left
? arrOfPossibleOutputs.map((t) => ({
family: t.category,
type: t.full ? "" : t.nodes.join(", "),
? arrOfPossibleOutputs.map((output) => ({
family: output.category,
type: output.full ? "" : output.nodes.join(", "),
}))
: arrOfPossibleInputs.map((t) => ({
family: t.category,
type: t.full ? "" : t.nodes.join(", "),
: arrOfPossibleInputs.map((input) => ({
family: input.category,
type: input.full ? "" : input.nodes.join(", "),
}));
}