Merge branch 'python_custom_node_component' into ChatWidgetAPI

This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-07-27 17:38:35 -03:00
commit 397c665536
102 changed files with 6034 additions and 671 deletions

View file

@ -27,6 +27,7 @@ import {
classNames,
getRandomKeyByssmm,
groupByFamily,
groupByFamilyCustom,
} from "../../../../utils/utils";
export default function ParameterComponent({
@ -49,7 +50,9 @@ export default function ParameterComponent({
const infoHtml = useRef(null);
const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0);
const { setTabsState, tabId, save } = useContext(TabsContext);
const { setTabsState, tabId, save, flows } = useContext(TabsContext);
const flow = flows.find((f) => f.id === tabId).data?.nodes ?? null;
// Update component position
useEffect(() => {
@ -80,9 +83,11 @@ export default function ParameterComponent({
[tabId]: {
...prev[tabId],
isPending: true,
formKeysData: prev[tabId].formKeysData,
},
};
});
renderTooltips();
};
useEffect(() => {
@ -98,57 +103,76 @@ export default function ParameterComponent({
);
}, [info]);
useEffect(() => {
const groupedObj = groupByFamily(myData, tooltipTitle, left, data.type);
function renderTooltips() {
let groupedObj = groupByFamily(myData, tooltipTitle, left, data.type, flow);
refNumberComponents.current = groupedObj[0]?.type?.length;
if (groupedObj?.length === 0 && flow && flow.length > 0) {
groupedObj = groupByFamilyCustom(
myData,
tooltipTitle,
left,
data.type,
flow
);
}
refHtml.current = groupedObj.map((item, i) => {
const Icon: any = nodeIconsLucide[item.family];
if (groupedObj) {
refNumberComponents.current = groupedObj[0]?.type?.length;
return (
<span
key={getRandomKeyByssmm() + item.family + i}
className={classNames(
i > 0 ? "mt-2 flex items-center" : "flex items-center"
)}
>
<div
className="h-5 w-5"
style={{
color: nodeColors[item.family],
}}
refHtml.current = groupedObj.map((item, i) => {
const Icon: any = nodeIconsLucide[item.family];
return (
<span
key={getRandomKeyByssmm() + item.family + i}
className={classNames(
i > 0 ? "mt-2 flex items-center" : "flex items-center"
)}
>
<Icon
<div
className="h-5 w-5"
strokeWidth={1.5}
style={{
color: nodeColors[item.family] ?? nodeColors.unknown,
color: nodeColors[item.family],
}}
/>
</div>
<span className="ps-2 text-xs text-foreground">
{nodeNames[item.family] ?? ""}{" "}
<span className="text-xs">
{" "}
{item.type === "" ? "" : " - "}
{item.type.split(", ").length > 2
? item.type.split(", ").map((el, i) => (
<React.Fragment key={el + i}>
<span>
{i === item.type.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.type}
>
<Icon
className="h-5 w-5"
strokeWidth={1.5}
style={{
color: nodeColors[item.family] ?? nodeColors.unknown,
}}
/>
</div>
<span className="ps-2 text-xs text-foreground">
{item.family !== "custom_components"
? nodeNames[item.family]
: item.component ?? ""}{" "}
<span className="text-xs">
{" "}
{item.type === "" ? "" : " - "}
{item.type.split(", ").length > 2
? item.type.split(", ").map((el, i) => (
<React.Fragment key={el + i}>
<span>
{i === item.type.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.type}
</span>
</span>
</span>
</span>
);
});
}, [tooltipTitle]);
);
});
}
}
useEffect(() => {
renderTooltips();
}, [tooltipTitle, flow]);
return (
<div
ref={ref}
@ -280,6 +304,7 @@ export default function ParameterComponent({
) : left === true && type === "code" ? (
<div className="mt-2 w-full">
<CodeAreaComponent
dynamic={data.node.template[name].dynamic ?? false}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
}}

View file

@ -95,6 +95,11 @@ export default function GenericNode({
"generic-node-div"
)}
>
{data.node.beta && (
<div className="beta-badge-wrapper">
<div className="beta-badge-content">BETA</div>
</div>
)}
<div className="generic-node-div-title">
<div className="generic-node-title-arrangement">
<IconComponent
@ -127,7 +132,7 @@ export default function GenericNode({
</span>
) : (
<div className="max-h-96 overflow-auto">
{validationStatus.params
{typeof validationStatus.params === "string"
? validationStatus.params
.split("\n")
.map((line, index) => <div key={index}>{line}</div>)
@ -178,6 +183,14 @@ export default function GenericNode({
{data.node.template[t].show &&
!data.node.template[t].advanced ? (
<ParameterComponent
key={
(data.node.template[t].input_types?.join(";") ??
data.node.template[t].type) +
"|" +
t +
"|" +
data.id
}
data={data}
setData={setData}
color={
@ -225,6 +238,7 @@ export default function GenericNode({
{" "}
</div>
<ParameterComponent
key={[data.type, data.id, ...data.node.base_classes].join("|")}
data={data}
setData={setData}
color={nodeColors[types[data.type]] ?? nodeColors.unknown}

View file

@ -1,7 +1,8 @@
import React, { ChangeEvent, useState } from "react";
import React, { ChangeEvent, useEffect, useRef, useState } from "react";
import { Input } from "../../components/ui/input";
import { Label } from "../../components/ui/label";
import { Textarea } from "../../components/ui/textarea";
import { readFlowsFromDatabase } from "../../controllers/API";
type InputProps = {
name: string | null;
@ -9,6 +10,8 @@ type InputProps = {
maxLength?: number;
flows: Array<{ id: string; name: string; description: string }>;
tabId: string;
invalidName: boolean;
setInvalidName: (invalidName: boolean) => void;
setName: (name: string) => void;
setDescription: (description: string) => void;
updateFlow: (flow: { id: string; name: string }) => void;
@ -16,6 +19,8 @@ type InputProps = {
export const EditFlowSettings: React.FC<InputProps> = ({
name,
invalidName,
setInvalidName,
description,
maxLength = 50,
flows,
@ -25,6 +30,14 @@ export const EditFlowSettings: React.FC<InputProps> = ({
updateFlow,
}) => {
const [isMaxLength, setIsMaxLength] = useState(false);
const nameLists = useRef([]);
useEffect(() => {
readFlowsFromDatabase().then((flows) => {
flows.forEach((flow) => {
nameLists.current.push(flow.name);
});
});
}, []);
const handleNameChange = (event: ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
@ -33,7 +46,11 @@ export const EditFlowSettings: React.FC<InputProps> = ({
} else {
setIsMaxLength(false);
}
if (!nameLists.current.includes(value)) {
setInvalidName(false);
} else {
setInvalidName(true);
}
setName(value);
};
@ -55,6 +72,9 @@ export const EditFlowSettings: React.FC<InputProps> = ({
{isMaxLength && (
<span className="edit-flow-span">Character limit reached</span>
)}
{invalidName && (
<span className="edit-flow-span">Name already in use</span>
)}
</div>
<Input
className="nopan nodrag noundo nocopy mt-2 font-normal"

View file

@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import CodeAreaModal from "../../modals/codeAreaModal";
import { TextAreaComponentType } from "../../types/components";
import { CodeAreaComponentType } from "../../types/components";
import IconComponent from "../genericIconComponent";
@ -10,8 +10,9 @@ export default function CodeAreaComponent({
disabled,
editNode = false,
nodeClass,
dynamic,
setNodeClass,
}: TextAreaComponentType) {
}: CodeAreaComponentType) {
const [myValue, setMyValue] = useState(
typeof value == "string" ? value : JSON.stringify(value)
);
@ -29,6 +30,7 @@ export default function CodeAreaComponent({
return (
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
<CodeAreaModal
dynamic={dynamic}
value={myValue}
nodeClass={nodeClass}
setNodeClass={setNodeClass}

View file

@ -491,3 +491,14 @@ export const NOUNS: string[] = [
*
*/
export const USER_PROJECTS_HEADER = "My Collection";
/**
* URLs excluded from error retries.
* @constant
*
*/
export const URL_EXCLUDED_FROM_ERROR_RETRIES = [
"/api/v1/validate/code",
"/api/v1/custom_component",
"/api/v1/validate/prompt",
];

View file

@ -1,5 +1,6 @@
import axios, { AxiosError, AxiosInstance } from "axios";
import { useContext, useEffect, useRef } from "react";
import { URL_EXCLUDED_FROM_ERROR_RETRIES } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
// Create a new Axios instance
@ -15,6 +16,9 @@ function ApiInterceptor() {
const interceptor = api.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {
if (URL_EXCLUDED_FROM_ERROR_RETRIES.includes(error.config?.url)) {
return Promise.reject(error);
}
let retryCount = 0;
while (retryCount < 4) {
@ -31,7 +35,7 @@ function ApiInterceptor() {
"Refresh the page",
"Use a new flow tab",
"Check if the backend is up",
"Endpoint: " + error.config.url,
"Endpoint: " + error.config?.url,
],
});
return Promise.reject(error);

View file

@ -339,3 +339,10 @@ export async function uploadFile(
formData.append("file", file);
return await api.post(`/api/v1/upload/${id}`, formData);
}
export async function postCustomComponent(
code: string,
apiClass: APIClassType
): Promise<AxiosResponse<APIClassType>> {
return await api.post(`/api/v1/custom_component`, { code });
}

View file

@ -0,0 +1,22 @@
import { Infinity } from "lucide-react";
import { forwardRef } from "react";
const GradientSparkles = forwardRef<SVGSVGElement, React.PropsWithChildren<{}>>(
(props, ref) => {
return (
<>
<svg width="0" height="0" style={{ position: "absolute" }}>
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop className="gradient-start" offset="0%" />
<stop className="gradient-end" offset="100%" />
</linearGradient>
</defs>
</svg>
<Infinity stroke="url(#grad1)" ref={ref} {...props} />
</>
);
}
);
export default GradientSparkles;

View file

@ -261,6 +261,13 @@ const EditNodeModal = forwardRef(
) : myData.node.template[n].type === "code" ? (
<div className="mx-auto">
<CodeAreaComponent
dynamic={
data.node.template[n].dynamic ?? false
}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
}}
nodeClass={data.node}
disabled={disabled}
editNode={true}
value={myData.node.template[n].value ?? ""}

View file

@ -160,6 +160,11 @@ export default function ModalField({
) : type === "code" ? (
<div className="w-1/2">
<CodeAreaComponent
dynamic={data.node.template[name].dynamic ?? false}
setNodeClass={(nodeClass) => {
data.node = nodeClass;
}}
nodeClass={data.node}
disabled={false}
value={data.node.template[name].value ?? ""}
onChange={(t: string) => {

View file

@ -112,8 +112,9 @@ function BaseModal({
<div className={`mt-2 flex flex-col ${height} w-full `}>
{ContentChild}
</div>
<div className="flex flex-row-reverse">{ContentFooter}</div>
{ContentFooter && (
<div className="flex flex-row-reverse">{ContentFooter}</div>
)}
</DialogContent>
</Dialog>
);

View file

@ -3,14 +3,16 @@ 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 { ReactNode, useContext, useState } from "react";
// import "ace-builds/webpack-resolver";
import { ReactNode, useContext, useEffect, useState } from "react";
import AceEditor from "react-ace";
import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button";
import { CODE_PROMPT_DIALOG_SUBTITLE } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
import { darkContext } from "../../contexts/darkContext";
import { postValidateCode } from "../../controllers/API";
import { typesContext } from "../../contexts/typesContext";
import { postCustomComponent, postValidateCode } from "../../controllers/API";
import { APIClassType } from "../../types/api";
import BaseModal from "../baseModal";
@ -20,18 +22,34 @@ export default function CodeAreaModal({
nodeClass,
setNodeClass,
children,
dynamic,
}: {
setValue: (value: string) => void;
value: string;
nodeClass: APIClassType;
nodeClass?: APIClassType;
children: ReactNode;
setNodeClass: (Class: APIClassType) => void;
setNodeClass?: (Class: APIClassType) => void;
dynamic?: boolean;
}) {
const [code, setCode] = useState(value);
const { dark } = useContext(darkContext);
const { reactFlowInstance } = useContext(typesContext);
const [height, setHeight] = useState(null);
const { setErrorData, setSuccessData } = useContext(alertContext);
const [error, setError] = useState<{
detail: { error: string; traceback: string };
}>(null);
function handleClick() {
useEffect(() => {
// if nodeClass.template has more fields other than code and dynamic is true
// do not run handleClick
if (dynamic && Object.keys(nodeClass.template).length > 2) {
return;
}
processCode();
}, []);
function processNonDynamicField() {
postValidateCode(code)
.then((apiReturn) => {
if (apiReturn.data) {
@ -41,8 +59,9 @@ export default function CodeAreaModal({
setSuccessData({
title: "Code is ready to run",
});
setValue(code);
setOpen(false);
setValue(code);
// setValue(code);
} else {
if (funcErrors.length !== 0) {
setErrorData({
@ -70,6 +89,50 @@ export default function CodeAreaModal({
});
}
function processDynamicField() {
postCustomComponent(code, nodeClass)
.then((apiReturn) => {
const { data } = apiReturn;
if (data) {
setNodeClass(data);
setValue(code);
setError({ detail: { error: undefined, traceback: undefined } });
setOpen(false);
}
})
.catch((err) => {
setError(err.response.data);
});
}
function processCode() {
if (!dynamic) {
processNonDynamicField();
} else {
processDynamicField();
}
}
function handleClick() {
processCode();
}
useEffect(() => {
// Function to be executed after the state changes
const delayedFunction = setTimeout(() => {
if (error?.detail.error !== undefined) {
//trigger to update the height, does not really apply any height
setHeight("90%");
}
//600 to happen after the transition of 500ms
}, 600);
// Cleanup function to clear the timeout if the component unmounts or the state changes again
return () => {
clearTimeout(delayedFunction);
};
}, [error, setHeight]);
const [open, setOpen] = useState(false);
return (
@ -89,6 +152,7 @@ export default function CodeAreaModal({
<AceEditor
value={code}
mode="python"
height={height ?? "100%"}
highlightActiveLine={true}
showPrintMargin={false}
fontSize={14}
@ -99,9 +163,26 @@ export default function CodeAreaModal({
onChange={(value) => {
setCode(value);
}}
className="h-full w-full rounded-lg border-[1px] border-border custom-scroll"
className="h-full w-full rounded-lg border-[1px] border-gray-300 custom-scroll dark:border-gray-600"
/>
</div>
<div
className={
"w-full transition-all delay-500 " +
(error?.detail.error !== undefined ? "h-2/6" : "h-0")
}
>
<div className="mt-1 h-full w-full overflow-y-auto overflow-x-clip text-left custom-scroll">
<h1 className="text-lg text-destructive">
{error?.detail?.error}
</h1>
<div className="ml-2 w-full break-all text-sm text-status-red">
<pre className="w-full whitespace-pre-wrap break-all">
{error?.detail?.traceback}
</pre>
</div>
</div>
</div>
<div className="flex h-fit w-full justify-end">
<Button className="mt-3" onClick={handleClick} type="submit">
Check & Save

View file

@ -23,6 +23,8 @@ export default function FlowSettingsModal({
const [description, setDescription] = useState(
flows.find((f) => f.id === tabId).description
);
const [invalidName, setInvalidName] = useState(false);
function handleClick() {
let savedFlow = flows.find((f) => f.id === tabId);
savedFlow.name = name;
@ -39,6 +41,8 @@ export default function FlowSettingsModal({
</BaseModal.Header>
<BaseModal.Content>
<EditFlowSettings
invalidName={invalidName}
setInvalidName={setInvalidName}
name={name}
description={description}
flows={flows}
@ -50,7 +54,7 @@ export default function FlowSettingsModal({
</BaseModal.Content>
<BaseModal.Footer>
<Button onClick={handleClick} type="submit">
<Button disabled={invalidName} onClick={handleClick} type="submit">
Save
</Button>
</BaseModal.Footer>

View file

@ -31,6 +31,36 @@
}
}
@keyframes gradient-motion-start {
0% {
stop-color: rgb(156, 138, 236);
}
50% {
stop-color: rgb(255, 130, 184);
}
80% {
stop-color: rgb(255, 165, 100);
}
100% {
stop-color: rgb(156, 138, 236);
}
}
@keyframes gradient-motion-end {
0% {
stop-color: rgb(156, 138, 236);
}
50% {
stop-color: rgb(255, 165, 100);
}
80% {
stop-color: rgb(255, 130, 184);
}
100% {
stop-color: rgb(156, 138, 236);
}
}
@layer components {
.round-buttons-position {
@apply fixed right-4;
@ -979,6 +1009,13 @@
@apply font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70;
}
.beta-badge-wrapper {
@apply absolute right-0 top-0 h-16 w-16 overflow-hidden rounded-tr-lg;
}
.beta-badge-content {
@apply mt-2 w-24 rotate-45 bg-beta-background text-center text-xs font-semibold text-beta-foreground;
}
.chat-message-highlight {
@apply rounded-md bg-indigo-100 px-0.5 dark:bg-indigo-900;
}

View file

@ -1,30 +1,38 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
pre {
font-family: inherit;
font-family: inherit;
}
.react-flow__pane {
cursor: default;
cursor: default;
}
.AccordionContent {
overflow: hidden;
overflow: hidden;
}
.AccordionContent[data-state='open'] {
animation: slideDown 300ms ease-out;
.AccordionContent[data-state="open"] {
animation: slideDown 300ms ease-out;
}
.AccordionContent[data-state="closed"] {
animation: slideUp 300ms ease-out;
}
.gradient-end {
animation: gradient-motion-end 3s infinite forwards;
}
.gradient-start {
animation: gradient-motion-start 4s infinite forwards;
}
.AccordionContent[data-state='closed'] {
animation: slideUp 300ms ease-out;
}

View file

@ -2,125 +2,127 @@
@tailwind components;
@tailwind utilities;
/* TODO: Confirm that all colors here are found in tailwind config */
@layer base {
:root {
--background: 0 0% 100%; /* hsl(0 0% 100%) */
--foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--muted: 210 40% 98%; /* hsl(210 40% 98%) */
--muted-foreground: 215.4 16.3% 46.9%; /* hsl(215 16% 46%) */
--popover: 0 0% 100%; /* hsl(0 0% 100%) */
--popover-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--card: 0 0% 100%; /* hsl(0 0% 100%) */
--card-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--border: 214.3 21.8% 91.4%; /* hsl(214 32% 91%) */
--input: 214.3 21.8% 91.4%; /* hsl(214 32% 91%) */
--primary: 222.2 27% 11.2%; /* hsl(222 27% 18%) */
--primary-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--secondary: 210 40% 96.1%; /* hsl(210 40% 96%) */
--secondary-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--accent: 210 30% 96.1%; /* hsl(210 30% 96%) */
--accent-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--destructive: 0 100% 50%; /* hsl(0 100% 50%) */
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--radius: 0.5rem;
--ring: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
--round-btn-shadow: #00000063;
--error-background: #fef2f2;
--error-foreground: #991b1b;
--success-background: #f0fdf4;
--success-foreground: #14532d;
--background: 0 0% 100%; /* hsl(0 0% 100%) */
--foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--muted: 210 40% 98%; /* hsl(210 40% 98%) */
--muted-foreground: 215.4 16.3% 46.9%; /* hsl(215 16% 46%) */
--popover: 0 0% 100%; /* hsl(0 0% 100%) */
--popover-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--card: 0 0% 100%; /* hsl(0 0% 100%) */
--card-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--border: 214.3 21.8% 91.4%; /* hsl(214 32% 91%) */
--input: 214.3 21.8% 91.4%; /* hsl(214 32% 91%) */
--primary: 222.2 27% 11.2%; /* hsl(222 27% 18%) */
--primary-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--secondary: 210 40% 96.1%; /* hsl(210 40% 96%) */
--secondary-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--accent: 210 30% 96.1%; /* hsl(210 30% 96%) */
--accent-foreground: 222.2 47.4% 11.2%; /* hsl(222 47% 11%) */
--destructive: 0 100% 50%; /* hsl(0 100% 50%) */
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--radius: 0.5rem;
--ring: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
--round-btn-shadow: #00000063;
--info-background: #f0f4fd;
--info-foreground: #141653;
--error-background: #fef2f2;
--error-foreground: #991b1b;
--high-indigo: #4338ca;
--medium-indigo: #6366f1;
--low-indigo: #e0e7ff;
--success-background: #f0fdf4;
--success-foreground: #14532d;
--chat-bot-icon: #afe6ef;
--chat-user-icon: #aface9;
/* Colors that are shared in dark and light mode */
--blur-shared: #151923de;
--build-trigger: #dc735b;
--chat-trigger: #5c8be1;
--chat-trigger-disabled: #b4c3da;
--status-red: #ef4444;
--status-yellow: #eab308;
--chat-send: #059669;
--status-green: #4ade80;
--status-blue:#2563eb;
--connection: #555;
--info-background: #f0f4fd;
--info-foreground: #141653;
--high-indigo: #4338ca;
--medium-indigo: #6366f1;
--low-indigo: #e0e7ff;
--beta-background: rgb(219 234 254);
--beta-foreground: rgb(37 99 235);
--chat-bot-icon: #afe6ef;
--chat-user-icon: #aface9;
/* Colors that are shared in dark and light mode */
--blur-shared: #151923de;
--build-trigger: #dc735b;
--chat-trigger: #5c8be1;
--chat-trigger-disabled: #b4c3da;
--status-red: #ef4444;
--status-yellow: #eab308;
--chat-send: #059669;
--status-green: #4ade80;
--status-blue: #2563eb;
--connection: #555;
}
.dark {
--background: 224 35% 7.5%; /* hsl(224 40% 10%) */
--foreground: 213 31% 80%; /* hsl(213 31% 91%) */
--muted: 223 27% 11%; /* hsl(223 27% 11%) */
--muted-foreground: 215.4 16.3% 56.9%; /* hsl(215 16% 56%) */
--popover: 224 71% 4%; /* hsl(224 71% 4%) */
--popover-foreground: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
--card: 224 25% 15.5%; /* hsl(224 71% 4%) */
--card-foreground: 213 31% 80%; /* hsl(213 31% 91%) */
--border: 216 24% 17%; /* hsl(216 34% 17%) */
--input: 216 24% 17%; /* hsl(216 34% 17%) */
--primary: 210 20% 80%; /* hsl(210 20% 80%) */
--primary-foreground: 222.2 27.4% 1.2%; /* hsl(222 47% 1%) */
--secondary: 222.2 37.4% 7.2%; /* hsl(222 47% 11%) */
--secondary-foreground: 210 40% 80%; /* hsl(210 40% 80%) */
--accent: 216 24% 20%; /* hsl(216 34% 17%) */
--accent-foreground: 210 30% 98%; /* hsl(210 40% 98%) */
--destructive: 0 63% 31%; /* hsl(0 63% 31%) */
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--ring: 216 24% 30%; /* hsl(216 24% 30%) */
--radius: 0.5rem;
--round-btn-shadow: #00000063;
--success-background: #022c22;
--success-foreground: #ecfdf5;
--error-foreground: #fef2f2;
--error-background: #450a0a;
--info-foreground: #eff6ff;
--info-background: #172554;
--high-indigo: #4338ca;
--medium-indigo: #6366f1;
--low-indigo: #e0e7ff;
/* Colors that are shared in dark and light mode */
--blur-shared: #151923d2;
--build-trigger: #dc735b;
--chat-trigger: #5c8be1;
--chat-trigger-disabled: #2d3b54;
--status-red: #ef4444;
--status-yellow: #eab308;
--chat-send: #059669;
--status-green: #4ade80;
--status-blue: #2563eb;
--connection: #555;
--beta-background: rgb(37 99 235);
--beta-foreground: rgb(219 234 254);
--chat-bot-icon: #235d70;
--chat-user-icon: #4f3d6e;
}
}
.dark {
--background: 224 35% 7.5%; /* hsl(224 40% 10%) */
--foreground: 213 31% 80%; /* hsl(213 31% 91%) */
--muted: 223 27% 11%; /* hsl(223 27% 11%) */
--muted-foreground: 215.4 16.3% 56.9%; /* hsl(215 16% 56%) */
--popover: 224 71% 4%; /* hsl(224 71% 4%) */
--popover-foreground: 215 20.2% 65.1%; /* hsl(215 20% 65%) */
--card: 224 25% 15.5%; /* hsl(224 71% 4%) */
--card-foreground: 213 31% 80%; /* hsl(213 31% 91%) */
--border: 216 24% 17%; /* hsl(216 34% 17%) */
--input: 216 24% 17%; /* hsl(216 34% 17%) */
--primary: 210 20% 80%; /* hsl(210 20% 80%) */
--primary-foreground: 222.2 27.4% 1.2%; /* hsl(222 47% 1%) */
--secondary: 222.2 37.4% 7.2%; /* hsl(222 47% 11%) */
--secondary-foreground: 210 40% 80%; /* hsl(210 40% 80%) */
--accent: 216 24% 20%; /* hsl(216 34% 17%) */
--accent-foreground: 210 30% 98%; /* hsl(210 40% 98%) */
--destructive: 0 63% 31%; /* hsl(0 63% 31%) */
--destructive-foreground: 210 40% 98%; /* hsl(210 40% 98%) */
--ring: 216 24% 30%; /* hsl(216 24% 30%) */
--radius: 0.5rem;
--round-btn-shadow: #00000063;
--success-background: #022c22;
--success-foreground: #ecfdf5;
--error-foreground: #fef2f2;
--error-background: #450a0a;
--info-foreground: #eff6ff;
--info-background: #172554;
--high-indigo: #4338ca;
--medium-indigo: #6366f1;
--low-indigo: #e0e7ff;
/* Colors that are shared in dark and light mode */
--blur-shared: #151923d2;
--build-trigger: #dc735b;
--chat-trigger: #5c8be1;
--chat-trigger-disabled: #2d3b54;
--status-red: #ef4444;
--status-yellow: #eab308;
--chat-send: #059669;
--status-green: #4ade80;
--status-blue: #2563eb;
--connection: #555;
--chat-bot-icon: #235d70;
--chat-user-icon: #4f3d6e;
}}

View file

@ -14,8 +14,9 @@ export type APIClassType = {
display_name: string;
input_types?: Array<string>;
output_types?: Array<string>;
beta?: boolean;
documentation: string;
[key: string]: Array<string> | string | APITemplateType;
[key: string]: Array<string> | string | APITemplateType | boolean;
};
export type TemplateVariableType = {

View file

@ -62,8 +62,9 @@ export type CodeAreaComponentType = {
onChange: (value: string[] | string) => void;
value: string;
editNode?: boolean;
nodeClass: APIClassType;
setNodeClass: (value: APIClassType) => void;
nodeClass?: APIClassType;
setNodeClass?: (value: APIClassType) => void;
dynamic?: boolean;
};
export type FileComponentType = {

View file

@ -14,6 +14,7 @@ import {
Cpu,
Download,
DownloadCloud,
Edit,
Eraser,
ExternalLink,
File,
@ -74,6 +75,7 @@ import { EvernoteIcon } from "../icons/Evernote";
import { FBIcon } from "../icons/FacebookMessenger";
import { GitBookIcon } from "../icons/GitBook";
import { GoogleIcon } from "../icons/Google";
import GradientSparkles from "../icons/GradientSparkles";
import { HuggingFaceIcon } from "../icons/HuggingFace";
import { IFixIcon } from "../icons/IFixIt";
import { MetaIcon } from "../icons/Meta";
@ -146,6 +148,7 @@ export const nodeColors: { [char: string]: string } = {
str: "#049524",
retrievers: "#e6b25a",
unknown: "#9CA3AF",
custom_components: "#ab11ab",
};
export const nodeNames: { [char: string]: string } = {
@ -166,6 +169,7 @@ export const nodeNames: { [char: string]: string } = {
retrievers: "Retrievers",
utilities: "Utilities",
output_parsers: "Output Parsers",
custom_components: "Custom",
unknown: "Unknown",
};
@ -224,6 +228,8 @@ export const nodeIconsLucide = {
unknown: HelpCircle,
WikipediaQueryRun: SvgWikipedia,
WolframAlphaQueryRun: SvgWolfram,
custom_components: GradientSparkles,
custom: Edit,
Trash2,
X,
XCircle,

View file

@ -5,6 +5,7 @@ import { IVarHighlightType } from "../types/components";
import { FlowType } from "../types/flow";
import { TabsState } from "../types/tabs";
import { buildTweaks } from "./reactflowUtils";
import { nodeNames } from "./styleUtils";
export function classNames(...classes: Array<string>) {
return classes.filter(Boolean).join(" ");
@ -88,12 +89,13 @@ export function checkUpperWords(str: string) {
export const isWrappedWithClass = (event: any, className: string | undefined) =>
event.target.closest(`.${className}`);
export function groupByFamily(data, baseClasses, left, type) {
export function groupByFamily(data, baseClasses, left, type, flow) {
let parentOutput: string;
let arrOfParent: string[] = [];
let arrOfType: { family: string; type: string; component: string }[] = [];
let arrOfLength: { length: number; type: string }[] = [];
let lastType = "";
Object.keys(data).forEach((d) => {
Object.keys(data[d]).forEach((n) => {
try {
@ -165,7 +167,7 @@ export function groupByFamily(data, baseClasses, left, type) {
});
}
if (left === false) {
if (parentOutput !== "custom_components") {
let resFil = result.filter((group) => group.family === parentOutput);
result = resFil;
}
@ -203,6 +205,107 @@ export function groupByFamily(data, baseClasses, left, type) {
}
}
export function groupByFamilyCustom(data, baseClasses, left, type, flow) {
let arrOfParentCustom: string[] = [];
let arrOfType: { family: string; type: string; component: string }[] = [];
if (type === "CustomComponent") {
const uniqueValuesSet = new Set();
flow.forEach((element) => {
element["data"]["node"]["base_classes"].forEach((el) => {
if (!uniqueValuesSet.has(el)) {
arrOfParentCustom.push(el);
uniqueValuesSet.add(el);
}
});
});
}
if (left === false) {
arrOfParentCustom.map((n) => {
try {
arrOfType.push({
family: "custom_components",
type: n,
component: nodeNames["custom_components"],
});
} catch (e) {
console.log(e);
}
});
} else {
flow.forEach((element) => {
Object.keys(element["data"]["node"]["template"]).map((el) => {
if (
element["data"]["node"]["template"][el].input_types &&
element["data"]["node"]["template"][el].input_types.length > 0
) {
element["data"]["node"]["template"][el].input_types.map((n) => {
try {
arrOfType.push({
family: "custom_components",
type: n,
component: nodeNames["custom_components"],
});
} catch (e) {
console.log(e);
}
});
}
});
});
}
const groupedResult = {};
arrOfType.forEach((item) => {
const { family, type, component } = item;
if (groupedResult.hasOwnProperty(family)) {
if (!groupedResult[family].type.includes(type)) {
groupedResult[family].type += `, ${type}`;
}
} else {
groupedResult[family] = { family, type, component };
}
});
const result = Object.values(groupedResult);
if (left === false) {
let resultFiltered = [];
flow.forEach((element) => {
Object.keys(element["data"]["node"]["template"]).map((el) => {
if (
element["data"]["node"]["template"][el].input_types &&
element["data"]["node"]["template"][el].input_types.length > 0
) {
element["data"]["node"]["template"][el].input_types.map((n) => {
resultFiltered.push({
family: "custom_components",
type: n,
component: element["data"]["node"]["display_name"],
});
});
}
});
});
if (resultFiltered.length === 0) {
Object.keys(groupedResult).forEach((el) => {
resultFiltered.push({
family: "custom_components",
type: groupedResult[el].type,
component: nodeNames["custom_components"],
});
});
}
return resultFiltered;
} else {
return result;
}
}
export function buildInputs(tabsState, id) {
return tabsState &&
tabsState[id] &&

View file

@ -71,6 +71,8 @@ module.exports = {
"status-yellow": "var(--status-yellow)",
"success-background": "var(--success-background)",
"success-foreground": "var(--success-foreground)",
"beta-background": "var(--beta-background)",
"beta-foreground": "var(--beta-foreground)",
"chat-bot-icon": "var(--chat-bot-icon)",
"chat-user-icon": "var(--chat-user-icon)",