feat: changed tweaks into input schema and enabled persistence (#8235)
* Fixed layground and share font weight * Fixed namings and deleted unused api files * Deleted unused code * Changed tweaks to input schema * Changed TableAdvancedToggleCellRender to change Tweaks instead of Nodes if in tweaks page * Removed Hide Visibility to show tweaks' Enable Input * Set advanced of all nodes as true in tweaks * Changed column defs to change width and pass isTweaks to advanced * Changed tweaks store to identify changes in the Advanced field instead of the Values field * Changed to not change quantity of hooks * Changed table node cell render to not change the value of the tweaks * Changed to disable inputs * Added disabled behavior to table node cell render * Added cursor default to cell classes * Updated tweaks test * Added persistence in session * Removed initial setup from api modal to enable persistence * Added persistence to storage * Changed test to test persistence of tweaks * Re-added initial setup to api modal * Fixed size of tweaks modal
This commit is contained in:
parent
c13d3eb21b
commit
d88abdd658
25 changed files with 319 additions and 957 deletions
|
|
@ -4,7 +4,7 @@ import Markdown from "react-markdown";
|
||||||
import rehypeMathjax from "rehype-mathjax";
|
import rehypeMathjax from "rehype-mathjax";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
import ForwardedIconComponent from "../../common/genericIconComponent";
|
import ForwardedIconComponent from "../../common/genericIconComponent";
|
||||||
import SimplifiedCodeTabComponent from "../codeTabsComponent/ChatCodeTabComponent";
|
import SimplifiedCodeTabComponent from "../codeTabsComponent";
|
||||||
import DurationDisplay from "./DurationDisplay";
|
import DurationDisplay from "./DurationDisplay";
|
||||||
|
|
||||||
export default function ContentDisplay({
|
export default function ContentDisplay({
|
||||||
|
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
import { useState } from "react";
|
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
|
||||||
import { tomorrow } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
|
||||||
import IconComponent from "../../common/genericIconComponent";
|
|
||||||
import { Button } from "../../ui/button";
|
|
||||||
|
|
||||||
type SimplifiedCodeTabProps = {
|
|
||||||
code: string;
|
|
||||||
language: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function SimplifiedCodeTabComponent({
|
|
||||||
code,
|
|
||||||
language,
|
|
||||||
}: SimplifiedCodeTabProps) {
|
|
||||||
const [isCopied, setIsCopied] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const copyToClipboard = () => {
|
|
||||||
if (!navigator.clipboard || !navigator.clipboard.writeText) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
navigator.clipboard.writeText(code).then(() => {
|
|
||||||
setIsCopied(true);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsCopied(false);
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="mt-2 flex w-full flex-col overflow-hidden rounded-md text-left dark"
|
|
||||||
data-testid="chat-code-tab"
|
|
||||||
>
|
|
||||||
<div className="flex w-full items-center justify-between rounded-t-md border border-b-0 border-border bg-muted px-4 py-2">
|
|
||||||
<span className="dar text-sm font-semibold text-white">{language}</span>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="text-muted-foreground hover:bg-card"
|
|
||||||
data-testid="copy-code-button"
|
|
||||||
onClick={copyToClipboard}
|
|
||||||
>
|
|
||||||
{isCopied ? (
|
|
||||||
<IconComponent name="Check" className="h-4 w-4" />
|
|
||||||
) : (
|
|
||||||
<IconComponent name="Copy" className="h-4 w-4" />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<SyntaxHighlighter
|
|
||||||
language={language.toLowerCase()}
|
|
||||||
style={tomorrow}
|
|
||||||
className="!mt-0 h-full w-full overflow-scroll !rounded-b-md !rounded-t-none border border-border text-left !custom-scroll"
|
|
||||||
>
|
|
||||||
{code}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -37,7 +37,6 @@ export function TweakComponent({
|
||||||
<EditNodeComponent
|
<EditNodeComponent
|
||||||
open={open}
|
open={open}
|
||||||
autoHeight
|
autoHeight
|
||||||
hideVisibility
|
|
||||||
nodeClass={nodeClass}
|
nodeClass={nodeClass}
|
||||||
isTweaks
|
isTweaks
|
||||||
nodeId={node.data.id}
|
nodeId={node.data.id}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,26 @@
|
||||||
import ShadTooltip from "@/components/common/shadTooltipComponent";
|
|
||||||
import { useTweaksStore } from "@/stores/tweaksStore";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||||
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
import { tomorrow } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||||
import { useDarkStore } from "../../../stores/darkStore";
|
|
||||||
import { codeTabsPropsType } from "../../../types/components";
|
|
||||||
import IconComponent from "../../common/genericIconComponent";
|
import IconComponent from "../../common/genericIconComponent";
|
||||||
import { Button } from "../../ui/button";
|
import { Button } from "../../ui/button";
|
||||||
import { Label } from "../../ui/label";
|
|
||||||
import { Switch } from "../../ui/switch";
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../ui/tabs";
|
|
||||||
import { TweaksComponent } from "./components/tweaksComponent";
|
|
||||||
|
|
||||||
export default function CodeTabsComponent({
|
type SimplifiedCodeTabProps = {
|
||||||
open,
|
code: string;
|
||||||
tabs,
|
language: string;
|
||||||
activeTab,
|
};
|
||||||
setActiveTab,
|
|
||||||
isMessage,
|
export default function SimplifiedCodeTabComponent({
|
||||||
setActiveTweaks,
|
code,
|
||||||
activeTweaks,
|
language,
|
||||||
}: codeTabsPropsType) {
|
}: SimplifiedCodeTabProps) {
|
||||||
const [isCopied, setIsCopied] = useState<Boolean>(false);
|
const [isCopied, setIsCopied] = useState<boolean>(false);
|
||||||
const dark = useDarkStore((state) => state.dark);
|
|
||||||
const nodes = useTweaksStore((state) => state.nodes);
|
|
||||||
|
|
||||||
const copyToClipboard = () => {
|
const copyToClipboard = () => {
|
||||||
if (!navigator.clipboard || !navigator.clipboard.writeText) {
|
if (!navigator.clipboard || !navigator.clipboard.writeText) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.clipboard.writeText(tabs[activeTab].code).then(() => {
|
navigator.clipboard.writeText(code).then(() => {
|
||||||
setIsCopied(true);
|
setIsCopied(true);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -40,121 +30,33 @@ export default function CodeTabsComponent({
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<div
|
||||||
value={activeTab}
|
className="mt-2 flex w-full flex-col overflow-hidden rounded-md text-left dark"
|
||||||
className={
|
data-testid="chat-code-tab"
|
||||||
"api-modal-tabs inset-0 m-0 " +
|
|
||||||
(isMessage ? "dark" : "") +
|
|
||||||
(dark && isMessage ? "bg-background" : "")
|
|
||||||
}
|
|
||||||
onValueChange={(value) => {
|
|
||||||
setActiveTab(value);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div className="api-modal-tablist-div">
|
<div className="flex w-full items-center justify-between rounded-t-md border border-b-0 border-border bg-muted px-4 py-2">
|
||||||
{tabs.length > 0 && tabs[0].name !== "" ? (
|
<span className="dar text-sm font-semibold text-white">{language}</span>
|
||||||
<TabsList className="mt-[-1px] border-b">
|
<Button
|
||||||
{tabs.map((tab, index) => (
|
variant="ghost"
|
||||||
<TabsTrigger
|
size="icon"
|
||||||
className={
|
className="text-muted-foreground hover:bg-card"
|
||||||
isMessage ? "data-[state=active]:bg-primary-foreground" : ""
|
data-testid="copy-code-button"
|
||||||
}
|
onClick={copyToClipboard}
|
||||||
key={index}
|
|
||||||
value={index.toString()}
|
|
||||||
>
|
|
||||||
{tab.name}
|
|
||||||
</TabsTrigger>
|
|
||||||
))}
|
|
||||||
</TabsList>
|
|
||||||
) : (
|
|
||||||
<div></div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{tabs.map((tab, idx) => (
|
|
||||||
<TabsContent
|
|
||||||
value={idx.toString()}
|
|
||||||
className="api-modal-tabs-content overflow-hidden dark"
|
|
||||||
key={idx} // Remember to add a unique key prop
|
|
||||||
>
|
>
|
||||||
{tab.name.toLowerCase() !== "tweaks" ? (
|
{isCopied ? (
|
||||||
<div className="mt-2 flex h-full w-full flex-col">
|
<IconComponent name="Check" className="h-4 w-4" />
|
||||||
{tab.description && (
|
) : (
|
||||||
<div
|
<IconComponent name="Copy" className="h-4 w-4" />
|
||||||
className="mb-2 w-full text-left text-sm"
|
)}
|
||||||
dangerouslySetInnerHTML={{ __html: tab.description }}
|
</Button>
|
||||||
></div>
|
</div>
|
||||||
)}
|
<SyntaxHighlighter
|
||||||
<div className="flex w-full items-center justify-end gap-4 rounded-t-md border border-border bg-muted px-4 py-2">
|
language={language.toLowerCase()}
|
||||||
{nodes.length > 0 &&
|
style={tomorrow}
|
||||||
tabs.find((tab) => tab.name.toLowerCase() === "tweaks") &&
|
className="!mt-0 h-full w-full overflow-scroll !rounded-b-md !rounded-t-none border border-border text-left !custom-scroll"
|
||||||
tabs[activeTab].hasTweaks && (
|
>
|
||||||
<div className="flex items-center gap-2">
|
{code}
|
||||||
<ShadTooltip content="Makes temporary adjustments managed in 'Tweaks'">
|
</SyntaxHighlighter>
|
||||||
<div className="flex items-center gap-2">
|
</div>
|
||||||
<Label
|
|
||||||
className={"text-xs font-medium text-white"}
|
|
||||||
htmlFor="tweaks-switch"
|
|
||||||
>
|
|
||||||
Enable Tweaks
|
|
||||||
</Label>
|
|
||||||
<IconComponent
|
|
||||||
name="info"
|
|
||||||
className="h-3.5 w-3.5 text-placeholder-foreground"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ShadTooltip>
|
|
||||||
<Switch
|
|
||||||
style={{
|
|
||||||
transform: `scaleX(${0.85}) scaleY(${0.85})`,
|
|
||||||
}}
|
|
||||||
id="tweaks-switch"
|
|
||||||
onCheckedChange={setActiveTweaks}
|
|
||||||
checked={activeTweaks}
|
|
||||||
autoFocus={false}
|
|
||||||
/>
|
|
||||||
<span className="text-lg text-accent">|</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{tabs[activeTab].name.toLowerCase !== "tweaks" && (
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
onClick={copyToClipboard}
|
|
||||||
data-testid="btn-copy-code"
|
|
||||||
>
|
|
||||||
{isCopied ? (
|
|
||||||
<IconComponent
|
|
||||||
name="Check"
|
|
||||||
className="h-4 w-4 text-muted-foreground"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<IconComponent
|
|
||||||
name="Copy"
|
|
||||||
className="h-4 w-4 text-muted-foreground"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<SyntaxHighlighter
|
|
||||||
language={tab.language}
|
|
||||||
style={oneDark}
|
|
||||||
className="!my-0 h-full overflow-auto rounded-sm !rounded-t-none border border-t-0 border-border text-left custom-scroll"
|
|
||||||
>
|
|
||||||
{tab.code}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
</div>
|
|
||||||
) : tab.name.toLowerCase() === "tweaks" ? (
|
|
||||||
<>
|
|
||||||
<TweaksComponent open={open ?? false} />
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</TabsContent>
|
|
||||||
))}
|
|
||||||
</Tabs>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import { usePatchUpdateFlow } from "@/controllers/API/queries/flows/use-patch-up
|
||||||
import { CustomLink } from "@/customization/components/custom-link";
|
import { CustomLink } from "@/customization/components/custom-link";
|
||||||
import { ENABLE_PUBLISH, ENABLE_WIDGET } from "@/customization/feature-flags";
|
import { ENABLE_PUBLISH, ENABLE_WIDGET } from "@/customization/feature-flags";
|
||||||
import { customMcpOpen } from "@/customization/utils/custom-mcp-open";
|
import { customMcpOpen } from "@/customization/utils/custom-mcp-open";
|
||||||
import ApiModal from "@/modals/apiModal/new-api-modal";
|
import ApiModal from "@/modals/apiModal";
|
||||||
import EmbedModal from "@/modals/EmbedModal/embed-modal";
|
import EmbedModal from "@/modals/EmbedModal/embed-modal";
|
||||||
import useAlertStore from "@/stores/alertStore";
|
import useAlertStore from "@/stores/alertStore";
|
||||||
import useAuthStore from "@/stores/authStore";
|
import useAuthStore from "@/stores/authStore";
|
||||||
|
|
@ -87,7 +87,7 @@ export default function PublishDropdown() {
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="md"
|
size="md"
|
||||||
className="!px-2.5 font-medium"
|
className="!px-2.5 font-normal"
|
||||||
data-testid="publish-button"
|
data-testid="publish-button"
|
||||||
>
|
>
|
||||||
Share
|
Share
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
import useHandleOnNewValue from "@/CustomNodes/hooks/use-handle-new-value";
|
import useHandleOnNewValue from "@/CustomNodes/hooks/use-handle-new-value";
|
||||||
import ShadTooltip from "@/components/common/shadTooltipComponent";
|
import ShadTooltip from "@/components/common/shadTooltipComponent";
|
||||||
import useFlowStore from "@/stores/flowStore";
|
import useFlowStore from "@/stores/flowStore";
|
||||||
|
import { useTweaksStore } from "@/stores/tweaksStore";
|
||||||
import { APIClassType } from "@/types/api";
|
import { APIClassType } from "@/types/api";
|
||||||
import { isTargetHandleConnected } from "@/utils/reactflowUtils";
|
import { isTargetHandleConnected } from "@/utils/reactflowUtils";
|
||||||
import { CustomCellRendererProps } from "ag-grid-react";
|
import { CustomCellRendererProps } from "ag-grid-react";
|
||||||
import ToggleShadComponent from "../../../toggleShadComponent";
|
import ToggleShadComponent from "../../../toggleShadComponent";
|
||||||
|
|
||||||
export default function TableAdvancedToggleCellRender({
|
export default function TableAdvancedToggleCellRender({
|
||||||
value: { nodeId, parameterId },
|
value: { nodeId, parameterId, isTweaks },
|
||||||
}: CustomCellRendererProps) {
|
}: CustomCellRendererProps) {
|
||||||
const edges = useFlowStore((state) => state.edges);
|
const edges = useFlowStore((state) => state.edges);
|
||||||
const node = useFlowStore((state) => state.getNode(nodeId));
|
const node = isTweaks
|
||||||
|
? useTweaksStore((state) => state.getNode(nodeId))
|
||||||
|
: useFlowStore((state) => state.getNode(nodeId));
|
||||||
const parameter = node?.data?.node?.template?.[parameterId];
|
const parameter = node?.data?.node?.template?.[parameterId];
|
||||||
|
|
||||||
|
const setNode = useTweaksStore((state) => state.setNode);
|
||||||
|
|
||||||
const disabled = isTargetHandleConnected(
|
const disabled = isTargetHandleConnected(
|
||||||
edges,
|
edges,
|
||||||
parameterId,
|
parameterId,
|
||||||
|
|
@ -24,6 +29,7 @@ export default function TableAdvancedToggleCellRender({
|
||||||
node: node?.data.node as APIClassType,
|
node: node?.data.node as APIClassType,
|
||||||
nodeId,
|
nodeId,
|
||||||
name: parameterId,
|
name: parameterId,
|
||||||
|
setNode: isTweaks ? setNode : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -31,22 +37,24 @@ export default function TableAdvancedToggleCellRender({
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
content={
|
content={
|
||||||
disabled
|
disabled
|
||||||
? "Cannot change visibility of connected handles"
|
? isTweaks
|
||||||
: "Change visibility of the field"
|
? "Cannot enable input of connected handles"
|
||||||
|
: "Cannot change visibility of connected handles"
|
||||||
|
: isTweaks
|
||||||
|
? "Toggle input of the field in the API"
|
||||||
|
: "Change visibility of the field"
|
||||||
}
|
}
|
||||||
styleClasses="z-50"
|
styleClasses="z-50"
|
||||||
>
|
>
|
||||||
<div>
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<div className="flex h-full items-center">
|
<ToggleShadComponent
|
||||||
<ToggleShadComponent
|
disabled={disabled}
|
||||||
disabled={disabled}
|
value={!parameter.advanced}
|
||||||
value={!parameter.advanced}
|
handleOnNewValue={handleOnNewValue}
|
||||||
handleOnNewValue={handleOnNewValue}
|
editNode={true}
|
||||||
editNode={true}
|
showToogle
|
||||||
showToogle
|
id={"show" + parameterId}
|
||||||
id={"show" + parameterId}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import { ParameterRenderComponent } from "@/components/core/parameterRenderCompo
|
||||||
import { NodeInfoType } from "@/components/core/parameterRenderComponent/types";
|
import { NodeInfoType } from "@/components/core/parameterRenderComponent/types";
|
||||||
import useAuthStore from "@/stores/authStore";
|
import useAuthStore from "@/stores/authStore";
|
||||||
import useFlowStore from "@/stores/flowStore";
|
import useFlowStore from "@/stores/flowStore";
|
||||||
import { useTweaksStore } from "@/stores/tweaksStore";
|
|
||||||
import { APIClassType } from "@/types/api";
|
import { APIClassType } from "@/types/api";
|
||||||
import { isTargetHandleConnected } from "@/utils/reactflowUtils";
|
import { isTargetHandleConnected } from "@/utils/reactflowUtils";
|
||||||
|
import { cn } from "@/utils/utils";
|
||||||
import { CustomCellRendererProps } from "ag-grid-react";
|
import { CustomCellRendererProps } from "ag-grid-react";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
|
@ -14,15 +14,11 @@ export default function TableNodeCellRender({
|
||||||
value: { nodeId, parameterId, isTweaks },
|
value: { nodeId, parameterId, isTweaks },
|
||||||
}: CustomCellRendererProps) {
|
}: CustomCellRendererProps) {
|
||||||
const edges = useFlowStore((state) => state.edges);
|
const edges = useFlowStore((state) => state.edges);
|
||||||
const node = isTweaks
|
const node = useFlowStore((state) => state.getNode(nodeId));
|
||||||
? useTweaksStore((state) => state.getNode(nodeId))
|
|
||||||
: useFlowStore((state) => state.getNode(nodeId));
|
|
||||||
const parameter = node?.data?.node?.template?.[parameterId];
|
const parameter = node?.data?.node?.template?.[parameterId];
|
||||||
const currentFlow = useFlowStore((state) => state.currentFlow);
|
const currentFlow = useFlowStore((state) => state.currentFlow);
|
||||||
const isAuth = useAuthStore((state) => state.isAuthenticated);
|
const isAuth = useAuthStore((state) => state.isAuthenticated);
|
||||||
|
|
||||||
const setNode = useTweaksStore((state) => state.setNode);
|
|
||||||
|
|
||||||
const disabled = isTargetHandleConnected(
|
const disabled = isTargetHandleConnected(
|
||||||
edges,
|
edges,
|
||||||
parameterId,
|
parameterId,
|
||||||
|
|
@ -34,12 +30,12 @@ export default function TableNodeCellRender({
|
||||||
node: node?.data.node as APIClassType,
|
node: node?.data.node as APIClassType,
|
||||||
nodeId,
|
nodeId,
|
||||||
name: parameterId,
|
name: parameterId,
|
||||||
setNode: isTweaks ? setNode : undefined,
|
setNode: isTweaks ? () => {} : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { handleNodeClass } = useHandleNodeClass(
|
const { handleNodeClass } = useHandleNodeClass(
|
||||||
nodeId,
|
nodeId,
|
||||||
isTweaks ? setNode : undefined,
|
isTweaks ? () => {} : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const nodeInformationMetadata: NodeInfoType = useMemo(() => {
|
const nodeInformationMetadata: NodeInfoType = useMemo(() => {
|
||||||
|
|
@ -54,7 +50,12 @@ export default function TableNodeCellRender({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
parameter && (
|
parameter && (
|
||||||
<div className="group mx-auto flex h-full max-h-48 w-[300px] items-center justify-center overflow-auto px-1 py-2.5 custom-scroll">
|
<div
|
||||||
|
className={cn(
|
||||||
|
"group mx-auto flex h-full max-h-48 w-[300px] items-center justify-center overflow-auto px-1 py-2.5 custom-scroll",
|
||||||
|
isTweaks && "pointer-events-none opacity-70",
|
||||||
|
)}
|
||||||
|
>
|
||||||
<ParameterRenderComponent
|
<ParameterRenderComponent
|
||||||
nodeId={nodeId}
|
nodeId={nodeId}
|
||||||
handleOnNewValue={handleOnNewValue}
|
handleOnNewValue={handleOnNewValue}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import {
|
|
||||||
getCurlRunCode,
|
|
||||||
getCurlWebhookCode,
|
|
||||||
} from "@/modals/apiModal/utils/get-curl-code";
|
|
||||||
import getJsApiCode from "@/modals/apiModal/utils/get-js-api-code";
|
|
||||||
import getPythonApiCode from "@/modals/apiModal/utils/get-python-api-code";
|
|
||||||
import getPythonCode from "@/modals/apiModal/utils/get-python-code";
|
|
||||||
import getWidgetCode from "@/modals/apiModal/utils/get-widget-code";
|
|
||||||
|
|
||||||
export function useCustomAPICode() {
|
|
||||||
return {
|
|
||||||
getCurlRunCode,
|
|
||||||
getCurlWebhookCode,
|
|
||||||
getJsApiCode,
|
|
||||||
getPythonApiCode,
|
|
||||||
getPythonCode,
|
|
||||||
getWidgetCode,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { cn } from "@/utils/utils";
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
import Markdown from "react-markdown";
|
import Markdown from "react-markdown";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
import CodeTabsComponent from "../../../../../../components/core/codeTabsComponent/ChatCodeTabComponent";
|
import CodeTabsComponent from "../../../../../../components/core/codeTabsComponent";
|
||||||
import LogoIcon from "./chat-logo-icon";
|
import LogoIcon from "./chat-logo-icon";
|
||||||
|
|
||||||
export const ErrorView = ({
|
export const ErrorView = ({
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import Markdown from "react-markdown";
|
||||||
import rehypeMathjax from "rehype-mathjax";
|
import rehypeMathjax from "rehype-mathjax";
|
||||||
import rehypeRaw from "rehype-raw";
|
import rehypeRaw from "rehype-raw";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
import CodeTabsComponent from "../../../../../../components/core/codeTabsComponent/ChatCodeTabComponent";
|
import CodeTabsComponent from "../../../../../../components/core/codeTabsComponent";
|
||||||
|
|
||||||
type MarkdownFieldProps = {
|
type MarkdownFieldProps = {
|
||||||
chat: any;
|
chat: any;
|
||||||
|
|
|
||||||
|
|
@ -1,73 +1,226 @@
|
||||||
|
import { TweaksComponent } from "@/components/core/codeTabsComponent/components/tweaksComponent";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { CustomAPIGenerator } from "@/customization/components/custom-api-generator";
|
import { CustomAPIGenerator } from "@/customization/components/custom-api-generator";
|
||||||
import { useCustomAPICode } from "@/customization/hooks/use-custom-api-code";
|
import useSaveFlow from "@/hooks/flows/use-save-flow";
|
||||||
import useAuthStore from "@/stores/authStore";
|
import useAuthStore from "@/stores/authStore";
|
||||||
|
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
||||||
|
import useFlowStore from "@/stores/flowStore";
|
||||||
|
import { isEndpointNameValid } from "@/utils/utils";
|
||||||
import "ace-builds/src-noconflict/ext-language_tools";
|
import "ace-builds/src-noconflict/ext-language_tools";
|
||||||
import "ace-builds/src-noconflict/mode-python";
|
import "ace-builds/src-noconflict/mode-python";
|
||||||
import "ace-builds/src-noconflict/theme-github";
|
import "ace-builds/src-noconflict/theme-github";
|
||||||
import "ace-builds/src-noconflict/theme-twilight";
|
import "ace-builds/src-noconflict/theme-twilight";
|
||||||
import { ReactNode, useEffect, useState } from "react";
|
import { cloneDeep } from "lodash";
|
||||||
|
import { ChangeEvent, ReactNode, useEffect, useState } from "react";
|
||||||
|
import { useShallow } from "zustand/react/shallow";
|
||||||
import IconComponent from "../../components/common/genericIconComponent";
|
import IconComponent from "../../components/common/genericIconComponent";
|
||||||
import CodeTabsComponent from "../../components/core/codeTabsComponent";
|
|
||||||
import { EXPORT_CODE_DIALOG } from "../../constants/constants";
|
|
||||||
import { useTweaksStore } from "../../stores/tweaksStore";
|
import { useTweaksStore } from "../../stores/tweaksStore";
|
||||||
import { FlowType } from "../../types/flow/index";
|
|
||||||
import BaseModal from "../baseModal";
|
import BaseModal from "../baseModal";
|
||||||
|
import APITabsComponent from "./codeTabs/code-tabs";
|
||||||
|
|
||||||
|
const MAX_LENGTH = 20;
|
||||||
|
const MIN_LENGTH = 1;
|
||||||
|
|
||||||
export default function ApiModal({
|
export default function ApiModal({
|
||||||
flow,
|
|
||||||
children,
|
children,
|
||||||
open: myOpen,
|
open: myOpen,
|
||||||
setOpen: mySetOpen,
|
setOpen: mySetOpen,
|
||||||
}: {
|
}: {
|
||||||
flow: FlowType;
|
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
setOpen?: (a: boolean | ((o?: boolean) => boolean)) => void;
|
setOpen?: (a: boolean | ((o?: boolean) => boolean)) => void;
|
||||||
}) {
|
}) {
|
||||||
const autoLogin = useAuthStore((state) => state.autoLogin);
|
const autoLogin = useAuthStore((state) => state.autoLogin);
|
||||||
|
const nodes = useFlowStore((state) => state.nodes);
|
||||||
|
const [openTweaks, setOpenTweaks] = useState(false);
|
||||||
|
const tweaks = useTweaksStore((state) => state.tweaks);
|
||||||
const [open, setOpen] =
|
const [open, setOpen] =
|
||||||
mySetOpen !== undefined && myOpen !== undefined
|
mySetOpen !== undefined && myOpen !== undefined
|
||||||
? [myOpen, mySetOpen]
|
? [myOpen, mySetOpen]
|
||||||
: useState(false);
|
: useState(false);
|
||||||
const [activeTab, setActiveTab] = useState("0");
|
|
||||||
const activeTweaks = useTweaksStore((state) => state.activeTweaks);
|
|
||||||
const setActiveTweaks = useTweaksStore((state) => state.setActiveTweaks);
|
|
||||||
const tabs = useTweaksStore((state) => state.tabs);
|
|
||||||
const initialSetup = useTweaksStore((state) => state.initialSetup);
|
const initialSetup = useTweaksStore((state) => state.initialSetup);
|
||||||
|
|
||||||
const getCodes = useCustomAPICode();
|
const flowEndpointName = useFlowStore(
|
||||||
|
useShallow((state) => state.currentFlow?.endpoint_name),
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentFlowId = useFlowStore(
|
||||||
|
useShallow((state) => state.currentFlow?.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [endpointName, setEndpointName] = useState(flowEndpointName ?? "");
|
||||||
|
const [validEndpointName, setValidEndpointName] = useState(true);
|
||||||
|
|
||||||
|
const handleEndpointNameChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const { value } = event.target;
|
||||||
|
// Validate the endpoint name
|
||||||
|
// use this regex r'^[a-zA-Z0-9_-]+$'
|
||||||
|
const isValid = isEndpointNameValid(event.target.value, MAX_LENGTH);
|
||||||
|
setValidEndpointName(isValid);
|
||||||
|
|
||||||
|
// Only update if valid and meets minimum length (if set)
|
||||||
|
if (isValid && value.length >= MIN_LENGTH) {
|
||||||
|
setEndpointName!(value);
|
||||||
|
} else if (value.length === 0) {
|
||||||
|
// Always allow empty endpoint name (it's optional)
|
||||||
|
setEndpointName!("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) initialSetup(autoLogin ?? false, flow, getCodes);
|
if (open && currentFlowId) initialSetup(nodes, currentFlowId);
|
||||||
setActiveTab("0");
|
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
|
const autoSaving = useFlowsManagerStore((state) => state.autoSaving);
|
||||||
|
const saveFlow = useSaveFlow();
|
||||||
|
const setCurrentFlow = useFlowStore((state) => state.setCurrentFlow);
|
||||||
|
|
||||||
|
function handleSave(): void {
|
||||||
|
const newFlow = cloneDeep(useFlowStore.getState().currentFlow);
|
||||||
|
if (!newFlow) return;
|
||||||
|
newFlow.endpoint_name =
|
||||||
|
endpointName && endpointName.length > 0 ? endpointName : null;
|
||||||
|
|
||||||
|
if (autoSaving) {
|
||||||
|
saveFlow(newFlow);
|
||||||
|
} else {
|
||||||
|
setCurrentFlow(newFlow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!openTweaks && endpointName !== flowEndpointName) handleSave();
|
||||||
|
else if (openTweaks) {
|
||||||
|
setEndpointName(flowEndpointName ?? "");
|
||||||
|
}
|
||||||
|
}, [openTweaks]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseModal open={open} setOpen={setOpen} size="x-large">
|
<>
|
||||||
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
<BaseModal
|
||||||
<BaseModal.Header description={EXPORT_CODE_DIALOG}>
|
closeButtonClassName="!top-3"
|
||||||
<span className="pr-2">API</span>
|
open={open}
|
||||||
<IconComponent
|
setOpen={setOpen}
|
||||||
name="Code2"
|
size="medium"
|
||||||
className="h-6 w-6 pl-1 text-gray-800 dark:text-white"
|
className="pt-4"
|
||||||
aria-hidden="true"
|
>
|
||||||
/>
|
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
||||||
</BaseModal.Header>
|
<BaseModal.Header
|
||||||
<BaseModal.Content overflowHidden>
|
description={
|
||||||
{open && (
|
autoLogin ? undefined : (
|
||||||
<>
|
<>
|
||||||
<CustomAPIGenerator isOpen={open} />
|
<span className="pr-2">
|
||||||
<CodeTabsComponent
|
API access requires an API key. You can{" "}
|
||||||
open={open}
|
<a
|
||||||
tabs={tabs!}
|
href="/settings/api-keys"
|
||||||
activeTab={activeTab}
|
className="text-accent-pink-foreground"
|
||||||
setActiveTab={setActiveTab}
|
>
|
||||||
activeTweaks={activeTweaks}
|
{" "}
|
||||||
setActiveTweaks={setActiveTweaks}
|
create an API key
|
||||||
/>
|
</a>{" "}
|
||||||
</>
|
in settings.
|
||||||
)}
|
</span>
|
||||||
</BaseModal.Content>
|
</>
|
||||||
</BaseModal>
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconComponent
|
||||||
|
name="Code2"
|
||||||
|
className="h-6 w-6 text-gray-800 dark:text-white"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<span className="pl-2">API access</span>
|
||||||
|
{nodes.length > 0 && (
|
||||||
|
<div className="border-r-1 absolute right-12 flex items-center text-mmd font-medium leading-[16px]">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 px-3"
|
||||||
|
onClick={() => setOpenTweaks(true)}
|
||||||
|
data-testid="tweaks-button"
|
||||||
|
>
|
||||||
|
<IconComponent
|
||||||
|
name="SlidersHorizontal"
|
||||||
|
className="h-3.5 w-3.5"
|
||||||
|
/>
|
||||||
|
<span>Input Schema ({Object.keys(tweaks)?.length}) </span>
|
||||||
|
</Button>
|
||||||
|
<Separator orientation="vertical" className="ml-2 h-8" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</BaseModal.Header>
|
||||||
|
<BaseModal.Content overflowHidden>
|
||||||
|
{open && (
|
||||||
|
<>
|
||||||
|
<CustomAPIGenerator isOpen={open} />
|
||||||
|
<APITabsComponent />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</BaseModal.Content>
|
||||||
|
</BaseModal>
|
||||||
|
|
||||||
|
<BaseModal
|
||||||
|
open={openTweaks}
|
||||||
|
setOpen={setOpenTweaks}
|
||||||
|
size="medium-small-tall"
|
||||||
|
>
|
||||||
|
<BaseModal.Header
|
||||||
|
description={
|
||||||
|
autoLogin ? undefined : (
|
||||||
|
<>
|
||||||
|
<span className="pr-2">
|
||||||
|
API access requires an API key. You can{" "}
|
||||||
|
<a
|
||||||
|
href="/settings/api-keys"
|
||||||
|
className="text-accent-pink-foreground"
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
create an API key
|
||||||
|
</a>{" "}
|
||||||
|
in settings.
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<IconComponent name="SlidersHorizontal" className="text-f h-6 w-6" />
|
||||||
|
<span className="pl-2">Input Schema</span>
|
||||||
|
</BaseModal.Header>
|
||||||
|
<BaseModal.Content overflowHidden className="flex flex-col gap-4">
|
||||||
|
{true && (
|
||||||
|
<Label>
|
||||||
|
<div className="edit-flow-arrangement mt-2">
|
||||||
|
<span className="shrink-0 text-mmd font-medium">
|
||||||
|
Endpoint Name
|
||||||
|
</span>
|
||||||
|
{!validEndpointName && (
|
||||||
|
<span className="edit-flow-span">
|
||||||
|
Use only letters, numbers, hyphens, and underscores (
|
||||||
|
{MAX_LENGTH} characters max).
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
className="nopan nodelete nodrag noflow mt-2 font-normal"
|
||||||
|
onChange={handleEndpointNameChange}
|
||||||
|
type="text"
|
||||||
|
name="endpoint_name"
|
||||||
|
value={endpointName ?? ""}
|
||||||
|
placeholder="An alternative name to run the endpoint"
|
||||||
|
maxLength={MAX_LENGTH}
|
||||||
|
minLength={MIN_LENGTH}
|
||||||
|
id="endpoint_name"
|
||||||
|
/>
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
<div className="h-full w-full overflow-y-auto overflow-x-hidden rounded-lg bg-muted custom-scroll">
|
||||||
|
<TweaksComponent open={openTweaks} />
|
||||||
|
</div>
|
||||||
|
</BaseModal.Content>
|
||||||
|
</BaseModal>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,225 +0,0 @@
|
||||||
import { TweaksComponent } from "@/components/core/codeTabsComponent/components/tweaksComponent";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Label } from "@/components/ui/label";
|
|
||||||
import { Separator } from "@/components/ui/separator";
|
|
||||||
import { CustomAPIGenerator } from "@/customization/components/custom-api-generator";
|
|
||||||
import useSaveFlow from "@/hooks/flows/use-save-flow";
|
|
||||||
import useAuthStore from "@/stores/authStore";
|
|
||||||
import useFlowsManagerStore from "@/stores/flowsManagerStore";
|
|
||||||
import useFlowStore from "@/stores/flowStore";
|
|
||||||
import { isEndpointNameValid } from "@/utils/utils";
|
|
||||||
import "ace-builds/src-noconflict/ext-language_tools";
|
|
||||||
import "ace-builds/src-noconflict/mode-python";
|
|
||||||
import "ace-builds/src-noconflict/theme-github";
|
|
||||||
import "ace-builds/src-noconflict/theme-twilight";
|
|
||||||
import { cloneDeep } from "lodash";
|
|
||||||
import { ChangeEvent, ReactNode, useEffect, useState } from "react";
|
|
||||||
import { useShallow } from "zustand/react/shallow";
|
|
||||||
import IconComponent from "../../components/common/genericIconComponent";
|
|
||||||
import { useTweaksStore } from "../../stores/tweaksStore";
|
|
||||||
import BaseModal from "../baseModal";
|
|
||||||
import APITabsComponent from "./codeTabs/code-tabs";
|
|
||||||
|
|
||||||
const MAX_LENGTH = 20;
|
|
||||||
const MIN_LENGTH = 1;
|
|
||||||
|
|
||||||
export default function ApiModal({
|
|
||||||
children,
|
|
||||||
open: myOpen,
|
|
||||||
setOpen: mySetOpen,
|
|
||||||
}: {
|
|
||||||
children: ReactNode;
|
|
||||||
open?: boolean;
|
|
||||||
setOpen?: (a: boolean | ((o?: boolean) => boolean)) => void;
|
|
||||||
}) {
|
|
||||||
const autoLogin = useAuthStore((state) => state.autoLogin);
|
|
||||||
const nodes = useFlowStore((state) => state.nodes);
|
|
||||||
const [openTweaks, setOpenTweaks] = useState(false);
|
|
||||||
const tweaks = useTweaksStore((state) => state.tweaks);
|
|
||||||
const [open, setOpen] =
|
|
||||||
mySetOpen !== undefined && myOpen !== undefined
|
|
||||||
? [myOpen, mySetOpen]
|
|
||||||
: useState(false);
|
|
||||||
const newInitialSetup = useTweaksStore((state) => state.newInitialSetup);
|
|
||||||
|
|
||||||
const flowEndpointName = useFlowStore(
|
|
||||||
useShallow((state) => state.currentFlow?.endpoint_name),
|
|
||||||
);
|
|
||||||
|
|
||||||
const [endpointName, setEndpointName] = useState(flowEndpointName ?? "");
|
|
||||||
const [validEndpointName, setValidEndpointName] = useState(true);
|
|
||||||
|
|
||||||
const handleEndpointNameChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const { value } = event.target;
|
|
||||||
// Validate the endpoint name
|
|
||||||
// use this regex r'^[a-zA-Z0-9_-]+$'
|
|
||||||
const isValid = isEndpointNameValid(event.target.value, MAX_LENGTH);
|
|
||||||
setValidEndpointName(isValid);
|
|
||||||
|
|
||||||
// Only update if valid and meets minimum length (if set)
|
|
||||||
if (isValid && value.length >= MIN_LENGTH) {
|
|
||||||
setEndpointName!(value);
|
|
||||||
} else if (value.length === 0) {
|
|
||||||
// Always allow empty endpoint name (it's optional)
|
|
||||||
setEndpointName!("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (open) newInitialSetup(nodes);
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
const autoSaving = useFlowsManagerStore((state) => state.autoSaving);
|
|
||||||
const saveFlow = useSaveFlow();
|
|
||||||
const setCurrentFlow = useFlowStore((state) => state.setCurrentFlow);
|
|
||||||
|
|
||||||
function handleSave(): void {
|
|
||||||
const newFlow = cloneDeep(useFlowStore.getState().currentFlow);
|
|
||||||
if (!newFlow) return;
|
|
||||||
newFlow.endpoint_name =
|
|
||||||
endpointName && endpointName.length > 0 ? endpointName : null;
|
|
||||||
|
|
||||||
if (autoSaving) {
|
|
||||||
saveFlow(newFlow);
|
|
||||||
} else {
|
|
||||||
setCurrentFlow(newFlow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!openTweaks && endpointName !== flowEndpointName) handleSave();
|
|
||||||
else if (openTweaks) {
|
|
||||||
setEndpointName(flowEndpointName ?? "");
|
|
||||||
}
|
|
||||||
}, [openTweaks]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<BaseModal
|
|
||||||
closeButtonClassName="!top-3"
|
|
||||||
open={open}
|
|
||||||
setOpen={setOpen}
|
|
||||||
size="medium"
|
|
||||||
className="pt-4"
|
|
||||||
>
|
|
||||||
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
|
||||||
<BaseModal.Header
|
|
||||||
description={
|
|
||||||
autoLogin ? undefined : (
|
|
||||||
<>
|
|
||||||
<span className="pr-2">
|
|
||||||
API access requires an API key. You can{" "}
|
|
||||||
<a
|
|
||||||
href="/settings/api-keys"
|
|
||||||
className="text-accent-pink-foreground"
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
create an API key
|
|
||||||
</a>{" "}
|
|
||||||
in settings.
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<IconComponent
|
|
||||||
name="Code2"
|
|
||||||
className="h-6 w-6 text-gray-800 dark:text-white"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<span className="pl-2">API access</span>
|
|
||||||
{nodes.length > 0 && (
|
|
||||||
<div className="border-r-1 absolute right-12 flex items-center text-mmd font-medium leading-[16px]">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
className="h-8 px-3"
|
|
||||||
onClick={() => setOpenTweaks(true)}
|
|
||||||
data-testid="tweaks-button"
|
|
||||||
>
|
|
||||||
<IconComponent
|
|
||||||
name="SlidersHorizontal"
|
|
||||||
className="h-3.5 w-3.5"
|
|
||||||
/>
|
|
||||||
<span>Tweaks ({Object.keys(tweaks)?.length}) </span>
|
|
||||||
</Button>
|
|
||||||
<Separator orientation="vertical" className="ml-2 h-8" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</BaseModal.Header>
|
|
||||||
<BaseModal.Content overflowHidden>
|
|
||||||
{open && (
|
|
||||||
<>
|
|
||||||
<CustomAPIGenerator isOpen={open} />
|
|
||||||
<APITabsComponent />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</BaseModal.Content>
|
|
||||||
</BaseModal>
|
|
||||||
|
|
||||||
<BaseModal
|
|
||||||
open={openTweaks}
|
|
||||||
setOpen={setOpenTweaks}
|
|
||||||
size="medium-small-tall"
|
|
||||||
>
|
|
||||||
<BaseModal.Header
|
|
||||||
description={
|
|
||||||
autoLogin ? undefined : (
|
|
||||||
<>
|
|
||||||
<span className="pr-2">
|
|
||||||
API access requires an API key. You can{" "}
|
|
||||||
<a
|
|
||||||
href="/settings/api-keys"
|
|
||||||
className="text-accent-pink-foreground"
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
create an API key
|
|
||||||
</a>{" "}
|
|
||||||
in settings.
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<IconComponent
|
|
||||||
name="SlidersHorizontal"
|
|
||||||
className="h-6 w-6 text-gray-800 dark:text-white"
|
|
||||||
/>
|
|
||||||
<span className="pl-2">Tweaks</span>
|
|
||||||
</BaseModal.Header>
|
|
||||||
<BaseModal.Content overflowHidden className="flex flex-col gap-4">
|
|
||||||
{true && (
|
|
||||||
<Label>
|
|
||||||
<div className="edit-flow-arrangement mt-2">
|
|
||||||
<span className="shrink-0 text-mmd font-medium">
|
|
||||||
Endpoint Name
|
|
||||||
</span>
|
|
||||||
{!validEndpointName && (
|
|
||||||
<span className="edit-flow-span">
|
|
||||||
Use only letters, numbers, hyphens, and underscores (
|
|
||||||
{MAX_LENGTH} characters max).
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Input
|
|
||||||
className="nopan nodelete nodrag noflow mt-2 font-normal"
|
|
||||||
onChange={handleEndpointNameChange}
|
|
||||||
type="text"
|
|
||||||
name="endpoint_name"
|
|
||||||
value={endpointName ?? ""}
|
|
||||||
placeholder="An alternative name to run the endpoint"
|
|
||||||
maxLength={MAX_LENGTH}
|
|
||||||
minLength={MIN_LENGTH}
|
|
||||||
id="endpoint_name"
|
|
||||||
/>
|
|
||||||
</Label>
|
|
||||||
)}
|
|
||||||
<div className="h-full w-full overflow-y-auto overflow-x-hidden rounded-lg bg-muted custom-scroll">
|
|
||||||
<TweaksComponent open={openTweaks} />
|
|
||||||
</div>
|
|
||||||
</BaseModal.Content>
|
|
||||||
</BaseModal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +1,7 @@
|
||||||
import { ENABLE_DATASTAX_LANGFLOW } from "@/customization/feature-flags";
|
import { ENABLE_DATASTAX_LANGFLOW } from "@/customization/feature-flags";
|
||||||
import { customGetHostProtocol } from "@/customization/utils/custom-get-host-protocol";
|
import { customGetHostProtocol } from "@/customization/utils/custom-get-host-protocol";
|
||||||
import useFlowStore from "@/stores/flowStore";
|
|
||||||
import { GetCodeType } from "@/types/tweaks";
|
import { GetCodeType } from "@/types/tweaks";
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to get the curl code for the API
|
|
||||||
* @param {string} flowId - The id of the flow
|
|
||||||
* @param {boolean} isAuth - If the API is authenticated
|
|
||||||
* @returns {string} - The curl code
|
|
||||||
*/
|
|
||||||
export function getCurlRunCode({
|
|
||||||
flowId,
|
|
||||||
isAuth,
|
|
||||||
tweaksBuildedObject,
|
|
||||||
endpointName,
|
|
||||||
activeTweaks,
|
|
||||||
}: GetCodeType): string {
|
|
||||||
let tweaksString = "{}";
|
|
||||||
const inputs = useFlowStore.getState().inputs;
|
|
||||||
const outputs = useFlowStore.getState().outputs;
|
|
||||||
const hasChatInput = inputs.some((input) => input.type === "ChatInput");
|
|
||||||
const hasChatOutput = outputs.some((output) => output.type === "ChatOutput");
|
|
||||||
if (tweaksBuildedObject)
|
|
||||||
tweaksString = JSON.stringify(tweaksBuildedObject, null, 2);
|
|
||||||
// show the endpoint name in the curl command if it exists
|
|
||||||
|
|
||||||
const { protocol, host } = customGetHostProtocol();
|
|
||||||
|
|
||||||
return `curl -X POST \\
|
|
||||||
"${protocol}//${host}/api/v1/run/${endpointName || flowId}?stream=false" \\
|
|
||||||
-H 'Content-Type: application/json'\\${
|
|
||||||
!isAuth ? `\n -H 'x-api-key: <your api key>'\\` : ""
|
|
||||||
}
|
|
||||||
-d '{${!activeTweaks ? `"input_value": "message",` : ""}
|
|
||||||
"output_type": ${hasChatOutput ? '"chat"' : '"text"'},
|
|
||||||
"input_type": ${hasChatInput ? '"chat"' : '"text"'},
|
|
||||||
"tweaks": ${tweaksString}}'
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a cURL command for making a POST request to a webhook endpoint.
|
* Generates a cURL command for making a POST request to a webhook endpoint.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,5 @@
|
||||||
import { customGetHostProtocol } from "@/customization/utils/custom-get-host-protocol";
|
import { customGetHostProtocol } from "@/customization/utils/custom-get-host-protocol";
|
||||||
import useFlowStore from "@/stores/flowStore";
|
|
||||||
import { GetCodeType } from "@/types/tweaks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to generate JavaScript code for interfacing with an API using the LangflowClient class.
|
|
||||||
* @param {string} flowId - The id of the flow.
|
|
||||||
* @param {boolean} isAuth - Whether the API requires authentication.
|
|
||||||
* @param {any[]} tweaksBuildedObject - Customizations applied to the flow.
|
|
||||||
* @param {string} [endpointName] - Optional endpoint name.
|
|
||||||
* @returns {string} - The JavaScript code as a string.
|
|
||||||
*/
|
|
||||||
export default function getJsApiCode({
|
|
||||||
flowId,
|
|
||||||
isAuth,
|
|
||||||
tweaksBuildedObject,
|
|
||||||
endpointName,
|
|
||||||
activeTweaks,
|
|
||||||
}: GetCodeType): string {
|
|
||||||
let tweaksString = "{}";
|
|
||||||
if (tweaksBuildedObject)
|
|
||||||
tweaksString = JSON.stringify(tweaksBuildedObject, null, 8);
|
|
||||||
const inputs = useFlowStore.getState().inputs;
|
|
||||||
const outputs = useFlowStore.getState().outputs;
|
|
||||||
const hasChatInput = inputs.some((input) => input.type === "ChatInput");
|
|
||||||
const hasChatOutput = outputs.some((output) => output.type === "ChatOutput");
|
|
||||||
|
|
||||||
const { protocol, host } = customGetHostProtocol();
|
|
||||||
|
|
||||||
return `${activeTweaks ? "" : 'let inputValue = ""; // Insert input value here\n\n'}fetch(
|
|
||||||
"${protocol}//${host}/api/v1/run/${endpointName || flowId}?stream=false",
|
|
||||||
{
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Authorization": "Bearer <TOKEN>",
|
|
||||||
"Content-Type": "application/json",${isAuth ? '\n\t\t\t"x-api-key": <your api key>' : ""}
|
|
||||||
},
|
|
||||||
body: JSON.stringify({${activeTweaks ? "" : "\n\t\t\tinput_value: inputValue, "}
|
|
||||||
output_type: ${hasChatOutput ? '"chat"' : '"text"'},
|
|
||||||
input_type: ${hasChatInput ? '"chat"' : '"text"'},
|
|
||||||
tweaks: ${tweaksString}
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.then(res => res.json())
|
|
||||||
.then(data => console.log(data))
|
|
||||||
.catch(error => console.error('Error:', error));
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Generates JavaScript code for making API calls to a Langflow endpoint.
|
* Generates JavaScript code for making API calls to a Langflow endpoint.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,14 @@ import { AllNodeType } from "@/types/flow";
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import { LANGFLOW_SUPPORTED_TYPES } from "../../../constants/constants";
|
import { LANGFLOW_SUPPORTED_TYPES } from "../../../constants/constants";
|
||||||
|
|
||||||
export const getNodesWithDefaultValue = (nodes: AllNodeType[]) => {
|
export const getNodesWithDefaultValue = (
|
||||||
|
nodes: AllNodeType[],
|
||||||
|
oldTweaks: {
|
||||||
|
[key: string]: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
) => {
|
||||||
const filteredNodes: AllNodeType[] = [];
|
const filteredNodes: AllNodeType[] = [];
|
||||||
|
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
|
|
@ -20,6 +27,8 @@ export const getNodesWithDefaultValue = (nodes: AllNodeType[]) => {
|
||||||
if (newNode?.data?.node?.template) {
|
if (newNode?.data?.node?.template) {
|
||||||
newNode.data.node.template = templateKeys.reduce((acc, key) => {
|
newNode.data.node.template = templateKeys.reduce((acc, key) => {
|
||||||
acc[key] = cloneDeep(node?.data?.node?.template[key]);
|
acc[key] = cloneDeep(node?.data?.node?.template[key]);
|
||||||
|
acc[key].advanced =
|
||||||
|
node.id in oldTweaks && key in oldTweaks[node.id] ? false : true;
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,120 +1,4 @@
|
||||||
import { customGetHostProtocol } from "@/customization/utils/custom-get-host-protocol";
|
import { customGetHostProtocol } from "@/customization/utils/custom-get-host-protocol";
|
||||||
import { GetCodeType } from "@/types/tweaks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to get the python code for the API
|
|
||||||
* @param {string} flowId - The id of the flow
|
|
||||||
* @param {boolean} isAuth - If the API is authenticated
|
|
||||||
* @param {any[]} tweaksBuildedObject - The tweaks
|
|
||||||
* @param {string} [endpointName] - The optional endpoint name
|
|
||||||
* @returns {string} - The python code
|
|
||||||
*/
|
|
||||||
export default function getPythonApiCode({
|
|
||||||
flowId,
|
|
||||||
tweaksBuildedObject,
|
|
||||||
endpointName,
|
|
||||||
activeTweaks,
|
|
||||||
}: GetCodeType): string {
|
|
||||||
let tweaksString = "{}";
|
|
||||||
if (tweaksBuildedObject)
|
|
||||||
tweaksString = JSON.stringify(tweaksBuildedObject, null, 2)
|
|
||||||
.replace(/true/g, "True")
|
|
||||||
.replace(/false/g, "False")
|
|
||||||
.replace(/null|undefined/g, "None");
|
|
||||||
|
|
||||||
return `import argparse
|
|
||||||
import json
|
|
||||||
from argparse import RawTextHelpFormatter
|
|
||||||
import requests
|
|
||||||
from typing import Optional
|
|
||||||
import warnings
|
|
||||||
try:
|
|
||||||
from langflow.load import upload_file
|
|
||||||
except ImportError:
|
|
||||||
warnings.warn("Langflow provides a function to help you upload files to the flow. Please install langflow to use it.")
|
|
||||||
upload_file = None
|
|
||||||
|
|
||||||
BASE_API_URL = "${window.location.protocol}//${window.location.host}"
|
|
||||||
FLOW_ID = "${flowId}"
|
|
||||||
ENDPOINT = "${endpointName || ""}" ${
|
|
||||||
endpointName
|
|
||||||
? `# The endpoint name of the flow`
|
|
||||||
: `# You can set a specific endpoint name in the flow settings`
|
|
||||||
}
|
|
||||||
|
|
||||||
# You can tweak the flow by adding a tweaks dictionary
|
|
||||||
# e.g {"OpenAI-XXXXX": {"model_name": "gpt-4"}}
|
|
||||||
TWEAKS = ${tweaksString}
|
|
||||||
|
|
||||||
def run_flow(message: str,
|
|
||||||
endpoint: str,
|
|
||||||
output_type: str = "chat",
|
|
||||||
input_type: str = "chat",
|
|
||||||
tweaks: Optional[dict] = None,
|
|
||||||
api_key: Optional[str] = None) -> dict:
|
|
||||||
"""
|
|
||||||
Run a flow with a given message and optional tweaks.
|
|
||||||
|
|
||||||
:param message: The message to send to the flow
|
|
||||||
:param endpoint: The ID or the endpoint name of the flow
|
|
||||||
:param tweaks: Optional tweaks to customize the flow
|
|
||||||
:return: The JSON response from the flow
|
|
||||||
"""
|
|
||||||
api_url = f"{BASE_API_URL}/api/v1/run/{endpoint}"
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
${!activeTweaks ? `"input_value": message,` : ""}
|
|
||||||
"output_type": output_type,
|
|
||||||
"input_type": input_type,
|
|
||||||
}
|
|
||||||
headers = None
|
|
||||||
if tweaks:
|
|
||||||
payload["tweaks"] = tweaks
|
|
||||||
if api_key:
|
|
||||||
headers = {"x-api-key": api_key}
|
|
||||||
response = requests.post(api_url, json=payload, headers=headers)
|
|
||||||
return response.json()
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(description="""Run a flow with a given message and optional tweaks.\nRun it like: python <your file>.py "your message here" --endpoint "your_endpoint" --tweaks '{"key": "value"}'""",
|
|
||||||
formatter_class=RawTextHelpFormatter)
|
|
||||||
parser.add_argument("message", type=str, help="The message to send to the flow")
|
|
||||||
parser.add_argument("--endpoint", type=str, default=ENDPOINT or FLOW_ID, help="The ID or the endpoint name of the flow")
|
|
||||||
parser.add_argument("--tweaks", type=str, help="JSON string representing the tweaks to customize the flow", default=json.dumps(TWEAKS))
|
|
||||||
parser.add_argument("--api_key", type=str, help="API key for authentication", default=None)
|
|
||||||
parser.add_argument("--output_type", type=str, default="chat", help="The output type")
|
|
||||||
parser.add_argument("--input_type", type=str, default="chat", help="The input type")
|
|
||||||
parser.add_argument("--upload_file", type=str, help="Path to the file to upload", default=None)
|
|
||||||
parser.add_argument("--components", type=str, help="Components to upload the file to", default=None)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
try:
|
|
||||||
tweaks = json.loads(args.tweaks)
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
raise ValueError("Invalid tweaks JSON string")
|
|
||||||
|
|
||||||
if args.upload_file:
|
|
||||||
if not upload_file:
|
|
||||||
raise ImportError("Langflow is not installed. Please install it to use the upload_file function.")
|
|
||||||
elif not args.components:
|
|
||||||
raise ValueError("You need to provide the components to upload the file to.")
|
|
||||||
tweaks = upload_file(file_path=args.upload_file, host=BASE_API_URL, flow_id=args.endpoint, components=[args.components], tweaks=tweaks)
|
|
||||||
|
|
||||||
response = run_flow(
|
|
||||||
message=args.message,
|
|
||||||
endpoint=args.endpoint,
|
|
||||||
output_type=args.output_type,
|
|
||||||
input_type=args.input_type,
|
|
||||||
tweaks=tweaks,
|
|
||||||
api_key=args.api_key
|
|
||||||
)
|
|
||||||
|
|
||||||
print(json.dumps(response, indent=2))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getNewPythonApiCode({
|
export function getNewPythonApiCode({
|
||||||
flowId,
|
flowId,
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import { GetCodeType } from "@/types/tweaks";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to get the python code for the API
|
|
||||||
* @param {string} flow - The current flow
|
|
||||||
* @param {any[]} tweaksBuildedObject - The tweaks
|
|
||||||
* @returns {string} - The python code
|
|
||||||
*/
|
|
||||||
export default function getPythonCode({
|
|
||||||
flowName,
|
|
||||||
tweaksBuildedObject,
|
|
||||||
activeTweaks,
|
|
||||||
}: GetCodeType): string {
|
|
||||||
let tweaksString = "{}";
|
|
||||||
if (tweaksBuildedObject)
|
|
||||||
tweaksString = JSON.stringify(tweaksBuildedObject, null, 2)
|
|
||||||
.replace(/true/g, "True")
|
|
||||||
.replace(/false/g, "False")
|
|
||||||
.replace(/null|undefined/g, "None");
|
|
||||||
|
|
||||||
return `from langflow.load import run_flow_from_json
|
|
||||||
TWEAKS = ${tweaksString}
|
|
||||||
|
|
||||||
result = run_flow_from_json(flow="${flowName}.json",
|
|
||||||
${!activeTweaks ? `input_value="message",\n ` : ""}session_id="", # provide a session id if you want to use session state
|
|
||||||
fallback_to_env_vars=True, # False by default
|
|
||||||
tweaks=TWEAKS)`;
|
|
||||||
}
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
import { tabsArrayType } from "@/types/components";
|
|
||||||
|
|
||||||
export function createTabsArray(
|
|
||||||
codes: { [key: string]: string },
|
|
||||||
includeTweaks = false,
|
|
||||||
) {
|
|
||||||
const tabs: tabsArrayType[] = [];
|
|
||||||
if (codes.runCurlCode) {
|
|
||||||
tabs.push({
|
|
||||||
name: "cURL",
|
|
||||||
mode: "bash",
|
|
||||||
image: "https://curl.se/logo/curl-symbol-transparent.png",
|
|
||||||
language: "sh",
|
|
||||||
code: codes.runCurlCode,
|
|
||||||
hasTweaks: includeTweaks,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (codes.webhookCurlCode) {
|
|
||||||
tabs.push({
|
|
||||||
name: "Webhook cURL",
|
|
||||||
mode: "bash",
|
|
||||||
image: "https://curl.se/logo/curl-symbol-transparent.png",
|
|
||||||
language: "sh",
|
|
||||||
code: codes.webhookCurlCode,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (codes.pythonApiCode) {
|
|
||||||
tabs.push({
|
|
||||||
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.pythonApiCode,
|
|
||||||
hasTweaks: includeTweaks,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (codes.jsApiCode) {
|
|
||||||
tabs.push({
|
|
||||||
name: "JS API",
|
|
||||||
mode: "javascript",
|
|
||||||
image: "https://cdn-icons-png.flaticon.com/512/136/136530.png",
|
|
||||||
language: "js",
|
|
||||||
code: codes.jsApiCode,
|
|
||||||
hasTweaks: includeTweaks,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (codes.pythonCode) {
|
|
||||||
tabs.push({
|
|
||||||
name: "Python Code",
|
|
||||||
mode: "python",
|
|
||||||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
|
||||||
language: "py",
|
|
||||||
code: codes.pythonCode,
|
|
||||||
hasTweaks: includeTweaks,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (codes.widgetCode) {
|
|
||||||
tabs.push({
|
|
||||||
name: "Chat Widget HTML",
|
|
||||||
description:
|
|
||||||
"Insert this code anywhere in your <body> 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: "html",
|
|
||||||
code: codes.widgetCode,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (includeTweaks) {
|
|
||||||
tabs.push({
|
|
||||||
name: "Tweaks",
|
|
||||||
mode: "python",
|
|
||||||
image: "https://cdn-icons-png.flaticon.com/512/5968/5968350.png",
|
|
||||||
language: "py",
|
|
||||||
code: codes.tweaksCode,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return tabs;
|
|
||||||
}
|
|
||||||
|
|
@ -31,7 +31,7 @@ export const switchCaseModalSize = (size: string) => {
|
||||||
height = "h-fit";
|
height = "h-fit";
|
||||||
break;
|
break;
|
||||||
case "medium-small-tall":
|
case "medium-small-tall":
|
||||||
minWidth = "min-w-[50vw]";
|
minWidth = "w-[900px] max-w-[98vw]";
|
||||||
height = "h-[70vh]";
|
height = "h-[70vh]";
|
||||||
break;
|
break;
|
||||||
case "small-h-full":
|
case "small-h-full":
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ const useColumnDefs = (
|
||||||
autoHeight: true,
|
autoHeight: true,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
cellClass: "no-border",
|
cellClass: "no-border cursor-default",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headerName: "Description",
|
headerName: "Description",
|
||||||
|
|
@ -36,7 +36,7 @@ const useColumnDefs = (
|
||||||
autoHeight: true,
|
autoHeight: true,
|
||||||
flex: 2,
|
flex: 2,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
cellClass: "no-border",
|
cellClass: "no-border cursor-default",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headerName: "Value",
|
headerName: "Value",
|
||||||
|
|
@ -61,24 +61,26 @@ const useColumnDefs = (
|
||||||
autoHeight: true,
|
autoHeight: true,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
cellClass: "no-border",
|
cellClass: "no-border cursor-default",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
if (!hideVisibility) {
|
if (!hideVisibility) {
|
||||||
colDefs.push({
|
colDefs.push({
|
||||||
headerName: "Show",
|
headerName: isTweaks ? "Enable Input" : "Show",
|
||||||
field: "advanced",
|
field: "advanced",
|
||||||
cellRenderer: TableAdvancedToggleCellRender,
|
cellRenderer: TableAdvancedToggleCellRender,
|
||||||
valueGetter: (params: ValueGetterParams) => {
|
valueGetter: (params: ValueGetterParams) => {
|
||||||
return {
|
return {
|
||||||
nodeId,
|
nodeId,
|
||||||
parameterId: params.data.key,
|
parameterId: params.data.key,
|
||||||
|
isTweaks,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
editable: false,
|
editable: false,
|
||||||
maxWidth: 80,
|
maxWidth: !isTweaks ? 80 : 120,
|
||||||
|
minWidth: !isTweaks ? 80 : 120,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
cellClass: "no-border",
|
cellClass: "no-border cursor-default",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return colDefs;
|
return colDefs;
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ import useAlertStore from "./alertStore";
|
||||||
import { useDarkStore } from "./darkStore";
|
import { useDarkStore } from "./darkStore";
|
||||||
import useFlowsManagerStore from "./flowsManagerStore";
|
import useFlowsManagerStore from "./flowsManagerStore";
|
||||||
import { useGlobalVariablesStore } from "./globalVariablesStore/globalVariables";
|
import { useGlobalVariablesStore } from "./globalVariablesStore/globalVariables";
|
||||||
|
import { useTweaksStore } from "./tweaksStore";
|
||||||
import { useTypesStore } from "./typesStore";
|
import { useTypesStore } from "./typesStore";
|
||||||
|
|
||||||
// this is our useStore hook that we can use in our components to get parts of the store and call actions
|
// this is our useStore hook that we can use in our components to get parts of the store and call actions
|
||||||
|
|
@ -226,6 +227,9 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
) as string[],
|
) as string[],
|
||||||
});
|
});
|
||||||
unselectAllNodesEdges(nodes, edges);
|
unselectAllNodesEdges(nodes, edges);
|
||||||
|
if (flow?.id) {
|
||||||
|
useTweaksStore.getState().initialSetup(nodes, flow?.id);
|
||||||
|
}
|
||||||
set({
|
set({
|
||||||
nodes,
|
nodes,
|
||||||
edges: newEdges,
|
edges: newEdges,
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,13 @@
|
||||||
import { getChangesType } from "@/modals/apiModal/utils/get-changes-types";
|
import { getChangesType } from "@/modals/apiModal/utils/get-changes-types";
|
||||||
import { getNodesWithDefaultValue } from "@/modals/apiModal/utils/get-nodes-with-default-value";
|
import { getNodesWithDefaultValue } from "@/modals/apiModal/utils/get-nodes-with-default-value";
|
||||||
import { createTabsArray } from "@/modals/apiModal/utils/tabs-array";
|
import { AllNodeType, NodeDataType } from "@/types/flow";
|
||||||
import { AllNodeType, FlowType, NodeDataType } from "@/types/flow";
|
import { getLocalStorage, setLocalStorage } from "@/utils/local-storage-util";
|
||||||
import { GetCodesType } from "@/types/tweaks";
|
|
||||||
import { customStringify } from "@/utils/reactflowUtils";
|
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { TweaksStoreType } from "../types/zustand/tweaks";
|
import { TweaksStoreType } from "../types/zustand/tweaks";
|
||||||
import useFlowStore from "./flowStore";
|
import useFlowStore from "./flowStore";
|
||||||
|
|
||||||
export const useTweaksStore = create<TweaksStoreType>((set, get) => ({
|
export const useTweaksStore = create<TweaksStoreType>((set, get) => ({
|
||||||
activeTweaks: false,
|
|
||||||
tweaks: {},
|
tweaks: {},
|
||||||
setActiveTweaks: (activeTweaks: boolean) => {
|
|
||||||
set({ activeTweaks }), get().refreshTabs();
|
|
||||||
},
|
|
||||||
nodes: [],
|
nodes: [],
|
||||||
setNodes: (change) => {
|
setNodes: (change) => {
|
||||||
let newChange = typeof change === "function" ? change(get().nodes) : change;
|
let newChange = typeof change === "function" ? change(get().nodes) : change;
|
||||||
|
|
@ -21,7 +15,6 @@ export const useTweaksStore = create<TweaksStoreType>((set, get) => ({
|
||||||
set({
|
set({
|
||||||
nodes: newChange,
|
nodes: newChange,
|
||||||
});
|
});
|
||||||
get().refreshTabs();
|
|
||||||
get().updateTweaks();
|
get().updateTweaks();
|
||||||
},
|
},
|
||||||
setNode: (id, change) => {
|
setNode: (id, change) => {
|
||||||
|
|
@ -44,112 +37,28 @@ export const useTweaksStore = create<TweaksStoreType>((set, get) => ({
|
||||||
getNode: (id: string) => {
|
getNode: (id: string) => {
|
||||||
return get().nodes.find((node) => node.id === id);
|
return get().nodes.find((node) => node.id === id);
|
||||||
},
|
},
|
||||||
autoLogin: false,
|
currentFlowId: "",
|
||||||
flow: null,
|
initialSetup: (nodes: AllNodeType[], flowId: string) => {
|
||||||
getCodes: {},
|
|
||||||
initialSetup: (
|
|
||||||
autoLogin: boolean,
|
|
||||||
flow: FlowType,
|
|
||||||
getCodes: GetCodesType,
|
|
||||||
) => {
|
|
||||||
useFlowStore.getState().unselectAll();
|
useFlowStore.getState().unselectAll();
|
||||||
set({
|
set({
|
||||||
nodes: getNodesWithDefaultValue(flow?.data?.nodes ?? []),
|
currentFlowId: flowId,
|
||||||
autoLogin,
|
|
||||||
flow,
|
|
||||||
getCodes,
|
|
||||||
});
|
});
|
||||||
get().refreshTabs();
|
const tweaks = JSON.parse(getLocalStorage(`lf_tweaks_${flowId}`) || "{}");
|
||||||
},
|
|
||||||
newInitialSetup: (nodes: AllNodeType[]) => {
|
|
||||||
useFlowStore.getState().unselectAll();
|
|
||||||
set({
|
set({
|
||||||
nodes: getNodesWithDefaultValue(nodes),
|
nodes: getNodesWithDefaultValue(nodes, tweaks),
|
||||||
});
|
});
|
||||||
get().updateTweaks();
|
get().updateTweaks();
|
||||||
},
|
},
|
||||||
tabs: [],
|
|
||||||
refreshTabs: () => {
|
|
||||||
const autoLogin = get().autoLogin;
|
|
||||||
const flow = get().flow;
|
|
||||||
const tweak = {};
|
|
||||||
const nodes = get().nodes;
|
|
||||||
const originalNodes = flow?.data?.nodes;
|
|
||||||
if (!flow) return;
|
|
||||||
|
|
||||||
nodes.forEach((node) => {
|
|
||||||
const originalNodeTemplate = originalNodes?.find((n) => n.id === node.id)
|
|
||||||
?.data?.node?.template;
|
|
||||||
const nodeTemplate = node.data?.node?.template;
|
|
||||||
if (originalNodeTemplate && nodeTemplate && node.type === "genericNode") {
|
|
||||||
const currentTweak = {};
|
|
||||||
Object.keys(nodeTemplate).forEach((name) => {
|
|
||||||
if (
|
|
||||||
customStringify(nodeTemplate[name]) !==
|
|
||||||
customStringify(originalNodeTemplate[name]) ||
|
|
||||||
get().activeTweaks
|
|
||||||
) {
|
|
||||||
currentTweak[name] = getChangesType(
|
|
||||||
nodeTemplate[name].value,
|
|
||||||
nodeTemplate[name],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
tweak[node.id] = currentTweak;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const codesObj = {};
|
|
||||||
const getCodes = get().getCodes;
|
|
||||||
|
|
||||||
const props = {
|
|
||||||
flowId: flow?.id,
|
|
||||||
flowName: flow?.name,
|
|
||||||
isAuth: autoLogin,
|
|
||||||
tweaksBuildedObject: tweak,
|
|
||||||
endpointName: flow?.endpoint_name,
|
|
||||||
activeTweaks: get().activeTweaks,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (getCodes) {
|
|
||||||
if (getCodes.getCurlRunCode) {
|
|
||||||
codesObj["runCurlCode"] = getCodes.getCurlRunCode(props);
|
|
||||||
}
|
|
||||||
if (getCodes.getCurlWebhookCode && !!flow.webhook) {
|
|
||||||
codesObj["webhookCurlCode"] = getCodes.getCurlWebhookCode(props);
|
|
||||||
}
|
|
||||||
if (getCodes.getJsApiCode) {
|
|
||||||
codesObj["jsApiCode"] = getCodes.getJsApiCode(props);
|
|
||||||
}
|
|
||||||
if (getCodes.getPythonApiCode) {
|
|
||||||
codesObj["pythonApiCode"] = getCodes.getPythonApiCode(props);
|
|
||||||
}
|
|
||||||
if (getCodes.getPythonCode) {
|
|
||||||
codesObj["pythonCode"] = getCodes.getPythonCode(props);
|
|
||||||
}
|
|
||||||
if (getCodes.getWidgetCode) {
|
|
||||||
codesObj["widgetCode"] = getCodes.getWidgetCode(props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set({
|
|
||||||
tabs: createTabsArray(codesObj, nodes.length > 0),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
updateTweaks: () => {
|
updateTweaks: () => {
|
||||||
const nodes = get().nodes;
|
const nodes = get().nodes;
|
||||||
const originalNodes = useFlowStore.getState().nodes;
|
|
||||||
const tweak = {};
|
const tweak = {};
|
||||||
|
const flowId = get().currentFlowId;
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
const originalNodeTemplate = originalNodes?.find((n) => n.id === node.id)
|
|
||||||
?.data?.node?.template;
|
|
||||||
const nodeTemplate = node.data?.node?.template;
|
const nodeTemplate = node.data?.node?.template;
|
||||||
if (originalNodeTemplate && nodeTemplate && node.type === "genericNode") {
|
if (nodeTemplate && node.type === "genericNode") {
|
||||||
const currentTweak = {};
|
const currentTweak = {};
|
||||||
Object.keys(nodeTemplate).forEach((name) => {
|
Object.keys(nodeTemplate).forEach((name) => {
|
||||||
if (
|
if (!nodeTemplate[name].advanced) {
|
||||||
customStringify(nodeTemplate[name]) !==
|
|
||||||
customStringify(originalNodeTemplate[name])
|
|
||||||
) {
|
|
||||||
currentTweak[name] = getChangesType(
|
currentTweak[name] = getChangesType(
|
||||||
nodeTemplate[name].value,
|
nodeTemplate[name].value,
|
||||||
nodeTemplate[name],
|
nodeTemplate[name],
|
||||||
|
|
@ -161,6 +70,7 @@ export const useTweaksStore = create<TweaksStoreType>((set, get) => ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
setLocalStorage(`lf_tweaks_${flowId}`, JSON.stringify(tweak));
|
||||||
set({
|
set({
|
||||||
tweaks: tweak,
|
tweaks: tweak,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1240,7 +1240,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.playground-btn-flow-toolbar {
|
.playground-btn-flow-toolbar {
|
||||||
@apply relative inline-flex h-8 w-full items-center justify-center gap-1.5 rounded px-3 py-1.5 text-sm font-semibold transition-all duration-500 ease-in-out;
|
@apply relative inline-flex h-8 w-full items-center justify-center gap-1.5 rounded px-3 py-1.5 text-sm font-normal transition-all duration-500 ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-slider-text {
|
.input-slider-text {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
import { AllNodeType, FlowType, TweaksType } from "@/types/flow";
|
import { AllNodeType } from "@/types/flow";
|
||||||
import { GetCodesType } from "@/types/tweaks";
|
|
||||||
import { tabsArrayType } from "../../components";
|
|
||||||
|
|
||||||
export type TweaksStoreType = {
|
export type TweaksStoreType = {
|
||||||
activeTweaks: boolean;
|
|
||||||
setActiveTweaks: (activeTweaks: boolean) => void;
|
|
||||||
nodes: AllNodeType[];
|
nodes: AllNodeType[];
|
||||||
|
currentFlowId: string;
|
||||||
setNodes: (
|
setNodes: (
|
||||||
update: AllNodeType[] | ((oldState: AllNodeType[]) => AllNodeType[]),
|
update: AllNodeType[] | ((oldState: AllNodeType[]) => AllNodeType[]),
|
||||||
skipSave?: boolean,
|
skipSave?: boolean,
|
||||||
|
|
@ -14,18 +11,8 @@ export type TweaksStoreType = {
|
||||||
id: string,
|
id: string,
|
||||||
update: AllNodeType | ((oldState: AllNodeType) => AllNodeType),
|
update: AllNodeType | ((oldState: AllNodeType) => AllNodeType),
|
||||||
) => void;
|
) => void;
|
||||||
getCodes: GetCodesType;
|
|
||||||
getNode: (id: string) => AllNodeType | undefined;
|
getNode: (id: string) => AllNodeType | undefined;
|
||||||
tabs: tabsArrayType[];
|
initialSetup: (nodes: AllNodeType[], flowId: string) => void;
|
||||||
initialSetup: (
|
|
||||||
autoLogin: boolean,
|
|
||||||
flow: FlowType,
|
|
||||||
getCodes: GetCodesType,
|
|
||||||
) => void;
|
|
||||||
newInitialSetup: (nodes: AllNodeType[]) => void;
|
|
||||||
refreshTabs: () => void;
|
|
||||||
autoLogin: boolean;
|
|
||||||
flow: FlowType | null;
|
|
||||||
updateTweaks: () => void;
|
updateTweaks: () => void;
|
||||||
tweaks: {
|
tweaks: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
|
|
|
||||||
|
|
@ -27,17 +27,11 @@ test(
|
||||||
.first()
|
.first()
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
await page.waitForSelector(
|
await page.waitForSelector('[data-testid="showstream"]', {
|
||||||
'[data-testid="popover-anchor-input-openai_api_base-edit"]',
|
timeout: 1000,
|
||||||
{
|
});
|
||||||
timeout: 1000,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
await page
|
await page.getByTestId("showstream").first().click();
|
||||||
.getByTestId("popover-anchor-input-openai_api_base-edit")
|
|
||||||
.first()
|
|
||||||
.fill("teste");
|
|
||||||
|
|
||||||
await page.getByText("Close").last().click();
|
await page.getByText("Close").last().click();
|
||||||
|
|
||||||
|
|
@ -50,6 +44,13 @@ test(
|
||||||
const newValue = clipboardContent2;
|
const newValue = clipboardContent2;
|
||||||
expect(oldValue).not.toBe(newValue);
|
expect(oldValue).not.toBe(newValue);
|
||||||
expect(clipboardContent2.length).toBeGreaterThan(clipboardContent.length);
|
expect(clipboardContent2.length).toBeGreaterThan(clipboardContent.length);
|
||||||
|
await awaitBootstrapTest(page, { skipModal: true });
|
||||||
|
await page.getByText("Basic Prompting").first().click();
|
||||||
|
await page.getByTestId("publish-button").click();
|
||||||
|
await page.getByTestId("api-access-item").click();
|
||||||
|
expect(
|
||||||
|
await page.getByText("Input Schema (1)", { exact: true }).isVisible(),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -131,5 +132,5 @@ test("check if tweaks are updating when someothing on the flow changes", async (
|
||||||
await page.getByText("collection_name_test_123123123!@#$&*(&%$@").isVisible();
|
await page.getByText("collection_name_test_123123123!@#$&*(&%$@").isVisible();
|
||||||
await page.getByText("persist_directory_123123123!@#$&*(&%$@").isVisible();
|
await page.getByText("persist_directory_123123123!@#$&*(&%$@").isVisible();
|
||||||
|
|
||||||
expect(await page.getByText("Tweaks (2)", { exact: true }).isVisible());
|
expect(await page.getByText("Input Schema (2)", { exact: true }).isVisible());
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue