Merge branch 'zustand/io/migration' of github.com:logspace-ai/langflow into zustand/io/migration
This commit is contained in:
commit
fca95763a8
14 changed files with 298 additions and 297 deletions
|
|
@ -20,6 +20,7 @@ import useAlertStore from "./stores/alertStore";
|
||||||
import { useDarkStore } from "./stores/darkStore";
|
import { useDarkStore } from "./stores/darkStore";
|
||||||
import useFlowsManagerStore from "./stores/flowsManagerStore";
|
import useFlowsManagerStore from "./stores/flowsManagerStore";
|
||||||
import { useTypesStore } from "./stores/typesStore";
|
import { useTypesStore } from "./stores/typesStore";
|
||||||
|
import { useStoreStore } from "./stores/storeStore";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const removeFromTempNotificationList = useAlertStore(
|
const removeFromTempNotificationList = useAlertStore(
|
||||||
|
|
@ -28,7 +29,6 @@ export default function App() {
|
||||||
const tempNotificationList = useAlertStore(
|
const tempNotificationList = useAlertStore(
|
||||||
(state) => state.tempNotificationList
|
(state) => state.tempNotificationList
|
||||||
);
|
);
|
||||||
const loading = useAlertStore((state) => state.loading);
|
|
||||||
const [fetchError, setFetchError] = useState(false);
|
const [fetchError, setFetchError] = useState(false);
|
||||||
const isLoading = useFlowsManagerStore((state) => state.isLoading);
|
const isLoading = useFlowsManagerStore((state) => state.isLoading);
|
||||||
|
|
||||||
|
|
@ -38,9 +38,11 @@ export default function App() {
|
||||||
|
|
||||||
const { isAuthenticated } = useContext(AuthContext);
|
const { isAuthenticated } = useContext(AuthContext);
|
||||||
const refreshFlows = useFlowsManagerStore((state) => state.refreshFlows);
|
const refreshFlows = useFlowsManagerStore((state) => state.refreshFlows);
|
||||||
|
const fetchApiData = useStoreStore((state) => state.fetchApiData);
|
||||||
const getTypes = useTypesStore((state) => state.getTypes);
|
const getTypes = useTypesStore((state) => state.getTypes);
|
||||||
const refreshVersion = useDarkStore((state) => state.refreshVersion);
|
const refreshVersion = useDarkStore((state) => state.refreshVersion);
|
||||||
const refreshStars = useDarkStore((state) => state.refreshStars);
|
const refreshStars = useDarkStore((state) => state.refreshStars);
|
||||||
|
const checkHasStore = useStoreStore((state) => state.checkHasStore);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
refreshStars();
|
refreshStars();
|
||||||
|
|
@ -52,6 +54,8 @@ export default function App() {
|
||||||
getTypes().then(() => {
|
getTypes().then(() => {
|
||||||
refreshFlows();
|
refreshFlows();
|
||||||
});
|
});
|
||||||
|
checkHasStore();
|
||||||
|
fetchApiData();
|
||||||
}
|
}
|
||||||
}, [isAuthenticated]);
|
}, [isAuthenticated]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@ export default function GenericNode({
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null
|
validationStatus: validationStatusType | null
|
||||||
) => {
|
) => {
|
||||||
|
console.log(buildStatus);
|
||||||
const isValid = validationStatus && validationStatus.valid;
|
const isValid = validationStatus && validationStatus.valid;
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
return "green-status";
|
return "green-status";
|
||||||
|
|
@ -192,7 +193,6 @@ export default function GenericNode({
|
||||||
};
|
};
|
||||||
const renderIconPlayOrPauseComponents = (
|
const renderIconPlayOrPauseComponents = (
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null
|
|
||||||
) => {
|
) => {
|
||||||
if (buildStatus === BuildStatus.BUILDING) {
|
if (buildStatus === BuildStatus.BUILDING) {
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
|
|
@ -200,7 +200,7 @@ export default function GenericNode({
|
||||||
return (
|
return (
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Play"
|
name="Play"
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground hover:text-medium-indigo"
|
className="absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground group-hover:text-medium-indigo"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -467,44 +467,21 @@ export default function GenericNode({
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className={"group h-9 px-1.5"}
|
className={"group h-9 px-1.5"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (buildStatus === BuildStatus.BUILDING || isBuilding)
|
if (data?.buildStatus === BuildStatus.BUILDING || isBuilding)
|
||||||
return;
|
return;
|
||||||
buildFlow(data.id);
|
buildFlow(data.id);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
styleClasses="cursor-default"
|
|
||||||
content={
|
content={
|
||||||
buildStatus === BuildStatus.BUILDING ? (
|
"Build"
|
||||||
<span>Building...</span>
|
|
||||||
) : !validationStatus ? (
|
|
||||||
<span className="flex">
|
|
||||||
Build{" "}
|
|
||||||
<IconComponent
|
|
||||||
name="Play"
|
|
||||||
className=" h-5 stroke-status-green stroke-2"
|
|
||||||
/>{" "}
|
|
||||||
flow to validate status.
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<div className="max-h-96 overflow-auto">
|
|
||||||
{typeof validationStatus.params === "string"
|
|
||||||
? `${durationString}\n${validationStatus.params}`
|
|
||||||
.split("\n")
|
|
||||||
.map((line, index) => (
|
|
||||||
<div key={index}>{line}</div>
|
|
||||||
))
|
|
||||||
: durationString}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
side="bottom"
|
side="bottom"
|
||||||
>
|
>
|
||||||
<div className="generic-node-status-position flex items-center justify-center">
|
<div className="generic-node-status-position flex items-center justify-center">
|
||||||
{renderIconPlayOrPauseComponents(
|
{renderIconPlayOrPauseComponents(
|
||||||
buildStatus,
|
buildStatus,
|
||||||
validationStatus
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
|
|
@ -513,7 +490,6 @@ export default function GenericNode({
|
||||||
)}
|
)}
|
||||||
<div className="">
|
<div className="">
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
styleClasses="cursor-default"
|
|
||||||
content={
|
content={
|
||||||
data?.buildStatus === BuildStatus.BUILDING ? (
|
data?.buildStatus === BuildStatus.BUILDING ? (
|
||||||
<span>Building...</span>
|
<span>Building...</span>
|
||||||
|
|
@ -529,7 +505,6 @@ export default function GenericNode({
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
side="bottom"
|
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
{renderIconStatusComponents(
|
{renderIconStatusComponents(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
|
import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
|
||||||
import BaseModal from "../../modals/baseModal";
|
import BaseModal from "../../modals/baseModal";
|
||||||
|
import useAlertStore from "../../stores/alertStore";
|
||||||
import useFlowStore from "../../stores/flowStore";
|
import useFlowStore from "../../stores/flowStore";
|
||||||
|
import { NodeType } from "../../types/flow";
|
||||||
|
import { validateNodes } from "../../utils/reactflowUtils";
|
||||||
import { cn } from "../../utils/utils";
|
import { cn } from "../../utils/utils";
|
||||||
import AccordionComponent from "../AccordionComponent";
|
import AccordionComponent from "../AccordionComponent";
|
||||||
import IOInputField from "../IOInputField";
|
import IOInputField from "../IOInputField";
|
||||||
|
|
@ -16,19 +20,22 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
const inputs = useFlowStore((state) => state.inputs).filter(
|
const inputs = useFlowStore((state) => state.inputs).filter(
|
||||||
(input) => input.type !== "ChatInput"
|
(input) => input.type !== "ChatInput"
|
||||||
);
|
);
|
||||||
|
const chatInput = useFlowStore((state) => state.inputs).find(
|
||||||
|
(input) => input.type === "ChatInput"
|
||||||
|
);
|
||||||
const outputs = useFlowStore((state) => state.outputs).filter(
|
const outputs = useFlowStore((state) => state.outputs).filter(
|
||||||
(output) => output.type !== "ChatOutput"
|
(output) => output.type !== "ChatOutput"
|
||||||
);
|
);
|
||||||
|
const chatOutput = useFlowStore((state) => state.outputs).find(
|
||||||
|
(output) => output.type === "ChatOutput"
|
||||||
|
);
|
||||||
const nodes = useFlowStore((state) => state.nodes).filter(
|
const nodes = useFlowStore((state) => state.nodes).filter(
|
||||||
(node) =>
|
(node) =>
|
||||||
(inputs.some((input) => input.id === node.id) ||
|
(inputs.some((input) => input.id === node.id) ||
|
||||||
outputs.some((output) => output.id === node.id)) &&
|
outputs.some((output) => output.id === node.id))
|
||||||
node.type !== "ChatInput" &&
|
|
||||||
node.type !== "ChatOutput"
|
|
||||||
);
|
|
||||||
const haveChat = useFlowStore((state) => state.outputs).some(
|
|
||||||
(output) => output.type === "ChatOutput"
|
|
||||||
);
|
);
|
||||||
|
const haveChat =
|
||||||
|
chatInput || chatOutput
|
||||||
const [selectedTab, setSelectedTab] = useState(
|
const [selectedTab, setSelectedTab] = useState(
|
||||||
inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0
|
inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0
|
||||||
);
|
);
|
||||||
|
|
@ -36,6 +43,45 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
{ type: string; id: string } | undefined
|
{ type: string; id: string } | undefined
|
||||||
>(undefined);
|
>(undefined);
|
||||||
|
|
||||||
|
const { getNode, setNode, buildFlow, getFlow } = useFlowStore();
|
||||||
|
const { setErrorData } = useAlertStore();
|
||||||
|
const setIsBuilding = useFlowStore((state) => state.setIsBuilding);
|
||||||
|
const [lockChat, setLockChat] = useState(false);
|
||||||
|
const [chatValue, setChatValue] = useState("");
|
||||||
|
const isBuilding = useFlowStore((state) => state.isBuilding);
|
||||||
|
|
||||||
|
async function sendMessage(count = 1): Promise<void> {
|
||||||
|
if (isBuilding) return;
|
||||||
|
const { nodes, edges } = getFlow();
|
||||||
|
let nodeValidationErrors = validateNodes(nodes, edges);
|
||||||
|
if (nodeValidationErrors.length === 0) {
|
||||||
|
setIsBuilding(true);
|
||||||
|
setLockChat(true);
|
||||||
|
setChatValue("");
|
||||||
|
const chatInputNode = nodes.find((node) => node.id === chatInput?.id);
|
||||||
|
if (chatInputNode) {
|
||||||
|
let newNode = cloneDeep(chatInputNode);
|
||||||
|
newNode.data.node!.template["message"].value = chatValue;
|
||||||
|
setNode(chatInput!.id, newNode);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
await buildFlow().catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
setLockChat(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setLockChat(false);
|
||||||
|
|
||||||
|
//set chat message in the flow and run build
|
||||||
|
//@ts-ignore
|
||||||
|
} else {
|
||||||
|
setErrorData({
|
||||||
|
title: "Oops! Looks like you missed some required information:",
|
||||||
|
list: nodeValidationErrors,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedViewField(undefined);
|
setSelectedViewField(undefined);
|
||||||
setSelectedTab(inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0);
|
setSelectedTab(inputs.length > 0 ? 1 : outputs.length > 0 ? 2 : 0);
|
||||||
|
|
@ -43,7 +89,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseModal
|
<BaseModal
|
||||||
size={haveChat ? "large" : "small"}
|
size={haveChat ? (selectedTab === 0 ? "large-thin" : "large") : "small"}
|
||||||
open={open}
|
open={open}
|
||||||
setOpen={setOpen}
|
setOpen={setOpen}
|
||||||
>
|
>
|
||||||
|
|
@ -60,178 +106,196 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
</BaseModal.Header>
|
</BaseModal.Header>
|
||||||
<BaseModal.Content>
|
<BaseModal.Content>
|
||||||
<div className="flex-max-width mt-2 h-[80vh]">
|
<div className="flex h-full flex-col">
|
||||||
{selectedTab !== 0 && (
|
<div className="flex-max-width mt-2 h-full">
|
||||||
<div
|
{selectedTab !== 0 && (
|
||||||
className={cn(
|
<div
|
||||||
"mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start overflow-auto scrollbar-hide",
|
className={cn(
|
||||||
haveChat ? "w-2/6" : "w-full"
|
"mr-6 flex h-full w-2/6 flex-shrink-0 flex-col justify-start overflow-auto scrollbar-hide",
|
||||||
)}
|
haveChat ? "w-2/6" : "w-full"
|
||||||
>
|
)}
|
||||||
<Tabs
|
|
||||||
value={selectedTab.toString()}
|
|
||||||
className={"api-modal-tabs "}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
setSelectedTab(Number(value));
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className="api-modal-tablist-div">
|
<Tabs
|
||||||
<TabsList>
|
value={selectedTab.toString()}
|
||||||
{inputs.length > 0 && (
|
className={
|
||||||
<TabsTrigger value={"1"}>Inputs</TabsTrigger>
|
"flex h-full flex-col overflow-hidden rounded-md border bg-muted text-center"
|
||||||
)}
|
}
|
||||||
{outputs.length > 0 && (
|
onValueChange={(value) => {
|
||||||
<TabsTrigger value={"2"}>Outputs</TabsTrigger>
|
setSelectedTab(Number(value));
|
||||||
)}
|
}}
|
||||||
</TabsList>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<TabsContent
|
|
||||||
value={"1"}
|
|
||||||
className="api-modal-tabs-content mt-4"
|
|
||||||
>
|
>
|
||||||
<div className="mx-2 mb-2 flex items-center gap-2 text-sm font-bold">
|
<div className="api-modal-tablist-div">
|
||||||
<IconComponent className="h-4 w-4" name={"Type"} />
|
<TabsList>
|
||||||
Text Inputs
|
{inputs.length > 0 && (
|
||||||
|
<TabsTrigger value={"1"}>Inputs</TabsTrigger>
|
||||||
|
)}
|
||||||
|
{outputs.length > 0 && (
|
||||||
|
<TabsTrigger value={"2"}>Outputs</TabsTrigger>
|
||||||
|
)}
|
||||||
|
</TabsList>
|
||||||
</div>
|
</div>
|
||||||
{nodes
|
|
||||||
.filter((node) =>
|
|
||||||
inputs.some((input) => input.id === node.id)
|
|
||||||
)
|
|
||||||
.map((node, index) => {
|
|
||||||
const input = inputs.find(
|
|
||||||
(input) => input.id === node.id
|
|
||||||
)!;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="file-component-accordion-div"
|
|
||||||
key={index}
|
|
||||||
>
|
|
||||||
<AccordionComponent
|
|
||||||
trigger={
|
|
||||||
<div className="file-component-badge-div">
|
|
||||||
<Badge variant="gray" size="md">
|
|
||||||
{input.id}
|
|
||||||
</Badge>
|
|
||||||
{haveChat && (
|
|
||||||
<div
|
|
||||||
className="-mb-1 pr-4"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
setSelectedViewField(input);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconComponent
|
|
||||||
className="h-4 w-4"
|
|
||||||
name="ExternalLink"
|
|
||||||
></IconComponent>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
key={index}
|
|
||||||
keyValue={input.id}
|
|
||||||
>
|
|
||||||
<div className="file-component-tab-column">
|
|
||||||
<div className="">
|
|
||||||
{input && (
|
|
||||||
<IOInputField
|
|
||||||
inputType={input.type}
|
|
||||||
inputId={input.id}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionComponent>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TabsContent>
|
|
||||||
<TabsContent
|
|
||||||
value={"2"}
|
|
||||||
className="api-modal-tabs-content mt-4"
|
|
||||||
>
|
|
||||||
<div className="mx-2 mb-2 flex items-center gap-2 text-sm font-bold">
|
|
||||||
<IconComponent className="h-4 w-4" name={"Braces"} />
|
|
||||||
Prompt Outputs
|
|
||||||
</div>
|
|
||||||
{nodes
|
|
||||||
.filter((node) =>
|
|
||||||
outputs.some((output) => output.id === node.id)
|
|
||||||
)
|
|
||||||
.map((node, index) => {
|
|
||||||
const output = outputs.find(
|
|
||||||
(output) => output.id === node.id
|
|
||||||
)!;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="file-component-accordion-div"
|
|
||||||
key={index}
|
|
||||||
>
|
|
||||||
<AccordionComponent
|
|
||||||
trigger={
|
|
||||||
<div className="file-component-badge-div">
|
|
||||||
<Badge variant="gray" size="md">
|
|
||||||
{output.id}
|
|
||||||
</Badge>
|
|
||||||
{haveChat && (
|
|
||||||
<div
|
|
||||||
className="-mb-1 pr-4"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
setSelectedViewField(output);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconComponent
|
|
||||||
className="h-4 w-4"
|
|
||||||
name="ExternalLink"
|
|
||||||
></IconComponent>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
key={index}
|
|
||||||
keyValue={output.id}
|
|
||||||
>
|
|
||||||
<div className="file-component-tab-column">
|
|
||||||
<div className="">
|
|
||||||
{output && (
|
|
||||||
<IOOutputView
|
|
||||||
outputType={output.type}
|
|
||||||
outputId={output.id}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionComponent>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</TabsContent>
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{haveChat ? (
|
<TabsContent
|
||||||
selectedViewField ? (
|
value={"1"}
|
||||||
inputs.some((input) => input.id === selectedViewField.id) ? (
|
className="api-modal-tabs-content mt-4"
|
||||||
<IOInputField
|
>
|
||||||
inputType={selectedViewField.type!}
|
<div className="mx-2 mb-2 flex items-center gap-2 text-sm font-bold">
|
||||||
inputId={selectedViewField.id!}
|
<IconComponent className="h-4 w-4" name={"Type"} />
|
||||||
/>
|
Text Inputs
|
||||||
|
</div>
|
||||||
|
{nodes
|
||||||
|
.filter((node) =>
|
||||||
|
inputs.some((input) => input.id === node.id)
|
||||||
|
)
|
||||||
|
.map((node, index) => {
|
||||||
|
const input = inputs.find(
|
||||||
|
(input) => input.id === node.id
|
||||||
|
)!;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="file-component-accordion-div"
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
<AccordionComponent
|
||||||
|
trigger={
|
||||||
|
<div className="file-component-badge-div">
|
||||||
|
<Badge variant="gray" size="md">
|
||||||
|
{input.id}
|
||||||
|
</Badge>
|
||||||
|
{haveChat && (
|
||||||
|
<div
|
||||||
|
className="-mb-1 pr-4"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
setSelectedViewField(input);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconComponent
|
||||||
|
className="h-4 w-4"
|
||||||
|
name="ExternalLink"
|
||||||
|
></IconComponent>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
key={index}
|
||||||
|
keyValue={input.id}
|
||||||
|
>
|
||||||
|
<div className="file-component-tab-column">
|
||||||
|
<div className="">
|
||||||
|
{input && (
|
||||||
|
<IOInputField
|
||||||
|
inputType={input.type}
|
||||||
|
inputId={input.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionComponent>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent
|
||||||
|
value={"2"}
|
||||||
|
className="api-modal-tabs-content mt-4"
|
||||||
|
>
|
||||||
|
<div className="mx-2 mb-2 flex items-center gap-2 text-sm font-bold">
|
||||||
|
<IconComponent className="h-4 w-4" name={"Braces"} />
|
||||||
|
Prompt Outputs
|
||||||
|
</div>
|
||||||
|
{nodes
|
||||||
|
.filter((node) =>
|
||||||
|
outputs.some((output) => output.id === node.id)
|
||||||
|
)
|
||||||
|
.map((node, index) => {
|
||||||
|
const output = outputs.find(
|
||||||
|
(output) => output.id === node.id
|
||||||
|
)!;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="file-component-accordion-div"
|
||||||
|
key={index}
|
||||||
|
>
|
||||||
|
<AccordionComponent
|
||||||
|
trigger={
|
||||||
|
<div className="file-component-badge-div">
|
||||||
|
<Badge variant="gray" size="md">
|
||||||
|
{output.id}
|
||||||
|
</Badge>
|
||||||
|
{haveChat && (
|
||||||
|
<div
|
||||||
|
className="-mb-1 pr-4"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
setSelectedViewField(output);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<IconComponent
|
||||||
|
className="h-4 w-4"
|
||||||
|
name="ExternalLink"
|
||||||
|
></IconComponent>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
key={index}
|
||||||
|
keyValue={output.id}
|
||||||
|
>
|
||||||
|
<div className="file-component-tab-column">
|
||||||
|
<div className="">
|
||||||
|
{output && (
|
||||||
|
<IOOutputView
|
||||||
|
outputType={output.type}
|
||||||
|
outputId={output.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionComponent>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{haveChat ? (
|
||||||
|
selectedViewField ? (
|
||||||
|
inputs.some((input) => input.id === selectedViewField.id) ? (
|
||||||
|
<IOInputField
|
||||||
|
inputType={selectedViewField.type!}
|
||||||
|
inputId={selectedViewField.id!}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<IOOutputView
|
||||||
|
outputType={selectedViewField.type!}
|
||||||
|
outputId={selectedViewField.id!}
|
||||||
|
/>
|
||||||
|
)
|
||||||
) : (
|
) : (
|
||||||
<IOOutputView
|
<NewChatView
|
||||||
outputType={selectedViewField.type!}
|
sendMessage={sendMessage}
|
||||||
outputId={selectedViewField.id!}
|
chatValue={chatValue}
|
||||||
|
setChatValue={setChatValue}
|
||||||
|
lockChat={lockChat}
|
||||||
|
setLockChat={setLockChat}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<NewChatView />
|
<div className="absolute bottom-8 right-8"></div>
|
||||||
)
|
)}
|
||||||
) : (
|
</div>
|
||||||
<div className="absolute bottom-8 right-8">
|
{!haveChat && (
|
||||||
<Button className="px-3">
|
<div className="flex w-full justify-end pt-6">
|
||||||
<IconComponent name="Play" className="h-6 w-6" />
|
<Button variant={"outline"} className="px-3 flex gap-2" onClick={() => sendMessage(1)}>
|
||||||
|
|
||||||
|
<IconComponent
|
||||||
|
name={isBuilding ? "Loader2" : "Play"}
|
||||||
|
className={cn("h-4 w-4", isBuilding ? "animate-spin" : "fill-current text-medium-indigo")}
|
||||||
|
/>
|
||||||
|
Run Flow
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export default function ChatTrigger({}): JSX.Element {
|
||||||
>
|
>
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Sliders"
|
name="Zap"
|
||||||
className={"message-button-icon h-6 w-6 transition-all"}
|
className={"message-button-icon h-6 w-6 transition-all"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ export default function ChatMessage({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// This effect is specifically for calling updateChat after streaming ends
|
// This effect is specifically for calling updateChat after streaming ends
|
||||||
if (!isStreaming && streamUrl === null) {
|
if (!isStreaming && streamUrl) {
|
||||||
if (updateChat) {
|
if (updateChat) {
|
||||||
updateChat(chat, chatMessage, streamUrl);
|
updateChat(chat, chatMessage, streamUrl);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import _, { cloneDeep } from "lodash";
|
import _ from "lodash";
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import { deleteFlowPool } from "../../controllers/API";
|
import { deleteFlowPool } from "../../controllers/API";
|
||||||
|
|
@ -11,30 +11,21 @@ import {
|
||||||
ChatOutputType,
|
ChatOutputType,
|
||||||
FlowPoolObjectType,
|
FlowPoolObjectType,
|
||||||
} from "../../types/chat";
|
} from "../../types/chat";
|
||||||
import { NodeType } from "../../types/flow";
|
|
||||||
import { validateNodes } from "../../utils/reactflowUtils";
|
|
||||||
import { classNames } from "../../utils/utils";
|
import { classNames } from "../../utils/utils";
|
||||||
import ChatInput from "./chatInput";
|
import ChatInput from "./chatInput";
|
||||||
import ChatMessage from "./chatMessage";
|
import ChatMessage from "./chatMessage";
|
||||||
|
|
||||||
export default function NewChatView(): JSX.Element {
|
export default function NewChatView({
|
||||||
const [chatValue, setChatValue] = useState("");
|
sendMessage,
|
||||||
const {
|
chatValue,
|
||||||
flowPool,
|
setChatValue,
|
||||||
outputs,
|
lockChat,
|
||||||
inputs,
|
setLockChat,
|
||||||
getNode,
|
}): JSX.Element {
|
||||||
setNode,
|
const { flowPool, outputs, inputs, CleanFlowPool } = useFlowStore();
|
||||||
buildFlow,
|
const { setNoticeData } = useAlertStore();
|
||||||
getFlow,
|
|
||||||
CleanFlowPool,
|
|
||||||
} = useFlowStore();
|
|
||||||
const { setErrorData, setNoticeData } = useAlertStore();
|
|
||||||
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||||
const setIsBuilding = useFlowStore((state) => state.setIsBuilding);
|
|
||||||
const [lockChat, setLockChat] = useState(false);
|
|
||||||
const messagesRef = useRef<HTMLDivElement | null>(null);
|
const messagesRef = useRef<HTMLDivElement | null>(null);
|
||||||
const isBuilding = useFlowStore((state) => state.isBuilding);
|
|
||||||
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
|
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
|
||||||
|
|
||||||
const inputTypes = inputs.map((obj) => obj.type);
|
const inputTypes = inputs.map((obj) => obj.type);
|
||||||
|
|
@ -115,40 +106,6 @@ export default function NewChatView(): JSX.Element {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function sendMessage(count = 1): Promise<void> {
|
|
||||||
if (isBuilding) return;
|
|
||||||
const { nodes, edges } = getFlow();
|
|
||||||
let nodeValidationErrors = validateNodes(nodes, edges);
|
|
||||||
if (nodeValidationErrors.length === 0) {
|
|
||||||
setIsBuilding(true);
|
|
||||||
setLockChat(true);
|
|
||||||
setChatValue("");
|
|
||||||
const chatInputId = inputIds.find((inputId) =>
|
|
||||||
inputId.includes("ChatInput")
|
|
||||||
);
|
|
||||||
const chatInput: NodeType = getNode(chatInputId!) as NodeType;
|
|
||||||
if (chatInput) {
|
|
||||||
let newNode = cloneDeep(chatInput);
|
|
||||||
newNode.data.node!.template["message"].value = chatValue;
|
|
||||||
setNode(chatInputId!, newNode);
|
|
||||||
}
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
await buildFlow().catch((err) => {
|
|
||||||
console.error(err);
|
|
||||||
setLockChat(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setLockChat(false);
|
|
||||||
|
|
||||||
//set chat message in the flow and run build
|
|
||||||
//@ts-ignore
|
|
||||||
} else {
|
|
||||||
setErrorData({
|
|
||||||
title: "Oops! Looks like you missed some required information:",
|
|
||||||
list: nodeValidationErrors,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function clearChat(): void {
|
function clearChat(): void {
|
||||||
setChatHistory([]);
|
setChatHistory([]);
|
||||||
deleteFlowPool(currentFlowId).then((_) => {
|
deleteFlowPool(currentFlowId).then((_) => {
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ interface BaseModalProps {
|
||||||
| "small"
|
| "small"
|
||||||
| "medium"
|
| "medium"
|
||||||
| "large"
|
| "large"
|
||||||
|
| "large-thin"
|
||||||
| "large-h-full"
|
| "large-h-full"
|
||||||
| "small-h-full"
|
| "small-h-full"
|
||||||
| "medium-h-full"
|
| "medium-h-full"
|
||||||
|
|
@ -128,6 +129,10 @@ function BaseModal({
|
||||||
minWidth = "min-w-[80vw]";
|
minWidth = "min-w-[80vw]";
|
||||||
height = "h-[80vh]";
|
height = "h-[80vh]";
|
||||||
break;
|
break;
|
||||||
|
case "large-thin":
|
||||||
|
minWidth = "min-w-[65vw]";
|
||||||
|
height = "h-[80vh]";
|
||||||
|
break;
|
||||||
case "large-h-full":
|
case "large-h-full":
|
||||||
minWidth = "min-w-[80vw]";
|
minWidth = "min-w-[80vw]";
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -576,6 +576,7 @@ export default function FormModal({
|
||||||
chatHistory.length - 1 === index ? true : false
|
chatHistory.length - 1 === index ? true : false
|
||||||
}
|
}
|
||||||
key={index}
|
key={index}
|
||||||
|
updateChat={() => {}}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,6 @@ export default function StorePage(): JSX.Element {
|
||||||
const loadingApiKey = useStoreStore((state) => state.loadingApiKey);
|
const loadingApiKey = useStoreStore((state) => state.loadingApiKey);
|
||||||
|
|
||||||
const setValidApiKey = useStoreStore((state) => state.updateValidApiKey);
|
const setValidApiKey = useStoreStore((state) => state.updateValidApiKey);
|
||||||
const setLoadingApiKey = useStoreStore((state) => state.updateLoadingApiKey);
|
|
||||||
const setHasApiKey = useStoreStore((state) => state.updateHasApiKey);
|
|
||||||
|
|
||||||
const { apiKey } = useContext(AuthContext);
|
const { apiKey } = useContext(AuthContext);
|
||||||
|
|
||||||
|
|
@ -48,6 +46,9 @@ export default function StorePage(): JSX.Element {
|
||||||
const setCurrentFlowId = useFlowsManagerStore(
|
const setCurrentFlowId = useFlowsManagerStore(
|
||||||
(state) => state.setCurrentFlowId
|
(state) => state.setCurrentFlowId
|
||||||
);
|
);
|
||||||
|
const currentFlowId = useFlowsManagerStore(
|
||||||
|
(state) => state.currentFlowId
|
||||||
|
);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [loadingTags, setLoadingTags] = useState(true);
|
const [loadingTags, setLoadingTags] = useState(true);
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
@ -63,10 +64,6 @@ export default function StorePage(): JSX.Element {
|
||||||
const [searchNow, setSearchNow] = useState("");
|
const [searchNow, setSearchNow] = useState("");
|
||||||
const [selectFilter, setSelectFilter] = useState("all");
|
const [selectFilter, setSelectFilter] = useState("all");
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
handleGetTags();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loadingApiKey) {
|
if (!loadingApiKey) {
|
||||||
if (!hasApiKey) {
|
if (!hasApiKey) {
|
||||||
|
|
@ -86,9 +83,10 @@ export default function StorePage(): JSX.Element {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [loadingApiKey, validApiKey, hasApiKey]);
|
}, [loadingApiKey, validApiKey, hasApiKey, currentFlowId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
handleGetTags();
|
||||||
handleGetComponents();
|
handleGetComponents();
|
||||||
}, [
|
}, [
|
||||||
tabActive,
|
tabActive,
|
||||||
|
|
@ -119,7 +117,7 @@ export default function StorePage(): JSX.Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleGetComponents() {
|
function handleGetComponents() {
|
||||||
if (!hasApiKey || loadingApiKey) return;
|
if (loadingApiKey) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
getStoreComponents({
|
getStoreComponents({
|
||||||
component_id: id,
|
component_id: id,
|
||||||
|
|
@ -176,23 +174,6 @@ export default function StorePage(): JSX.Element {
|
||||||
setPageSize(12);
|
setPageSize(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchApiData = async () => {
|
|
||||||
setLoadingApiKey(true);
|
|
||||||
try {
|
|
||||||
const res = await checkHasApiKey();
|
|
||||||
setHasApiKey(res?.has_api_key ?? false);
|
|
||||||
setValidApiKey(res?.is_valid ?? false);
|
|
||||||
setLoadingApiKey(false);
|
|
||||||
} catch (e) {
|
|
||||||
setLoadingApiKey(false);
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchApiData();
|
|
||||||
}, [apiKey]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout
|
<PageLayout
|
||||||
betaIcon
|
betaIcon
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,34 @@
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { checkHasStore } from "../controllers/API";
|
import { checkHasApiKey, checkHasStore } from "../controllers/API";
|
||||||
import { StoreStoreType } from "../types/zustand/store";
|
import { StoreStoreType } from "../types/zustand/store";
|
||||||
|
import useAlertStore from "./alertStore";
|
||||||
|
|
||||||
export const useStoreStore = create<StoreStoreType>((set) => ({
|
export const useStoreStore = create<StoreStoreType>((set) => ({
|
||||||
hasStore: true,
|
hasStore: true,
|
||||||
validApiKey: false,
|
validApiKey: false,
|
||||||
hasApiKey: false,
|
hasApiKey: false,
|
||||||
loadingApiKey: true,
|
loadingApiKey: true,
|
||||||
updateHasStore: (hasStore) => set(() => ({ hasStore: hasStore })),
|
checkHasStore: () => {
|
||||||
|
checkHasStore().then((res) => {
|
||||||
|
set({ hasStore: res?.enabled ?? false });
|
||||||
|
});
|
||||||
|
},
|
||||||
updateValidApiKey: (validApiKey) => set(() => ({ validApiKey: validApiKey })),
|
updateValidApiKey: (validApiKey) => set(() => ({ validApiKey: validApiKey })),
|
||||||
updateLoadingApiKey: (loadingApiKey) =>
|
updateLoadingApiKey: (loadingApiKey) =>
|
||||||
set(() => ({ loadingApiKey: loadingApiKey })),
|
set(() => ({ loadingApiKey: loadingApiKey })),
|
||||||
updateHasApiKey: (hasApiKey) => set(() => ({ hasApiKey: hasApiKey })),
|
updateHasApiKey: (hasApiKey) => set(() => ({ hasApiKey: hasApiKey })),
|
||||||
|
fetchApiData: async () => {
|
||||||
|
set({ loadingApiKey: true });
|
||||||
|
try {
|
||||||
|
const res = await checkHasApiKey();
|
||||||
|
set({
|
||||||
|
validApiKey: res?.is_valid ?? false,
|
||||||
|
hasApiKey: res?.has_api_key ?? false,
|
||||||
|
loadingApiKey: false,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
set({ loadingApiKey: false });
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
checkHasStore().then((res) => {
|
|
||||||
useStoreStore.setState({ hasStore: res?.enabled ?? false });
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@
|
||||||
@apply fixed bottom-4 right-4;
|
@apply fixed bottom-4 right-4;
|
||||||
}
|
}
|
||||||
.message-button-icon {
|
.message-button-icon {
|
||||||
@apply fill-chat-trigger stroke-chat-trigger stroke-1;
|
@apply fill-medium-indigo stroke-medium-indigo stroke-1;
|
||||||
}
|
}
|
||||||
.disabled-message-button-icon {
|
.disabled-message-button-icon {
|
||||||
@apply fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1;
|
@apply fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1;
|
||||||
|
|
|
||||||
|
|
@ -524,11 +524,7 @@ export type chatMessagePropsType = {
|
||||||
chat: ChatMessageType;
|
chat: ChatMessageType;
|
||||||
lockChat: boolean;
|
lockChat: boolean;
|
||||||
lastMessage: boolean;
|
lastMessage: boolean;
|
||||||
updateChat?: (
|
updateChat: (chat: ChatMessageType, message: string, stream_url: string) => void;
|
||||||
chat: ChatMessageType,
|
|
||||||
message: string,
|
|
||||||
stream_url: string | null
|
|
||||||
) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type formModalPropsType = {
|
export type formModalPropsType = {
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,9 @@ export type StoreStoreType = {
|
||||||
validApiKey: boolean;
|
validApiKey: boolean;
|
||||||
hasApiKey: boolean;
|
hasApiKey: boolean;
|
||||||
loadingApiKey: boolean;
|
loadingApiKey: boolean;
|
||||||
updateHasStore: (hasStore: boolean) => void;
|
checkHasStore: () => void;
|
||||||
updateValidApiKey: (validApiKey: boolean) => void;
|
updateValidApiKey: (validApiKey: boolean) => void;
|
||||||
updateHasApiKey: (hasApiKey: boolean) => void;
|
updateHasApiKey: (hasApiKey: boolean) => void;
|
||||||
updateLoadingApiKey: (loadingApiKey: boolean) => void;
|
updateLoadingApiKey: (loadingApiKey: boolean) => void;
|
||||||
|
fetchApiData: () => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -58,11 +58,12 @@ export function normalCaseToSnakeCase(str: string): string {
|
||||||
.join("_");
|
.join("_");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toTitleCase(str: string | undefined): string {
|
export function toTitleCase(str: string | undefined, isNodeField?: boolean): string {
|
||||||
if (!str) return "";
|
if (!str) return "";
|
||||||
let result = str
|
let result = str
|
||||||
.split("_")
|
.split("_")
|
||||||
.map((word, index) => {
|
.map((word, index) => {
|
||||||
|
if (isNodeField) return word
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
return checkUpperWords(
|
return checkUpperWords(
|
||||||
word[0].toUpperCase() + word.slice(1).toLowerCase()
|
word[0].toUpperCase() + word.slice(1).toLowerCase()
|
||||||
|
|
@ -75,6 +76,7 @@ export function toTitleCase(str: string | undefined): string {
|
||||||
return result
|
return result
|
||||||
.split("-")
|
.split("-")
|
||||||
.map((word, index) => {
|
.map((word, index) => {
|
||||||
|
if (isNodeField) return word
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
return checkUpperWords(
|
return checkUpperWords(
|
||||||
word[0].toUpperCase() + word.slice(1).toLowerCase()
|
word[0].toUpperCase() + word.slice(1).toLowerCase()
|
||||||
|
|
@ -642,6 +644,6 @@ export function getFieldTitle(
|
||||||
return template[templateField].display_name
|
return template[templateField].display_name
|
||||||
? template[templateField].display_name!
|
? template[templateField].display_name!
|
||||||
: template[templateField].name
|
: template[templateField].name
|
||||||
? toTitleCase(template[templateField].name!)
|
? toTitleCase(template[templateField].name!, true)
|
||||||
: toTitleCase(templateField);
|
: toTitleCase(templateField, true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue