Merge branch 'logspace-ai:dev' into dev
This commit is contained in:
commit
fd638994cb
14 changed files with 264 additions and 200 deletions
|
|
@ -1,5 +1,5 @@
|
|||
from importlib.metadata import version
|
||||
import logging
|
||||
from importlib.metadata import version
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
|
|
|
|||
|
|
@ -54,4 +54,4 @@ def post_validate_node(node_id: str, data: dict):
|
|||
return json.dumps({"valid": True, "params": str(node._built_object_repr())})
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
return json.dumps({"valid": False})
|
||||
return json.dumps({"valid": False, "params": str(e)})
|
||||
|
|
|
|||
|
|
@ -143,7 +143,8 @@ class DocumentLoaderNode(Node):
|
|||
# This built_object is a list of documents. Maybe we should
|
||||
# show how many documents are in the list?
|
||||
if self._built_object:
|
||||
return f"""{self.node_type}({len(self._built_object)} documents)\nDocuments: {self._built_object[:3]}..."""
|
||||
return f"""{self.node_type}({len(self._built_object)} documents)
|
||||
Documents: {self._built_object[:3]}..."""
|
||||
return f"{self.node_type}()"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
from typing import Dict, List, Optional
|
||||
from typing import Dict, List, Optional, Type
|
||||
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.custom_lists import embedding_type_to_cls_dict
|
||||
from langflow.settings import settings
|
||||
from langflow.template.base import FrontendNode
|
||||
from langflow.template.nodes import EmbeddingFrontendNode
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.utils.util import build_template_from_class
|
||||
|
||||
|
|
@ -14,6 +16,10 @@ class EmbeddingCreator(LangChainTypeCreator):
|
|||
def type_to_loader_dict(self) -> Dict:
|
||||
return embedding_type_to_cls_dict
|
||||
|
||||
@property
|
||||
def frontend_node_class(self) -> Type[FrontendNode]:
|
||||
return EmbeddingFrontendNode
|
||||
|
||||
def get_signature(self, name: str) -> Optional[Dict]:
|
||||
"""Get the signature of an embedding."""
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ def instantiate_toolkit(node_type, class_object, params):
|
|||
|
||||
def instantiate_embedding(class_object, params):
|
||||
params.pop("model", None)
|
||||
params.pop("headers", None)
|
||||
try:
|
||||
return class_object(**params)
|
||||
except ValidationError:
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ import io
|
|||
from typing import Any, Dict, List, Tuple
|
||||
|
||||
from chromadb.errors import NotEnoughElementsException # type: ignore
|
||||
from langchain.schema import AgentAction
|
||||
|
||||
from langflow.api.callback import AsyncStreamingLLMCallbackHandler, StreamingLLMCallbackHandler # type: ignore
|
||||
from langflow.cache.base import compute_dict_hash, load_cache, memoize_dict
|
||||
from langflow.graph.graph import Graph
|
||||
from langflow.utils.logger import logger
|
||||
from langchain.schema import AgentAction
|
||||
|
||||
|
||||
def load_langchain_object(data_graph, is_first_message=False):
|
||||
|
|
|
|||
|
|
@ -614,3 +614,11 @@ class LLMFrontendNode(FrontendNode):
|
|||
elif field.name in ["model_name", "temperature"]:
|
||||
field.advanced = False
|
||||
field.show = True
|
||||
|
||||
|
||||
class EmbeddingFrontendNode(FrontendNode):
|
||||
@staticmethod
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
FrontendNode.format_field(field, name)
|
||||
if field.name == "headers":
|
||||
field.show = False
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
import { BugAntIcon, Cog6ToothIcon, ExclamationCircleIcon, InformationCircleIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||
import {
|
||||
BugAntIcon,
|
||||
CheckCircleIcon,
|
||||
Cog6ToothIcon,
|
||||
EllipsisHorizontalCircleIcon,
|
||||
ExclamationCircleIcon,
|
||||
InformationCircleIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { classNames, nodeColors, nodeIcons, toNormalCase } from "../../utils";
|
||||
import ParameterComponent from "./components/parameterComponent";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
|
|
@ -12,11 +20,11 @@ import { TabsContext } from "../../contexts/tabsContext";
|
|||
import { debounce } from "../../utils";
|
||||
import Tooltip from "../../components/TooltipComponent";
|
||||
export default function GenericNode({
|
||||
data,
|
||||
selected,
|
||||
data,
|
||||
selected,
|
||||
}: {
|
||||
data: NodeDataType;
|
||||
selected: boolean;
|
||||
data: NodeDataType;
|
||||
selected: boolean;
|
||||
}) {
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
const showError = useRef(true);
|
||||
|
|
@ -30,32 +38,28 @@ export default function GenericNode({
|
|||
const { reactFlowInstance } = useContext(typesContext);
|
||||
const [params, setParams] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (reactFlowInstance) {
|
||||
setParams(Object.values(reactFlowInstance.toObject()));
|
||||
}
|
||||
}, [save]);
|
||||
useEffect(() => {
|
||||
if (reactFlowInstance) {
|
||||
setParams(Object.values(reactFlowInstance.toObject()));
|
||||
}
|
||||
}, [save]);
|
||||
|
||||
const validateNode = useCallback(
|
||||
debounce(async () => {
|
||||
try {
|
||||
const response = await fetch(`/validate/node/${data.id}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(reactFlowInstance.toObject()),
|
||||
});
|
||||
const validateNode = useCallback(
|
||||
debounce(async () => {
|
||||
try {
|
||||
const response = await fetch(`/validate/node/${data.id}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(reactFlowInstance.toObject()),
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
let jsonResponse = await response.json();
|
||||
let jsonResponseParsed = await JSON.parse(jsonResponse);
|
||||
console.log(jsonResponseParsed);
|
||||
if(jsonResponseParsed.valid){
|
||||
setValidationStatus(jsonResponseParsed.params);
|
||||
} else {
|
||||
setValidationStatus("error");
|
||||
}
|
||||
setValidationStatus(jsonResponseParsed);
|
||||
}
|
||||
} catch (error) {
|
||||
// console.error("Error validating node:", error);
|
||||
|
|
@ -70,31 +74,22 @@ export default function GenericNode({
|
|||
}
|
||||
}, [params, validateNode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (validationStatus !== "error") {
|
||||
setIsValid(true);
|
||||
} else {
|
||||
setIsValid(false);
|
||||
if (!Icon) {
|
||||
if (showError.current) {
|
||||
setErrorData({
|
||||
title: data.type
|
||||
? `The ${data.type} node could not be rendered, please review your json file`
|
||||
: "There was a node that can't be rendered, please review your json file",
|
||||
});
|
||||
showError.current = false;
|
||||
}
|
||||
}, [validationStatus]);
|
||||
|
||||
if (!Icon) {
|
||||
if (showError.current) {
|
||||
setErrorData({
|
||||
title: data.type
|
||||
? `The ${data.type} node could not be rendered, please review your json file`
|
||||
: "There was a node that can't be rendered, please review your json file",
|
||||
});
|
||||
showError.current = false;
|
||||
}
|
||||
deleteNode(data.id);
|
||||
return;
|
||||
}
|
||||
deleteNode(data.id);
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
isValid ? "animate-pulse-green" : "border-red-outline",
|
||||
selected ? "border border-blue-500" : "border dark:border-gray-700",
|
||||
"prompt-node relative bg-white dark:bg-gray-900 w-96 rounded-lg flex flex-col justify-center"
|
||||
)}
|
||||
|
|
@ -108,15 +103,48 @@ export default function GenericNode({
|
|||
}}
|
||||
/>
|
||||
<div className="ml-2 truncate">{data.type}</div>
|
||||
{validationStatus && validationStatus !== "error" ?
|
||||
<Tooltip title={
|
||||
<div className="max-h-96 overflow-auto">
|
||||
{validationStatus}
|
||||
</div>}>
|
||||
<ExclamationCircleIcon className="w-5 hover:text-gray-500 hover:dark:text-gray-300" />
|
||||
</Tooltip>
|
||||
: <></>
|
||||
}
|
||||
<div>
|
||||
<Tooltip
|
||||
title={
|
||||
!validationStatus ? (
|
||||
"Validating..."
|
||||
) : (
|
||||
<div className="max-h-96 overflow-auto">
|
||||
{validationStatus.params.split("\n").map((line, index) => (
|
||||
<div key={index}>{line}</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="relative w-5 h-5">
|
||||
<CheckCircleIcon
|
||||
className={classNames(
|
||||
validationStatus && validationStatus.valid
|
||||
? "text-green-500 opacity-100"
|
||||
: "text-green-500 opacity-0 animate-spin",
|
||||
"absolute w-5 hover:text-gray-500 hover:dark:text-gray-300 transition-all ease-in-out duration-200"
|
||||
)}
|
||||
/>
|
||||
<ExclamationCircleIcon
|
||||
className={classNames(
|
||||
validationStatus && !validationStatus.valid
|
||||
? "text-red-500 opacity-100"
|
||||
: "text-red-500 opacity-0 animate-spin",
|
||||
"w-5 absolute hover:text-gray-500 hover:dark:text-gray-600 transition-all ease-in-out duration-200"
|
||||
)}
|
||||
/>
|
||||
<EllipsisHorizontalCircleIcon
|
||||
className={classNames(
|
||||
!validationStatus
|
||||
? "text-yellow-500 opacity-100"
|
||||
: "text-yellow-500 opacity-0 animate-spin",
|
||||
"w-5 absolute hover:text-gray-500 hover:dark:text-gray-600 transition-all ease-in-out duration-300"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
|
|
@ -162,12 +190,12 @@ export default function GenericNode({
|
|||
{data.node.description}
|
||||
</div>
|
||||
|
||||
<>
|
||||
{Object.keys(data.node.template)
|
||||
.filter((t) => t.charAt(0) !== "_")
|
||||
.map((t: string, idx) => (
|
||||
<div key={idx}>
|
||||
{/* {idx === 0 ? (
|
||||
<>
|
||||
{Object.keys(data.node.template)
|
||||
.filter((t) => t.charAt(0) !== "_")
|
||||
.map((t: string, idx) => (
|
||||
<div key={idx}>
|
||||
{/* {idx === 0 ? (
|
||||
<div
|
||||
className={classNames(
|
||||
"px-5 py-2 mt-2 dark:text-white text-center",
|
||||
|
|
@ -186,59 +214,59 @@ export default function GenericNode({
|
|||
) : (
|
||||
<></>
|
||||
)} */}
|
||||
{data.node.template[t].show &&
|
||||
!data.node.template[t].advanced ? (
|
||||
<ParameterComponent
|
||||
data={data}
|
||||
color={
|
||||
nodeColors[types[data.node.template[t].type]] ??
|
||||
nodeColors.unknown
|
||||
}
|
||||
title={
|
||||
data.node.template[t].display_name
|
||||
? data.node.template[t].display_name
|
||||
: data.node.template[t].name
|
||||
? toNormalCase(data.node.template[t].name)
|
||||
: toNormalCase(t)
|
||||
}
|
||||
name={t}
|
||||
tooltipTitle={
|
||||
"Type: " +
|
||||
data.node.template[t].type +
|
||||
(data.node.template[t].list ? " list" : "")
|
||||
}
|
||||
required={data.node.template[t].required}
|
||||
id={data.node.template[t].type + "|" + t + "|" + data.id}
|
||||
left={true}
|
||||
type={data.node.template[t].type}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div
|
||||
className={classNames(
|
||||
Object.keys(data.node.template).length < 1 ? "hidden" : "",
|
||||
"w-full flex justify-center"
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
</div>
|
||||
{/* <div className="px-5 py-2 mt-2 dark:text-white text-center">
|
||||
{data.node.template[t].show &&
|
||||
!data.node.template[t].advanced ? (
|
||||
<ParameterComponent
|
||||
data={data}
|
||||
color={
|
||||
nodeColors[types[data.node.template[t].type]] ??
|
||||
nodeColors.unknown
|
||||
}
|
||||
title={
|
||||
data.node.template[t].display_name
|
||||
? data.node.template[t].display_name
|
||||
: data.node.template[t].name
|
||||
? toNormalCase(data.node.template[t].name)
|
||||
: toNormalCase(t)
|
||||
}
|
||||
name={t}
|
||||
tooltipTitle={
|
||||
"Type: " +
|
||||
data.node.template[t].type +
|
||||
(data.node.template[t].list ? " list" : "")
|
||||
}
|
||||
required={data.node.template[t].required}
|
||||
id={data.node.template[t].type + "|" + t + "|" + data.id}
|
||||
left={true}
|
||||
type={data.node.template[t].type}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div
|
||||
className={classNames(
|
||||
Object.keys(data.node.template).length < 1 ? "hidden" : "",
|
||||
"w-full flex justify-center"
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
</div>
|
||||
{/* <div className="px-5 py-2 mt-2 dark:text-white text-center">
|
||||
Output
|
||||
</div> */}
|
||||
<ParameterComponent
|
||||
data={data}
|
||||
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
|
||||
title={data.type}
|
||||
tooltipTitle={`Type: ${data.node.base_classes.join(" | ")}`}
|
||||
id={[data.type, data.id, ...data.node.base_classes].join("|")}
|
||||
type={data.node.base_classes.join("|")}
|
||||
left={false}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<ParameterComponent
|
||||
data={data}
|
||||
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
|
||||
title={data.type}
|
||||
tooltipTitle={`Type: ${data.node.base_classes.join(" | ")}`}
|
||||
id={[data.type, data.id, ...data.node.base_classes].join("|")}
|
||||
type={data.node.base_classes.join("|")}
|
||||
left={false}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export default function ChatTrigger({ open, setOpen }) {
|
|||
leaveFrom="translate-y-0"
|
||||
leaveTo="translate-y-96"
|
||||
>
|
||||
<div className="absolute bottom-2 right-3">
|
||||
<div className="absolute bottom-4 right-3">
|
||||
<div
|
||||
style={{ backgroundColor: nodeColors["chat"] }}
|
||||
className="border flex justify-center align-center py-1 px-3 w-12 h-12 rounded-full dark:bg-gray-800 dark:border-gray-600 dark:text-white"
|
||||
|
|
|
|||
|
|
@ -47,44 +47,42 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
return newNodeId.current;
|
||||
}
|
||||
function save() {
|
||||
//disabled until flows can be saved on local storage again without bugs
|
||||
// if (flows.length !== 0)
|
||||
// window.localStorage.setItem(
|
||||
// "tabsData",
|
||||
// JSON.stringify({ tabIndex, flows, id, nodeId: newNodeId.current })
|
||||
// );
|
||||
if (flows.length !== 0)
|
||||
window.localStorage.setItem(
|
||||
"tabsData",
|
||||
JSON.stringify({ tabIndex, flows, id, nodeId: newNodeId.current })
|
||||
);
|
||||
}
|
||||
useEffect(() => {
|
||||
//disabled until flows can be saved on local storage again without bugs
|
||||
//save tabs locally
|
||||
// save();
|
||||
save();
|
||||
|
||||
}, [flows, id, tabIndex, newNodeId]);
|
||||
|
||||
// useEffect(() => {
|
||||
// //get tabs locally saved
|
||||
// let cookie = window.localStorage.getItem("tabsData");
|
||||
// if (cookie && Object.keys(templates).length > 0) {
|
||||
// let cookieObject: LangFlowState = JSON.parse(cookie);
|
||||
// cookieObject.flows.forEach((flow) => {
|
||||
// flow.data.nodes.forEach((node) => {
|
||||
// if (Object.keys(templates[node.data.type]["template"]).length > 0) {
|
||||
// node.data.node.template = updateTemplate(
|
||||
// templates[node.data.type][
|
||||
// "template"
|
||||
// ] as unknown as APITemplateType,
|
||||
useEffect(() => {
|
||||
//get tabs locally saved
|
||||
let cookie = window.localStorage.getItem("tabsData");
|
||||
if (cookie && Object.keys(templates).length > 0) {
|
||||
let cookieObject: LangFlowState = JSON.parse(cookie);
|
||||
cookieObject.flows.forEach((flow) => {
|
||||
flow.data.nodes.forEach((node) => {
|
||||
if (Object.keys(templates[node.data.type]["template"]).length > 0) {
|
||||
node.data.node.template = updateTemplate(
|
||||
templates[node.data.type][
|
||||
"template"
|
||||
] as unknown as APITemplateType,
|
||||
|
||||
// node.data.node.template as APITemplateType
|
||||
// );
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// setTabIndex(cookieObject.tabIndex);
|
||||
// setFlows(cookieObject.flows);
|
||||
// setId(cookieObject.id);
|
||||
// newNodeId.current = cookieObject.nodeId;
|
||||
// }
|
||||
// }, [templates]);
|
||||
node.data.node.template as APITemplateType
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
setTabIndex(cookieObject.tabIndex);
|
||||
setFlows(cookieObject.flows);
|
||||
setId(cookieObject.id);
|
||||
newNodeId.current = cookieObject.nodeId;
|
||||
}
|
||||
}, [templates]);
|
||||
|
||||
function hardReset() {
|
||||
newNodeId.current = 0;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
|
||||
const apiRoutes = ["/all", "/predict", "^/validate/*", "^/chat/*"];
|
||||
const apiRoutes = [
|
||||
"/all",
|
||||
"/predict",
|
||||
"^/validate/*",
|
||||
"^/chat/*",
|
||||
"/version",
|
||||
"/health",
|
||||
];
|
||||
|
||||
// Use environment variable to determine the target.
|
||||
const target = process.env.VITE_PROXY_TARGET || "http://127.0.0.1:7860";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue