remove dead code and unused dependencies (#1488)
* added esLint to frontend project * Remove formModalPropsType from index.ts * Remove chat input component * Delete codeBlock and fileComponent components * Remove unused code for BuildTrigger component * Refactor Chat component and remove unused code * Delete chatTrigger component * Delete unused SVG and PNG files * Remove LightTooltipComponent * Remove LoadingSpinner component * Delete RadialProgressComponent * Delete ReactTooltipComponent * Remove TooltipComponent * Delete ElementStack component * Remove ToggleComponent * Remove unused dependencies from package.json * Update package json * add accordion * change accordion sidebar to shadcn * added million lint * Remove MillionCompiler plugin from Vite config * Refactor authContext autoLogin dependency * Add console.log statements for debugging * Refactored position of elements in IO branch * fix assets imports * fix re-render on pageComponent * Add StrictMode to root render and update key in ExtraSidebarComponent * Add showCanvas state and useEffect to update it to improve performance * Refactor FlowPage component and remove ExtraSidebar from main area * Refactor FlowToolbar to prevent Unnecessary re-render * get node position with zustand in NodeToolbarComponent * Fix ShareModal rendering issue * Remove ExtraSidebar component and fix CodeAreaComponent bug * Refactor: Use useMemo to avoid unnecessary render * merge zustandIo * Remove console.log statements * Update package-lock.json and refactor extraSidebarComponent * update package json * Remove unused msgpack wheel file and add new nvidia_nvjitlink_cu12 wheel file * Fix import formatting in loading.py * Imported missing module and removed unused import --------- Co-authored-by: cristhianzl <cristhian.lousa@gmail.com> Co-authored-by: Lucas Oliveira <lucas.edu.oli@hotmail.com> Co-authored-by: igorrCarvalho <igorsilvabhz6@gmail.com> Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@logspace.ai>
This commit is contained in:
parent
e564991040
commit
e61896bc1f
71 changed files with 3496 additions and 3399 deletions
4
poetry.lock
generated
4
poetry.lock
generated
|
|
@ -4846,7 +4846,6 @@ files = [
|
||||||
{file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"},
|
{file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"},
|
||||||
{file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"},
|
{file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"},
|
||||||
{file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"},
|
{file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"},
|
||||||
{file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"},
|
|
||||||
{file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
|
{file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -5316,6 +5315,7 @@ description = "Nvidia JIT LTO Library"
|
||||||
optional = true
|
optional = true
|
||||||
python-versions = ">=3"
|
python-versions = ">=3"
|
||||||
files = [
|
files = [
|
||||||
|
{file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-manylinux2014_aarch64.whl", hash = "sha256:75d6498c96d9adb9435f2bbdbddb479805ddfb97b5c1b32395c694185c20ca57"},
|
||||||
{file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c6428836d20fe7e327191c175791d38570e10762edc588fb46749217cd444c74"},
|
{file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c6428836d20fe7e327191c175791d38570e10762edc588fb46749217cd444c74"},
|
||||||
{file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-win_amd64.whl", hash = "sha256:991905ffa2144cb603d8ca7962d75c35334ae82bf92820b6ba78157277da1ad2"},
|
{file = "nvidia_nvjitlink_cu12-12.4.99-py3-none-win_amd64.whl", hash = "sha256:991905ffa2144cb603d8ca7962d75c35334ae82bf92820b6ba78157277da1ad2"},
|
||||||
]
|
]
|
||||||
|
|
@ -10240,4 +10240,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.10,<3.12"
|
python-versions = ">=3.10,<3.12"
|
||||||
content-hash = "b66acb0ed04e62c9f311828307ac1503bc7a19912753c217d4ea6237f474543a"
|
content-hash = "014454e08274cc189a4b8b74c6577a8731bb0d9693f40f6f98b4c20bdf70b58d"
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,12 @@ pytube = "^15.0.0"
|
||||||
llama-index = "^0.10.13"
|
llama-index = "^0.10.13"
|
||||||
langchain-openai = "^0.0.5"
|
langchain-openai = "^0.0.5"
|
||||||
unstructured = { extras = ["md"], version = "^0.12.4" }
|
unstructured = { extras = ["md"], version = "^0.12.4" }
|
||||||
|
opentelemetry-api = "^1.23.0"
|
||||||
|
opentelemetry-sdk = "^1.23.0"
|
||||||
|
opentelemetry-exporter-otlp = "^1.23.0"
|
||||||
|
opentelemetry-instrumentation-fastapi = "^0.44b0"
|
||||||
|
opentelemetry-instrumentation-httpx = "^0.44b0"
|
||||||
|
opentelemetry-instrumentation-asgi = "^0.44b0"
|
||||||
dspy-ai = "^2.4.0"
|
dspy-ai = "^2.4.0"
|
||||||
crewai = "^0.22.5"
|
crewai = "^0.22.5"
|
||||||
html2text = "^2024.2.26"
|
html2text = "^2024.2.26"
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ class FileComponent(CustomComponent):
|
||||||
|
|
||||||
def build_config(self) -> Dict[str, Any]:
|
def build_config(self) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
"path": {
|
"paths": {
|
||||||
"display_name": "Path",
|
"display_name": "Paths",
|
||||||
"field_type": "file",
|
"field_type": "file",
|
||||||
"file_types": TEXT_FILE_TYPES,
|
"file_types": TEXT_FILE_TYPES,
|
||||||
"info": f"Supported file types: {', '.join(TEXT_FILE_TYPES)}",
|
"info": f"Supported file types: {', '.join(TEXT_FILE_TYPES)}",
|
||||||
|
|
|
||||||
30
src/backend/base/langflow/components/inputs/JSONInput.py
Normal file
30
src/backend/base/langflow/components/inputs/JSONInput.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import ast
|
||||||
|
import json
|
||||||
|
|
||||||
|
from langflow import CustomComponent
|
||||||
|
from langflow.schema import Record
|
||||||
|
|
||||||
|
|
||||||
|
class JSONInputComponent(CustomComponent):
|
||||||
|
display_name = "JSON Input"
|
||||||
|
description = "Load a JSON object as input."
|
||||||
|
|
||||||
|
def build_config(self):
|
||||||
|
return {
|
||||||
|
"json_str": {
|
||||||
|
"display_name": "JSON String",
|
||||||
|
"multiline": True,
|
||||||
|
"info": "The JSON string to load.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def build(self, json_str: str) -> Record:
|
||||||
|
try:
|
||||||
|
data = json.loads(json_str)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
try:
|
||||||
|
data = ast.literal_eval(json_str)
|
||||||
|
except (SyntaxError, ValueError):
|
||||||
|
raise ValueError("Invalid JSON string.")
|
||||||
|
record = Record(data=data)
|
||||||
|
return record
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import ast
|
import ast
|
||||||
import json
|
import json
|
||||||
from typing import AsyncIterator, Callable, Dict, Iterator, List, Optional, Union
|
from typing import AsyncIterator, Callable, Dict, Iterator, List, Optional, Union
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from langchain_core.messages import AIMessage
|
from langchain_core.messages import AIMessage
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import TYPE_CHECKING, Any, Awaitable, Callable, List, Optional, Tuple, Type, Union, cast
|
from typing import TYPE_CHECKING, Any, Callable, Coroutine, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from pydantic.v1 import BaseModel, Field, create_model
|
from pydantic.v1 import BaseModel, Field, create_model
|
||||||
from sqlmodel import select
|
from sqlmodel import select
|
||||||
|
|
@ -63,30 +63,28 @@ def find_flow(flow_name: str, user_id: str) -> Optional[str]:
|
||||||
|
|
||||||
|
|
||||||
async def run_flow(
|
async def run_flow(
|
||||||
inputs: Optional[Union[dict, List[dict]]] = None,
|
inputs: Union[dict, List[dict]] = None,
|
||||||
tweaks: Optional[dict] = None,
|
tweaks: Optional[dict] = None,
|
||||||
flow_id: Optional[str] = None,
|
flow_id: Optional[str] = None,
|
||||||
flow_name: Optional[str] = None,
|
flow_name: Optional[str] = None,
|
||||||
user_id: Optional[str] = None,
|
user_id: Optional[str] = None,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
if not user_id:
|
|
||||||
raise ValueError("Session is invalid")
|
|
||||||
graph = await load_flow(user_id, flow_id, flow_name, tweaks)
|
graph = await load_flow(user_id, flow_id, flow_name, tweaks)
|
||||||
|
|
||||||
if inputs is None:
|
if inputs is None:
|
||||||
inputs = []
|
inputs = []
|
||||||
inputs_list: list[dict[str, str]] = []
|
inputs_list = []
|
||||||
inputs_components = []
|
inputs_components = []
|
||||||
types = []
|
types = []
|
||||||
for input_dict in inputs:
|
for input_dict in inputs:
|
||||||
inputs_list.append({INPUT_FIELD_NAME: cast(str, input_dict.get("input_value", ""))})
|
inputs_list.append({INPUT_FIELD_NAME: input_dict.get("input_value")})
|
||||||
inputs_components.append(input_dict.get("components", []))
|
inputs_components.append(input_dict.get("components", []))
|
||||||
types.append(input_dict.get("type", []))
|
types.append(input_dict.get("type", []))
|
||||||
|
|
||||||
return await graph.arun(inputs_list, inputs_components=inputs_components, types=types)
|
return await graph.arun(inputs_list, inputs_components=inputs_components, types=types)
|
||||||
|
|
||||||
|
|
||||||
def generate_function_for_flow(inputs: List["Vertex"], flow_id: str) -> Callable[..., Awaitable[Any]]:
|
def generate_function_for_flow(inputs: List["Vertex"], flow_id: str) -> Coroutine:
|
||||||
"""
|
"""
|
||||||
Generate a dynamic flow function based on the given inputs and flow ID.
|
Generate a dynamic flow function based on the given inputs and flow ID.
|
||||||
|
|
||||||
|
|
@ -140,14 +138,12 @@ async def flow_function({func_args}):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
compiled_func = compile(func_body, "<string>", "exec")
|
compiled_func = compile(func_body, "<string>", "exec")
|
||||||
local_scope: dict = {}
|
local_scope = {}
|
||||||
exec(compiled_func, globals(), local_scope)
|
exec(compiled_func, globals(), local_scope)
|
||||||
return local_scope["flow_function"]
|
return local_scope["flow_function"]
|
||||||
|
|
||||||
|
|
||||||
def build_function_and_schema(
|
def build_function_and_schema(flow_record: Record, graph: "Graph") -> Tuple[Callable, BaseModel]:
|
||||||
flow_record: Record, graph: "Graph"
|
|
||||||
) -> Tuple[Callable[..., Awaitable[Any]], Type[BaseModel]]:
|
|
||||||
"""
|
"""
|
||||||
Builds a dynamic function and schema for a given flow.
|
Builds a dynamic function and schema for a given flow.
|
||||||
|
|
||||||
|
|
@ -182,7 +178,7 @@ def get_flow_inputs(graph: "Graph") -> List["Vertex"]:
|
||||||
return inputs
|
return inputs
|
||||||
|
|
||||||
|
|
||||||
def build_schema_from_inputs(name: str, inputs: List["Vertex"]) -> Type[BaseModel]:
|
def build_schema_from_inputs(name: str, inputs: List[tuple[str, str, str]]) -> BaseModel:
|
||||||
"""
|
"""
|
||||||
Builds a schema from the given inputs.
|
Builds a schema from the given inputs.
|
||||||
|
|
||||||
|
|
@ -200,4 +196,4 @@ def build_schema_from_inputs(name: str, inputs: List["Vertex"]) -> Type[BaseMode
|
||||||
field_name = input_.display_name.lower().replace(" ", "_")
|
field_name = input_.display_name.lower().replace(" ", "_")
|
||||||
description = input_.description
|
description = input_.description
|
||||||
fields[field_name] = (str, Field(default="", description=description))
|
fields[field_name] = (str, Field(default="", description=description))
|
||||||
return create_model(name, **fields) # type: ignore
|
return create_model(name, **fields)
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@
|
||||||
"list": false,
|
"list": false,
|
||||||
"show": true,
|
"show": true,
|
||||||
"multiline": true,
|
"multiline": true,
|
||||||
"value": "from langchain_core.prompts import PromptTemplate\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Prompt, TemplateField, Text\n\n\nclass PromptComponent(CustomComponent):\n display_name: str = \"Prompt\"\n description: str = \"A component for creating prompts using templates\"\n icon = \"terminal-square\"\n\n def build_config(self):\n return {\n \"template\": TemplateField(display_name=\"Template\"),\n \"code\": TemplateField(advanced=True),\n }\n\n def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Text:\n from langflow.base.prompts.utils import dict_values_to_string\n\n prompt_template = PromptTemplate.from_template(Text(template))\n kwargs = dict_values_to_string(kwargs)\n kwargs = {k: \"\\n\".join(v) if isinstance(v, list) else v for k, v in kwargs.items()}\n try:\n formated_prompt = prompt_template.format(**kwargs)\n except Exception as exc:\n raise ValueError(f\"Error formatting prompt: {exc}\") from exc\n self.status = f'Prompt:\\n\"{formated_prompt}\"'\n return formated_prompt\n",
|
"value": "from langchain_core.prompts import PromptTemplate\n\nfrom langflow import CustomComponent\nfrom langflow.field_typing import Prompt, TemplateField, Text\n\n\nclass PromptComponent(CustomComponent):\n display_name: str = \"Prompt\"\n description: str = \"A component for creating prompts using templates\"\n icon = \"terminal-square\"\n\n def build_config(self):\n return {\n \"template\": TemplateField(display_name=\"Template\"),\n \"code\": TemplateField(advanced=True),\n }\n\n def build(\n self,\n template: Prompt,\n **kwargs,\n ) -> Text:\n from langflow.base.prompts.utils import dict_values_to_string\n\n prompt_template = PromptTemplate.from_template(Text(template))\n kwargs = dict_values_to_string(kwargs)\n kwargs = {k: \"\\n\".join(v) if isinstance(v, list) else v for k, v in kwargs.items()}\n try:\n formated_prompt = prompt_template.format(**kwargs)\n except Exception as exc:\n raise ValueError(f\"Error formatting prompt: {exc}\") from exc\n self.status = f'Prompt:\\n\"{formated_prompt}\"'\n return formated_prompt\n",
|
||||||
"fileTypes": [],
|
"fileTypes": [],
|
||||||
"file_path": "",
|
"file_path": "",
|
||||||
"password": false,
|
"password": false,
|
||||||
|
|
@ -797,7 +797,7 @@
|
||||||
"list": false,
|
"list": false,
|
||||||
"show": true,
|
"show": true,
|
||||||
"multiline": true,
|
"multiline": true,
|
||||||
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI's models.\"\n icon = \"OpenAI\"\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": False,\n \"required\": False,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n \"required\": False,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"required\": False,\n \"options\": [\n \"gpt-4-turbo-preview\",\n \"gpt-4-0125-preview\",\n \"gpt-4-1106-preview\",\n \"gpt-4-vision-preview\",\n \"gpt-3.5-turbo-0125\",\n \"gpt-3.5-turbo-1106\",\n ],\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": False,\n \"required\": False,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"advanced\": False,\n \"required\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"required\": False,\n \"value\": 0.7,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": \"Stream the response from the model.\",\n },\n }\n\n def build(\n self,\n input_value: Text,\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n model_name: str = \"gpt-4-1106-preview\",\n openai_api_base: Optional[str] = None,\n openai_api_key: Optional[str] = None,\n temperature: float = 0.7,\n stream: bool = False,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=openai_api_key,\n temperature=temperature,\n )\n\n return self.get_result(output=output, stream=stream, input_value=input_value)\n",
|
"value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.components.models.base.model import LCModelComponent\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI's models.\"\n icon = \"OpenAI\"\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": False,\n \"required\": False,\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n \"required\": False,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"required\": False,\n \"options\": [\n \"gpt-4-turbo-preview\",\n \"gpt-4-0125-preview\",\n \"gpt-4-1106-preview\",\n \"gpt-4-vision-preview\",\n \"gpt-3.5-turbo-0125\",\n \"gpt-3.5-turbo-1106\",\n ],\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": False,\n \"required\": False,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"advanced\": False,\n \"required\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"required\": False,\n \"value\": 0.7,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": \"Stream the response from the model.\",\n },\n }\n\n def build(\n self,\n input_value: Text,\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n model_name: str = \"gpt-4-1106-preview\",\n openai_api_base: Optional[str] = None,\n openai_api_key: Optional[str] = None,\n temperature: float = 0.7,\n stream: bool = False,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n output = ChatOpenAI(\n max_tokens=max_tokens,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=openai_api_key,\n temperature=temperature,\n )\n\n return self.get_result(output=output, stream=stream, input_value=input_value)\n",
|
||||||
"fileTypes": [],
|
"fileTypes": [],
|
||||||
"file_path": "",
|
"file_path": "",
|
||||||
"password": false,
|
"password": false,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import inspect
|
||||||
import json
|
import json
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Sequence, Type
|
from typing import TYPE_CHECKING, Any, Callable, Dict, Sequence, Type
|
||||||
|
|
||||||
|
|
||||||
import orjson
|
import orjson
|
||||||
from langchain.agents import agent as agent_module
|
from langchain.agents import agent as agent_module
|
||||||
from langchain.agents.agent import AgentExecutor
|
from langchain.agents.agent import AgentExecutor
|
||||||
|
|
|
||||||
0
src/backend/langflow/base/agents/__init__.py
Normal file
0
src/backend/langflow/base/agents/__init__.py
Normal file
70
src/backend/langflow/base/agents/agent.py
Normal file
70
src/backend/langflow/base/agents/agent.py
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
from typing import List, Union
|
||||||
|
|
||||||
|
from langchain.agents import AgentExecutor, BaseMultiActionAgent, BaseSingleActionAgent
|
||||||
|
|
||||||
|
from langflow import CustomComponent
|
||||||
|
from langflow.field_typing import BaseMemory, Text, Tool
|
||||||
|
|
||||||
|
|
||||||
|
class LCAgentComponent(CustomComponent):
|
||||||
|
def build_config(self):
|
||||||
|
return {
|
||||||
|
"lc": {
|
||||||
|
"display_name": "LangChain",
|
||||||
|
"info": "The LangChain to interact with.",
|
||||||
|
},
|
||||||
|
"handle_parsing_errors": {
|
||||||
|
"display_name": "Handle Parsing Errors",
|
||||||
|
"info": "If True, the agent will handle parsing errors. If False, the agent will raise an error.",
|
||||||
|
"advanced": True,
|
||||||
|
},
|
||||||
|
"output_key": {
|
||||||
|
"display_name": "Output Key",
|
||||||
|
"info": "The key to use to get the output from the agent.",
|
||||||
|
"advanced": True,
|
||||||
|
},
|
||||||
|
"memory": {
|
||||||
|
"display_name": "Memory",
|
||||||
|
"info": "Memory to use for the agent.",
|
||||||
|
},
|
||||||
|
"tools": {
|
||||||
|
"display_name": "Tools",
|
||||||
|
"info": "Tools the agent can use.",
|
||||||
|
},
|
||||||
|
"input_value": {
|
||||||
|
"display_name": "Input",
|
||||||
|
"info": "Input text to pass to the agent.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
async def run_agent(
|
||||||
|
self,
|
||||||
|
agent: Union[BaseSingleActionAgent, BaseMultiActionAgent, AgentExecutor],
|
||||||
|
inputs: str,
|
||||||
|
input_variables: list[str],
|
||||||
|
tools: List[Tool],
|
||||||
|
memory: BaseMemory = None,
|
||||||
|
handle_parsing_errors: bool = True,
|
||||||
|
output_key: str = "output",
|
||||||
|
) -> Text:
|
||||||
|
if isinstance(agent, AgentExecutor):
|
||||||
|
runnable = agent
|
||||||
|
else:
|
||||||
|
runnable = AgentExecutor.from_agent_and_tools(
|
||||||
|
agent=agent, tools=tools, verbose=True, memory=memory, handle_parsing_errors=handle_parsing_errors
|
||||||
|
)
|
||||||
|
input_dict = {"input": inputs}
|
||||||
|
for var in input_variables:
|
||||||
|
if var not in ["agent_scratchpad", "input"]:
|
||||||
|
input_dict[var] = ""
|
||||||
|
result = await runnable.ainvoke(input_dict)
|
||||||
|
self.status = result
|
||||||
|
if output_key in result:
|
||||||
|
return result.get(output_key)
|
||||||
|
elif "output" not in result:
|
||||||
|
if output_key != "output":
|
||||||
|
raise ValueError(f"Output key not found in result. Tried '{output_key}' and 'output'.")
|
||||||
|
else:
|
||||||
|
raise ValueError("Output key not found in result. Tried 'output'.")
|
||||||
|
|
||||||
|
return result.get("output")
|
||||||
3
src/backend/langflow/base/models/__init__.py
Normal file
3
src/backend/langflow/base/models/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from .model import LCModelComponent
|
||||||
|
|
||||||
|
__all__ = ["LCModelComponent"]
|
||||||
48
src/backend/langflow/base/models/model.py
Normal file
48
src/backend/langflow/base/models/model.py
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from langchain_core.language_models.chat_models import BaseChatModel
|
||||||
|
from langchain_core.language_models.llms import LLM
|
||||||
|
from langchain_core.messages import HumanMessage, SystemMessage
|
||||||
|
|
||||||
|
from langflow import CustomComponent
|
||||||
|
|
||||||
|
|
||||||
|
class LCModelComponent(CustomComponent):
|
||||||
|
display_name: str = "Model Name"
|
||||||
|
description: str = "Model Description"
|
||||||
|
|
||||||
|
def get_result(self, runnable: LLM, stream: bool, input_value: str):
|
||||||
|
"""
|
||||||
|
Retrieves the result from the output of a Runnable object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output (Runnable): The output object to retrieve the result from.
|
||||||
|
stream (bool): Indicates whether to use streaming or invocation mode.
|
||||||
|
input_value (str): The input value to pass to the output object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The result obtained from the output object.
|
||||||
|
"""
|
||||||
|
if stream:
|
||||||
|
result = runnable.stream(input_value)
|
||||||
|
else:
|
||||||
|
message = runnable.invoke(input_value)
|
||||||
|
result = message.content if hasattr(message, "content") else message
|
||||||
|
self.status = result
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_chat_result(
|
||||||
|
self, runnable: BaseChatModel, stream: bool, input_value: str, system_message: Optional[str] = None
|
||||||
|
):
|
||||||
|
messages = []
|
||||||
|
if input_value:
|
||||||
|
messages.append(HumanMessage(input_value))
|
||||||
|
if system_message:
|
||||||
|
messages.append(SystemMessage(system_message))
|
||||||
|
if stream:
|
||||||
|
result = runnable.stream(messages)
|
||||||
|
else:
|
||||||
|
message = runnable.invoke(messages)
|
||||||
|
result = message.content
|
||||||
|
self.status = result
|
||||||
|
return result
|
||||||
0
src/backend/langflow/components/models/base/__init__.py
Normal file
0
src/backend/langflow/components/models/base/__init__.py
Normal file
37
src/backend/langflow/components/tools/SearchAPITool.py
Normal file
37
src/backend/langflow/components/tools/SearchAPITool.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
from langchain_community.tools.searchapi import SearchAPIRun
|
||||||
|
from langchain_community.utilities.searchapi import SearchApiAPIWrapper
|
||||||
|
|
||||||
|
from langflow import CustomComponent
|
||||||
|
from langflow.field_typing import Tool
|
||||||
|
|
||||||
|
|
||||||
|
class SearchApiToolComponent(CustomComponent):
|
||||||
|
display_name: str = "SearchApi Tool"
|
||||||
|
description: str = "Real-time search engine results API."
|
||||||
|
documentation: str = "https://www.searchapi.io/docs/google"
|
||||||
|
field_config = {
|
||||||
|
"engine": {
|
||||||
|
"display_name": "Engine",
|
||||||
|
"field_type": "str",
|
||||||
|
"info": "The search engine to use.",
|
||||||
|
},
|
||||||
|
"api_key": {
|
||||||
|
"display_name": "API Key",
|
||||||
|
"field_type": "str",
|
||||||
|
"required": True,
|
||||||
|
"password": True,
|
||||||
|
"info": "The API key to use SearchApi.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def build(
|
||||||
|
self,
|
||||||
|
engine: str,
|
||||||
|
api_key: str,
|
||||||
|
) -> Tool:
|
||||||
|
search_api_wrapper = SearchApiAPIWrapper(engine=engine, searchapi_api_key=api_key)
|
||||||
|
|
||||||
|
tool = SearchAPIRun(api_wrapper=search_api_wrapper)
|
||||||
|
|
||||||
|
self.status = tool
|
||||||
|
return tool
|
||||||
0
src/backend/langflow/components/tools/SearchApi.py
Normal file
0
src/backend/langflow/components/tools/SearchApi.py
Normal file
103
src/backend/langflow/services/deps.py
Normal file
103
src/backend/langflow/services/deps.py
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from typing import TYPE_CHECKING, Generator
|
||||||
|
|
||||||
|
from langflow.services import ServiceType, service_manager
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from sqlmodel import Session
|
||||||
|
|
||||||
|
from langflow.services.cache.service import CacheService
|
||||||
|
from langflow.services.chat.service import ChatService
|
||||||
|
from langflow.services.credentials.service import CredentialService
|
||||||
|
from langflow.services.database.service import DatabaseService
|
||||||
|
from langflow.services.monitor.service import MonitorService
|
||||||
|
from langflow.services.plugins.service import PluginService
|
||||||
|
from langflow.services.session.service import SessionService
|
||||||
|
from langflow.services.settings.service import SettingsService
|
||||||
|
from langflow.services.socket.service import SocketIOService
|
||||||
|
from langflow.services.storage.service import StorageService
|
||||||
|
from langflow.services.store.service import StoreService
|
||||||
|
from langflow.services.task.service import TaskService
|
||||||
|
|
||||||
|
|
||||||
|
def get_socket_service() -> "SocketIOService":
|
||||||
|
return service_manager.get(ServiceType.SOCKETIO_SERVICE) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def get_storage_service() -> "StorageService":
|
||||||
|
return service_manager.get(ServiceType.STORAGE_SERVICE) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def get_credential_service() -> "CredentialService":
|
||||||
|
return service_manager.get(ServiceType.CREDENTIAL_SERVICE) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugins_service() -> "PluginService":
|
||||||
|
return service_manager.get(ServiceType.PLUGIN_SERVICE) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def get_settings_service() -> "SettingsService":
|
||||||
|
try:
|
||||||
|
return service_manager.get(ServiceType.SETTINGS_SERVICE) # type: ignore
|
||||||
|
except ValueError:
|
||||||
|
# initialize settings service
|
||||||
|
from langflow.services.manager import initialize_settings_service
|
||||||
|
|
||||||
|
initialize_settings_service()
|
||||||
|
return service_manager.get(ServiceType.SETTINGS_SERVICE) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def get_db_service() -> "DatabaseService":
|
||||||
|
return service_manager.get(ServiceType.DATABASE_SERVICE) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def get_session() -> Generator["Session", None, None]:
|
||||||
|
db_service = get_db_service()
|
||||||
|
yield from db_service.get_session()
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def session_scope():
|
||||||
|
"""
|
||||||
|
Context manager for managing a session scope.
|
||||||
|
|
||||||
|
Yields:
|
||||||
|
session: The session object.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: If an error occurs during the session scope.
|
||||||
|
|
||||||
|
"""
|
||||||
|
session = next(get_session())
|
||||||
|
try:
|
||||||
|
yield session
|
||||||
|
session.commit()
|
||||||
|
except:
|
||||||
|
session.rollback()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
|
||||||
|
def get_cache_service() -> "CacheService":
|
||||||
|
return service_manager.get(ServiceType.CACHE_SERVICE) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def get_session_service() -> "SessionService":
|
||||||
|
return service_manager.get(ServiceType.SESSION_SERVICE) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def get_monitor_service() -> "MonitorService":
|
||||||
|
return service_manager.get(ServiceType.MONITOR_SERVICE) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def get_task_service() -> "TaskService":
|
||||||
|
return service_manager.get(ServiceType.TASK_SERVICE) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def get_chat_service() -> "ChatService":
|
||||||
|
return service_manager.get(ServiceType.CHAT_SERVICE) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def get_store_service() -> "StoreService":
|
||||||
|
return service_manager.get(ServiceType.STORE_SERVICE) # type: ignore
|
||||||
83
src/backend/langflow/services/factory.py
Normal file
83
src/backend/langflow/services/factory.py
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
import importlib
|
||||||
|
import inspect
|
||||||
|
from typing import TYPE_CHECKING, Type, get_type_hints
|
||||||
|
|
||||||
|
from cachetools import LRUCache, cached
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from langflow.services.schema import ServiceType
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from langflow.services.base import Service
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceFactory:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
service_class,
|
||||||
|
):
|
||||||
|
self.service_class = service_class
|
||||||
|
self.dependencies = infer_service_types(self, import_all_services_into_a_dict())
|
||||||
|
|
||||||
|
def create(self, *args, **kwargs) -> "Service":
|
||||||
|
raise self.service_class(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def hash_factory(factory: ServiceFactory) -> str:
|
||||||
|
return factory.service_class.__name__
|
||||||
|
|
||||||
|
|
||||||
|
def hash_dict(d: dict) -> str:
|
||||||
|
return str(d)
|
||||||
|
|
||||||
|
|
||||||
|
def hash_infer_service_types_args(factory_class: Type[ServiceFactory], available_services=None) -> str:
|
||||||
|
factory_hash = hash_factory(factory_class)
|
||||||
|
services_hash = hash_dict(available_services)
|
||||||
|
return f"{factory_hash}_{services_hash}"
|
||||||
|
|
||||||
|
|
||||||
|
@cached(cache=LRUCache(maxsize=10), key=hash_infer_service_types_args)
|
||||||
|
def infer_service_types(factory_class: Type[ServiceFactory], available_services=None) -> "ServiceType":
|
||||||
|
create_method = factory_class.create
|
||||||
|
type_hints = get_type_hints(create_method, globalns=available_services)
|
||||||
|
service_types = []
|
||||||
|
for param_name, param_type in type_hints.items():
|
||||||
|
# Skip the return type if it's included in type hints
|
||||||
|
if param_name == "return":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Convert the type to the expected enum format directly without appending "_SERVICE"
|
||||||
|
type_name = param_type.__name__.upper().replace("SERVICE", "_SERVICE")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Attempt to find a matching enum value
|
||||||
|
service_type = ServiceType[type_name]
|
||||||
|
service_types.append(service_type)
|
||||||
|
except KeyError:
|
||||||
|
raise ValueError(f"No matching ServiceType for parameter type: {param_type.__name__}")
|
||||||
|
return service_types
|
||||||
|
|
||||||
|
|
||||||
|
@cached(cache=LRUCache(maxsize=1))
|
||||||
|
def import_all_services_into_a_dict():
|
||||||
|
# Services are all in langflow.services.{service_name}.service
|
||||||
|
# and are subclass of Service
|
||||||
|
# We want to import all of them and put them in a dict
|
||||||
|
# to use as globals
|
||||||
|
from langflow.services.base import Service
|
||||||
|
|
||||||
|
services = {}
|
||||||
|
for service_type in ServiceType:
|
||||||
|
try:
|
||||||
|
service_name = ServiceType(service_type).value.replace("_service", "")
|
||||||
|
module_name = f"langflow.services.{service_name}.service"
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
for name, obj in inspect.getmembers(module, inspect.isclass):
|
||||||
|
if issubclass(obj, Service) and obj is not Service:
|
||||||
|
services[name] = obj
|
||||||
|
break
|
||||||
|
except Exception as exc:
|
||||||
|
logger.exception(exc)
|
||||||
|
raise RuntimeError("Could not initialize services. Please check your settings.") from exc
|
||||||
|
return services
|
||||||
32
src/frontend/.eslintrc.json
Normal file
32
src/frontend/.eslintrc.json
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:node/recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2018
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-console": "warn",
|
||||||
|
"no-self-assign": "warn",
|
||||||
|
"no-self-compare":"warn",
|
||||||
|
"complexity": ["error", { "max": 15 }],
|
||||||
|
"indent": ["error", 2, { "SwitchCase": 1 }],
|
||||||
|
"no-dupe-keys": "error",
|
||||||
|
"no-invalid-regexp": "error",
|
||||||
|
"no-undef": "error",
|
||||||
|
"no-return-assign": "error",
|
||||||
|
"no-redeclare": "error",
|
||||||
|
"no-empty": "error",
|
||||||
|
"no-await-in-loop": "error",
|
||||||
|
"node/exports-style": ["error", "module.exports"],
|
||||||
|
"node/file-extension-in-import": ["error", "always"],
|
||||||
|
"node/prefer-global/buffer": ["error", "always"],
|
||||||
|
"node/prefer-global/console": ["error", "always"],
|
||||||
|
"node/prefer-global/process": ["error", "always"],
|
||||||
|
"node/prefer-global/url-search-params": ["error", "always"],
|
||||||
|
"node/prefer-global/url": ["error", "always"],
|
||||||
|
"node/prefer-promises/dns": "error",
|
||||||
|
"node/prefer-promises/fs": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
4010
src/frontend/package-lock.json
generated
4010
src/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,12 +3,8 @@
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.1",
|
|
||||||
"@emotion/styled": "^11.11.0",
|
|
||||||
"@headlessui/react": "^1.7.17",
|
"@headlessui/react": "^1.7.17",
|
||||||
"@heroicons/react": "^2.0.18",
|
"@million/lint": "^0.0.73",
|
||||||
"@mui/material": "^5.14.7",
|
|
||||||
"@preact/signals-react": "^2.0.0",
|
|
||||||
"@radix-ui/react-accordion": "^1.1.2",
|
"@radix-ui/react-accordion": "^1.1.2",
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.0.4",
|
||||||
"@radix-ui/react-dialog": "^1.0.4",
|
"@radix-ui/react-dialog": "^1.0.4",
|
||||||
|
|
@ -29,9 +25,7 @@
|
||||||
"@tailwindcss/forms": "^0.5.6",
|
"@tailwindcss/forms": "^0.5.6",
|
||||||
"@tailwindcss/line-clamp": "^0.4.4",
|
"@tailwindcss/line-clamp": "^0.4.4",
|
||||||
"@types/axios": "^0.14.0",
|
"@types/axios": "^0.14.0",
|
||||||
"accordion": "^3.0.2",
|
|
||||||
"ace-builds": "^1.24.1",
|
"ace-builds": "^1.24.1",
|
||||||
"add": "^2.0.6",
|
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
|
|
@ -43,6 +37,7 @@
|
||||||
"framer-motion": "^11.0.6",
|
"framer-motion": "^11.0.6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.331.0",
|
"lucide-react": "^0.331.0",
|
||||||
|
"million": "^3.0.6",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"playwright": "^1.42.0",
|
"playwright": "^1.42.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
|
@ -55,8 +50,6 @@
|
||||||
"react-markdown": "^8.0.7",
|
"react-markdown": "^8.0.7",
|
||||||
"react-router-dom": "^6.15.0",
|
"react-router-dom": "^6.15.0",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
"react-tabs": "^6.0.2",
|
|
||||||
"react-tooltip": "^5.21.1",
|
|
||||||
"react18-json-view": "^0.2.3",
|
"react18-json-view": "^0.2.3",
|
||||||
"reactflow": "^11.9.2",
|
"reactflow": "^11.9.2",
|
||||||
"rehype-mathjax": "^4.0.3",
|
"rehype-mathjax": "^4.0.3",
|
||||||
|
|
@ -64,8 +57,6 @@
|
||||||
"remark-math": "^5.1.1",
|
"remark-math": "^5.1.1",
|
||||||
"shadcn-ui": "^0.2.3",
|
"shadcn-ui": "^0.2.3",
|
||||||
"short-unique-id": "^4.4.4",
|
"short-unique-id": "^4.4.4",
|
||||||
"switch": "^0.0.0",
|
|
||||||
"table": "^6.8.1",
|
|
||||||
"tailwind-merge": "^1.14.0",
|
"tailwind-merge": "^1.14.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
|
|
@ -117,6 +108,8 @@
|
||||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||||
"autoprefixer": "^10.4.15",
|
"autoprefixer": "^10.4.15",
|
||||||
"daisyui": "^4.0.4",
|
"daisyui": "^4.0.4",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"postcss": "^8.4.29",
|
"postcss": "^8.4.29",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"prettier-plugin-organize-imports": "^3.2.3",
|
"prettier-plugin-organize-imports": "^3.2.3",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
|
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
|
||||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
|
|
@ -293,7 +293,6 @@ export default function GenericNode({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSpecificClassFromBuildStatus = (
|
const getSpecificClassFromBuildStatus = (
|
||||||
buildStatus: BuildStatus | undefined,
|
buildStatus: BuildStatus | undefined,
|
||||||
validationStatus: validationStatusType | null
|
validationStatus: validationStatusType | null
|
||||||
|
|
@ -342,32 +341,37 @@ export default function GenericNode({
|
||||||
const getNodeSizeClass = (showNode) =>
|
const getNodeSizeClass = (showNode) =>
|
||||||
showNode ? "w-96 rounded-lg" : "w-26 h-26 rounded-full";
|
showNode ? "w-96 rounded-lg" : "w-26 h-26 rounded-full";
|
||||||
|
|
||||||
return (
|
const memoizedNodeToolbarComponent = useMemo(() => {
|
||||||
<>
|
return (
|
||||||
<NodeToolbar>
|
<NodeToolbar>
|
||||||
<NodeToolbarComponent
|
<NodeToolbarComponent
|
||||||
position={{ x: xPos, y: yPos }}
|
data={data}
|
||||||
data={data}
|
deleteNode={(id) => {
|
||||||
deleteNode={(id) => {
|
takeSnapshot();
|
||||||
takeSnapshot();
|
deleteNode(id);
|
||||||
deleteNode(id);
|
}}
|
||||||
}}
|
setShowNode={(show) => {
|
||||||
setShowNode={(show: boolean) => {
|
setNode(data.id, (old) => ({
|
||||||
setNode(data.id, (old) => ({
|
...old,
|
||||||
...old,
|
data: { ...old.data, showNode: show },
|
||||||
data: { ...old.data, showNode: show },
|
}));
|
||||||
}));
|
}}
|
||||||
}}
|
setShowState={setShowNode}
|
||||||
setShowState={setShowNode}
|
numberOfHandles={handles}
|
||||||
numberOfHandles={handles}
|
showNode={showNode}
|
||||||
showNode={showNode}
|
openAdvancedModal={false}
|
||||||
openAdvancedModal={false}
|
onCloseAdvancedModal={() => {}}
|
||||||
onCloseAdvancedModal={() => {}}
|
updateNodeCode={updateNodeCode}
|
||||||
updateNodeCode={updateNodeCode}
|
isOutdated={isOutdated}
|
||||||
isOutdated={isOutdated}
|
selected={selected}
|
||||||
selected={selected}
|
/>
|
||||||
></NodeToolbarComponent>
|
|
||||||
</NodeToolbar>
|
</NodeToolbar>
|
||||||
|
)
|
||||||
|
}, [data, deleteNode, takeSnapshot, setNode, setShowNode, handles, showNode, updateNodeCode, isOutdated, selected]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{memoizedNodeToolbarComponent}
|
||||||
<div
|
<div
|
||||||
className={getNodeBorderClassName(
|
className={getNodeBorderClassName(
|
||||||
selected,
|
selected,
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="271px" height="271px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
|
||||||
<defs>
|
|
||||||
<filter id="ldio-978hsxudfzl-filter" x="-100%" y="-100%" width="300%" height="300%" color-interpolation-filters="sRGB">
|
|
||||||
<feGaussianBlur in="SourceGraphic" stdDeviation="3.6"></feGaussianBlur>
|
|
||||||
<feComponentTransfer result="cutoff">
|
|
||||||
<feFuncA type="table" tableValues="0 0 0 0 0 0 1 1 1 1 1"></feFuncA>
|
|
||||||
</feComponentTransfer>
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
<g filter="url(#ldio-978hsxudfzl-filter)"><g transform="translate(50 50)">
|
|
||||||
<g>
|
|
||||||
<circle cx="8" cy="0" r="5" fill="#2edbb5">
|
|
||||||
<animate attributeName="r" keyTimes="0;0.5;1" values="5.3999999999999995;12.6;5.3999999999999995" dur="5s" repeatCount="indefinite" begin="-0.2s"></animate>
|
|
||||||
</circle>
|
|
||||||
<animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="5s" repeatCount="indefinite" begin="0s"></animateTransform>
|
|
||||||
</g>
|
|
||||||
</g><g transform="translate(50 50)">
|
|
||||||
<g>
|
|
||||||
<circle cx="8" cy="0" r="5" fill="#1d99ff">
|
|
||||||
<animate attributeName="r" keyTimes="0;0.5;1" values="5.3999999999999995;12.6;5.3999999999999995" dur="2.5s" repeatCount="indefinite" begin="-0.15000000000000002s"></animate>
|
|
||||||
</circle>
|
|
||||||
<animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="2.5s" repeatCount="indefinite" begin="-0.05s"></animateTransform>
|
|
||||||
</g>
|
|
||||||
</g><g transform="translate(50 50)">
|
|
||||||
<g>
|
|
||||||
<circle cx="8" cy="0" r="5" fill="#4f41ff">
|
|
||||||
<animate attributeName="r" keyTimes="0;0.5;1" values="5.3999999999999995;12.6;5.3999999999999995" dur="1.6666666666666665s" repeatCount="indefinite" begin="-0.1s"></animate>
|
|
||||||
</circle>
|
|
||||||
<animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="1.6666666666666665s" repeatCount="indefinite" begin="-0.1s"></animateTransform>
|
|
||||||
</g>
|
|
||||||
</g><g transform="translate(50 50)">
|
|
||||||
<g>
|
|
||||||
<circle cx="8" cy="0" r="5" fill="#8400ff">
|
|
||||||
<animate attributeName="r" keyTimes="0;0.5;1" values="5.3999999999999995;12.6;5.3999999999999995" dur="1.25s" repeatCount="indefinite" begin="-0.05s"></animate>
|
|
||||||
</circle>
|
|
||||||
<animateTransform attributeName="transform" type="rotate" keyTimes="0;1" values="0;360" dur="1.25s" repeatCount="indefinite" begin="-0.15000000000000002s"></animateTransform>
|
|
||||||
</g>
|
|
||||||
</g></g>
|
|
||||||
<!-- [ldio] generated by https://loading.io/ --></svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
|
|
@ -12,6 +12,7 @@ export default function AccordionComponent({
|
||||||
children,
|
children,
|
||||||
open = [],
|
open = [],
|
||||||
keyValue,
|
keyValue,
|
||||||
|
sideBar,
|
||||||
}: AccordionComponentType): JSX.Element {
|
}: AccordionComponentType): JSX.Element {
|
||||||
const [value, setValue] = useState(
|
const [value, setValue] = useState(
|
||||||
open.length === 0 ? "" : getOpenAccordion()
|
open.length === 0 ? "" : getOpenAccordion()
|
||||||
|
|
@ -45,7 +46,9 @@ export default function AccordionComponent({
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleClick();
|
handleClick();
|
||||||
}}
|
}}
|
||||||
className="ml-3"
|
className={
|
||||||
|
sideBar ? "w-full bg-muted px-[0.75rem] py-[0.5rem]" : "ml-3"
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{trigger}
|
{trigger}
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
|
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
import { cloneDeep } from "lodash";
|
|
||||||
import useFlowStore from "../../stores/flowStore";
|
|
||||||
import { IOInputProps } from "../../types/components";
|
|
||||||
import IOFileInput from "../IOInputs/FileInput";
|
|
||||||
import { Textarea } from "../ui/textarea";
|
|
||||||
|
|
||||||
export default function IOInputField({
|
|
||||||
inputType,
|
|
||||||
inputId,
|
|
||||||
left,
|
|
||||||
}: IOInputProps): JSX.Element | undefined {
|
|
||||||
const nodes = useFlowStore((state) => state.nodes);
|
|
||||||
const setNode = useFlowStore((state) => state.setNode);
|
|
||||||
const node = nodes.find((node) => node.id === inputId);
|
|
||||||
function handleInputType() {
|
|
||||||
if (!node) return <>"No node found!"</>;
|
|
||||||
switch (inputType) {
|
|
||||||
case "TextInput":
|
|
||||||
return (
|
|
||||||
<Textarea
|
|
||||||
className={`w-full ${left ? "" : " h-full"}`}
|
|
||||||
placeholder={"Enter text..."}
|
|
||||||
value={node.data.node!.template["input_value"].value}
|
|
||||||
onChange={(e) => {
|
|
||||||
e.target.value;
|
|
||||||
if (node) {
|
|
||||||
let newNode = cloneDeep(node);
|
|
||||||
newNode.data.node!.template["input_value"].value =
|
|
||||||
e.target.value;
|
|
||||||
setNode(node.id, newNode);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case "FileLoader":
|
|
||||||
return (
|
|
||||||
<IOFileInput
|
|
||||||
field={node.data.node!.template["file_path"]["value"]}
|
|
||||||
updateValue={(e) => {
|
|
||||||
if (node) {
|
|
||||||
let newNode = cloneDeep(node);
|
|
||||||
newNode.data.node!.template["file_path"].value = e;
|
|
||||||
setNode(node.id, newNode);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<Textarea
|
|
||||||
className="w-full custom-scroll"
|
|
||||||
placeholder={"Enter text..."}
|
|
||||||
value={node.data.node!.template["input_value"]}
|
|
||||||
onChange={(e) => {
|
|
||||||
e.target.value;
|
|
||||||
if (node) {
|
|
||||||
let newNode = cloneDeep(node);
|
|
||||||
newNode.data.node!.template["input_value"].value =
|
|
||||||
e.target.value;
|
|
||||||
setNode(node.id, newNode);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return handleInputType();
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
import useFlowStore from "../../stores/flowStore";
|
|
||||||
import { IOOutputProps } from "../../types/components";
|
|
||||||
import { Textarea } from "../ui/textarea";
|
|
||||||
|
|
||||||
export default function IOOutputView({
|
|
||||||
outputType,
|
|
||||||
outputId,
|
|
||||||
left,
|
|
||||||
}: IOOutputProps): JSX.Element | undefined {
|
|
||||||
const nodes = useFlowStore((state) => state.nodes);
|
|
||||||
const setNode = useFlowStore((state) => state.setNode);
|
|
||||||
const flowPool = useFlowStore((state) => state.flowPool);
|
|
||||||
const node = nodes.find((node) => node.id === outputId);
|
|
||||||
function handleOutputType() {
|
|
||||||
if (!node) return <>"No node found!"</>;
|
|
||||||
switch (outputType) {
|
|
||||||
case "TextOutput":
|
|
||||||
return (
|
|
||||||
<Textarea
|
|
||||||
className={`w-full custom-scroll ${left ? "" : " h-full"}`}
|
|
||||||
placeholder={"Empty"}
|
|
||||||
// update to real value on flowPool
|
|
||||||
value={
|
|
||||||
(flowPool[node.id] ?? [])[(flowPool[node.id]?.length ?? 1) - 1]
|
|
||||||
?.params ?? ""
|
|
||||||
}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<Textarea
|
|
||||||
className={`w-full custom-scroll ${left ? "" : " h-full"}`}
|
|
||||||
placeholder={"Empty"}
|
|
||||||
// update to real value on flowPool
|
|
||||||
value={
|
|
||||||
(flowPool[node.id] ?? [])[(flowPool[node.id]?.length ?? 1) - 1]
|
|
||||||
?.params ?? ""
|
|
||||||
}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return handleOutputType();
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
import Tooltip, { TooltipProps, tooltipClasses } from "@mui/material/Tooltip";
|
|
||||||
import { styled } from "@mui/material/styles";
|
|
||||||
|
|
||||||
export const LightTooltip = styled(({ className, ...props }: TooltipProps) => (
|
|
||||||
<Tooltip {...props} classes={{ popper: className }} />
|
|
||||||
))(({ theme }) => ({
|
|
||||||
[`& .${tooltipClasses.tooltip}`]: {
|
|
||||||
backgroundColor: theme.palette.common.white,
|
|
||||||
color: "rgba(0, 0, 0, 0.87)",
|
|
||||||
boxShadow: theme.shadows[2],
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
|
||||||
[`& .${tooltipClasses.arrow}:before`]: {
|
|
||||||
color: theme.palette.common.white,
|
|
||||||
boxShadow: theme.shadows[1],
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
export default function LoadingSpinner({}) {
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
|
||||||
import { cn } from "../../utils/utils";
|
|
||||||
import IconComponent from "../genericIconComponent";
|
|
||||||
import { Card, CardContent } from "../ui/card";
|
|
||||||
|
|
||||||
export default function NewFlowCardComponent({}: {}) {
|
|
||||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
className={cn(
|
|
||||||
"group relative flex h-48 w-2/6 flex-col justify-between overflow-hidden transition-all hover:shadow-md"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<CardContent className="flex h-full w-full items-center justify-center align-middle">
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
addFlow(true).then((id) => {
|
|
||||||
navigate("/flow/" + id);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<IconComponent
|
|
||||||
className={cn("h-12 w-12 text-muted-foreground")}
|
|
||||||
name="PlusCircle"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import { RadialProgressType } from "../../types/components";
|
|
||||||
|
|
||||||
export default function RadialProgressComponent({
|
|
||||||
value,
|
|
||||||
color,
|
|
||||||
}: RadialProgressType): JSX.Element {
|
|
||||||
const style = {
|
|
||||||
"--value": value! * 100,
|
|
||||||
"--size": "1.5rem",
|
|
||||||
"--thickness": "2px",
|
|
||||||
} as React.CSSProperties;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={"radial-progress " + color} style={style}>
|
|
||||||
<strong className="text-[8px]">{Math.trunc(value! * 100)}%</strong>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
"use client";
|
|
||||||
import type { FC } from "react";
|
|
||||||
import React from "react";
|
|
||||||
import { Tooltip as ReactTooltip } from "react-tooltip";
|
|
||||||
import "react-tooltip/dist/react-tooltip.css";
|
|
||||||
import { TooltipProps } from "../../types/components";
|
|
||||||
import { classNames } from "../../utils/utils";
|
|
||||||
|
|
||||||
const TooltipReact: FC<TooltipProps> = ({
|
|
||||||
selector,
|
|
||||||
content,
|
|
||||||
disabled,
|
|
||||||
position = "top",
|
|
||||||
children,
|
|
||||||
htmlContent,
|
|
||||||
className,
|
|
||||||
clickable,
|
|
||||||
delayShow,
|
|
||||||
}: TooltipProps): JSX.Element => {
|
|
||||||
return (
|
|
||||||
<div className="tooltip-container">
|
|
||||||
{React.cloneElement(children as React.ReactElement, {
|
|
||||||
"data-tooltip-id": selector,
|
|
||||||
})}
|
|
||||||
<ReactTooltip
|
|
||||||
id={selector}
|
|
||||||
content={content}
|
|
||||||
className={classNames(
|
|
||||||
"z-[9999] !bg-white !text-xs !font-normal !text-foreground !opacity-100 !shadow-md",
|
|
||||||
className!
|
|
||||||
)}
|
|
||||||
place={position}
|
|
||||||
clickable={clickable}
|
|
||||||
isOpen={disabled ? false : undefined}
|
|
||||||
delayShow={delayShow}
|
|
||||||
positionStrategy="absolute"
|
|
||||||
float={true}
|
|
||||||
>
|
|
||||||
{htmlContent && htmlContent}
|
|
||||||
</ReactTooltip>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TooltipReact;
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import { TooltipComponentType } from "../../types/components";
|
|
||||||
import { LightTooltip } from "../LightTooltipComponent";
|
|
||||||
|
|
||||||
export default function Tooltip({
|
|
||||||
children,
|
|
||||||
title,
|
|
||||||
placement,
|
|
||||||
}: TooltipComponentType): JSX.Element {
|
|
||||||
return (
|
|
||||||
<LightTooltip placement={placement} title={title} arrow>
|
|
||||||
{children}
|
|
||||||
</LightTooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
import { Transition } from "@headlessui/react";
|
|
||||||
import { useState } from "react";
|
|
||||||
import Loading from "../../../components/ui/loading";
|
|
||||||
import { FlowType } from "../../../types/flow";
|
|
||||||
|
|
||||||
import { MISSED_ERROR_ALERT } from "../../../constants/alerts_constants";
|
|
||||||
import { BuildStatus } from "../../../constants/enums";
|
|
||||||
import useAlertStore from "../../../stores/alertStore";
|
|
||||||
import useFlowStore from "../../../stores/flowStore";
|
|
||||||
import { validateNodes } from "../../../utils/reactflowUtils";
|
|
||||||
import RadialProgressComponent from "../../RadialProgress";
|
|
||||||
import IconComponent from "../../genericIconComponent";
|
|
||||||
|
|
||||||
export default function BuildTrigger({
|
|
||||||
open,
|
|
||||||
flow,
|
|
||||||
}: {
|
|
||||||
open: boolean;
|
|
||||||
flow: FlowType;
|
|
||||||
}): JSX.Element {
|
|
||||||
const isBuilding = useFlowStore((state) => state.isBuilding);
|
|
||||||
const setIsBuilding = useFlowStore((state) => state.setIsBuilding);
|
|
||||||
const buildFlow = useFlowStore((state) => state.buildFlow);
|
|
||||||
const nodes = useFlowStore((state) => state.nodes);
|
|
||||||
const edges = useFlowStore((state) => state.edges);
|
|
||||||
const updateBuildStatus = useFlowStore((state) => state.updateBuildStatus);
|
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
|
||||||
|
|
||||||
const eventClick = isBuilding ? "pointer-events-none" : "";
|
|
||||||
const [progress, setProgress] = useState(0);
|
|
||||||
|
|
||||||
async function handleBuild(flow: FlowType): Promise<void> {
|
|
||||||
try {
|
|
||||||
if (isBuilding) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const errorsObjs = validateNodes(nodes, edges);
|
|
||||||
const errors = errorsObjs.flatMap((errorObj) => errorObj.errors);
|
|
||||||
if (errors.length > 0) {
|
|
||||||
setErrorData({
|
|
||||||
title: MISSED_ERROR_ALERT,
|
|
||||||
list: errors,
|
|
||||||
});
|
|
||||||
const ids = errorsObjs.map((errorObj) => errorObj.id);
|
|
||||||
updateBuildStatus(ids, BuildStatus.ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const minimumLoadingTime = 200; // in milliseconds
|
|
||||||
const startTime = Date.now();
|
|
||||||
setIsBuilding(true);
|
|
||||||
|
|
||||||
await enforceMinimumLoadingTime(startTime, minimumLoadingTime);
|
|
||||||
await buildFlow({});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error:", error);
|
|
||||||
} finally {
|
|
||||||
setIsBuilding(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasIO = useFlowStore((state) => state.hasIO);
|
|
||||||
|
|
||||||
async function enforceMinimumLoadingTime(
|
|
||||||
startTime: number,
|
|
||||||
minimumLoadingTime: number
|
|
||||||
) {
|
|
||||||
const elapsedTime = Date.now() - startTime;
|
|
||||||
const remainingTime = minimumLoadingTime - elapsedTime;
|
|
||||||
|
|
||||||
if (remainingTime > 0) {
|
|
||||||
return new Promise((resolve) => setTimeout(resolve, remainingTime));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Transition
|
|
||||||
show={!open}
|
|
||||||
appear={true}
|
|
||||||
enter="transition ease-out duration-300"
|
|
||||||
enterFrom="translate-y-96"
|
|
||||||
enterTo="translate-y-0"
|
|
||||||
leave="transition ease-in duration-300"
|
|
||||||
leaveFrom="translate-y-0"
|
|
||||||
leaveTo="translate-y-96"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={hasIO ? "fixed bottom-20 right-4" : "fixed bottom-4 right-4"}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`${eventClick} round-button-form`}
|
|
||||||
onClick={() => {
|
|
||||||
handleBuild(flow);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<button>
|
|
||||||
<div className="round-button-div">
|
|
||||||
{isBuilding && progress < 1 ? (
|
|
||||||
// Render your loading animation here when isBuilding is true
|
|
||||||
<RadialProgressComponent
|
|
||||||
// ! confirm below works
|
|
||||||
color={"text-build-trigger"}
|
|
||||||
value={progress}
|
|
||||||
></RadialProgressComponent>
|
|
||||||
) : isBuilding ? (
|
|
||||||
<Loading
|
|
||||||
strokeWidth={1.5}
|
|
||||||
className="build-trigger-loading-icon"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<IconComponent
|
|
||||||
name="Zap"
|
|
||||||
className="sh-6 w-6 fill-build-trigger stroke-build-trigger stroke-1"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
import { Transition } from "@headlessui/react";
|
|
||||||
|
|
||||||
import {
|
|
||||||
CHAT_CANNOT_OPEN_DESCRIPTION,
|
|
||||||
CHAT_CANNOT_OPEN_TITLE,
|
|
||||||
FLOW_NOT_BUILT_DESCRIPTION,
|
|
||||||
FLOW_NOT_BUILT_TITLE,
|
|
||||||
} from "../../../constants/constants";
|
|
||||||
import useAlertStore from "../../../stores/alertStore";
|
|
||||||
import { chatTriggerPropType } from "../../../types/components";
|
|
||||||
import IconComponent from "../../genericIconComponent";
|
|
||||||
|
|
||||||
export default function ChatTrigger({
|
|
||||||
open,
|
|
||||||
setOpen,
|
|
||||||
isBuilt,
|
|
||||||
canOpen,
|
|
||||||
}: chatTriggerPropType): JSX.Element {
|
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
|
||||||
|
|
||||||
function handleClick(): void {
|
|
||||||
if (isBuilt) {
|
|
||||||
if (canOpen) {
|
|
||||||
setOpen(true);
|
|
||||||
} else {
|
|
||||||
setErrorData({
|
|
||||||
title: CHAT_CANNOT_OPEN_TITLE,
|
|
||||||
list: [CHAT_CANNOT_OPEN_DESCRIPTION],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setErrorData({
|
|
||||||
title: FLOW_NOT_BUILT_TITLE,
|
|
||||||
list: [FLOW_NOT_BUILT_DESCRIPTION],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Transition
|
|
||||||
show={!open}
|
|
||||||
appear={true}
|
|
||||||
enter="transition ease-out duration-300"
|
|
||||||
enterFrom="translate-y-96"
|
|
||||||
enterTo="translate-y-0"
|
|
||||||
leave="transition ease-in duration-300"
|
|
||||||
leaveFrom="translate-y-0"
|
|
||||||
leaveTo="translate-y-96"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
onClick={handleClick}
|
|
||||||
className={
|
|
||||||
"shadow-round-btn-shadow hover:shadow-round-btn-shadow message-button " +
|
|
||||||
(!isBuilt || !canOpen ? "cursor-not-allowed" : "cursor-pointer")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<IconComponent
|
|
||||||
name="MessagesSquare"
|
|
||||||
className={
|
|
||||||
"h-6 w-6 transition-all " +
|
|
||||||
(isBuilt && canOpen
|
|
||||||
? "message-button-icon"
|
|
||||||
: "disabled-message-button-icon")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</Transition>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -7,14 +7,12 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
import { useStoreStore } from "../../stores/storeStore";
|
import { useStoreStore } from "../../stores/storeStore";
|
||||||
import { ChatType } from "../../types/chat";
|
import { ChatType } from "../../types/chat";
|
||||||
import { classNames } from "../../utils/utils";
|
import { classNames } from "../../utils/utils";
|
||||||
import IOView from "../IOview";
|
import IOModal from "../../modals/IOModal";
|
||||||
import ForwardedIconComponent from "../genericIconComponent";
|
import ForwardedIconComponent from "../genericIconComponent";
|
||||||
import { Separator } from "../ui/separator";
|
import { Separator } from "../ui/separator";
|
||||||
|
|
||||||
export default function FlowToolbar({ flow }: ChatType): JSX.Element {
|
export default function FlowToolbar(): JSX.Element {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const flowState = useFlowStore((state) => state.flowState);
|
|
||||||
const nodes = useFlowStore((state) => state.nodes);
|
|
||||||
const hasIO = useFlowStore((state) => state.hasIO);
|
const hasIO = useFlowStore((state) => state.hasIO);
|
||||||
const hasStore = useStoreStore((state) => state.hasStore);
|
const hasStore = useStoreStore((state) => state.hasStore);
|
||||||
const validApiKey = useStoreStore((state) => state.validApiKey);
|
const validApiKey = useStoreStore((state) => state.validApiKey);
|
||||||
|
|
@ -92,15 +90,15 @@ export default function FlowToolbar({ flow }: ChatType): JSX.Element {
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="flex h-full w-full gap-1 rounded-sm text-medium-indigo transition-all">
|
<div className="flex h-full w-full gap-1 rounded-sm text-medium-indigo transition-all">
|
||||||
{hasIO ? (
|
{hasIO ? (
|
||||||
<IOView open={open} setOpen={setOpen} disable={!hasIO}>
|
<IOModal open={open} setOpen={setOpen} disable={!hasIO}>
|
||||||
<div className="relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-medium-indigo transition-all transition-all duration-150 ease-in-out ease-in-out hover:bg-hover">
|
<div className="relative inline-flex w-full items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-medium-indigo transition-all transition-all duration-500 ease-in-out ease-in-out hover:bg-hover">
|
||||||
<ForwardedIconComponent
|
<ForwardedIconComponent
|
||||||
name="Zap"
|
name="Zap"
|
||||||
className={"message-button-icon h-5 w-5 transition-all"}
|
className={"message-button-icon h-5 w-5 transition-all"}
|
||||||
/>
|
/>
|
||||||
Run
|
Run
|
||||||
</div>
|
</div>
|
||||||
</IOView>
|
</IOModal>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={`relative inline-flex w-full cursor-not-allowed items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-muted-foreground transition-all duration-150 ease-in-out ease-in-out`}
|
className={`relative inline-flex w-full cursor-not-allowed items-center justify-center gap-1 px-5 py-3 text-sm font-semibold text-muted-foreground transition-all duration-150 ease-in-out ease-in-out`}
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
|
||||||
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
|
||||||
import { programmingLanguages } from "../../../../constants/constants";
|
|
||||||
import { Props } from "../../../../types/components";
|
|
||||||
|
|
||||||
export function CodeBlock({ language, value }: Props): JSX.Element {
|
|
||||||
const [isCopied, setIsCopied] = useState<Boolean>(false);
|
|
||||||
|
|
||||||
const copyToClipboard = (): void => {
|
|
||||||
if (!navigator.clipboard || !navigator.clipboard.writeText) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
navigator.clipboard.writeText(value).then(() => {
|
|
||||||
setIsCopied(true);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
setIsCopied(false);
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const downloadAsFile = (): void => {
|
|
||||||
const fileExtension = programmingLanguages[language] || ".file";
|
|
||||||
const suggestedFileName = `${"generated-code"}${fileExtension}`;
|
|
||||||
const fileName = window.prompt("enter file name", suggestedFileName);
|
|
||||||
|
|
||||||
if (!fileName) {
|
|
||||||
// user pressed cancel on prompt
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const blob = new Blob([value], { type: "text/plain" });
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.download = fileName;
|
|
||||||
link.href = url;
|
|
||||||
link.style.display = "none";
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div className="codeblock font-sans text-[16px]">
|
|
||||||
<div className="code-block-modal">
|
|
||||||
<span className="code-block-modal-span">{language}</span>
|
|
||||||
|
|
||||||
<div className="flex items-center">
|
|
||||||
<button className="code-block-modal-button" onClick={copyToClipboard}>
|
|
||||||
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />}
|
|
||||||
{isCopied ? "Copied!" : "Copy Code"}
|
|
||||||
</button>
|
|
||||||
<button className="code-block-modal-button" onClick={downloadAsFile}>
|
|
||||||
<IconDownload size={18} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SyntaxHighlighter
|
|
||||||
className="overflow-auto"
|
|
||||||
language={language}
|
|
||||||
style={oneDark}
|
|
||||||
customStyle={{ margin: 0 }}
|
|
||||||
>
|
|
||||||
{value}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
CodeBlock.displayName = "CodeBlock";
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
import * as base64js from "base64-js";
|
|
||||||
import { useState } from "react";
|
|
||||||
import IconComponent from "../../../components/genericIconComponent";
|
|
||||||
import { fileCardPropsType } from "../../../types/components";
|
|
||||||
|
|
||||||
export default function FileCard({
|
|
||||||
fileName,
|
|
||||||
content,
|
|
||||||
fileType,
|
|
||||||
}: fileCardPropsType): JSX.Element {
|
|
||||||
const handleDownload = (): void => {
|
|
||||||
const byteArray = new Uint8Array(base64js.toByteArray(content));
|
|
||||||
const blob = new Blob([byteArray], { type: "application/octet-stream" });
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.href = url;
|
|
||||||
link.download = fileName + ".png";
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
};
|
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
|
||||||
function handleMouseEnter(): void {
|
|
||||||
setIsHovered(true);
|
|
||||||
}
|
|
||||||
function handleMouseLeave(): void {
|
|
||||||
setIsHovered(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileType === "image") {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="relative h-1/4 w-1/4"
|
|
||||||
onMouseEnter={handleMouseEnter}
|
|
||||||
onMouseLeave={handleMouseLeave}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={`data:image/png;base64,${content}`}
|
|
||||||
alt="generated image"
|
|
||||||
className="h-full w-full rounded-lg"
|
|
||||||
/>
|
|
||||||
{isHovered && (
|
|
||||||
<div className={`file-card-modal-image-div `}>
|
|
||||||
<button
|
|
||||||
className="file-card-modal-image-button "
|
|
||||||
onClick={handleDownload}
|
|
||||||
>
|
|
||||||
<IconComponent
|
|
||||||
name="DownloadCloud"
|
|
||||||
className="h-5 w-5 text-current hover:scale-110"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button onClick={handleDownload} className="file-card-modal-button">
|
|
||||||
<div className="file-card-modal-div">
|
|
||||||
ooooooooooooooo{" "}
|
|
||||||
{fileType === "image" ? (
|
|
||||||
<img
|
|
||||||
src={`data:image/png;base64,${content}`}
|
|
||||||
alt=""
|
|
||||||
className="h-8 w-8"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<IconComponent name="File" className="h-8 w-8" />
|
|
||||||
)}
|
|
||||||
<div className="file-card-modal-footer">
|
|
||||||
{" "}
|
|
||||||
<div className="file-card-modal-name">{fileName}</div>
|
|
||||||
<div className="file-card-modal-type">{fileType}</div>
|
|
||||||
</div>
|
|
||||||
<IconComponent
|
|
||||||
name="DownloadCloud"
|
|
||||||
className="ml-auto h-6 w-6 text-current"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
import React, { ReactNode } from "react";
|
|
||||||
|
|
||||||
interface ElementStackProps {
|
|
||||||
children: ReactNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const ElementStack: React.FC<ElementStackProps> = ({ children }) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`grid grid-cols-1`}
|
|
||||||
style={{ display: "grid", gridAutoFlow: "row" }}
|
|
||||||
>
|
|
||||||
{children.map((child, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
style={{
|
|
||||||
gridColumn: 1,
|
|
||||||
gridRow: 1,
|
|
||||||
transform: `translateX(${index * 0.1}rem)`,
|
|
||||||
zIndex: children.length - index,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{child}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ElementStack;
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
import { Switch } from "@headlessui/react";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
import { ToggleComponentType } from "../../types/components";
|
|
||||||
import { classNames } from "../../utils/utils";
|
|
||||||
|
|
||||||
export default function ToggleComponent({
|
|
||||||
enabled,
|
|
||||||
setEnabled,
|
|
||||||
disabled,
|
|
||||||
}: ToggleComponentType): JSX.Element {
|
|
||||||
// set component state as disabled
|
|
||||||
useEffect(() => {
|
|
||||||
if (disabled) {
|
|
||||||
setEnabled(false);
|
|
||||||
}
|
|
||||||
}, [disabled, setEnabled]);
|
|
||||||
return (
|
|
||||||
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
|
|
||||||
<Switch
|
|
||||||
checked={enabled}
|
|
||||||
onChange={(isEnabled: boolean) => {
|
|
||||||
setEnabled(isEnabled);
|
|
||||||
}}
|
|
||||||
className={classNames(
|
|
||||||
enabled ? "bg-primary" : "bg-input",
|
|
||||||
"toggle-component-switch "
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Use setting</span>
|
|
||||||
<span
|
|
||||||
className={classNames(
|
|
||||||
enabled ? "translate-x-5" : "translate-x-0",
|
|
||||||
"toggle-component-span",
|
|
||||||
disabled ? "bg-input " : "bg-background"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={classNames(
|
|
||||||
enabled
|
|
||||||
? "opacity-0 duration-100 ease-out"
|
|
||||||
: "opacity-100 duration-200 ease-in",
|
|
||||||
"toggle-component-second-span"
|
|
||||||
)}
|
|
||||||
aria-hidden="true"
|
|
||||||
></span>
|
|
||||||
<span
|
|
||||||
className={classNames(
|
|
||||||
enabled
|
|
||||||
? "opacity-100 duration-200 ease-in"
|
|
||||||
: "opacity-0 duration-100 ease-out",
|
|
||||||
"toggle-component-second-span"
|
|
||||||
)}
|
|
||||||
aria-hidden="true"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</Switch>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -32,7 +32,7 @@ const AccordionTrigger = React.forwardRef<
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<ChevronDownIcon className="h-4 w-4 text-muted-foreground transition-transform duration-200" />
|
<ChevronDownIcon className="h-4 w-4 font-bold text-primary transition-transform duration-200" />
|
||||||
</div>
|
</div>
|
||||||
</AccordionPrimitive.Trigger>
|
</AccordionPrimitive.Trigger>
|
||||||
</AccordionPrimitive.Header>
|
</AccordionPrimitive.Header>
|
||||||
|
|
|
||||||
|
|
@ -14,3 +14,8 @@ export enum BuildStatus {
|
||||||
INACTIVE = "INACTIVE",
|
INACTIVE = "INACTIVE",
|
||||||
ERROR = "ERROR",
|
ERROR = "ERROR",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum InputOutput {
|
||||||
|
INPUT = "input",
|
||||||
|
OUTPUT = "output",
|
||||||
|
}
|
||||||
|
|
@ -83,7 +83,7 @@ export function AuthProvider({ children }): React.ReactElement {
|
||||||
useFlowsManagerStore.setState({ isLoading: false });
|
useFlowsManagerStore.setState({ isLoading: false });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [setUserData, setLoading, autoLogin, setIsAdmin]);
|
}, [autoLogin]);
|
||||||
|
|
||||||
function getUser() {
|
function getUser() {
|
||||||
getLoggedUser()
|
getLoggedUser()
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,16 @@ import "./style/index.css";
|
||||||
import "./style/applies.css";
|
import "./style/applies.css";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import "./style/classes.css";
|
import "./style/classes.css";
|
||||||
|
import { StrictMode } from "react";
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById("root") as HTMLElement
|
document.getElementById("root") as HTMLElement
|
||||||
);
|
);
|
||||||
root.render(
|
root.render(
|
||||||
<ContextWrapper>
|
<StrictMode>
|
||||||
<App />
|
<ContextWrapper>
|
||||||
</ContextWrapper>
|
<App />
|
||||||
|
</ContextWrapper>
|
||||||
|
</StrictMode>
|
||||||
);
|
);
|
||||||
reportWebVitals();
|
reportWebVitals();
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { Button } from "../../ui/button";
|
import { Button } from "../../../../../../components/ui/button";
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { BASE_URL_API } from "../../../constants/constants";
|
import { BASE_URL_API } from "../../../../../../constants/constants";
|
||||||
import { uploadFile } from "../../../controllers/API";
|
import { uploadFile } from "../../../../../../controllers/API";
|
||||||
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../../../stores/flowsManagerStore";
|
||||||
import { IOFileInputProps } from "../../../types/components";
|
import { IOFileInputProps } from "../../../../../../types/components";
|
||||||
import IconComponent from "../../genericIconComponent";
|
import IconComponent from "../../../../../../components/genericIconComponent";
|
||||||
|
|
||||||
export default function IOFileInput({ field, updateValue }: IOFileInputProps) {
|
export default function IOFileInput({ field, updateValue }: IOFileInputProps) {
|
||||||
//component to handle file upload from chatIO
|
//component to handle file upload from chatIO
|
||||||
109
src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx
Normal file
109
src/frontend/src/modals/IOModal/components/IOFieldView/index.tsx
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
|
import { InputOutput } from "../../../../constants/enums";
|
||||||
|
import useFlowStore from "../../../../stores/flowStore";
|
||||||
|
import { IOFieldViewProps } from "../../../../types/components";
|
||||||
|
import IOFileInput from "./components/FileInput";
|
||||||
|
import { Textarea } from "../../../../components/ui/textarea";
|
||||||
|
|
||||||
|
export default function IOFieldView({
|
||||||
|
type,
|
||||||
|
fieldType,
|
||||||
|
fieldId,
|
||||||
|
left,
|
||||||
|
}: IOFieldViewProps): JSX.Element | undefined {
|
||||||
|
const nodes = useFlowStore((state) => state.nodes);
|
||||||
|
const setNode = useFlowStore((state) => state.setNode);
|
||||||
|
const flowPool = useFlowStore((state) => state.flowPool);
|
||||||
|
const node = nodes.find((node) => node.id === fieldId);
|
||||||
|
function handleOutputType() {
|
||||||
|
if (!node) return <>"No node found!"</>;
|
||||||
|
switch (type) {
|
||||||
|
case InputOutput.INPUT:
|
||||||
|
switch (fieldType) {
|
||||||
|
case "TextInput":
|
||||||
|
return (
|
||||||
|
<Textarea
|
||||||
|
className={`w-full ${left ? "" : " h-full"}`}
|
||||||
|
placeholder={"Enter text..."}
|
||||||
|
value={node.data.node!.template["input_value"].value}
|
||||||
|
onChange={(e) => {
|
||||||
|
e.target.value;
|
||||||
|
if (node) {
|
||||||
|
let newNode = cloneDeep(node);
|
||||||
|
newNode.data.node!.template["input_value"].value =
|
||||||
|
e.target.value;
|
||||||
|
setNode(node.id, newNode);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case "FileLoader":
|
||||||
|
return (
|
||||||
|
<IOFileInput
|
||||||
|
field={node.data.node!.template["file_path"]["value"]}
|
||||||
|
updateValue={(e) => {
|
||||||
|
if (node) {
|
||||||
|
let newNode = cloneDeep(node);
|
||||||
|
newNode.data.node!.template["file_path"].value = e;
|
||||||
|
setNode(node.id, newNode);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<Textarea
|
||||||
|
className="w-full custom-scroll"
|
||||||
|
placeholder={"Enter text..."}
|
||||||
|
value={node.data.node!.template["input_value"]}
|
||||||
|
onChange={(e) => {
|
||||||
|
e.target.value;
|
||||||
|
if (node) {
|
||||||
|
let newNode = cloneDeep(node);
|
||||||
|
newNode.data.node!.template["input_value"].value =
|
||||||
|
e.target.value;
|
||||||
|
setNode(node.id, newNode);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case InputOutput.OUTPUT:
|
||||||
|
switch (fieldType) {
|
||||||
|
case "TextOutput":
|
||||||
|
return (
|
||||||
|
<Textarea
|
||||||
|
className={`w-full custom-scroll ${left ? "" : " h-full"}`}
|
||||||
|
placeholder={"Empty"}
|
||||||
|
// update to real value on flowPool
|
||||||
|
value={
|
||||||
|
(flowPool[node.id] ?? [])[
|
||||||
|
(flowPool[node.id]?.length ?? 1) - 1
|
||||||
|
]?.params ?? ""
|
||||||
|
}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<Textarea
|
||||||
|
className={`w-full custom-scroll ${left ? "" : " h-full"}`}
|
||||||
|
placeholder={"Empty"}
|
||||||
|
// update to real value on flowPool
|
||||||
|
value={
|
||||||
|
(flowPool[node.id] ?? [])[
|
||||||
|
(flowPool[node.id]?.length ?? 1) - 1
|
||||||
|
]?.params ?? ""
|
||||||
|
}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handleOutputType();
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import IconComponent from "../../../components/genericIconComponent";
|
import IconComponent from "../../../../../components/genericIconComponent";
|
||||||
import { Textarea } from "../../../components/ui/textarea";
|
import { Textarea } from "../../../../../components/ui/textarea";
|
||||||
import {
|
import {
|
||||||
CHAT_INPUT_PLACEHOLDER,
|
CHAT_INPUT_PLACEHOLDER,
|
||||||
CHAT_INPUT_PLACEHOLDER_SEND,
|
CHAT_INPUT_PLACEHOLDER_SEND,
|
||||||
} from "../../../constants/constants";
|
} from "../../../../../constants/constants";
|
||||||
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../../stores/flowsManagerStore";
|
||||||
import { chatInputType } from "../../../types/components";
|
import { chatInputType } from "../../../../../types/components";
|
||||||
import { classNames } from "../../../utils/utils";
|
import { classNames } from "../../../../../utils/utils";
|
||||||
|
|
||||||
export default function ChatInput({
|
export default function ChatInput({
|
||||||
lockChat,
|
lockChat,
|
||||||
|
|
@ -25,14 +25,6 @@ export default function ChatInput({
|
||||||
}
|
}
|
||||||
}, [lockChat, inputRef]);
|
}, [lockChat, inputRef]);
|
||||||
|
|
||||||
/* function handleChange(value: number) {
|
|
||||||
console.log(value);
|
|
||||||
if (value > 0) {
|
|
||||||
setRepeat(value);
|
|
||||||
} else {
|
|
||||||
setRepeat(1);
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (inputRef.current) {
|
if (inputRef.current) {
|
||||||
|
|
@ -50,7 +42,7 @@ export default function ChatInput({
|
||||||
event.key === "Enter" &&
|
event.key === "Enter" &&
|
||||||
!lockChat &&
|
!lockChat &&
|
||||||
!saveLoading &&
|
!saveLoading &&
|
||||||
!event.shiftKey
|
!event.shiftKey && !event.nativeEvent.isComposing
|
||||||
) {
|
) {
|
||||||
sendMessage(repeat);
|
sendMessage(repeat);
|
||||||
}
|
}
|
||||||
|
|
@ -2,8 +2,8 @@ import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||||
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
||||||
import { programmingLanguages } from "../../../../constants/constants";
|
import { programmingLanguages } from "../../../../../../constants/constants";
|
||||||
import { Props } from "../../../../types/components";
|
import { Props } from "../../../../../../types/components";
|
||||||
|
|
||||||
export function CodeBlock({ language, value }: Props): JSX.Element {
|
export function CodeBlock({ language, value }: Props): JSX.Element {
|
||||||
const [isCopied, setIsCopied] = useState<Boolean>(false);
|
const [isCopied, setIsCopied] = useState<Boolean>(false);
|
||||||
|
|
@ -4,15 +4,14 @@ import Markdown from "react-markdown";
|
||||||
import rehypeMathjax from "rehype-mathjax";
|
import rehypeMathjax from "rehype-mathjax";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
import remarkMath from "remark-math";
|
import remarkMath from "remark-math";
|
||||||
import MaleTechnology from "../../../assets/male-technologist.png";
|
import MaleTechnology from "../../../../../assets/male-technologist.png";
|
||||||
import Robot from "../../../assets/robot.png";
|
import Robot from "../../../../../assets/robot.png";
|
||||||
import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper";
|
import SanitizedHTMLWrapper from "../../../../../components/SanitizedHTMLWrapper";
|
||||||
import CodeTabsComponent from "../../../components/codeTabsComponent";
|
import CodeTabsComponent from "../../../../../components/codeTabsComponent";
|
||||||
import IconComponent from "../../../components/genericIconComponent";
|
import IconComponent from "../../../../../components/genericIconComponent";
|
||||||
import useAlertStore from "../../../stores/alertStore";
|
import useFlowStore from "../../../../../stores/flowStore";
|
||||||
import useFlowStore from "../../../stores/flowStore";
|
import { chatMessagePropsType } from "../../../../../types/components";
|
||||||
import { chatMessagePropsType } from "../../../types/components";
|
import { classNames } from "../../../../../utils/utils";
|
||||||
import { classNames, cn } from "../../../utils/utils";
|
|
||||||
import FileCard from "../fileComponent";
|
import FileCard from "../fileComponent";
|
||||||
|
|
||||||
export default function ChatMessage({
|
export default function ChatMessage({
|
||||||
|
|
@ -34,7 +33,6 @@ export default function ChatMessage({
|
||||||
const [isStreaming, setIsStreaming] = useState(false);
|
const [isStreaming, setIsStreaming] = useState(false);
|
||||||
const eventSource = useRef<EventSource | undefined>(undefined);
|
const eventSource = useRef<EventSource | undefined>(undefined);
|
||||||
const updateFlowPool = useFlowStore((state) => state.updateFlowPool);
|
const updateFlowPool = useFlowStore((state) => state.updateFlowPool);
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
|
||||||
const chatMessageRef = useRef(chatMessage);
|
const chatMessageRef = useRef(chatMessage);
|
||||||
|
|
||||||
// Sync ref with state
|
// Sync ref with state
|
||||||
|
|
@ -59,17 +57,7 @@ export default function ChatMessage({
|
||||||
setIsStreaming(false);
|
setIsStreaming(false);
|
||||||
eventSource.current?.close();
|
eventSource.current?.close();
|
||||||
setStreamUrl(undefined);
|
setStreamUrl(undefined);
|
||||||
// property data is not available in the event object
|
reject(new Error("Streaming failed"));
|
||||||
// so check if the event object has a data property
|
|
||||||
if (event.data) {
|
|
||||||
let parsedData = JSON.parse(event.data);
|
|
||||||
if (parsedData.error) {
|
|
||||||
reject(new Error(parsedData.error));
|
|
||||||
} else
|
|
||||||
reject(new Error("An error occurred while streaming the output"));
|
|
||||||
} else {
|
|
||||||
reject(new Error("An error occurred while streaming the output"));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
eventSource.current.addEventListener("close", (event) => {
|
eventSource.current.addEventListener("close", (event) => {
|
||||||
setStreamUrl(undefined); // Update state to reflect the stream is closed
|
setStreamUrl(undefined); // Update state to reflect the stream is closed
|
||||||
|
|
@ -91,10 +79,7 @@ export default function ChatMessage({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setErrorData({
|
console.error(error);
|
||||||
title: "Streaming Error",
|
|
||||||
list: [error.message],
|
|
||||||
});
|
|
||||||
setLockChat(false);
|
setLockChat(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -123,28 +108,30 @@ export default function ChatMessage({
|
||||||
chat.isSend ? "" : " "
|
chat.isSend ? "" : " "
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div className={classNames("form-modal-chatbot-icon")}>
|
||||||
className={classNames(
|
{!chat.isSend ? (
|
||||||
"mr-3 mt-1 flex max-w-16 flex-col items-center gap-1 overflow-hidden px-3 pb-3"
|
<div className="form-modal-chat-image">
|
||||||
)}
|
<div className="form-modal-chat-bot-icon ">
|
||||||
>
|
<img
|
||||||
<div className="flex flex-col items-center gap-1">
|
src={Robot}
|
||||||
<div
|
className="form-modal-chat-icon-img"
|
||||||
className={cn(
|
alt="robot_image"
|
||||||
"relative flex h-8 w-8 items-center justify-center overflow-hidden rounded-md p-5 text-2xl",
|
/>
|
||||||
!chat.isSend ? "bg-chat-bot-icon" : "bg-chat-user-icon"
|
</div>
|
||||||
)}
|
<span className="truncate text-xs">{chat.sender_name}</span>
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={!chat.isSend ? Robot : MaleTechnology}
|
|
||||||
className="absolute scale-[60%]"
|
|
||||||
alt={!chat.isSend ? "robot_image" : "male_technology"}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="max-w-16 truncate text-xs">
|
) : (
|
||||||
{chat.sender_name}
|
<div className="form-modal-chat-image">
|
||||||
</span>
|
<div className="form-modal-chat-user-icon ">
|
||||||
</div>
|
<img
|
||||||
|
src={MaleTechnology}
|
||||||
|
className="form-modal-chat-icon-img"
|
||||||
|
alt="male_technology"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="truncate text-xs">{chat.sender_name}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!chat.isSend ? (
|
{!chat.isSend ? (
|
||||||
<div className="form-modal-chat-text-position min-w-96 flex-grow">
|
<div className="form-modal-chat-text-position min-w-96 flex-grow">
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import * as base64js from "base64-js";
|
import * as base64js from "base64-js";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import IconComponent from "../../../components/genericIconComponent";
|
import IconComponent from "../../../../../components/genericIconComponent";
|
||||||
import { fileCardPropsType } from "../../../types/components";
|
import { fileCardPropsType } from "../../../../../types/components";
|
||||||
|
|
||||||
export default function FileCard({
|
export default function FileCard({
|
||||||
fileName,
|
fileName,
|
||||||
|
|
@ -1,31 +1,32 @@
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../../../components/genericIconComponent";
|
||||||
import { NOCHATOUTPUT_NOTICE_ALERT } from "../../constants/alerts_constants";
|
import { NOCHATOUTPUT_NOTICE_ALERT } from "../../../../constants/alerts_constants";
|
||||||
import {
|
import {
|
||||||
CHAT_FIRST_INITIAL_TEXT,
|
CHAT_FIRST_INITIAL_TEXT,
|
||||||
CHAT_SECOND_INITIAL_TEXT,
|
CHAT_SECOND_INITIAL_TEXT,
|
||||||
} from "../../constants/constants";
|
} from "../../../../constants/constants";
|
||||||
import { deleteFlowPool } from "../../controllers/API";
|
import { deleteFlowPool } from "../../../../controllers/API";
|
||||||
import useAlertStore from "../../stores/alertStore";
|
import useAlertStore from "../../../../stores/alertStore";
|
||||||
import useFlowStore from "../../stores/flowStore";
|
import useFlowStore from "../../../../stores/flowStore";
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||||
import { sendAllProps } from "../../types/api";
|
import { sendAllProps } from "../../../../types/api";
|
||||||
import {
|
import {
|
||||||
ChatMessageType,
|
ChatMessageType,
|
||||||
ChatOutputType,
|
ChatOutputType,
|
||||||
FlowPoolObjectType,
|
FlowPoolObjectType,
|
||||||
} from "../../types/chat";
|
} from "../../../../types/chat";
|
||||||
import { classNames } from "../../utils/utils";
|
import { classNames } from "../../../../utils/utils";
|
||||||
import ChatInput from "./chatInput";
|
import ChatInput from "./chatInput";
|
||||||
import ChatMessage from "./chatMessage";
|
import ChatMessage from "./chatMessage";
|
||||||
|
import { chatViewProps } from "../../../../types/components";
|
||||||
|
|
||||||
export default function NewChatView({
|
export default function ChatView({
|
||||||
sendMessage,
|
sendMessage,
|
||||||
chatValue,
|
chatValue,
|
||||||
setChatValue,
|
setChatValue,
|
||||||
lockChat,
|
lockChat,
|
||||||
setLockChat,
|
setLockChat,
|
||||||
}): JSX.Element {
|
}: chatViewProps): JSX.Element {
|
||||||
const { flowPool, outputs, inputs, CleanFlowPool } = useFlowStore();
|
const { flowPool, outputs, inputs, CleanFlowPool } = useFlowStore();
|
||||||
const { setNoticeData } = useAlertStore();
|
const { setNoticeData } = useAlertStore();
|
||||||
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
|
||||||
|
|
@ -1,36 +1,37 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import AccordionComponent from "../../components/AccordionComponent";
|
||||||
|
import IOFieldView from "./components/IOFieldView";
|
||||||
|
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||||
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
|
import ChatView from "./components/chatView";
|
||||||
|
import { Badge } from "../../components/ui/badge";
|
||||||
|
import { Button } from "../../components/ui/button";
|
||||||
|
import {
|
||||||
|
Tabs,
|
||||||
|
TabsContent,
|
||||||
|
TabsList,
|
||||||
|
TabsTrigger,
|
||||||
|
} from "../../components/ui/tabs";
|
||||||
import {
|
import {
|
||||||
CHAT_FORM_DIALOG_SUBTITLE,
|
CHAT_FORM_DIALOG_SUBTITLE,
|
||||||
OUTPUTS_MODAL_TITLE,
|
OUTPUTS_MODAL_TITLE,
|
||||||
TEXT_INPUT_MODAL_TITLE,
|
TEXT_INPUT_MODAL_TITLE,
|
||||||
} from "../../constants/constants";
|
} from "../../constants/constants";
|
||||||
import BaseModal from "../../modals/baseModal";
|
import { InputOutput } from "../../constants/enums";
|
||||||
import useFlowStore from "../../stores/flowStore";
|
import useFlowStore from "../../stores/flowStore";
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
import { NodeType } from "../../types/flow";
|
import { NodeType } from "../../types/flow";
|
||||||
import { updateVerticesOrder } from "../../utils/buildUtils";
|
import { updateVerticesOrder } from "../../utils/buildUtils";
|
||||||
import { cn } from "../../utils/utils";
|
import { cn } from "../../utils/utils";
|
||||||
import AccordionComponent from "../AccordionComponent";
|
import BaseModal from "../baseModal";
|
||||||
import IOInputField from "../IOInputField";
|
import { IOModalPropsType } from "../../types/components";
|
||||||
import IOOutputView from "../IOOutputView";
|
|
||||||
import ShadTooltip from "../ShadTooltipComponent";
|
|
||||||
import IconComponent from "../genericIconComponent";
|
|
||||||
import NewChatView from "../newChatView";
|
|
||||||
import { Badge } from "../ui/badge";
|
|
||||||
import { Button } from "../ui/button";
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
|
||||||
|
|
||||||
export default function IOView({
|
export default function IOModal({
|
||||||
children,
|
children,
|
||||||
open,
|
open,
|
||||||
setOpen,
|
setOpen,
|
||||||
disable,
|
disable,
|
||||||
}: {
|
}: IOModalPropsType): JSX.Element {
|
||||||
children: JSX.Element;
|
|
||||||
open: boolean;
|
|
||||||
setOpen: (open: boolean) => void;
|
|
||||||
disable?: boolean;
|
|
||||||
}): JSX.Element {
|
|
||||||
const inputs = useFlowStore((state) => state.inputs).filter(
|
const inputs = useFlowStore((state) => state.inputs).filter(
|
||||||
(input) => input.type !== "ChatInput"
|
(input) => input.type !== "ChatInput"
|
||||||
);
|
);
|
||||||
|
|
@ -212,10 +213,11 @@ export default function IOView({
|
||||||
<div className="file-component-tab-column">
|
<div className="file-component-tab-column">
|
||||||
<div className="">
|
<div className="">
|
||||||
{input && (
|
{input && (
|
||||||
<IOInputField
|
<IOFieldView
|
||||||
|
type={InputOutput.INPUT}
|
||||||
left={true}
|
left={true}
|
||||||
inputType={input.type}
|
fieldType={input.type}
|
||||||
inputId={input.id}
|
fieldId={input.id}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -279,10 +281,11 @@ export default function IOView({
|
||||||
<div className="file-component-tab-column">
|
<div className="file-component-tab-column">
|
||||||
<div className="">
|
<div className="">
|
||||||
{output && (
|
{output && (
|
||||||
<IOOutputView
|
<IOFieldView
|
||||||
|
type={InputOutput.OUTPUT}
|
||||||
left={true}
|
left={true}
|
||||||
outputType={output.type}
|
fieldType={output.type}
|
||||||
outputId={output.id}
|
fieldId={output.id}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -318,16 +321,18 @@ export default function IOView({
|
||||||
{inputs.some(
|
{inputs.some(
|
||||||
(input) => input.id === selectedViewField.id
|
(input) => input.id === selectedViewField.id
|
||||||
) ? (
|
) ? (
|
||||||
<IOInputField
|
<IOFieldView
|
||||||
|
type={InputOutput.INPUT}
|
||||||
left={false}
|
left={false}
|
||||||
inputType={selectedViewField.type!}
|
fieldType={selectedViewField.type!}
|
||||||
inputId={selectedViewField.id!}
|
fieldId={selectedViewField.id!}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<IOOutputView
|
<IOFieldView
|
||||||
|
type={InputOutput.OUTPUT}
|
||||||
left={false}
|
left={false}
|
||||||
outputType={selectedViewField.type!}
|
fieldType={selectedViewField.type!}
|
||||||
outputId={selectedViewField.id!}
|
fieldId={selectedViewField.id!}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -339,7 +344,7 @@ export default function IOView({
|
||||||
selectedViewField ? "hidden" : ""
|
selectedViewField ? "hidden" : ""
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<NewChatView
|
<ChatView
|
||||||
sendMessage={sendMessage}
|
sendMessage={sendMessage}
|
||||||
chatValue={chatValue}
|
chatValue={chatValue}
|
||||||
setChatValue={setChatValue}
|
setChatValue={setChatValue}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||||
import { Card, CardContent, CardDescription, CardTitle } from "../ui/card";
|
import { Card, CardContent, CardDescription, CardTitle } from "../../../../components/ui/card";
|
||||||
|
|
||||||
export default function NewFlowCardComponent() {
|
export default function NewFlowCardComponent() {
|
||||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||||
|
|
@ -1,21 +1,22 @@
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
/// <reference types="vite-plugin-svgr/client" />
|
/// <reference types="vite-plugin-svgr/client" />
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import { ReactComponent as TransferFiles } from "../../assets/undraw_transfer_files_re_a2a9.svg";
|
import { ReactComponent as TransferFiles } from "../../../../assets/undraw_transfer_files_re_a2a9.svg";
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import { ReactComponent as BasicPrompt } from "../../assets/undraw_design_components_9vy6.svg";
|
import { ReactComponent as BasicPrompt } from "../../../../assets/undraw_design_components_9vy6.svg";
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import { ReactComponent as ChatWithHistory } from "../../assets/undraw_mobile_messages_re_yx8w.svg";
|
import { ReactComponent as ChatWithHistory } from "../../../../assets/undraw_mobile_messages_re_yx8w.svg";
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import { ReactComponent as Assistant } from "../../assets/undraw_team_collaboration_re_ow29.svg";
|
import { ReactComponent as Assistant } from "../../../../assets/undraw_team_collaboration_re_ow29.svg";
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import { ReactComponent as APIRequest } from "../../assets/undraw_real_time_analytics_re_yliv.svg";
|
import { ReactComponent as APIRequest } from "../../../../assets/undraw_real_time_analytics_re_yliv.svg";
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||||
import { FlowType } from "../../types/flow";
|
import { FlowType } from "../../../../types/flow";
|
||||||
import { updateIds } from "../../utils/reactflowUtils";
|
import { updateIds } from "../../../../utils/reactflowUtils";
|
||||||
import { Card, CardContent, CardDescription, CardTitle } from "../ui/card";
|
import { Card, CardContent, CardDescription, CardTitle } from "../../../../components/ui/card";
|
||||||
|
import { UndrawCardComponentProps } from "../../../../types/components";
|
||||||
|
|
||||||
export default function UndrawCardComponent({ flow }: { flow: FlowType }) {
|
export default function UndrawCardComponent({ flow }: UndrawCardComponentProps): JSX.Element {
|
||||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
32
src/frontend/src/modals/NewFlowModal/index.tsx
Normal file
32
src/frontend/src/modals/NewFlowModal/index.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import NewFlowCardComponent from "./components/NewFlowCardComponent";
|
||||||
|
import UndrawCardComponent from "./components/undrawCards";
|
||||||
|
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
|
import BaseModal from "../baseModal";
|
||||||
|
import { newFlowModalPropsType } from "../../types/components";
|
||||||
|
|
||||||
|
export default function NewFlowModal({ open, setOpen }: newFlowModalPropsType): JSX.Element {
|
||||||
|
const examples = useFlowsManagerStore((state) => state.examples);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseModal size="three-cards" open={open} setOpen={setOpen}>
|
||||||
|
<BaseModal.Header description={"Select a template below"}>
|
||||||
|
<span className="pr-2" data-testid="modal-title">
|
||||||
|
Get Started
|
||||||
|
</span>
|
||||||
|
{/* <IconComponent
|
||||||
|
name="Group"
|
||||||
|
className="h-6 w-6 stroke-2 text-primary "
|
||||||
|
aria-hidden="true"
|
||||||
|
/> */}
|
||||||
|
</BaseModal.Header>
|
||||||
|
<BaseModal.Content>
|
||||||
|
<div className=" grid h-full w-full grid-cols-3 gap-3 overflow-auto p-4 custom-scroll">
|
||||||
|
<NewFlowCardComponent />
|
||||||
|
{examples.map((example, idx) => {
|
||||||
|
return <UndrawCardComponent key={idx} flow={example} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</BaseModal.Content>
|
||||||
|
</BaseModal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
import { useEffect } from "react";
|
|
||||||
import IconComponent from "../../../components/genericIconComponent";
|
|
||||||
import { Textarea } from "../../../components/ui/textarea";
|
|
||||||
import {
|
|
||||||
CHAT_INPUT_PLACEHOLDER,
|
|
||||||
CHAT_INPUT_PLACEHOLDER_SEND,
|
|
||||||
} from "../../../constants/constants";
|
|
||||||
import { chatInputType } from "../../../types/components";
|
|
||||||
import { classNames } from "../../../utils/utils";
|
|
||||||
|
|
||||||
export default function ChatInput({
|
|
||||||
lockChat,
|
|
||||||
chatValue,
|
|
||||||
sendMessage,
|
|
||||||
setChatValue,
|
|
||||||
inputRef,
|
|
||||||
noInput,
|
|
||||||
}: chatInputType): JSX.Element {
|
|
||||||
useEffect(() => {
|
|
||||||
if (!lockChat && inputRef.current) {
|
|
||||||
inputRef.current.focus();
|
|
||||||
}
|
|
||||||
}, [lockChat, inputRef]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (inputRef.current) {
|
|
||||||
inputRef.current.style.height = "inherit"; // Reset the height
|
|
||||||
inputRef.current.style.height = `${inputRef.current.scrollHeight}px`; // Set it to the scrollHeight
|
|
||||||
}
|
|
||||||
}, [chatValue]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
<Textarea
|
|
||||||
onKeyDown={(event) => {
|
|
||||||
if (event.key === "Enter" && !event.nativeEvent.isComposing && !lockChat && !event.shiftKey) {
|
|
||||||
sendMessage();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
rows={1}
|
|
||||||
ref={inputRef}
|
|
||||||
disabled={lockChat || noInput}
|
|
||||||
style={{
|
|
||||||
resize: "none",
|
|
||||||
bottom: `${inputRef?.current?.scrollHeight}px`,
|
|
||||||
maxHeight: "150px",
|
|
||||||
overflow: `${
|
|
||||||
inputRef.current && inputRef.current.scrollHeight > 150
|
|
||||||
? "auto"
|
|
||||||
: "hidden"
|
|
||||||
}`,
|
|
||||||
}}
|
|
||||||
value={
|
|
||||||
lockChat
|
|
||||||
? "Thinking..."
|
|
||||||
: typeof chatValue === "object" &&
|
|
||||||
Object.keys(chatValue)?.length === 0
|
|
||||||
? CHAT_INPUT_PLACEHOLDER
|
|
||||||
: chatValue
|
|
||||||
}
|
|
||||||
onChange={(event): void => {
|
|
||||||
setChatValue(event.target.value);
|
|
||||||
}}
|
|
||||||
className={classNames(
|
|
||||||
lockChat
|
|
||||||
? " form-modal-lock-true bg-input"
|
|
||||||
: noInput
|
|
||||||
? "form-modal-no-input bg-input"
|
|
||||||
: " form-modal-lock-false bg-background",
|
|
||||||
|
|
||||||
"form-modal-lockchat"
|
|
||||||
)}
|
|
||||||
placeholder={
|
|
||||||
noInput ? CHAT_INPUT_PLACEHOLDER : CHAT_INPUT_PLACEHOLDER_SEND
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div className="form-modal-send-icon-position">
|
|
||||||
<button
|
|
||||||
className={classNames(
|
|
||||||
"form-modal-send-button",
|
|
||||||
noInput
|
|
||||||
? "bg-high-indigo text-background"
|
|
||||||
: chatValue === ""
|
|
||||||
? "text-primary"
|
|
||||||
: "bg-chat-send text-background"
|
|
||||||
)}
|
|
||||||
disabled={lockChat}
|
|
||||||
onClick={(): void => sendMessage()}
|
|
||||||
>
|
|
||||||
{lockChat ? (
|
|
||||||
<IconComponent
|
|
||||||
name="Lock"
|
|
||||||
className="form-modal-lock-icon"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
) : noInput ? (
|
|
||||||
<IconComponent
|
|
||||||
name="Sparkles"
|
|
||||||
className="form-modal-play-icon"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<IconComponent
|
|
||||||
name="LucideSend"
|
|
||||||
className="form-modal-send-icon "
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,221 +0,0 @@
|
||||||
import Convert from "ansi-to-html";
|
|
||||||
import { useState } from "react";
|
|
||||||
import ReactMarkdown from "react-markdown";
|
|
||||||
import rehypeMathjax from "rehype-mathjax";
|
|
||||||
import remarkGfm from "remark-gfm";
|
|
||||||
import remarkMath from "remark-math";
|
|
||||||
import MaleTechnology from "../../../assets/male-technologist.png";
|
|
||||||
import Robot from "../../../assets/robot.png";
|
|
||||||
import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper";
|
|
||||||
import CodeTabsComponent from "../../../components/codeTabsComponent";
|
|
||||||
import IconComponent from "../../../components/genericIconComponent";
|
|
||||||
import { chatMessagePropsType } from "../../../types/components";
|
|
||||||
import { classNames } from "../../../utils/utils";
|
|
||||||
import FileCard from "../fileComponent";
|
|
||||||
|
|
||||||
export default function ChatMessage({
|
|
||||||
chat,
|
|
||||||
lockChat,
|
|
||||||
lastMessage,
|
|
||||||
}: chatMessagePropsType): JSX.Element {
|
|
||||||
const convert = new Convert({ newline: true });
|
|
||||||
const [hidden, setHidden] = useState(true);
|
|
||||||
const template = chat.template;
|
|
||||||
const [promptOpen, setPromptOpen] = useState(false);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classNames("form-modal-chat-position", chat.isSend ? "" : " ")}
|
|
||||||
>
|
|
||||||
<div className={classNames("form-modal-chatbot-icon ")}>
|
|
||||||
{!chat.isSend ? (
|
|
||||||
<div className="form-modal-chat-image">
|
|
||||||
<div className="form-modal-chat-bot-icon ">
|
|
||||||
<img
|
|
||||||
src={Robot}
|
|
||||||
className="form-modal-chat-icon-img"
|
|
||||||
alt="robot_image"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="form-modal-chat-image">
|
|
||||||
<div className="form-modal-chat-user-icon ">
|
|
||||||
<img
|
|
||||||
src={MaleTechnology}
|
|
||||||
className="form-modal-chat-icon-img"
|
|
||||||
alt="male_technology"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{!chat.isSend ? (
|
|
||||||
<div className="form-modal-chat-text-position">
|
|
||||||
<div className="form-modal-chat-text">
|
|
||||||
{hidden && chat.thought && chat.thought !== "" && (
|
|
||||||
<div
|
|
||||||
onClick={(): void => setHidden((prev) => !prev)}
|
|
||||||
className="form-modal-chat-icon-div"
|
|
||||||
>
|
|
||||||
<IconComponent
|
|
||||||
name="MessageSquare"
|
|
||||||
className="form-modal-chat-icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{chat.thought && chat.thought !== "" && !hidden && (
|
|
||||||
<SanitizedHTMLWrapper
|
|
||||||
className=" form-modal-chat-thought"
|
|
||||||
content={convert.toHtml(chat.thought)}
|
|
||||||
onClick={() => setHidden((prev) => !prev)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{chat.thought && chat.thought !== "" && !hidden && <br></br>}
|
|
||||||
<div className="w-full">
|
|
||||||
<div className="w-full dark:text-white">
|
|
||||||
<div className="w-full">
|
|
||||||
{chat.message.toString() === "" && lockChat ? (
|
|
||||||
<IconComponent
|
|
||||||
name="MoreHorizontal"
|
|
||||||
className="h-8 w-8 animate-pulse"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ReactMarkdown
|
|
||||||
remarkPlugins={[remarkGfm, remarkMath]}
|
|
||||||
rehypePlugins={[rehypeMathjax]}
|
|
||||||
className="markdown prose min-w-full text-primary word-break-break-word
|
|
||||||
dark:prose-invert"
|
|
||||||
components={{
|
|
||||||
pre({ node, ...props }) {
|
|
||||||
return <>{props.children}</>;
|
|
||||||
},
|
|
||||||
code: ({
|
|
||||||
node,
|
|
||||||
inline,
|
|
||||||
className,
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}) => {
|
|
||||||
if (children.length) {
|
|
||||||
if (children[0] === "▍") {
|
|
||||||
return (
|
|
||||||
<span className="form-modal-markdown-span">
|
|
||||||
▍
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
children[0] = (children[0] as string).replace(
|
|
||||||
"`▍`",
|
|
||||||
"▍"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const match = /language-(\w+)/.exec(className || "");
|
|
||||||
|
|
||||||
return !inline ? (
|
|
||||||
<CodeTabsComponent
|
|
||||||
isMessage
|
|
||||||
tabs={[
|
|
||||||
{
|
|
||||||
name: (match && match[1]) || "",
|
|
||||||
mode: (match && match[1]) || "",
|
|
||||||
image:
|
|
||||||
"https://curl.se/logo/curl-symbol-transparent.png",
|
|
||||||
language: (match && match[1]) || "",
|
|
||||||
code: String(children).replace(/\n$/, ""),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
activeTab={"0"}
|
|
||||||
setActiveTab={() => {}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<code className={className} {...props}>
|
|
||||||
{children}
|
|
||||||
</code>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{chat.message.toString()}
|
|
||||||
</ReactMarkdown>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{chat.files && (
|
|
||||||
<div className="my-2 w-full">
|
|
||||||
{chat.files.map((file, index) => {
|
|
||||||
return (
|
|
||||||
<div key={index} className="my-2 w-full">
|
|
||||||
<FileCard
|
|
||||||
fileName={"Generated File"}
|
|
||||||
fileType={file.data_type}
|
|
||||||
content={file.data}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
{template ? (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
className="form-modal-initial-prompt-btn"
|
|
||||||
onClick={() => {
|
|
||||||
setPromptOpen((old) => !old);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Display Prompt
|
|
||||||
<IconComponent
|
|
||||||
name="ChevronDown"
|
|
||||||
className={
|
|
||||||
"h-3 w-3 transition-all " + (promptOpen ? "rotate-180" : "")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<span className="prose text-primary word-break-break-word dark:prose-invert">
|
|
||||||
{promptOpen
|
|
||||||
? template?.split("\n")?.map((line, index) => {
|
|
||||||
const regex = /{([^}]+)}/g;
|
|
||||||
let match;
|
|
||||||
let parts: Array<JSX.Element | string> = [];
|
|
||||||
let lastIndex = 0;
|
|
||||||
while ((match = regex.exec(line)) !== null) {
|
|
||||||
// Push text up to the match
|
|
||||||
if (match.index !== lastIndex) {
|
|
||||||
parts.push(line.substring(lastIndex, match.index));
|
|
||||||
}
|
|
||||||
// Push div with matched text
|
|
||||||
if (chat.message[match[1]]) {
|
|
||||||
parts.push(
|
|
||||||
<span className="chat-message-highlight">
|
|
||||||
{chat.message[match[1]]}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update last index
|
|
||||||
lastIndex = regex.lastIndex;
|
|
||||||
}
|
|
||||||
// Push text after the last match
|
|
||||||
if (lastIndex !== line.length) {
|
|
||||||
parts.push(line.substring(lastIndex));
|
|
||||||
}
|
|
||||||
return <p>{parts}</p>;
|
|
||||||
})
|
|
||||||
: chat.message[chat.chatKey]}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<span>{chat.message[chat.chatKey]}</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,650 +0,0 @@
|
||||||
import { useContext, useEffect, useRef, useState } from "react";
|
|
||||||
import { sendAllProps } from "../../types/api";
|
|
||||||
import { ChatMessageType } from "../../types/chat";
|
|
||||||
import { FlowType } from "../../types/flow";
|
|
||||||
import { classNames } from "../../utils/utils";
|
|
||||||
import ChatInput from "./chatInput";
|
|
||||||
import ChatMessage from "./chatMessage";
|
|
||||||
|
|
||||||
import _, { cloneDeep } from "lodash";
|
|
||||||
import AccordionComponent from "../../components/AccordionComponent";
|
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
|
||||||
import ToggleShadComponent from "../../components/toggleShadComponent";
|
|
||||||
import { Badge } from "../../components/ui/badge";
|
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "../../components/ui/dialog";
|
|
||||||
import { Textarea } from "../../components/ui/textarea";
|
|
||||||
import {
|
|
||||||
CHAT_ERROR_ALERT,
|
|
||||||
INFO_MISSING_ALERT,
|
|
||||||
MSG_ERROR_ALERT,
|
|
||||||
} from "../../constants/alerts_constants";
|
|
||||||
import {
|
|
||||||
CHAT_FIRST_INITIAL_TEXT,
|
|
||||||
CHAT_FORM_DIALOG_SUBTITLE,
|
|
||||||
CHAT_SECOND_INITIAL_TEXT,
|
|
||||||
LANGFLOW_CHAT_TITLE,
|
|
||||||
} from "../../constants/constants";
|
|
||||||
import { BuildStatus } from "../../constants/enums";
|
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
|
||||||
import { getBuildStatus } from "../../controllers/API";
|
|
||||||
import useAlertStore from "../../stores/alertStore";
|
|
||||||
import useFlowStore from "../../stores/flowStore";
|
|
||||||
import { FlowState } from "../../types/tabs";
|
|
||||||
import { validateNodes } from "../../utils/reactflowUtils";
|
|
||||||
|
|
||||||
export default function FormModal({
|
|
||||||
flow,
|
|
||||||
open,
|
|
||||||
setOpen,
|
|
||||||
}: {
|
|
||||||
open: boolean;
|
|
||||||
setOpen: (open: boolean) => void;
|
|
||||||
flow: FlowType;
|
|
||||||
}): JSX.Element {
|
|
||||||
const nodes = useFlowStore((state) => state.nodes);
|
|
||||||
const edges = useFlowStore((state) => state.edges);
|
|
||||||
const updateBuildStatus = useFlowStore((state) => state.updateBuildStatus);
|
|
||||||
const flowState = useFlowStore((state) => state.flowState);
|
|
||||||
const setFlowState = useFlowStore((state) => state.setFlowState);
|
|
||||||
const [chatValue, setChatValue] = useState(() => {
|
|
||||||
try {
|
|
||||||
if (!flowState) {
|
|
||||||
throw new Error("flowState is undefined");
|
|
||||||
}
|
|
||||||
const inputKeys = flowState.input_keys;
|
|
||||||
const handleKeys = flowState.handle_keys;
|
|
||||||
|
|
||||||
const keyToUse = Object.keys(inputKeys!).find(
|
|
||||||
(key) => !handleKeys?.some((j) => j === key) && inputKeys![key] === ""
|
|
||||||
);
|
|
||||||
|
|
||||||
return inputKeys![keyToUse!];
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
// return a sensible default or `undefined` if no default is possible
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
|
|
||||||
const template = useRef(flowState?.template ?? undefined);
|
|
||||||
const { accessToken } = useContext(AuthContext);
|
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
|
||||||
const ws = useRef<WebSocket | null>(null);
|
|
||||||
const [lockChat, setLockChat] = useState(false);
|
|
||||||
const isOpen = useRef(open);
|
|
||||||
const messagesRef = useRef<HTMLDivElement | null>(null);
|
|
||||||
|
|
||||||
const [chatKey, setChatKey] = useState(() => {
|
|
||||||
if (flowState?.input_keys) {
|
|
||||||
return Object.keys(flowState.input_keys!).find(
|
|
||||||
(key) =>
|
|
||||||
!flowState.handle_keys!.some((j) => j === key) &&
|
|
||||||
flowState.input_keys![key] === ""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// TODO: return a sensible default
|
|
||||||
return "";
|
|
||||||
});
|
|
||||||
useEffect(() => {
|
|
||||||
if (messagesRef.current) {
|
|
||||||
messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
|
|
||||||
}
|
|
||||||
}, [chatHistory]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
isOpen.current = open;
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
var isStream = false;
|
|
||||||
|
|
||||||
const addChatHistory = (
|
|
||||||
message: string | Object,
|
|
||||||
isSend: boolean,
|
|
||||||
chatKey: string,
|
|
||||||
template?: string,
|
|
||||||
thought?: string,
|
|
||||||
files?: Array<any>
|
|
||||||
) => {
|
|
||||||
setChatHistory((old) => {
|
|
||||||
let newChat = _.cloneDeep(old);
|
|
||||||
if (files) {
|
|
||||||
newChat.push({ message, isSend, files, thought, chatKey });
|
|
||||||
} else if (thought) {
|
|
||||||
newChat.push({ message, isSend, thought, chatKey });
|
|
||||||
} else if (template) {
|
|
||||||
newChat.push({ message, isSend, chatKey, template });
|
|
||||||
} else {
|
|
||||||
newChat.push({ message, isSend, chatKey });
|
|
||||||
}
|
|
||||||
return newChat;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
//add proper type signature for function
|
|
||||||
|
|
||||||
function updateLastMessage({
|
|
||||||
str,
|
|
||||||
thought,
|
|
||||||
prompt,
|
|
||||||
end = false,
|
|
||||||
files,
|
|
||||||
}: {
|
|
||||||
str?: string;
|
|
||||||
thought?: string;
|
|
||||||
prompt?: string;
|
|
||||||
end?: boolean;
|
|
||||||
files?: Array<any>;
|
|
||||||
}) {
|
|
||||||
setChatHistory((old) => {
|
|
||||||
let newChat = [...old];
|
|
||||||
if (str) {
|
|
||||||
if (end) {
|
|
||||||
newChat[newChat.length - 1].message = str;
|
|
||||||
} else {
|
|
||||||
newChat[newChat.length - 1].message =
|
|
||||||
newChat[newChat.length - 1].message + str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thought && newChat[newChat.length - 1]?.thought) {
|
|
||||||
newChat[newChat.length - 1].thought = thought;
|
|
||||||
}
|
|
||||||
if (files && newChat[newChat.length - 1]?.files) {
|
|
||||||
newChat[newChat.length - 1].files = files;
|
|
||||||
}
|
|
||||||
if (prompt && newChat[newChat.length - 2]?.template) {
|
|
||||||
newChat[newChat.length - 2].template = prompt;
|
|
||||||
}
|
|
||||||
return newChat;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleOnClose(event: CloseEvent): void {
|
|
||||||
if (isOpen.current) {
|
|
||||||
//check if the user has been logged out, if so close the chat when the user is redirected to the login page
|
|
||||||
if (window.location.href.includes("login")) {
|
|
||||||
setOpen(false);
|
|
||||||
ws.current?.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getBuildStatus(flow.id)
|
|
||||||
.then((response) => {
|
|
||||||
if (response.data.built) {
|
|
||||||
connectWS();
|
|
||||||
} else {
|
|
||||||
setErrorData({
|
|
||||||
title: CHAT_ERROR_ALERT,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
setErrorData({
|
|
||||||
title: error.data?.detail ? error.data.detail : error.message,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
setErrorData({ title: event.reason });
|
|
||||||
setTimeout(() => {
|
|
||||||
setLockChat(false);
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//TODO improve check of user authentication
|
|
||||||
function getWebSocketUrl(
|
|
||||||
chatId: string,
|
|
||||||
isDevelopment: boolean = false
|
|
||||||
): string {
|
|
||||||
const isSecureProtocol =
|
|
||||||
window.location.protocol === "https:" || window.location.port === "443";
|
|
||||||
const webSocketProtocol = isSecureProtocol ? "wss" : "ws";
|
|
||||||
const host = isDevelopment ? "localhost:7860" : window.location.host;
|
|
||||||
|
|
||||||
const chatEndpoint = `/api/v1/chat/${chatId}`;
|
|
||||||
|
|
||||||
return `${
|
|
||||||
isDevelopment ? "ws" : webSocketProtocol
|
|
||||||
}://${host}${chatEndpoint}?token=${encodeURIComponent(accessToken!)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleWsMessage(data: any) {
|
|
||||||
if (Array.isArray(data) && data.length > 0) {
|
|
||||||
//set chat history
|
|
||||||
setChatHistory((_) => {
|
|
||||||
let newChatHistory: ChatMessageType[] = [];
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
if (data[i].type === "prompt" && data[i].prompt) {
|
|
||||||
if (data[i - 1] && !data[i - 1].is_bot) {
|
|
||||||
data[i - 1].prompt = data[i].prompt;
|
|
||||||
template.current = data[i].prompt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data = data.filter((item: any) => item.type !== "prompt");
|
|
||||||
data.forEach(
|
|
||||||
(chatItem: {
|
|
||||||
intermediate_steps?: string;
|
|
||||||
is_bot: boolean;
|
|
||||||
message: string;
|
|
||||||
prompt?: string;
|
|
||||||
type: string;
|
|
||||||
chatKey: string;
|
|
||||||
files?: Array<any>;
|
|
||||||
}) => {
|
|
||||||
if (chatItem.message) {
|
|
||||||
newChatHistory.push(
|
|
||||||
chatItem.files
|
|
||||||
? {
|
|
||||||
isSend: !chatItem.is_bot,
|
|
||||||
message: chatItem.message,
|
|
||||||
template: chatItem.prompt,
|
|
||||||
thought: chatItem.intermediate_steps,
|
|
||||||
files: chatItem.files,
|
|
||||||
chatKey: chatItem.chatKey,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
isSend: !chatItem.is_bot,
|
|
||||||
message: chatItem.message,
|
|
||||||
template: chatItem.prompt,
|
|
||||||
thought: chatItem.intermediate_steps,
|
|
||||||
chatKey: chatItem.chatKey,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return newChatHistory;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (data.type === "start") {
|
|
||||||
addChatHistory("", false, chatKey!);
|
|
||||||
isStream = true;
|
|
||||||
}
|
|
||||||
if (data.type === "end") {
|
|
||||||
if (data.message) {
|
|
||||||
updateLastMessage({
|
|
||||||
str: data.message,
|
|
||||||
end: true,
|
|
||||||
prompt: template.current,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (data.intermediate_steps) {
|
|
||||||
updateLastMessage({
|
|
||||||
str: data.message,
|
|
||||||
thought: data.intermediate_steps,
|
|
||||||
end: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (data.files) {
|
|
||||||
updateLastMessage({
|
|
||||||
end: true,
|
|
||||||
files: data.files,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setLockChat(false);
|
|
||||||
isStream = false;
|
|
||||||
}
|
|
||||||
if (data.type == "prompt" && data.prompt) {
|
|
||||||
template.current = data.prompt;
|
|
||||||
}
|
|
||||||
if (data.type === "stream" && isStream) {
|
|
||||||
updateLastMessage({ str: data.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function connectWS(): void {
|
|
||||||
try {
|
|
||||||
const urlWs = getWebSocketUrl(
|
|
||||||
flow.id,
|
|
||||||
process.env.NODE_ENV === "development"
|
|
||||||
);
|
|
||||||
const newWs = new WebSocket(urlWs);
|
|
||||||
newWs.onopen = () => {
|
|
||||||
console.log("WebSocket connection established!");
|
|
||||||
};
|
|
||||||
newWs.onmessage = (event) => {
|
|
||||||
const data = JSON.parse(event.data);
|
|
||||||
handleWsMessage(data);
|
|
||||||
//get chat history
|
|
||||||
};
|
|
||||||
newWs.onclose = (event) => {
|
|
||||||
handleOnClose(event);
|
|
||||||
};
|
|
||||||
newWs.onerror = (ev) => {
|
|
||||||
console.log(ev);
|
|
||||||
connectWS();
|
|
||||||
};
|
|
||||||
ws.current = newWs;
|
|
||||||
} catch (error) {
|
|
||||||
if (flow.id === "") {
|
|
||||||
connectWS();
|
|
||||||
}
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
connectWS();
|
|
||||||
return () => {
|
|
||||||
console.log(ws);
|
|
||||||
if (ws.current) {
|
|
||||||
ws.current.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// do not add connectWS on dependencies array
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
if (ws.current) {
|
|
||||||
console.log("closing ws");
|
|
||||||
ws.current.close();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
ws.current &&
|
|
||||||
(ws.current.readyState === ws.current.CLOSED ||
|
|
||||||
ws.current.readyState === ws.current.CLOSING)
|
|
||||||
) {
|
|
||||||
connectWS();
|
|
||||||
setLockChat(false);
|
|
||||||
}
|
|
||||||
// do not add connectWS on dependencies array
|
|
||||||
}, [lockChat]);
|
|
||||||
|
|
||||||
async function sendAll(data: sendAllProps): Promise<void> {
|
|
||||||
try {
|
|
||||||
if (ws) {
|
|
||||||
ws.current?.send(JSON.stringify(data));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
setErrorData({
|
|
||||||
title: MSG_ERROR_ALERT,
|
|
||||||
list: [(error as { message: string }).message],
|
|
||||||
});
|
|
||||||
setChatValue(data.inputs);
|
|
||||||
connectWS();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (ref.current) ref.current.scrollIntoView({ behavior: "smooth" });
|
|
||||||
}, [chatHistory]);
|
|
||||||
|
|
||||||
const ref = useRef<HTMLDivElement | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (open && ref.current) {
|
|
||||||
ref.current.focus();
|
|
||||||
}
|
|
||||||
}, [open]);
|
|
||||||
|
|
||||||
function sendMessage(): void {
|
|
||||||
let nodeValidationErrors = validateNodes(nodes, edges);
|
|
||||||
const errors = nodeValidationErrors.flatMap((error) => error.errors);
|
|
||||||
if (errors.length === 0) {
|
|
||||||
setLockChat(true);
|
|
||||||
let inputs = flowState?.input_keys;
|
|
||||||
setChatValue("");
|
|
||||||
const message = inputs;
|
|
||||||
addChatHistory(message!, true, chatKey!, template.current);
|
|
||||||
sendAll({
|
|
||||||
...flow.data!,
|
|
||||||
inputs: inputs!,
|
|
||||||
chatHistory,
|
|
||||||
name: flow.name,
|
|
||||||
description: flow.description,
|
|
||||||
chatKey: chatKey!,
|
|
||||||
});
|
|
||||||
if (flowState && chatKey) {
|
|
||||||
setFlowState((old: FlowState | undefined) => {
|
|
||||||
let newFlowState = cloneDeep(old!);
|
|
||||||
newFlowState.input_keys![chatKey] = "";
|
|
||||||
return newFlowState;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setErrorData({
|
|
||||||
title: INFO_MISSING_ALERT,
|
|
||||||
list: errors,
|
|
||||||
});
|
|
||||||
const ids = nodeValidationErrors.map((error) => error.id);
|
|
||||||
updateBuildStatus(ids, BuildStatus.ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function clearChat(): void {
|
|
||||||
setChatHistory([]);
|
|
||||||
template.current = flowState?.template;
|
|
||||||
ws.current?.send(JSON.stringify({ clear_history: true }));
|
|
||||||
if (lockChat) setLockChat(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleOnCheckedChange(checked: boolean, i: string) {
|
|
||||||
if (checked === true) {
|
|
||||||
setChatKey(i);
|
|
||||||
setChatValue(flowState?.input_keys![i] ?? "");
|
|
||||||
} else {
|
|
||||||
setChatKey(null!);
|
|
||||||
setChatValue("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
|
||||||
<DialogTrigger hidden></DialogTrigger>
|
|
||||||
{flowState && flowState && (
|
|
||||||
<DialogContent className="min-w-[80vw]">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="flex items-center">
|
|
||||||
<span className="pr-2">Chat</span>
|
|
||||||
<IconComponent
|
|
||||||
name="prompts"
|
|
||||||
className="h-6 w-6 pl-1 text-foreground"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogDescription>{CHAT_FORM_DIALOG_SUBTITLE}</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="flex-max-width mt-2 h-[80vh]">
|
|
||||||
<div className="form-modal-iv-size">
|
|
||||||
<div className="file-component-arrangement">
|
|
||||||
<IconComponent
|
|
||||||
name="Variable"
|
|
||||||
className=" file-component-variable"
|
|
||||||
/>
|
|
||||||
<span className="file-component-variables-span text-md">
|
|
||||||
Input Variables
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="file-component-variables-title">
|
|
||||||
<div className="file-component-variables-div">
|
|
||||||
<span className="text-sm font-medium text-primary">Name</span>
|
|
||||||
</div>
|
|
||||||
<div className="file-component-variables-div">
|
|
||||||
<span className="text-sm font-medium text-primary">
|
|
||||||
Chat Input
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{flowState?.input_keys
|
|
||||||
? Object.keys(flowState?.input_keys!).map((key, index) => (
|
|
||||||
<div className="file-component-accordion-div" key={index}>
|
|
||||||
<AccordionComponent
|
|
||||||
trigger={
|
|
||||||
<div className="file-component-badge-div">
|
|
||||||
<Badge variant="gray" size="md">
|
|
||||||
{key}
|
|
||||||
</Badge>
|
|
||||||
|
|
||||||
<div
|
|
||||||
className="-mb-1"
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ToggleShadComponent
|
|
||||||
enabled={chatKey === key}
|
|
||||||
setEnabled={(value) =>
|
|
||||||
handleOnCheckedChange(value, key)
|
|
||||||
}
|
|
||||||
size="small"
|
|
||||||
disabled={flowState.handle_keys!.some(
|
|
||||||
(t) => t === key
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
key={index}
|
|
||||||
keyValue={key}
|
|
||||||
>
|
|
||||||
<div className="file-component-tab-column">
|
|
||||||
{flowState?.handle_keys!.some((t) => t === key) && (
|
|
||||||
<div className="font-normal text-muted-foreground ">
|
|
||||||
Source: Component
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<Textarea
|
|
||||||
className="custom-scroll"
|
|
||||||
value={flowState?.input_keys![key]}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (flowState) {
|
|
||||||
setFlowState((old: FlowState | undefined) => {
|
|
||||||
let newFlowState = cloneDeep(old!);
|
|
||||||
newFlowState.input_keys![key] =
|
|
||||||
e.target.value;
|
|
||||||
return newFlowState;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
disabled={chatKey === key}
|
|
||||||
placeholder="Enter text..."
|
|
||||||
></Textarea>
|
|
||||||
</div>
|
|
||||||
</AccordionComponent>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
: null}
|
|
||||||
{flowState?.memory_keys!.map((key, index) => (
|
|
||||||
<div className="file-component-accordion-div" key={index}>
|
|
||||||
<AccordionComponent
|
|
||||||
trigger={
|
|
||||||
<div className="file-component-badge-div">
|
|
||||||
<Badge variant="gray" size="md">
|
|
||||||
{key}
|
|
||||||
</Badge>
|
|
||||||
<div className="-mb-1">
|
|
||||||
<ToggleShadComponent
|
|
||||||
enabled={chatKey === key}
|
|
||||||
setEnabled={() => {}}
|
|
||||||
size="small"
|
|
||||||
disabled={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
key={index}
|
|
||||||
keyValue={key}
|
|
||||||
>
|
|
||||||
<div className="file-component-tab-column">
|
|
||||||
<div className="font-normal text-muted-foreground ">
|
|
||||||
Source: Memory
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionComponent>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="eraser-column-arrangement">
|
|
||||||
<div className="eraser-size">
|
|
||||||
<div className="eraser-position">
|
|
||||||
<button disabled={lockChat} onClick={() => clearChat()}>
|
|
||||||
<IconComponent
|
|
||||||
name="Eraser"
|
|
||||||
className={classNames(
|
|
||||||
"h-5 w-5",
|
|
||||||
lockChat
|
|
||||||
? "animate-pulse text-primary"
|
|
||||||
: "text-primary hover:text-gray-600"
|
|
||||||
)}
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div ref={messagesRef} className="chat-message-div">
|
|
||||||
{chatHistory.length > 0 ? (
|
|
||||||
chatHistory.map((chat, index) => (
|
|
||||||
<ChatMessage
|
|
||||||
lockChat={lockChat}
|
|
||||||
chat={chat}
|
|
||||||
lastMessage={
|
|
||||||
chatHistory.length - 1 === index ? true : false
|
|
||||||
}
|
|
||||||
key={index}
|
|
||||||
updateChat={() => {}}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<div className="chat-alert-box">
|
|
||||||
<span>
|
|
||||||
👋{" "}
|
|
||||||
<span className="langflow-chat-span">
|
|
||||||
{LANGFLOW_CHAT_TITLE}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<br />
|
|
||||||
<div className="langflow-chat-desc">
|
|
||||||
<span className="langflow-chat-desc-span">
|
|
||||||
{CHAT_FIRST_INITIAL_TEXT}{" "}
|
|
||||||
<span>
|
|
||||||
<IconComponent
|
|
||||||
name="MessageSquare"
|
|
||||||
className="mx-1 inline h-5 w-5 animate-bounce "
|
|
||||||
/>
|
|
||||||
</span>{" "}
|
|
||||||
{CHAT_SECOND_INITIAL_TEXT}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div ref={ref}></div>
|
|
||||||
</div>
|
|
||||||
<div className="langflow-chat-input-div">
|
|
||||||
<div className="langflow-chat-input">
|
|
||||||
<ChatInput
|
|
||||||
chatValue={chatValue}
|
|
||||||
noInput={!chatKey}
|
|
||||||
lockChat={lockChat}
|
|
||||||
sendMessage={sendMessage}
|
|
||||||
setChatValue={(value) => {
|
|
||||||
setChatValue(value);
|
|
||||||
if (flowState && chatKey) {
|
|
||||||
setFlowState((old: FlowState | undefined) => {
|
|
||||||
let newFlowState = cloneDeep(old!);
|
|
||||||
newFlowState.input_keys![chatKey] = value;
|
|
||||||
return newFlowState;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
inputRef={ref}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
)}
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -141,6 +141,7 @@ export default function ShareModal({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
console.log("ShareModal");
|
||||||
|
|
||||||
const handleUpdateComponent = () => {
|
const handleUpdateComponent = () => {
|
||||||
handleShareComponent(true);
|
handleShareComponent(true);
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ import {
|
||||||
import { getRandomName, isWrappedWithClass } from "../../../../utils/utils";
|
import { getRandomName, isWrappedWithClass } from "../../../../utils/utils";
|
||||||
import ConnectionLineComponent from "../ConnectionLineComponent";
|
import ConnectionLineComponent from "../ConnectionLineComponent";
|
||||||
import SelectionMenu from "../SelectionMenuComponent";
|
import SelectionMenu from "../SelectionMenuComponent";
|
||||||
import ExtraSidebar from "../extraSidebarComponent";
|
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
genericNode: GenericNode,
|
genericNode: GenericNode,
|
||||||
|
|
@ -59,6 +58,8 @@ export default function Page({
|
||||||
const templates = useTypesStore((state) => state.templates);
|
const templates = useTypesStore((state) => state.templates);
|
||||||
const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
|
const setFilterEdge = useFlowStore((state) => state.setFilterEdge);
|
||||||
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
const reactFlowWrapper = useRef<HTMLDivElement>(null);
|
||||||
|
const [showCanvas, setSHowCanvas] = useState(Object.keys(templates).length > 0 &&
|
||||||
|
Object.keys(types).length > 0)
|
||||||
|
|
||||||
const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance);
|
const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance);
|
||||||
const setReactFlowInstance = useFlowStore(
|
const setReactFlowInstance = useFlowStore(
|
||||||
|
|
@ -271,6 +272,10 @@ export default function Page({
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSHowCanvas(Object.keys(templates).length > 0 && Object.keys(types).length > 0)
|
||||||
|
}, [templates, types])
|
||||||
|
|
||||||
const onConnectMod = useCallback(
|
const onConnectMod = useCallback(
|
||||||
(params: Connection) => {
|
(params: Connection) => {
|
||||||
takeSnapshot();
|
takeSnapshot();
|
||||||
|
|
@ -432,71 +437,62 @@ export default function Page({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full overflow-hidden">
|
|
||||||
{!view && <ExtraSidebar />}
|
<div className="h-full w-full" ref={reactFlowWrapper}>
|
||||||
{/* Main area */}
|
{showCanvas ? (
|
||||||
<main className="flex flex-1">
|
<div id="react-flow-id" className="h-full w-full">
|
||||||
{/* Primary column */}
|
<ReactFlow
|
||||||
<div className="h-full w-full">
|
nodes={nodes}
|
||||||
<div className="h-full w-full" ref={reactFlowWrapper}>
|
edges={edges}
|
||||||
{Object.keys(templates).length > 0 &&
|
onNodesChange={onNodesChange}
|
||||||
Object.keys(types).length > 0 ? (
|
onEdgesChange={onEdgesChange}
|
||||||
<div id="react-flow-id" className="h-full w-full">
|
onConnect={onConnectMod}
|
||||||
<ReactFlow
|
disableKeyboardA11y={true}
|
||||||
nodes={nodes}
|
onInit={setReactFlowInstance}
|
||||||
edges={edges}
|
nodeTypes={nodeTypes}
|
||||||
onNodesChange={onNodesChange}
|
onEdgeUpdate={onEdgeUpdate}
|
||||||
onEdgesChange={onEdgesChange}
|
onEdgeUpdateStart={onEdgeUpdateStart}
|
||||||
onConnect={onConnectMod}
|
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||||
disableKeyboardA11y={true}
|
onNodeDragStart={onNodeDragStart}
|
||||||
onInit={setReactFlowInstance}
|
onNodeDragStop={onNodeDragStop}
|
||||||
nodeTypes={nodeTypes}
|
onSelectionDragStart={onSelectionDragStart}
|
||||||
onEdgeUpdate={onEdgeUpdate}
|
onSelectionEnd={onSelectionEnd}
|
||||||
onEdgeUpdateStart={onEdgeUpdateStart}
|
onSelectionStart={onSelectionStart}
|
||||||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
connectionLineComponent={ConnectionLineComponent}
|
||||||
onNodeDragStart={onNodeDragStart}
|
onDragOver={onDragOver}
|
||||||
onNodeDragStop={onNodeDragStop}
|
onMoveEnd={onMoveEnd}
|
||||||
onSelectionDragStart={onSelectionDragStart}
|
onDrop={onDrop}
|
||||||
onSelectionEnd={onSelectionEnd}
|
onSelectionChange={onSelectionChange}
|
||||||
onSelectionStart={onSelectionStart}
|
deleteKeyCode={[]}
|
||||||
connectionLineComponent={ConnectionLineComponent}
|
className="theme-attribution"
|
||||||
onDragOver={onDragOver}
|
minZoom={0.01}
|
||||||
onMoveEnd={onMoveEnd}
|
maxZoom={8}
|
||||||
onDrop={onDrop}
|
zoomOnScroll={!view}
|
||||||
onSelectionChange={onSelectionChange}
|
zoomOnPinch={!view}
|
||||||
deleteKeyCode={[]}
|
panOnDrag={!view}
|
||||||
className="theme-attribution"
|
proOptions={{ hideAttribution: true }}
|
||||||
minZoom={0.01}
|
onPaneClick={onPaneClick}
|
||||||
maxZoom={8}
|
>
|
||||||
zoomOnScroll={!view}
|
<Background className="" />
|
||||||
zoomOnPinch={!view}
|
{!view && (
|
||||||
panOnDrag={!view}
|
<Controls
|
||||||
proOptions={{ hideAttribution: true }}
|
className="bg-muted fill-foreground stroke-foreground text-primary
|
||||||
onPaneClick={onPaneClick}
|
|
||||||
>
|
|
||||||
<Background className="" />
|
|
||||||
{!view && (
|
|
||||||
<Controls
|
|
||||||
className="bg-muted fill-foreground stroke-foreground text-primary
|
|
||||||
[&>button]:border-b-border hover:[&>button]:bg-border"
|
[&>button]:border-b-border hover:[&>button]:bg-border"
|
||||||
></Controls>
|
></Controls>
|
||||||
)}
|
|
||||||
<SelectionMenu
|
|
||||||
isVisible={selectionMenuVisible}
|
|
||||||
nodes={lastSelection?.nodes}
|
|
||||||
onClick={() => {
|
|
||||||
handleGroupNode();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ReactFlow>
|
|
||||||
{!view && <FlowToolbar flow={flow} />}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
<SelectionMenu
|
||||||
|
isVisible={selectionMenuVisible}
|
||||||
|
nodes={lastSelection?.nodes}
|
||||||
|
onClick={() => {
|
||||||
|
handleGroupNode();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ReactFlow>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import { LinkIcon, SparklesIcon } from "lucide-react";
|
import { LinkIcon, SparklesIcon } from "lucide-react";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import AccordionComponent from "../../../../components/AccordionComponent";
|
||||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||||
import IconComponent from "../../../../components/genericIconComponent";
|
import IconComponent from "../../../../components/genericIconComponent";
|
||||||
import { Input } from "../../../../components/ui/input";
|
import { Input } from "../../../../components/ui/input";
|
||||||
|
|
@ -234,6 +235,20 @@ export default function ExtraSidebar(): JSX.Element {
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getIcon = useMemo(() => {
|
||||||
|
return (SBSectionName: string) => {
|
||||||
|
if (nodeIconsLucide[SBSectionName]) {
|
||||||
|
return (
|
||||||
|
<IconComponent
|
||||||
|
name={SBSectionName}
|
||||||
|
strokeWidth={1.5}
|
||||||
|
className="w-[22px] text-primary"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="side-bar-arrangement">
|
<div className="side-bar-arrangement">
|
||||||
<div className="side-bar-search-div-placement">
|
<div className="side-bar-search-div-placement">
|
||||||
|
|
@ -310,7 +325,7 @@ export default function ExtraSidebar(): JSX.Element {
|
||||||
<SidebarDraggableComponent
|
<SidebarDraggableComponent
|
||||||
sectionName={SBSectionName as string}
|
sectionName={SBSectionName as string}
|
||||||
apiClass={dataFilter[SBSectionName][SBItemName]}
|
apiClass={dataFilter[SBSectionName][SBItemName]}
|
||||||
key={index}
|
key={index+ SBItemName}
|
||||||
onDragStart={(event) =>
|
onDragStart={(event) =>
|
||||||
onDragStart(event, {
|
onDragStart(event, {
|
||||||
//split type to remove type in nodes saved with same name removing it's
|
//split type to remove type in nodes saved with same name removing it's
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ import ToolbarSelectItem from "./toolbarSelectItem";
|
||||||
export default function NodeToolbarComponent({
|
export default function NodeToolbarComponent({
|
||||||
data,
|
data,
|
||||||
deleteNode,
|
deleteNode,
|
||||||
position,
|
|
||||||
setShowNode,
|
setShowNode,
|
||||||
numberOfHandles,
|
numberOfHandles,
|
||||||
showNode,
|
showNode,
|
||||||
|
|
@ -79,6 +78,7 @@ export default function NodeToolbarComponent({
|
||||||
const setEdges = useFlowStore((state) => state.setEdges);
|
const setEdges = useFlowStore((state) => state.setEdges);
|
||||||
const unselectAll = useFlowStore((state) => state.unselectAll);
|
const unselectAll = useFlowStore((state) => state.unselectAll);
|
||||||
const saveComponent = useFlowsManagerStore((state) => state.saveComponent);
|
const saveComponent = useFlowsManagerStore((state) => state.saveComponent);
|
||||||
|
const getNodePosition = useFlowStore((state) => state.getNodePosition);
|
||||||
const flows = useFlowsManagerStore((state) => state.flows);
|
const flows = useFlowsManagerStore((state) => state.flows);
|
||||||
const version = useDarkStore((state) => state.version);
|
const version = useDarkStore((state) => state.version);
|
||||||
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
||||||
|
|
@ -146,7 +146,7 @@ export default function NodeToolbarComponent({
|
||||||
takeSnapshot();
|
takeSnapshot();
|
||||||
expandGroupNode(
|
expandGroupNode(
|
||||||
data.id,
|
data.id,
|
||||||
updateFlowPosition(position, data.node?.flow!),
|
updateFlowPosition(getNodePosition(data.id), data.node?.flow!),
|
||||||
data.node!.template,
|
data.node!.template,
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
|
|
@ -631,7 +631,7 @@ export default function NodeToolbarComponent({
|
||||||
)}
|
)}
|
||||||
{hasCode && (
|
{hasCode && (
|
||||||
<div className="hidden">
|
<div className="hidden">
|
||||||
<CodeAreaComponent
|
{openModal&& <CodeAreaComponent
|
||||||
open={openModal}
|
open={openModal}
|
||||||
setOpen={setOpenModal}
|
setOpen={setOpenModal}
|
||||||
readonly={
|
readonly={
|
||||||
|
|
@ -646,7 +646,7 @@ export default function NodeToolbarComponent({
|
||||||
value={data.node?.template[name].value ?? ""}
|
value={data.node?.template[name].value ?? ""}
|
||||||
onChange={handleOnNewValue}
|
onChange={handleOnNewValue}
|
||||||
id={"code-input-node-toolbar-" + name}
|
id={"code-input-node-toolbar-" + name}
|
||||||
/>
|
/>}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@ import Header from "../../components/headerComponent";
|
||||||
import { useDarkStore } from "../../stores/darkStore";
|
import { useDarkStore } from "../../stores/darkStore";
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
import Page from "./components/PageComponent";
|
import Page from "./components/PageComponent";
|
||||||
|
import ExtraSidebar from "./components/extraSidebarComponent";
|
||||||
|
import FlowToolbar from "../../components/chatComponent";
|
||||||
|
|
||||||
export default function FlowPage(): JSX.Element {
|
export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
|
||||||
const setCurrentFlowId = useFlowsManagerStore(
|
const setCurrentFlowId = useFlowsManagerStore(
|
||||||
(state) => state.setCurrentFlowId
|
(state) => state.setCurrentFlowId
|
||||||
);
|
);
|
||||||
|
|
@ -17,12 +19,23 @@ export default function FlowPage(): JSX.Element {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setCurrentFlowId(id!);
|
setCurrentFlowId(id!);
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<Header />
|
||||||
<div className="flow-page-positioning">
|
<div className="flow-page-positioning">
|
||||||
{currentFlow && <Page flow={currentFlow} />}
|
{currentFlow &&
|
||||||
|
<div className="flex h-full overflow-hidden">
|
||||||
|
{!view && <ExtraSidebar />}
|
||||||
|
<main className="flex flex-1">
|
||||||
|
{/* Primary column */}
|
||||||
|
<div className="h-full w-full">
|
||||||
|
<Page flow={currentFlow} />
|
||||||
|
</div>
|
||||||
|
{!view && <FlowToolbar />}
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<a
|
<a
|
||||||
target={"_blank"}
|
target={"_blank"}
|
||||||
href="https://logspace.ai/"
|
href="https://logspace.ai/"
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ import { Group, ToyBrick } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||||
import DropdownButton from "../../components/DropdownButtonComponent";
|
import DropdownButton from "../../components/DropdownButtonComponent";
|
||||||
import NewFlowCardComponent from "../../components/NewFLowCard2";
|
import NewFlowCardComponent from "../../modals/NewFlowModal/components/NewFlowCardComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import PageLayout from "../../components/pageLayout";
|
import PageLayout from "../../components/pageLayout";
|
||||||
import SidebarNav from "../../components/sidebarComponent";
|
import SidebarNav from "../../components/sidebarComponent";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
import UndrawCardComponent from "../../components/undrawCards";
|
import UndrawCardComponent from "../../modals/NewFlowModal/components/undrawCards";
|
||||||
import { CONSOLE_ERROR_MSG } from "../../constants/alerts_constants";
|
import { CONSOLE_ERROR_MSG } from "../../constants/alerts_constants";
|
||||||
import {
|
import {
|
||||||
MY_COLLECTION_DESC,
|
MY_COLLECTION_DESC,
|
||||||
|
|
@ -17,8 +17,8 @@ import BaseModal from "../../modals/baseModal";
|
||||||
import useAlertStore from "../../stores/alertStore";
|
import useAlertStore from "../../stores/alertStore";
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||||
import { downloadFlows } from "../../utils/reactflowUtils";
|
import { downloadFlows } from "../../utils/reactflowUtils";
|
||||||
|
import NewFlowModal from "../../modals/NewFlowModal";
|
||||||
export default function HomePage(): JSX.Element {
|
export default function HomePage(): JSX.Element {
|
||||||
const addFlow = useFlowsManagerStore((state) => state.addFlow);
|
|
||||||
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
|
const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow);
|
||||||
const setCurrentFlowId = useFlowsManagerStore(
|
const setCurrentFlowId = useFlowsManagerStore(
|
||||||
(state) => state.setCurrentFlowId
|
(state) => state.setCurrentFlowId
|
||||||
|
|
@ -29,7 +29,6 @@ export default function HomePage(): JSX.Element {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const pathname = location.pathname;
|
const pathname = location.pathname;
|
||||||
const [openModal, setOpenModal] = useState(false);
|
const [openModal, setOpenModal] = useState(false);
|
||||||
const examples = useFlowsManagerStore((state) => state.examples);
|
|
||||||
const is_component = pathname === "/components";
|
const is_component = pathname === "/components";
|
||||||
const dropdownOptions = [
|
const dropdownOptions = [
|
||||||
{
|
{
|
||||||
|
|
@ -119,26 +118,7 @@ export default function HomePage(): JSX.Element {
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<BaseModal size="three-cards" open={openModal} setOpen={setOpenModal}>
|
<NewFlowModal open={openModal} setOpen={setOpenModal} />
|
||||||
<BaseModal.Header description={"Select a template below"}>
|
|
||||||
<span className="pr-2" data-testid="modal-title">
|
|
||||||
Get Started
|
|
||||||
</span>
|
|
||||||
{/* <IconComponent
|
|
||||||
name="Group"
|
|
||||||
className="h-6 w-6 stroke-2 text-primary "
|
|
||||||
aria-hidden="true"
|
|
||||||
/> */}
|
|
||||||
</BaseModal.Header>
|
|
||||||
<BaseModal.Content>
|
|
||||||
<div className=" grid h-full w-full grid-cols-3 gap-3 overflow-auto p-4 custom-scroll">
|
|
||||||
<NewFlowCardComponent />
|
|
||||||
{examples.map((example, idx) => {
|
|
||||||
return <UndrawCardComponent key={idx} flow={example} />;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</BaseModal.Content>
|
|
||||||
</BaseModal>
|
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ export default function DeleteAccountPage() {
|
||||||
// Implement your account deletion logic here
|
// Implement your account deletion logic here
|
||||||
// For example, make an API call to delete the account
|
// For example, make an API call to delete the account
|
||||||
// Upon successful deletion, you can redirect the user to another page
|
// Upon successful deletion, you can redirect the user to another page
|
||||||
console.log("Account deleted!");
|
|
||||||
// Implement the logic to redirect the user after account deletion.
|
// Implement the logic to redirect the user after account deletion.
|
||||||
// For example, use react-router-dom's useHistory hook.
|
// For example, use react-router-dom's useHistory hook.
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,10 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
}
|
}
|
||||||
get().setFlowPool(newFlowPool);
|
get().setFlowPool(newFlowPool);
|
||||||
},
|
},
|
||||||
|
getNodePosition: (nodeId: string) => {
|
||||||
|
const node = get().nodes.find((node) => node.id === nodeId);
|
||||||
|
return node?.position||{x:0,y:0};
|
||||||
|
},
|
||||||
updateFlowPool: (
|
updateFlowPool: (
|
||||||
nodeId: string,
|
nodeId: string,
|
||||||
data: FlowPoolObjectType | ChatOutputType | chatInputType,
|
data: FlowPoolObjectType | ChatOutputType | chatInputType,
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||||
setFlows: (flows: FlowType[]) => {
|
setFlows: (flows: FlowType[]) => {
|
||||||
set({
|
set({
|
||||||
flows,
|
flows,
|
||||||
currentFlow: flows.find((flow) => flow.id === get().currentFlowId),
|
// currentFlow: flows.find((flow) => flow.id === get().currentFlowId),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
currentFlow: undefined,
|
currentFlow: undefined,
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,7 @@
|
||||||
@apply flex w-full select-none items-center justify-between border-y border-y-input bg-background px-3 py-2;
|
@apply flex w-full select-none items-center justify-between border-y border-y-input bg-background px-3 py-2;
|
||||||
}
|
}
|
||||||
.components-disclosure-arrangement {
|
.components-disclosure-arrangement {
|
||||||
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;
|
@apply -mt-px flex w-full select-none items-center justify-between border-y border-t-2 border-y-input bg-muted px-3 py-2;
|
||||||
}
|
}
|
||||||
.components-disclosure-arrangement-child {
|
.components-disclosure-arrangement-child {
|
||||||
/* different color than the non child */
|
/* different color than the non child */
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { ReactElement, ReactNode, SetStateAction } from "react";
|
import { ReactElement, ReactNode, SetStateAction } from "react";
|
||||||
import { ReactFlowJsonObject, XYPosition } from "reactflow";
|
import { ReactFlowJsonObject, XYPosition } from "reactflow";
|
||||||
|
import { InputOutput } from "../../constants/enums";
|
||||||
import { APIClassType, APITemplateType, TemplateVariableType } from "../api";
|
import { APIClassType, APITemplateType, TemplateVariableType } from "../api";
|
||||||
import { ChatMessageType } from "../chat";
|
import { ChatMessageType } from "../chat";
|
||||||
import { FlowStyleType, FlowType, NodeDataType, NodeType } from "../flow/index";
|
import { FlowStyleType, FlowType, NodeDataType, NodeType } from "../flow/index";
|
||||||
|
|
@ -217,6 +218,8 @@ export type AccordionComponentType = {
|
||||||
open?: string[];
|
open?: string[];
|
||||||
trigger?: string | ReactElement;
|
trigger?: string | ReactElement;
|
||||||
keyValue?: string;
|
keyValue?: string;
|
||||||
|
openDisc?: boolean;
|
||||||
|
sideBar?: boolean;
|
||||||
};
|
};
|
||||||
export type Side = "top" | "right" | "bottom" | "left";
|
export type Side = "top" | "right" | "bottom" | "left";
|
||||||
|
|
||||||
|
|
@ -498,7 +501,6 @@ export type fileCardPropsType = {
|
||||||
export type nodeToolbarPropsType = {
|
export type nodeToolbarPropsType = {
|
||||||
data: NodeDataType;
|
data: NodeDataType;
|
||||||
deleteNode: (idx: string) => void;
|
deleteNode: (idx: string) => void;
|
||||||
position: XYPosition;
|
|
||||||
setShowNode: (boolean: any) => void;
|
setShowNode: (boolean: any) => void;
|
||||||
numberOfHandles: number;
|
numberOfHandles: number;
|
||||||
showNode: boolean;
|
showNode: boolean;
|
||||||
|
|
@ -563,12 +565,6 @@ export type chatMessagePropsType = {
|
||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type formModalPropsType = {
|
|
||||||
open: boolean;
|
|
||||||
setOpen: Function;
|
|
||||||
flow: FlowType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type genericModalPropsType = {
|
export type genericModalPropsType = {
|
||||||
field_name?: string;
|
field_name?: string;
|
||||||
setValue: (value: string) => void;
|
setValue: (value: string) => void;
|
||||||
|
|
@ -583,6 +579,18 @@ export type genericModalPropsType = {
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type newFlowModalPropsType = {
|
||||||
|
open: boolean;
|
||||||
|
setOpen: (open: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IOModalPropsType = {
|
||||||
|
children: JSX.Element;
|
||||||
|
open: boolean;
|
||||||
|
setOpen: (open: boolean) => void;
|
||||||
|
disable?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type buttonBoxPropsType = {
|
export type buttonBoxPropsType = {
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
title: string;
|
title: string;
|
||||||
|
|
@ -693,15 +701,21 @@ export type dropdownButtonPropsType = {
|
||||||
dropdownOptions?: boolean;
|
dropdownOptions?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IOInputProps = {
|
export type IOFieldViewProps = {
|
||||||
inputType: string;
|
type: InputOutput;
|
||||||
inputId: string;
|
fieldType: string;
|
||||||
|
fieldId: string;
|
||||||
left?: boolean;
|
left?: boolean;
|
||||||
};
|
};
|
||||||
export type IOOutputProps = {
|
|
||||||
outputType: string;
|
export type UndrawCardComponentProps = { flow: FlowType };
|
||||||
outputId: string;
|
|
||||||
left?: boolean;
|
export type chatViewProps = {
|
||||||
|
sendMessage: (count?: number) => void;
|
||||||
|
chatValue: string;
|
||||||
|
setChatValue: (value: string) => void;
|
||||||
|
lockChat: boolean;
|
||||||
|
setLockChat: (lock: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type IOFileInputProps = {
|
export type IOFileInputProps = {
|
||||||
|
|
|
||||||
|
|
@ -131,4 +131,5 @@ export type FlowStoreType = {
|
||||||
data: FlowPoolObjectType | ChatOutputType | chatInputType,
|
data: FlowPoolObjectType | ChatOutputType | chatInputType,
|
||||||
buildId?: string
|
buildId?: string
|
||||||
) => void;
|
) => void;
|
||||||
|
getNodePosition: (nodeId: string) => { x: number; y: number };
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import react from "@vitejs/plugin-react-swc";
|
import react from "@vitejs/plugin-react-swc";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import svgr from "vite-plugin-svgr";
|
import svgr from "vite-plugin-svgr";
|
||||||
|
import MillionCompiler from "@million/lint";
|
||||||
const apiRoutes = ["^/api/v1/", "/health"];
|
const apiRoutes = ["^/api/v1/", "/health"];
|
||||||
|
|
||||||
// Use environment variable to determine the target.
|
// Use environment variable to determine the target.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue