Merge remote-tracking branch 'origin/form_io' into python_custom_node_component
This commit is contained in:
commit
6416c43ff2
37 changed files with 669 additions and 423 deletions
|
|
@ -24,24 +24,30 @@ def remove_api_keys(flow: dict):
|
||||||
return flow
|
return flow
|
||||||
|
|
||||||
|
|
||||||
def build_input_keys_response(langchain_object):
|
def build_input_keys_response(langchain_object, artifacts):
|
||||||
"""Build the input keys response."""
|
"""Build the input keys response."""
|
||||||
|
|
||||||
input_keys_response = {
|
input_keys_response = {
|
||||||
"input_keys": {key: "" for key in langchain_object.input_keys},
|
"input_keys": {key: "" for key in langchain_object.input_keys},
|
||||||
"memory_keys": [],
|
"memory_keys": [],
|
||||||
|
"handle_keys": artifacts.get("handle_keys", []),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Set the input keys values from artifacts
|
||||||
|
for key, value in artifacts.items():
|
||||||
|
if key in input_keys_response["input_keys"]:
|
||||||
|
input_keys_response["input_keys"][key] = value
|
||||||
# If the object has memory, that memory will have a memory_variables attribute
|
# If the object has memory, that memory will have a memory_variables attribute
|
||||||
# memory variables should be removed from the input keys
|
# memory variables should be removed from the input keys
|
||||||
if hasattr(langchain_object, "memory") and hasattr(
|
if hasattr(langchain_object, "memory") and hasattr(
|
||||||
langchain_object.memory, "memory_variables"
|
langchain_object.memory, "memory_variables"
|
||||||
):
|
):
|
||||||
# Remove memory variables from input keys
|
# Remove memory variables from input keys
|
||||||
input_keys_response["input_keys"] = [
|
input_keys_response["input_keys"] = {
|
||||||
key
|
key: value
|
||||||
for key in input_keys_response["input_keys"]
|
for key, value in input_keys_response["input_keys"].items()
|
||||||
if key not in langchain_object.memory.memory_variables
|
if key not in langchain_object.memory.memory_variables
|
||||||
]
|
}
|
||||||
# Add memory variables to memory_keys
|
# Add memory variables to memory_keys
|
||||||
input_keys_response["memory_keys"] = langchain_object.memory.memory_variables
|
input_keys_response["memory_keys"] = langchain_object.memory.memory_variables
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ async def stream_build(flow_id: str):
|
||||||
|
|
||||||
async def event_stream(flow_id):
|
async def event_stream(flow_id):
|
||||||
final_response = {"end_of_stream": True}
|
final_response = {"end_of_stream": True}
|
||||||
|
artifacts = {}
|
||||||
try:
|
try:
|
||||||
if flow_id not in flow_data_store:
|
if flow_id not in flow_data_store:
|
||||||
error_message = "Invalid session ID"
|
error_message = "Invalid session ID"
|
||||||
|
|
@ -108,6 +109,11 @@ async def stream_build(flow_id: str):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Building node {params[:50]}{'...' if len(params) > 50 else ''}"
|
f"Building node {params[:50]}{'...' if len(params) > 50 else ''}"
|
||||||
)
|
)
|
||||||
|
if vertex.artifacts:
|
||||||
|
# The artifacts will be prompt variables
|
||||||
|
# passed to build_input_keys_response
|
||||||
|
# to set the input_keys values
|
||||||
|
artifacts.update(vertex.artifacts)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
params = str(exc)
|
params = str(exc)
|
||||||
valid = False
|
valid = False
|
||||||
|
|
@ -124,13 +130,16 @@ async def stream_build(flow_id: str):
|
||||||
langchain_object = graph.build()
|
langchain_object = graph.build()
|
||||||
# Now we need to check the input_keys to send them to the client
|
# Now we need to check the input_keys to send them to the client
|
||||||
if hasattr(langchain_object, "input_keys"):
|
if hasattr(langchain_object, "input_keys"):
|
||||||
input_keys_response = build_input_keys_response(langchain_object)
|
input_keys_response = build_input_keys_response(
|
||||||
|
langchain_object, artifacts
|
||||||
|
)
|
||||||
yield str(StreamData(event="message", data=input_keys_response))
|
yield str(StreamData(event="message", data=input_keys_response))
|
||||||
|
|
||||||
chat_manager.set_cache(flow_id, langchain_object)
|
chat_manager.set_cache(flow_id, langchain_object)
|
||||||
# We need to reset the chat history
|
# We need to reset the chat history
|
||||||
chat_manager.chat_history.empty_history(flow_id)
|
chat_manager.chat_history.empty_history(flow_id)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
logger.exception(exc)
|
||||||
logger.error("Error while building the flow: %s", exc)
|
logger.error("Error while building the flow: %s", exc)
|
||||||
yield str(StreamData(event="error", data={"error": str(exc)}))
|
yield str(StreamData(event="error", data={"error": str(exc)}))
|
||||||
finally:
|
finally:
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,11 @@ def post_validate_prompt(prompt: ValidatePromptRequest):
|
||||||
try:
|
try:
|
||||||
template_field = TemplateField(
|
template_field = TemplateField(
|
||||||
name=variable,
|
name=variable,
|
||||||
|
display_name=variable,
|
||||||
field_type="str",
|
field_type="str",
|
||||||
show=True,
|
show=True,
|
||||||
advanced=False,
|
advanced=False,
|
||||||
input_types=["BaseLoader"],
|
input_types=["BaseLoader", "BaseOutputParser"],
|
||||||
)
|
)
|
||||||
|
|
||||||
prompt.frontend_node.template[variable] = template_field.to_dict()
|
prompt.frontend_node.template[variable] = template_field.to_dict()
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,6 @@ vectorstores:
|
||||||
- MongoDBAtlasVectorSearch
|
- MongoDBAtlasVectorSearch
|
||||||
wrappers:
|
wrappers:
|
||||||
- RequestsWrapper
|
- RequestsWrapper
|
||||||
# - ChatPromptTemplate
|
output_parsers:
|
||||||
# - SystemMessagePromptTemplate
|
- StructuredOutputParser
|
||||||
# - HumanMessagePromptTemplate
|
- ResponseSchema
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,15 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class Edge:
|
class Edge:
|
||||||
def __init__(self, source: "Vertex", target: "Vertex"):
|
def __init__(self, source: "Vertex", target: "Vertex", edge: dict):
|
||||||
self.source: "Vertex" = source
|
self.source: "Vertex" = source
|
||||||
self.target: "Vertex" = target
|
self.target: "Vertex" = target
|
||||||
|
self.source_handle = edge.get("sourceHandle", "")
|
||||||
|
self.target_handle = edge.get("targetHandle", "")
|
||||||
|
# 'BaseLoader;BaseOutputParser|documents|PromptTemplate-zmTlD'
|
||||||
|
# target_param is documents
|
||||||
|
self.target_param = self.target_handle.split("|")[1]
|
||||||
|
|
||||||
self.validate_edge()
|
self.validate_edge()
|
||||||
|
|
||||||
def validate_edge(self) -> None:
|
def validate_edge(self) -> None:
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ class Graph:
|
||||||
raise ValueError(f"Source node {edge['source']} not found")
|
raise ValueError(f"Source node {edge['source']} not found")
|
||||||
if target is None:
|
if target is None:
|
||||||
raise ValueError(f"Target node {edge['target']} not found")
|
raise ValueError(f"Target node {edge['target']} not found")
|
||||||
edges.append(Edge(source, target))
|
edges.append(Edge(source, target, edge))
|
||||||
return edges
|
return edges
|
||||||
|
|
||||||
def _get_vertex_class(self, node_type: str, node_lc_type: str) -> Type[Vertex]:
|
def _get_vertex_class(self, node_type: str, node_lc_type: str) -> Type[Vertex]:
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,5 @@
|
||||||
from langflow.graph.vertex.base import Vertex
|
from langflow.graph.vertex.base import Vertex
|
||||||
from langflow.graph.vertex.types import (
|
from langflow.graph.vertex import types
|
||||||
AgentVertex,
|
|
||||||
ChainVertex,
|
|
||||||
DocumentLoaderVertex,
|
|
||||||
EmbeddingVertex,
|
|
||||||
LLMVertex,
|
|
||||||
MemoryVertex,
|
|
||||||
PromptVertex,
|
|
||||||
TextSplitterVertex,
|
|
||||||
ToolVertex,
|
|
||||||
ToolkitVertex,
|
|
||||||
VectorStoreVertex,
|
|
||||||
WrapperVertex,
|
|
||||||
)
|
|
||||||
from langflow.interface.agents.base import agent_creator
|
from langflow.interface.agents.base import agent_creator
|
||||||
from langflow.interface.chains.base import chain_creator
|
from langflow.interface.chains.base import chain_creator
|
||||||
from langflow.interface.document_loaders.base import documentloader_creator
|
from langflow.interface.document_loaders.base import documentloader_creator
|
||||||
|
|
@ -25,22 +12,24 @@ from langflow.interface.toolkits.base import toolkits_creator
|
||||||
from langflow.interface.tools.base import tool_creator
|
from langflow.interface.tools.base import tool_creator
|
||||||
from langflow.interface.vector_store.base import vectorstore_creator
|
from langflow.interface.vector_store.base import vectorstore_creator
|
||||||
from langflow.interface.wrappers.base import wrapper_creator
|
from langflow.interface.wrappers.base import wrapper_creator
|
||||||
|
from langflow.interface.output_parsers.base import output_parser_creator
|
||||||
|
|
||||||
|
|
||||||
from typing import Dict, Type
|
from typing import Dict, Type
|
||||||
|
|
||||||
|
|
||||||
VERTEX_TYPE_MAP: Dict[str, Type[Vertex]] = {
|
VERTEX_TYPE_MAP: Dict[str, Type[Vertex]] = {
|
||||||
**{t: PromptVertex for t in prompt_creator.to_list()},
|
**{t: types.PromptVertex for t in prompt_creator.to_list()},
|
||||||
**{t: AgentVertex for t in agent_creator.to_list()},
|
**{t: types.AgentVertex for t in agent_creator.to_list()},
|
||||||
**{t: ChainVertex for t in chain_creator.to_list()},
|
**{t: types.ChainVertex for t in chain_creator.to_list()},
|
||||||
**{t: ToolVertex for t in tool_creator.to_list()},
|
**{t: types.ToolVertex for t in tool_creator.to_list()},
|
||||||
**{t: ToolkitVertex for t in toolkits_creator.to_list()},
|
**{t: types.ToolkitVertex for t in toolkits_creator.to_list()},
|
||||||
**{t: WrapperVertex for t in wrapper_creator.to_list()},
|
**{t: types.WrapperVertex for t in wrapper_creator.to_list()},
|
||||||
**{t: LLMVertex for t in llm_creator.to_list()},
|
**{t: types.LLMVertex for t in llm_creator.to_list()},
|
||||||
**{t: MemoryVertex for t in memory_creator.to_list()},
|
**{t: types.MemoryVertex for t in memory_creator.to_list()},
|
||||||
**{t: EmbeddingVertex for t in embedding_creator.to_list()},
|
**{t: types.EmbeddingVertex for t in embedding_creator.to_list()},
|
||||||
**{t: VectorStoreVertex for t in vectorstore_creator.to_list()},
|
**{t: types.VectorStoreVertex for t in vectorstore_creator.to_list()},
|
||||||
**{t: DocumentLoaderVertex for t in documentloader_creator.to_list()},
|
**{t: types.DocumentLoaderVertex for t in documentloader_creator.to_list()},
|
||||||
**{t: TextSplitterVertex for t in textsplitter_creator.to_list()},
|
**{t: types.TextSplitterVertex for t in textsplitter_creator.to_list()},
|
||||||
|
**{t: types.OutputParserVertex for t in output_parser_creator.to_list()},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,11 @@
|
||||||
from langflow.utils.constants import DIRECT_TYPES
|
|
||||||
from langflow.interface.initialize import loading
|
from langflow.interface.initialize import loading
|
||||||
from langflow.interface.listing import ALL_TYPES_DICT
|
from langflow.interface.listing import ALL_TYPES_DICT
|
||||||
from langflow.utils.logger import logger
|
from langflow.utils.logger import logger
|
||||||
from langflow.utils.util import sync_to_async
|
from langflow.utils.util import sync_to_async
|
||||||
|
|
||||||
|
|
||||||
import contextlib
|
|
||||||
import inspect
|
import inspect
|
||||||
import types
|
import types
|
||||||
import warnings
|
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
|
@ -25,6 +22,7 @@ class Vertex:
|
||||||
self._parse_data()
|
self._parse_data()
|
||||||
self._built_object = None
|
self._built_object = None
|
||||||
self._built = False
|
self._built = False
|
||||||
|
self.artifacts: Dict[str, Any] = {}
|
||||||
|
|
||||||
def _parse_data(self) -> None:
|
def _parse_data(self) -> None:
|
||||||
self.data = self._data["data"]
|
self.data = self._data["data"]
|
||||||
|
|
@ -45,6 +43,14 @@ class Vertex:
|
||||||
for key, value in template_dicts.items()
|
for key, value in template_dicts.items()
|
||||||
if not value["required"]
|
if not value["required"]
|
||||||
]
|
]
|
||||||
|
# Add the template_dicts[key]["input_types"] to the optional_inputs
|
||||||
|
self.optional_inputs.extend(
|
||||||
|
[
|
||||||
|
input_type
|
||||||
|
for value in template_dicts.values()
|
||||||
|
for input_type in value.get("input_types", [])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
template_dict = self.data["node"]["template"]
|
template_dict = self.data["node"]["template"]
|
||||||
self.vertex_type = (
|
self.vertex_type = (
|
||||||
|
|
@ -60,6 +66,7 @@ class Vertex:
|
||||||
break
|
break
|
||||||
|
|
||||||
def _build_params(self):
|
def _build_params(self):
|
||||||
|
# sourcery skip: merge-list-append, remove-redundant-if
|
||||||
# Some params are required, some are optional
|
# Some params are required, some are optional
|
||||||
# but most importantly, some params are python base classes
|
# but most importantly, some params are python base classes
|
||||||
# like str and others are LangChain objects like LLMChain, BasePromptTemplate
|
# like str and others are LangChain objects like LLMChain, BasePromptTemplate
|
||||||
|
|
@ -80,8 +87,19 @@ class Vertex:
|
||||||
if isinstance(value, dict)
|
if isinstance(value, dict)
|
||||||
}
|
}
|
||||||
params = {}
|
params = {}
|
||||||
|
|
||||||
|
for edge in self.edges:
|
||||||
|
param_key = edge.target_param
|
||||||
|
if param_key in template_dict:
|
||||||
|
if template_dict[param_key]["list"]:
|
||||||
|
if param_key not in params:
|
||||||
|
params[param_key] = []
|
||||||
|
params[param_key].append(edge.source)
|
||||||
|
else:
|
||||||
|
params[param_key] = edge.source
|
||||||
|
|
||||||
for key, value in template_dict.items():
|
for key, value in template_dict.items():
|
||||||
if key == "_type":
|
if key == "_type" or not value.get("show"):
|
||||||
continue
|
continue
|
||||||
# If the type is not transformable to a python base class
|
# If the type is not transformable to a python base class
|
||||||
# then we need to get the edge that connects to this node
|
# then we need to get the edge that connects to this node
|
||||||
|
|
@ -92,45 +110,8 @@ class Vertex:
|
||||||
file_path = value.get("file_path")
|
file_path = value.get("file_path")
|
||||||
|
|
||||||
params[key] = file_path
|
params[key] = file_path
|
||||||
|
elif value.get("type") in ["str", "prompt"] and params.get(key) is None:
|
||||||
elif value.get("type") not in DIRECT_TYPES:
|
params[key] = value.get("value")
|
||||||
# Get the edge that connects to this node
|
|
||||||
edges = [
|
|
||||||
edge
|
|
||||||
for edge in self.edges
|
|
||||||
if edge.target == self and edge.matched_type in value["type"]
|
|
||||||
]
|
|
||||||
|
|
||||||
# Get the output of the node that the edge connects to
|
|
||||||
# if the value['list'] is True, then there will be more
|
|
||||||
# than one time setting to params[key]
|
|
||||||
# so we need to append to a list if it exists
|
|
||||||
# or create a new list if it doesn't
|
|
||||||
|
|
||||||
if value["required"] and not edges:
|
|
||||||
# If a required parameter is not found, raise an error
|
|
||||||
raise ValueError(
|
|
||||||
f"Required input {key} for module {self.vertex_type} not found"
|
|
||||||
)
|
|
||||||
elif value["list"]:
|
|
||||||
# If this is a list parameter, append all sources to a list
|
|
||||||
params[key] = [edge.source for edge in edges]
|
|
||||||
elif edges:
|
|
||||||
# If a single parameter is found, use its source
|
|
||||||
params[key] = edges[0].source
|
|
||||||
|
|
||||||
elif value["required"] or value.get("value"):
|
|
||||||
# If value does not have value this still passes
|
|
||||||
# but then gives a keyError
|
|
||||||
# so we need to check if value has value
|
|
||||||
new_value = value.get("value")
|
|
||||||
if new_value is None:
|
|
||||||
warnings.warn(f"Value for {key} in {self.vertex_type} is None. ")
|
|
||||||
if value.get("type") == "int":
|
|
||||||
with contextlib.suppress(TypeError, ValueError):
|
|
||||||
new_value = int(new_value) # type: ignore
|
|
||||||
params[key] = new_value
|
|
||||||
|
|
||||||
# Add _type to params
|
# Add _type to params
|
||||||
self.params = params
|
self.params = params
|
||||||
|
|
||||||
|
|
@ -195,11 +176,17 @@ class Vertex:
|
||||||
# and return the instance
|
# and return the instance
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._built_object = loading.instantiate_class(
|
result = loading.instantiate_class(
|
||||||
node_type=self.vertex_type,
|
node_type=self.vertex_type,
|
||||||
base_type=self.base_type,
|
base_type=self.base_type,
|
||||||
params=self.params,
|
params=self.params,
|
||||||
)
|
)
|
||||||
|
# Result could be the _built_object or
|
||||||
|
# (_built_object, dict) tuple
|
||||||
|
if isinstance(result, tuple):
|
||||||
|
self._built_object, self.artifacts = result
|
||||||
|
else:
|
||||||
|
self._built_object = result
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Error building node {self.vertex_type}: {str(exc)}"
|
f"Error building node {self.vertex_type}: {str(exc)}"
|
||||||
|
|
|
||||||
|
|
@ -194,3 +194,8 @@ class PromptVertex(Vertex):
|
||||||
|
|
||||||
self._build()
|
self._build()
|
||||||
return self._built_object
|
return self._built_object
|
||||||
|
|
||||||
|
|
||||||
|
class OutputParserVertex(Vertex):
|
||||||
|
def __init__(self, data: Dict):
|
||||||
|
super().__init__(data, base_type="output_parsers")
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ def import_by_type(_type: str, name: str) -> Any:
|
||||||
"documentloaders": import_documentloader,
|
"documentloaders": import_documentloader,
|
||||||
"textsplitters": import_textsplitter,
|
"textsplitters": import_textsplitter,
|
||||||
"utilities": import_utility,
|
"utilities": import_utility,
|
||||||
|
"output_parsers": import_output_parser,
|
||||||
}
|
}
|
||||||
if _type == "llms":
|
if _type == "llms":
|
||||||
key = "chat" if "chat" in name.lower() else "llm"
|
key = "chat" if "chat" in name.lower() else "llm"
|
||||||
|
|
@ -55,6 +56,11 @@ def import_by_type(_type: str, name: str) -> Any:
|
||||||
return loaded_func(name)
|
return loaded_func(name)
|
||||||
|
|
||||||
|
|
||||||
|
def import_output_parser(output_parser: str) -> Any:
|
||||||
|
"""Import output parser from output parser name"""
|
||||||
|
return import_module(f"from langchain.output_parsers import {output_parser}")
|
||||||
|
|
||||||
|
|
||||||
def import_chat_llm(llm: str) -> BaseChatModel:
|
def import_chat_llm(llm: str) -> BaseChatModel:
|
||||||
"""Import chat llm from llm name"""
|
"""Import chat llm from llm name"""
|
||||||
return import_class(f"langchain.chat_models.{llm}")
|
return import_class(f"langchain.chat_models.{llm}")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import json
|
import json
|
||||||
from typing import Any, Callable, Dict, Sequence
|
from typing import Any, Callable, Dict, List, Sequence
|
||||||
|
|
||||||
from langchain.agents import ZeroShotAgent
|
from langchain.agents import ZeroShotAgent
|
||||||
from langchain.agents import agent as agent_module
|
from langchain.agents import agent as agent_module
|
||||||
|
|
@ -8,6 +8,7 @@ from langchain.agents.agent_toolkits.base import BaseToolkit
|
||||||
from langchain.agents.tools import BaseTool
|
from langchain.agents.tools import BaseTool
|
||||||
from langflow.interface.initialize.vector_store import vecstore_initializer
|
from langflow.interface.initialize.vector_store import vecstore_initializer
|
||||||
|
|
||||||
|
from langchain.schema import Document, BaseOutputParser
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from langflow.interface.importing.utils import (
|
from langflow.interface.importing.utils import (
|
||||||
|
|
@ -18,6 +19,7 @@ from langflow.interface.importing.utils import (
|
||||||
from langflow.interface.custom_lists import CUSTOM_NODES
|
from langflow.interface.custom_lists import CUSTOM_NODES
|
||||||
from langflow.interface.toolkits.base import toolkits_creator
|
from langflow.interface.toolkits.base import toolkits_creator
|
||||||
from langflow.interface.chains.base import chain_creator
|
from langflow.interface.chains.base import chain_creator
|
||||||
|
from langflow.interface.output_parsers.base import output_parser_creator
|
||||||
from langflow.interface.utils import load_file_into_dict
|
from langflow.interface.utils import load_file_into_dict
|
||||||
from langflow.utils import validate
|
from langflow.utils import validate
|
||||||
|
|
||||||
|
|
@ -76,10 +78,21 @@ def instantiate_based_on_type(class_object, base_type, node_type, params):
|
||||||
return instantiate_utility(node_type, class_object, params)
|
return instantiate_utility(node_type, class_object, params)
|
||||||
elif base_type == "chains":
|
elif base_type == "chains":
|
||||||
return instantiate_chains(node_type, class_object, params)
|
return instantiate_chains(node_type, class_object, params)
|
||||||
|
elif base_type == "output_parsers":
|
||||||
|
return instantiate_output_parser(node_type, class_object, params)
|
||||||
else:
|
else:
|
||||||
return class_object(**params)
|
return class_object(**params)
|
||||||
|
|
||||||
|
|
||||||
|
def instantiate_output_parser(node_type, class_object, params):
|
||||||
|
if node_type in output_parser_creator.from_method_nodes:
|
||||||
|
method = output_parser_creator.from_method_nodes[node_type]
|
||||||
|
if class_method := getattr(class_object, method, None):
|
||||||
|
return class_method(**params)
|
||||||
|
raise ValueError(f"Method {method} not found in {class_object}")
|
||||||
|
return class_object(**params)
|
||||||
|
|
||||||
|
|
||||||
def instantiate_chains(node_type, class_object, params):
|
def instantiate_chains(node_type, class_object, params):
|
||||||
if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
|
if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
|
||||||
params["retriever"] = params["retriever"].as_retriever()
|
params["retriever"] = params["retriever"].as_retriever()
|
||||||
|
|
@ -116,19 +129,45 @@ def instantiate_prompt(node_type, class_object, params):
|
||||||
|
|
||||||
prompt = class_object(**params)
|
prompt = class_object(**params)
|
||||||
|
|
||||||
# Now we go through input_variables
|
|
||||||
# Check if they are in params, if so
|
|
||||||
# get their values and set them
|
|
||||||
format_kwargs = {}
|
format_kwargs = {}
|
||||||
for input_variable in prompt.input_variables:
|
for input_variable in prompt.input_variables:
|
||||||
if input_variable in params:
|
if input_variable in params:
|
||||||
input_value = params[input_variable]
|
variable = params[input_variable]
|
||||||
format_kwargs[input_variable] = input_value
|
if isinstance(variable, str):
|
||||||
|
format_kwargs[input_variable] = variable
|
||||||
|
elif isinstance(variable, BaseOutputParser) and hasattr(
|
||||||
|
variable, "get_format_instructions"
|
||||||
|
):
|
||||||
|
format_kwargs[input_variable] = variable.get_format_instructions()
|
||||||
|
# check if is a list of Document
|
||||||
|
elif isinstance(variable, List) and all(
|
||||||
|
isinstance(item, Document) for item in variable
|
||||||
|
):
|
||||||
|
# Format document to contain page_content and metadata
|
||||||
|
# as one string separated by a newline
|
||||||
|
format_kwargs[input_variable] = "\n".join(
|
||||||
|
[
|
||||||
|
f"Document:{item.page_content}\nMetadata:{item.metadata}"
|
||||||
|
for item in variable
|
||||||
|
]
|
||||||
|
)
|
||||||
|
# handle_keys will be a list but it does not exist yet
|
||||||
|
# so we need to create it
|
||||||
|
|
||||||
if format_kwargs:
|
if (
|
||||||
prompt = prompt.partial(**format_kwargs)
|
isinstance(variable, List)
|
||||||
|
and all(isinstance(item, Document) for item in variable)
|
||||||
|
) or (
|
||||||
|
isinstance(variable, BaseOutputParser)
|
||||||
|
and hasattr(variable, "get_format_instructions")
|
||||||
|
):
|
||||||
|
if "handle_keys" not in format_kwargs:
|
||||||
|
format_kwargs["handle_keys"] = []
|
||||||
|
|
||||||
return prompt
|
# Add the handle_keys to the list
|
||||||
|
format_kwargs["handle_keys"].append(input_variable)
|
||||||
|
|
||||||
|
return prompt, format_kwargs
|
||||||
|
|
||||||
|
|
||||||
def instantiate_tool(node_type, class_object, params):
|
def instantiate_tool(node_type, class_object, params):
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from langflow.interface.tools.base import tool_creator
|
||||||
from langflow.interface.utilities.base import utility_creator
|
from langflow.interface.utilities.base import utility_creator
|
||||||
from langflow.interface.vector_store.base import vectorstore_creator
|
from langflow.interface.vector_store.base import vectorstore_creator
|
||||||
from langflow.interface.wrappers.base import wrapper_creator
|
from langflow.interface.wrappers.base import wrapper_creator
|
||||||
|
from langflow.interface.output_parsers.base import output_parser_creator
|
||||||
|
|
||||||
|
|
||||||
def get_type_dict():
|
def get_type_dict():
|
||||||
|
|
@ -28,6 +29,7 @@ def get_type_dict():
|
||||||
"embeddings": embedding_creator.to_list(),
|
"embeddings": embedding_creator.to_list(),
|
||||||
"textSplitters": textsplitter_creator.to_list(),
|
"textSplitters": textsplitter_creator.to_list(),
|
||||||
"utilities": utility_creator.to_list(),
|
"utilities": utility_creator.to_list(),
|
||||||
|
"outputParsers": output_parser_creator.to_list(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
64
src/backend/langflow/interface/output_parsers/base.py
Normal file
64
src/backend/langflow/interface/output_parsers/base.py
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
from typing import Dict, List, Optional, Type
|
||||||
|
|
||||||
|
from langchain import output_parsers
|
||||||
|
|
||||||
|
from langflow.interface.base import LangChainTypeCreator
|
||||||
|
from langflow.interface.importing.utils import import_class
|
||||||
|
from langflow.settings import settings
|
||||||
|
from langflow.template.frontend_node.output_parsers import OutputParserFrontendNode
|
||||||
|
from langflow.utils.logger import logger
|
||||||
|
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||||
|
|
||||||
|
|
||||||
|
class OutputParserCreator(LangChainTypeCreator):
|
||||||
|
type_name: str = "output_parsers"
|
||||||
|
from_method_nodes = {
|
||||||
|
"StructuredOutputParser": "from_response_schemas",
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def frontend_node_class(self) -> Type[OutputParserFrontendNode]:
|
||||||
|
return OutputParserFrontendNode
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type_to_loader_dict(self) -> Dict:
|
||||||
|
if self.type_dict is None:
|
||||||
|
self.type_dict = {
|
||||||
|
output_parser_name: import_class(
|
||||||
|
f"langchain.output_parsers.{output_parser_name}"
|
||||||
|
)
|
||||||
|
# if output_parser_name is not lower case it is a class
|
||||||
|
for output_parser_name in output_parsers.__all__
|
||||||
|
}
|
||||||
|
self.type_dict = {
|
||||||
|
name: output_parser
|
||||||
|
for name, output_parser in self.type_dict.items()
|
||||||
|
if name in settings.output_parsers or settings.dev
|
||||||
|
}
|
||||||
|
return self.type_dict
|
||||||
|
|
||||||
|
def get_signature(self, name: str) -> Optional[Dict]:
|
||||||
|
try:
|
||||||
|
if name in self.from_method_nodes:
|
||||||
|
return build_template_from_method(
|
||||||
|
name,
|
||||||
|
type_to_cls_dict=self.type_to_loader_dict,
|
||||||
|
method_name=self.from_method_nodes[name],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return build_template_from_class(
|
||||||
|
name,
|
||||||
|
type_to_cls_dict=self.type_to_loader_dict,
|
||||||
|
)
|
||||||
|
except ValueError as exc:
|
||||||
|
# raise ValueError("OutputParser not found") from exc
|
||||||
|
logger.error(f"OutputParser {name} not found: {exc}")
|
||||||
|
except AttributeError as exc:
|
||||||
|
logger.error(f"OutputParser {name} not loaded: {exc}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def to_list(self) -> List[str]:
|
||||||
|
return list(self.type_to_loader_dict.keys())
|
||||||
|
|
||||||
|
|
||||||
|
output_parser_creator = OutputParserCreator()
|
||||||
|
|
@ -14,6 +14,23 @@ def build_langchain_object_with_caching(data_graph):
|
||||||
return graph.build()
|
return graph.build()
|
||||||
|
|
||||||
|
|
||||||
|
@memoize_dict(maxsize=10)
|
||||||
|
def build_sorted_vertices_with_caching(data_graph):
|
||||||
|
"""
|
||||||
|
Build langchain object from data_graph.
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.debug("Building langchain object")
|
||||||
|
graph = Graph.from_payload(data_graph)
|
||||||
|
sorted_vertices = graph.topological_sort()
|
||||||
|
artifacts = {}
|
||||||
|
for vertex in sorted_vertices:
|
||||||
|
vertex.build()
|
||||||
|
if vertex.artifacts:
|
||||||
|
artifacts.update(vertex.artifacts)
|
||||||
|
return graph.build(), artifacts
|
||||||
|
|
||||||
|
|
||||||
def build_langchain_object(data_graph):
|
def build_langchain_object(data_graph):
|
||||||
"""
|
"""
|
||||||
Build langchain object from data_graph.
|
Build langchain object from data_graph.
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from langflow.interface.tools.base import tool_creator
|
||||||
from langflow.interface.utilities.base import utility_creator
|
from langflow.interface.utilities.base import utility_creator
|
||||||
from langflow.interface.vector_store.base import vectorstore_creator
|
from langflow.interface.vector_store.base import vectorstore_creator
|
||||||
from langflow.interface.wrappers.base import wrapper_creator
|
from langflow.interface.wrappers.base import wrapper_creator
|
||||||
|
from langflow.interface.output_parsers.base import output_parser_creator
|
||||||
|
|
||||||
from langflow.template.field.base import TemplateField
|
from langflow.template.field.base import TemplateField
|
||||||
from langflow.template.frontend_node.tools import CustomComponentNode
|
from langflow.template.frontend_node.tools import CustomComponentNode
|
||||||
|
|
@ -47,6 +48,7 @@ def build_langchain_types_dict(): # sourcery skip: dict-assign-update-to-union
|
||||||
documentloader_creator,
|
documentloader_creator,
|
||||||
textsplitter_creator,
|
textsplitter_creator,
|
||||||
utility_creator,
|
utility_creator,
|
||||||
|
output_parser_creator,
|
||||||
]
|
]
|
||||||
|
|
||||||
all_types = {}
|
all_types = {}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from pathlib import Path
|
||||||
from langchain.schema import AgentAction
|
from langchain.schema import AgentAction
|
||||||
import json
|
import json
|
||||||
from langflow.interface.run import (
|
from langflow.interface.run import (
|
||||||
build_langchain_object_with_caching,
|
build_sorted_vertices_with_caching,
|
||||||
get_memory_key,
|
get_memory_key,
|
||||||
update_memory_keys,
|
update_memory_keys,
|
||||||
)
|
)
|
||||||
|
|
@ -88,8 +88,16 @@ def process_graph_cached(data_graph: Dict[str, Any], inputs: Optional[dict] = No
|
||||||
with PromptTemplate,then run the graph and return the result and thought.
|
with PromptTemplate,then run the graph and return the result and thought.
|
||||||
"""
|
"""
|
||||||
# Load langchain object
|
# Load langchain object
|
||||||
langchain_object = build_langchain_object_with_caching(data_graph)
|
langchain_object, artifacts = build_sorted_vertices_with_caching(data_graph)
|
||||||
logger.debug("Loaded LangChain object")
|
logger.debug("Loaded LangChain object")
|
||||||
|
if inputs is None:
|
||||||
|
inputs = {}
|
||||||
|
for (
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
) in artifacts.items():
|
||||||
|
if key not in inputs or not inputs[key]:
|
||||||
|
inputs[key] = value
|
||||||
|
|
||||||
if langchain_object is None:
|
if langchain_object is None:
|
||||||
# Raise user facing error
|
# Raise user facing error
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ class Settings(BaseSettings):
|
||||||
toolkits: List[str] = []
|
toolkits: List[str] = []
|
||||||
textsplitters: List[str] = []
|
textsplitters: List[str] = []
|
||||||
utilities: List[str] = []
|
utilities: List[str] = []
|
||||||
|
output_parsers: List[str] = []
|
||||||
dev: bool = False
|
dev: bool = False
|
||||||
database_url: str = "sqlite:///./langflow.db"
|
database_url: str = "sqlite:///./langflow.db"
|
||||||
cache: str = "InMemoryCache"
|
cache: str = "InMemoryCache"
|
||||||
|
|
@ -48,6 +49,10 @@ class Settings(BaseSettings):
|
||||||
self.toolkits = new_settings.toolkits or []
|
self.toolkits = new_settings.toolkits or []
|
||||||
self.textsplitters = new_settings.textsplitters or []
|
self.textsplitters = new_settings.textsplitters or []
|
||||||
self.utilities = new_settings.utilities or []
|
self.utilities = new_settings.utilities or []
|
||||||
|
self.embeddings = new_settings.embeddings or []
|
||||||
|
self.vectorstores = new_settings.vectorstores or []
|
||||||
|
self.documentloaders = new_settings.documentloaders or []
|
||||||
|
self.output_parsers = new_settings.output_parsers or []
|
||||||
self.dev = dev
|
self.dev = dev
|
||||||
|
|
||||||
def update_settings(self, **kwargs):
|
def update_settings(self, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
from typing import Optional
|
||||||
|
from langflow.template.field.base import TemplateField
|
||||||
|
from langflow.template.frontend_node.base import FrontendNode
|
||||||
|
|
||||||
|
|
||||||
|
class OutputParserFrontendNode(FrontendNode):
|
||||||
|
@staticmethod
|
||||||
|
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||||
|
FrontendNode.format_field(field, name)
|
||||||
|
field.show = True
|
||||||
|
|
@ -25,6 +25,7 @@ import { nodeColors } from "../../../../utils";
|
||||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||||
import { PopUpContext } from "../../../../contexts/popUpContext";
|
import { PopUpContext } from "../../../../contexts/popUpContext";
|
||||||
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
||||||
|
import * as _ from "lodash";
|
||||||
|
|
||||||
export default function ParameterComponent({
|
export default function ParameterComponent({
|
||||||
left,
|
left,
|
||||||
|
|
@ -36,6 +37,7 @@ export default function ParameterComponent({
|
||||||
type,
|
type,
|
||||||
name = "",
|
name = "",
|
||||||
required = false,
|
required = false,
|
||||||
|
optionalHandle = null,
|
||||||
}: ParameterComponentType) {
|
}: ParameterComponentType) {
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const refHtml = useRef(null);
|
const refHtml = useRef(null);
|
||||||
|
|
@ -73,6 +75,7 @@ export default function ParameterComponent({
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[tabId]: {
|
[tabId]: {
|
||||||
|
...prev[tabId],
|
||||||
isPending: true,
|
isPending: true,
|
||||||
formKeysData: prev[tabId].formKeysData,
|
formKeysData: prev[tabId].formKeysData,
|
||||||
},
|
},
|
||||||
|
|
@ -85,7 +88,7 @@ export default function ParameterComponent({
|
||||||
|
|
||||||
refHtml.current = groupedObj.map((item, i) => (
|
refHtml.current = groupedObj.map((item, i) => (
|
||||||
<span
|
<span
|
||||||
key={getRandomKeyByssmm()}
|
key={getRandomKeyByssmm() + item.family + i}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
i > 0 ? "items-center flex mt-3" : "items-center flex"
|
i > 0 ? "items-center flex mt-3" : "items-center flex"
|
||||||
)}
|
)}
|
||||||
|
|
@ -132,13 +135,14 @@ export default function ParameterComponent({
|
||||||
<span className="text-red-600">{required ? " *" : ""}</span>
|
<span className="text-red-600">{required ? " *" : ""}</span>
|
||||||
</div>
|
</div>
|
||||||
{left &&
|
{left &&
|
||||||
(type === "str" ||
|
((type === "str" ||
|
||||||
type === "bool" ||
|
type === "bool" ||
|
||||||
type === "float" ||
|
type === "float" ||
|
||||||
type === "code" ||
|
type === "code" ||
|
||||||
type === "prompt" ||
|
type === "prompt" ||
|
||||||
type === "file" ||
|
type === "file" ||
|
||||||
type === "int") ? (
|
type === "int") && !optionalHandle
|
||||||
|
) ? (
|
||||||
<></>
|
<></>
|
||||||
) : (
|
) : (
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ export default function GenericNode({
|
||||||
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
|
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="ml-2 truncate">
|
<div className="ml-2 truncate flex-1">
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
delayDuration={1500}
|
delayDuration={1500}
|
||||||
content={data.node.display_name}
|
content={data.node.display_name}
|
||||||
|
|
@ -204,6 +204,7 @@ export default function GenericNode({
|
||||||
data={data}
|
data={data}
|
||||||
color={
|
color={
|
||||||
nodeColors[types[data.node.template[t].type]] ??
|
nodeColors[types[data.node.template[t].type]] ??
|
||||||
|
nodeColors[data.node.template[t].type] ??
|
||||||
nodeColors.unknown
|
nodeColors.unknown
|
||||||
}
|
}
|
||||||
title={
|
title={
|
||||||
|
|
@ -214,11 +215,12 @@ export default function GenericNode({
|
||||||
: toTitleCase(t)
|
: toTitleCase(t)
|
||||||
}
|
}
|
||||||
name={t}
|
name={t}
|
||||||
tooltipTitle={data.node.template[t].type}
|
tooltipTitle={data.node.template[t].input_types?.join("\n") ?? data.node.template[t].type}
|
||||||
required={data.node.template[t].required}
|
required={data.node.template[t].required}
|
||||||
id={data.node.template[t].type + "|" + t + "|" + data.id}
|
id={(data.node.template[t].input_types?.join(";") ?? data.node.template[t].type) + "|" + t + "|" + data.id}
|
||||||
left={true}
|
left={true}
|
||||||
type={data.node.template[t].type}
|
type={data.node.template[t].type}
|
||||||
|
optionalHandle={data.node.template[t].input_types}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useContext, useEffect, useRef, useState } from "react";
|
||||||
import { useNodes } from "reactflow";
|
import { useNodes } from "reactflow";
|
||||||
import { ChatType } from "../../types/chat";
|
import { ChatType } from "../../types/chat";
|
||||||
import ChatTrigger from "./chatTrigger";
|
import ChatTrigger from "./chatTrigger";
|
||||||
|
|
@ -7,11 +7,13 @@ import BuildTrigger from "./buildTrigger";
|
||||||
import { getBuildStatus } from "../../controllers/API";
|
import { getBuildStatus } from "../../controllers/API";
|
||||||
import { NodeType } from "../../types/flow";
|
import { NodeType } from "../../types/flow";
|
||||||
import FormModal from "../../modals/formModal";
|
import FormModal from "../../modals/formModal";
|
||||||
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
|
import * as _ from "lodash";
|
||||||
|
|
||||||
export default function Chat({ flow }: ChatType) {
|
export default function Chat({ flow }: ChatType) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [openForm, setOpenForm] = useState(false);
|
|
||||||
const [isBuilt, setIsBuilt] = useState(false);
|
const [isBuilt, setIsBuilt] = useState(false);
|
||||||
|
const {tabsState} = useContext(TabsContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
|
@ -44,21 +46,19 @@ export default function Chat({ flow }: ChatType) {
|
||||||
const prevNodesRef = useRef<any[] | undefined>();
|
const prevNodesRef = useRef<any[] | undefined>();
|
||||||
const nodes = useNodes();
|
const nodes = useNodes();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
const prevNodes = prevNodesRef.current;
|
const prevNodes = prevNodesRef.current;
|
||||||
const currentNodes = nodes.map(
|
const currentNodes = nodes.map(
|
||||||
(node: NodeType) => node.data.node.template
|
(node: NodeType) => _.cloneDeep(node.data.node.template)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
prevNodes &&
|
tabsState && tabsState[flow.id] && tabsState[flow.id].isPending
|
||||||
JSON.stringify(prevNodes) !== JSON.stringify(currentNodes)
|
&& JSON.stringify(prevNodes) !== JSON.stringify(currentNodes)
|
||||||
) {
|
) {
|
||||||
setIsBuilt(false);
|
setIsBuilt(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
prevNodesRef.current = currentNodes;
|
prevNodesRef.current = currentNodes;
|
||||||
}, [nodes]);
|
}, [tabsState]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ import GenericModal from "../../modals/genericModal";
|
||||||
import { TypeModal } from "../../utils";
|
import { TypeModal } from "../../utils";
|
||||||
import { INPUT_STYLE } from "../../constants";
|
import { INPUT_STYLE } from "../../constants";
|
||||||
import { ExternalLink } from "lucide-react";
|
import { ExternalLink } from "lucide-react";
|
||||||
|
import { postValidatePrompt } from "../../controllers/API";
|
||||||
|
import { typesContext } from "../../contexts/typesContext";
|
||||||
|
import * as _ from "lodash";
|
||||||
|
|
||||||
export default function PromptAreaComponent({
|
export default function PromptAreaComponent({
|
||||||
setNodeClass,
|
setNodeClass,
|
||||||
|
|
@ -14,8 +17,10 @@ export default function PromptAreaComponent({
|
||||||
disabled,
|
disabled,
|
||||||
editNode = false,
|
editNode = false,
|
||||||
}: TextAreaComponentType) {
|
}: TextAreaComponentType) {
|
||||||
const [myValue, setMyValue] = useState(value);
|
const [myValue, setMyValue] = useState("");
|
||||||
const { openPopUp } = useContext(PopUpContext);
|
const { openPopUp } = useContext(PopUpContext);
|
||||||
|
const { reactFlowInstance } = useContext(typesContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (disabled) {
|
if (disabled) {
|
||||||
setMyValue("");
|
setMyValue("");
|
||||||
|
|
@ -24,8 +29,19 @@ export default function PromptAreaComponent({
|
||||||
}, [disabled, onChange]);
|
}, [disabled, onChange]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (value !== "" && myValue !== value && reactFlowInstance) { // only executed once
|
||||||
setMyValue(value);
|
setMyValue(value);
|
||||||
}, [value]);
|
postValidatePrompt(value, nodeClass)
|
||||||
|
.then((apiReturn) => {
|
||||||
|
if (apiReturn.data) {
|
||||||
|
setNodeClass(apiReturn.data.frontend_node);
|
||||||
|
// need to update reactFlowInstance to re-render the nodes.
|
||||||
|
reactFlowInstance.setEdges(_.cloneDeep(reactFlowInstance.getEdges()));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {});
|
||||||
|
}
|
||||||
|
}, [reactFlowInstance]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,6 @@ export default function ToggleShadComponent({
|
||||||
disabled,
|
disabled,
|
||||||
size,
|
size,
|
||||||
}: ToggleComponentType) {
|
}: ToggleComponentType) {
|
||||||
useEffect(() => {
|
|
||||||
if (disabled) {
|
|
||||||
setEnabled(false);
|
|
||||||
}
|
|
||||||
}, [disabled, setEnabled]);
|
|
||||||
let scaleX, scaleY;
|
let scaleX, scaleY;
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case "small":
|
case "small":
|
||||||
|
|
@ -32,11 +27,12 @@ export default function ToggleShadComponent({
|
||||||
scaleY = 1;
|
scaleY = 1;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
|
<div className={disabled ? "pointer-events-none cursor-not-allowed " : ""}>
|
||||||
<Switch
|
<Switch
|
||||||
style={{
|
style={{
|
||||||
transform: `scaleX(${scaleX}) scaleY(${scaleY})`,
|
transform: `scaleX(${scaleX}) scaleY(${scaleY})`,
|
||||||
}}
|
}}
|
||||||
|
disabled={disabled}
|
||||||
className="data-[state=unchecked]:bg-slate-500"
|
className="data-[state=unchecked]:bg-slate-500"
|
||||||
checked={enabled}
|
checked={enabled}
|
||||||
onCheckedChange={(x: boolean) => {
|
onCheckedChange={(x: boolean) => {
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ export const DESCRIPTIONS: string[] = [
|
||||||
"Your Hub for Text Generation.",
|
"Your Hub for Text Generation.",
|
||||||
"Promptly Ingenious!",
|
"Promptly Ingenious!",
|
||||||
"Building Linguistic Labyrinths.",
|
"Building Linguistic Labyrinths.",
|
||||||
"LangFlow: Create, Chain, Communicate.",
|
"Create, Chain, Communicate.",
|
||||||
"Connect the Dots, Craft Language.",
|
"Connect the Dots, Craft Language.",
|
||||||
"Interactive Language Weaving.",
|
"Interactive Language Weaving.",
|
||||||
"Generate, Innovate, Communicate.",
|
"Generate, Innovate, Communicate.",
|
||||||
|
|
@ -183,6 +183,46 @@ export const DESCRIPTIONS: string[] = [
|
||||||
"Nurture NLP Nodes Here.",
|
"Nurture NLP Nodes Here.",
|
||||||
"Conversational Cartography Unlocked.",
|
"Conversational Cartography Unlocked.",
|
||||||
"Design, Develop, Dialogize.",
|
"Design, Develop, Dialogize.",
|
||||||
|
"Unleashing Linguistic Creativity.",
|
||||||
|
"Graph Your Way to Great Conversations.",
|
||||||
|
"The Power of Language at Your Fingertips.",
|
||||||
|
"Sculpting Language with Precision.",
|
||||||
|
"Where Language Meets Logic.",
|
||||||
|
"Building Intelligent Interactions.",
|
||||||
|
"Your Passport to Linguistic Landscapes.",
|
||||||
|
"Create, Curate, Communicate with LangFlow.",
|
||||||
|
"Flow into the Future of Language.",
|
||||||
|
"Mapping Meaningful Conversations.",
|
||||||
|
"Unravel the Art of Articulation.",
|
||||||
|
"Language Engineering Excellence.",
|
||||||
|
"Navigate the Networks of Conversation.",
|
||||||
|
"Crafting Conversations, One Node at a Time.",
|
||||||
|
"The Pinnacle of Prompt Generation.",
|
||||||
|
"Language Models, Mapped and Mastered.",
|
||||||
|
"Powerful Prompts, Perfectly Positioned.",
|
||||||
|
"Innovation in Interaction with LangFlow.",
|
||||||
|
"Your Toolkit for Text Generation.",
|
||||||
|
"Unfolding Linguistic Possibilities.",
|
||||||
|
"Building Powerful Solutions with Language Models.",
|
||||||
|
"Uncover Business Opportunities with NLP.",
|
||||||
|
"Harness the Power of Conversational AI.",
|
||||||
|
"Transform Your Business with Smart Dialogues.",
|
||||||
|
"Craft Meaningful Interactions, Generate Value.",
|
||||||
|
"Unleashing Business Potential through Language Engineering.",
|
||||||
|
"Empowering Enterprises with Intelligent Interactions.",
|
||||||
|
"Driving Innovation in Business Communication.",
|
||||||
|
"Catalyzing Business Growth through Conversational AI.",
|
||||||
|
"Text Generation Meets Business Transformation.",
|
||||||
|
"Navigate the Linguistic Landscape, Discover Opportunities.",
|
||||||
|
"Create Powerful Connections, Boost Business Value.",
|
||||||
|
"Empowering Communication, Enabling Opportunities.",
|
||||||
|
"Advanced NLP for Groundbreaking Business Solutions.",
|
||||||
|
"Innovation in Interaction, Revolution in Revenue.",
|
||||||
|
"Maximize Impact with Intelligent Conversations.",
|
||||||
|
"Beyond Text Generation - Unleashing Business Opportunities.",
|
||||||
|
"Unlock the Power of AI in Your Business Conversations.",
|
||||||
|
"Crafting Dialogues that Drive Business Success.",
|
||||||
|
"Engineered for Excellence, Built for Business.",
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -256,8 +296,49 @@ export const ADJECTIVES: string[] = [
|
||||||
"thirsty",
|
"thirsty",
|
||||||
"tiny",
|
"tiny",
|
||||||
"trusting",
|
"trusting",
|
||||||
|
"bubbly",
|
||||||
|
"charming",
|
||||||
|
"cheerful",
|
||||||
|
"comical",
|
||||||
|
"dazzling",
|
||||||
|
"delighted",
|
||||||
|
"dynamic",
|
||||||
|
"effervescent",
|
||||||
|
"enthusiastic",
|
||||||
|
"exuberant",
|
||||||
|
"fluffy",
|
||||||
|
"friendly",
|
||||||
|
"funky",
|
||||||
|
"giddy",
|
||||||
|
"giggly",
|
||||||
|
"gleeful",
|
||||||
|
"goofy",
|
||||||
|
"graceful",
|
||||||
|
"grinning",
|
||||||
|
"hilarious",
|
||||||
|
"inquisitive",
|
||||||
|
"joyous",
|
||||||
|
"jubilant",
|
||||||
|
"lively",
|
||||||
|
"mirthful",
|
||||||
|
"mischievous",
|
||||||
|
"optimistic",
|
||||||
|
"peppy",
|
||||||
|
"perky",
|
||||||
|
"playful",
|
||||||
|
"quirky",
|
||||||
|
"radiant",
|
||||||
|
"sassy",
|
||||||
|
"silly",
|
||||||
|
"spirited",
|
||||||
|
"sprightly",
|
||||||
|
"twinkly",
|
||||||
|
"upbeat",
|
||||||
|
"vibrant",
|
||||||
|
"witty",
|
||||||
|
"zany",
|
||||||
|
"zealous",
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nouns for the name of the flow
|
* Nouns for the name of the flow
|
||||||
* @constant
|
* @constant
|
||||||
|
|
@ -406,6 +487,38 @@ export const NOUNS: string[] = [
|
||||||
"wright",
|
"wright",
|
||||||
"yalow",
|
"yalow",
|
||||||
"yonath",
|
"yonath",
|
||||||
|
"coulomb",
|
||||||
|
"degrasse",
|
||||||
|
"dewey",
|
||||||
|
"edison",
|
||||||
|
"eratosthenes",
|
||||||
|
"faraday",
|
||||||
|
"galton",
|
||||||
|
"gauss",
|
||||||
|
"herschel",
|
||||||
|
"hubble",
|
||||||
|
"joule",
|
||||||
|
"kaku",
|
||||||
|
"kepler",
|
||||||
|
"khayyam",
|
||||||
|
"lavoisier",
|
||||||
|
"maxwell",
|
||||||
|
"mendel",
|
||||||
|
"mendeleev",
|
||||||
|
"ohm",
|
||||||
|
"pascal",
|
||||||
|
"planck",
|
||||||
|
"riemann",
|
||||||
|
"schrodinger",
|
||||||
|
"sagan",
|
||||||
|
"tesla",
|
||||||
|
"tyson",
|
||||||
|
"volta",
|
||||||
|
"watt",
|
||||||
|
"weber",
|
||||||
|
"wien",
|
||||||
|
"zoBell",
|
||||||
|
"zuse",
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -189,26 +189,28 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function processFlowEdges(flow) {
|
function processFlowEdges(flow) {
|
||||||
if(!flow.data || !flow.data.edges) return;
|
if (!flow.data || !flow.data.edges) return;
|
||||||
flow.data.edges.forEach((edge) => {
|
flow.data.edges.forEach((edge) => {
|
||||||
edge.className = "";
|
edge.className = "";
|
||||||
edge.style = { stroke: "#555555" };
|
edge.style = { stroke: "#555555" };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function updateDisplay_name(node:NodeType,template:APIClassType) {
|
function updateDisplay_name(node: NodeType, template: APIClassType) {
|
||||||
node.data.node.display_name = template["display_name"]?template["display_name"]:node.data.type;
|
node.data.node.display_name = template["display_name"]
|
||||||
|
? template["display_name"]
|
||||||
|
: node.data.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
function processFlowNodes(flow) {
|
function processFlowNodes(flow) {
|
||||||
if(!flow.data || !flow.data.nodes) return;
|
if (!flow.data || !flow.data.nodes) return;
|
||||||
flow.data.nodes.forEach((node:NodeType) => {
|
flow.data.nodes.forEach((node: NodeType) => {
|
||||||
const template = templates[node.data.type];
|
const template = templates[node.data.type];
|
||||||
if (!template) {
|
if (!template) {
|
||||||
setErrorData({ title: `Unknown node type: ${node.data.type}` });
|
setErrorData({ title: `Unknown node type: ${node.data.type}` });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Object.keys(template["template"]).length > 0) {
|
if (Object.keys(template["template"]).length > 0) {
|
||||||
updateDisplay_name(node,template);
|
updateDisplay_name(node, template);
|
||||||
updateNodeBaseClasses(node, template);
|
updateNodeBaseClasses(node, template);
|
||||||
updateNodeEdges(flow, node, template);
|
updateNodeEdges(flow, node, template);
|
||||||
updateNodeDescription(node, template);
|
updateNodeDescription(node, template);
|
||||||
|
|
@ -217,11 +219,15 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateNodeBaseClasses(node:NodeType,template:APIClassType) {
|
function updateNodeBaseClasses(node: NodeType, template: APIClassType) {
|
||||||
node.data.node.base_classes = template["base_classes"];
|
node.data.node.base_classes = template["base_classes"];
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateNodeEdges(flow:FlowType, node:NodeType,template:APIClassType) {
|
function updateNodeEdges(
|
||||||
|
flow: FlowType,
|
||||||
|
node: NodeType,
|
||||||
|
template: APIClassType
|
||||||
|
) {
|
||||||
flow.data.edges.forEach((edge) => {
|
flow.data.edges.forEach((edge) => {
|
||||||
if (edge.source === node.id) {
|
if (edge.source === node.id) {
|
||||||
edge.sourceHandle = edge.sourceHandle
|
edge.sourceHandle = edge.sourceHandle
|
||||||
|
|
@ -233,11 +239,11 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateNodeDescription(node:NodeType,template:APIClassType) {
|
function updateNodeDescription(node: NodeType, template: APIClassType) {
|
||||||
node.data.node.description = template["description"];
|
node.data.node.description = template["description"];
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateNodeTemplate(node:NodeType,template:APIClassType) {
|
function updateNodeTemplate(node: NodeType, template: APIClassType) {
|
||||||
node.data.node.template = updateTemplate(
|
node.data.node.template = updateTemplate(
|
||||||
template["template"] as unknown as APITemplateType,
|
template["template"] as unknown as APITemplateType,
|
||||||
node.data.node.template as APITemplateType
|
node.data.node.template as APITemplateType
|
||||||
|
|
@ -302,7 +308,15 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
* If the file type is application/json, the file is read and parsed into a JSON object.
|
* If the file type is application/json, the file is read and parsed into a JSON object.
|
||||||
* The resulting JSON object is passed to the addFlow function.
|
* The resulting JSON object is passed to the addFlow function.
|
||||||
*/
|
*/
|
||||||
function uploadFlow(newProject?: boolean) {
|
function uploadFlow(newProject?: boolean, file?: File) {
|
||||||
|
if (file) {
|
||||||
|
file.text().then((text) => {
|
||||||
|
// parse the text into a JSON object
|
||||||
|
let flow: FlowType = JSON.parse(text);
|
||||||
|
|
||||||
|
addFlow(flow, newProject);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
// create a file input
|
// create a file input
|
||||||
const input = document.createElement("input");
|
const input = document.createElement("input");
|
||||||
input.type = "file";
|
input.type = "file";
|
||||||
|
|
@ -310,11 +324,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
// add a change event listener to the file input
|
// add a change event listener to the file input
|
||||||
input.onchange = (e: Event) => {
|
input.onchange = (e: Event) => {
|
||||||
// check if the file type is application/json
|
// check if the file type is application/json
|
||||||
if ((e.target as HTMLInputElement).files[0].type === "application/json") {
|
if (
|
||||||
|
(e.target as HTMLInputElement).files[0].type === "application/json"
|
||||||
|
) {
|
||||||
// get the file from the file input
|
// get the file from the file input
|
||||||
const file = (e.target as HTMLInputElement).files[0];
|
const currentfile = (e.target as HTMLInputElement).files[0];
|
||||||
// read the file as text
|
// read the file as text
|
||||||
file.text().then((text) => {
|
currentfile.text().then((text) => {
|
||||||
// parse the text into a JSON object
|
// parse the text into a JSON object
|
||||||
let flow: FlowType = JSON.parse(text);
|
let flow: FlowType = JSON.parse(text);
|
||||||
|
|
||||||
|
|
@ -325,6 +341,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
// trigger the file input click event to open the file dialog
|
// trigger the file input click event to open the file dialog
|
||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function uploadFlows() {
|
function uploadFlows() {
|
||||||
// create a file input
|
// create a file input
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ export default function ChatInput({
|
||||||
lockChat
|
lockChat
|
||||||
? " bg-input text-black dark:bg-gray-700 dark:text-gray-300"
|
? " bg-input text-black dark:bg-gray-700 dark:text-gray-300"
|
||||||
: " bg-white-200 text-black dark:bg-gray-900 dark:text-gray-300",
|
: " bg-white-200 text-black dark:bg-gray-900 dark:text-gray-300",
|
||||||
"p-4 form-input block w-full custom-scroll rounded-md border-gray-300 dark:border-gray-600 pr-12 sm:text-sm" +
|
"p-4 form-input block w-full custom-scroll rounded-md border-gray-300 dark:border-gray-600 pr-16 sm:text-sm" +
|
||||||
INPUT_STYLE
|
INPUT_STYLE
|
||||||
)}
|
)}
|
||||||
placeholder={"Send a message..."}
|
placeholder={"Send a message..."}
|
||||||
|
|
@ -62,7 +62,7 @@ export default function ChatInput({
|
||||||
<button className={classNames("p-2 px-1 transition-all duration-300 rounded-md",chatValue == "" ? "text-primary" : " bg-indigo-600 text-background")} disabled={lockChat} onClick={() => sendMessage()}>
|
<button className={classNames("p-2 px-1 transition-all duration-300 rounded-md",chatValue == "" ? "text-primary" : " bg-indigo-600 text-background")} disabled={lockChat} onClick={() => sendMessage()}>
|
||||||
{lockChat ? (
|
{lockChat ? (
|
||||||
<Lock
|
<Lock
|
||||||
className="h-5 w-5 ml-1 mr-11 animate-pulse"
|
className="h-5 w-5 ml-1 mr-1 animate-pulse"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ export const CodeBlock: FC<Props> = memo(({ language, value }) => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
className=" w-[570px]"
|
className=" w-[47vw]"
|
||||||
language={language}
|
language={language}
|
||||||
style={oneDark}
|
style={oneDark}
|
||||||
customStyle={{ margin: 0 }}
|
customStyle={{ margin: 0 }}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ export default function ChatMessage({
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[remarkGfm, remarkMath]}
|
remarkPlugins={[remarkGfm, remarkMath]}
|
||||||
rehypePlugins={[rehypeMathjax]}
|
rehypePlugins={[rehypeMathjax]}
|
||||||
className="markdown prose dark:prose-invert text-primary"
|
className="markdown prose dark:prose-invert text-primary max-w-full"
|
||||||
components={{
|
components={{
|
||||||
code({ node, inline, className, children, ...props }) {
|
code({ node, inline, className, children, ...props }) {
|
||||||
if (children.length) {
|
if (children.length) {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,13 @@ import { alertContext } from "../../contexts/alertContext";
|
||||||
import { classNames, validateNodes } from "../../utils";
|
import { classNames, validateNodes } from "../../utils";
|
||||||
import { typesContext } from "../../contexts/typesContext";
|
import { typesContext } from "../../contexts/typesContext";
|
||||||
import ChatMessage from "./chatMessage";
|
import ChatMessage from "./chatMessage";
|
||||||
import { TerminalSquare, MessageSquare, Variable, Eraser } from "lucide-react";
|
import {
|
||||||
|
TerminalSquare,
|
||||||
|
MessageSquare,
|
||||||
|
Variable,
|
||||||
|
Eraser,
|
||||||
|
MessageSquarePlus,
|
||||||
|
} from "lucide-react";
|
||||||
import { sendAllProps } from "../../types/api";
|
import { sendAllProps } from "../../types/api";
|
||||||
import { ChatMessageType } from "../../types/chat";
|
import { ChatMessageType } from "../../types/chat";
|
||||||
import ChatInput from "./chatInput";
|
import ChatInput from "./chatInput";
|
||||||
|
|
@ -30,6 +36,14 @@ import {
|
||||||
import { Textarea } from "../../components/ui/textarea";
|
import { Textarea } from "../../components/ui/textarea";
|
||||||
import { Badge } from "../../components/ui/badge";
|
import { Badge } from "../../components/ui/badge";
|
||||||
import ToggleShadComponent from "../../components/toggleShadComponent";
|
import ToggleShadComponent from "../../components/toggleShadComponent";
|
||||||
|
import Dropdown from "../../components/dropdownComponent";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "../../components/ui/dropdown-menu";
|
||||||
|
import { Button } from "../../components/ui/button";
|
||||||
|
|
||||||
export default function FormModal({
|
export default function FormModal({
|
||||||
flow,
|
flow,
|
||||||
|
|
@ -40,18 +54,27 @@ export default function FormModal({
|
||||||
setOpen: Function;
|
setOpen: Function;
|
||||||
flow: FlowType;
|
flow: FlowType;
|
||||||
}) {
|
}) {
|
||||||
const [chatValue, setChatValue] = useState("");
|
const { tabsState, setTabsState } = useContext(TabsContext);
|
||||||
const [keysValue, setKeysValue] = useState([]);
|
const [chatValue, setChatValue] = useState(
|
||||||
|
tabsState[flow.id].formKeysData.input_keys[
|
||||||
|
Object.keys(tabsState[flow.id].formKeysData.input_keys).find(
|
||||||
|
(k) => !tabsState[flow.id].formKeysData.handle_keys.some((j) => j === k)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
);
|
||||||
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
|
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
|
||||||
const { reactFlowInstance } = useContext(typesContext);
|
const { reactFlowInstance } = useContext(typesContext);
|
||||||
const { setErrorData, setNoticeData } = useContext(alertContext);
|
const { setErrorData, setNoticeData } = useContext(alertContext);
|
||||||
const { tabsState } = useContext(TabsContext);
|
|
||||||
const ws = useRef<WebSocket | null>(null);
|
const ws = useRef<WebSocket | null>(null);
|
||||||
const [lockChat, setLockChat] = useState(false);
|
const [lockChat, setLockChat] = useState(false);
|
||||||
const isOpen = useRef(open);
|
const isOpen = useRef(open);
|
||||||
const messagesRef = useRef(null);
|
const messagesRef = useRef(null);
|
||||||
const id = useRef(flow.id);
|
const id = useRef(flow.id);
|
||||||
const [chatKey, setChatKey] = useState(0);
|
const [chatKey, setChatKey] = useState(
|
||||||
|
Object.keys(tabsState[flow.id].formKeysData.input_keys).find(
|
||||||
|
(k) => !tabsState[flow.id].formKeysData.handle_keys.some((j) => j === k)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (messagesRef.current) {
|
if (messagesRef.current) {
|
||||||
|
|
@ -64,12 +87,6 @@ export default function FormModal({
|
||||||
}, [open]);
|
}, [open]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
id.current = flow.id;
|
id.current = flow.id;
|
||||||
|
|
||||||
if (tabsState[flow.id].formKeysData) {
|
|
||||||
setKeysValue(
|
|
||||||
Array(tabsState[flow.id].formKeysData.input_keys.length).fill("")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [flow.id, tabsState[flow.id], tabsState[flow.id].formKeysData]);
|
}, [flow.id, tabsState[flow.id], tabsState[flow.id].formKeysData]);
|
||||||
|
|
||||||
var isStream = false;
|
var isStream = false;
|
||||||
|
|
@ -166,13 +183,13 @@ export default function FormModal({
|
||||||
chatItem.files
|
chatItem.files
|
||||||
? {
|
? {
|
||||||
isSend: !chatItem.is_bot,
|
isSend: !chatItem.is_bot,
|
||||||
message: chatItem.is_bot ? chatItem.message : formatMessage(chatItem.message),
|
message: formatMessage(chatItem.message),
|
||||||
thought: chatItem.intermediate_steps,
|
thought: chatItem.intermediate_steps,
|
||||||
files: chatItem.files,
|
files: chatItem.files,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
isSend: !chatItem.is_bot,
|
isSend: !chatItem.is_bot,
|
||||||
message: chatItem.is_bot ? chatItem.message : formatMessage(chatItem.message),
|
message: formatMessage(chatItem.message),
|
||||||
thought: chatItem.intermediate_steps,
|
thought: chatItem.intermediate_steps,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
@ -305,6 +322,7 @@ export default function FormModal({
|
||||||
}, [open]);
|
}, [open]);
|
||||||
function formatMessage(inputs: any): string {
|
function formatMessage(inputs: any): string {
|
||||||
if (inputs) {
|
if (inputs) {
|
||||||
|
if (typeof inputs == "string") return inputs;
|
||||||
// inputs is a object with the keys and values being input_keys and keysValue
|
// inputs is a object with the keys and values being input_keys and keysValue
|
||||||
// so the formated message is a string with the keys and values separated by ": "
|
// so the formated message is a string with the keys and values separated by ": "
|
||||||
let message = "";
|
let message = "";
|
||||||
|
|
@ -325,13 +343,7 @@ export default function FormModal({
|
||||||
let nodeValidationErrors = validateNodes(reactFlowInstance);
|
let nodeValidationErrors = validateNodes(reactFlowInstance);
|
||||||
if (nodeValidationErrors.length === 0) {
|
if (nodeValidationErrors.length === 0) {
|
||||||
setLockChat(true);
|
setLockChat(true);
|
||||||
// Message variable makes a object with the keys being the names from tabsState[flow.id].formKeysData.input_keys and the values being the keysValue of the correspondent index
|
let inputs = tabsState[id.current].formKeysData.input_keys;
|
||||||
let keys = tabsState[flow.id].formKeysData.input_keys; // array of keys
|
|
||||||
let values = keysValue.map((k, i) => (i == chatKey ? chatValue : k)); // array of values
|
|
||||||
let inputs = keys.reduce((object, key, index) => {
|
|
||||||
object[key] = values[index];
|
|
||||||
return object;
|
|
||||||
}, {});
|
|
||||||
setChatValue("");
|
setChatValue("");
|
||||||
const message = formatMessage(inputs);
|
const message = formatMessage(inputs);
|
||||||
addChatHistory(message, true);
|
addChatHistory(message, true);
|
||||||
|
|
@ -342,6 +354,11 @@ export default function FormModal({
|
||||||
name: flow.name,
|
name: flow.name,
|
||||||
description: flow.description,
|
description: flow.description,
|
||||||
});
|
});
|
||||||
|
setTabsState((old) => {
|
||||||
|
let newTabsState = _.cloneDeep(old);
|
||||||
|
newTabsState[id.current].formKeysData.input_keys[chatKey] = "";
|
||||||
|
return newTabsState;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
setErrorData({
|
setErrorData({
|
||||||
title: "Oops! Looks like you missed some required information:",
|
title: "Oops! Looks like you missed some required information:",
|
||||||
|
|
@ -365,13 +382,12 @@ export default function FormModal({
|
||||||
setOpen(x);
|
setOpen(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleOnCheckedChange(checked: boolean, index: number) {
|
function handleOnCheckedChange(checked: boolean, i: string) {
|
||||||
if (checked === true) {
|
if (checked === true) {
|
||||||
setChatKey(index);
|
setChatKey(i);
|
||||||
|
setChatValue(tabsState[flow.id].formKeysData.input_keys[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(chatHistory)
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={setModalOpen}>
|
<Dialog open={open} onOpenChange={setModalOpen}>
|
||||||
<DialogTrigger className="hidden"></DialogTrigger>
|
<DialogTrigger className="hidden"></DialogTrigger>
|
||||||
|
|
@ -388,47 +404,90 @@ export default function FormModal({
|
||||||
<DialogDescription>{CHAT_FORM_DIALOG_SUBTITLE}</DialogDescription>
|
<DialogDescription>{CHAT_FORM_DIALOG_SUBTITLE}</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="flex h-[80vh] w-full mt-2">
|
<div className="flex h-[80vh] w-full mt-2 ">
|
||||||
<div className="w-1/4 h-full flex flex-col justify-start mr-6">
|
<div className="w-2/5 h-full overflow-auto scrollbar-hide flex flex-col justify-start mr-6">
|
||||||
<div className="flex py-2">
|
<div className="flex justify-between items-center">
|
||||||
<Variable className="w-6 h-6 pe-1 text-gray-700 stroke-2 dark:text-slate-200"></Variable>
|
<div className="flex py-2 items-center">
|
||||||
<span className="text-md font-semibold text-gray-800 dark:text-white">
|
<Variable className="w-4 h-4 mr-1 text-primary"></Variable>
|
||||||
|
<span className="text-sm font-semibold text-primary">
|
||||||
Input Variables
|
Input Variables
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Accordion type="single" collapsible className="w-full">
|
<div className="flex mr-2.5 py-2 items-center">
|
||||||
{tabsState[id.current].formKeysData.input_keys.map((i, k) => (
|
<span className="text-sm font-semibold text-primary">
|
||||||
<AccordionItem key={k} value={i}>
|
Chat Input
|
||||||
<AccordionTrigger><Badge variant="gray" size="md">{i}</Badge></AccordionTrigger>
|
</span>
|
||||||
<AccordionContent>
|
</div>
|
||||||
<div className="p-1 flex flex-col gap-2">
|
</div>
|
||||||
<div className="flex items-center space-x-2">
|
<Accordion type="multiple" className="w-full">
|
||||||
<Label htmlFor="airplane-mode" className="-mt-1">From Chat</Label>
|
{Object.keys(tabsState[id.current].formKeysData.input_keys).map(
|
||||||
|
(i, k) => (
|
||||||
|
<div className="flex items-start gap-3" key={k}>
|
||||||
|
<AccordionItem className="w-full" key={k} value={i}>
|
||||||
|
<AccordionTrigger className="flex gap-2">
|
||||||
|
<div className="flex items-center w-full justify-between">
|
||||||
|
<Badge variant="gray" size="md">
|
||||||
|
{i}
|
||||||
|
</Badge>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="-mb-1"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ToggleShadComponent
|
<ToggleShadComponent
|
||||||
enabled={chatKey === k}
|
enabled={chatKey === i}
|
||||||
setEnabled={(value) =>
|
setEnabled={(value) =>
|
||||||
handleOnCheckedChange(value, k)
|
handleOnCheckedChange(value, i)
|
||||||
}
|
}
|
||||||
size="small"
|
size="small"
|
||||||
disabled={false}
|
disabled={tabsState[
|
||||||
|
id.current
|
||||||
|
].formKeysData.handle_keys.some((t) => t === i)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent>
|
||||||
|
<div className="p-1 flex flex-col gap-2">
|
||||||
|
{tabsState[
|
||||||
|
id.current
|
||||||
|
].formKeysData.handle_keys.some((t) => t === i) && (
|
||||||
|
<div className="font-normal text-muted-foreground ">
|
||||||
|
Source: Component
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Textarea
|
<Textarea
|
||||||
value={keysValue[k]}
|
value={
|
||||||
onChange={(e) =>
|
tabsState[id.current].formKeysData.input_keys[i]
|
||||||
setKeysValue((old) => [...old.slice(0, k), e.target.value, ...old.slice(k + 1)])
|
|
||||||
}
|
}
|
||||||
disabled={chatKey === k}
|
onChange={(e) => {
|
||||||
|
setTabsState((old) => {
|
||||||
|
let newTabsState = _.cloneDeep(old);
|
||||||
|
newTabsState[
|
||||||
|
id.current
|
||||||
|
].formKeysData.input_keys[i] = e.target.value;
|
||||||
|
return newTabsState;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
disabled={chatKey === i}
|
||||||
placeholder="Enter text..."
|
placeholder="Enter text..."
|
||||||
></Textarea>
|
></Textarea>
|
||||||
</div>
|
</div>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
))}
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
{tabsState[id.current].formKeysData.memory_keys.map((i, k) => (
|
{tabsState[id.current].formKeysData.memory_keys.map((i, k) => (
|
||||||
<AccordionItem key={k} value={i}>
|
<AccordionItem key={k} value={i}>
|
||||||
<div className="flex flex-1 items-center justify-between py-4 font-normal transition-all group text-muted-foreground text-sm">
|
<div className="flex flex-1 items-center justify-between py-4 font-normal transition-all group text-muted-foreground text-sm">
|
||||||
<div className="group-hover:underline"><Badge size="md" variant="gray">{i}</Badge></div>
|
<div className="group-hover:underline">
|
||||||
|
<Badge size="md" variant="gray">
|
||||||
|
{i}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
Used as Memory Key
|
Used as Memory Key
|
||||||
</div>
|
</div>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
|
|
@ -440,7 +499,12 @@ export default function FormModal({
|
||||||
<div className="absolute right-3 top-3 z-50">
|
<div className="absolute right-3 top-3 z-50">
|
||||||
<button disabled={lockChat} onClick={() => clearChat()}>
|
<button disabled={lockChat} onClick={() => clearChat()}>
|
||||||
<Eraser
|
<Eraser
|
||||||
className={classNames("h-5 w-5", lockChat ? "text-primary animate-pulse" : "text-primary hover:text-gray-600")}
|
className={classNames(
|
||||||
|
"h-5 w-5",
|
||||||
|
lockChat
|
||||||
|
? "text-primary animate-pulse"
|
||||||
|
: "text-primary hover:text-gray-600"
|
||||||
|
)}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -486,7 +550,16 @@ export default function FormModal({
|
||||||
chatValue={chatValue}
|
chatValue={chatValue}
|
||||||
lockChat={lockChat}
|
lockChat={lockChat}
|
||||||
sendMessage={sendMessage}
|
sendMessage={sendMessage}
|
||||||
setChatValue={setChatValue}
|
setChatValue={(value) => {
|
||||||
|
setChatValue(value);
|
||||||
|
setTabsState((old) => {
|
||||||
|
let newTabsState = _.cloneDeep(old);
|
||||||
|
newTabsState[id.current].formKeysData.input_keys[
|
||||||
|
chatKey
|
||||||
|
] = chatValue;
|
||||||
|
return newTabsState;
|
||||||
|
});
|
||||||
|
}}
|
||||||
inputRef={ref}
|
inputRef={ref}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
|
||||||
import { XMarkIcon, DocumentTextIcon } from "@heroicons/react/24/outline";
|
|
||||||
import { Fragment, useContext, useRef, useState } from "react";
|
|
||||||
import { PopUpContext } from "../../contexts/popUpContext";
|
|
||||||
import { darkContext } from "../../contexts/darkContext";
|
|
||||||
import { postValidatePrompt } from "../../controllers/API";
|
|
||||||
import { alertContext } from "../../contexts/alertContext";
|
|
||||||
export default function PromptAreaModal({
|
|
||||||
value,
|
|
||||||
setValue,
|
|
||||||
}: {
|
|
||||||
setValue: (value: string) => void;
|
|
||||||
value: string;
|
|
||||||
}) {
|
|
||||||
const [open, setOpen] = useState(true);
|
|
||||||
const [myValue, setMyValue] = useState(value);
|
|
||||||
const { dark } = useContext(darkContext);
|
|
||||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
|
||||||
const { closePopUp } = useContext(PopUpContext);
|
|
||||||
const ref = useRef();
|
|
||||||
function setModalOpen(x: boolean) {
|
|
||||||
setOpen(x);
|
|
||||||
if (x === false) {
|
|
||||||
setTimeout(() => {
|
|
||||||
closePopUp();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Transition.Root show={open} appear={true} as={Fragment}>
|
|
||||||
<Dialog
|
|
||||||
as="div"
|
|
||||||
className="relative z-10"
|
|
||||||
onClose={setModalOpen}
|
|
||||||
initialFocus={ref}
|
|
||||||
>
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100"
|
|
||||||
leaveTo="opacity-0"
|
|
||||||
>
|
|
||||||
<div className="fixed inset-0 bg-ring dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
|
|
||||||
</Transition.Child>
|
|
||||||
|
|
||||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
|
||||||
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
|
||||||
<Transition.Child
|
|
||||||
as={Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
>
|
|
||||||
<Dialog.Panel className="relative flex flex-col justify-between transform h-[600px] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[700px]">
|
|
||||||
<div className=" z-50 absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="rounded-md text-gray-400 hover:text-gray-500"
|
|
||||||
onClick={() => {
|
|
||||||
setModalOpen(false);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Close</span>
|
|
||||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="h-full w-full flex flex-col justify-center items-center">
|
|
||||||
<div className="flex w-full pb-4 z-10 justify-center shadow-sm">
|
|
||||||
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-gray-900 sm:mx-0 sm:h-10 sm:w-10">
|
|
||||||
<DocumentTextIcon
|
|
||||||
className="h-6 w-6 text-blue-600"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="mt-4 text-center sm:ml-4 sm:text-left">
|
|
||||||
<Dialog.Title
|
|
||||||
as="h3"
|
|
||||||
className="text-lg font-medium dark:text-white leading-10 text-gray-900"
|
|
||||||
>
|
|
||||||
Edit Prompt
|
|
||||||
</Dialog.Title>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="h-full w-full bg-accent overflow-auto dark:bg-gray-900 p-4 gap-4 flex flex-row justify-center items-center">
|
|
||||||
<div className="flex h-full w-full">
|
|
||||||
<div className="overflow-hidden px-4 py-5 sm:p-6 w-full h-full rounded-lg bg-white dark:bg-gray-800 shadow">
|
|
||||||
<textarea
|
|
||||||
ref={ref}
|
|
||||||
className="form-input h-full w-full rounded-lg border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-white"
|
|
||||||
value={myValue}
|
|
||||||
onChange={(e) => {
|
|
||||||
setMyValue(e.target.value);
|
|
||||||
setValue(e.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="bg-gray-200 dark:bg-gray-900 w-full pb-3 flex flex-row-reverse px-4">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="inline-flex w-full justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-ring focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:ring-offset-1 sm:ml-3 sm:w-auto sm:text-sm"
|
|
||||||
onClick={() => {
|
|
||||||
postValidatePrompt(myValue)
|
|
||||||
.then((apiReturn) => {
|
|
||||||
if (apiReturn.data) {
|
|
||||||
let inputVariables =
|
|
||||||
apiReturn.data.input_variables;
|
|
||||||
if (inputVariables.length === 0) {
|
|
||||||
setErrorData({
|
|
||||||
title:
|
|
||||||
"The template you are attempting to use does not contain any variables for data entry.",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setSuccessData({
|
|
||||||
title: "Prompt is ready",
|
|
||||||
});
|
|
||||||
setModalOpen(false);
|
|
||||||
setValue(myValue);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setErrorData({
|
|
||||||
title: "Something went wrong, please try again",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
return setErrorData({
|
|
||||||
title:
|
|
||||||
"There is something wrong with this prompt, please review it",
|
|
||||||
list: [error.response.data.detail],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Check & Save
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog.Panel>
|
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</Transition.Root>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -37,6 +37,7 @@ const nodeTypes = {
|
||||||
export default function Page({ flow }: { flow: FlowType }) {
|
export default function Page({ flow }: { flow: FlowType }) {
|
||||||
let {
|
let {
|
||||||
updateFlow,
|
updateFlow,
|
||||||
|
uploadFlow,
|
||||||
disableCopyPaste,
|
disableCopyPaste,
|
||||||
addFlow,
|
addFlow,
|
||||||
getNodeId,
|
getNodeId,
|
||||||
|
|
@ -151,6 +152,7 @@ export default function Page({ flow }: { flow: FlowType }) {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[tabId]: {
|
[tabId]: {
|
||||||
|
...prev[tabId],
|
||||||
isPending: true,
|
isPending: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -166,6 +168,7 @@ export default function Page({ flow }: { flow: FlowType }) {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[tabId]: {
|
[tabId]: {
|
||||||
|
...prev[tabId],
|
||||||
isPending: true,
|
isPending: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -217,20 +220,26 @@ export default function Page({ flow }: { flow: FlowType }) {
|
||||||
|
|
||||||
const onDragOver = useCallback((event: React.DragEvent) => {
|
const onDragOver = useCallback((event: React.DragEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
if (event.dataTransfer.types.some((t) => t === "nodedata")) {
|
||||||
event.dataTransfer.dropEffect = "move";
|
event.dataTransfer.dropEffect = "move";
|
||||||
|
} else {
|
||||||
|
event.dataTransfer.dropEffect = "copy";
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onDrop = useCallback(
|
const onDrop = useCallback(
|
||||||
(event: React.DragEvent) => {
|
(event: React.DragEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
if (event.dataTransfer.types.some((t) => t === "nodedata")) {
|
||||||
takeSnapshot();
|
takeSnapshot();
|
||||||
|
|
||||||
// Get the current bounds of the ReactFlow wrapper element
|
// Get the current bounds of the ReactFlow wrapper element
|
||||||
const reactflowBounds = reactFlowWrapper.current.getBoundingClientRect();
|
const reactflowBounds =
|
||||||
|
reactFlowWrapper.current.getBoundingClientRect();
|
||||||
|
|
||||||
// Extract the data from the drag event and parse it as a JSON object
|
// Extract the data from the drag event and parse it as a JSON object
|
||||||
let data: { type: string; node?: APIClassType } = JSON.parse(
|
let data: { type: string; node?: APIClassType } = JSON.parse(
|
||||||
event.dataTransfer.getData("json")
|
event.dataTransfer.getData("nodedata")
|
||||||
);
|
);
|
||||||
|
|
||||||
// If data type is not "chatInput" or if there are no "chatInputNode" nodes present in the ReactFlow instance, create a new node
|
// If data type is not "chatInput" or if there are no "chatInputNode" nodes present in the ReactFlow instance, create a new node
|
||||||
|
|
@ -273,6 +282,10 @@ export default function Page({ flow }: { flow: FlowType }) {
|
||||||
// Add the new node to the list of nodes in state
|
// Add the new node to the list of nodes in state
|
||||||
}
|
}
|
||||||
setNodes((nds) => nds.concat(newNode));
|
setNodes((nds) => nds.concat(newNode));
|
||||||
|
} else if (event.dataTransfer.types.some((t) => t === "Files")) {
|
||||||
|
takeSnapshot();
|
||||||
|
uploadFlow(false, event.dataTransfer.files.item(0));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
// Specify dependencies for useCallback
|
// Specify dependencies for useCallback
|
||||||
[getNodeId, reactFlowInstance, setErrorData, setNodes, takeSnapshot]
|
[getNodeId, reactFlowInstance, setErrorData, setNodes, takeSnapshot]
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ export default function ExtraSidebar() {
|
||||||
crt.classList.add("cursor-grabbing");
|
crt.classList.add("cursor-grabbing");
|
||||||
document.body.appendChild(crt);
|
document.body.appendChild(crt);
|
||||||
event.dataTransfer.setDragImage(crt, 0, 0);
|
event.dataTransfer.setDragImage(crt, 0, 0);
|
||||||
event.dataTransfer.setData("json", JSON.stringify(data));
|
event.dataTransfer.setData("nodedata", JSON.stringify(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSearchInput(e: string) {
|
function handleSearchInput(e: string) {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ export type APIClassType = {
|
||||||
description: string;
|
description: string;
|
||||||
template: APITemplateType;
|
template: APITemplateType;
|
||||||
display_name: string;
|
display_name: string;
|
||||||
|
input_types?: Array<string>;
|
||||||
[key: string]: Array<string> | string | APITemplateType;
|
[key: string]: Array<string> | string | APITemplateType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ export type ParameterComponentType = {
|
||||||
name?: string;
|
name?: string;
|
||||||
tooltipTitle: string;
|
tooltipTitle: string;
|
||||||
dataContext?: typesContextType;
|
dataContext?: typesContextType;
|
||||||
|
optionalHandle?: Array<String>;
|
||||||
};
|
};
|
||||||
export type InputListComponentType = {
|
export type InputListComponentType = {
|
||||||
value: string[];
|
value: string[];
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export type TabsContextType = {
|
||||||
downloadFlow: (flow: FlowType) => void;
|
downloadFlow: (flow: FlowType) => void;
|
||||||
downloadFlows: () => void;
|
downloadFlows: () => void;
|
||||||
uploadFlows: () => void;
|
uploadFlows: () => void;
|
||||||
uploadFlow: (newFlow?: boolean) => void;
|
uploadFlow: (newFlow?: boolean, file?: File) => void;
|
||||||
hardReset: () => void;
|
hardReset: () => void;
|
||||||
//disable CopyPaste
|
//disable CopyPaste
|
||||||
disableCopyPaste: boolean;
|
disableCopyPaste: boolean;
|
||||||
|
|
@ -33,6 +33,6 @@ export type TabsContextType = {
|
||||||
export type TabsState = {
|
export type TabsState = {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
isPending: boolean;
|
isPending: boolean;
|
||||||
formKeysData: {input_keys?: Array<string>, memory_keys?: Array<string>};
|
formKeysData: {input_keys?: Object, memory_keys?: Array<string>, handle_keys?: Array<string>};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import { twMerge } from "tailwind-merge";
|
||||||
import { ADJECTIVES, DESCRIPTIONS, NOUNS } from "./constants";
|
import { ADJECTIVES, DESCRIPTIONS, NOUNS } from "./constants";
|
||||||
import { ComponentType, SVGProps } from "react";
|
import { ComponentType, SVGProps } from "react";
|
||||||
import {
|
import {
|
||||||
|
Compass,
|
||||||
Cpu,
|
Cpu,
|
||||||
Fingerprint,
|
Fingerprint,
|
||||||
Gift,
|
Gift,
|
||||||
|
|
@ -139,6 +140,8 @@ export const nodeColors: { [char: string]: string } = {
|
||||||
toolkits: "#DB2C2C",
|
toolkits: "#DB2C2C",
|
||||||
wrappers: "#E6277A",
|
wrappers: "#E6277A",
|
||||||
utilities: "#31A3CC",
|
utilities: "#31A3CC",
|
||||||
|
output_parsers: "#E6A627",
|
||||||
|
str: "#049524",
|
||||||
unknown: "#9CA3AF",
|
unknown: "#9CA3AF",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -158,6 +161,7 @@ export const nodeNames: { [char: string]: string } = {
|
||||||
wrappers: "Wrappers",
|
wrappers: "Wrappers",
|
||||||
textsplitters: "Text Splitters",
|
textsplitters: "Text Splitters",
|
||||||
utilities: "Utilities",
|
utilities: "Utilities",
|
||||||
|
output_parsers: "Output Parsers",
|
||||||
unknown: "Unknown",
|
unknown: "Unknown",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -363,6 +367,9 @@ export const nodeIconsLucide: {
|
||||||
utilities: Wand2 as React.ForwardRefExoticComponent<
|
utilities: Wand2 as React.ForwardRefExoticComponent<
|
||||||
ComponentType<SVGProps<SVGSVGElement>>
|
ComponentType<SVGProps<SVGSVGElement>>
|
||||||
>,
|
>,
|
||||||
|
output_parsers: Compass as React.ForwardRefExoticComponent<
|
||||||
|
ComponentType<SVGProps<SVGSVGElement>>
|
||||||
|
>,
|
||||||
unknown: HelpCircle as React.ForwardRefExoticComponent<
|
unknown: HelpCircle as React.ForwardRefExoticComponent<
|
||||||
ComponentType<SVGProps<SVGSVGElement>>
|
ComponentType<SVGProps<SVGSVGElement>>
|
||||||
>,
|
>,
|
||||||
|
|
@ -625,11 +632,11 @@ export function isValidConnection(
|
||||||
reactFlowInstance: ReactFlowInstance
|
reactFlowInstance: ReactFlowInstance
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
sourceHandle.split("|")[0] === targetHandle.split("|")[0] ||
|
targetHandle.split("|")[0].split(";").some((n) => n === sourceHandle.split("|")[0]) ||
|
||||||
sourceHandle
|
sourceHandle
|
||||||
.split("|")
|
.split("|")
|
||||||
.slice(2)
|
.slice(2)
|
||||||
.some((t) => t === targetHandle.split("|")[0]) ||
|
.some((t) => targetHandle.split("|")[0].split(";").some((n) => n === t)) ||
|
||||||
targetHandle.split("|")[0] === "str"
|
targetHandle.split("|")[0] === "str"
|
||||||
) {
|
) {
|
||||||
let targetNode = reactFlowInstance.getNode(target).data.node;
|
let targetNode = reactFlowInstance.getNode(target).data.node;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue