diff --git a/src/backend/base/langflow/components/inputs/JsonInput.py b/src/backend/base/langflow/components/inputs/JsonInput.py
new file mode 100644
index 000000000..713868147
--- /dev/null
+++ b/src/backend/base/langflow/components/inputs/JsonInput.py
@@ -0,0 +1,17 @@
+from langflow.base.io.text import TextComponent
+from langflow.field_typing.constants import Data, NestedDict
+
+class JsonInput(TextComponent):
+ display_name = "JSON Input"
+ description = "JSON Input."
+
+ def build_config(self):
+ return {
+ "input_value": {
+ "display_name": "JSON",
+ "field_type": "NestedDict"
+ }
+ }
+
+ def build(self, input_value: NestedDict) -> NestedDict:
+ return input_value
diff --git a/src/backend/base/langflow/components/inputs/KeyPairInput.py b/src/backend/base/langflow/components/inputs/KeyPairInput.py
new file mode 100644
index 000000000..c48d0a0e1
--- /dev/null
+++ b/src/backend/base/langflow/components/inputs/KeyPairInput.py
@@ -0,0 +1,19 @@
+from langflow.base.io.text import TextComponent
+from langflow.field_typing.constants import Data
+
+
+class KeyPairInput(TextComponent):
+ display_name = "Dictionary Input"
+ description = "Dictionary Input."
+
+ def build_config(self):
+ return {
+ "input_value": {
+ "display_name": "Dictionaries",
+ "field_type": "dict",
+ "list": True
+ }
+ }
+
+ def build(self, input_value: dict) -> dict:
+ return input_value
diff --git a/src/backend/base/langflow/components/outputs/CSVOutput.py b/src/backend/base/langflow/components/outputs/CSVOutput.py
new file mode 100644
index 000000000..711a6ab96
--- /dev/null
+++ b/src/backend/base/langflow/components/outputs/CSVOutput.py
@@ -0,0 +1,17 @@
+from typing import Optional
+
+from langflow.base.io.text import TextComponent
+from langflow.field_typing import Text, Data
+
+
+class CSVOutput(TextComponent):
+ display_name = "CSV Output"
+ description = "Used view csv files"
+
+ field_config = {
+ "input_value": {"display_name": "csv","info":"A csv blob","input_types":["Data"]},
+ "separator": {"display_name": "separator","info":"The separator used in the csv file","input_types":["Text"], "field_type":"Text","default_value":";","options":[";", ",", "|"]},
+ }
+
+ def build(self, input_value: Data, separator) -> Data:
+ return {"data": input_value, "separator": separator}
diff --git a/src/backend/base/langflow/components/outputs/ImageOutput.py b/src/backend/base/langflow/components/outputs/ImageOutput.py
new file mode 100644
index 000000000..f50fb7bf7
--- /dev/null
+++ b/src/backend/base/langflow/components/outputs/ImageOutput.py
@@ -0,0 +1,15 @@
+from typing import Optional
+
+from langflow.base.io.text import TextComponent
+from langflow.field_typing import Text
+
+class ImageOutput(TextComponent):
+ display_name = "Image Output"
+ description = "Used view image files"
+
+ field_config = {
+ "input_value": {"display_name": "image","info":"A image url","input_types":["Text"]},
+ }
+
+ def build(self, input_value: Text) -> Text:
+ return input_value
\ No newline at end of file
diff --git a/src/backend/base/langflow/components/outputs/JsonOutput.py b/src/backend/base/langflow/components/outputs/JsonOutput.py
new file mode 100644
index 000000000..cbbc10ddc
--- /dev/null
+++ b/src/backend/base/langflow/components/outputs/JsonOutput.py
@@ -0,0 +1,17 @@
+from langflow.base.io.text import TextComponent
+from langflow.field_typing.constants import Data, NestedDict
+
+class JsonOutput(TextComponent):
+ display_name = "JSON Output"
+ description = "JSON Output."
+
+ def build_config(self):
+ return {
+ "input_value": {
+ "display_name": "JSON",
+ "field_type": "NestedDict"
+ }
+ }
+
+ def build(self, input_value: NestedDict) -> NestedDict:
+ return input_value
diff --git a/src/backend/base/langflow/components/outputs/PDFOutput.py b/src/backend/base/langflow/components/outputs/PDFOutput.py
new file mode 100644
index 000000000..75e1fabf4
--- /dev/null
+++ b/src/backend/base/langflow/components/outputs/PDFOutput.py
@@ -0,0 +1,16 @@
+from typing import Optional
+
+from langflow.base.io.text import TextComponent
+from langflow.field_typing import Text
+
+
+class PDFOutput(TextComponent):
+ display_name = "PDF Output"
+ description = "Used view pdf files"
+
+ field_config = {
+ "input_value": {"display_name": "pdf","info":"A pdf url","input_types":["Text"]},
+ }
+
+ def build(self, input_value: Text) -> Text:
+ return input_value
diff --git a/src/frontend/src/App.css b/src/frontend/src/App.css
index 6aa681415..22a596730 100644
--- a/src/frontend/src/App.css
+++ b/src/frontend/src/App.css
@@ -96,6 +96,18 @@ body {
}
.custom-hover:hover {
- background-color: rgba(99, 102, 241, 0.1); /* Medium indigo color with 20% opacity */
+ background-color: rgba(
+ 99,
+ 102,
+ 241,
+ 0.1
+ ); /* Medium indigo color with 20% opacity */
}
+.json-view-playground .json-view {
+ background-color: #fff !important;
+}
+
+.json-view-flow .json-view {
+ background-color: #bbb !important;
+}
diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx
index eef861758..ad00225b8 100644
--- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx
+++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx
@@ -618,7 +618,7 @@ export default function ParameterComponent({
diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx
index 21701f459..06e87602f 100644
--- a/src/frontend/src/components/cardComponent/index.tsx
+++ b/src/frontend/src/components/cardComponent/index.tsx
@@ -1,7 +1,9 @@
import { useEffect, useState } from "react";
import { getComponent, postLikeComponent } from "../../controllers/API";
import DeleteConfirmationModal from "../../modals/DeleteConfirmationModal";
+import IOModal from "../../modals/IOModal";
import useAlertStore from "../../stores/alertStore";
+import useFlowStore from "../../stores/flowStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { useStoreStore } from "../../stores/storeStore";
import { storeComponent } from "../../types/store";
@@ -18,8 +20,6 @@ import {
CardHeader,
CardTitle,
} from "../ui/card";
-import IOModal from "../../modals/IOModal";
-import useFlowStore from "../../stores/flowStore";
export default function CollectionCardComponent({
data,
@@ -27,7 +27,7 @@ export default function CollectionCardComponent({
disabled = false,
button,
onDelete,
- playground
+ playground,
}: {
data: storeComponent;
authorized?: boolean;
@@ -55,9 +55,9 @@ export default function CollectionCardComponent({
const setNodes = useFlowStore((state) => state.setNodes);
const setEdges = useFlowStore((state) => state.setEdges);
const [openPlayground, setOpenPlayground] = useState(false);
- const setCurrentFlowId = useFlowsManagerStore((state) => state.setCurrentFlowId);
-
-
+ const setCurrentFlowId = useFlowsManagerStore(
+ (state) => state.setCurrentFlowId
+ );
const name = data.is_component ? "Component" : "Flow";
@@ -86,16 +86,18 @@ export default function CollectionCardComponent({
addFlow(true, newFlow)
.then((id) => {
setSuccessData({
- title: `${name} ${isStore ? "Downloaded" : "Installed"
- } Successfully.`,
+ title: `${name} ${
+ isStore ? "Downloaded" : "Installed"
+ } Successfully.`,
});
setLoading(false);
})
.catch((error) => {
setLoading(false);
setErrorData({
- title: `Error ${isStore ? "downloading" : "installing"
- } the ${name}`,
+ title: `Error ${
+ isStore ? "downloading" : "installing"
+ } the ${name}`,
list: [error["response"]["data"]["detail"]],
});
});
@@ -362,15 +364,13 @@ export default function CollectionCardComponent({
)}
{button && button}
- {playground &&
+ {playground && (
- }
+ )}
-
+
);
}
diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx
index 78c344714..3666c61a6 100644
--- a/src/frontend/src/components/codeTabsComponent/index.tsx
+++ b/src/frontend/src/components/codeTabsComponent/index.tsx
@@ -267,7 +267,7 @@ export default function CodeTabsComponent({
{node.data.node.template[
templateField
- ].list ? (
+ ]?.list ? (
diff --git a/src/frontend/src/components/keypairListComponent/index.tsx b/src/frontend/src/components/keypairListComponent/index.tsx
index fcd35e01e..2fa12e3b8 100644
--- a/src/frontend/src/components/keypairListComponent/index.tsx
+++ b/src/frontend/src/components/keypairListComponent/index.tsx
@@ -20,7 +20,13 @@ export default function KeypairListComponent({
}
}, [disabled]);
- const ref = useRef(value.length === 0 ? [{ "": "" }] : value);
+ const checkValueType = (value) => {
+ return Array.isArray(value) ? value : [value];
+ };
+
+ const ref = useRef([]);
+ ref.current =
+ !value || value?.length === 0 ? [{ "": "" }] : checkValueType(value);
useEffect(() => {
if (JSON.stringify(value) !== JSON.stringify(ref.current)) {
diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts
index 49483875c..f8d45da02 100644
--- a/src/frontend/src/constants/constants.ts
+++ b/src/frontend/src/constants/constants.ts
@@ -711,13 +711,19 @@ export const LANGFLOW_SUPPORTED_TYPES = new Set([
export const priorityFields = new Set(["code", "template"]);
-export const INPUT_TYPES = new Set(["ChatInput", "TextInput", "KeyPairInput"]);
+export const INPUT_TYPES = new Set([
+ "ChatInput",
+ "TextInput",
+ "KeyPairInput",
+ "JsonInput",
+]);
export const OUTPUT_TYPES = new Set([
"ChatOutput",
"TextOutput",
"PDFOutput",
"ImageOutput",
"CSVOutput",
+ "JsonOutput",
]);
export const CHAT_FIRST_INITIAL_TEXT =
diff --git a/src/frontend/src/modals/EditNodeModal/index.tsx b/src/frontend/src/modals/EditNodeModal/index.tsx
index 17da8c73b..87c72d1df 100644
--- a/src/frontend/src/modals/EditNodeModal/index.tsx
+++ b/src/frontend/src/modals/EditNodeModal/index.tsx
@@ -203,7 +203,7 @@ const EditNodeModal = forwardRef(
!myData.node.template[templateParam].options ? (
{myData.node.template[templateParam]
- .list ? (
+ ?.list ? (
@@ -420,6 +420,10 @@ const EditNodeModal = forwardRef(
.type === "int" ? (
{
+ if (value) onChange(value);
+ }, [value]);
+ const isDark = useDarkStore((state) => state.dark);
+
+ const ref = useRef(null);
+ ref.current = value;
+
+ const getClassNames = () => {
+ if (!isDark && !left) return "json-view-playground-white";
+ if (!isDark && left) return "json-view-playground-white-left";
+ if (isDark && left) return "json-view-playground-dark-left";
+ if (isDark && !left) return "json-view-playground-dark";
+ };
+
+ return (
+
+ {
+ ref.current = edit["src"];
+ }}
+ onChange={(edit) => {
+ ref.current = edit["src"];
+ }}
+ src={ref.current}
+ />
+
+ );
+}
diff --git a/src/frontend/src/modals/IOModal/components/IOFieldView/components/keyPairInput/index.tsx b/src/frontend/src/modals/IOModal/components/IOFieldView/components/keyPairInput/index.tsx
new file mode 100644
index 000000000..b0b557c55
--- /dev/null
+++ b/src/frontend/src/modals/IOModal/components/IOFieldView/components/keyPairInput/index.tsx
@@ -0,0 +1,91 @@
+import _ from "lodash";
+import { useRef } from "react";
+import IconComponent from "../../../../../../components/genericIconComponent";
+import { Input } from "../../../../../../components/ui/input";
+import { classNames } from "../../../../../../utils/utils";
+
+const IOKeyPairInput = ({ value, onChange, duplicateKey, isList = true }) => {
+ const checkValueType = (value) => {
+ return Array.isArray(value) ? value : [value];
+ };
+
+ const ref = useRef([]);
+ ref.current =
+ !value || value?.length === 0 ? [{ "": "" }] : checkValueType(value);
+
+ const handleChangeKey = (event, idx) => {
+ const oldKey = Object.keys(ref.current[idx])[0];
+ const updatedObj = { [event.target.value]: ref.current[idx][oldKey] };
+ ref.current[idx] = updatedObj;
+ onChange(ref.current);
+ };
+
+ const handleChangeValue = (newValue, idx) => {
+ const key = Object.keys(ref.current[idx])[0];
+ ref.current[idx][key] = newValue;
+ onChange(ref.current);
+ };
+
+ return (
+ <>
+
+ >
+ );
+};
+
+export default IOKeyPairInput;
diff --git a/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx b/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx
index dd02d19ca..468b090fe 100644
--- a/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx
+++ b/src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx
@@ -1,4 +1,5 @@
import { cloneDeep } from "lodash";
+import { useState } from "react";
import ImageViewer from "../../../../components/ImageViewer";
import CsvOutputComponent from "../../../../components/csvOutputComponent";
import PdfViewer from "../../../../components/pdfViewer";
@@ -15,7 +16,13 @@ import { PDFViewConstant } from "../../../../constants/constants";
import { InputOutput } from "../../../../constants/enums";
import useFlowStore from "../../../../stores/flowStore";
import { IOFieldViewProps } from "../../../../types/components";
+import {
+ convertValuesToNumbers,
+ hasDuplicateKeys,
+} from "../../../../utils/reactflowUtils";
import IOFileInput from "./components/FileInput";
+import IoJsonInput from "./components/JSONInput";
+import IOKeyPairInput from "./components/keyPairInput";
export default function IOFieldView({
type,
@@ -39,6 +46,7 @@ export default function IOFieldView({
}
}
};
+ const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
function handleOutputType() {
if (!node) return <>"No node found!">;
@@ -78,6 +86,39 @@ export default function IOFieldView({
/>
);
+ case "KeyPairInput":
+ return (
+ {
+ if (node) {
+ let newNode = cloneDeep(node);
+ newNode.data.node!.template["input_value"].value = e;
+ setNode(node.id, newNode);
+ }
+ const valueToNumbers = convertValuesToNumbers(e);
+ setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers));
+ }}
+ duplicateKey={errorDuplicateKey}
+ isList={node.data.node!.template["input_value"]?.list ?? false}
+ />
+ );
+
+ case "JsonInput":
+ return (
+ {
+ if (node) {
+ let newNode = cloneDeep(node);
+ newNode.data.node!.template["input_value"].value = e;
+ setNode(node.id, newNode);
+ }
+ }}
+ left={left}
+ />
+ );
+
default:
return (
);
+ case "JsonOutput":
+ return (
+ {
+ if (node) {
+ let newNode = cloneDeep(node);
+ newNode.data.node!.template["input_value"].value = e;
+ setNode(node.id, newNode);
+ }
+ }}
+ left={left}
+ output
+ />
+ );
+
default:
return (